diff options
| author | Dustin Mierau <dustin@mierau.me> | 2024-05-13 14:12:42 -0700 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2024-05-13 14:12:42 -0700 |
| commit | f04aef1044687253fbc56a0db0f3a905540eb03d (patch) | |
| tree | bde1890ba122d7a04a3cde98fb5fd053ca181a9d /Hotline | |
| parent | 67050d06a3bc2d28e969cf1388eafa95400c35a3 (diff) | |
Refined news and file listing. Tweaked news icons. New tracker window icons. New server toolbar globe icon. Fixing disabling of toolbar buttons in files.
Diffstat (limited to 'Hotline')
23 files changed, 254 insertions, 140 deletions
diff --git a/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle.png b/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle.png Binary files differindex ea6fe08..b0f4ee4 100644 --- a/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle.png +++ b/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle.png diff --git a/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle@2x.png b/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle@2x.png Binary files differindex 8271cc3..4728426 100644 --- a/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle@2x.png +++ b/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle@2x.png diff --git a/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle@3x.png b/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle@3x.png Binary files differindex b018b6d..cc7e0a2 100644 --- a/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle@3x.png +++ b/Hotline/Assets.xcassets/News Bundle.imageset/News Bundle@3x.png diff --git a/Hotline/Assets.xcassets/News Category.imageset/News Category.png b/Hotline/Assets.xcassets/News Category.imageset/News Category.png Binary files differindex cc8554b..5f58a1e 100644 --- a/Hotline/Assets.xcassets/News Category.imageset/News Category.png +++ b/Hotline/Assets.xcassets/News Category.imageset/News Category.png diff --git a/Hotline/Assets.xcassets/News Category.imageset/News Category@2x.png b/Hotline/Assets.xcassets/News Category.imageset/News Category@2x.png Binary files differindex 28053fd..d3eff96 100644 --- a/Hotline/Assets.xcassets/News Category.imageset/News Category@2x.png +++ b/Hotline/Assets.xcassets/News Category.imageset/News Category@2x.png diff --git a/Hotline/Assets.xcassets/News Category.imageset/News Category@3x.png b/Hotline/Assets.xcassets/News Category.imageset/News Category@3x.png Binary files differindex 0c968bb..371d15a 100644 --- a/Hotline/Assets.xcassets/News Category.imageset/News Category@3x.png +++ b/Hotline/Assets.xcassets/News Category.imageset/News Category@3x.png diff --git a/Hotline/Assets.xcassets/Server Large.imageset/Contents.json b/Hotline/Assets.xcassets/Server Large.imageset/Contents.json new file mode 100644 index 0000000..0c15637 --- /dev/null +++ b/Hotline/Assets.xcassets/Server Large.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Server Large.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Server Large@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Server Large@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Hotline/Assets.xcassets/Server Large.imageset/Server Large.png b/Hotline/Assets.xcassets/Server Large.imageset/Server Large.png Binary files differnew file mode 100644 index 0000000..25a97b0 --- /dev/null +++ b/Hotline/Assets.xcassets/Server Large.imageset/Server Large.png diff --git a/Hotline/Assets.xcassets/Server Large.imageset/Server Large@2x.png b/Hotline/Assets.xcassets/Server Large.imageset/Server Large@2x.png Binary files differnew file mode 100644 index 0000000..63fe295 --- /dev/null +++ b/Hotline/Assets.xcassets/Server Large.imageset/Server Large@2x.png diff --git a/Hotline/Assets.xcassets/Server Large.imageset/Server Large@3x.png b/Hotline/Assets.xcassets/Server Large.imageset/Server Large@3x.png Binary files differnew file mode 100644 index 0000000..7b8cd6e --- /dev/null +++ b/Hotline/Assets.xcassets/Server Large.imageset/Server Large@3x.png diff --git a/Hotline/Assets.xcassets/Server.imageset/Contents.json b/Hotline/Assets.xcassets/Server.imageset/Contents.json new file mode 100644 index 0000000..0bfbf05 --- /dev/null +++ b/Hotline/Assets.xcassets/Server.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Server.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Server@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Server@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Hotline/Assets.xcassets/Server.imageset/Server.png b/Hotline/Assets.xcassets/Server.imageset/Server.png Binary files differnew file mode 100644 index 0000000..3b63a79 --- /dev/null +++ b/Hotline/Assets.xcassets/Server.imageset/Server.png diff --git a/Hotline/Assets.xcassets/Server.imageset/Server@2x.png b/Hotline/Assets.xcassets/Server.imageset/Server@2x.png Binary files differnew file mode 100644 index 0000000..ede8ce0 --- /dev/null +++ b/Hotline/Assets.xcassets/Server.imageset/Server@2x.png diff --git a/Hotline/Assets.xcassets/Server.imageset/Server@3x.png b/Hotline/Assets.xcassets/Server.imageset/Server@3x.png Binary files differnew file mode 100644 index 0000000..eb3e466 --- /dev/null +++ b/Hotline/Assets.xcassets/Server.imageset/Server@3x.png diff --git a/Hotline/Assets.xcassets/Tracker.imageset/Contents.json b/Hotline/Assets.xcassets/Tracker.imageset/Contents.json new file mode 100644 index 0000000..7006942 --- /dev/null +++ b/Hotline/Assets.xcassets/Tracker.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Tracker.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Tracker@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Tracker@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Hotline/Assets.xcassets/Tracker.imageset/Tracker.png b/Hotline/Assets.xcassets/Tracker.imageset/Tracker.png Binary files differnew file mode 100644 index 0000000..6fc5957 --- /dev/null +++ b/Hotline/Assets.xcassets/Tracker.imageset/Tracker.png diff --git a/Hotline/Assets.xcassets/Tracker.imageset/Tracker@2x.png b/Hotline/Assets.xcassets/Tracker.imageset/Tracker@2x.png Binary files differnew file mode 100644 index 0000000..ba3b2bb --- /dev/null +++ b/Hotline/Assets.xcassets/Tracker.imageset/Tracker@2x.png diff --git a/Hotline/Assets.xcassets/Tracker.imageset/Tracker@3x.png b/Hotline/Assets.xcassets/Tracker.imageset/Tracker@3x.png Binary files differnew file mode 100644 index 0000000..32e9056 --- /dev/null +++ b/Hotline/Assets.xcassets/Tracker.imageset/Tracker@3x.png diff --git a/Hotline/macOS/FilesView.swift b/Hotline/macOS/FilesView.swift index 133e1fd..e3f5118 100644 --- a/Hotline/macOS/FilesView.swift +++ b/Hotline/macOS/FilesView.swift @@ -11,7 +11,7 @@ struct FileView: View { let depth: Int var body: some View { - HStack { + HStack(alignment: .center, spacing: 0) { if file.isFolder { Button { if file.isFolder { @@ -26,16 +26,20 @@ struct FileView: View { .buttonStyle(.plain) .frame(width: 10) .padding(.leading, 4) + .padding(.trailing, 8) } else { - HStack { - - }.frame(width: 10) + Spacer() + .frame(width: 10) .padding(.leading, 4) + .padding(.trailing, 8) } + HStack(alignment: .center) { if file.isUnavailable { - Image(systemName: "questionmark.app.fill").opacity(0.5) + Image(systemName: "questionmark.app.fill") + .frame(width: 16, height: 16) + .opacity(0.5) } else if file.isFolder { FolderIconView() @@ -46,8 +50,13 @@ struct FileView: View { .frame(width: 16, height: 16) } } - .frame(width: 15) - Text(file.name).lineLimit(1).truncationMode(.tail).opacity(file.isUnavailable ? 0.5 : 1.0) + .frame(width: 16) + .padding(.trailing, 6) + + Text(file.name) + .lineLimit(1) + .truncationMode(.tail) + .opacity(file.isUnavailable ? 0.5 : 1.0) if file.isFolder && loading { ProgressView().controlSize(.small).padding([.leading, .trailing], 1) @@ -55,9 +64,10 @@ struct FileView: View { Spacer() if !file.isUnavailable { if file.isFolder { - Text("^[\(file.fileSize) file](inflect: true)") + Text("^[\(file.fileSize) \("file")](inflect: true)") .foregroundStyle(.secondary) .lineLimit(1) + .padding(.trailing, 6) } else { Text(formattedFileSize(file.fileSize)).foregroundStyle(.secondary).lineLimit(1) @@ -185,9 +195,10 @@ struct FilesView: View { ToolbarItem(placement: .primaryAction) { Button { } label: { - Label("Delete File", systemImage: "trash") + Label("Delete", systemImage: "trash") } .help("Delete") + .disabled(true) } ToolbarItem(placement: .primaryAction) { @@ -200,10 +211,10 @@ struct FilesView: View { } } } label: { - Label("Preview File", systemImage: "eye") + Label("Preview", systemImage: "eye") } - .help("Preview File") - .disabled(selection?.isPreviewable == false) + .help("Preview") + .disabled(selection == nil || selection?.isPreviewable == false) } ToolbarItem(placement: .primaryAction) { @@ -214,9 +225,10 @@ struct FilesView: View { } } } label: { - Label("Get File Info", systemImage: "info.circle") + Label("Get Info", systemImage: "info.circle") } - .help("Get File Info") + .help("Get Info") + .disabled(selection == nil) } ToolbarItem(placement: .primaryAction) { @@ -225,9 +237,10 @@ struct FilesView: View { model.downloadFile(s.name, path: s.path) } } label: { - Label("Download File", systemImage: "square.and.arrow.down") + Label("Download", systemImage: "arrow.down") } .help("Download") + .disabled(selection == nil || selection?.isFolder == true) } } } diff --git a/Hotline/macOS/NewsItemView.swift b/Hotline/macOS/NewsItemView.swift new file mode 100644 index 0000000..5cd9ce4 --- /dev/null +++ b/Hotline/macOS/NewsItemView.swift @@ -0,0 +1,135 @@ +import SwiftUI + +struct NewsItemView: View { + @Environment(Hotline.self) private var model: Hotline + + var news: NewsInfo + let depth: Int + + static var dateFormatter: DateFormatter = { + var dateFormatter = DateFormatter() + dateFormatter.dateStyle = .long + dateFormatter.timeStyle = .short + dateFormatter.timeZone = .gmt + return dateFormatter + }() + + static var relativeDateFormatter: RelativeDateTimeFormatter = { + var formatter = RelativeDateTimeFormatter() + formatter.unitsStyle = .full + formatter.dateTimeStyle = .named + formatter.formattingContext = .listItem + return formatter + }() + + var body: some View { + HStack(alignment: .center, spacing: 0) { + if news.expandable { + Button { + news.expanded.toggle() + } label: { + Text(Image(systemName: news.expanded ? "chevron.down" : "chevron.right")) + .bold() + .font(.system(size: 10)) + .opacity(0.5) + .frame(alignment: .center) + } + .buttonStyle(.plain) + .frame(width: 10) + .padding(.leading, 4) + .padding(.trailing, 8) + } + else { + Spacer() + .frame(width: 10) + .padding(.leading, 4) + .padding(.trailing, 8) + } + + // Tree indent + Spacer() + .frame(width: (CGFloat(depth) * 22)) + + switch news.type { + case .category: + Image("News Category") + .resizable() + .frame(width: 16, height: 16, alignment: .center) + .padding(.trailing, 6) + case .bundle: + Image("News Bundle") + .resizable() + .frame(width: 16, height: 16, alignment: .center) + .padding(.trailing, 6) + case .article: + EmptyView() + } + + Text(news.name) + .fontWeight((news.type == .bundle || news.type == .category || !news.read) ? .semibold : .regular) + .lineLimit(1) + .truncationMode(.tail) + + if news.type == .article && news.articleUsername != nil { + Text(news.articleUsername!) + .foregroundStyle(.secondary) + .lineLimit(1) + .padding(.leading, 8) + } + + Spacer() + + if news.type == .bundle || news.type == .category { + ZStack { +// Text("^[\(news.count) \(news.type == .bundle ? "Category" : "Post")](inflect: true)") + Text("\(news.count)") + .foregroundStyle(.clear) +// .font(.caption) + .lineLimit(1) + .padding([.leading, .trailing], 8) + .padding([.top, .bottom], 2) + .background(.secondary) + .clipShape(Capsule()) + + Text("\(news.count)") + .foregroundStyle(.white) +// .font(.caption) + .lineLimit(1) + .padding([.leading, .trailing], 8) + .padding([.top, .bottom], 2) + .blendMode(.destinationOut) + } + .drawingGroup(opaque: false) + } + if news.type == .article && news.articleUsername != nil { + if let d = news.articleDate { + Text(NewsItemView.relativeDateFormatter.localizedString(for: d, relativeTo: Date.now)) + .lineLimit(1) + .foregroundStyle(.secondary) + .padding(.trailing, 8) + } + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .onChange(of: news.expanded) { + guard news.expanded, news.type == .bundle || news.type == .category else { + return + } + + Task { + await model.getNewsList(at: news.path) + } + } + + if news.expanded { + ForEach(news.children.reversed(), id: \.self) { childNews in + NewsItemView(news: childNews, depth: self.depth + 1).tag(childNews.id) + } + } + } +} + +#Preview { + NewsItemView(news: NewsInfo(hotlineNewsArticle: HotlineNewsArticle(id: 0, parentID: 0, flags: 0, title: "Title", username: "username", date: Date.now, flavors: [("", 1)], path: ["Guest"])), depth: 0) + .environment(Hotline(trackerClient: HotlineTrackerClient(), client: HotlineClient())) +} diff --git a/Hotline/macOS/NewsView.swift b/Hotline/macOS/NewsView.swift index f440cb2..8df741f 100644 --- a/Hotline/macOS/NewsView.swift +++ b/Hotline/macOS/NewsView.swift @@ -244,120 +244,6 @@ struct NewsView: View { } } -struct NewsItemView: View { - @Environment(Hotline.self) private var model: Hotline - - var news: NewsInfo - let depth: Int - - static var dateFormatter: DateFormatter = { - var dateFormatter = DateFormatter() - dateFormatter.dateStyle = .long - dateFormatter.timeStyle = .short - dateFormatter.timeZone = .gmt - return dateFormatter - }() - - static var relativeDateFormatter: RelativeDateTimeFormatter = { - var formatter = RelativeDateTimeFormatter() - formatter.unitsStyle = .full - formatter.dateTimeStyle = .named - formatter.formattingContext = .listItem - return formatter - }() - - var body: some View { - HStack(alignment: .center, spacing: 6) { - if news.expandable { - Button { - news.expanded.toggle() - } label: { - Text(Image(systemName: news.expanded ? "chevron.down" : "chevron.right")) - .bold() - .font(.system(size: 10)) - .opacity(0.5) - .frame(alignment: .center) - } - .buttonStyle(.plain) - .frame(width: 10) - .padding(.leading, 4) - } - else { - Spacer() - .frame(width: 14) - } - - // Tree indent - Spacer() - .frame(width: (CGFloat(depth) * 22)) - - switch news.type { - case .category: - Image("News Category") - .resizable() - .frame(width: 16, height: 16) - case .bundle: - Image("News Bundle") - .resizable() - .frame(width: 16, height: 16) - case .article: - EmptyView() - } - - Text(news.name) - .fontWeight((news.type == .bundle || news.type == .category || !news.read) ? .semibold : .regular) - .lineLimit(1) - .truncationMode(.tail) - if news.type == .article && news.articleUsername != nil { - Text(news.articleUsername!).foregroundStyle(.secondary).lineLimit(1) - } - Spacer() - if news.type == .bundle || news.type == .category { - ZStack { - Text("^[\(news.count) \(news.type == .bundle ? "Category" : "Post")](inflect: true)") - .foregroundStyle(.clear) - .font(.caption2) - .lineLimit(1) - .padding([.leading, .trailing], 8) - .padding([.top, .bottom], 2) - .background(.tertiary) - .clipShape(Capsule()) - - Text("^[\(news.count) \(news.type == .bundle ? "Category" : "Post")](inflect: true)") - .foregroundStyle(.white) - .font(.caption2) - .lineLimit(1) - .padding([.leading, .trailing], 8) - .padding([.top, .bottom], 2) - .blendMode(.destinationOut) - } - .drawingGroup(opaque: false) - } - if news.type == .article && news.articleUsername != nil { - if let d = news.articleDate { - Text(NewsItemView.relativeDateFormatter.localizedString(for: d, relativeTo: Date.now)).lineLimit(1).foregroundStyle(.secondary) - } - } - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .onChange(of: news.expanded) { - guard news.expanded, news.type == .bundle || news.type == .category else { - return - } - - Task { - await model.getNewsList(at: news.path) - } - } - - if news.expanded { - ForEach(news.children.reversed(), id: \.self) { childNews in - NewsItemView(news: childNews, depth: self.depth + 1).tag(childNews.id) - } - } - } -} - #Preview { NewsView() .environment(Hotline(trackerClient: HotlineTrackerClient(), client: HotlineClient())) diff --git a/Hotline/macOS/ServerView.swift b/Hotline/macOS/ServerView.swift index 21d7cbb..9b4c044 100644 --- a/Hotline/macOS/ServerView.swift +++ b/Hotline/macOS/ServerView.swift @@ -146,10 +146,10 @@ struct ServerView: View { @Binding var server: Server static var menuItems: [ServerMenuItem] = [ - ServerMenuItem(type: .chat, name: "Chat", image: "bubble.fill"), - ServerMenuItem(type: .board, name: "Board", image: "pin.fill"), - ServerMenuItem(type: .news, name: "News", image: "newspaper.fill"), - ServerMenuItem(type: .files, name: "Files", image: "folder.fill"), + ServerMenuItem(type: .chat, name: "Chat", image: "bubble"), + ServerMenuItem(type: .board, name: "Board", image: "pin"), + ServerMenuItem(type: .news, name: "News", image: "newspaper"), + ServerMenuItem(type: .files, name: "Files", image: "folder"), ] enum FocusFields { @@ -197,12 +197,12 @@ struct ServerView: View { .onChange(of: Prefs.shared.automaticMessage) { sendPreferences() } .toolbar { ToolbarItem(placement: .navigation) { - Image(systemName: "globe.americas.fill") - .renderingMode(.template) + Image("Server Large") +// .renderingMode(.template) .resizable() .scaledToFit() - .frame(width: 18) + .frame(width: 24) .opacity(controlActiveState == .inactive ? 0.4 : 1.0) } } diff --git a/Hotline/macOS/TrackerView.swift b/Hotline/macOS/TrackerView.swift index a07d40c..f9755f8 100644 --- a/Hotline/macOS/TrackerView.swift +++ b/Hotline/macOS/TrackerView.swift @@ -311,17 +311,28 @@ struct TrackerItemView: View { .padding(.leading, 4) } - HStack(alignment: .center) { + HStack(alignment: .center, spacing: 0) { if let bookmark = item.bookmark { switch bookmark.type { case .tracker: - Image(systemName: "point.3.filled.connected.trianglepath.dotted") +// Image(systemName: "point.3.filled.connected.trianglepath.dotted") + Image("Tracker") + .resizable() + .scaledToFit() + .frame(width: 16, height: 16) case .server: - Image(systemName: "globe.americas.fill") + Image("Server") + .resizable() + .scaledToFit() + .frame(width: 16, height: 16) +// Image(systemName: "globe.americas.fill") } } else if let _ = item.server { - Image(systemName: "globe.americas.fill") + Image("Server") + .resizable() + .scaledToFit() + .frame(width: 16, height: 16) } } .frame(width: 15) |