Add avatar position setting (#14)

Co-authored-by: Jérôme Danthinne <jerome.danthinne@smile.eu>
This commit is contained in:
Jérôme Danthinne 2022-12-31 12:29:19 +01:00 committed by GitHub
parent 315a57b447
commit 810e9fbe6b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 17 deletions

View file

@ -71,11 +71,17 @@ struct SettingsTabs: View {
ColorPicker("Tint color", selection: $theme.tintColor) ColorPicker("Tint color", selection: $theme.tintColor)
ColorPicker("Background color", selection: $theme.primaryBackgroundColor) ColorPicker("Background color", selection: $theme.primaryBackgroundColor)
ColorPicker("Secondary Background color", selection: $theme.secondaryBackgroundColor) ColorPicker("Secondary Background color", selection: $theme.secondaryBackgroundColor)
Picker("Avatar position", selection: $theme.avatarPosition) {
ForEach(Theme.AvatarPosition.allCases, id: \.rawValue) { position in
Text(position.description).tag(position)
}
}
Button { Button {
theme.colorScheme = "dark" theme.colorScheme = "dark"
theme.tintColor = .brand theme.tintColor = .brand
theme.primaryBackgroundColor = .primaryBackground theme.primaryBackgroundColor = .primaryBackground
theme.secondaryBackgroundColor = .secondaryBackground theme.secondaryBackgroundColor = .secondaryBackground
theme.avatarPosition = .top
} label: { } label: {
Text("Restore default") Text("Restore default")
} }

View file

@ -4,5 +4,6 @@ public struct DS {
public enum Constants { public enum Constants {
public static let layoutPadding: CGFloat = 20 public static let layoutPadding: CGFloat = 20
public static let dividerPadding: CGFloat = 4 public static let dividerPadding: CGFloat = 4
public static let statusColumnsSpacing: CGFloat = 8
} }
} }

View file

@ -1,8 +1,23 @@
import Combine
import SwiftUI 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
}
public enum AvatarPosition: String, CaseIterable {
case leading, top
public var description: LocalizedStringKey {
switch self {
case .leading:
return "Leading"
case .top:
return "Top"
}
}
} }
@AppStorage("is_previously_set") var isSet: Bool = false @AppStorage("is_previously_set") var isSet: Bool = false
@ -19,12 +34,27 @@ public class Theme: ObservableObject {
@AppStorage(ThemeKey.primaryBackground.rawValue) public var primaryBackgroundColor: Color = .white @AppStorage(ThemeKey.primaryBackground.rawValue) public var primaryBackgroundColor: Color = .white
@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
@Published public var avatarPosition: AvatarPosition = .top
private var cancellables = Set<AnyCancellable>()
public init() { public init() {
if !isSet { if !isSet {
setColor(set: DarkSet()) setColor(set: DarkSet())
isSet.toggle() isSet.toggle()
} }
avatarPosition = AvatarPosition(rawValue: rawAvatarPosition) ?? .top
$avatarPosition
.dropFirst()
.map(\.rawValue)
.sink { [weak self] position in
self?.rawAvatarPosition = position
}
.store(in: &cancellables)
} }
public func setColor(set: ColorSet) { public func setColor(set: ColorSet) {

View file

@ -6,7 +6,7 @@ public struct AvatarView: View {
public enum Size { public enum Size {
case account, status, embed, badge, boost case account, status, embed, badge, boost
var size: CGSize { public var size: CGSize {
switch self { switch self {
case .account: case .account:
return .init(width: 80, height: 80) return .init(width: 80, height: 80)

View file

@ -7,6 +7,7 @@ import DesignSystem
public struct StatusMediaPreviewView: View { public struct StatusMediaPreviewView: View {
@EnvironmentObject private var quickLook: QuickLook @EnvironmentObject private var quickLook: QuickLook
@EnvironmentObject private var theme: Theme
public let attachements: [MediaAttachement] public let attachements: [MediaAttachement]
public let isCompact: Bool public let isCompact: Bool
@ -95,8 +96,10 @@ public struct StatusMediaPreviewView: View {
switch attachement.supportedType { switch attachement.supportedType {
case .image: case .image:
if let size = size(for: attachement) { if let size = size(for: attachement) {
let avatarColumnWidth = theme.avatarPosition == .leading ? AvatarView.Size.status.size.width + DS.Constants.statusColumnsSpacing : 0
let availableWidth = UIScreen.main.bounds.width - (DS.Constants.layoutPadding * 2) - avatarColumnWidth
let newSize = imageSize(from: size, let newSize = imageSize(from: size,
newWidth: UIScreen.main.bounds.width - (DS.Constants.layoutPadding * 2)) newWidth: availableWidth)
LazyImage(url: attachement.url) { state in LazyImage(url: attachement.url) { state in
if let image = state.image { if let image = state.image {
image image

View file

@ -18,20 +18,27 @@ public struct StatusRowView: View {
} }
public var body: some View { public var body: some View {
VStack(alignment: .leading) { HStack(alignment: .top, spacing: DS.Constants.statusColumnsSpacing) {
if !viewModel.isCompact { if !viewModel.isCompact,
reblogView theme.avatarPosition == .leading,
replyView let status: AnyStatus = viewModel.status.reblog ?? viewModel.status {
AvatarView(url: status.account.avatar, size: .status)
} }
statusView VStack(alignment: .leading) {
if !viewModel.isCompact { if !viewModel.isCompact {
StatusActionsView(viewModel: viewModel) reblogView
.padding(.vertical, 8) replyView
.tint(viewModel.isFocused ? theme.tintColor : .gray) }
.contentShape(Rectangle()) statusView
.onTapGesture { if !viewModel.isCompact {
routeurPath.navigate(to: .statusDetail(id: viewModel.status.reblog?.id ?? viewModel.status.id)) StatusActionsView(viewModel: viewModel)
} .padding(.vertical, 8)
.tint(viewModel.isFocused ? theme.tintColor : .gray)
.contentShape(Rectangle())
.onTapGesture {
routeurPath.navigate(to: .statusDetail(id: viewModel.status.reblog?.id ?? viewModel.status.id))
}
}
} }
} }
.onAppear { .onAppear {
@ -155,7 +162,9 @@ public struct StatusRowView: View {
@ViewBuilder @ViewBuilder
private func accountView(status: AnyStatus) -> some View { private func accountView(status: AnyStatus) -> some View {
HStack(alignment: .center) { HStack(alignment: .center) {
AvatarView(url: status.account.avatar, size: .status) if theme.avatarPosition == .top {
AvatarView(url: status.account.avatar, size: .status)
}
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
status.account.displayNameWithEmojis status.account.displayNameWithEmojis
.font(.headline) .font(.headline)