Compare commits
No commits in common. "1fda4248ec994b2e6f7cbe8853eb58ce5adffad8" and "7edf0fdb93931737d1cff649a8a6e407c710306d" have entirely different histories.
1fda4248ec
...
7edf0fdb93
|
@ -141,9 +141,6 @@
|
||||||
D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */; };
|
D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */; };
|
||||||
D646C95A213B5D0500269FB5 /* LargeImageInteractionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */; };
|
D646C95A213B5D0500269FB5 /* LargeImageInteractionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */; };
|
||||||
D647D92824257BEB0005044F /* AttachmentPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D647D92724257BEB0005044F /* AttachmentPreviewViewController.swift */; };
|
D647D92824257BEB0005044F /* AttachmentPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D647D92724257BEB0005044F /* AttachmentPreviewViewController.swift */; };
|
||||||
D64AAE9126C80DC600FC57FB /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9026C80DC600FC57FB /* ToastView.swift */; };
|
|
||||||
D64AAE9526C88C5000FC57FB /* ToastableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9426C88C5000FC57FB /* ToastableViewController.swift */; };
|
|
||||||
D64AAE9726C88DC400FC57FB /* ToastConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64AAE9626C88DC400FC57FB /* ToastConfiguration.swift */; };
|
|
||||||
D64BC18623C1253A000D0238 /* AssetPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18523C1253A000D0238 /* AssetPreviewViewController.swift */; };
|
D64BC18623C1253A000D0238 /* AssetPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18523C1253A000D0238 /* AssetPreviewViewController.swift */; };
|
||||||
D64BC18823C1640A000D0238 /* PinStatusActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18723C1640A000D0238 /* PinStatusActivity.swift */; };
|
D64BC18823C1640A000D0238 /* PinStatusActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18723C1640A000D0238 /* PinStatusActivity.swift */; };
|
||||||
D64BC18A23C16487000D0238 /* UnpinStatusActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18923C16487000D0238 /* UnpinStatusActivity.swift */; };
|
D64BC18A23C16487000D0238 /* UnpinStatusActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64BC18923C16487000D0238 /* UnpinStatusActivity.swift */; };
|
||||||
|
@ -547,9 +544,6 @@
|
||||||
D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageShrinkAnimationController.swift; sourceTree = "<group>"; };
|
D646C957213B367000269FB5 /* LargeImageShrinkAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageShrinkAnimationController.swift; sourceTree = "<group>"; };
|
||||||
D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageInteractionController.swift; sourceTree = "<group>"; };
|
D646C959213B5D0500269FB5 /* LargeImageInteractionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeImageInteractionController.swift; sourceTree = "<group>"; };
|
||||||
D647D92724257BEB0005044F /* AttachmentPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentPreviewViewController.swift; sourceTree = "<group>"; };
|
D647D92724257BEB0005044F /* AttachmentPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentPreviewViewController.swift; sourceTree = "<group>"; };
|
||||||
D64AAE9026C80DC600FC57FB /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
|
|
||||||
D64AAE9426C88C5000FC57FB /* ToastableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastableViewController.swift; sourceTree = "<group>"; };
|
|
||||||
D64AAE9626C88DC400FC57FB /* ToastConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastConfiguration.swift; sourceTree = "<group>"; };
|
|
||||||
D64BC18523C1253A000D0238 /* AssetPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetPreviewViewController.swift; sourceTree = "<group>"; };
|
D64BC18523C1253A000D0238 /* AssetPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetPreviewViewController.swift; sourceTree = "<group>"; };
|
||||||
D64BC18723C1640A000D0238 /* PinStatusActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinStatusActivity.swift; sourceTree = "<group>"; };
|
D64BC18723C1640A000D0238 /* PinStatusActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinStatusActivity.swift; sourceTree = "<group>"; };
|
||||||
D64BC18923C16487000D0238 /* UnpinStatusActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnpinStatusActivity.swift; sourceTree = "<group>"; };
|
D64BC18923C16487000D0238 /* UnpinStatusActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnpinStatusActivity.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1068,27 +1062,27 @@
|
||||||
D641C780213DD7C4004B4513 /* Screens */ = {
|
D641C780213DD7C4004B4513 /* Screens */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D6A3BC822321F69400FD64D5 /* Account List */,
|
D6C693FA2162FE5D007D6A6D /* Utilities */,
|
||||||
D6B053A023BD2BED00A066FA /* Asset Picker */,
|
|
||||||
0411610522B457290030A9B7 /* Attachment Gallery */,
|
|
||||||
D627944823A6AD5100D38C68 /* Bookmarks */,
|
|
||||||
D641C787213DD862004B4513 /* Compose */,
|
|
||||||
D641C785213DD83B004B4513 /* Conversation */,
|
|
||||||
D6F2E960249E772F005846BB /* Crash Reporter */,
|
D6F2E960249E772F005846BB /* Crash Reporter */,
|
||||||
|
D641C782213DD7F0004B4513 /* Main */,
|
||||||
|
D641C783213DD7FE004B4513 /* Onboarding */,
|
||||||
|
D6A4DCC92553666600D9DE31 /* Fast Account Switcher */,
|
||||||
|
D641C781213DD7DD004B4513 /* Timeline */,
|
||||||
|
D641C784213DD819004B4513 /* Profile */,
|
||||||
|
D641C785213DD83B004B4513 /* Conversation */,
|
||||||
|
D641C786213DD852004B4513 /* Notifications */,
|
||||||
|
D641C787213DD862004B4513 /* Compose */,
|
||||||
|
D6B053A023BD2BED00A066FA /* Asset Picker */,
|
||||||
D627FF77217E94F200CC0648 /* Drafts */,
|
D627FF77217E94F200CC0648 /* Drafts */,
|
||||||
D627943C23A5635D00D38C68 /* Explore */,
|
D627943C23A5635D00D38C68 /* Explore */,
|
||||||
D6A4DCC92553666600D9DE31 /* Fast Account Switcher */,
|
|
||||||
D641C788213DD86D004B4513 /* Large Image */,
|
|
||||||
D627944B23A9A02400D38C68 /* Lists */,
|
|
||||||
D641C782213DD7F0004B4513 /* Main */,
|
|
||||||
D641C786213DD852004B4513 /* Notifications */,
|
|
||||||
D641C783213DD7FE004B4513 /* Onboarding */,
|
|
||||||
D641C789213DD87E004B4513 /* Preferences */,
|
|
||||||
D641C784213DD819004B4513 /* Profile */,
|
|
||||||
D6BC9DD8232D8BCA002CA326 /* Search */,
|
D6BC9DD8232D8BCA002CA326 /* Search */,
|
||||||
|
D627944B23A9A02400D38C68 /* Lists */,
|
||||||
|
D641C788213DD86D004B4513 /* Large Image */,
|
||||||
|
0411610522B457290030A9B7 /* Attachment Gallery */,
|
||||||
|
D6A3BC822321F69400FD64D5 /* Account List */,
|
||||||
D6A3BC8C2321FF9B00FD64D5 /* Status Action Account List */,
|
D6A3BC8C2321FF9B00FD64D5 /* Status Action Account List */,
|
||||||
D641C781213DD7DD004B4513 /* Timeline */,
|
D627944823A6AD5100D38C68 /* Bookmarks */,
|
||||||
D6C693FA2162FE5D007D6A6D /* Utilities */,
|
D641C789213DD87E004B4513 /* Preferences */,
|
||||||
);
|
);
|
||||||
path = Screens;
|
path = Screens;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1269,16 +1263,6 @@
|
||||||
path = Transitions;
|
path = Transitions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
D64AAE8F26C80DB600FC57FB /* Toast */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D64AAE9026C80DC600FC57FB /* ToastView.swift */,
|
|
||||||
D64AAE9626C88DC400FC57FB /* ToastConfiguration.swift */,
|
|
||||||
D64AAE9426C88C5000FC57FB /* ToastableViewController.swift */,
|
|
||||||
);
|
|
||||||
path = Toast;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D65A37F221472F300087646E /* Frameworks */ = {
|
D65A37F221472F300087646E /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1470,36 +1454,35 @@
|
||||||
D6BED1722126661300F02DA0 /* Views */ = {
|
D6BED1722126661300F02DA0 /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */,
|
|
||||||
D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */,
|
|
||||||
D620483523D38075008A63EF /* ContentTextView.swift */,
|
|
||||||
D6E426B225337C7000C02E1C /* CustomEmojiImageView.swift */,
|
|
||||||
D6969E9F240C8384002843CE /* EmojiLabel.swift */,
|
|
||||||
D67C1794266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift */,
|
|
||||||
D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */,
|
|
||||||
D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */,
|
|
||||||
D620483323D3801D008A63EF /* LinkTextView.swift */,
|
D620483323D3801D008A63EF /* LinkTextView.swift */,
|
||||||
|
D620483523D38075008A63EF /* ContentTextView.swift */,
|
||||||
|
D620483723D38190008A63EF /* StatusContentTextView.swift */,
|
||||||
|
D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */,
|
||||||
|
D6969E9F240C8384002843CE /* EmojiLabel.swift */,
|
||||||
D68E6F58253C9969001A1B4C /* MultiSourceEmojiLabel.swift */,
|
D68E6F58253C9969001A1B4C /* MultiSourceEmojiLabel.swift */,
|
||||||
|
D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */,
|
||||||
|
04ED00B021481ED800567C53 /* SteppedProgressView.swift */,
|
||||||
D627943A23A55BA600D38C68 /* NavigableTableViewCell.swift */,
|
D627943A23A55BA600D38C68 /* NavigableTableViewCell.swift */,
|
||||||
D627943123A5466600D38C68 /* SelectableTableViewCell.swift */,
|
D627943123A5466600D38C68 /* SelectableTableViewCell.swift */,
|
||||||
D620483723D38190008A63EF /* StatusContentTextView.swift */,
|
D627944623A6AC9300D38C68 /* BasicTableViewCell.xib */,
|
||||||
04ED00B021481ED800567C53 /* SteppedProgressView.swift */,
|
|
||||||
D6403CC124A6B72D00E81C55 /* VisualEffectImageButton.swift */,
|
D6403CC124A6B72D00E81C55 /* VisualEffectImageButton.swift */,
|
||||||
D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */,
|
D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */,
|
||||||
D627944623A6AC9300D38C68 /* BasicTableViewCell.xib */,
|
D6B4A4FE2506B81A000C81C1 /* AccountDisplayNameLabel.swift */,
|
||||||
D6A3BC872321F78000FD64D5 /* Account Cell */,
|
D6E426B225337C7000C02E1C /* CustomEmojiImageView.swift */,
|
||||||
|
D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */,
|
||||||
|
D67C1794266D57D10070F250 /* FastAccountSwitcherIndicatorView.swift */,
|
||||||
D67C57A721E2649B00C3118B /* Account Detail */,
|
D67C57A721E2649B00C3118B /* Account Detail */,
|
||||||
D626494023C122C800612E6E /* Asset Picker */,
|
D626494023C122C800612E6E /* Asset Picker */,
|
||||||
D6C7D27B22B6EBE200071952 /* Attachments */,
|
|
||||||
D6DEA0DB268400AF00FE896A /* Confirm Load More Cell */,
|
|
||||||
D61959D0241E842400A37B8E /* Draft Cell */,
|
D61959D0241E842400A37B8E /* Draft Cell */,
|
||||||
D611C2CC232DC5FC00C86A49 /* Hashtag Cell */,
|
D641C78A213DD926004B4513 /* Status */,
|
||||||
D61AC1DA232EA43100C54D2D /* Instance Cell */,
|
D6C7D27B22B6EBE200071952 /* Attachments */,
|
||||||
D641C78C213DD937004B4513 /* Notifications */,
|
|
||||||
D623A53B2635F4E20095BD04 /* Poll */,
|
D623A53B2635F4E20095BD04 /* Poll */,
|
||||||
D641C78B213DD92F004B4513 /* Profile Header */,
|
D641C78B213DD92F004B4513 /* Profile Header */,
|
||||||
D641C78A213DD926004B4513 /* Status */,
|
D641C78C213DD937004B4513 /* Notifications */,
|
||||||
D64AAE8F26C80DB600FC57FB /* Toast */,
|
D6A3BC872321F78000FD64D5 /* Account Cell */,
|
||||||
|
D611C2CC232DC5FC00C86A49 /* Hashtag Cell */,
|
||||||
|
D61AC1DA232EA43100C54D2D /* Instance Cell */,
|
||||||
|
D6DEA0DB268400AF00FE896A /* Confirm Load More Cell */,
|
||||||
D6420AEB26BED17500ED8175 /* Timeline Description Cell */,
|
D6420AEB26BED17500ED8175 /* Timeline Description Cell */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
|
@ -1508,24 +1491,24 @@
|
||||||
D6C693FA2162FE5D007D6A6D /* Utilities */ = {
|
D6C693FA2162FE5D007D6A6D /* Utilities */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */,
|
||||||
|
D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */,
|
||||||
|
D6E0DC8D216EDF1E00369478 /* Previewing.swift */,
|
||||||
|
D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */,
|
||||||
|
D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */,
|
||||||
|
D6BC8747219738E1006163F1 /* EnhancedTableViewController.swift */,
|
||||||
|
D6B22A0E2560D52D004D82EF /* TabbedPageViewController.swift */,
|
||||||
|
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */,
|
||||||
|
D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */,
|
||||||
|
D693DE5823FE24300061E07D /* InteractivePushTransition.swift */,
|
||||||
|
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */,
|
||||||
|
D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */,
|
||||||
D65C6BF425478A9C00A6E89C /* BackgroundableViewController.swift */,
|
D65C6BF425478A9C00A6E89C /* BackgroundableViewController.swift */,
|
||||||
|
D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */,
|
||||||
|
D65234C8256189D0001AF9CF /* TimelineLikeTableViewController.swift */,
|
||||||
D653F410267D1E32004E32B1 /* DiffableTimelineLikeTableViewController.swift */,
|
D653F410267D1E32004E32B1 /* DiffableTimelineLikeTableViewController.swift */,
|
||||||
D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */,
|
D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */,
|
||||||
D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */,
|
|
||||||
D6BC8747219738E1006163F1 /* EnhancedTableViewController.swift */,
|
|
||||||
D693DE5823FE24300061E07D /* InteractivePushTransition.swift */,
|
|
||||||
D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */,
|
|
||||||
D6E0DC8D216EDF1E00369478 /* Previewing.swift */,
|
|
||||||
D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */,
|
|
||||||
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */,
|
|
||||||
D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */,
|
D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */,
|
||||||
D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */,
|
|
||||||
D6B22A0E2560D52D004D82EF /* TabbedPageViewController.swift */,
|
|
||||||
D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */,
|
|
||||||
D65234C8256189D0001AF9CF /* TimelineLikeTableViewController.swift */,
|
|
||||||
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */,
|
|
||||||
D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */,
|
|
||||||
D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */,
|
|
||||||
);
|
);
|
||||||
path = Utilities;
|
path = Utilities;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1572,36 +1555,36 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D6E4885C24A2890C0011C13E /* Tusker.entitlements */,
|
D6E4885C24A2890C0011C13E /* Tusker.entitlements */,
|
||||||
D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */,
|
|
||||||
D6D4DDDB212518A200E1C4BB /* Info.plist */,
|
|
||||||
D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */,
|
D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */,
|
||||||
D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */,
|
D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */,
|
||||||
D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */,
|
D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */,
|
||||||
D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */,
|
D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */,
|
||||||
D6E4269C2532A3E100C02E1C /* FuzzyMatcher.swift */,
|
|
||||||
D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */,
|
|
||||||
D60E2F2B24423EAD005F8713 /* LazilyDecoding.swift */,
|
|
||||||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */,
|
D64D0AAC2128D88B005A6F37 /* LocalData.swift */,
|
||||||
D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */,
|
|
||||||
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */,
|
|
||||||
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
||||||
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */,
|
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */,
|
||||||
D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */,
|
D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */,
|
||||||
D6D4DDD6212518A200E1C4BB /* Assets.xcassets */,
|
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */,
|
||||||
D6AEBB3F2321640F00E5038B /* Activities */,
|
D60E2F2B24423EAD005F8713 /* LazilyDecoding.swift */,
|
||||||
D6F1F84E2193B9BE00F5FE67 /* Caching */,
|
D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */,
|
||||||
D6F953F121251A2F00CF0F2B /* Controllers */,
|
D6E4269C2532A3E100C02E1C /* FuzzyMatcher.swift */,
|
||||||
D6370B9924421FE00092A7FF /* CoreData */,
|
D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */,
|
||||||
D667E5F62135C2ED0057A976 /* Extensions */,
|
|
||||||
D6D4DDD8212518A200E1C4BB /* LaunchScreen.storyboard */,
|
|
||||||
D6E57FA525C26FAB00341037 /* Localizable.stringsdict */,
|
D6E57FA525C26FAB00341037 /* Localizable.stringsdict */,
|
||||||
D61959D2241E846D00A37B8E /* Models */,
|
|
||||||
D663626021360A9600C9CBA2 /* Preferences */,
|
|
||||||
D641C780213DD7C4004B4513 /* Screens */,
|
|
||||||
D62D241E217AA46B005076CC /* Shortcuts */,
|
|
||||||
D67B506B250B28FF00FAECFB /* Vendor */,
|
D67B506B250B28FF00FAECFB /* Vendor */,
|
||||||
D6BED1722126661300F02DA0 /* Views */,
|
D6F1F84E2193B9BE00F5FE67 /* Caching */,
|
||||||
D6757A7A2157E00100721E32 /* XCallbackURL */,
|
D6757A7A2157E00100721E32 /* XCallbackURL */,
|
||||||
|
D62D241E217AA46B005076CC /* Shortcuts */,
|
||||||
|
D663626021360A9600C9CBA2 /* Preferences */,
|
||||||
|
D6AEBB3F2321640F00E5038B /* Activities */,
|
||||||
|
D667E5F62135C2ED0057A976 /* Extensions */,
|
||||||
|
D61959D2241E846D00A37B8E /* Models */,
|
||||||
|
D6370B9924421FE00092A7FF /* CoreData */,
|
||||||
|
D6F953F121251A2F00CF0F2B /* Controllers */,
|
||||||
|
D641C780213DD7C4004B4513 /* Screens */,
|
||||||
|
D6BED1722126661300F02DA0 /* Views */,
|
||||||
|
D6D4DDD6212518A200E1C4BB /* Assets.xcassets */,
|
||||||
|
D6D4DDD8212518A200E1C4BB /* LaunchScreen.storyboard */,
|
||||||
|
D6D4DDDB212518A200E1C4BB /* Info.plist */,
|
||||||
|
D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */,
|
||||||
);
|
);
|
||||||
path = Tusker;
|
path = Tusker;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -2084,7 +2067,6 @@
|
||||||
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
|
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
|
||||||
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
|
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
|
||||||
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */,
|
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */,
|
||||||
D64AAE9126C80DC600FC57FB /* ToastView.swift in Sources */,
|
|
||||||
D62275AA24F1E01C00B82A16 /* ComposeTextView.swift in Sources */,
|
D62275AA24F1E01C00B82A16 /* ComposeTextView.swift in Sources */,
|
||||||
0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */,
|
0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */,
|
||||||
D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */,
|
D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */,
|
||||||
|
@ -2166,7 +2148,6 @@
|
||||||
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */,
|
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */,
|
||||||
D6EAE0DB2550CC8A002DB0AC /* FocusableTextField.swift in Sources */,
|
D6EAE0DB2550CC8A002DB0AC /* FocusableTextField.swift in Sources */,
|
||||||
D623A53D2635F5590095BD04 /* StatusPollView.swift in Sources */,
|
D623A53D2635F5590095BD04 /* StatusPollView.swift in Sources */,
|
||||||
D64AAE9726C88DC400FC57FB /* ToastConfiguration.swift in Sources */,
|
|
||||||
D6093FB025BE0B01004811E6 /* TrendingHashtagTableViewCell.swift in Sources */,
|
D6093FB025BE0B01004811E6 /* TrendingHashtagTableViewCell.swift in Sources */,
|
||||||
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */,
|
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */,
|
||||||
D6B053A223BD2C0600A066FA /* AssetPickerViewController.swift in Sources */,
|
D6B053A223BD2C0600A066FA /* AssetPickerViewController.swift in Sources */,
|
||||||
|
@ -2181,7 +2162,6 @@
|
||||||
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,
|
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,
|
||||||
D6F0B17524A3A1AA001E48C3 /* MainSidebarViewController.swift in Sources */,
|
D6F0B17524A3A1AA001E48C3 /* MainSidebarViewController.swift in Sources */,
|
||||||
D622757424EDF1CD00B82A16 /* ComposeAttachmentsList.swift in Sources */,
|
D622757424EDF1CD00B82A16 /* ComposeAttachmentsList.swift in Sources */,
|
||||||
D64AAE9526C88C5000FC57FB /* ToastableViewController.swift in Sources */,
|
|
||||||
D6420AEE26BED18B00ED8175 /* PublicTimelineDescriptionTableViewCell.swift in Sources */,
|
D6420AEE26BED18B00ED8175 /* PublicTimelineDescriptionTableViewCell.swift in Sources */,
|
||||||
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */,
|
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */,
|
||||||
D6BED174212667E900F02DA0 /* TimelineStatusTableViewCell.swift in Sources */,
|
D6BED174212667E900F02DA0 /* TimelineStatusTableViewCell.swift in Sources */,
|
||||||
|
|
|
@ -89,11 +89,3 @@ extension AccountSwitchingContainerViewController: TuskerRootViewController {
|
||||||
root.performSearch(query: query)
|
root.performSearch(query: query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AccountSwitchingContainerViewController: BackgroundableViewController {
|
|
||||||
func sceneDidEnterBackground() {
|
|
||||||
if let backgroundable = root as? BackgroundableViewController {
|
|
||||||
backgroundable.sceneDidEnterBackground()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -29,8 +29,6 @@ class InstanceSelectorTableViewController: UITableViewController {
|
||||||
var urlHandler: AnyCancellable?
|
var urlHandler: AnyCancellable?
|
||||||
var currentQuery: String?
|
var currentQuery: String?
|
||||||
|
|
||||||
private var activityIndicator: UIActivityIndicatorView!
|
|
||||||
|
|
||||||
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||||
return .portrait
|
return .portrait
|
||||||
|
@ -58,9 +56,9 @@ class InstanceSelectorTableViewController: UITableViewController {
|
||||||
navigationItem.scrollEdgeAppearance = appearance
|
navigationItem.scrollEdgeAppearance = appearance
|
||||||
|
|
||||||
tableView.register(UINib(nibName: "InstanceTableViewCell", bundle: .main), forCellReuseIdentifier: instanceCell)
|
tableView.register(UINib(nibName: "InstanceTableViewCell", bundle: .main), forCellReuseIdentifier: instanceCell)
|
||||||
|
|
||||||
tableView.rowHeight = UITableView.automaticDimension
|
tableView.rowHeight = UITableView.automaticDimension
|
||||||
tableView.estimatedRowHeight = 120
|
tableView.estimatedRowHeight = 120
|
||||||
createActivityIndicatorHeader()
|
|
||||||
|
|
||||||
dataSource = DataSource(tableView: tableView, cellProvider: { (tableView, indexPath, item) -> UITableViewCell? in
|
dataSource = DataSource(tableView: tableView, cellProvider: { (tableView, indexPath, item) -> UITableViewCell? in
|
||||||
switch item {
|
switch item {
|
||||||
|
@ -80,19 +78,12 @@ class InstanceSelectorTableViewController: UITableViewController {
|
||||||
searchController.obscuresBackgroundDuringPresentation = false
|
searchController.obscuresBackgroundDuringPresentation = false
|
||||||
searchController.searchBar.searchTextField.autocapitalizationType = .none
|
searchController.searchBar.searchTextField.autocapitalizationType = .none
|
||||||
navigationItem.searchController = searchController
|
navigationItem.searchController = searchController
|
||||||
navigationItem.hidesSearchBarWhenScrolling = false
|
|
||||||
definesPresentationContext = true
|
definesPresentationContext = true
|
||||||
|
|
||||||
urlHandler = urlCheckerSubject
|
urlHandler = urlCheckerSubject
|
||||||
.compactMap { $0?.trimmingCharacters(in: .whitespacesAndNewlines) }
|
|
||||||
.map { [weak self] (s) -> String in
|
|
||||||
if !s.isEmpty {
|
|
||||||
self?.activityIndicator.startAnimating()
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
.debounce(for: .seconds(1), scheduler: RunLoop.main)
|
.debounce(for: .seconds(1), scheduler: RunLoop.main)
|
||||||
.sink { [weak self] in self?.updateSpecificInstance(domain: $0) }
|
.compactMap { $0?.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||||
|
.sink(receiveValue: updateSpecificInstance)
|
||||||
|
|
||||||
loadRecommendedInstances()
|
loadRecommendedInstances()
|
||||||
}
|
}
|
||||||
|
@ -126,8 +117,6 @@ class InstanceSelectorTableViewController: UITableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateSpecificInstance(domain: String) {
|
private func updateSpecificInstance(domain: String) {
|
||||||
activityIndicator.startAnimating()
|
|
||||||
|
|
||||||
let components = parseURLComponents(input: domain)
|
let components = parseURLComponents(input: domain)
|
||||||
let url = components.url!
|
let url = components.url!
|
||||||
|
|
||||||
|
@ -149,13 +138,7 @@ class InstanceSelectorTableViewController: UITableViewController {
|
||||||
snapshot.appendItems([.selected(url, instance)], toSection: .selected)
|
snapshot.appendItems([.selected(url, instance)], toSection: .selected)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.dataSource.apply(snapshot) {
|
self.dataSource.apply(snapshot)
|
||||||
self.activityIndicator.stopAnimating()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.activityIndicator.stopAnimating()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,28 +158,6 @@ class InstanceSelectorTableViewController: UITableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createActivityIndicatorHeader() {
|
|
||||||
let header = UITableViewHeaderFooterView()
|
|
||||||
header.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
header.contentView.backgroundColor = .secondarySystemBackground
|
|
||||||
|
|
||||||
activityIndicator = UIActivityIndicatorView(style: .large)
|
|
||||||
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
header.contentView.addSubview(activityIndicator)
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
activityIndicator.centerXAnchor.constraint(equalTo: header.contentView.centerXAnchor),
|
|
||||||
activityIndicator.topAnchor.constraint(equalTo: header.contentView.topAnchor, constant: 4),
|
|
||||||
activityIndicator.bottomAnchor.constraint(equalTo: header.contentView.bottomAnchor, constant: -4),
|
|
||||||
])
|
|
||||||
|
|
||||||
let fittingSize = CGSize(width: tableView.bounds.width - (tableView.safeAreaInsets.left + tableView.safeAreaInsets.right), height: 0)
|
|
||||||
let size = header.systemLayoutSizeFitting(fittingSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
|
|
||||||
header.frame = CGRect(origin: .zero, size: size)
|
|
||||||
|
|
||||||
tableView.tableHeaderView = header
|
|
||||||
}
|
|
||||||
|
|
||||||
private func showRecommendationsError(_ error: Client.Error) {
|
private func showRecommendationsError(_ error: Client.Error) {
|
||||||
let footer = UITableViewHeaderFooterView()
|
let footer = UITableViewHeaderFooterView()
|
||||||
footer.translatesAutoresizingMaskIntoConstraints = false
|
footer.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
|
@ -70,8 +70,8 @@ class ProfileViewController: UIPageViewController {
|
||||||
|
|
||||||
let composeButton = UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(composeMentioning))
|
let composeButton = UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(composeMentioning))
|
||||||
composeButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: [
|
composeButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: [
|
||||||
UIAction(title: "Direct Message", image: UIImage(systemName: Status.Visibility.direct.unfilledImageName), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off, handler: { [weak self] (_) in
|
UIAction(title: "Direct Message", image: UIImage(systemName: Status.Visibility.direct.unfilledImageName), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off, handler: { (_) in
|
||||||
self?.composeDirectMentioning()
|
self.composeDirectMentioning()
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
composeButton.isEnabled = mastodonController.loggedIn
|
composeButton.isEnabled = mastodonController.loggedIn
|
||||||
|
|
|
@ -59,7 +59,6 @@ class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable,
|
||||||
super.viewDidDisappear(animated)
|
super.viewDidDisappear(animated)
|
||||||
|
|
||||||
pruneOffscreenRows()
|
pruneOffscreenRows()
|
||||||
currentToast?.dismissToast(animated: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class func refreshCommandTitle() -> String {
|
class func refreshCommandTitle() -> String {
|
||||||
|
@ -112,26 +111,13 @@ class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable,
|
||||||
state = .loadingInitial
|
state = .loadingInitial
|
||||||
|
|
||||||
loadInitialItems() { result in
|
loadInitialItems() { result in
|
||||||
|
guard case let .success(snapshot) = result else {
|
||||||
|
self.state = .unloaded
|
||||||
|
return
|
||||||
|
}
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
switch result {
|
self.dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
case let .success(snapshot):
|
self.state = .loaded
|
||||||
self.dataSource.apply(snapshot, animatingDifferences: false)
|
|
||||||
self.state = .loaded
|
|
||||||
case let .failure(.client(error)):
|
|
||||||
self.state = .unloaded
|
|
||||||
var config = ToastConfiguration(title: "Error Loading")
|
|
||||||
config.subtitle = error.localizedDescription
|
|
||||||
config.systemImageName = error.systemImageName
|
|
||||||
config.actionTitle = "Retry"
|
|
||||||
config.action = { [weak self] (toast) in
|
|
||||||
toast.dismissToast(animated: true)
|
|
||||||
self?.loadInitial()
|
|
||||||
}
|
|
||||||
self.showToast(configuration: config, animated: true)
|
|
||||||
|
|
||||||
default:
|
|
||||||
self.state = .unloaded
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,27 +133,13 @@ class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable,
|
||||||
state = .loadingOlder
|
state = .loadingOlder
|
||||||
|
|
||||||
loadOlderItems(currentSnapshot: dataSource.snapshot()) { result in
|
loadOlderItems(currentSnapshot: dataSource.snapshot()) { result in
|
||||||
DispatchQueue.main.async {
|
guard case let .success(snapshot) = result else {
|
||||||
|
self.state = .loaded
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
self.state = .loaded
|
self.state = .loaded
|
||||||
|
|
||||||
switch result {
|
|
||||||
case let .success(snapshot):
|
|
||||||
self.dataSource.apply(snapshot, animatingDifferences: false)
|
|
||||||
|
|
||||||
case let .failure(.client(error)):
|
|
||||||
var config = ToastConfiguration(title: "Error Loading Older")
|
|
||||||
config.subtitle = error.localizedDescription
|
|
||||||
config.systemImageName = error.systemImageName
|
|
||||||
config.actionTitle = "Retry"
|
|
||||||
config.action = { [weak self] (toast) in
|
|
||||||
toast.dismissToast(animated: true)
|
|
||||||
self?.loadOlder()
|
|
||||||
}
|
|
||||||
self.showToast(configuration: config, animated: true)
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,32 +196,23 @@ class DiffableTimelineLikeTableViewController<Section: Hashable & CaseIterable,
|
||||||
}
|
}
|
||||||
|
|
||||||
loadNewerItems(currentSnapshot: snapshot) { result in
|
loadNewerItems(currentSnapshot: snapshot) { result in
|
||||||
|
guard case let .success(snapshot) = result else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.refreshControl?.endRefreshing()
|
||||||
|
self.state = .loaded
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.refreshControl?.endRefreshing()
|
self.refreshControl?.endRefreshing()
|
||||||
|
self.dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
self.state = .loaded
|
self.state = .loaded
|
||||||
|
|
||||||
switch result {
|
if let item = item,
|
||||||
case let .success(snapshot):
|
let indexPath = self.dataSource.indexPath(for: item) {
|
||||||
self.dataSource.apply(snapshot, animatingDifferences: false)
|
// maintain the current position in the list (don't scroll to top)
|
||||||
if let item = item,
|
self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
|
||||||
let indexPath = self.dataSource.indexPath(for: item) {
|
|
||||||
// maintain the current position in the list (don't scroll to top)
|
|
||||||
self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
case let .failure(.client(error)):
|
|
||||||
var config = ToastConfiguration(title: "Error Loading Newer")
|
|
||||||
config.subtitle = error.localizedDescription
|
|
||||||
config.systemImageName = error.systemImageName
|
|
||||||
config.actionTitle = "Retry"
|
|
||||||
config.action = { [weak self] (toast) in
|
|
||||||
toast.dismissToast(animated: true)
|
|
||||||
self?.refresh()
|
|
||||||
}
|
|
||||||
self.showToast(configuration: config, animated: true)
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,20 +267,5 @@ extension DiffableTimelineLikeTableViewController {
|
||||||
extension DiffableTimelineLikeTableViewController: BackgroundableViewController {
|
extension DiffableTimelineLikeTableViewController: BackgroundableViewController {
|
||||||
func sceneDidEnterBackground() {
|
func sceneDidEnterBackground() {
|
||||||
pruneOffscreenRows()
|
pruneOffscreenRows()
|
||||||
currentToast?.dismissToast(animated: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension DiffableTimelineLikeTableViewController: ToastableViewController {
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate extension Client.Error {
|
|
||||||
var systemImageName: String {
|
|
||||||
switch self {
|
|
||||||
case .networkError(_):
|
|
||||||
return "wifi.exclamationmark"
|
|
||||||
default:
|
|
||||||
return "exclamationmark.triangle"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
//
|
|
||||||
// ToastConfiguration.swift
|
|
||||||
// ToastConfiguration
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 8/14/21.
|
|
||||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
struct ToastConfiguration {
|
|
||||||
var systemImageName: String?
|
|
||||||
var title: String
|
|
||||||
var subtitle: String?
|
|
||||||
var actionTitle: String?
|
|
||||||
var action: ((ToastView) -> Void)?
|
|
||||||
var edgeSpacing: CGFloat = 8
|
|
||||||
var edge: Edge = .automatic
|
|
||||||
var dismissOnScroll = true
|
|
||||||
|
|
||||||
init(title: String) {
|
|
||||||
self.title = title
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Edge: Equatable {
|
|
||||||
case top
|
|
||||||
case bottom
|
|
||||||
/// Determines edge based on the current device. Bottom on iPhone, top on iPad/Mac.
|
|
||||||
case automatic
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
//
|
|
||||||
// ToastView.swift
|
|
||||||
// ToastView
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 8/14/21.
|
|
||||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class ToastView: UIView {
|
|
||||||
|
|
||||||
let configuration: ToastConfiguration
|
|
||||||
|
|
||||||
private var shrinkAnimator: UIViewPropertyAnimator?
|
|
||||||
private var recognizedGesture = false
|
|
||||||
|
|
||||||
private var offscreenTranslation: CGFloat {
|
|
||||||
var translation = bounds.height + configuration.edgeSpacing
|
|
||||||
if configuration.edge == .bottom {
|
|
||||||
translation += superview?.safeAreaInsets.bottom ?? 0
|
|
||||||
} else {
|
|
||||||
translation += superview?.safeAreaInsets.top ?? 0
|
|
||||||
translation *= -1
|
|
||||||
}
|
|
||||||
return translation
|
|
||||||
}
|
|
||||||
|
|
||||||
init(configuration: ToastConfiguration) {
|
|
||||||
precondition(configuration.edge != .automatic)
|
|
||||||
self.configuration = configuration
|
|
||||||
|
|
||||||
super.init(frame: .zero)
|
|
||||||
|
|
||||||
setupView()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setupView() {
|
|
||||||
backgroundColor = .systemBlue
|
|
||||||
layer.shadowColor = UIColor.black.cgColor
|
|
||||||
layer.shadowRadius = 5
|
|
||||||
layer.shadowOffset = CGSize(width: 0, height: 2.5)
|
|
||||||
layer.shadowOpacity = 0.5
|
|
||||||
layer.masksToBounds = false
|
|
||||||
|
|
||||||
let stack = UIStackView()
|
|
||||||
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
stack.axis = .horizontal
|
|
||||||
stack.spacing = 8
|
|
||||||
|
|
||||||
if let name = configuration.systemImageName {
|
|
||||||
let imageView = UIImageView(image: UIImage(systemName: name))
|
|
||||||
imageView.tintColor = .white
|
|
||||||
imageView.contentMode = .scaleAspectFit
|
|
||||||
stack.addArrangedSubview(imageView)
|
|
||||||
}
|
|
||||||
|
|
||||||
let titleLabel = UILabel()
|
|
||||||
titleLabel.text = configuration.title
|
|
||||||
titleLabel.textColor = .white
|
|
||||||
titleLabel.font = .boldSystemFont(ofSize: 14)
|
|
||||||
titleLabel.adjustsFontSizeToFitWidth = true
|
|
||||||
|
|
||||||
if let subtitle = configuration.subtitle {
|
|
||||||
let subtitleLabel = UILabel()
|
|
||||||
subtitleLabel.text = subtitle
|
|
||||||
subtitleLabel.textColor = .white
|
|
||||||
subtitleLabel.numberOfLines = 0
|
|
||||||
subtitleLabel.font = .systemFont(ofSize: 14)
|
|
||||||
let vStack = UIStackView(arrangedSubviews: [
|
|
||||||
titleLabel,
|
|
||||||
subtitleLabel
|
|
||||||
])
|
|
||||||
vStack.axis = .vertical
|
|
||||||
vStack.spacing = 4
|
|
||||||
stack.addArrangedSubview(vStack)
|
|
||||||
} else {
|
|
||||||
stack.addArrangedSubview(titleLabel)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let actionTitle = configuration.actionTitle {
|
|
||||||
let actionLabel = UILabel()
|
|
||||||
actionLabel.text = actionTitle
|
|
||||||
actionLabel.font = .boldSystemFont(ofSize: 16)
|
|
||||||
actionLabel.textColor = .white
|
|
||||||
stack.addArrangedSubview(actionLabel)
|
|
||||||
}
|
|
||||||
|
|
||||||
addSubview(stack)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
stack.leadingAnchor.constraint(equalToSystemSpacingAfter: leadingAnchor, multiplier: 1),
|
|
||||||
trailingAnchor.constraint(equalToSystemSpacingAfter: stack.trailingAnchor, multiplier: 1),
|
|
||||||
stack.topAnchor.constraint(equalTo: topAnchor, constant: 4),
|
|
||||||
stack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4),
|
|
||||||
])
|
|
||||||
|
|
||||||
let pan = UIPanGestureRecognizer(target: self, action: #selector(panRecognized))
|
|
||||||
addGestureRecognizer(pan)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layoutSubviews() {
|
|
||||||
super.layoutSubviews()
|
|
||||||
|
|
||||||
layer.cornerRadius = min(32, bounds.height / 2)
|
|
||||||
layer.shadowPath = CGPath(roundedRect: bounds, cornerWidth: layer.cornerRadius, cornerHeight: layer.cornerRadius, transform: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
|
||||||
super.touchesBegan(touches, with: event)
|
|
||||||
|
|
||||||
recognizedGesture = false
|
|
||||||
shrinkAnimator = UIViewPropertyAnimator(duration: 0.1, curve: .easeInOut) {
|
|
||||||
self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)
|
|
||||||
}
|
|
||||||
shrinkAnimator?.startAnimation(afterDelay: 0.1)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
|
||||||
super.touchesEnded(touches, with: event)
|
|
||||||
|
|
||||||
if !recognizedGesture {
|
|
||||||
guard let shrinkAnimator = shrinkAnimator else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
shrinkAnimator.stopAnimation(true)
|
|
||||||
UIView.animate(withDuration: 0.1, delay: 0, options: .curveEaseInOut) {
|
|
||||||
self.transform = .identity
|
|
||||||
}
|
|
||||||
configuration.action?(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
shrinkAnimator = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func panRecognized(_ recognizer: UIPanGestureRecognizer) {
|
|
||||||
let translation = recognizer.translation(in: self).y
|
|
||||||
|
|
||||||
let isDraggingAwayFromDismissalEdge = (configuration.edge == .top && translation > 0) || (configuration.edge == .bottom && translation < 0)
|
|
||||||
|
|
||||||
switch recognizer.state {
|
|
||||||
case .began:
|
|
||||||
recognizedGesture = true
|
|
||||||
UIView.animate(withDuration: 0.1, delay: 0, options: [.curveEaseInOut, .allowUserInteraction]) {
|
|
||||||
self.transform = .identity
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case .changed:
|
|
||||||
var distance: CGFloat
|
|
||||||
if isDraggingAwayFromDismissalEdge {
|
|
||||||
distance = sqrt(abs(translation))
|
|
||||||
if configuration.edge == .bottom {
|
|
||||||
distance *= -1
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
distance = translation
|
|
||||||
}
|
|
||||||
transform = CGAffineTransform(translationX: 0, y: distance)
|
|
||||||
|
|
||||||
case .ended, .cancelled:
|
|
||||||
let velocity = recognizer.velocity(in: self).y
|
|
||||||
let distance = isDraggingAwayFromDismissalEdge ? sqrt(abs(translation)) : translation
|
|
||||||
|
|
||||||
let minDismissalDistance = configuration.edgeSpacing + bounds.height / 2
|
|
||||||
let dismissDueToDistance = configuration.edge == .bottom ? distance > minDismissalDistance : -distance > minDismissalDistance
|
|
||||||
|
|
||||||
let minDismissalVelocity: CGFloat = 250
|
|
||||||
let dismissDueToVelocity = configuration.edge == .bottom ? velocity > minDismissalDistance : velocity < -minDismissalVelocity
|
|
||||||
if dismissDueToDistance || dismissDueToVelocity {
|
|
||||||
|
|
||||||
if abs(translation) < abs(offscreenTranslation) {
|
|
||||||
let distanceLeft = offscreenTranslation - translation
|
|
||||||
let duration = 1 / TimeInterval(max(velocity, minDismissalVelocity) / distanceLeft)
|
|
||||||
|
|
||||||
UIView.animate(withDuration: duration, delay: 0, options: .allowUserInteraction) {
|
|
||||||
self.transform = CGAffineTransform(translationX: 0, y: self.offscreenTranslation)
|
|
||||||
} completion: { (_) in
|
|
||||||
self.removeFromSuperview()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.removeFromSuperview()
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
let duration = 0.5
|
|
||||||
let velocity = distance * duration
|
|
||||||
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.65, initialSpringVelocity: velocity, options: .allowUserInteraction) {
|
|
||||||
self.transform = .identity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func dismissToast(animated: Bool) {
|
|
||||||
guard animated else {
|
|
||||||
removeFromSuperview()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseInOut) {
|
|
||||||
self.transform = CGAffineTransform(translationX: 0, y: self.offscreenTranslation)
|
|
||||||
} completion: { (_) in
|
|
||||||
self.removeFromSuperview()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func animateAppearance() {
|
|
||||||
self.transform = CGAffineTransform(translationX: 0, y: offscreenTranslation)
|
|
||||||
let duration = 0.5
|
|
||||||
let velocity = 0.5
|
|
||||||
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.65, initialSpringVelocity: velocity, options: []) {
|
|
||||||
self.transform = .identity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
//
|
|
||||||
// ToastableViewController.swift
|
|
||||||
// ToastableViewController
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 8/14/21.
|
|
||||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
protocol ToastableViewController: UIViewController {
|
|
||||||
|
|
||||||
var toastParentView: UIView { get }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private var currentToastKey = "Tusker_currentToast"
|
|
||||||
|
|
||||||
extension ToastableViewController {
|
|
||||||
|
|
||||||
private(set) var currentToast: ToastView? {
|
|
||||||
get {
|
|
||||||
let holder = objc_getAssociatedObject(self, ¤tToastKey) as? WeakHolder<ToastView>
|
|
||||||
return holder?.object
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
if let newValue = newValue {
|
|
||||||
objc_setAssociatedObject(self, ¤tToastKey, WeakHolder(object: newValue), .OBJC_ASSOCIATION_RETAIN)
|
|
||||||
} else {
|
|
||||||
objc_setAssociatedObject(self, ¤tToastKey, nil, .OBJC_ASSOCIATION_RETAIN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var toastParentView: UIView { view }
|
|
||||||
|
|
||||||
func showToast(configuration config: ToastConfiguration, animated: Bool) {
|
|
||||||
currentToast?.dismissToast(animated: false)
|
|
||||||
|
|
||||||
var config = config
|
|
||||||
config.edge = effectiveEdge(edge: config.edge)
|
|
||||||
|
|
||||||
let toast = ToastView(configuration: config)
|
|
||||||
currentToast = toast
|
|
||||||
|
|
||||||
let parentSafeArea = toastParentView.safeAreaLayoutGuide
|
|
||||||
toast.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
toastParentView.addSubview(toast)
|
|
||||||
|
|
||||||
let yConstraint: NSLayoutConstraint
|
|
||||||
switch config.edge {
|
|
||||||
case .top:
|
|
||||||
yConstraint = toast.topAnchor.constraint(equalTo: parentSafeArea.topAnchor, constant: config.edgeSpacing)
|
|
||||||
case .bottom:
|
|
||||||
yConstraint = parentSafeArea.bottomAnchor.constraint(equalTo: toast.bottomAnchor, constant: config.edgeSpacing)
|
|
||||||
case .automatic:
|
|
||||||
fatalError("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
toast.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: parentSafeArea.leadingAnchor, multiplier: 1),
|
|
||||||
parentSafeArea.trailingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: toast.trailingAnchor, multiplier: 1),
|
|
||||||
parentSafeArea.centerXAnchor.constraint(equalTo: toast.centerXAnchor),
|
|
||||||
yConstraint,
|
|
||||||
])
|
|
||||||
|
|
||||||
if animated {
|
|
||||||
toast.animateAppearance()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func effectiveEdge(edge: ToastConfiguration.Edge) -> ToastConfiguration.Edge {
|
|
||||||
guard case .automatic = edge else {
|
|
||||||
return edge
|
|
||||||
}
|
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
|
||||||
return .bottom
|
|
||||||
} else {
|
|
||||||
return .top
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate class WeakHolder<T: AnyObject> {
|
|
||||||
weak var object: T?
|
|
||||||
|
|
||||||
init(object: T) {
|
|
||||||
self.object = object
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue