Avatar shape (#30)

* Avatar shape settings

* chore: fix rebase

Co-authored-by: Jérôme Danthinne <jerome.danthinne@smile.eu>
This commit is contained in:
Jérôme Danthinne 2023-01-04 17:48:02 +01:00 committed by GitHub
parent d152f14bdc
commit 62b96cac69
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 23 deletions

View file

@ -75,6 +75,11 @@ struct SettingsTabs: View {
Text(position.description).tag(position) 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 { Button {
theme.selectedSet = .iceCubeDark theme.selectedSet = .iceCubeDark
} label: { } label: {

View file

@ -72,10 +72,6 @@ struct AccountDetailHeaderView: View {
private var accountAvatarView: some View { private var accountAvatarView: some View {
HStack { HStack {
AvatarView(url: account.avatar, size: .account) AvatarView(url: account.avatar, size: .account)
.overlay(
RoundedRectangle(cornerRadius: 4)
.stroke(.white, lineWidth: 1)
)
.onTapGesture { .onTapGesture {
Task { Task {
await quickLook.prepareFor(urls: [account.avatar], selectedURL: account.avatar) await quickLook.prepareFor(urls: [account.avatar], selectedURL: account.avatar)

View file

@ -4,7 +4,7 @@ import SwiftUI
public class Theme: ObservableObject { public class Theme: ObservableObject {
enum ThemeKey: String { enum ThemeKey: String {
case colorScheme, tint, label, primaryBackground, secondaryBackground case colorScheme, tint, label, primaryBackground, secondaryBackground
case avatarPosition case avatarPosition, avatarShape
case selectedSet, selectedScheme 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("is_previously_set") private var isSet: Bool = false
@AppStorage(ThemeKey.selectedScheme.rawValue) public var selectedScheme: ColorScheme = .dark @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.secondaryBackground.rawValue) public var secondaryBackgroundColor: Color = .gray
@AppStorage(ThemeKey.label.rawValue) public var labelColor: Color = .black @AppStorage(ThemeKey.label.rawValue) public var labelColor: Color = .black
@AppStorage(ThemeKey.avatarPosition.rawValue) var rawAvatarPosition: String = AvatarPosition.top.rawValue @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 @AppStorage(ThemeKey.selectedSet.rawValue) var storedSet: ColorSetName = .iceCubeDark
@Published public var avatarPosition: AvatarPosition = .top @Published public var avatarPosition: AvatarPosition = .top
@Published public var avatarShape: AvatarShape = .rounded
@Published public var selectedSet: ColorSetName = .iceCubeDark @Published public var selectedSet: ColorSetName = .iceCubeDark
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
public init() { public init() {
@ -45,7 +61,8 @@ public class Theme: ObservableObject {
} }
avatarPosition = AvatarPosition(rawValue: rawAvatarPosition) ?? .top avatarPosition = AvatarPosition(rawValue: rawAvatarPosition) ?? .top
avatarShape = AvatarShape(rawValue: rawAvatarShape) ?? .rounded
$avatarPosition $avatarPosition
.dropFirst() .dropFirst()
.map(\.rawValue) .map(\.rawValue)
@ -53,6 +70,14 @@ public class Theme: ObservableObject {
self?.rawAvatarPosition = position self?.rawAvatarPosition = position
} }
.store(in: &cancellables) .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 // Workaround, since @AppStorage can't be directly observed
$selectedSet $selectedSet

View file

@ -3,6 +3,8 @@ import Shimmer
import NukeUI import NukeUI
public struct AvatarView: View { public struct AvatarView: View {
@EnvironmentObject private var theme: Theme
public enum Size { public enum Size {
case account, status, embed, badge, boost case account, status, embed, badge, boost
@ -41,24 +43,39 @@ public struct AvatarView: View {
} }
public var body: some View { public var body: some View {
if reasons == .placeholder { Group {
RoundedRectangle(cornerRadius: size.cornerRadius) if reasons == .placeholder {
.fill(.gray) RoundedRectangle(cornerRadius: size.cornerRadius)
.frame(maxWidth: size.size.width, maxHeight: size.size.height) .fill(.gray)
} else { .frame(maxWidth: size.size.width, maxHeight: size.size.height)
LazyImage(url: url) { state in
if let image = state.image {
image
.resizingMode(.aspectFit)
} else if state.isLoading {
placeholderView
.shimmering()
} else { } 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)]) .clipShape(clipShape)
.frame(width: size.size.width, height: size.size.height) .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))
} }
} }