diff options
| author | Dustin Mierau <dustin@mierau.me> | 2023-12-11 23:32:28 -0800 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2023-12-11 23:32:28 -0800 |
| commit | 18c4b8643df09be4ce52a4b110ea21ac1ad053fc (patch) | |
| tree | 335d4f0a6b430b983b6f00567b91eb1ee6b16b4b /Hotline/Views | |
| parent | 84aaa9ef0a1be6986e38d7e4f58e07d3cb84979e (diff) | |
Move to new Hotline model for data observation and let client exist standalone.
Diffstat (limited to 'Hotline/Views')
| -rw-r--r-- | Hotline/Views/ChatView.swift | 38 | ||||
| -rw-r--r-- | Hotline/Views/FilesView.swift | 31 | ||||
| -rw-r--r-- | Hotline/Views/MessageBoardView.swift | 27 | ||||
| -rw-r--r-- | Hotline/Views/ServerView.swift | 36 | ||||
| -rw-r--r-- | Hotline/Views/TrackerView.swift | 214 | ||||
| -rw-r--r-- | Hotline/Views/UsersView.swift (renamed from Hotline/Views/UserListView.swift) | 16 |
6 files changed, 269 insertions, 93 deletions
diff --git a/Hotline/Views/ChatView.swift b/Hotline/Views/ChatView.swift index 1b3ba52..ce3942e 100644 --- a/Hotline/Views/ChatView.swift +++ b/Hotline/Views/ChatView.swift @@ -7,7 +7,8 @@ extension View { } struct ChatView: View { - @Environment(HotlineClient.self) private var hotline +// @Environment(HotlineClient.self) private var hotline + @Environment(Hotline.self) private var model: Hotline @Environment(\.colorScheme) var colorScheme @State var input: String = "" @@ -22,7 +23,7 @@ struct ChatView: View { ScrollView { ScrollViewReader { reader in LazyVStack(alignment: .leading) { - ForEach(hotline.chatMessages) { msg in + ForEach(model.chat) { msg in if msg.type == .agreement { VStack(alignment: .leading) { VStack(alignment: .leading, spacing: 0) { @@ -32,7 +33,7 @@ struct ChatView: View { .opacity(0.75) HStack { Spacer() - Text((hotline.server?.name ?? "") + " Server Agreement") + Text((model.server?.name ?? "") + " Server Agreement") .font(.caption) .fontWeight(.medium) .opacity(0.4) @@ -49,10 +50,21 @@ struct ChatView: View { } .padding() } + else if msg.type == .status { + HStack { + Spacer() + Text(msg.text) + .lineLimit(1) + .truncationMode(.middle) + .opacity(0.3) + Spacer() + } + .padding() + } else { HStack(alignment: .firstTextBaseline) { - if !msg.username.isEmpty { - Text("**\(msg.username):** \(msg.text)") + if let username = msg.username { + Text("**\(username):** \(msg.text)") } else { Text(msg.text) @@ -63,9 +75,9 @@ struct ChatView: View { .padding() } } - Text("").id(bottomID) + EmptyView().id(bottomID) } - .onChange(of: hotline.chatMessages.count) { + .onChange(of: model.chat.count) { withAnimation { reader.scrollTo(bottomID, anchor: .bottom) } @@ -92,14 +104,16 @@ struct ChatView: View { .lineLimit(1...5) .onSubmit { if !self.input.isEmpty { - hotline.sendChat(message: self.input) + model.sendChat(self.input) +// hotline.sendChat(message: self.input) } self.input = "" } .frame(maxWidth: .infinity) Button { if !self.input.isEmpty { - hotline.sendChat(message: self.input) + model.sendChat(self.input) +// hotline.sendChat(message: self.input) } self.input = "" } label: { @@ -114,12 +128,12 @@ struct ChatView: View { .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .principal) { - Text(hotline.server?.name ?? "") + Text(model.server?.name ?? "") .font(.headline) } ToolbarItem(placement: .navigationBarLeading) { Button { - hotline.disconnect() + model.disconnect() } label: { Text(Image(systemName: "xmark.circle.fill")) .symbolRenderingMode(.hierarchical) @@ -166,5 +180,5 @@ struct ChatView: View { #Preview { ChatView() - .environment(HotlineClient()) + .environment(Hotline(trackerClient: HotlineTrackerClient(), client: HotlineNewClient())) } diff --git a/Hotline/Views/FilesView.swift b/Hotline/Views/FilesView.swift index 23cd914..11db93c 100644 --- a/Hotline/Views/FilesView.swift +++ b/Hotline/Views/FilesView.swift @@ -2,16 +2,17 @@ import SwiftUI import UniformTypeIdentifiers struct FileView: View { - @Environment(HotlineClient.self) private var hotline +// @Environment(HotlineClient.self) private var hotline + @Environment(Hotline.self) private var model: Hotline @State var expanded = false - var file: HotlineFile + var file: FileInfo var body: some View { if file.isFolder { DisclosureGroup(isExpanded: $expanded) { - ForEach(file.files!) { childFile in + ForEach(file.children!) { childFile in FileView(file: childFile) .frame(height: 44) } @@ -27,9 +28,9 @@ struct FileView: View { } } .onChange(of: expanded) { - print("EXPANDED CHANGED") - - hotline.sendGetFileList(path: file.path) + Task { + await model.getFileList(path: file.path) + } } } else { @@ -48,7 +49,7 @@ struct FileView: View { static let byteFormatter = ByteCountFormatter() - private func formattedFileSize(_ fileSize: UInt32) -> String { + private func formattedFileSize(_ fileSize: UInt) -> String { // let bcf = ByteCountFormatter() FileView.byteFormatter.allowedUnits = [.useAll] FileView.byteFormatter.countStyle = .file @@ -83,35 +84,33 @@ struct FileView: View { } struct FilesView: View { - @Environment(HotlineClient.self) private var hotline +// @Environment(HotlineClient.self) private var hotline + @Environment(Hotline.self) private var model: Hotline @State var initialLoad = false var body: some View { NavigationStack { - List(hotline.fileList) { file in -// OutlineGroup(hotline.fileList, children: \.files, expanded: $expandedFolders) { file in + List(model.files) { file in FileView(file: file) .frame(height: 44) -// } } .task { if !initialLoad { - hotline.sendGetFileList(path: []) { - initialLoad = true - } + let _ = await model.getFileList() + initialLoad = true } } .listStyle(.plain) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .principal) { - Text(hotline.server?.name ?? "") + Text(model.server?.name ?? "") .font(.headline) } ToolbarItem(placement: .navigationBarLeading) { Button { - hotline.disconnect() + model.disconnect() } label: { Text(Image(systemName: "xmark.circle.fill")) .symbolRenderingMode(.hierarchical) diff --git a/Hotline/Views/MessageBoardView.swift b/Hotline/Views/MessageBoardView.swift index 04f51df..473162e 100644 --- a/Hotline/Views/MessageBoardView.swift +++ b/Hotline/Views/MessageBoardView.swift @@ -1,8 +1,9 @@ import SwiftUI struct MessageBoardView: View { - @Environment(HotlineState.self) private var appState - @Environment(HotlineClient.self) private var hotline + @Environment(Hotline.self) private var model: Hotline +// @Environment(HotlineState.self) private var appState +// @Environment(HotlineClient.self) private var hotline @State private var fetched = false @@ -11,7 +12,7 @@ struct MessageBoardView: View { NavigationStack { ScrollView { LazyVStack(alignment: .leading) { - ForEach(hotline.messageBoardMessages, id: \.self) { + ForEach(model.messageBoard, id: \.self) { Text($0) .lineLimit(100) .padding() @@ -23,23 +24,26 @@ struct MessageBoardView: View { } .task { if !fetched { - hotline.sendGetMessageBoard() { - fetched = true - } + let _ = await model.getMessageBoard() +// hotline.sendGetMessageBoard() { + fetched = true +// } } } .refreshable { - hotline.sendGetMessageBoard() + let _ = await model.getMessageBoard() +// hotline.sendGetMessageBoard() } .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .principal) { - Text(hotline.server?.name ?? "") + Text(model.server?.name ?? "") .font(.headline) } ToolbarItem(placement: .navigationBarLeading) { Button { - hotline.disconnect() + model.disconnect() +// hotline.disconnect() } label: { Text(Image(systemName: "xmark.circle.fill")) .symbolRenderingMode(.hierarchical) @@ -65,6 +69,7 @@ struct MessageBoardView: View { #Preview { MessageBoardView() - .environment(HotlineState()) - .environment(HotlineClient()) + .environment(Hotline(trackerClient: HotlineTrackerClient(), client: HotlineNewClient())) +// .environment(HotlineState()) +// .environment(HotlineClient()) } diff --git a/Hotline/Views/ServerView.swift b/Hotline/Views/ServerView.swift index 221156a..09ab877 100644 --- a/Hotline/Views/ServerView.swift +++ b/Hotline/Views/ServerView.swift @@ -1,28 +1,9 @@ import SwiftUI struct ServerView: View { - @Environment(HotlineClient.self) private var hotline + @Environment(Hotline.self) private var model: Hotline @Environment(\.colorScheme) var colorScheme - func connectionStatusTitle(status: HotlineClientStatus) -> String { - switch(status) { - case .disconnected: - return "Disconnected" - case .connecting: - return "Connecting" - case .connected: - return "Connected" - case .loggingIn: - return "Logging In" - case .loggedIn: - return "Logged In" - } - } - - func connectionProgress(status: HotlineClientStatus) -> Double { - return Double(status.rawValue) / Double(HotlineClientStatus.loggedIn.rawValue) - } - enum Tab { case chat, users, news, messageBoard, files } @@ -35,17 +16,19 @@ struct ServerView: View { } .tag(Tab.chat) - UserListView() + UsersView() .tabItem { Image(systemName: "person.2") } .tag(Tab.users) - NewsView() - .tabItem { - Image(systemName: "newspaper") - } - .tag(Tab.news) + if let v = model.serverVersion, v >= 150 { + NewsView() + .tabItem { + Image(systemName: "newspaper") + } + .tag(Tab.news) + } MessageBoardView() .tabItem { @@ -65,5 +48,4 @@ struct ServerView: View { #Preview { ServerView() - .environment(HotlineClient()) } diff --git a/Hotline/Views/TrackerView.swift b/Hotline/Views/TrackerView.swift index 2d2032e..2fc27c7 100644 --- a/Hotline/Views/TrackerView.swift +++ b/Hotline/Views/TrackerView.swift @@ -1,20 +1,172 @@ import SwiftUI +struct TrackerConnectView: View { + @Environment(Hotline.self) private var model: Hotline + @Environment(\.dismiss) var dismiss + @Environment(\.colorScheme) var colorScheme + + @State private var server: Server? + @State private var address = "" + @State private var login = "" + @State private var password = "" + @State private var connecting = false + + func connectionStatusToProgress(status: HotlineNewClientStatus) -> Double { + switch status { + case .disconnected: + return 0.0 + case .connecting: + return 0.1 + case .connected: + return 0.25 + case .loggingIn: + return 0.5 + case .loggedIn: + return 1.0 + } + } + + + + var body: some View { + VStack(alignment: .leading) { + if !connecting { + TextField("Server Address", text: $address) + .keyboardType(.URL) + .disableAutocorrection(true) + .frame(height: 48) + .textFieldStyle(.plain) + .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) + .background { + Color.black.cornerRadius(8).blendMode(.overlay) + } + TextField("Login", text: $login) + .disableAutocorrection(true) + .frame(height: 48) + .textFieldStyle(.plain) + .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) + .background { + Color.black.cornerRadius(8).blendMode(.overlay) + } + SecureField("Password", text: $password) + .disableAutocorrection(true) + .textFieldStyle(.plain) + .frame(height: 48) + .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) + .background { + Color.black.cornerRadius(8).blendMode(.overlay) + } + } + else { + ProgressView(value: connectionStatusToProgress(status: model.status)) + .frame(minHeight: 10) + .accentColor(colorScheme == .dark ? .white : .black) + } + + Spacer() + + HStack { + Button { + dismiss() + server = nil + model.disconnect() + } label: { + Text("Cancel") + } + .bold() + .padding(EdgeInsets(top: 16, leading: 24, bottom: 16, trailing: 24)) + .frame(maxWidth: .infinity) + .foregroundColor(colorScheme == .dark ? .white : .black) + .background( + colorScheme == .dark ? + LinearGradient(gradient: Gradient(colors: [Color(white: 0.4), Color(white: 0.3)]), startPoint: .top, endPoint: .bottom) + : + LinearGradient(gradient: Gradient(colors: [Color(white: 0.95), Color(white: 0.91)]), startPoint: .top, endPoint: .bottom) + ) + .overlay( + RoundedRectangle(cornerRadius: 10.0).stroke(.black, lineWidth: 3).opacity(colorScheme == .dark ? 0.0 : 0.2) + ) + .cornerRadius(10.0) + Button { + let s = Server(name: address, description: nil, address: address, port: Server.defaultPort, users: 0) + server = s + connecting = true + Task { + let loggedIn = await model.login(server: s, login: login, password: password, username: "bolt", iconID: 128) + if !loggedIn { + connecting = false + } + } + } label: { + Text("Connect") + } + .disabled(connecting) + .bold() + .padding(EdgeInsets(top: 16, leading: 24, bottom: 16, trailing: 24)) + .frame(maxWidth: .infinity) + .foregroundColor(colorScheme == .dark ? .white : .black) + .background( + colorScheme == .dark ? + LinearGradient(gradient: Gradient(colors: [Color(white: 0.4), Color(white: 0.3)]), startPoint: .top, endPoint: .bottom) + : + LinearGradient(gradient: Gradient(colors: [Color(white: 0.95), Color(white: 0.91)]), startPoint: .top, endPoint: .bottom) + ) + .overlay( + RoundedRectangle(cornerRadius: 10.0).stroke(.black, lineWidth: 3).opacity(colorScheme == .dark ? 0.0 : 0.2) + ) + .cornerRadius(10.0) + } + .padding() + } + .padding() + .onChange(of: model.status) { + print("MODEL STATUS CHANGED") + if model.server != nil && server != nil && model.server! == server! { + if model.status == .loggedIn { + dismiss() + } + else { + connecting = (model.status != .disconnected) + } + } + } + .toolbar { + ToolbarItem(placement: .principal) { + Text("Connect to Server") + .font(.headline) + } + } +// .presentationBackground(.regularMaterial, in: Color(uiColor: .systemGroupedBackground)) + .presentationBackground { + Color.clear + .background(Material.regular) + } + .presentationDetents([.height(300), .large]) + .presentationDragIndicator(.visible) + .presentationCornerRadius(20) + // .background(Color(uiColor: .systemGroupedBackground)) + } +} + struct TrackerView: View { // @Environment(\.modelContext) private var modelContext // @Query private var items: [Item] - @Environment(HotlineClient.self) private var hotline @Environment(Hotline.self) private var model: Hotline @Environment(\.colorScheme) var colorScheme -// @State private var tracker = Tracker(address: "hltracker.com", service: trackerService) + // @State private var tracker = Tracker(address: "hltracker.com", service: trackerService) @State private var servers: [Server] = [] @State private var selectedServer: Server? - @State var scrollOffset: CGFloat = CGFloat.zero + @State private var scrollOffset: CGFloat = CGFloat.zero @State private var initialLoadComplete = false + @State private var refreshing = false + @State private var topBarOpacity: Double = 1.0 + @State private var connectVisible = false + @State private var connectDismissed = true + @State private var serverVisible = false func shouldDisplayDescription(server: Server) -> Bool { guard let desc = server.description else { @@ -24,7 +176,7 @@ struct TrackerView: View { return desc.count > 0 && desc != server.name } - func connectionStatusToProgress(status: HotlineClientStatus) -> Double { + func connectionStatusToProgress(status: HotlineNewClientStatus) -> Double { switch status { case .disconnected: return 0.0 @@ -60,7 +212,8 @@ struct TrackerView: View { HStack(alignment: .center) { Spacer() Button { - // hotline.disconnect() + connectVisible = true + connectDismissed = false } label: { Text(Image(systemName: "point.3.connected.trianglepath.dotted")) .symbolRenderingMode(.hierarchical) @@ -68,17 +221,22 @@ struct TrackerView: View { .font(.title2) .padding(.trailing, 16) } + .sheet(isPresented: $connectVisible) { + connectDismissed = true + } content: { + TrackerConnectView() + } } .frame(height: 40.0) } .padding() - .opacity(scrollOffset > 80 ? 0 : 1.0) -// .padding(.top, 5) - .opacity(inverseLerp(lower: -80, upper: 0, v: scrollOffset)) -// .opacity(inverseLerp(lower: 20, upper: 0, v: scrollOffset)) Spacer() } + .opacity(inverseLerp(lower: -50, upper: 0, v: scrollOffset)) + .opacity(scrollOffset > 65 ? 0.0 : 1.0) + .opacity(topBarOpacity) + .zIndex(scrollOffset > 0 ? 1 : 3) ObservableScrollView(scrollOffset: $scrollOffset) { LazyVStack(alignment: .leading) { ForEach(self.servers) { server in @@ -102,14 +260,16 @@ struct TrackerView: View { if server == selectedServer { Spacer(minLength: 16) - if hotline.connectionStatus != .disconnected && hotline.server! == server { - ProgressView(value: connectionStatusToProgress(status: hotline.connectionStatus)) + if model.status != .disconnected && model.server != nil && model.server! == server { + ProgressView(value: connectionStatusToProgress(status: model.status)) .frame(minHeight: 10) .accentColor(colorScheme == .dark ? .white : .black) } else { Button("Connect") { - hotline.connect(to: HotlineServer(address: server.address, port: UInt16(server.port), users: UInt16(server.users), name: server.name, description: server.description)) + Task { + await model.login(server: server, login: "", password: "", username: "bolt", iconID: 128) + } } .bold() .padding(EdgeInsets(top: 16, leading: 24, bottom: 16, trailing: 24)) @@ -119,7 +279,7 @@ struct TrackerView: View { colorScheme == .dark ? LinearGradient(gradient: Gradient(colors: [Color(white: 0.4), Color(white: 0.3)]), startPoint: .top, endPoint: .bottom) : - LinearGradient(gradient: Gradient(colors: [Color(white: 0.95), Color(white: 0.91)]), startPoint: .top, endPoint: .bottom) + LinearGradient(gradient: Gradient(colors: [Color(white: 0.95), Color(white: 0.91)]), startPoint: .top, endPoint: .bottom) ) .overlay( RoundedRectangle(cornerRadius: 10.0).stroke(.black, lineWidth: 3).opacity(colorScheme == .dark ? 0.0 : 0.2) @@ -143,6 +303,7 @@ struct TrackerView: View { } .padding(EdgeInsets(top: 75, leading: 0, bottom: 0, trailing: 0)) } + .zIndex(2) .overlay { if !initialLoadComplete { VStack { @@ -153,29 +314,44 @@ struct TrackerView: View { } } .refreshable { - initialLoadComplete = true + DispatchQueue.main.async { + withAnimation(.easeOut(duration: 0.1)) { + topBarOpacity = 0.0 + } + initialLoadComplete = true + } + + model.disconnectTracker() await updateServers() + + DispatchQueue.main.async { + withAnimation(.easeOut(duration: 1.0).delay(0.75)) { + topBarOpacity = 1.0 + } + } } + } - .fullScreenCover(isPresented: Binding(get: { return hotline.connectionStatus == .loggedIn }, set: { _ in }), onDismiss: { - hotline.disconnect() + .fullScreenCover(isPresented: Binding(get: { return (connectDismissed && serverVisible) }, set: { _ in }), onDismiss: { + model.disconnect() }) { ServerView() } + .onChange(of: model.status) { + serverVisible = (model.status == .loggedIn) + } .background(Color(uiColor: UIColor.systemGroupedBackground)) .frame(maxWidth: .infinity) .task { await updateServers() initialLoadComplete = true -// tracker.fetch() } } } #Preview { TrackerView() - .environment(HotlineClient()) - .environment(HotlineState()) + .environment(Hotline(trackerClient: HotlineTrackerClient(), client: HotlineNewClient())) // .modelContainer(for: Item.self, inMemory: true) } diff --git a/Hotline/Views/UserListView.swift b/Hotline/Views/UsersView.swift index 8ea186e..0397ef7 100644 --- a/Hotline/Views/UserListView.swift +++ b/Hotline/Views/UsersView.swift @@ -1,28 +1,28 @@ import SwiftUI -struct UserListView: View { - @Environment(HotlineClient.self) private var hotline +struct UsersView: View { + @Environment(Hotline.self) private var model: Hotline var body: some View { NavigationStack { - List(hotline.userList) { u in + List(model.users) { u in Text("🤖 \(u.name)") .fontWeight(.medium) .lineLimit(1) .truncationMode(.tail) - .foregroundStyle(u.isAdmin ? Color(hex: 0xE10000) : Color.accentColor) - .opacity(u.isIdle ? 0.5 : 1.0) + .foregroundStyle(u.status.contains(.admin) ? Color(hex: 0xE10000) : Color.accentColor) + .opacity(u.status.contains(.idle) ? 0.5 : 1.0) } // .listStyle(.grouped) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .principal) { - Text(hotline.server?.name ?? "") + Text(model.server?.name ?? "") .font(.headline) } ToolbarItem(placement: .navigationBarLeading) { Button { - hotline.disconnect() + model.disconnect() } label: { Text(Image(systemName: "xmark.circle.fill")) .symbolRenderingMode(.hierarchical) @@ -37,5 +37,5 @@ struct UserListView: View { #Preview { ChatView() - .environment(HotlineClient()) + .environment(Hotline(trackerClient: HotlineTrackerClient(), client: HotlineNewClient())) } |