diff options
| author | Dustin Mierau <dustin@mierau.me> | 2024-05-03 14:05:56 -0700 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2024-05-03 14:05:56 -0700 |
| commit | 0471c4c4594f0406e85687ef84a29d99616c16cf (patch) | |
| tree | 9ab5473eb5bce415dcffad34869ec222af37c146 /Hotline | |
| parent | 5d61fbfd67c3f59bcffa997590d1bc7d41e61528 (diff) | |
Add unread badge to public chat in sidebar. Display server messages.
Diffstat (limited to 'Hotline')
| -rw-r--r-- | Hotline/Models/Hotline.swift | 6 | ||||
| -rw-r--r-- | Hotline/macOS/ChatView.swift | 12 | ||||
| -rw-r--r-- | Hotline/macOS/ServerView.swift | 48 |
3 files changed, 48 insertions, 18 deletions
diff --git a/Hotline/Models/Hotline.swift b/Hotline/Models/Hotline.swift index 564845a..ccc23c1 100644 --- a/Hotline/Models/Hotline.swift +++ b/Hotline/Models/Hotline.swift @@ -136,6 +136,7 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { var transfers: [TransferInfo] = [] var downloads: [HotlineFileClient] = [] var unreadInstantMessages: [UInt16:UInt16] = [:] + var unreadPublicChat: Bool = false @ObservationIgnored var bannerClient: HotlineFileClient? #if os(macOS) @@ -226,6 +227,10 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { } } + func markPublicChatAsRead() { + self.unreadPublicChat = false + } + func hasUnreadInstantMessages(userID: UInt16) -> Bool { return self.unreadInstantMessages[userID] != nil } @@ -737,6 +742,7 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { SoundEffectPlayer.shared.playSoundEffect(.chatMessage) } self.chat.append(ChatMessage(text: message, type: .message, date: Date())) + self.unreadPublicChat = true } func hotlineReceivedUserList(users: [HotlineUser]) { diff --git a/Hotline/macOS/ChatView.swift b/Hotline/macOS/ChatView.swift index 951544f..5bd626f 100644 --- a/Hotline/macOS/ChatView.swift +++ b/Hotline/macOS/ChatView.swift @@ -67,6 +67,13 @@ struct ChatView: View { .clipShape(RoundedRectangle(cornerRadius: 8)) .padding(.bottom, 16) } + // MARK: Server Message + else if msg.type == .server { + Text(msg.text) + .lineSpacing(4) + .multilineTextAlignment(.leading) + .textSelection(.enabled) + } // MARK: Status else if msg.type == .status { HStack { @@ -141,7 +148,10 @@ struct ChatView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) .defaultScrollAnchor(.bottom) .onChange(of: model.chat.count) { - reader.scrollTo(bottomID, anchor: .bottom) + withAnimation(.easeOut(duration: 0.15).delay(0.25)) { + reader.scrollTo(bottomID, anchor: .bottom) + } + model.markPublicChatAsRead() } .onAppear { reader.scrollTo(bottomID, anchor: .bottom) diff --git a/Hotline/macOS/ServerView.swift b/Hotline/macOS/ServerView.swift index 01651d3..190bcc2 100644 --- a/Hotline/macOS/ServerView.swift +++ b/Hotline/macOS/ServerView.swift @@ -29,14 +29,12 @@ struct ServerMenuItem: Identifiable, Hashable { let type: ServerNavigationType let name: String let image: String - let selectedImage: String - init(type: ServerNavigationType, name: String, image: String, selectedImage: String) { + init(type: ServerNavigationType, name: String, image: String) { self.id = UUID() self.type = type self.name = name self.image = image - self.selectedImage = selectedImage } func hash(into hasher: inout Hasher) { @@ -62,6 +60,7 @@ struct ServerMenuItem: Identifiable, Hashable { struct ListItemView: View { let icon: String let title: String + let unread: Bool var body: some View { HStack { @@ -71,6 +70,15 @@ struct ListItemView: View { .frame(width: 16, height: 16) .padding(.leading, 4) Text(title) + .lineLimit(1) + .truncationMode(.tail) + Spacer() + if unread { + Circle() + .frame(width: 6, height: 6) + .padding(EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 2)) + .opacity(0.5) + } } } } @@ -138,10 +146,10 @@ struct ServerView: View { @Binding var server: Server static var menuItems: [ServerMenuItem] = [ - ServerMenuItem(type: .chat, name: "Chat", image: "bubble.fill", selectedImage: "bubble.fill"), - ServerMenuItem(type: .board, name: "Board", image: "pin.fill", selectedImage: "pin.fill"), - ServerMenuItem(type: .news, name: "News", image: "newspaper.fill", selectedImage: "newspaper.fill"), - ServerMenuItem(type: .files, name: "Files", image: "folder.fill", selectedImage: "folder.fill"), + 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"), ] enum FocusFields { @@ -340,8 +348,12 @@ struct ServerView: View { var navigationList: some View { List(selection: $state.selection) { ForEach(ServerView.menuItems) { menuItem in - ListItemView(icon: state.selection == menuItem.type ? menuItem.selectedImage : menuItem.image, title: menuItem.name) - .tag(menuItem.type) + if menuItem.type == .chat { + ListItemView(icon: menuItem.image, title: menuItem.name, unread: model.unreadPublicChat).tag(menuItem.type) + } + else { + ListItemView(icon: menuItem.image, title: menuItem.name, unread: false).tag(menuItem.type) + } } if model.transfers.count > 0 { @@ -350,14 +362,16 @@ struct ServerView: View { if model.users.count > 0 { self.usersSection - .onChange(of: state.selection) { - switch(state.selection) { - case .user(let userID): - model.markInstantMessagesAsRead(userID: userID) - default: - break - } - } + } + } + .onChange(of: state.selection) { + switch(state.selection) { + case .chat: + model.markPublicChatAsRead() + case .user(let userID): + model.markInstantMessagesAsRead(userID: userID) + default: + break } } } |