aboutsummaryrefslogtreecommitdiff
path: root/Hotline/iOS
diff options
context:
space:
mode:
authorRuben Beltran del Rio <git@r.bdr.sh>2025-02-05 22:27:17 +0100
committerRuben Beltran del Rio <git@r.bdr.sh>2025-02-05 22:27:17 +0100
commit45829daa856b376b1ab04d415917110b71b1dec5 (patch)
tree8e52daed6897b6f489d455736fe256cb9bd90fef /Hotline/iOS
parent5c3ea897d062a47bc8cd6255fb8c36bad2f0733f (diff)
Apply formatting
Diffstat (limited to 'Hotline/iOS')
-rw-r--r--Hotline/iOS/ChatView.swift28
-rw-r--r--Hotline/iOS/FilesView.swift53
-rw-r--r--Hotline/iOS/MessageBoardView.swift8
-rw-r--r--Hotline/iOS/NewsView.swift74
-rw-r--r--Hotline/iOS/ServerView.swift12
-rw-r--r--Hotline/iOS/TrackerView.swift302
-rw-r--r--Hotline/iOS/UsersView.swift2
7 files changed, 244 insertions, 235 deletions
diff --git a/Hotline/iOS/ChatView.swift b/Hotline/iOS/ChatView.swift
index dadd5e6..e5105d0 100644
--- a/Hotline/iOS/ChatView.swift
+++ b/Hotline/iOS/ChatView.swift
@@ -2,20 +2,21 @@ import SwiftUI
extension View {
func endEditing() {
- UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
+ UIApplication.shared.sendAction(
+ #selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
struct ChatView: View {
@Environment(Hotline.self) private var model: Hotline
@Environment(\.colorScheme) var colorScheme
-
+
@State var input: String = ""
@State private var scrollPos: Int?
@State private var contentHeight: CGFloat = 0
-
+
@Namespace var bottomID
-
+
var body: some View {
NavigationStack {
VStack(spacing: 0) {
@@ -24,7 +25,7 @@ struct ChatView: View {
LazyVStack(alignment: .leading) {
ForEach(model.chat) { msg in
if msg.type == .agreement {
-
+
VStack(alignment: .center) {
if let bannerImage = self.model.bannerImage {
Image(uiImage: bannerImage)
@@ -33,7 +34,7 @@ struct ChatView: View {
.frame(maxWidth: 468.0)
.clipShape(RoundedRectangle(cornerRadius: 5))
}
-
+
VStack(alignment: .leading) {
HStack {
Text(msg.text)
@@ -51,8 +52,7 @@ struct ChatView: View {
}
.frame(maxWidth: .infinity)
.padding()
- }
- else if msg.type == .status {
+ } else if msg.type == .status {
HStack {
Spacer()
Text(msg.text)
@@ -62,13 +62,11 @@ struct ChatView: View {
Spacer()
}
.padding()
- }
- else {
+ } else {
HStack(alignment: .firstTextBaseline) {
if let username = msg.username {
Text("**\(username):** \(msg.text)")
- }
- else {
+ } else {
Text(msg.text)
.textSelection(.enabled)
}
@@ -96,9 +94,9 @@ struct ChatView: View {
self.endEditing()
}
}
-
+
Divider()
-
+
HStack(alignment: .top) {
Image(systemName: "chevron.right").opacity(0.4)
TextField("", text: $input, axis: .vertical)
@@ -144,7 +142,7 @@ struct ChatView: View {
}
}
}
-
+
}
}
}
diff --git a/Hotline/iOS/FilesView.swift b/Hotline/iOS/FilesView.swift
index 017a469..70b07c2 100644
--- a/Hotline/iOS/FilesView.swift
+++ b/Hotline/iOS/FilesView.swift
@@ -3,11 +3,11 @@ import UniformTypeIdentifiers
struct FileView: View {
@Environment(Hotline.self) private var model: Hotline
-
+
@State var expanded = false
-
+
var file: FileInfo
-
+
var body: some View {
if file.isFolder {
DisclosureGroup(isExpanded: $expanded) {
@@ -17,7 +17,7 @@ struct FileView: View {
}
} label: {
HStack {
- HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) {
+ HStack(alignment: /*@START_MENU_TOKEN@*/ .center /*@END_MENU_TOKEN@*/) {
Image(systemName: "folder.fill")
}
.frame(minWidth: 25)
@@ -30,20 +30,19 @@ struct FileView: View {
if !expanded {
return
}
-
+
// Some servers don't reply when asking for the contents of an empty folder.
if file.isFolder && file.fileSize == 0 {
return
}
-
+
Task {
await model.getFileList(path: file.path)
}
}
- }
- else {
+ } else {
HStack {
- HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) {
+ HStack(alignment: /*@START_MENU_TOKEN@*/ .center /*@END_MENU_TOKEN@*/) {
fileIcon(name: file.name)
.renderingMode(.template)
}
@@ -54,53 +53,49 @@ struct FileView: View {
}
}
}
-
+
static let byteFormatter = ByteCountFormatter()
-
+
private func formattedFileSize(_ fileSize: UInt) -> String {
// let bcf = ByteCountFormatter()
FileView.byteFormatter.allowedUnits = [.useAll]
FileView.byteFormatter.countStyle = .file
return FileView.byteFormatter.string(fromByteCount: Int64(fileSize))
}
-
+
private func fileIcon(name: String) -> Image {
// func utTypeForFilename(_ filename: String) -> UTType? {
let fileExtension = (name as NSString).pathExtension
if let fileType = UTType(filenameExtension: fileExtension) {
print("\(name) \(fileExtension) = \(fileType)")
-
+
if fileType.isSubtype(of: .movie) {
return Image(systemName: "play.rectangle")
-// return UIImage(systemName: "play.rectangle")!
- }
- else if fileType.isSubtype(of: .image) {
+ // return UIImage(systemName: "play.rectangle")!
+ } else if fileType.isSubtype(of: .image) {
return Image(systemName: "photo")
-// return UIImage(systemName: "photo")!
- }
- else if fileType.isSubtype(of: .archive) {
+ // return UIImage(systemName: "photo")!
+ } else if fileType.isSubtype(of: .archive) {
return Image(systemName: "doc.zipper")
-// return UIImage(systemName: "doc.zipper")!
- }
- else if fileType.isSubtype(of: .text) {
+ // return UIImage(systemName: "doc.zipper")!
+ } else if fileType.isSubtype(of: .text) {
return Image(systemName: "doc.text")
-// return UIImage(systemName: "doc.text")!
- }
- else {
+ // return UIImage(systemName: "doc.text")!
+ } else {
return Image(systemName: "doc")
-// return UIImage(systemName: "doc")!
+ // return UIImage(systemName: "doc")!
}
}
-
+
return Image(systemName: "doc")
}
}
struct FilesView: View {
@Environment(Hotline.self) private var model: Hotline
-
+
@State var initialLoadComplete = false
-
+
var body: some View {
NavigationStack {
List(model.files) { file in
diff --git a/Hotline/iOS/MessageBoardView.swift b/Hotline/iOS/MessageBoardView.swift
index 0d3968f..56fd4cc 100644
--- a/Hotline/iOS/MessageBoardView.swift
+++ b/Hotline/iOS/MessageBoardView.swift
@@ -2,10 +2,10 @@ import SwiftUI
struct MessageBoardView: View {
@Environment(Hotline.self) private var model: Hotline
-
+
@State private var initialLoadComplete = false
@State private var fetched = false
-
+
var body: some View {
NavigationStack {
ScrollView {
@@ -57,14 +57,14 @@ struct MessageBoardView: View {
}
ToolbarItem(placement: .navigationBarTrailing) {
Button {
-
+
} label: {
Image(systemName: "square.and.pencil")
}
}
}
}
-
+
}
}
diff --git a/Hotline/iOS/NewsView.swift b/Hotline/iOS/NewsView.swift
index 460dcd3..72dd034 100644
--- a/Hotline/iOS/NewsView.swift
+++ b/Hotline/iOS/NewsView.swift
@@ -4,16 +4,16 @@ import UniformTypeIdentifiers
struct NewsItemView: View {
@Environment(Hotline.self) private var model: Hotline
@Environment(NewsItemSelection.self) private var selectedArticle: NewsItemSelection
-
+
let news: NewsInfo
-
+
static var dateFormatter: DateFormatter = {
var formatter = DateFormatter()
formatter.dateFormat = "MM/dd/yy, h:mm a"
formatter.timeZone = .gmt
return formatter
}()
-
+
static var relativeDateFormatter: RelativeDateTimeFormatter = {
var formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .abbreviated
@@ -21,9 +21,9 @@ struct NewsItemView: View {
formatter.formattingContext = .listItem
return formatter
}()
-
+
@State var expanded = false
-
+
var body: some View {
if news.count > 0 {
DisclosureGroup(isExpanded: $expanded) {
@@ -49,13 +49,12 @@ struct NewsItemView: View {
if !expanded {
return
}
-
+
Task {
await model.getNewsList(at: news.path)
}
}
- }
- else {
+ } else {
HStack(alignment: .firstTextBaseline) {
Text(Image(systemName: "quote.opening"))
Text(news.name)
@@ -66,13 +65,12 @@ struct NewsItemView: View {
Text("\(news.count)")
.lineLimit(1)
.foregroundStyle(.secondary)
- }
- else if let postAuthor = news.articleUsername {
-// Text("\(NewsItemView.relativeDateFormatter.localizedString(for: postDate, relativeTo: Date.now))")
+ } else if let postAuthor = news.articleUsername {
+ // Text("\(NewsItemView.relativeDateFormatter.localizedString(for: postDate, relativeTo: Date.now))")
Text(postAuthor)
.foregroundStyle(.secondary)
.truncationMode(.tail)
-// .frame(maxWidth: 50)
+ // .frame(maxWidth: 50)
.font(.caption)
.lineLimit(1)
}
@@ -90,7 +88,7 @@ struct NewsItemView: View {
@Observable
class NewsItemSelection: Equatable {
var selectedArticle: NewsInfo? = nil
-
+
static func == (lhs: NewsItemSelection, rhs: NewsItemSelection) -> Bool {
return lhs.selectedArticle == rhs.selectedArticle
}
@@ -99,25 +97,24 @@ class NewsItemSelection: Equatable {
struct NewsView: View {
@Environment(Hotline.self) private var model: Hotline
@Environment(\.colorScheme) var colorScheme
-
+
@State private var fetched = false
@State private var selectedCategory: NewsInfo? = nil
@State private var topListHeight: CGFloat = 280
@State private var dividerHeight: CGFloat = 30
-
+
@State private var articleSelection = NewsItemSelection()
@State private var articleText = ""
-
-// @State private var selectedArticleID: UInt?
-
+
+ // @State private var selectedArticleID: UInt?
+
var articleList: some View {
VStack(spacing: 0) {
if model.news.count == 0 {
Text("No News Available")
.font(.headline)
.opacity(0.3)
- }
- else {
+ } else {
List(model.news) { category in
NewsItemView(news: category)
.frame(height: 38)
@@ -128,10 +125,10 @@ struct NewsView: View {
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(uiColor: .systemGroupedBackground))
-
+
// .listStyle(.plain)
}
-
+
var body: some View {
NavigationStack {
VStack(spacing: 0) {
@@ -140,19 +137,21 @@ struct NewsView: View {
.frame(minHeight: topListHeight)
.onChange(of: self.articleSelection.selectedArticle) {
self.articleText = ""
- if
- let article = self.articleSelection.selectedArticle,
+ if let article = self.articleSelection.selectedArticle,
let articleFlavor = article.articleFlavors?.first,
- let articleID = article.articleID {
+ let articleID = article.articleID
+ {
Task {
- if let articleText = await self.model.getNewsArticle(id: articleID, at: article.path, flavor: articleFlavor) {
+ if let articleText = await self.model.getNewsArticle(
+ id: articleID, at: article.path, flavor: articleFlavor)
+ {
self.articleText = articleText
}
}
}
-// print("SELECTED ARTICLE", articleSelection.selectedArticle?.name)
+ // print("SELECTED ARTICLE", articleSelection.selectedArticle?.name)
}
-
+
// Movable Divider
VStack(alignment: .center) {
Divider()
@@ -165,7 +164,9 @@ struct NewsView: View {
}
Spacer()
}
- .background(colorScheme == .dark ? Color(white: 0.1) : Color(uiColor: UIColor.systemBackground))
+ .background(
+ colorScheme == .dark ? Color(white: 0.1) : Color(uiColor: UIColor.systemBackground)
+ )
.frame(maxWidth: .infinity)
.frame(height: dividerHeight)
.gesture(
@@ -175,11 +176,11 @@ struct NewsView: View {
topListHeight = max(min(topListHeight + delta, 500), 50)
}
)
-
+
// Reader View
ScrollView(.vertical) {
VStack(alignment: .leading) {
-
+
if let news = self.articleSelection.selectedArticle {
if let poster = news.articleUsername, let postDate = news.articleDate {
HStack(alignment: .firstTextBaseline) {
@@ -189,7 +190,7 @@ struct NewsView: View {
.lineLimit(1)
.truncationMode(.tail)
.textSelection(.enabled)
-// .padding()
+ // .padding()
Spacer()
Text("\(NewsItemView.dateFormatter.string(from: postDate))")
.foregroundStyle(.secondary)
@@ -198,16 +199,16 @@ struct NewsView: View {
.textSelection(.enabled)
}
.padding(.bottom, 8)
-
+
Divider()
.padding(.bottom, 16)
}
-
+
Text(news.name).font(.title3).fontWeight(.medium)
.textSelection(.enabled)
.padding(.bottom, 8)
}
-
+
Text(self.articleText)
.multilineTextAlignment(.leading)
.textSelection(.enabled)
@@ -216,7 +217,8 @@ struct NewsView: View {
.padding()
}
.scrollBounceBehavior(.basedOnSize)
- .background(colorScheme == .dark ? Color(white: 0.1) : Color(uiColor: UIColor.systemBackground))
+ .background(
+ colorScheme == .dark ? Color(white: 0.1) : Color(uiColor: UIColor.systemBackground))
}
.task {
if !fetched {
diff --git a/Hotline/iOS/ServerView.swift b/Hotline/iOS/ServerView.swift
index 1ce0a9c..11f57aa 100644
--- a/Hotline/iOS/ServerView.swift
+++ b/Hotline/iOS/ServerView.swift
@@ -3,11 +3,11 @@ import SwiftUI
struct ServerView: View {
@Environment(Hotline.self) private var model: Hotline
@Environment(\.colorScheme) var colorScheme
-
+
enum Tab {
case chat, users, news, messageBoard, files
}
-
+
var body: some View {
TabView {
ChatView()
@@ -15,13 +15,13 @@ struct ServerView: View {
Image(systemName: "bubble")
}
.tag(Tab.chat)
-
+
UsersView()
.tabItem {
Image(systemName: "person.2")
}
.tag(Tab.users)
-
+
if let v = model.serverVersion, v >= 150 {
NewsView()
.tabItem {
@@ -29,13 +29,13 @@ struct ServerView: View {
}
.tag(Tab.news)
}
-
+
MessageBoardView()
.tabItem {
Image(systemName: "note.text")
}
.tag(Tab.messageBoard)
-
+
FilesView()
.tabItem {
Image(systemName: "folder").tint(.black)
diff --git a/Hotline/iOS/TrackerView.swift b/Hotline/iOS/TrackerView.swift
index 2a3583b..43b1916 100644
--- a/Hotline/iOS/TrackerView.swift
+++ b/Hotline/iOS/TrackerView.swift
@@ -4,13 +4,13 @@ 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
-
+ // @State private var connecting = false
+
func connectionStatusToProgress(status: HotlineClientStatus) -> Double {
switch status {
case .disconnected:
@@ -25,118 +25,126 @@ struct TrackerConnectView: View {
return 1.0
}
}
-
+
var body: some View {
- VStack(alignment: .leading) {
- if self.model.status == .disconnected {
- 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")
+ VStack(alignment: .leading) {
+ if self.model.status == .disconnected {
+ 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)
}
- .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: nil, description: nil, address: address, port: HotlinePorts.DefaultServerPort, users: 0, login: login, password: password)
- server = s
- self.model.login(server: s, username: "bolt", iconID: 128) { success in
- if !success {
- print("FAILED LOGIN??")
- }
- else {
- self.model.sendUserInfo(username: "bolt", iconID: 128)
- self.model.getUserList()
- }
+ 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: nil, description: nil, address: address, port: HotlinePorts.DefaultServerPort,
+ users: 0, login: login, password: password)
+ server = s
+ self.model.login(server: s, username: "bolt", iconID: 128) { success in
+ if !success {
+ print("FAILED LOGIN??")
+ } else {
+ self.model.sendUserInfo(username: "bolt", iconID: 128)
+ self.model.getUserList()
}
- } label: {
- Text("Connect")
}
- .disabled(self.model.status != .disconnected)
- .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)
+ } label: {
+ Text("Connect")
}
- .padding()
+ .disabled(self.model.status != .disconnected)
+ .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()
- .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)
-// }
+ }
+ .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)
- }
+ }
+ .toolbar {
+ ToolbarItem(placement: .principal) {
+ Text("Connect to Server")
+ .font(.headline)
}
-// .presentationBackground(.regularMaterial, in: Color(uiColor: .systemGroupedBackground))
+ }
+ // .presentationBackground(.regularMaterial, in: Color(uiColor: .systemGroupedBackground))
.presentationBackground {
Color.clear
.background(Material.regular)
@@ -151,7 +159,7 @@ struct TrackerConnectView: View {
struct TrackerView: View {
@Environment(Hotline.self) private var model: Hotline
@Environment(\.colorScheme) var colorScheme
-
+
@State private var servers: [Server] = []
@State private var selectedServer: Server?
@State private var scrollOffset: CGFloat = CGFloat.zero
@@ -161,15 +169,15 @@ struct TrackerView: View {
@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 {
return false
}
-
+
return desc.count > 0 && desc != server.name
}
-
+
func connectionStatusToProgress(status: HotlineClientStatus) -> Double {
switch status {
case .disconnected:
@@ -184,21 +192,21 @@ struct TrackerView: View {
return 1.0
}
}
-
+
func inverseLerp(lower: Double, upper: Double, v: Double) -> Double {
return (v - lower) / (upper - lower)
}
-
+
func updateServers() async {
-// "hltracker.com"
-// "tracker.preterhuman.net"
-// "hotline.ubersoft.org"
-// "tracked.nailbat.com"
-// "hotline.duckdns.org"
-// "tracked.agent79.org"
+ // "hltracker.com"
+ // "tracker.preterhuman.net"
+ // "hotline.ubersoft.org"
+ // "tracked.nailbat.com"
+ // "hotline.duckdns.org"
+ // "tracked.agent79.org"
self.servers = await model.getServerList(tracker: "hltracker.com")
}
-
+
var body: some View {
ZStack(alignment: .center) {
VStack(alignment: .center) {
@@ -249,7 +257,7 @@ struct TrackerView: View {
.frame(height: 40.0)
}
.padding()
-
+
Spacer()
}
.opacity(inverseLerp(lower: -50, upper: 0, v: scrollOffset))
@@ -269,7 +277,8 @@ struct TrackerView: View {
Text(server.description!).opacity(0.5).font(.system(size: 16))
}
Spacer()
- Text("\(server.address):" + String(format: "%i", server.port)).opacity(0.3).font(.system(size: 13))
+ Text("\(server.address):" + String(format: "%i", server.port)).opacity(0.3).font(
+ .system(size: 13))
}
Spacer()
if server.users > 0 {
@@ -278,19 +287,17 @@ struct TrackerView: View {
}
if server == selectedServer {
Spacer(minLength: 16)
-
+
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 {
+ } else {
Button("Connect") {
self.model.login(server: server, username: "bolt", iconID: 128) { success in
if !success {
print("FAILED LOGIN??")
- }
- else {
+ } else {
self.model.sendUserInfo(username: "bolt", iconID: 128)
self.model.getUserList()
self.model.downloadBanner()
@@ -302,13 +309,17 @@ struct TrackerView: View {
.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)
+ 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)
+ RoundedRectangle(cornerRadius: 10.0).stroke(.black, lineWidth: 3).opacity(
+ colorScheme == .dark ? 0.0 : 0.2)
)
.cornerRadius(10.0)
}
@@ -346,22 +357,24 @@ struct TrackerView: View {
}
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 (connectDismissed && serverVisible) }, set: { _ in }), onDismiss: {
- model.disconnect()
- }) {
+ .fullScreenCover(
+ isPresented: Binding(get: { return (connectDismissed && serverVisible) }, set: { _ in }),
+ onDismiss: {
+ model.disconnect()
+ }
+ ) {
ServerView()
}
.onChange(of: model.status) {
@@ -377,26 +390,27 @@ struct TrackerView: View {
guard url.scheme == "hotline" else {
return
}
-
+
if let address = url.host() {
let login = url.user(percentEncoded: false) ?? ""
let password = url.password(percentEncoded: false) ?? ""
let port = url.port ?? HotlinePorts.DefaultServerPort
-
+
self.model.disconnect()
-
- let tempServer = Server(name: nil, description: nil, address: address, port: port, users: 0, login: login, password: password)
+
+ let tempServer = Server(
+ name: nil, description: nil, address: address, port: port, users: 0, login: login,
+ password: password)
self.model.login(server: tempServer, username: "bolt", iconID: 128) { success in
if !success {
print("FAILED LOGIN??")
- }
- else {
+ } else {
self.model.sendUserInfo(username: "bolt", iconID: 128)
self.model.getUserList()
self.model.downloadBanner()
}
}
-
+
// TODO: Find a better way to show login status when trying to connect outside of the Tracker server list. Perhaps this opens the connect sheet prefilled.
}
})
diff --git a/Hotline/iOS/UsersView.swift b/Hotline/iOS/UsersView.swift
index 5d7a198..e1297c8 100644
--- a/Hotline/iOS/UsersView.swift
+++ b/Hotline/iOS/UsersView.swift
@@ -2,7 +2,7 @@ import SwiftUI
struct UsersView: View {
@Environment(Hotline.self) private var model: Hotline
-
+
var body: some View {
NavigationStack {
List(model.users) { u in