diff --git a/Shared/Navigation/ContentView.swift b/Shared/Navigation/ContentView.swift index d5c1e4d..327cdfa 100644 --- a/Shared/Navigation/ContentView.swift +++ b/Shared/Navigation/ContentView.swift @@ -53,25 +53,6 @@ struct ContentView: View { #if os(macOS) ZStack { PostListView(selectedCollection: nil, showAllPosts: model.account.isLoggedIn) - .toolbar { - ToolbarItemGroup(placement: .primaryAction) { - if let selectedPost = model.selectedPost { - ActivePostToolbarView(activePost: selectedPost) - .alert(isPresented: $model.isPresentingNetworkErrorAlert, content: { - Alert( - title: Text("Connection Error"), - message: Text(""" - There is no internet connection at the moment. \ - Please reconnect or try again later. - """), - dismissButton: .default(Text("OK"), action: { - model.isPresentingNetworkErrorAlert = false - }) - ) - }) - } - } - } if model.isProcessingRequest { ZStack { Color(NSColor.controlBackgroundColor).opacity(0.75) diff --git a/Shared/PostList/PostListView.swift b/Shared/PostList/PostListView.swift index 8199e17..e20651a 100644 --- a/Shared/PostList/PostListView.swift +++ b/Shared/PostList/PostListView.swift @@ -92,7 +92,8 @@ struct PostListView: View { Alert( title: Text("Connection Error"), message: Text(""" - There is no internet connection at the moment. Please reconnect or try again later. + There is no internet connection at the moment. \ + Please reconnect or try again later. """), dismissButton: .default(Text("OK"), action: { model.isPresentingNetworkErrorAlert = false diff --git a/Shared/WriteFreely_MultiPlatformApp.swift b/Shared/WriteFreely_MultiPlatformApp.swift index 4bc9109..2b249d3 100644 --- a/Shared/WriteFreely_MultiPlatformApp.swift +++ b/Shared/WriteFreely_MultiPlatformApp.swift @@ -55,6 +55,7 @@ struct WriteFreely_MultiPlatformApp: App { .keyboardShortcut("r", modifiers: [.command]) } SidebarCommands() + PostCommands(model: model) CommandGroup(after: .help) { Button("Visit Support Forum") { #if os(macOS) diff --git a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj index f94b76a..d11a2f9 100644 --- a/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj +++ b/WriteFreely-MultiPlatform.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 1765F62A24E18EA200C9EBF0 /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1765F62924E18EA200C9EBF0 /* SidebarView.swift */; }; 1765F62B24E18EA200C9EBF0 /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1765F62924E18EA200C9EBF0 /* SidebarView.swift */; }; 17681E412519410E00D394AE /* UINavigationController+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17681E402519410E00D394AE /* UINavigationController+Appearance.swift */; }; + 1780F6EF25895EDB00FE45FF /* PostCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1780F6EE25895EDB00FE45FF /* PostCommands.swift */; }; 17A5388824DDA31F00DEFF9A /* MacAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388724DDA31F00DEFF9A /* MacAccountView.swift */; }; 17A5388C24DDC83F00DEFF9A /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388B24DDC83F00DEFF9A /* AccountModel.swift */; }; 17A5388F24DDEC7400DEFF9A /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388D24DDEC7400DEFF9A /* AccountView.swift */; }; @@ -143,6 +144,7 @@ 1756DC0024FEE18400207AB8 /* WFACollection+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WFACollection+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; }; 1765F62924E18EA200C9EBF0 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = ""; }; 17681E402519410E00D394AE /* UINavigationController+Appearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Appearance.swift"; sourceTree = ""; }; + 1780F6EE25895EDB00FE45FF /* PostCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCommands.swift; sourceTree = ""; }; 17A5388724DDA31F00DEFF9A /* MacAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAccountView.swift; sourceTree = ""; }; 17A5388B24DDC83F00DEFF9A /* AccountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountModel.swift; sourceTree = ""; }; 17A5388D24DDEC7400DEFF9A /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = ""; }; @@ -325,6 +327,7 @@ isa = PBXGroup; children = ( 17BC617825715068003363CA /* ActivePostToolbarView.swift */, + 1780F6EE25895EDB00FE45FF /* PostCommands.swift */, ); path = Navigation; sourceTree = ""; @@ -781,6 +784,7 @@ 1756DC0224FEE18400207AB8 /* WFACollection+CoreDataClass.swift in Sources */, 1756DBB424FECDBB00207AB8 /* PostEditorStatusToolbarView.swift in Sources */, 17A5388F24DDEC7400DEFF9A /* AccountView.swift in Sources */, + 1780F6EF25895EDB00FE45FF /* PostCommands.swift in Sources */, 170DFA35251BBC44001D82A0 /* PostEditorModel.swift in Sources */, 1756AE7524CB26FA00FD7257 /* PostCellView.swift in Sources */, 17A5388824DDA31F00DEFF9A /* MacAccountView.swift in Sources */, diff --git a/macOS/Navigation/ActivePostToolbarView.swift b/macOS/Navigation/ActivePostToolbarView.swift index f34fa8f..57f8b73 100644 --- a/macOS/Navigation/ActivePostToolbarView.swift +++ b/macOS/Navigation/ActivePostToolbarView.swift @@ -5,28 +5,101 @@ struct ActivePostToolbarView: View { @ObservedObject var activePost: WFAPost @State private var isPresentingSharingServicePicker: Bool = false + @State private var selectedCollection: WFACollection? + + @FetchRequest( + entity: WFACollection.entity(), + sortDescriptors: [NSSortDescriptor(keyPath: \WFACollection.title, ascending: true)] + ) var collections: FetchedResults + var body: some View { - HStack(spacing: 16) { - PostEditorStatusToolbarView(post: activePost) - HStack(spacing: 4) { - Button( - action: { - self.isPresentingSharingServicePicker = true - }, - label: { Image(systemName: "square.and.arrow.up") } - ) - .disabled(activePost.status == PostStatus.local.rawValue) - .popover(isPresented: $isPresentingSharingServicePicker) { - PostEditorSharingPicker( - isPresented: $isPresentingSharingServicePicker, - sharingItems: createPostUrl() - ) - .frame(width: .zero, height: .zero) + HStack { + if model.account.isLoggedIn && + activePost.status != PostStatus.local.rawValue && + !(activePost.wasDeletedFromServer || activePost.hasNewerRemoteCopy) { + Section(header: Text("Move To:")) { + Picker(selection: $selectedCollection, label: Text("Move To…"), content: { + Text("\(model.account.server == "https://write.as" ? "Anonymous" : "Drafts")") + .tag(nil as WFACollection?) + Divider() + ForEach(collections) { collection in + Text("\(collection.title)").tag(collection as WFACollection?) + } + }) + } + } + PostEditorStatusToolbarView(post: activePost) + .frame(minWidth: 50, alignment: .center) + .layoutPriority(1) + .padding(.horizontal) + if activePost.status == PostStatus.local.rawValue { + Menu(content: { + Label("Publish To:", systemImage: "paperplane") + Divider() + Button(action: { + if model.account.isLoggedIn { + withAnimation { + activePost.collectionAlias = nil + publishPost(activePost) + } + } else { + openSettingsWindow() + } + }, label: { + Text("\(model.account.server == "https://write.as" ? "Anonymous" : "Drafts")") + }) + ForEach(collections) { collection in + Button(action: { + if model.account.isLoggedIn { + withAnimation { + activePost.collectionAlias = collection.alias + publishPost(activePost) + } + } else { + openSettingsWindow() + } + }, label: { + Text("\(collection.title)") + }) + } + }, label: { + Label("Publish…", systemImage: "paperplane") + }) + .disabled(activePost.body.isEmpty) + } else { + HStack(spacing: 4) { + Button( + action: { + self.isPresentingSharingServicePicker = true + }, + label: { Image(systemName: "square.and.arrow.up") } + ) + .disabled(activePost.status == PostStatus.local.rawValue) + .popover(isPresented: $isPresentingSharingServicePicker) { + PostEditorSharingPicker( + isPresented: $isPresentingSharingServicePicker, + sharingItems: createPostUrl() + ) + .frame(width: .zero, height: .zero) + } + Button(action: { publishPost(activePost) }, label: { Image(systemName: "paperplane") }) + .disabled(activePost.body.isEmpty || activePost.status == PostStatus.published.rawValue) } - Button(action: { publishPost(activePost) }, label: { Image(systemName: "paperplane") }) - .disabled(activePost.body.isEmpty || activePost.status == PostStatus.published.rawValue) } } + .onAppear(perform: { + self.selectedCollection = collections.first { $0.alias == activePost.collectionAlias } + }) + .onChange(of: selectedCollection, perform: { [selectedCollection] newCollection in + if activePost.collectionAlias == newCollection?.alias { + return + } else { + withAnimation { + activePost.collectionAlias = newCollection?.alias + model.move(post: activePost, from: selectedCollection, to: newCollection) + } + } + }) } private func createPostUrl() -> [Any] { @@ -44,4 +117,9 @@ struct ActivePostToolbarView: View { model.publish(post: post) } } + + private func openSettingsWindow() { + guard let menuItem = NSApplication.shared.mainMenu?.item(at: 0)?.submenu?.item(at: 2) else { return } + NSApplication.shared.sendAction(menuItem.action!, to: menuItem.target, from: nil) + } } diff --git a/macOS/Navigation/PostCommands.swift b/macOS/Navigation/PostCommands.swift new file mode 100644 index 0000000..2b2d45a --- /dev/null +++ b/macOS/Navigation/PostCommands.swift @@ -0,0 +1,25 @@ +import SwiftUI + +struct PostCommands: Commands { + @ObservedObject var model: WriteFreelyModel + + var body: some Commands { + CommandMenu("Post") { + Group { + Button(action: sendPostUrlToPasteboard, label: { Text("Copy Link To Published Post") }) + .disabled(model.selectedPost?.status == PostStatus.local.rawValue) + } + .disabled(model.selectedPost == nil || !model.account.isLoggedIn) + } + } + + private func sendPostUrlToPasteboard() { + guard let activePost = model.selectedPost else { return } + guard let postId = activePost.postId else { return } + guard let urlString = activePost.slug != nil ? + "\(model.account.server)/\((activePost.collectionAlias)!)/\((activePost.slug)!)" : + "\(model.account.server)/\((postId))" else { return } + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(urlString, forType: .string) + } +}