forked from shadowfacts/Tusker
Add multi-window support and auxiliary windows
This commit is contained in:
parent
67a029180e
commit
522c9b2b03
|
@ -186,6 +186,7 @@
|
||||||
D681E4D9246E346E0053414F /* AccountActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */; };
|
D681E4D9246E346E0053414F /* AccountActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */; };
|
||||||
D68232F72464F4FD00325FB8 /* ComposeDrawingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */; };
|
D68232F72464F4FD00325FB8 /* ComposeDrawingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */; };
|
||||||
D686BBE324FBF8110068E6AA /* WrappedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */; };
|
D686BBE324FBF8110068E6AA /* WrappedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */; };
|
||||||
|
D68C2AE325869BAB00548EFF /* AuxiliarySceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */; };
|
||||||
D68E525B24A3D77E0054355A /* TuskerRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E525A24A3D77E0054355A /* TuskerRootViewController.swift */; };
|
D68E525B24A3D77E0054355A /* TuskerRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E525A24A3D77E0054355A /* TuskerRootViewController.swift */; };
|
||||||
D68E525D24A3E8F00054355A /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E525C24A3E8F00054355A /* SearchViewController.swift */; };
|
D68E525D24A3E8F00054355A /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E525C24A3E8F00054355A /* SearchViewController.swift */; };
|
||||||
D68E6F59253C9969001A1B4C /* MultiSourceEmojiLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E6F58253C9969001A1B4C /* MultiSourceEmojiLabel.swift */; };
|
D68E6F59253C9969001A1B4C /* MultiSourceEmojiLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E6F58253C9969001A1B4C /* MultiSourceEmojiLabel.swift */; };
|
||||||
|
@ -199,6 +200,8 @@
|
||||||
D6945C3423AC6431005C403C /* AddSavedHashtagViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */; };
|
D6945C3423AC6431005C403C /* AddSavedHashtagViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */; };
|
||||||
D6945C3823AC739F005C403C /* InstanceTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */; };
|
D6945C3823AC739F005C403C /* InstanceTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */; };
|
||||||
D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */; };
|
D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */; };
|
||||||
|
D69693F42585941A00F4E116 /* UIWindowSceneDelegate+Close.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69693F32585941A00F4E116 /* UIWindowSceneDelegate+Close.swift */; };
|
||||||
|
D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */; };
|
||||||
D6969E9E240C81B9002843CE /* NSTextAttachment+Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6969E9D240C81B9002843CE /* NSTextAttachment+Emoji.swift */; };
|
D6969E9E240C81B9002843CE /* NSTextAttachment+Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6969E9D240C81B9002843CE /* NSTextAttachment+Emoji.swift */; };
|
||||||
D6969EA0240C8384002843CE /* EmojiLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6969E9F240C8384002843CE /* EmojiLabel.swift */; };
|
D6969EA0240C8384002843CE /* EmojiLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6969E9F240C8384002843CE /* EmojiLabel.swift */; };
|
||||||
D6969EA4240DD28D002843CE /* UnknownNotificationTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6969EA2240DD28D002843CE /* UnknownNotificationTableViewCell.xib */; };
|
D6969EA4240DD28D002843CE /* UnknownNotificationTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6969EA2240DD28D002843CE /* UnknownNotificationTableViewCell.xib */; };
|
||||||
|
@ -221,7 +224,7 @@
|
||||||
D6A5BB2D23BBA9C4003BF21D /* MyProfileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */; };
|
D6A5BB2D23BBA9C4003BF21D /* MyProfileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */; };
|
||||||
D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */; };
|
D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */; };
|
||||||
D6A5BB3123BBAD87003BF21D /* JSONResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */; };
|
D6A5BB3123BBAD87003BF21D /* JSONResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */; };
|
||||||
D6AC956723C4347E008C9946 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AC956623C4347E008C9946 /* SceneDelegate.swift */; };
|
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */; };
|
||||||
D6ACE1AC240C3BAD004EA8E2 /* Ambassador.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F613023AE99E000F3CFD3 /* Ambassador.framework */; };
|
D6ACE1AC240C3BAD004EA8E2 /* Ambassador.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F613023AE99E000F3CFD3 /* Ambassador.framework */; };
|
||||||
D6ACE1AD240C3BAD004EA8E2 /* Embassy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F612D23AE990C00F3CFD3 /* Embassy.framework */; };
|
D6ACE1AD240C3BAD004EA8E2 /* Embassy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F612D23AE990C00F3CFD3 /* Embassy.framework */; };
|
||||||
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; };
|
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; };
|
||||||
|
@ -541,6 +544,7 @@
|
||||||
D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountActivityItemSource.swift; sourceTree = "<group>"; };
|
D681E4D8246E346E0053414F /* AccountActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountActivityItemSource.swift; sourceTree = "<group>"; };
|
||||||
D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeDrawingViewController.swift; sourceTree = "<group>"; };
|
D68232F62464F4FD00325FB8 /* ComposeDrawingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeDrawingViewController.swift; sourceTree = "<group>"; };
|
||||||
D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedProgressView.swift; sourceTree = "<group>"; };
|
D686BBE224FBF8110068E6AA /* WrappedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedProgressView.swift; sourceTree = "<group>"; };
|
||||||
|
D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuxiliarySceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
D68E525A24A3D77E0054355A /* TuskerRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerRootViewController.swift; sourceTree = "<group>"; };
|
D68E525A24A3D77E0054355A /* TuskerRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerRootViewController.swift; sourceTree = "<group>"; };
|
||||||
D68E525C24A3E8F00054355A /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = "<group>"; };
|
D68E525C24A3E8F00054355A /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = "<group>"; };
|
||||||
D68E6F58253C9969001A1B4C /* MultiSourceEmojiLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiSourceEmojiLabel.swift; sourceTree = "<group>"; };
|
D68E6F58253C9969001A1B4C /* MultiSourceEmojiLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiSourceEmojiLabel.swift; sourceTree = "<group>"; };
|
||||||
|
@ -554,6 +558,8 @@
|
||||||
D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSavedHashtagViewController.swift; sourceTree = "<group>"; };
|
D6945C3323AC6431005C403C /* AddSavedHashtagViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSavedHashtagViewController.swift; sourceTree = "<group>"; };
|
||||||
D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceTimelineViewController.swift; sourceTree = "<group>"; };
|
D6945C3723AC739F005C403C /* InstanceTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceTimelineViewController.swift; sourceTree = "<group>"; };
|
||||||
D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FindInstanceViewController.swift; path = Tusker/Screens/FindInstanceViewController.swift; sourceTree = SOURCE_ROOT; };
|
D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FindInstanceViewController.swift; path = Tusker/Screens/FindInstanceViewController.swift; sourceTree = SOURCE_ROOT; };
|
||||||
|
D69693F32585941A00F4E116 /* UIWindowSceneDelegate+Close.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIWindowSceneDelegate+Close.swift"; sourceTree = "<group>"; };
|
||||||
|
D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeSceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
D6969E9D240C81B9002843CE /* NSTextAttachment+Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextAttachment+Emoji.swift"; sourceTree = "<group>"; };
|
D6969E9D240C81B9002843CE /* NSTextAttachment+Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextAttachment+Emoji.swift"; sourceTree = "<group>"; };
|
||||||
D6969E9F240C8384002843CE /* EmojiLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLabel.swift; sourceTree = "<group>"; };
|
D6969E9F240C8384002843CE /* EmojiLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLabel.swift; sourceTree = "<group>"; };
|
||||||
D6969EA2240DD28D002843CE /* UnknownNotificationTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UnknownNotificationTableViewCell.xib; sourceTree = "<group>"; };
|
D6969EA2240DD28D002843CE /* UnknownNotificationTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UnknownNotificationTableViewCell.xib; sourceTree = "<group>"; };
|
||||||
|
@ -575,7 +581,7 @@
|
||||||
D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyProfileTests.swift; sourceTree = "<group>"; };
|
D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyProfileTests.swift; sourceTree = "<group>"; };
|
||||||
D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegatingResponse.swift; sourceTree = "<group>"; };
|
D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegatingResponse.swift; sourceTree = "<group>"; };
|
||||||
D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONResponse.swift; sourceTree = "<group>"; };
|
D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONResponse.swift; sourceTree = "<group>"; };
|
||||||
D6AC956623C4347E008C9946 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; };
|
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; };
|
||||||
D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMesasgeActivity.swift; sourceTree = "<group>"; };
|
D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMesasgeActivity.swift; sourceTree = "<group>"; };
|
||||||
D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInSafariActivity.swift; sourceTree = "<group>"; };
|
D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInSafariActivity.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1177,6 +1183,7 @@
|
||||||
D6D3F4C324FDB6B700EC4A6A /* View+ConditionalModifier.swift */,
|
D6D3F4C324FDB6B700EC4A6A /* View+ConditionalModifier.swift */,
|
||||||
D6D4CC90250D2C3100FCCF8D /* UIAccessibility.swift */,
|
D6D4CC90250D2C3100FCCF8D /* UIAccessibility.swift */,
|
||||||
D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */,
|
D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */,
|
||||||
|
D69693F32585941A00F4E116 /* UIWindowSceneDelegate+Close.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1421,7 +1428,9 @@
|
||||||
children = (
|
children = (
|
||||||
D6E4885C24A2890C0011C13E /* Tusker.entitlements */,
|
D6E4885C24A2890C0011C13E /* Tusker.entitlements */,
|
||||||
D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */,
|
D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */,
|
||||||
D6AC956623C4347E008C9946 /* SceneDelegate.swift */,
|
D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */,
|
||||||
|
D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */,
|
||||||
|
D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */,
|
||||||
D64D0AAC2128D88B005A6F37 /* LocalData.swift */,
|
D64D0AAC2128D88B005A6F37 /* LocalData.swift */,
|
||||||
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
||||||
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */,
|
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */,
|
||||||
|
@ -1854,6 +1863,7 @@
|
||||||
D6BC9DB3232D4C07002CA326 /* WellnessPrefsView.swift in Sources */,
|
D6BC9DB3232D4C07002CA326 /* WellnessPrefsView.swift in Sources */,
|
||||||
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */,
|
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */,
|
||||||
D693DE5923FE24310061E07D /* InteractivePushTransition.swift in Sources */,
|
D693DE5923FE24310061E07D /* InteractivePushTransition.swift in Sources */,
|
||||||
|
D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */,
|
||||||
D65234C9256189D0001AF9CF /* TimelineLikeTableViewController.swift in Sources */,
|
D65234C9256189D0001AF9CF /* TimelineLikeTableViewController.swift in Sources */,
|
||||||
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
|
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
|
||||||
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
|
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
|
||||||
|
@ -1875,7 +1885,7 @@
|
||||||
D6945C3423AC6431005C403C /* AddSavedHashtagViewController.swift in Sources */,
|
D6945C3423AC6431005C403C /* AddSavedHashtagViewController.swift in Sources */,
|
||||||
D627943723A552C200D38C68 /* BookmarkStatusActivity.swift in Sources */,
|
D627943723A552C200D38C68 /* BookmarkStatusActivity.swift in Sources */,
|
||||||
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */,
|
D62D2426217ABF63005076CC /* UserActivityType.swift in Sources */,
|
||||||
D6AC956723C4347E008C9946 /* SceneDelegate.swift in Sources */,
|
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */,
|
||||||
D6BC9DD7232D7811002CA326 /* TimelinesPageViewController.swift in Sources */,
|
D6BC9DD7232D7811002CA326 /* TimelinesPageViewController.swift in Sources */,
|
||||||
D60E2F2E244248BF005F8713 /* MastodonCachePersistentStore.swift in Sources */,
|
D60E2F2E244248BF005F8713 /* MastodonCachePersistentStore.swift in Sources */,
|
||||||
D620483623D38075008A63EF /* ContentTextView.swift in Sources */,
|
D620483623D38075008A63EF /* ContentTextView.swift in Sources */,
|
||||||
|
@ -1946,6 +1956,7 @@
|
||||||
D622757424EDF1CD00B82A16 /* ComposeAttachmentsList.swift in Sources */,
|
D622757424EDF1CD00B82A16 /* ComposeAttachmentsList.swift in Sources */,
|
||||||
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */,
|
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */,
|
||||||
D6BED174212667E900F02DA0 /* TimelineStatusTableViewCell.swift in Sources */,
|
D6BED174212667E900F02DA0 /* TimelineStatusTableViewCell.swift in Sources */,
|
||||||
|
D69693F42585941A00F4E116 /* UIWindowSceneDelegate+Close.swift in Sources */,
|
||||||
D6C143DA253510F4007DC240 /* ComposeContentWarningTextField.swift in Sources */,
|
D6C143DA253510F4007DC240 /* ComposeContentWarningTextField.swift in Sources */,
|
||||||
0427033822B30F5F000D31B6 /* BehaviorPrefsView.swift in Sources */,
|
0427033822B30F5F000D31B6 /* BehaviorPrefsView.swift in Sources */,
|
||||||
D627943923A553B600D38C68 /* UnbookmarkStatusActivity.swift in Sources */,
|
D627943923A553B600D38C68 /* UnbookmarkStatusActivity.swift in Sources */,
|
||||||
|
@ -1983,6 +1994,7 @@
|
||||||
D6D3F4C424FDB6B700EC4A6A /* View+ConditionalModifier.swift in Sources */,
|
D6D3F4C424FDB6B700EC4A6A /* View+ConditionalModifier.swift in Sources */,
|
||||||
D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */,
|
D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */,
|
||||||
D67C57AD21E265FC00C3118B /* LargeAccountDetailView.swift in Sources */,
|
D67C57AD21E265FC00C3118B /* LargeAccountDetailView.swift in Sources */,
|
||||||
|
D68C2AE325869BAB00548EFF /* AuxiliarySceneDelegate.swift in Sources */,
|
||||||
D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */,
|
D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */,
|
||||||
D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */,
|
D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */,
|
||||||
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */,
|
D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */,
|
||||||
|
|
|
@ -52,4 +52,33 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
MenuController.buildMainMenu(builder: builder)
|
MenuController.buildMainMenu(builder: builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||||
|
let name = getSceneNameForActivity(options.userActivities.first)
|
||||||
|
return UISceneConfiguration(name: name, sessionRole: connectingSceneSession.role)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getSceneNameForActivity(_ activity: NSUserActivity?) -> String {
|
||||||
|
guard let activity = activity,
|
||||||
|
let type = UserActivityType(rawValue: activity.activityType) else {
|
||||||
|
return "main-scene"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch type {
|
||||||
|
case .showConversation,
|
||||||
|
.showTimeline,
|
||||||
|
.checkNotifications,
|
||||||
|
.search,
|
||||||
|
.bookmarks,
|
||||||
|
.myProfile,
|
||||||
|
.showProfile:
|
||||||
|
return "auxiliary"
|
||||||
|
|
||||||
|
case .newPost:
|
||||||
|
return "compose"
|
||||||
|
|
||||||
|
default:
|
||||||
|
fatalError("no scene for activity type \(type)")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
//
|
||||||
|
// AuxiliarySceneDelegate.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 12/13/20.
|
||||||
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Pachyderm
|
||||||
|
|
||||||
|
class AuxiliarySceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
|
var window: UIWindow?
|
||||||
|
|
||||||
|
private var launchActivity: NSUserActivity?
|
||||||
|
|
||||||
|
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||||
|
guard let windowScene = scene as? UIWindowScene else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let activity = connectionOptions.userActivities.first ?? session.stateRestorationActivity else {
|
||||||
|
// without an account, we don't know what type of auxiliary scene this is and can't do anything
|
||||||
|
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
launchActivity = activity
|
||||||
|
|
||||||
|
let account: LocalData.UserAccountInfo
|
||||||
|
|
||||||
|
if let activityAccount = UserActivityManager.getAccount(from: activity) {
|
||||||
|
account = activityAccount
|
||||||
|
} else if let mostRecent = LocalData.shared.getMostRecentAccount() {
|
||||||
|
account = mostRecent
|
||||||
|
} else {
|
||||||
|
// without an account, we can't do anything so we just destroy the scene
|
||||||
|
// todo: this isn't really true for instance public timelines, how much do we care about that?
|
||||||
|
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = MastodonController.getForAccount(account)
|
||||||
|
session.mastodonController = controller
|
||||||
|
|
||||||
|
guard let rootVC = viewController(for: activity, mastodonController: controller) else {
|
||||||
|
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rootVC.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(close))
|
||||||
|
let nav = EnhancedNavigationViewController(rootViewController: rootVC)
|
||||||
|
|
||||||
|
window = UIWindow(windowScene: windowScene)
|
||||||
|
window!.rootViewController = nav
|
||||||
|
window!.makeKeyAndVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneWillResignActive(_ scene: UIScene) {
|
||||||
|
scene.userActivity = launchActivity
|
||||||
|
}
|
||||||
|
|
||||||
|
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
|
||||||
|
return scene.userActivity
|
||||||
|
}
|
||||||
|
|
||||||
|
private func viewController(for activity: NSUserActivity, mastodonController: MastodonController) -> UIViewController? {
|
||||||
|
switch UserActivityType(rawValue: activity.activityType) {
|
||||||
|
case .showTimeline:
|
||||||
|
guard let timeline = UserActivityManager.getTimeline(from: activity) else { return nil }
|
||||||
|
return timelineViewController(for: timeline, mastodonController: mastodonController)
|
||||||
|
|
||||||
|
case .showConversation:
|
||||||
|
guard let id = UserActivityManager.getConversationStatus(from: activity) else { return nil }
|
||||||
|
return ConversationTableViewController(for: id, mastodonController: mastodonController)
|
||||||
|
|
||||||
|
case .checkNotifications:
|
||||||
|
guard let mode = UserActivityManager.getNotificationsMode(from: activity) else { return nil }
|
||||||
|
return NotificationsPageViewController(initialMode: mode, mastodonController: mastodonController)
|
||||||
|
|
||||||
|
case .search:
|
||||||
|
return SearchViewController(mastodonController: mastodonController)
|
||||||
|
|
||||||
|
case .bookmarks:
|
||||||
|
return BookmarksTableViewController(mastodonController: mastodonController)
|
||||||
|
|
||||||
|
case .myProfile:
|
||||||
|
return MyProfileViewController(mastodonController: mastodonController)
|
||||||
|
|
||||||
|
case .showProfile:
|
||||||
|
guard let id = UserActivityManager.getProfile(from: activity) else { return nil }
|
||||||
|
return ProfileViewController(accountID: id, mastodonController: mastodonController)
|
||||||
|
|
||||||
|
default:
|
||||||
|
fatalError("invalid activity type for auxiliary scene: \(activity.activityType)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func timelineViewController(for timeline: Timeline, mastodonController: MastodonController) -> UIViewController {
|
||||||
|
switch timeline {
|
||||||
|
// todo: list/hashtag controllers need whole objects which must be fetched asynchronously
|
||||||
|
default:
|
||||||
|
return TimelineTableViewController(for: timeline, mastodonController: mastodonController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func close() {
|
||||||
|
closeWindow()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
//
|
||||||
|
// ComposeSceneDelegate.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 12/12/20.
|
||||||
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class ComposeSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
|
var window: UIWindow?
|
||||||
|
|
||||||
|
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||||
|
guard let windowScene = scene as? UIWindowScene else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let account: LocalData.UserAccountInfo
|
||||||
|
let draft: Draft?
|
||||||
|
|
||||||
|
if let activity = connectionOptions.userActivities.first ?? session.stateRestorationActivity,
|
||||||
|
let activityAccount = UserActivityManager.getAccount(from: activity) {
|
||||||
|
account = activityAccount
|
||||||
|
draft = UserActivityManager.getDraft(from: activity)
|
||||||
|
} else {
|
||||||
|
account = LocalData.shared.getMostRecentAccount()!
|
||||||
|
draft = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = MastodonController.getForAccount(account)
|
||||||
|
session.mastodonController = controller
|
||||||
|
|
||||||
|
let composeVC = ComposeHostingController(draft: draft, mastodonController: controller)
|
||||||
|
composeVC.delegate = self
|
||||||
|
let nav = EnhancedNavigationViewController(rootViewController: composeVC)
|
||||||
|
|
||||||
|
window = UIWindow(windowScene: windowScene)
|
||||||
|
window!.rootViewController = nav
|
||||||
|
window!.makeKeyAndVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneWillResignActive(_ scene: UIScene) {
|
||||||
|
DraftsManager.save()
|
||||||
|
|
||||||
|
if let window = window,
|
||||||
|
let nav = window.rootViewController as? UINavigationController,
|
||||||
|
let compose = nav.topViewController as? ComposeHostingController {
|
||||||
|
scene.userActivity = UserActivityManager.editDraftActivity(id: compose.draft.id, accountID: scene.session.mastodonController!.accountInfo!.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
|
||||||
|
return scene.userActivity
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ComposeSceneDelegate: ComposeHostingControllerDelegate {
|
||||||
|
func dismissCompose(mode: ComposeUIState.DismissMode) -> Bool {
|
||||||
|
let animation: UIWindowScene.DismissalAnimation
|
||||||
|
switch mode {
|
||||||
|
case .cancel:
|
||||||
|
animation = .decline
|
||||||
|
case .post:
|
||||||
|
animation = .commit
|
||||||
|
}
|
||||||
|
closeWindow(animation: animation)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// UIWindowSceneDelegate+Close.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 12/12/20.
|
||||||
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UIWindowSceneDelegate {
|
||||||
|
|
||||||
|
func closeWindow(animation: UIWindowScene.DismissalAnimation = .standard, errorHandler: ((Error) -> Void)? = nil) {
|
||||||
|
guard let session = self.window??.windowScene?.session else { return }
|
||||||
|
let options = UIWindowSceneDestructionRequestOptions()
|
||||||
|
options.windowDismissalAnimation = animation
|
||||||
|
UIApplication.shared.requestSceneSessionDestruction(session, options: options, errorHandler: errorHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -51,21 +51,24 @@
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
<string>Post videos from the camera.</string>
|
<string>Post videos from the camera.</string>
|
||||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||||
<string>Save photos directly from other people's posts.</string>
|
<string>Save photos directly from other people's posts.</string>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
<string>Post photos from the photo library.</string>
|
<string>Post photos from the photo library.</string>
|
||||||
<key>NSUserActivityTypes</key>
|
<key>NSUserActivityTypes</key>
|
||||||
<array>
|
<array>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.show-conversation</string>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.show-timeline</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.show-timeline</string>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.check-notifications</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.check-notifications</string>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.check-mentions</string>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.new-post</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.new-post</string>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.search</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.search</string>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.bookmarks</string>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.my-profile</string>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.show-profile</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UIApplicationSceneManifest</key>
|
<key>UIApplicationSceneManifest</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIApplicationSupportsMultipleScenes</key>
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
<false/>
|
<true/>
|
||||||
<key>UISceneConfigurations</key>
|
<key>UISceneConfigurations</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIWindowSceneSessionRoleApplication</key>
|
<key>UIWindowSceneSessionRoleApplication</key>
|
||||||
|
@ -76,7 +79,23 @@
|
||||||
<key>UISceneConfigurationName</key>
|
<key>UISceneConfigurationName</key>
|
||||||
<string>main-scene</string>
|
<string>main-scene</string>
|
||||||
<key>UISceneDelegateClassName</key>
|
<key>UISceneDelegateClassName</key>
|
||||||
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
<string>$(PRODUCT_MODULE_NAME).MainSceneDelegate</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UISceneClassName</key>
|
||||||
|
<string>UIWindowScene</string>
|
||||||
|
<key>UISceneDelegateClassName</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).AuxiliarySceneDelegate</string>
|
||||||
|
<key>UISceneConfigurationName</key>
|
||||||
|
<string>auxiliary</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UISceneClassName</key>
|
||||||
|
<string>UIWindowScene</string>
|
||||||
|
<key>UISceneDelegateClassName</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).ComposeSceneDelegate</string>
|
||||||
|
<key>UISceneConfigurationName</key>
|
||||||
|
<string>compose</string>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// SceneDelegate.swift
|
// MainSceneDelegate.swift
|
||||||
// Tusker
|
// Tusker
|
||||||
//
|
//
|
||||||
// Created by Shadowfacts on 1/6/20.
|
// Created by Shadowfacts on 1/6/20.
|
||||||
|
@ -11,7 +11,7 @@ import Pachyderm
|
||||||
import CrashReporter
|
import CrashReporter
|
||||||
import MessageUI
|
import MessageUI
|
||||||
|
|
||||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
class MainSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
|
|
||||||
|
@ -195,13 +195,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SceneDelegate: OnboardingViewControllerDelegate {
|
extension MainSceneDelegate: OnboardingViewControllerDelegate {
|
||||||
func didFinishOnboarding(account: LocalData.UserAccountInfo) {
|
func didFinishOnboarding(account: LocalData.UserAccountInfo) {
|
||||||
activateAccount(account, animated: false)
|
activateAccount(account, animated: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SceneDelegate: MFMailComposeViewControllerDelegate {
|
extension MainSceneDelegate: MFMailComposeViewControllerDelegate {
|
||||||
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||||
controller.dismiss(animated: true) {
|
controller.dismiss(animated: true) {
|
||||||
self.showAppOrOnboardingUI()
|
self.showAppOrOnboardingUI()
|
|
@ -47,4 +47,8 @@ class DraftsManager: Codable {
|
||||||
drafts.removeAll { $0 == draft }
|
drafts.removeAll { $0 == draft }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getBy(id: UUID) -> Draft? {
|
||||||
|
return drafts.first { $0.id == id }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,14 @@ import Combine
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import PencilKit
|
import PencilKit
|
||||||
|
|
||||||
|
protocol ComposeHostingControllerDelegate: class {
|
||||||
|
func dismissCompose(mode: ComposeUIState.DismissMode) -> Bool
|
||||||
|
}
|
||||||
|
|
||||||
class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
|
|
||||||
|
weak var delegate: ComposeHostingControllerDelegate?
|
||||||
|
|
||||||
let mastodonController: MastodonController
|
let mastodonController: MastodonController
|
||||||
|
|
||||||
let uiState: ComposeUIState
|
let uiState: ComposeUIState
|
||||||
|
@ -61,7 +67,7 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
|
|
||||||
pasteConfiguration = UIPasteConfiguration(forAccepting: CompositionAttachment.self)
|
pasteConfiguration = UIPasteConfiguration(forAccepting: CompositionAttachment.self)
|
||||||
|
|
||||||
userActivity = UserActivityManager.newPostActivity()
|
userActivity = UserActivityManager.newPostActivity(accountID: mastodonController.accountInfo!.id)
|
||||||
|
|
||||||
self.uiState.$draft
|
self.uiState.$draft
|
||||||
.flatMap(\.$visibility)
|
.flatMap(\.$visibility)
|
||||||
|
@ -81,21 +87,6 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
// can't do this in viewDidLoad because viewDidLoad isn't called for UIHostingController
|
|
||||||
// if mainToolbar.superview == nil {
|
|
||||||
// view.addSubview(mainToolbar)
|
|
||||||
// NSLayoutConstraint.activate([
|
|
||||||
// mainToolbar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
||||||
// mainToolbar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
|
||||||
// // use the top anchor of the toolbar so our additionalSafeAreaInsets (which has the bottom as the toolbar height) don't affect it
|
|
||||||
// mainToolbar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
|
|
||||||
// ])
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
override func didMove(toParent parent: UIViewController?) {
|
override func didMove(toParent parent: UIViewController?) {
|
||||||
super.didMove(toParent: parent)
|
super.didMove(toParent: parent)
|
||||||
|
|
||||||
|
@ -287,8 +278,11 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
extension ComposeHostingController: ComposeUIStateDelegate {
|
extension ComposeHostingController: ComposeUIStateDelegate {
|
||||||
var assetPickerDelegate: AssetPickerViewControllerDelegate? { self }
|
var assetPickerDelegate: AssetPickerViewControllerDelegate? { self }
|
||||||
|
|
||||||
func dismissCompose() {
|
func dismissCompose(mode: ComposeUIState.DismissMode) {
|
||||||
self.dismiss(animated: true)
|
let dismissed = delegate?.dismissCompose(mode: mode) ?? false
|
||||||
|
if !dismissed {
|
||||||
|
self.dismiss(animated: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func presentAssetPickerSheet() {
|
func presentAssetPickerSheet() {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import SwiftUI
|
||||||
protocol ComposeUIStateDelegate: class {
|
protocol ComposeUIStateDelegate: class {
|
||||||
var assetPickerDelegate: AssetPickerViewControllerDelegate? { get }
|
var assetPickerDelegate: AssetPickerViewControllerDelegate? { get }
|
||||||
|
|
||||||
func dismissCompose()
|
func dismissCompose(mode: ComposeUIState.DismissMode)
|
||||||
func presentAssetPickerSheet()
|
func presentAssetPickerSheet()
|
||||||
func presentComposeDrawing()
|
func presentComposeDrawing()
|
||||||
|
|
||||||
|
@ -54,6 +54,12 @@ extension ComposeUIState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ComposeUIState {
|
||||||
|
enum DismissMode {
|
||||||
|
case cancel, post
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protocol ComposeAutocompleteHandler: class {
|
protocol ComposeAutocompleteHandler: class {
|
||||||
func autocomplete(with string: String)
|
func autocomplete(with string: String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,14 +168,14 @@ struct ComposeView: View {
|
||||||
private func cancel() {
|
private func cancel() {
|
||||||
if Preferences.shared.automaticallySaveDrafts {
|
if Preferences.shared.automaticallySaveDrafts {
|
||||||
// draft is already stored in drafts manager, drafts manager is saved by ComposeHostingController.viewWillDisappear
|
// draft is already stored in drafts manager, drafts manager is saved by ComposeHostingController.viewWillDisappear
|
||||||
uiState.delegate?.dismissCompose()
|
uiState.delegate?.dismissCompose(mode: .cancel)
|
||||||
} else {
|
} else {
|
||||||
// if the draft doesn't have content, it doesn't need to be saved
|
// if the draft doesn't have content, it doesn't need to be saved
|
||||||
if draft.hasContent {
|
if draft.hasContent {
|
||||||
uiState.isShowingSaveDraftSheet = true
|
uiState.isShowingSaveDraftSheet = true
|
||||||
} else {
|
} else {
|
||||||
DraftsManager.shared.remove(draft)
|
DraftsManager.shared.remove(draft)
|
||||||
uiState.delegate?.dismissCompose()
|
uiState.delegate?.dismissCompose(mode: .cancel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,12 +185,12 @@ struct ComposeView: View {
|
||||||
.default(Text("Save Draft"), action: {
|
.default(Text("Save Draft"), action: {
|
||||||
// draft is already stored in drafts manager, drafts manager is saved by ComposeHostingController.viewWillDisappear
|
// draft is already stored in drafts manager, drafts manager is saved by ComposeHostingController.viewWillDisappear
|
||||||
uiState.isShowingSaveDraftSheet = false
|
uiState.isShowingSaveDraftSheet = false
|
||||||
uiState.delegate?.dismissCompose()
|
uiState.delegate?.dismissCompose(mode: .cancel)
|
||||||
}),
|
}),
|
||||||
.destructive(Text("Delete Draft"), action: {
|
.destructive(Text("Delete Draft"), action: {
|
||||||
DraftsManager.shared.remove(draft)
|
DraftsManager.shared.remove(draft)
|
||||||
uiState.isShowingSaveDraftSheet = false
|
uiState.isShowingSaveDraftSheet = false
|
||||||
uiState.delegate?.dismissCompose()
|
uiState.delegate?.dismissCompose(mode: .cancel)
|
||||||
}),
|
}),
|
||||||
.cancel(),
|
.cancel(),
|
||||||
])
|
])
|
||||||
|
@ -242,7 +242,7 @@ struct ComposeView: View {
|
||||||
|
|
||||||
// wait .25 seconds so the user can see the progress bar has completed
|
// wait .25 seconds so the user can see the progress bar has completed
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(250)) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(250)) {
|
||||||
self.uiState.delegate?.dismissCompose()
|
self.uiState.delegate?.dismissCompose(mode: .post)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ class CrashReporterViewController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func cancelPressed(_ sender: Any) {
|
@IBAction func cancelPressed(_ sender: Any) {
|
||||||
(view.window!.windowScene!.delegate as! SceneDelegate).showAppOrOnboardingUI()
|
(view.window!.windowScene!.delegate as! MainSceneDelegate).showAppOrOnboardingUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ class CrashReporterViewController: UIViewController {
|
||||||
extension CrashReporterViewController: MFMailComposeViewControllerDelegate {
|
extension CrashReporterViewController: MFMailComposeViewControllerDelegate {
|
||||||
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||||
controller.dismiss(animated: true) {
|
controller.dismiss(animated: true) {
|
||||||
(self.view.window!.windowScene!.delegate as! SceneDelegate).showAppOrOnboardingUI()
|
(self.view.window!.windowScene!.delegate as! MainSceneDelegate).showAppOrOnboardingUI()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ class FastAccountSwitcherViewController: UIViewController {
|
||||||
selectionChangedFeedbackGenerator = nil
|
selectionChangedFeedbackGenerator = nil
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
(self.view.window!.windowScene!.delegate as! SceneDelegate).activateAccount(account, animated: true)
|
(self.view.window!.windowScene!.delegate as! MainSceneDelegate).activateAccount(account, animated: true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hide()
|
hide()
|
||||||
|
|
|
@ -80,6 +80,7 @@ class MainSidebarViewController: UIViewController {
|
||||||
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
collectionView.backgroundColor = .clear
|
collectionView.backgroundColor = .clear
|
||||||
collectionView.delegate = self
|
collectionView.delegate = self
|
||||||
|
collectionView.dragDelegate = self
|
||||||
view.addSubview(collectionView)
|
view.addSubview(collectionView)
|
||||||
|
|
||||||
dataSource = createDataSource()
|
dataSource = createDataSource()
|
||||||
|
@ -220,6 +221,32 @@ class MainSidebarViewController: UIViewController {
|
||||||
present(navController, animated: true)
|
present(navController, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func userActivityForItem(_ item: Item) -> NSUserActivity? {
|
||||||
|
guard let id = mastodonController.accountInfo?.id else { return nil }
|
||||||
|
|
||||||
|
switch item {
|
||||||
|
case .tab(.notifications):
|
||||||
|
return UserActivityManager.checkNotificationsActivity(mode: Preferences.shared.defaultNotificationsMode)
|
||||||
|
case .tab(.compose):
|
||||||
|
return UserActivityManager.newPostActivity(accountID: id)
|
||||||
|
case .search:
|
||||||
|
return UserActivityManager.searchActivity()
|
||||||
|
case .bookmarks:
|
||||||
|
return UserActivityManager.bookmarksActivity()
|
||||||
|
case .tab(.myProfile):
|
||||||
|
return UserActivityManager.myProfileActivity()
|
||||||
|
case let .list(list):
|
||||||
|
return UserActivityManager.showTimelineActivity(timeline: .list(id: list.id), accountID: id)
|
||||||
|
case let .savedHashtag(tag):
|
||||||
|
return UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: tag.name), accountID: id)
|
||||||
|
case .savedInstance(_):
|
||||||
|
// todo: show timeline activity doesn't work for public timelines
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS 14.0, *)
|
@available(iOS 14.0, *)
|
||||||
|
@ -367,6 +394,19 @@ extension MainSidebarViewController: UICollectionViewDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@available(iOS 14.0, *)
|
||||||
|
extension MainSidebarViewController: UICollectionViewDragDelegate {
|
||||||
|
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
||||||
|
guard let item = dataSource.itemIdentifier(for: indexPath),
|
||||||
|
let activity = userActivityForItem(item) else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
let provider = NSItemProvider(object: activity)
|
||||||
|
return [UIDragItem(itemProvider: provider)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@available(iOS 14.0, *)
|
@available(iOS 14.0, *)
|
||||||
extension MainSidebarViewController: InstanceTimelineViewControllerDelegate {
|
extension MainSidebarViewController: InstanceTimelineViewControllerDelegate {
|
||||||
func didSaveInstance(url: URL) {
|
func didSaveInstance(url: URL) {
|
||||||
|
|
|
@ -16,16 +16,19 @@ class NotificationsPageViewController: SegmentedPageViewController {
|
||||||
|
|
||||||
weak var mastodonController: MastodonController!
|
weak var mastodonController: MastodonController!
|
||||||
|
|
||||||
init(mastodonController: MastodonController) {
|
var initialMode: NotificationsMode?
|
||||||
|
|
||||||
|
init(initialMode: NotificationsMode? = nil, mastodonController: MastodonController) {
|
||||||
|
self.initialMode = initialMode
|
||||||
self.mastodonController = mastodonController
|
self.mastodonController = mastodonController
|
||||||
|
|
||||||
let notifications = NotificationsTableViewController(allowedTypes: Pachyderm.Notification.Kind.allCases, mastodonController: mastodonController)
|
let notifications = NotificationsTableViewController(allowedTypes: Pachyderm.Notification.Kind.allCases, mastodonController: mastodonController)
|
||||||
notifications.title = notificationsTitle
|
notifications.title = notificationsTitle
|
||||||
notifications.userActivity = UserActivityManager.checkNotificationsActivity()
|
notifications.userActivity = UserActivityManager.checkNotificationsActivity(mode: .allNotifications)
|
||||||
|
|
||||||
let mentions = NotificationsTableViewController(allowedTypes: [.mention], mastodonController: mastodonController)
|
let mentions = NotificationsTableViewController(allowedTypes: [.mention], mastodonController: mastodonController)
|
||||||
mentions.title = mentionsTitle
|
mentions.title = mentionsTitle
|
||||||
mentions.userActivity = UserActivityManager.checkMentionsActivity()
|
mentions.userActivity = UserActivityManager.checkNotificationsActivity(mode: .mentionsOnly)
|
||||||
|
|
||||||
super.init(titles: [
|
super.init(titles: [
|
||||||
notificationsTitle,
|
notificationsTitle,
|
||||||
|
@ -46,7 +49,7 @@ class NotificationsPageViewController: SegmentedPageViewController {
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
selectMode(Preferences.shared.defaultNotificationsMode)
|
selectMode(initialMode ?? Preferences.shared.defaultNotificationsMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectMode(_ mode: NotificationsMode) {
|
func selectMode(_ mode: NotificationsMode) {
|
||||||
|
|
|
@ -63,7 +63,8 @@ class PreferencesNavigationController: UINavigationController {
|
||||||
// the propper fix would be to figure out what's leaking instances of this class
|
// the propper fix would be to figure out what's leaking instances of this class
|
||||||
guard let window = self.view.window,
|
guard let window = self.view.window,
|
||||||
let windowScene = window.windowScene,
|
let windowScene = window.windowScene,
|
||||||
let sceneDelegate = windowScene.delegate as? SceneDelegate else {
|
// todo: my profile can be torn off into a separate window, this doesn't work
|
||||||
|
let sceneDelegate = windowScene.delegate as? MainSceneDelegate else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let account = notification.userInfo!["account"] as! LocalData.UserAccountInfo
|
let account = notification.userInfo!["account"] as! LocalData.UserAccountInfo
|
||||||
|
@ -76,7 +77,8 @@ class PreferencesNavigationController: UINavigationController {
|
||||||
@objc func userLoggedOut() {
|
@objc func userLoggedOut() {
|
||||||
guard let window = self.view.window,
|
guard let window = self.view.window,
|
||||||
let windowScene = window.windowScene,
|
let windowScene = window.windowScene,
|
||||||
let sceneDelegate = windowScene.delegate as? SceneDelegate else {
|
// todo: my profile can be torn off into a separate window, this doesn't work
|
||||||
|
let sceneDelegate = windowScene.delegate as? MainSceneDelegate else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
isSwitchingAccounts = true
|
isSwitchingAccounts = true
|
||||||
|
@ -90,7 +92,8 @@ class PreferencesNavigationController: UINavigationController {
|
||||||
extension PreferencesNavigationController: OnboardingViewControllerDelegate {
|
extension PreferencesNavigationController: OnboardingViewControllerDelegate {
|
||||||
func didFinishOnboarding(account: LocalData.UserAccountInfo) {
|
func didFinishOnboarding(account: LocalData.UserAccountInfo) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let sceneDelegate = self.view.window!.windowScene!.delegate as! SceneDelegate
|
// todo: my profile can be torn off into a separate window, this will crash
|
||||||
|
let sceneDelegate = self.view.window!.windowScene!.delegate as! MainSceneDelegate
|
||||||
self.dismiss(animated: true) { // dismiss instance selector
|
self.dismiss(animated: true) { // dismiss instance selector
|
||||||
self.dismiss(animated: true) { // dismiss preferences
|
self.dismiss(animated: true) { // dismiss preferences
|
||||||
sceneDelegate.activateAccount(account, animated: false)
|
sceneDelegate.activateAccount(account, animated: false)
|
||||||
|
|
|
@ -37,6 +37,8 @@ class MyProfileViewController: ProfileViewController {
|
||||||
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Preferences", style: .plain, target: self, action: #selector(preferencesPressed))
|
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Preferences", style: .plain, target: self, action: #selector(preferencesPressed))
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
||||||
|
|
||||||
|
userActivity = UserActivityManager.myProfileActivity()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setAvatarTabBarImage<Account: AccountProtocol>(account: Account) {
|
private func setAvatarTabBarImage<Account: AccountProtocol>(account: Account) {
|
||||||
|
|
|
@ -78,6 +78,8 @@ class ProfileViewController: UIPageViewController {
|
||||||
}
|
}
|
||||||
navigationItem.rightBarButtonItem = composeButton
|
navigationItem.rightBarButtonItem = composeButton
|
||||||
|
|
||||||
|
userActivity = UserActivityManager.showProfileActivity(id: accountID!, accountID: mastodonController.accountInfo!.id)
|
||||||
|
|
||||||
headerView = ProfileHeaderView.create()
|
headerView = ProfileHeaderView.create()
|
||||||
headerView.delegate = self
|
headerView.delegate = self
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ class SearchViewController: UIViewController {
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
view.backgroundColor = .systemBackground
|
||||||
|
|
||||||
resultsController = SearchResultsViewController(mastodonController: mastodonController)
|
resultsController = SearchResultsViewController(mastodonController: mastodonController)
|
||||||
resultsController.exploreNavigationController = self.navigationController
|
resultsController.exploreNavigationController = self.navigationController
|
||||||
searchController = UISearchController(searchResultsController: resultsController)
|
searchController = UISearchController(searchResultsController: resultsController)
|
||||||
|
|
|
@ -28,7 +28,9 @@ class TimelineTableViewController: TimelineLikeTableViewController<TimelineEntry
|
||||||
title = timeline.title
|
title = timeline.title
|
||||||
tabBarItem.image = timeline.tabBarImage
|
tabBarItem.image = timeline.tabBarImage
|
||||||
|
|
||||||
userActivity = UserActivityManager.showTimelineActivity(timeline: timeline)
|
if let id = mastodonController.accountInfo?.id {
|
||||||
|
userActivity = UserActivityManager.showTimelineActivity(timeline: timeline, accountID: id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
|
@ -51,6 +53,8 @@ class TimelineTableViewController: TimelineLikeTableViewController<TimelineEntry
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
tableView.register(UINib(nibName: "TimelineStatusTableViewCell", bundle: .main), forCellReuseIdentifier: "statusCell")
|
tableView.register(UINib(nibName: "TimelineStatusTableViewCell", bundle: .main), forCellReuseIdentifier: "statusCell")
|
||||||
|
|
||||||
|
tableView.dragDelegate = self
|
||||||
}
|
}
|
||||||
|
|
||||||
override class func refreshCommandTitle() -> String {
|
override class func refreshCommandTitle() -> String {
|
||||||
|
@ -171,3 +175,18 @@ extension TimelineTableViewController: UITableViewDataSourcePrefetching {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension TimelineTableViewController: UITableViewDragDelegate {
|
||||||
|
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
||||||
|
let id = item(for: indexPath).id
|
||||||
|
guard let status = mastodonController.persistentContainer.status(for: id),
|
||||||
|
let accountId = mastodonController.accountInfo?.id else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let activity = UserActivityManager.showConversationActivity(mainStatusID: id, accountID: accountId)
|
||||||
|
let itemProvider = NSItemProvider(object: status.url! as NSURL)
|
||||||
|
itemProvider.registerObject(activity, visibility: .all)
|
||||||
|
let dragItem = UIDragItem(itemProvider: itemProvider)
|
||||||
|
return [dragItem]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -189,7 +189,7 @@ extension MenuPreviewProvider {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
let shareSection = [
|
var shareSection = [
|
||||||
openInSafariAction(url: status.url!),
|
openInSafariAction(url: status.url!),
|
||||||
createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { [weak self] (_) in
|
createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { [weak self] (_) in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
@ -197,14 +197,30 @@ extension MenuPreviewProvider {
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
shareSection.append(createAction(identifier: "new_window", title: "Open in New Window", systemImageName: "", handler: { (_) in
|
||||||
|
guard let id = mastodonController.accountInfo?.id else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// todo: this should try to find an existing session
|
||||||
|
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: UserActivityManager.showConversationActivity(mainStatusID: statusID, accountID: id), options: nil, errorHandler: nil)
|
||||||
|
}))
|
||||||
|
#endif
|
||||||
|
|
||||||
return [
|
return [
|
||||||
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection),
|
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection),
|
||||||
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: actionsSection),
|
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: actionsSection),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createAction(identifier: String, title: String, systemImageName: String, handler: @escaping UIActionHandler) -> UIAction {
|
private func createAction(identifier: String, title: String, systemImageName: String?, handler: @escaping UIActionHandler) -> UIAction {
|
||||||
return UIAction(title: title, image: UIImage(systemName: systemImageName), identifier: UIAction.Identifier(identifier), discoverabilityTitle: nil, attributes: [], state: .off, handler: handler)
|
let image: UIImage?
|
||||||
|
if let name = systemImageName {
|
||||||
|
image = UIImage(systemName: name)
|
||||||
|
} else {
|
||||||
|
image = nil
|
||||||
|
}
|
||||||
|
return UIAction(title: title, image: image, identifier: UIAction.Identifier(identifier), discoverabilityTitle: nil, attributes: [], state: .off, handler: handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openInSafariAction(url: URL) -> UIAction {
|
private func openInSafariAction(url: URL) -> UIAction {
|
||||||
|
|
|
@ -30,18 +30,27 @@ class UserActivityManager {
|
||||||
private static func present(_ vc: UIViewController, animated: Bool = true) {
|
private static func present(_ vc: UIViewController, animated: Bool = true) {
|
||||||
getMainViewController().present(vc, animated: animated)
|
getMainViewController().present(vc, animated: animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func getAccount(from activity: NSUserActivity) -> LocalData.UserAccountInfo? {
|
||||||
|
guard let id = activity.userInfo?["accountID"] as? String else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return LocalData.shared.getAccount(id: id)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - New Post
|
// MARK: - New Post
|
||||||
static func newPostActivity(mentioning: Account? = nil) -> NSUserActivity {
|
static func newPostActivity(mentioning: Account? = nil, accountID: String) -> NSUserActivity {
|
||||||
// todo: update to use managed objects
|
// todo: update to use managed objects
|
||||||
let activity = NSUserActivity(type: .newPost)
|
let activity = NSUserActivity(type: .newPost)
|
||||||
activity.isEligibleForPrediction = true
|
activity.isEligibleForPrediction = true
|
||||||
|
activity.userInfo = [
|
||||||
|
"accountID": accountID,
|
||||||
|
]
|
||||||
if let mentioning = mentioning {
|
if let mentioning = mentioning {
|
||||||
activity.userInfo = ["mentioning": mentioning.acct]
|
activity.userInfo!["mentioning"] = mentioning.acct
|
||||||
activity.title = "Send a message to \(mentioning.displayName)"
|
activity.title = "Send a message to \(mentioning.displayName)"
|
||||||
activity.suggestedInvocationPhrase = "Send a message to \(mentioning.displayName)"
|
activity.suggestedInvocationPhrase = "Send a message to \(mentioning.displayName)"
|
||||||
} else {
|
} else {
|
||||||
activity.userInfo = [:]
|
|
||||||
activity.title = "New Post"
|
activity.title = "New Post"
|
||||||
activity.suggestedInvocationPhrase = "Post in Tusker"
|
activity.suggestedInvocationPhrase = "Post in Tusker"
|
||||||
}
|
}
|
||||||
|
@ -56,12 +65,39 @@ class UserActivityManager {
|
||||||
present(UINavigationController(rootViewController: composeVC))
|
present(UINavigationController(rootViewController: composeVC))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func editDraftActivity(id: UUID, accountID: String) -> NSUserActivity {
|
||||||
|
let activity = NSUserActivity(type: .newPost)
|
||||||
|
activity.userInfo = [
|
||||||
|
"accountID": accountID,
|
||||||
|
"draftID": id.uuidString,
|
||||||
|
]
|
||||||
|
return activity
|
||||||
|
}
|
||||||
|
|
||||||
|
static func getDraft(from activity: NSUserActivity) -> Draft? {
|
||||||
|
guard activity.activityType == UserActivityType.newPost.rawValue,
|
||||||
|
let str = activity.userInfo?["draftID"] as? String,
|
||||||
|
let uuid = UUID(uuidString: str) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return DraftsManager.shared.getBy(id: uuid)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Check Notifications
|
// MARK: - Check Notifications
|
||||||
static func checkNotificationsActivity() -> NSUserActivity {
|
static func checkNotificationsActivity(mode: NotificationsMode = .allNotifications) -> NSUserActivity {
|
||||||
let activity = NSUserActivity(type: .checkNotifications)
|
let activity = NSUserActivity(type: .checkNotifications)
|
||||||
activity.isEligibleForPrediction = true
|
activity.isEligibleForPrediction = true
|
||||||
activity.title = NSLocalizedString("Check Notifications", comment: "check notifications shortcut title")
|
activity.addUserInfoEntries(from: [
|
||||||
activity.suggestedInvocationPhrase = NSLocalizedString("Check my Tusker notifications", comment: "check notifications shortcut invocation phrase")
|
"notificationsMode": mode.rawValue
|
||||||
|
])
|
||||||
|
switch mode {
|
||||||
|
case .allNotifications:
|
||||||
|
activity.title = NSLocalizedString("Check Notifications", comment: "check notifications shortcut title")
|
||||||
|
activity.suggestedInvocationPhrase = NSLocalizedString("Check my Tusker notifications", comment: "check notifications shortcut invocation phrase")
|
||||||
|
case .mentionsOnly:
|
||||||
|
activity.title = NSLocalizedString("Check Mentions", comment: "check mentions shortcut title")
|
||||||
|
activity.suggestedInvocationPhrase = NSLocalizedString("Check my mentions", comment: "check mentions shortcut invocation phrase")
|
||||||
|
}
|
||||||
return activity
|
return activity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,37 +108,27 @@ class UserActivityManager {
|
||||||
let notificationsPageController = navigationController.viewControllers.first as? NotificationsPageViewController {
|
let notificationsPageController = navigationController.viewControllers.first as? NotificationsPageViewController {
|
||||||
navigationController.popToRootViewController(animated: false)
|
navigationController.popToRootViewController(animated: false)
|
||||||
notificationsPageController.loadViewIfNeeded()
|
notificationsPageController.loadViewIfNeeded()
|
||||||
notificationsPageController.selectMode(.allNotifications)
|
notificationsPageController.selectMode(getNotificationsMode(from: activity) ?? Preferences.shared.defaultNotificationsMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Check Mentions
|
static func getNotificationsMode(from activity: NSUserActivity) -> NotificationsMode? {
|
||||||
static func checkMentionsActivity() -> NSUserActivity {
|
guard let str = activity.userInfo?["notificationsMode"] as? String else {
|
||||||
let activity = NSUserActivity(type: .checkMentions)
|
return nil
|
||||||
activity.isEligibleForPrediction = true
|
|
||||||
activity.title = NSLocalizedString("Check Mentions", comment: "check mentions shortcut title")
|
|
||||||
activity.suggestedInvocationPhrase = NSLocalizedString("Check my mentions", comment: "check mentions shortcut invocation phrase")
|
|
||||||
return activity
|
|
||||||
}
|
|
||||||
|
|
||||||
static func handleCheckMentions(activity: NSUserActivity) {
|
|
||||||
let mainViewController = getMainViewController()
|
|
||||||
mainViewController.select(tab: .notifications)
|
|
||||||
if let navController = mainViewController.getTabController(tab: .notifications) as? UINavigationController,
|
|
||||||
let notificationsPageController = navController.viewControllers.first as? NotificationsPageViewController {
|
|
||||||
navController.popToRootViewController(animated: false)
|
|
||||||
notificationsPageController.loadViewIfNeeded()
|
|
||||||
notificationsPageController.selectMode(.mentionsOnly)
|
|
||||||
}
|
}
|
||||||
|
return NotificationsMode(rawValue: str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Show Timeline
|
// MARK: - Show Timeline
|
||||||
static func showTimelineActivity(timeline: Timeline) -> NSUserActivity? {
|
static func showTimelineActivity(timeline: Timeline, accountID: String) -> NSUserActivity? {
|
||||||
guard let timelineData = try? encoder.encode(timeline) else { return nil }
|
guard let timelineData = try? encoder.encode(timeline) else { return nil }
|
||||||
|
|
||||||
let activity = NSUserActivity(type: .showTimeline)
|
let activity = NSUserActivity(type: .showTimeline)
|
||||||
activity.isEligibleForPrediction = true
|
activity.isEligibleForPrediction = true
|
||||||
activity.userInfo = ["timelineData": timelineData]
|
activity.userInfo = [
|
||||||
|
"timelineData": timelineData,
|
||||||
|
"accountID": accountID,
|
||||||
|
]
|
||||||
switch timeline {
|
switch timeline {
|
||||||
case .home:
|
case .home:
|
||||||
activity.title = NSLocalizedString("Show Home Timeline", comment: "home timeline shortcut title")
|
activity.title = NSLocalizedString("Show Home Timeline", comment: "home timeline shortcut title")
|
||||||
|
@ -127,11 +153,16 @@ class UserActivityManager {
|
||||||
return activity
|
return activity
|
||||||
}
|
}
|
||||||
|
|
||||||
static func handleShowTimeline(activity: NSUserActivity) {
|
static func getTimeline(from activity: NSUserActivity) -> Timeline? {
|
||||||
guard let timelineData = activity.userInfo?["timelineData"] as? Data,
|
guard activity.activityType == UserActivityType.showTimeline.rawValue,
|
||||||
let timeline = try? decoder.decode(Timeline.self, from: timelineData) else {
|
let data = activity.userInfo?["timelineData"] as? Data else {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
return try? decoder.decode(Timeline.self, from: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func handleShowTimeline(activity: NSUserActivity) {
|
||||||
|
guard let timeline = getTimeline(from: activity) else { return }
|
||||||
|
|
||||||
let mainViewController = getMainViewController()
|
let mainViewController = getMainViewController()
|
||||||
mainViewController.select(tab: .timelines)
|
mainViewController.select(tab: .timelines)
|
||||||
|
@ -162,6 +193,21 @@ class UserActivityManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Show Conversation
|
||||||
|
static func showConversationActivity(mainStatusID: String, accountID: String, isEligibleForPrediction: Bool = false) -> NSUserActivity {
|
||||||
|
let activity = NSUserActivity(type: .showConversation)
|
||||||
|
activity.userInfo = [
|
||||||
|
"mainStatusID": mainStatusID,
|
||||||
|
"accountID": accountID,
|
||||||
|
]
|
||||||
|
activity.isEligibleForPrediction = isEligibleForPrediction
|
||||||
|
return activity
|
||||||
|
}
|
||||||
|
|
||||||
|
static func getConversationStatus(from activity: NSUserActivity) -> String? {
|
||||||
|
return activity.userInfo?["mainStatusID"] as? String
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Explore
|
// MARK: - Explore
|
||||||
|
|
||||||
static func searchActivity() -> NSUserActivity {
|
static func searchActivity() -> NSUserActivity {
|
||||||
|
@ -200,4 +246,33 @@ class UserActivityManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - My Profile
|
||||||
|
static func myProfileActivity() -> NSUserActivity {
|
||||||
|
let activity = NSUserActivity(type: .myProfile)
|
||||||
|
activity.isEligibleForPrediction = true
|
||||||
|
activity.title = NSLocalizedString("My Profile", comment: "my profile shortcut title")
|
||||||
|
activity.suggestedInvocationPhrase = NSLocalizedString("Show my Mastodon profile", comment: "my profile shortuct invocation phrase")
|
||||||
|
return activity
|
||||||
|
}
|
||||||
|
|
||||||
|
static func handleMyProfile(activity: NSUserActivity) {
|
||||||
|
let mainViewController = getMainViewController()
|
||||||
|
mainViewController.select(tab: .myProfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Show Profile
|
||||||
|
static func showProfileActivity(id profileID: String, accountID: String) -> NSUserActivity {
|
||||||
|
let activity = NSUserActivity(type: .showProfile)
|
||||||
|
activity.userInfo = [
|
||||||
|
"profileID": profileID,
|
||||||
|
"accountID": accountID,
|
||||||
|
]
|
||||||
|
// todo: should this be eligible for prediction?
|
||||||
|
return activity
|
||||||
|
}
|
||||||
|
|
||||||
|
static func getProfile(from activity: NSUserActivity) -> String? {
|
||||||
|
return activity.userInfo?["profileID"] as? String
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,14 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum UserActivityType: String {
|
enum UserActivityType: String {
|
||||||
case newPost = "net.shadowfacts.Tusker.activity.new-post"
|
case newPost = "space.vaccor.Tusker.activity.new-post"
|
||||||
case checkNotifications = "net.shadowfacts.Tusker.activity.check-notifications"
|
case checkNotifications = "space.vaccor.Tusker.activity.check-notifications"
|
||||||
case checkMentions = "net.shadowfacts.Tusker.activity.check-mentions"
|
case showTimeline = "space.vaccor.Tusker.activity.show-timeline"
|
||||||
case showTimeline = "net.shadowfacts.Tusker.activity.show-timeline"
|
case search = "space.vaccor.Tusker.activity.search"
|
||||||
case search = "net.shadowfacts.Tusker.activity.search"
|
case bookmarks = "space.vaccor.Tusker.activity.bookmarks"
|
||||||
case bookmarks = "net.shadowfacts.Tusker.activity.bookmarks"
|
case showConversation = "space.vaccor.Tusker.activity.show-conversation"
|
||||||
|
case myProfile = "space.vaccor.Tusker.activity.my-profile"
|
||||||
|
case showProfile = "space.vaccor.Tusker.activity.show-profile"
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UserActivityType {
|
extension UserActivityType {
|
||||||
|
@ -24,14 +26,18 @@ extension UserActivityType {
|
||||||
return UserActivityManager.handleNewPost
|
return UserActivityManager.handleNewPost
|
||||||
case .checkNotifications:
|
case .checkNotifications:
|
||||||
return UserActivityManager.handleCheckNotifications
|
return UserActivityManager.handleCheckNotifications
|
||||||
case .checkMentions:
|
|
||||||
return UserActivityManager.handleCheckMentions
|
|
||||||
case .showTimeline:
|
case .showTimeline:
|
||||||
return UserActivityManager.handleShowTimeline
|
return UserActivityManager.handleShowTimeline
|
||||||
case .search:
|
case .search:
|
||||||
return UserActivityManager.handleSearch
|
return UserActivityManager.handleSearch
|
||||||
case .bookmarks:
|
case .bookmarks:
|
||||||
return UserActivityManager.handleBookmarks
|
return UserActivityManager.handleBookmarks
|
||||||
|
case .showConversation:
|
||||||
|
fatalError("cannot handle show conversation activity")
|
||||||
|
case .myProfile:
|
||||||
|
return UserActivityManager.handleMyProfile
|
||||||
|
case .showProfile:
|
||||||
|
fatalError("cannot handle show profile activity")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,9 +81,10 @@ class BaseStatusTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
displayNameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
displayNameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||||
usernameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
usernameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||||
|
|
||||||
avatarImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
avatarImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||||
|
|
||||||
avatarImageView.layer.masksToBounds = true
|
avatarImageView.layer.masksToBounds = true
|
||||||
|
avatarImageView.addInteraction(UIDragInteraction(delegate: self))
|
||||||
|
|
||||||
attachmentsView.delegate = self
|
attachmentsView.delegate = self
|
||||||
|
|
||||||
|
@ -479,3 +480,13 @@ extension BaseStatusTableViewCell: MenuPreviewProvider {
|
||||||
return self.getStatusCellPreviewProviders(for: location, sourceViewController: sourceViewController)
|
return self.getStatusCellPreviewProviders(for: location, sourceViewController: sourceViewController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension BaseStatusTableViewCell: UIDragInteractionDelegate {
|
||||||
|
func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
|
||||||
|
guard let currentAccountID = mastodonController.accountInfo?.id else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let provider = NSItemProvider(object: UserActivityManager.showProfileActivity(id: accountID, accountID: currentAccountID))
|
||||||
|
return [UIDragItem(itemProvider: provider)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue