diff options
Diffstat (limited to 'Hotline/macOS')
| -rw-r--r-- | Hotline/macOS/AboutView.swift | 52 | ||||
| -rw-r--r-- | Hotline/macOS/ChatView.swift | 34 | ||||
| -rw-r--r-- | Hotline/macOS/ServerAgreementView.swift | 81 | ||||
| -rw-r--r-- | Hotline/macOS/ServerMessageView.swift | 26 | ||||
| -rw-r--r-- | Hotline/macOS/ServerView.swift | 8 | ||||
| -rw-r--r-- | Hotline/macOS/SettingsView.swift | 1 |
6 files changed, 156 insertions, 46 deletions
diff --git a/Hotline/macOS/AboutView.swift b/Hotline/macOS/AboutView.swift index 7bd1c54..1cf7dc2 100644 --- a/Hotline/macOS/AboutView.swift +++ b/Hotline/macOS/AboutView.swift @@ -77,11 +77,12 @@ struct AboutView: View { } } .frame(height: 40) -// .padding(.top, 4) Spacer() } - .frame(width: 270) + .frame(width: 250) + + Spacer() ScrollView(.vertical) { VStack(alignment: .leading, spacing: 16) { @@ -118,17 +119,38 @@ struct AboutView: View { Link(destination: contributor.webURL) { HStack { if let pictureURL = contributor.pictureURL { - AsyncImage(url: pictureURL) { img in - img - .interpolation(.high) - .resizable() - .scaledToFit() - .background(.white) - } placeholder: { - Color.white.opacity(0.2) + AsyncImage(url: pictureURL) { phase in + if let image = phase.image { + image + .interpolation(.high) + .resizable() + .scaledToFit() + .background(.white) + .frame(width: 32, height: 32) + } else if phase.error != nil { + Color.clear + .frame(width: 32, height: 32) + } else { + Color.white + .opacity(0.2) + .frame(width: 32, height: 32) + } } .frame(width: 32, height: 32) .clipShape(Circle()) + +// AsyncImage(url: pictureURL) { img in +// img +// .interpolation(.high) +// .resizable() +// .scaledToFit() +// .background(.white) +// } placeholder: { +// Color.white.opacity(0.2) +// .frame(width: 32, height: 32) +// } +// .frame(width: 32, height: 32) +// .clipShape(Circle()) } VStack(alignment: .leading, spacing: 2) { @@ -143,16 +165,12 @@ struct AboutView: View { .font(.system(size: 11)) .foregroundStyle(.white.opacity(0.4)) } - - Spacer() } } } } } - .ignoresSafeArea() .scrollClipDisabled() - .padding(.leading, 24) } .frame(width: 570, height: 330) .background( @@ -162,7 +180,7 @@ struct AboutView: View { Spacer() } .frame(height: 330 + 100) - .offset(x: 270) + .offset(x: 250) } ) .background(Color.hotlineRed) @@ -191,7 +209,9 @@ struct AboutView: View { } } - contributors = newContributors + withAnimation { + contributors = newContributors + } } func checkForUpdate() async { diff --git a/Hotline/macOS/ChatView.swift b/Hotline/macOS/ChatView.swift index 98fb852..0875106 100644 --- a/Hotline/macOS/ChatView.swift +++ b/Hotline/macOS/ChatView.swift @@ -44,35 +44,17 @@ struct ChatView: View { .clipShape(RoundedRectangle(cornerRadius: 3)) } #endif - ScrollView(.vertical) { - HStack { - Spacer() - Text(msg.text.convertToAttributedStringWithLinks()) - .font(.system(size: 12)) - .fontDesign(.monospaced) - .textSelection(.enabled) - .tint(Color("Link Color")) - .frame(maxWidth: 400, alignment: .center) - .padding(16) - Spacer() - } - } - .frame(maxWidth: .infinity, maxHeight: 375) - .scrollBounceBehavior(.basedOnSize) -#if os(iOS) - .background(Color("Agreement Background")) -#elseif os(macOS) - .background(VisualEffectView(material: .titlebar, blendingMode: .withinWindow)) -#endif - .clipShape(RoundedRectangle(cornerRadius: 8)) - .padding(.bottom, 16) + ServerAgreementView(text: msg.text) + .padding(.bottom, 16) } // MARK: Server Message else if msg.type == .server { - Text(msg.text) - .lineSpacing(4) - .multilineTextAlignment(.leading) - .textSelection(.enabled) + HStack { + Spacer() + ServerMessageView(message: msg.text) + Spacer() + } + .padding(EdgeInsets(top: 2, leading: 0, bottom: 2, trailing: 0)) } // MARK: Status else if msg.type == .status { diff --git a/Hotline/macOS/ServerAgreementView.swift b/Hotline/macOS/ServerAgreementView.swift new file mode 100644 index 0000000..a1f96d9 --- /dev/null +++ b/Hotline/macOS/ServerAgreementView.swift @@ -0,0 +1,81 @@ +import SwiftUI + +fileprivate let MAX_AGREEMENT_HEIGHT: CGFloat = 280 + +struct ServerAgreementView: View { + let text: String + + @State private var expandable: Bool = false + @State private var expanded: Bool = false + + var body: some View { + ScrollView(.vertical) { + HStack(alignment: .top) { + Spacer() + Text(text.convertToAttributedStringWithLinks()) + .font(.system(size: 12)) + .fontDesign(.monospaced) + .textSelection(.enabled) + .tint(Color("Link Color")) + .frame(maxWidth: 400) + .padding(16) + .background( + GeometryReader { geometry in + Color.clear.onAppear { + if geometry.size.height > MAX_AGREEMENT_HEIGHT { + expandable = true + } + else { + expandable = false + } + } + } + ) + Spacer() + } + } + .scrollIndicators(.never) + .frame(maxWidth: .infinity, maxHeight: (expandable && expanded) ? nil : MAX_AGREEMENT_HEIGHT) + .scrollBounceBehavior(.basedOnSize) +#if os(iOS) + .background(Color("Agreement Background")) +#elseif os(macOS) + .background(VisualEffectView(material: .titlebar, blendingMode: .withinWindow)) +#endif + .overlay( + ZStack(alignment: .bottomTrailing) { + Group { + if !expandable || expanded { + EmptyView() + } + else { + Button(action: { + withAnimation(.easeOut(duration: 0.15)) { + expanded = true + } + }, label: { + Color.black + .opacity(0.00001) + .frame(width: 32, height: 32) + .overlay( + Image(systemName: "arrow.up.left.and.arrow.down.right") + .resizable() + .scaledToFit() + .fontWeight(.semibold) + .frame(width: 12, height: 12) + .foregroundColor(.primary.opacity(0.8)) + , alignment: .center) + }) + .buttonStyle(.plain) + .help("Expand Server Agreement") + } + } + } + , alignment: .bottomTrailing) + .clipShape(RoundedRectangle(cornerRadius: 8)) + } +} + +#Preview { + ServerAgreementView(text: "Hello there and welcome to this server.") +} diff --git a/Hotline/macOS/ServerMessageView.swift b/Hotline/macOS/ServerMessageView.swift new file mode 100644 index 0000000..373c2a6 --- /dev/null +++ b/Hotline/macOS/ServerMessageView.swift @@ -0,0 +1,26 @@ +import SwiftUI + +struct ServerMessageView: View { + let message: String + + var body: some View { + HStack(alignment: .center, spacing: 8) { + Image(systemName: "exclamationmark.triangle.fill") + .symbolRenderingMode(.multicolor) + .resizable() + .scaledToFit() + .frame(width: 16, height: 16) + Text("**\(message)**") + .lineSpacing(4) + .multilineTextAlignment(.leading) + .textSelection(.enabled) + } + .padding() + .background(Color("Agreement Background")) + .clipShape(RoundedRectangle(cornerRadius: 8)) + } +} + +#Preview { + ServerMessageView(message: "This server has something important to say.") +} diff --git a/Hotline/macOS/ServerView.swift b/Hotline/macOS/ServerView.swift index 190bcc2..21d7cbb 100644 --- a/Hotline/macOS/ServerView.swift +++ b/Hotline/macOS/ServerView.swift @@ -400,19 +400,19 @@ struct ServerView: View { } Text(user.name) - .foregroundStyle(user.isAdmin ? Color(hex: 0xE10000) : .primary) + .foregroundStyle(user.isAdmin ? Color.hotlineRed : .primary) Spacer() if model.hasUnreadInstantMessages(userID: user.id) { Circle() .frame(width: 6, height: 6) + .foregroundStyle(user.isAdmin ? Color.hotlineRed : .primary.opacity(0.5)) .padding(EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 2)) - .opacity(0.5) } } - .opacity(user.isIdle ? 0.6 : 1.0) - .opacity(controlActiveState == .inactive ? 0.4 : 1.0) + .opacity(user.isIdle ? 0.5 : 1.0) + .opacity(controlActiveState == .inactive ? 0.5 : 1.0) .tag(ServerNavigationType.user(userID: user.id)) } } diff --git a/Hotline/macOS/SettingsView.swift b/Hotline/macOS/SettingsView.swift index 24cfddf..79f0156 100644 --- a/Hotline/macOS/SettingsView.swift +++ b/Hotline/macOS/SettingsView.swift @@ -71,6 +71,7 @@ struct IconSettingsView: View { .interpolation(.none) .scaledToFit() .frame(width: 32, height: 16) + .help("Icon \(String(iconID))") } .tag(iconID) .frame(width: 32, height: 32) |