diff options
| author | Dustin Mierau <dustin@mierau.me> | 2024-05-14 13:35:26 -0700 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2024-05-14 13:35:26 -0700 |
| commit | 87e979fb48da4a46e64544cea3e7b7d5fc32caa1 (patch) | |
| tree | 4c10cdba5a88544543bf45ae454f643decf1358c /Hotline/macOS | |
| parent | e9fcf8c3b14e77479a6fb471bb51cd82a008147c (diff) | |
Newsgroup posting now works. Added reload button to news. More work on server and user icons.
Diffstat (limited to 'Hotline/macOS')
| -rw-r--r-- | Hotline/macOS/AboutView.swift | 13 | ||||
| -rw-r--r-- | Hotline/macOS/ChatView.swift | 8 | ||||
| -rw-r--r-- | Hotline/macOS/FileDetailsView.swift | 2 | ||||
| -rw-r--r-- | Hotline/macOS/FilePreviewImageView.swift | 4 | ||||
| -rw-r--r-- | Hotline/macOS/FilesView.swift | 18 | ||||
| -rw-r--r-- | Hotline/macOS/HotlinePanelView.swift | 3 | ||||
| -rw-r--r-- | Hotline/macOS/NewsEditorView.swift | 189 | ||||
| -rw-r--r-- | Hotline/macOS/NewsView.swift | 65 | ||||
| -rw-r--r-- | Hotline/macOS/ServerMessageView.swift | 6 |
9 files changed, 201 insertions, 107 deletions
diff --git a/Hotline/macOS/AboutView.swift b/Hotline/macOS/AboutView.swift index 1cf7dc2..baafcf8 100644 --- a/Hotline/macOS/AboutView.swift +++ b/Hotline/macOS/AboutView.swift @@ -89,20 +89,21 @@ struct AboutView: View { VStack(alignment: .leading, spacing: 4) { Link(destination: URL(string: "https://github.com/mierau/hotline")!) { - HStack(alignment: .center) { - Text("GitHub Contributors") + HStack(alignment: .center, spacing: 4) { + Text("Contributors") .lineLimit(1) .font(.system(size: 16)) .fontWeight(.semibold) .foregroundStyle(.black) - .opacity(0.5) - + .opacity(0.75) + Image(systemName: "arrow.forward.circle.fill") .resizable() + .fontWeight(.bold) .scaledToFit() - .frame(width: 16, height: 16) + .frame(width: 12, height: 12) .foregroundStyle(.black) - .opacity(0.5) + .opacity(0.75) } } .padding(.top, 24) diff --git a/Hotline/macOS/ChatView.swift b/Hotline/macOS/ChatView.swift index 0875106..62bab69 100644 --- a/Hotline/macOS/ChatView.swift +++ b/Hotline/macOS/ChatView.swift @@ -49,12 +49,8 @@ struct ChatView: View { } // MARK: Server Message else if msg.type == .server { - HStack { - Spacer() - ServerMessageView(message: msg.text) - Spacer() - } - .padding(EdgeInsets(top: 2, leading: 0, bottom: 2, trailing: 0)) + ServerMessageView(message: msg.text) + .padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0)) } // MARK: Status else if msg.type == .status { diff --git a/Hotline/macOS/FileDetailsView.swift b/Hotline/macOS/FileDetailsView.swift index d3c10e8..aa7ab09 100644 --- a/Hotline/macOS/FileDetailsView.swift +++ b/Hotline/macOS/FileDetailsView.swift @@ -15,7 +15,7 @@ struct FileDetailsView: View { Form { HStack(alignment: .center){ FileIconView(filename: fd.name) - .frame(width: 16, height: 16) + .frame(width: 32, height: 32) TextField("", text: $filename) .disabled(!self.canRename()) } diff --git a/Hotline/macOS/FilePreviewImageView.swift b/Hotline/macOS/FilePreviewImageView.swift index ae9a825..b8ad37b 100644 --- a/Hotline/macOS/FilePreviewImageView.swift +++ b/Hotline/macOS/FilePreviewImageView.swift @@ -80,9 +80,9 @@ struct FilePreviewImageView: View { Button { let _ = preview?.data?.saveAsFileToDownloads(filename: info.name) } label: { - Label("Save Image...", systemImage: "photo.badge.arrow.down") + Label("Download Image...", systemImage: "arrow.down") } - .help("Save Image") + .help("Download Image") } ToolbarItem(placement: .primaryAction) { diff --git a/Hotline/macOS/FilesView.swift b/Hotline/macOS/FilesView.swift index e3f5118..a225549 100644 --- a/Hotline/macOS/FilesView.swift +++ b/Hotline/macOS/FilesView.swift @@ -192,18 +192,18 @@ struct FilesView: View { } } .toolbar { - ToolbarItem(placement: .primaryAction) { - Button { - } label: { - Label("Delete", systemImage: "trash") - } - .help("Delete") - .disabled(true) - } +// ToolbarItem(placement: .primaryAction) { +// Button { +// } label: { +// Label("Delete", systemImage: "trash") +// } +// .help("Delete") +// .disabled(true) +// } ToolbarItem(placement: .primaryAction) { Button { - if let s = selection, !s.isFolder { + if let s = selection, s.isPreviewable { model.previewFile(s.name, path: s.path) { info in if let info = info { openPreviewWindow(info) diff --git a/Hotline/macOS/HotlinePanelView.swift b/Hotline/macOS/HotlinePanelView.swift index d486761..69d00aa 100644 --- a/Hotline/macOS/HotlinePanelView.swift +++ b/Hotline/macOS/HotlinePanelView.swift @@ -4,9 +4,6 @@ struct HotlinePanelView: View { @Environment(\.openWindow) var openWindow @Environment(\.colorScheme) var colorScheme - @FocusedValue(\.activeHotlineModel) private var activeHotline: Hotline? - @FocusedValue(\.activeServerState) private var activeServerState: ServerState? - var body: some View { VStack(spacing: 0) { Image(nsImage: ApplicationState.shared.activeServerBanner ?? NSImage(named: "Default Banner")!) diff --git a/Hotline/macOS/NewsEditorView.swift b/Hotline/macOS/NewsEditorView.swift index 18c4a0d..ff33281 100644 --- a/Hotline/macOS/NewsEditorView.swift +++ b/Hotline/macOS/NewsEditorView.swift @@ -1,5 +1,9 @@ import SwiftUI -import UniformTypeIdentifiers + +private enum FocusFields { + case title + case body +} struct NewsEditorView: View { @Environment(\.controlActiveState) private var controlActiveState @@ -7,94 +11,145 @@ struct NewsEditorView: View { @Environment(\.dismiss) private var dismiss @Environment(Hotline.self) private var model: Hotline -// @Binding var article: NewsArticle? + let editorTitle: String + let isReply: Bool + let path: [String] + let parentID: UInt32 + @State var title: String = "" - @State var text: String = "" + @State private var text: String = "" + @State private var sending: Bool = false + + @FocusState private var focusedField: FocusFields? + + func sendArticle() async -> Bool { + sending = true + + let success = await model.postNewsArticle(title: title, body: text, at: path, parentID: parentID) + if success { + await model.getNewsList(at: path) + } + + sending = false + + return success + } var body: some View { - NavigationStack { - VStack(alignment: .leading, spacing: 0) { - HStack { - Button { - dismiss() - } label: { - Image(systemName: "xmark.circle.fill") - .resizable() - .scaledToFit() - } - .buttonStyle(.plain) - .frame(width: 16, height: 16) - .padding() - - Spacer() - + VStack(alignment: .leading, spacing: 0) { + HStack(alignment: .center, spacing: 0) { + Button { + dismiss() + } label: { + Image(systemName: "xmark") + .resizable() + .scaledToFit() + .frame(width: 14, height: 14) + .opacity(0.5) + } + .buttonStyle(.plain) + .frame(width: 16, height: 16) + + Spacer() + + if !isReply { + Image("News Category") + .resizable() + .scaledToFit() + .frame(width: 16, height: 16) + .padding(.trailing, 6) + } + + Text(editorTitle) + .fontWeight(.semibold) + .lineLimit(1) + .truncationMode(.middle) + + Spacer() + + if sending { + ProgressView() + .controlSize(.small) + .frame(width: 22, height: 22) + } + else { Button { + Task { + if await sendArticle() { + dismiss() + } + } } label: { - Image(systemName: "paperplane") + Image(systemName: "arrow.up.circle.fill") .resizable() + .renderingMode(.template) .scaledToFit() + .foregroundColor((title.isEmpty || text.isEmpty) ? .secondary : .accentColor) } .buttonStyle(.plain) - .frame(width: 16, height: 16) - .padding() + .frame(width: 22, height: 22) + .help("Post to Newsgroup") + .disabled(title.isEmpty || text.isEmpty) } + } + .frame(maxWidth: .infinity) + .padding([.leading, .top, .trailing]) + + TextField("Title", text: $title, axis: .vertical) + .textFieldStyle(.plain) + .lineLimit(3) + .padding() + .focusEffectDisabled() + .fontWeight(.semibold) .frame(maxWidth: .infinity) - TextField("Title", text: $title, axis: .vertical) - .textFieldStyle(.plain) + .border(Color.pink, width: 0) + .background(.tertiary.opacity(0.2)) + .clipShape(RoundedRectangle(cornerRadius: 8)) + .padding() + .focused($focusedField, equals: .title) + + Divider() + + TextEditor(text: $text) + .textEditorStyle(.plain) + .font(.system(size: 14, design: .monospaced)) + .lineSpacing(3) + .padding(16) + .contentMargins(.top, -16.0, for: .scrollIndicators) + .contentMargins(.bottom, -16.0, for: .scrollIndicators) + .contentMargins(.trailing, -16.0, for: .scrollIndicators) + .scrollClipDisabled() + .frame(maxWidth: .infinity, maxHeight: .infinity) + .clipped() + .focused($focusedField, equals: .body) + + HStack(alignment: .center) { + Spacer() + + Text(String("**bold** _italics_ [link name](url) ")) + .foregroundStyle(.secondary) + .font(.caption) + .fontDesign(.monospaced) + .lineLimit(1) + .truncationMode(.middle) .padding() - .focusEffectDisabled() - .font(.title) - .frame(maxWidth: .infinity) - .border(Color.pink, width: 0) - Divider() - TextEditor(text: $text) - .textEditorStyle(.plain) - .font(.system(size: 14)) - .lineSpacing(3) - .padding(16) - .contentMargins(.top, -16.0, for: .scrollIndicators) - .contentMargins(.bottom, -16.0, for: .scrollIndicators) - .contentMargins(.trailing, -16.0, for: .scrollIndicators) - .scrollClipDisabled() - .frame(maxWidth: .infinity, maxHeight: .infinity) + + Spacer() } + .frame(maxWidth: .infinity) + .background(.tertiary.opacity(0.15)) } .frame(minWidth: 300, idealWidth: 450, maxWidth: .infinity, minHeight: 300, idealHeight: 500, maxHeight: .infinity) .background(Color(nsColor: .textBackgroundColor)) .presentationCompactAdaptation(.sheet) .toolbarTitleDisplayMode(.inlineLarge) -// .toolbar { -// ToolbarItem(placement: .navigation) { -// Button("Post", action: {}) -// } -// ToolbarItem(placement: .automatic) { -// Button("Delete", action: { -// dismiss() -// }) -// } -// } - .task { -// if let info = info { -// preview = FilePreview(info: info) -// preview?.download() -// } - } .onAppear { -// if info == nil { -// Task { -// dismiss() -// } -// return -// } + if !title.isEmpty { + focusedField = .body + } } .onDisappear { -// preview?.cancel() dismiss() } -// .onChange(of: preview?.state) { -// if preview?.state == .failed { -// dismiss() -// } -// } } } diff --git a/Hotline/macOS/NewsView.swift b/Hotline/macOS/NewsView.swift index 8df741f..ed5d969 100644 --- a/Hotline/macOS/NewsView.swift +++ b/Hotline/macOS/NewsView.swift @@ -12,6 +12,8 @@ struct NewsView: View { @State private var splitHidden = SideHolder(.bottom) @State private var splitFraction = FractionHolder.usingUserDefaults(0.25, key: "News Split Fraction") @State private var editorOpen: Bool = false + @State private var replyOpen: Bool = false + @State private var loading: Bool = true var body: some View { Group { @@ -63,45 +65,82 @@ struct NewsView: View { } .task { if !model.newsLoaded { - let _ = await model.getNewsList() + await model.getNewsList() + loading = false } } } } .sheet(isPresented: $editorOpen) { - print("Sheet dismissed!") } content: { - NewsEditorView() - } - .toolbar { - ToolbarItem(placement: .primaryAction) { - Button { - - } label: { - Image(systemName: "trash") + if let selection = selection { + switch selection.type { + case .article, .category: + NewsEditorView(editorTitle: selection.path.last ?? "New Post", isReply: false, path: selection.path, parentID: 0) + default: + EmptyView() } } - + else { + EmptyView() + } + } + .sheet(isPresented: $replyOpen) { + } content: { + if let selection = selection, selection.type == .article { + NewsEditorView(editorTitle: "Reply to \(selection.articleUsername ?? "Post")", isReply: true, path: selection.path, parentID: UInt32(selection.articleID!), title: selection.name.replyToString()) + } + else { + EmptyView() + } + } + .toolbar { ToolbarItem(placement: .primaryAction) { Button { if selection?.type == .category || selection?.type == .article { editorOpen = true -// openWindow(id: "news-editor", value: NewsArticle(parentID: nil, path: selection.path, title: "", body: "")) } } label: { Image(systemName: "square.and.pencil") } + .help("New Post") .disabled(selection?.type != .category && selection?.type != .article) } ToolbarItem(placement: .primaryAction) { Button { - + if selection?.type == .article { + replyOpen = true + } } label: { Image(systemName: "arrowshape.turn.up.left") } + .help("Reply to Post") .disabled(selection?.type != .article) } + + ToolbarItem(placement: .primaryAction) { + Button { + if let selectionPath = selection?.path { + Task { + loading = true + await model.getNewsList(at: selectionPath) + loading = false + } + } + else { + Task { + loading = true + await model.getNewsList() + loading = false + } + } + } label: { + Image(systemName: "arrow.clockwise") + } + .help("Reload Newsgroup") + .disabled(loading) + } } } diff --git a/Hotline/macOS/ServerMessageView.swift b/Hotline/macOS/ServerMessageView.swift index 742703f..8413a87 100644 --- a/Hotline/macOS/ServerMessageView.swift +++ b/Hotline/macOS/ServerMessageView.swift @@ -15,9 +15,15 @@ struct ServerMessageView: View { .lineSpacing(4) .multilineTextAlignment(.leading) .textSelection(.enabled) + Spacer() } .padding() + .frame(maxWidth: .infinity) +#if os(iOS) .background(Color("Agreement Background")) +#elseif os(macOS) + .background(VisualEffectView(material: .titlebar, blendingMode: .withinWindow)) +#endif .clipShape(RoundedRectangle(cornerRadius: 8)) } } |