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("Background color", selection: $theme.primaryBackgroundColor)
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 {
theme.colorScheme = "dark"
theme.tintColor = .brand
theme.primaryBackgroundColor = .primaryBackground
theme.secondaryBackgroundColor = .secondaryBackground
theme.avatarPosition = .top
} label: {
Text("Restore default")
}

View file

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

View file

@ -1,8 +1,23 @@
import Combine
import SwiftUI
public class Theme: ObservableObject {
enum ThemeKey: String {
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
@ -19,12 +34,27 @@ public class Theme: ObservableObject {
@AppStorage(ThemeKey.primaryBackground.rawValue) public var primaryBackgroundColor: Color = .white
@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
@Published public var avatarPosition: AvatarPosition = .top
private var cancellables = Set<AnyCancellable>()
public init() {
if !isSet {
setColor(set: DarkSet())
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) {

View file

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

View file

@ -7,6 +7,7 @@ import DesignSystem
public struct StatusMediaPreviewView: View {
@EnvironmentObject private var quickLook: QuickLook
@EnvironmentObject private var theme: Theme
public let attachements: [MediaAttachement]
public let isCompact: Bool
@ -95,8 +96,10 @@ public struct StatusMediaPreviewView: View {
switch attachement.supportedType {
case .image:
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,
newWidth: UIScreen.main.bounds.width - (DS.Constants.layoutPadding * 2))
newWidth: availableWidth)
LazyImage(url: attachement.url) { state in
if let image = state.image {
image

View file

@ -18,20 +18,27 @@ public struct StatusRowView: View {
}
public var body: some View {
VStack(alignment: .leading) {
if !viewModel.isCompact {
reblogView
replyView
HStack(alignment: .top, spacing: DS.Constants.statusColumnsSpacing) {
if !viewModel.isCompact,
theme.avatarPosition == .leading,
let status: AnyStatus = viewModel.status.reblog ?? viewModel.status {
AvatarView(url: status.account.avatar, size: .status)
}
statusView
if !viewModel.isCompact {
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))
}
VStack(alignment: .leading) {
if !viewModel.isCompact {
reblogView
replyView
}
statusView
if !viewModel.isCompact {
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 {
@ -155,7 +162,9 @@ public struct StatusRowView: View {
@ViewBuilder
private func accountView(status: AnyStatus) -> some View {
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) {
status.account.displayNameWithEmojis
.font(.headline)