diff options
| author | Dustin Mierau <dustin@mierau.me> | 2023-12-04 09:12:58 -0800 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2023-12-04 09:12:58 -0800 |
| commit | 471546236b8991f61d26c0e3aa8ee48b83b983af (patch) | |
| tree | b74331a8c8ea61ee7e1eaedfc7bbd1686bbc4702 | |
| parent | 885119acdd27bd53ed3096285c3eca3e2d99ad70 (diff) | |
Starting to hash out a server view and tabs. Added research pdfs. etc.
21 files changed, 416 insertions, 162 deletions
diff --git a/Hotline.xcodeproj/project.pbxproj b/Hotline.xcodeproj/project.pbxproj index 05dc8fd..0bd761f 100644 --- a/Hotline.xcodeproj/project.pbxproj +++ b/Hotline.xcodeproj/project.pbxproj @@ -7,25 +7,37 @@ objects = { /* Begin PBXBuildFile section */ + DA43205C2B1D003B00FC8843 /* HotlineClientModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA43205B2B1D003B00FC8843 /* HotlineClientModel.swift */; }; + DA43205E2B1D615600FC8843 /* ServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA43205D2B1D615600FC8843 /* ServerView.swift */; }; DA4F2BF62B1668A700D8ADDC /* HotlineClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4F2BF52B1668A700D8ADDC /* HotlineClient.swift */; }; DA4F2BF82B16A17200D8ADDC /* HotlineProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4F2BF72B16A17200D8ADDC /* HotlineProtocol.swift */; }; + DA4F2BFB2B1A518500D8ADDC /* AgreementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4F2BFA2B1A518500D8ADDC /* AgreementView.swift */; }; + DA4F2BFD2B1A52EF00D8ADDC /* TrackerServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4F2BFC2B1A52EF00D8ADDC /* TrackerServerView.swift */; }; + DA4F2BFF2B1A54FF00D8ADDC /* HotlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4F2BFE2B1A54FF00D8ADDC /* HotlineView.swift */; }; + DA4F2C012B1A558E00D8ADDC /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4F2C002B1A558E00D8ADDC /* ChatView.swift */; }; DA9CAFBB2B126D5700CDA197 /* HotlineApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9CAFBA2B126D5700CDA197 /* HotlineApp.swift */; }; DA9CAFBD2B126D5700CDA197 /* TrackerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9CAFBC2B126D5700CDA197 /* TrackerView.swift */; }; DA9CAFC12B126D5800CDA197 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA9CAFC02B126D5800CDA197 /* Assets.xcassets */; }; DA9CAFC42B126D5800CDA197 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA9CAFC32B126D5800CDA197 /* Preview Assets.xcassets */; }; - DA9CAFCB2B126E3300CDA197 /* HotlineTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9CAFCA2B126E3300CDA197 /* HotlineTracker.swift */; }; + DA9CAFCB2B126E3300CDA197 /* HotlineTrackerClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9CAFCA2B126E3300CDA197 /* HotlineTrackerClient.swift */; }; DABFCC292B1530DC009F40D2 /* DataExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DABFCC282B1530DC009F40D2 /* DataExtensions.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + DA43205B2B1D003B00FC8843 /* HotlineClientModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotlineClientModel.swift; sourceTree = "<group>"; }; + DA43205D2B1D615600FC8843 /* ServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerView.swift; sourceTree = "<group>"; }; DA4F2BF52B1668A700D8ADDC /* HotlineClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotlineClient.swift; sourceTree = "<group>"; }; DA4F2BF72B16A17200D8ADDC /* HotlineProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotlineProtocol.swift; sourceTree = "<group>"; }; + DA4F2BFA2B1A518500D8ADDC /* AgreementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgreementView.swift; sourceTree = "<group>"; }; + DA4F2BFC2B1A52EF00D8ADDC /* TrackerServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerServerView.swift; sourceTree = "<group>"; }; + DA4F2BFE2B1A54FF00D8ADDC /* HotlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotlineView.swift; sourceTree = "<group>"; }; + DA4F2C002B1A558E00D8ADDC /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; }; DA9CAFB72B126D5700CDA197 /* Hotline.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Hotline.app; sourceTree = BUILT_PRODUCTS_DIR; }; DA9CAFBA2B126D5700CDA197 /* HotlineApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotlineApp.swift; sourceTree = "<group>"; }; DA9CAFBC2B126D5700CDA197 /* TrackerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerView.swift; sourceTree = "<group>"; }; DA9CAFC02B126D5800CDA197 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; DA9CAFC32B126D5800CDA197 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; }; - DA9CAFCA2B126E3300CDA197 /* HotlineTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotlineTracker.swift; sourceTree = "<group>"; }; + DA9CAFCA2B126E3300CDA197 /* HotlineTrackerClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HotlineTrackerClient.swift; sourceTree = "<group>"; }; DABFCC282B1530DC009F40D2 /* DataExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataExtensions.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ @@ -40,6 +52,19 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + DA4F2BF92B1A516F00D8ADDC /* Views */ = { + isa = PBXGroup; + children = ( + DA4F2BFA2B1A518500D8ADDC /* AgreementView.swift */, + DA4F2BFE2B1A54FF00D8ADDC /* HotlineView.swift */, + DA43205D2B1D615600FC8843 /* ServerView.swift */, + DA4F2C002B1A558E00D8ADDC /* ChatView.swift */, + DA4F2BFC2B1A52EF00D8ADDC /* TrackerServerView.swift */, + DA9CAFBC2B126D5700CDA197 /* TrackerView.swift */, + ); + path = Views; + sourceTree = "<group>"; + }; DA9CAFAE2B126D5700CDA197 = { isa = PBXGroup; children = ( @@ -60,8 +85,8 @@ isa = PBXGroup; children = ( DA9CAFBA2B126D5700CDA197 /* HotlineApp.swift */, - DA9CAFBC2B126D5700CDA197 /* TrackerView.swift */, - DABFCC262B1530AE009F40D2 /* Network */, + DA4F2BF92B1A516F00D8ADDC /* Views */, + DABFCC262B1530AE009F40D2 /* Hotline */, DABFCC272B1530BE009F40D2 /* Utility */, DA9CAFC02B126D5800CDA197 /* Assets.xcassets */, DA9CAFC22B126D5800CDA197 /* Preview Content */, @@ -77,14 +102,15 @@ path = "Preview Content"; sourceTree = "<group>"; }; - DABFCC262B1530AE009F40D2 /* Network */ = { + DABFCC262B1530AE009F40D2 /* Hotline */ = { isa = PBXGroup; children = ( DA4F2BF52B1668A700D8ADDC /* HotlineClient.swift */, - DA9CAFCA2B126E3300CDA197 /* HotlineTracker.swift */, + DA9CAFCA2B126E3300CDA197 /* HotlineTrackerClient.swift */, DA4F2BF72B16A17200D8ADDC /* HotlineProtocol.swift */, + DA43205B2B1D003B00FC8843 /* HotlineClientModel.swift */, ); - path = Network; + path = Hotline; sourceTree = "<group>"; }; DABFCC272B1530BE009F40D2 /* Utility */ = { @@ -165,12 +191,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DA43205C2B1D003B00FC8843 /* HotlineClientModel.swift in Sources */, + DA4F2BFB2B1A518500D8ADDC /* AgreementView.swift in Sources */, + DA43205E2B1D615600FC8843 /* ServerView.swift in Sources */, + DA4F2BFD2B1A52EF00D8ADDC /* TrackerServerView.swift in Sources */, DA9CAFBD2B126D5700CDA197 /* TrackerView.swift in Sources */, - DA9CAFCB2B126E3300CDA197 /* HotlineTracker.swift in Sources */, + DA9CAFCB2B126E3300CDA197 /* HotlineTrackerClient.swift in Sources */, DA9CAFBB2B126D5700CDA197 /* HotlineApp.swift in Sources */, + DA4F2BFF2B1A54FF00D8ADDC /* HotlineView.swift in Sources */, DA4F2BF62B1668A700D8ADDC /* HotlineClient.swift in Sources */, DABFCC292B1530DC009F40D2 /* DataExtensions.swift in Sources */, DA4F2BF82B16A17200D8ADDC /* HotlineProtocol.swift in Sources */, + DA4F2C012B1A558E00D8ADDC /* ChatView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Hotline/Network/HotlineClient.swift b/Hotline/Hotline/HotlineClient.swift index b5092a0..b5ea1d9 100644 --- a/Hotline/Network/HotlineClient.swift +++ b/Hotline/Hotline/HotlineClient.swift @@ -9,8 +9,15 @@ enum HotlineClientStatus: Int { case loggedIn } -class HotlineClient : ObservableObject { - static let shared = HotlineClient() +struct HotlineChat { + let message: String + let userID: UInt16? + let isTopic: Bool +} + +@Observable +class HotlineClient { +// static let shared = HotlineClient() static let handshakePacket = Data([ 0x54, 0x52, 0x54, 0x50, // 'TRTP' protocol ID @@ -19,18 +26,19 @@ class HotlineClient : ObservableObject { 0x00, 0x02, // Sub-version ]) - @Published var connectionStatus: HotlineClientStatus = .disconnected - @Published var agreement: String? - @Published var userList: [HotlineUser] = [] - @Published var chatMessages: [String] = [] + var connectionStatus: HotlineClientStatus = .disconnected + var agreement: String? + var users: [UInt16:HotlineUser] = [:] + var userList: [UInt16] = [] + var chatMessages: [HotlineChat] = [] var userName: String = "bolt" var userIconID: UInt16 = 128 var serverVersion: UInt16 = 151 var server: HotlineServer? - var connection: NWConnection? - private var transactionLog: [UInt32:HotlineTransactionType] = [:] + @ObservationIgnored private var connection: NWConnection? + @ObservationIgnored private var transactionLog: [UInt32:HotlineTransactionType] = [:] init() { @@ -347,15 +355,19 @@ class HotlineClient : ObservableObject { } case .getUserNameList: print("GOT USER LIST") - var newUserList: [HotlineUser] = [] + var newUsers: [UInt16:HotlineUser] = [:] + var newUserList: [UInt16] = [] for u in transaction.getFieldList(type: .userNameWithInfo) { - let info = u.getUserInfo() - print("USER: \(info)") - let user = HotlineUser(id: info.id, iconID: info.iconID, status: info.flags, name: info.userName) - newUserList.append(user) + let user = u.getUser() + newUsers[user.id] = user + newUserList.append(user.id) } DispatchQueue.main.async { + self.users = newUsers self.userList = newUserList + + print("HotlineClient got users:\n") + print("\(self.userList)\n\n") } default: break @@ -386,16 +398,9 @@ class HotlineClient : ObservableObject { let userID = userIDParam.getUInt16() { print("HotlineClient: \(userName):\(userID): \(chatText)") DispatchQueue.main.async { - self.chatMessages.append(chatText) + self.chatMessages.append(HotlineChat(message: chatText, userID: userID, isTopic: false)) } } - case .getUserNameList: - print("HotlineClient: GOT USER NAME LIST!") - let userList = transaction.getFieldList(type: .userNameWithInfo) - for u in userList { - let userInfo = u.getUserInfo() - print("HotlineClient: user \(userInfo.userName)") - } case .notifyOfUserChange: // print("HotlineClient: user changed") if let p = transaction.getField(type: .userName), diff --git a/Hotline/Hotline/HotlineClientModel.swift b/Hotline/Hotline/HotlineClientModel.swift new file mode 100644 index 0000000..a45a5e2 --- /dev/null +++ b/Hotline/Hotline/HotlineClientModel.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@Observable +class HotlineClientModel { + var status: HotlineClientStatus = .disconnected + var server: HotlineServer? + var userlist: [HotlineUser]? + var chat: [(HotlineUser, String)]? + var agreement: String? +} diff --git a/Hotline/Network/HotlineProtocol.swift b/Hotline/Hotline/HotlineProtocol.swift index 5372e78..200ac47 100644 --- a/Hotline/Network/HotlineProtocol.swift +++ b/Hotline/Hotline/HotlineProtocol.swift @@ -73,22 +73,6 @@ struct HotlineUser: Identifiable, Hashable { } } -struct HotlineUserInfo { - let id: UInt16 - let iconID: UInt16 - let flags: UInt16 - let userName: String - - init(data: Data) { - self.id = data.readUInt16(at: 0)! - self.iconID = data.readUInt16(at: 2)! - self.flags = data.readUInt16(at: 4)! - - let userNameLength = data.readUInt16(at: 6)! - self.userName = data.readString(at: 8, length: Int(userNameLength), encoding: .ascii)! - } -} - struct HotlineTransactionField { let type: HotlineTransactionFieldType let dataSize: UInt16 @@ -171,8 +155,8 @@ struct HotlineTransactionField { return String(data: self.data, encoding: encoding) } - func getUserInfo() -> HotlineUserInfo { - return HotlineUserInfo(data: self.data) + func getUser() -> HotlineUser { + return HotlineUser(from: self.data) } } diff --git a/Hotline/Network/HotlineTracker.swift b/Hotline/Hotline/HotlineTrackerClient.swift index 2c44bc8..fd4e97b 100644 --- a/Hotline/Network/HotlineTracker.swift +++ b/Hotline/Hotline/HotlineTrackerClient.swift @@ -7,26 +7,42 @@ enum HotlineTrackerStatus: Int { case connected } -class HotlineTracker : ObservableObject { - let serverAddress: NWEndpoint.Host - let serverPort: NWEndpoint.Port = NWEndpoint.Port(rawValue: 5498)! +struct HotlineTracker: Identifiable { + var address: String + var port: UInt16 + var servers: [HotlineServer] = [] + var expanded: Bool = false + var id: String { get { return self.address } } + + init(_ address: String, port: UInt16 = 5498) { + self.address = address + self.port = port + } +} + +@Observable +class HotlineTrackerClient { static let magicPacket = Data([ 0x48, 0x54, 0x52, 0x4B, // 'HTRK' 0x00, 0x01 // Version ]) - var connection: NWConnection? - - var bytes = Data() - var maxDataLength: Int = 0 - var serverCount: Int = 0 + @ObservationIgnored private let serverAddress: NWEndpoint.Host + @ObservationIgnored private let serverPort: NWEndpoint.Port + @ObservationIgnored private var connection: NWConnection? + @ObservationIgnored private var bytes = Data() + @ObservationIgnored private var maxDataLength: Int = 0 + @ObservationIgnored private var serverCount: Int = 0 - @Published var connectionStatus: HotlineTrackerStatus = .disconnected - @Published var servers: [HotlineServer] = [] + var tracker: HotlineTracker + var connectionStatus: HotlineTrackerStatus = .disconnected + var servers: [HotlineServer] = [] - init(address: String) { - self.serverAddress = NWEndpoint.Host(address) + init(tracker: HotlineTracker) { + self.tracker = tracker + self.serverAddress = NWEndpoint.Host(tracker.address) + self.serverPort = NWEndpoint.Port(rawValue: tracker.port)! } func fetch() { @@ -88,7 +104,7 @@ class HotlineTracker : ObservableObject { // let packet: [UInt8] = [0x48, 0x54, 0x52, 0x4B, 0x00, self.serverVersion] - c.send(content: HotlineTracker.magicPacket, completion: .contentProcessed { [weak self] (error) in + c.send(content: HotlineTrackerClient.magicPacket, completion: .contentProcessed { [weak self] (error) in if let err = error { print("HotlineTracker: sending magic failed \(err)") return @@ -112,7 +128,7 @@ class HotlineTracker : ObservableObject { return } - if data.isEmpty || !data.elementsEqual(HotlineTracker.magicPacket) { + if data.isEmpty || !data.elementsEqual(HotlineTrackerClient.magicPacket) { print("HotlineTracker: invalid magic response") self.disconnect() return @@ -252,6 +268,7 @@ class HotlineTracker : ObservableObject { } DispatchQueue.main.async { + self.tracker.servers = foundServers // print("CALLING CALLBACK") self.servers = foundServers } diff --git a/Hotline/HotlineApp.swift b/Hotline/HotlineApp.swift index da49fd0..238ca48 100644 --- a/Hotline/HotlineApp.swift +++ b/Hotline/HotlineApp.swift @@ -16,9 +16,15 @@ struct HotlineApp: App { // } // }() + @State private var hotline = HotlineClient() + @State private var tracker = HotlineTrackerClient(tracker: HotlineTracker("hltracker.com")) + var body: some Scene { + WindowGroup { - TrackerView() + HotlineView() + .environment(hotline) + .environment(tracker) } // .modelContainer(sharedModelContainer) } diff --git a/Hotline/TrackerView.swift b/Hotline/TrackerView.swift deleted file mode 100644 index 1b5cce5..0000000 --- a/Hotline/TrackerView.swift +++ /dev/null @@ -1,100 +0,0 @@ -import SwiftUI -import SwiftData - -struct AgreementView: View { - @Environment(\.dismiss) var dismiss - - let text: String - - var body: some View { - Text(text) - } -} - -struct TrackerServerView: View { - @Environment(\.dismiss) var dismiss - - let server: HotlineServer - - var body: some View { - VStack(alignment: .leading) { - HStack { - Text("🌎") - Text(server.name!).bold().dynamicTypeSize(.xxLarge) - } - .padding(EdgeInsets(top: 0, leading: 0, bottom: 8.0, trailing: 0)) - Text(server.description!).opacity(0.6).dynamicTypeSize(.xLarge).padding(EdgeInsets(top: 0, leading: 0, bottom: 8.0, trailing: 0)) - Text(server.address).opacity(0.3).dynamicTypeSize(.medium) - Spacer() - HStack(alignment: .center) { - Button("Connect") { - print("WHAT", "HELLO", server.address) - HotlineClient.shared.connect(to: server) - dismiss() -// client = HotlineClient(server: selectedServer) - } - .bold() - .padding(EdgeInsets(top: 16, leading: 24, bottom: 16, trailing: 24)) - .frame(maxWidth: .infinity) - .foregroundColor(.black) - .background(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(0.4) - ) - .cornerRadius(10.0) - } - } - .padding(EdgeInsets(top: 28.0, leading: 24.0, bottom: 24.0, trailing: 24.0)) - .presentationDetents([.fraction(0.4)]) - .presentationDragIndicator(.automatic) - } -} - -struct TrackerView: View { - - // @Environment(\.modelContext) private var modelContext - // @Query private var items: [Item] - - @StateObject var tracker = HotlineTracker(address: "hltracker.com") - @StateObject var client = HotlineClient.shared - - @State private var selectedServer: HotlineServer? - @State private var showingAgreement = false - @State private var showingConnectSheet = false - - var body: some View { - List(selection: $selectedServer) { - ForEach(tracker.servers) { server in - HStack { - Text("🌎") - Text(server.name!).bold() - Spacer() - Text("\(server.users)").opacity(0.6) - } - .listRowBackground(Color(white: 0.96)) - .listRowInsets(EdgeInsets(top: 0, leading: 16.0, bottom: 0, trailing: 16.0)) - .listRowSeparator(.visible, edges: VerticalEdge.Set.all) - .listRowSeparatorTint(Color(white: 1.0)) - .tag(server) - } - } - .background(Color(white: 0.96)) - .listStyle(.plain) - .frame(maxWidth: .infinity) - .task { - tracker.fetch() - } - .sheet(item: $selectedServer) { item in - TrackerServerView(server: item) - } - .sheet(isPresented: Binding(get: { client.agreement != nil }, set: { _ in })) { - AgreementView(text: client.agreement!) - } - } -} - -#Preview { - TrackerView() - // .modelContainer(for: Item.self, inMemory: true) -} diff --git a/Hotline/Views/AgreementView.swift b/Hotline/Views/AgreementView.swift new file mode 100644 index 0000000..c61c9d7 --- /dev/null +++ b/Hotline/Views/AgreementView.swift @@ -0,0 +1,35 @@ +import SwiftUI + +struct AgreementView: View { + @Environment(\.dismiss) var dismiss + + let text: String + + var body: some View { + VStack(alignment: .leading) { + ScrollView { + VStack(alignment: .leading) { + Text(text) + .fontDesign(.monospaced) + .padding() + .dynamicTypeSize(.small) + .textSelection(.enabled) + } + } + Button("OK") { + print("DONE") + } + .bold() + .padding(EdgeInsets(top: 16, leading: 24, bottom: 16, trailing: 24)) + .frame(maxWidth: .infinity) + } + } +} + +#Preview { + AgreementView(text: """ +Welcome! + +Take it on real one. +""") +} diff --git a/Hotline/Views/ChatView.swift b/Hotline/Views/ChatView.swift new file mode 100644 index 0000000..532a890 --- /dev/null +++ b/Hotline/Views/ChatView.swift @@ -0,0 +1,63 @@ +import SwiftUI + +struct ChatView: View { +// @Binding private var input: String + @State var input: String = "" + @State private var scrollPos: Int? + @State private var contentHeight: CGFloat = 0 + + var body: some View { + VStack(spacing: 0) { + GeometryReader { geometry in + ScrollView(.vertical) { + ScrollViewReader { scrollReader in + VStack(alignment: .leading) { + Spacer() + LazyVStack(alignment: .leading, spacing: 10) { + HStack(alignment: .firstTextBaseline) { + Text("bolt:").bold().fontDesign(.monospaced).frame(minWidth: 60) + Text("hello!").fontDesign(.monospaced).textSelection(.enabled) + } + ForEach(0..<50) { i in + HStack(alignment: .firstTextBaseline) { + Text("mierau:").bold().fontDesign(.monospaced) + Text("g'day to you. what's going on this afternoon?") + .fontDesign(.monospaced) + .textSelection(.enabled) + } + } + Text("").font(.system(size: 0)).id("bottomScroll") + } + .padding() + } + // .frame(width: geometry.size.width) + .frame(minHeight: geometry.size.height) +// .background(Color.red) + .onAppear() { + scrollReader.scrollTo("bottomScroll", anchor: .bottom) + } + // .onChange() { + // scrollReader.scrollTo(10000, anchor: .bottomTrailing) + // } + } + } + } + + Divider() + + HStack(alignment: .top) { + Image(systemName: "chevron.right") + TextField("This is a chat topic", text: $input, axis: .vertical) + .lineLimit(1...5) + .onSubmit { + // HotlineClient.shared.sendChat(message: self.input) + self.input = "" + } + }.padding() + } + } +} + +#Preview { + ChatView() +} diff --git a/Hotline/Views/HotlineView.swift b/Hotline/Views/HotlineView.swift new file mode 100644 index 0000000..67ef067 --- /dev/null +++ b/Hotline/Views/HotlineView.swift @@ -0,0 +1,20 @@ +import SwiftUI + +struct HotlineView: View { + @Environment(HotlineClient.self) private var hotline + @Environment(HotlineTrackerClient.self) private var tracker + + @State private var isTrackerVisible = false + + var body: some View { + NavigationStack { + TrackerView() + } + } +} + +#Preview { + HotlineView() + .environment(HotlineClient()) + .environment(HotlineTrackerClient(tracker: HotlineTracker("hltracker.com"))) +} diff --git a/Hotline/Views/ServerView.swift b/Hotline/Views/ServerView.swift new file mode 100644 index 0000000..1610aa7 --- /dev/null +++ b/Hotline/Views/ServerView.swift @@ -0,0 +1,62 @@ +import SwiftUI + +struct ServerView: View { + @Environment(HotlineClient.self) private var hotline + @Environment(HotlineTrackerClient.self) private var tracker + + @State private var isTrackerVisible = false + + var body: some View { + TabView { + NavigationView { + ChatView() + .navigationTitle("Badmoon") + .navigationBarItems( + leading: Button(action: { + withAnimation { + isTrackerVisible.toggle() + } + }) { + Image(systemName: "line.horizontal.3") // Hamburger icon or similar + .imageScale(.large) + } + ) + .toolbarBackground(.visible, for: .navigationBar) + .toolbarBackground(.red, for: .navigationBar) + } + .navigationBarTitleDisplayMode(.inline) + .tabItem { + Image(systemName: "message") + } + + Text("Users") + .tabItem { + Image(systemName: "person.fill") + } + + Text("News") + .tabItem { + Image(systemName: "newspaper") + } + + Text("Files") + .tabItem { + Image(systemName: "folder") + } + + Text("Transfers") + .tabItem { + Image(systemName: "network") + } + } +// .sheet(isPresented: Binding(get: { hotline.connectionStatus != .loggedIn }, set: { _ in })) { +// TrackerView() +// } + } +} + +#Preview { + ServerView() + .environment(HotlineClient()) + .environment(HotlineTrackerClient(tracker: HotlineTracker("hltracker.com"))) +} diff --git a/Hotline/Views/TrackerServerView.swift b/Hotline/Views/TrackerServerView.swift new file mode 100644 index 0000000..5be6f7a --- /dev/null +++ b/Hotline/Views/TrackerServerView.swift @@ -0,0 +1,47 @@ +import SwiftUI + +struct TrackerServerView: View { + @Environment(\.dismiss) var dismiss + + let server: HotlineServer + + var body: some View { + VStack(alignment: .leading) { + HStack { + Text("🌎").dynamicTypeSize(.xxxLarge) + Text(server.name!).bold().dynamicTypeSize(.xxxLarge) + } + .padding(EdgeInsets(top: 0, leading: 0, bottom: 8.0, trailing: 0)) + + Text(server.description!).opacity(0.6).dynamicTypeSize(.xLarge).padding(EdgeInsets(top: 0, leading: 0, bottom: 8.0, trailing: 0)).textSelection(.enabled) + Text(server.address).opacity(0.3).dynamicTypeSize(.medium).textSelection(.enabled) + + Spacer() + + HStack(alignment: .center) { + Button("Connect") { + print("WHAT", "HELLO", server.address) +// HotlineClient.shared.connect(to: server) + dismiss() + } + .bold() + .padding(EdgeInsets(top: 16, leading: 24, bottom: 16, trailing: 24)) + .frame(maxWidth: .infinity) + .foregroundColor(.black) + .background(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(0.4) + ) + .cornerRadius(10.0) + } + } + .padding(EdgeInsets(top: 28.0, leading: 24.0, bottom: 24.0, trailing: 24.0)) + .presentationDetents([.fraction(0.4)]) + .presentationDragIndicator(.automatic) + } +} + +#Preview { + TrackerServerView(server: HotlineServer(address: "192.168.1.1", port: 5050, users: 5, name: "Ye Olde Server", description: "This is a server")) +} diff --git a/Hotline/Views/TrackerView.swift b/Hotline/Views/TrackerView.swift new file mode 100644 index 0000000..5d1afe2 --- /dev/null +++ b/Hotline/Views/TrackerView.swift @@ -0,0 +1,73 @@ +import SwiftUI + +struct TrackerView: View { + + // @Environment(\.modelContext) private var modelContext + // @Query private var items: [Item] + + @Environment(HotlineClient.self) private var hotline + @Environment(HotlineTrackerClient.self) private var tracker + + // @StateObject var tracker = HotlineTrackerClient(address: "hltracker.com") + // @StateObject var client = HotlineClient.shared + + @State private var selectedServer: HotlineServer? + @State private var showingAgreement = false + @State private var showingConnectSheet = false + + // @Bindable var trackerTest = tracker + + var body: some View { + List(selection: $selectedServer) { + ForEach(tracker.servers) { server in + HStack { + Text("🌎") + Text(server.name!).bold() + Spacer() + Text("\(server.users)").opacity(0.6) + } + .listRowBackground(Color(white: 0.96)) + .listRowInsets(EdgeInsets(top: 0, leading: 16.0, bottom: 0, trailing: 16.0)) + .listRowSeparator(.visible, edges: VerticalEdge.Set.all) + .listRowSeparatorTint(Color(white: 1.0)) + .tag(server) + } + } + .background(Color(white: 0.96)) + .listStyle(.plain) + .frame(maxWidth: .infinity) + .task { + tracker.fetch() + } + .sheet(item: $selectedServer) { item in + TrackerServerView(server: item) + } + .sheet(isPresented: Binding(get: { hotline.agreement != nil }, set: { _ in })) { + AgreementView(text: hotline.agreement!) + } + .navigationTitle("Tracker") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + Image(systemName: "gearshape.fill") + } + // ToolbarItem(placement: .topBarTrailing) { + // Image(systemName: "camera.fill") + // } + // ToolbarItem(placement: .principal) { + // Text("Username") + // } + } + .refreshable { + tracker.fetch() + } + .interactiveDismissDisabled() + } +} + +#Preview { + TrackerView() + .environment(HotlineClient()) + .environment(HotlineTrackerClient(tracker: HotlineTracker("hltracker.com"))) + // .modelContainer(for: Item.self, inMemory: true) +} diff --git a/Research/hotline 2 constants.pdf b/Research/hotline 2 constants.pdf Binary files differnew file mode 100644 index 0000000..48594d4 --- /dev/null +++ b/Research/hotline 2 constants.pdf diff --git a/Research/hotline access bits.pdf b/Research/hotline access bits.pdf Binary files differnew file mode 100644 index 0000000..af3be66 --- /dev/null +++ b/Research/hotline access bits.pdf diff --git a/Research/hotline date params.pdf b/Research/hotline date params.pdf Binary files differnew file mode 100644 index 0000000..a594e0a --- /dev/null +++ b/Research/hotline date params.pdf diff --git a/Research/hotline path params.pdf b/Research/hotline path params.pdf Binary files differnew file mode 100644 index 0000000..677afc2 --- /dev/null +++ b/Research/hotline path params.pdf diff --git a/Research/hotline protocol good.pdf b/Research/hotline protocol good.pdf Binary files differnew file mode 100644 index 0000000..fd52309 --- /dev/null +++ b/Research/hotline protocol good.pdf diff --git a/Research/hotline server protocol.pdf b/Research/hotline server protocol.pdf Binary files differnew file mode 100644 index 0000000..d5d5c6a --- /dev/null +++ b/Research/hotline server protocol.pdf diff --git a/Research/hotline trackers.pdf b/Research/hotline trackers.pdf Binary files differnew file mode 100644 index 0000000..96af1b6 --- /dev/null +++ b/Research/hotline trackers.pdf diff --git a/Research/hotline transactions.pdf b/Research/hotline transactions.pdf Binary files differnew file mode 100644 index 0000000..8dbe1f0 --- /dev/null +++ b/Research/hotline transactions.pdf |