Source code for the WriteFreely SwiftUI app for iOS, iPadOS, and macOS
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

153 linhas
6.8 KiB

  1. import SwiftUI
  2. struct ActivePostToolbarView: View {
  3. @EnvironmentObject var model: WriteFreelyModel
  4. @ObservedObject var activePost: WFAPost
  5. @State private var isPresentingSharingServicePicker: Bool = false
  6. @State private var selectedCollection: WFACollection?
  7. @FetchRequest(
  8. entity: WFACollection.entity(),
  9. sortDescriptors: [NSSortDescriptor(keyPath: \WFACollection.title, ascending: true)]
  10. ) var collections: FetchedResults<WFACollection>
  11. var body: some View {
  12. HStack {
  13. if model.account.isLoggedIn &&
  14. activePost.status != PostStatus.local.rawValue &&
  15. !(activePost.wasDeletedFromServer || activePost.hasNewerRemoteCopy) {
  16. Section(header: Text("Move To:")) {
  17. Picker(selection: $selectedCollection, label: Text("Move To…"), content: {
  18. Text("\(model.account.server == "https://write.as" ? "Anonymous" : "Drafts")")
  19. .tag(nil as WFACollection?)
  20. Divider()
  21. ForEach(collections) { collection in
  22. Text("\(collection.title)").tag(collection as WFACollection?)
  23. }
  24. })
  25. }
  26. }
  27. PostEditorStatusToolbarView(post: activePost)
  28. .frame(minWidth: 50, alignment: .center)
  29. .layoutPriority(1)
  30. .padding(.horizontal)
  31. if activePost.status == PostStatus.edited.rawValue {
  32. Button(action: {
  33. model.editor.postToUpdate = activePost
  34. model.updateFromServer(post: activePost)
  35. model.selectedPost = nil
  36. }, label: {
  37. Image(systemName: "clock.arrow.circlepath")
  38. .accessibilityLabel(Text("Revert post"))
  39. .accessibilityHint(Text("Replace the edited post with the published version from the server"))
  40. })
  41. }
  42. if activePost.status == PostStatus.local.rawValue {
  43. Menu(content: {
  44. Label("Publish To:", systemImage: "paperplane")
  45. Divider()
  46. Button(action: {
  47. if model.account.isLoggedIn {
  48. withAnimation {
  49. activePost.collectionAlias = nil
  50. publishPost(activePost)
  51. }
  52. } else {
  53. openSettingsWindow()
  54. }
  55. }, label: {
  56. Text("\(model.account.server == "https://write.as" ? "Anonymous" : "Drafts")")
  57. })
  58. ForEach(collections) { collection in
  59. Button(action: {
  60. if model.account.isLoggedIn {
  61. withAnimation {
  62. activePost.collectionAlias = collection.alias
  63. publishPost(activePost)
  64. }
  65. } else {
  66. openSettingsWindow()
  67. }
  68. }, label: {
  69. Text("\(collection.title)")
  70. })
  71. }
  72. }, label: {
  73. Label("Publish…", systemImage: "paperplane")
  74. })
  75. .disabled(model.selectedPost?.body.isEmpty ?? true)
  76. .help("Publish the post to the web.\(model.account.isLoggedIn ? "" : " You must be logged in to do this.")") // swiftlint:disable:this line_length
  77. } else {
  78. HStack(spacing: 4) {
  79. Button(
  80. action: {
  81. self.isPresentingSharingServicePicker = true
  82. },
  83. label: { Image(systemName: "square.and.arrow.up") }
  84. )
  85. .disabled(activePost.status == PostStatus.local.rawValue)
  86. .help("Copy the post's URL to your Mac's pasteboard.")
  87. .background(
  88. PostEditorSharingPicker(
  89. isPresented: $isPresentingSharingServicePicker,
  90. sharingItems: createPostUrl()
  91. )
  92. )
  93. Button(action: { publishPost(activePost) }, label: { Image(systemName: "paperplane") })
  94. .disabled(activePost.body.isEmpty || activePost.status == PostStatus.published.rawValue)
  95. .help("Publish the post to the web.\(model.account.isLoggedIn ? "" : " You must be logged in to do this.")") // swiftlint:disable:this line_length
  96. }
  97. }
  98. }
  99. .onAppear(perform: {
  100. self.selectedCollection = collections.first { $0.alias == activePost.collectionAlias }
  101. })
  102. .onChange(of: selectedCollection, perform: { [selectedCollection] newCollection in
  103. if activePost.collectionAlias == newCollection?.alias {
  104. return
  105. } else {
  106. withAnimation {
  107. activePost.collectionAlias = newCollection?.alias
  108. model.move(post: activePost, from: selectedCollection, to: newCollection)
  109. }
  110. }
  111. })
  112. }
  113. private func createPostUrl() -> [NSURL] {
  114. guard let postId = model.selectedPost?.postId else { return [] }
  115. var urlString: String
  116. if let postSlug = model.selectedPost?.slug,
  117. let postCollectionAlias = model.selectedPost?.collectionAlias {
  118. // This post is in a collection, so share the URL as baseURL/postSlug
  119. let urls = collections.filter { $0.alias == postCollectionAlias }
  120. let baseURL = urls.first?.url ?? "\(model.account.server)/\(postCollectionAlias)/"
  121. urlString = "\(baseURL)\(postSlug)"
  122. } else {
  123. // This is a draft post, so share the URL as server/postID
  124. urlString = "\(model.account.server)/\(postId)"
  125. }
  126. guard let data = URL(string: urlString) else { return [] }
  127. return [data as NSURL]
  128. }
  129. private func publishPost(_ post: WFAPost) {
  130. if post != model.selectedPost {
  131. return
  132. }
  133. DispatchQueue.main.async {
  134. LocalStorageManager.standard.saveContext()
  135. model.publish(post: post)
  136. }
  137. model.editor.setInitialValues(for: post)
  138. }
  139. private func openSettingsWindow() {
  140. guard let menuItem = NSApplication.shared.mainMenu?.item(at: 0)?.submenu?.item(at: 2) else { return }
  141. NSApplication.shared.sendAction(menuItem.action!, to: menuItem.target, from: nil)
  142. }
  143. }