From 62b96cac69d9a63d6030f9bf1773440e45c6a829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Danthinne?= Date: Wed, 4 Jan 2023 17:48:02 +0100 Subject: [PATCH] Avatar shape (#30) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Avatar shape settings * chore: fix rebase Co-authored-by: Jérôme Danthinne --- .../App/Tabs/Settings/SettingsTab.swift | 5 ++ .../Account/AccountDetailHeaderView.swift | 4 -- .../Sources/DesignSystem/Theme.swift | 31 ++++++++++-- .../DesignSystem/Views/AvatarView.swift | 49 +++++++++++++------ 4 files changed, 66 insertions(+), 23 deletions(-) diff --git a/IceCubesApp/App/Tabs/Settings/SettingsTab.swift b/IceCubesApp/App/Tabs/Settings/SettingsTab.swift index e8a20261..4c68e783 100644 --- a/IceCubesApp/App/Tabs/Settings/SettingsTab.swift +++ b/IceCubesApp/App/Tabs/Settings/SettingsTab.swift @@ -75,6 +75,11 @@ struct SettingsTabs: View { Text(position.description).tag(position) } } + Picker("Avatar shape", selection: $theme.avatarShape) { + ForEach(Theme.AvatarShape.allCases, id: \.rawValue) { shape in + Text(shape.description).tag(shape) + } + } Button { theme.selectedSet = .iceCubeDark } label: { diff --git a/Packages/Account/Sources/Account/AccountDetailHeaderView.swift b/Packages/Account/Sources/Account/AccountDetailHeaderView.swift index 7e16d126..235d174d 100644 --- a/Packages/Account/Sources/Account/AccountDetailHeaderView.swift +++ b/Packages/Account/Sources/Account/AccountDetailHeaderView.swift @@ -72,10 +72,6 @@ struct AccountDetailHeaderView: View { private var accountAvatarView: some View { HStack { AvatarView(url: account.avatar, size: .account) - .overlay( - RoundedRectangle(cornerRadius: 4) - .stroke(.white, lineWidth: 1) - ) .onTapGesture { Task { await quickLook.prepareFor(urls: [account.avatar], selectedURL: account.avatar) diff --git a/Packages/DesignSystem/Sources/DesignSystem/Theme.swift b/Packages/DesignSystem/Sources/DesignSystem/Theme.swift index 33c429ae..e5613d69 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Theme.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Theme.swift @@ -4,7 +4,7 @@ import SwiftUI public class Theme: ObservableObject { enum ThemeKey: String { case colorScheme, tint, label, primaryBackground, secondaryBackground - case avatarPosition + case avatarPosition, avatarShape case selectedSet, selectedScheme } @@ -20,6 +20,19 @@ public class Theme: ObservableObject { } } } + + public enum AvatarShape: String, CaseIterable { + case circle, rounded + + public var description: LocalizedStringKey { + switch self { + case .circle: + return "Circle" + case .rounded: + return "Rounded" + } + } + } @AppStorage("is_previously_set") private var isSet: Bool = false @AppStorage(ThemeKey.selectedScheme.rawValue) public var selectedScheme: ColorScheme = .dark @@ -28,10 +41,13 @@ public class Theme: ObservableObject { @AppStorage(ThemeKey.secondaryBackground.rawValue) public var secondaryBackgroundColor: Color = .gray @AppStorage(ThemeKey.label.rawValue) public var labelColor: Color = .black @AppStorage(ThemeKey.avatarPosition.rawValue) var rawAvatarPosition: String = AvatarPosition.top.rawValue + @AppStorage(ThemeKey.avatarShape.rawValue) var rawAvatarShape: String = AvatarShape.rounded.rawValue @AppStorage(ThemeKey.selectedSet.rawValue) var storedSet: ColorSetName = .iceCubeDark + @Published public var avatarPosition: AvatarPosition = .top + @Published public var avatarShape: AvatarShape = .rounded @Published public var selectedSet: ColorSetName = .iceCubeDark - + private var cancellables = Set() public init() { @@ -45,7 +61,8 @@ public class Theme: ObservableObject { } avatarPosition = AvatarPosition(rawValue: rawAvatarPosition) ?? .top - + avatarShape = AvatarShape(rawValue: rawAvatarShape) ?? .rounded + $avatarPosition .dropFirst() .map(\.rawValue) @@ -53,6 +70,14 @@ public class Theme: ObservableObject { self?.rawAvatarPosition = position } .store(in: &cancellables) + + $avatarShape + .dropFirst() + .map(\.rawValue) + .sink { [weak self] shape in + self?.rawAvatarShape = shape + } + .store(in: &cancellables) // Workaround, since @AppStorage can't be directly observed $selectedSet diff --git a/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift b/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift index 7b69bd60..d51eed7e 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Views/AvatarView.swift @@ -3,6 +3,8 @@ import Shimmer import NukeUI public struct AvatarView: View { + @EnvironmentObject private var theme: Theme + public enum Size { case account, status, embed, badge, boost @@ -41,24 +43,39 @@ public struct AvatarView: View { } public var body: some View { - if reasons == .placeholder { - RoundedRectangle(cornerRadius: size.cornerRadius) - .fill(.gray) - .frame(maxWidth: size.size.width, maxHeight: size.size.height) - } else { - LazyImage(url: url) { state in - if let image = state.image { - image - .resizingMode(.aspectFit) - } else if state.isLoading { - placeholderView - .shimmering() + Group { + if reasons == .placeholder { + RoundedRectangle(cornerRadius: size.cornerRadius) + .fill(.gray) + .frame(maxWidth: size.size.width, maxHeight: size.size.height) } else { - placeholderView + LazyImage(url: url) { state in + if let image = state.image { + image + .resizingMode(.aspectFit) + } else if state.isLoading { + placeholderView + .shimmering() + } else { + placeholderView + } + } + .processors([.resize(size: size.size), .roundedCorners(radius: size.cornerRadius)]) + .frame(width: size.size.width, height: size.size.height) } - } - .processors([.resize(size: size.size), .roundedCorners(radius: size.cornerRadius)]) - .frame(width: size.size.width, height: size.size.height) + } + .clipShape(clipShape) + .overlay( + clipShape.stroke(Color.primary.opacity(0.25), lineWidth: 1) + ) + } + + private var clipShape: some Shape { + switch theme.avatarShape { + case .circle: + return AnyShape(Circle()) + case .rounded: + return AnyShape(RoundedRectangle(cornerRadius: size.cornerRadius)) } }