aboutsummaryrefslogtreecommitdiff
path: root/Hotline
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2024-05-03 14:05:56 -0700
committerDustin Mierau <dustin@mierau.me>2024-05-03 14:05:56 -0700
commit0471c4c4594f0406e85687ef84a29d99616c16cf (patch)
tree9ab5473eb5bce415dcffad34869ec222af37c146 /Hotline
parent5d61fbfd67c3f59bcffa997590d1bc7d41e61528 (diff)
Add unread badge to public chat in sidebar. Display server messages.
Diffstat (limited to 'Hotline')
-rw-r--r--Hotline/Models/Hotline.swift6
-rw-r--r--Hotline/macOS/ChatView.swift12
-rw-r--r--Hotline/macOS/ServerView.swift48
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
}
}
}