Redesigned settings tab
This commit is contained in:
parent
03afa1f978
commit
7f5330f284
|
@ -30,6 +30,7 @@
|
|||
9F7335EF29674F7100AFF0BA /* QuickLook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F7335EE29674F7100AFF0BA /* QuickLook.framework */; };
|
||||
9F7335F22967608F00AFF0BA /* AddRemoteTimelineVIew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F7335F12967608F00AFF0BA /* AddRemoteTimelineVIew.swift */; };
|
||||
9F7335F72968274500AFF0BA /* AppAccountsSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F7335F62968274500AFF0BA /* AppAccountsSelectorView.swift */; };
|
||||
9F7335F92968576500AFF0BA /* DisplaySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F7335F82968576500AFF0BA /* DisplaySettingsView.swift */; };
|
||||
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAE4ACA293783B000772766 /* SettingsTab.swift */; };
|
||||
9FAE4ACE29379A5A00772766 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAE4ACD29379A5A00772766 /* KeychainSwift */; };
|
||||
9FAE4AD129379AD600772766 /* AppAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAE4AD029379AD600772766 /* AppAccount.swift */; };
|
||||
|
@ -67,6 +68,7 @@
|
|||
9F7335EE29674F7100AFF0BA /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.2.sdk/System/Library/Frameworks/QuickLook.framework; sourceTree = DEVELOPER_DIR; };
|
||||
9F7335F12967608F00AFF0BA /* AddRemoteTimelineVIew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRemoteTimelineVIew.swift; sourceTree = "<group>"; };
|
||||
9F7335F62968274500AFF0BA /* AppAccountsSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAccountsSelectorView.swift; sourceTree = "<group>"; };
|
||||
9F7335F82968576500AFF0BA /* DisplaySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplaySettingsView.swift; sourceTree = "<group>"; };
|
||||
9FAE4AC8293774FF00772766 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
9FAE4ACA293783B000772766 /* SettingsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTab.swift; sourceTree = "<group>"; };
|
||||
9FAE4AD029379AD600772766 /* AppAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAccount.swift; sourceTree = "<group>"; };
|
||||
|
@ -212,6 +214,7 @@
|
|||
9FE151A5293C90F900E9683D /* IconSelectorView.swift */,
|
||||
9F2B92F9295DA7D700DE16D0 /* AddAccountsView.swift */,
|
||||
9F2B92FB295DA94500DE16D0 /* InstanceInfoView.swift */,
|
||||
9F7335F82968576500AFF0BA /* DisplaySettingsView.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -306,6 +309,7 @@
|
|||
9F2B92FC295DA94500DE16D0 /* InstanceInfoView.swift in Sources */,
|
||||
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */,
|
||||
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
|
||||
9F7335F92968576500AFF0BA /* DisplaySettingsView.swift in Sources */,
|
||||
9FAE4AD32937A0C600772766 /* AppAccountsManager.swift in Sources */,
|
||||
9F2B92FF295EB87100DE16D0 /* AppAccountView.swift in Sources */,
|
||||
9F2B92F6295AE04800DE16D0 /* Tabs.swift in Sources */,
|
||||
|
|
|
@ -53,6 +53,8 @@ extension View {
|
|||
ListAddAccountView(account: account)
|
||||
case .addAccount:
|
||||
AddAccountView()
|
||||
case .addRemoteLocalTimeline:
|
||||
AddRemoteTimelineView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import DesignSystem
|
||||
import Status
|
||||
|
||||
struct DisplaySettingsView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
@State private var isThemeSelectorPresented = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section("Theme") {
|
||||
themeSelectorButton
|
||||
ColorPicker("Tint color", selection: $theme.tintColor)
|
||||
ColorPicker("Background color", selection: $theme.primaryBackgroundColor)
|
||||
ColorPicker("Secondary Background color", selection: $theme.secondaryBackgroundColor)
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
||||
Section("Display") {
|
||||
Picker("Avatar position", selection: $theme.avatarPosition) {
|
||||
ForEach(Theme.AvatarPosition.allCases, id: \.rawValue) { position in
|
||||
Text(position.description).tag(position)
|
||||
}
|
||||
}
|
||||
Picker("Avatar shape", selection: $theme.avatarShape) {
|
||||
ForEach(Theme.AvatarShape.allCases, id: \.rawValue) { shape in
|
||||
Text(shape.description).tag(shape)
|
||||
}
|
||||
}
|
||||
Picker("Status actions buttons", selection: $theme.statusActionsDisplay) {
|
||||
ForEach(Theme.StatusActionsDisplay.allCases, id: \.rawValue) { buttonStyle in
|
||||
Text(buttonStyle.description).tag(buttonStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
||||
Section {
|
||||
Button {
|
||||
theme.selectedSet = .iceCubeDark
|
||||
theme.avatarShape = .rounded
|
||||
theme.avatarPosition = .top
|
||||
theme.statusActionsDisplay = .full
|
||||
} label: {
|
||||
Text("Restore default")
|
||||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
.navigationTitle("Display Settings")
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.secondaryBackgroundColor)
|
||||
}
|
||||
|
||||
private var themeSelectorButton: some View {
|
||||
NavigationLink(destination: ThemePreviewView()) {
|
||||
HStack {
|
||||
Text("Theme")
|
||||
Spacer()
|
||||
Text(theme.selectedSet.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ struct InstanceInfoView: View {
|
|||
let instance: Instance
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section("Instance info") {
|
||||
LabeledContent("Name", value: instance.title)
|
||||
Text(instance.shortDescription)
|
||||
|
@ -27,4 +28,8 @@ struct InstanceInfoView: View {
|
|||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
.navigationTitle("Instance Info")
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.secondaryBackgroundColor)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import Models
|
|||
import DesignSystem
|
||||
|
||||
struct SettingsTabs: View {
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var client: Client
|
||||
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||
@EnvironmentObject private var appAccountsManager: AppAccountsManager
|
||||
|
@ -15,21 +16,21 @@ struct SettingsTabs: View {
|
|||
@StateObject private var routeurPath = RouterPath()
|
||||
|
||||
@State private var addAccountSheetPresented = false
|
||||
@State private var isThemeSelectorPresented = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack(path: $routeurPath.path) {
|
||||
Form {
|
||||
appSection
|
||||
accountsSection
|
||||
themeSection
|
||||
instanceSection
|
||||
generalSection
|
||||
}
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.secondaryBackgroundColor)
|
||||
.navigationTitle(Text("Settings"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbarBackground(theme.primaryBackgroundColor, for: .navigationBar)
|
||||
.withAppRouteur()
|
||||
.withSheetDestinations(sheetDestinations: $routeurPath.presentedSheet)
|
||||
}
|
||||
.onAppear {
|
||||
routeurPath.client = client
|
||||
|
@ -64,43 +65,29 @@ struct SettingsTabs: View {
|
|||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
|
||||
private var themeSection: some View {
|
||||
Section("Theme") {
|
||||
themeSelectorButton
|
||||
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)
|
||||
@ViewBuilder
|
||||
private var generalSection: some View {
|
||||
Section("General") {
|
||||
if let instanceData = currentInstance.instance {
|
||||
NavigationLink(destination: InstanceInfoView(instance: instanceData)) {
|
||||
Label("Instance Information", systemImage: "server.rack")
|
||||
}
|
||||
}
|
||||
Picker("Avatar shape", selection: $theme.avatarShape) {
|
||||
ForEach(Theme.AvatarShape.allCases, id: \.rawValue) { shape in
|
||||
Text(shape.description).tag(shape)
|
||||
NavigationLink(destination: DisplaySettingsView()) {
|
||||
Label("Display Settings", systemImage: "paintpalette")
|
||||
}
|
||||
}
|
||||
Button {
|
||||
theme.selectedSet = .iceCubeDark
|
||||
} label: {
|
||||
Text("Restore default")
|
||||
NavigationLink(destination: remoteLocalTimelinesView) {
|
||||
Label("Remote Local Timelines", systemImage: "dot.radiowaves.right")
|
||||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var instanceSection: some View {
|
||||
if let instanceData = currentInstance.instance {
|
||||
InstanceInfoView(instance: instanceData)
|
||||
}
|
||||
}
|
||||
|
||||
private var appSection: some View {
|
||||
Section("App") {
|
||||
NavigationLink(destination: IconSelectorView()) {
|
||||
Label {
|
||||
Text("Icon selector")
|
||||
Text("App Icon")
|
||||
} icon: {
|
||||
if let icon = IconSelectorView.Icon(string: UIApplication.shared.alternateIconName ?? "AppIcon") {
|
||||
Image(uiImage: .init(named: icon.iconName)!)
|
||||
|
@ -111,7 +98,7 @@ struct SettingsTabs: View {
|
|||
}
|
||||
}
|
||||
Link(destination: URL(string: "https://github.com/Dimillian/IceCubesApp")!) {
|
||||
Text("https://github.com/Dimillian/IceCubesApp")
|
||||
Label("Source (Github link)", systemImage: "link")
|
||||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
@ -128,25 +115,25 @@ struct SettingsTabs: View {
|
|||
}
|
||||
}
|
||||
|
||||
private var themeSelectorButton: some View {
|
||||
NavigationLink(destination: ThemePreviewView()) {
|
||||
private var remoteLocalTimelinesView: some View {
|
||||
Form {
|
||||
ForEach(preferences.remoteLocalTimelines, id: \.self) { server in
|
||||
Text(server)
|
||||
}.onDelete { indexes in
|
||||
if let index = indexes.first {
|
||||
_ = preferences.remoteLocalTimelines.remove(at: index)
|
||||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
Button {
|
||||
isThemeSelectorPresented.toggle()
|
||||
routeurPath.presentedSheet = .addRemoteLocalTimeline
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Theme")
|
||||
Spacer()
|
||||
Text(theme.selectedSet.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var signOutButton: some View {
|
||||
Button {
|
||||
appAccountsManager.delete(account: appAccountsManager.currentAccount)
|
||||
} label: {
|
||||
Text("Sign out").foregroundColor(.red)
|
||||
}
|
||||
Label("Add a local timeline", systemImage: "badge.plus.radiowaves.right")
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
.navigationTitle("Remote Local Timelines")
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.secondaryBackgroundColor)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,10 @@ import DesignSystem
|
|||
import NukeUI
|
||||
import Shimmer
|
||||
|
||||
struct AddRemoteTimelineVIew: View {
|
||||
struct AddRemoteTimelineView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
@State private var instanceName: String = ""
|
||||
|
@ -17,8 +18,6 @@ struct AddRemoteTimelineVIew: View {
|
|||
|
||||
@FocusState private var isInstanceURLFieldFocused: Bool
|
||||
|
||||
@Binding var addedInstance: String
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
|
@ -36,7 +35,7 @@ struct AddRemoteTimelineVIew: View {
|
|||
}
|
||||
Button {
|
||||
guard instance != nil else { return }
|
||||
addedInstance = instanceName
|
||||
preferences.remoteLocalTimelines.append(instanceName)
|
||||
dismiss()
|
||||
} label: {
|
||||
Text("Add")
|
||||
|
|
|
@ -18,9 +18,6 @@ struct TimelineTab: View {
|
|||
@State private var timeline: TimelineFilter = .home
|
||||
@State private var scrollToTopSignal: Int = 0
|
||||
|
||||
@State private var newlyAddedLocalTimeline: String = ""
|
||||
@State private var isAddRemoteLocalTimelinePresented: Bool = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack(path: $routeurPath.path) {
|
||||
TimelineView(timeline: $timeline, scrollToTopSignal: $scrollToTopSignal)
|
||||
|
@ -31,9 +28,6 @@ struct TimelineTab: View {
|
|||
}
|
||||
.id(currentAccount.account?.id)
|
||||
}
|
||||
.sheet(isPresented: $isAddRemoteLocalTimelinePresented) {
|
||||
AddRemoteTimelineVIew(addedInstance: $newlyAddedLocalTimeline)
|
||||
}
|
||||
.onAppear {
|
||||
routeurPath.client = client
|
||||
if !didAppear {
|
||||
|
@ -62,13 +56,6 @@ struct TimelineTab: View {
|
|||
.onChange(of: currentAccount.account?.id) { _ in
|
||||
routeurPath.path = []
|
||||
}
|
||||
.onChange(of: isAddRemoteLocalTimelinePresented) { isPresented in
|
||||
if !isPresented && !newlyAddedLocalTimeline.isEmpty {
|
||||
preferences.remoteLocalTimelines.append(newlyAddedLocalTimeline)
|
||||
timeline = .remoteLocal(server: newlyAddedLocalTimeline)
|
||||
newlyAddedLocalTimeline = ""
|
||||
}
|
||||
}
|
||||
.environmentObject(routeurPath)
|
||||
}
|
||||
|
||||
|
@ -106,7 +93,6 @@ struct TimelineTab: View {
|
|||
}
|
||||
}
|
||||
|
||||
if !preferences.remoteLocalTimelines.isEmpty {
|
||||
Menu("Local Timelines") {
|
||||
ForEach(preferences.remoteLocalTimelines, id: \.self) { server in
|
||||
Button {
|
||||
|
@ -115,15 +101,12 @@ struct TimelineTab: View {
|
|||
Label(server, systemImage: "dot.radiowaves.right")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
isAddRemoteLocalTimelinePresented = true
|
||||
routeurPath.presentedSheet = .addRemoteLocalTimeline
|
||||
} label: {
|
||||
Label("Add a local timeline", systemImage: "badge.plus.radiowaves.right")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private var addAccountButton: some View {
|
||||
|
|
|
@ -4,7 +4,7 @@ import SwiftUI
|
|||
public class Theme: ObservableObject {
|
||||
enum ThemeKey: String {
|
||||
case colorScheme, tint, label, primaryBackground, secondaryBackground
|
||||
case avatarPosition, avatarShape
|
||||
case avatarPosition, avatarShape, statusActionsDisplay
|
||||
case selectedSet, selectedScheme
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,21 @@ public class Theme: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
public enum StatusActionsDisplay: String, CaseIterable {
|
||||
case full, discret, none
|
||||
|
||||
public var description: LocalizedStringKey {
|
||||
switch self {
|
||||
case .full:
|
||||
return "All"
|
||||
case .discret:
|
||||
return "Only buttons"
|
||||
case .none:
|
||||
return "No buttons"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AppStorage("is_previously_set") private var isSet: Bool = false
|
||||
@AppStorage(ThemeKey.selectedScheme.rawValue) public var selectedScheme: ColorScheme = .dark
|
||||
@AppStorage(ThemeKey.tint.rawValue) public var tintColor: Color = .black
|
||||
|
@ -43,6 +58,7 @@ public class Theme: ObservableObject {
|
|||
@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.statusActionsDisplay.rawValue) public var statusActionsDisplay: StatusActionsDisplay = .full
|
||||
|
||||
@Published public var avatarPosition: AvatarPosition = .top
|
||||
@Published public var avatarShape: AvatarShape = .rounded
|
||||
|
|
|
@ -25,6 +25,7 @@ public enum SheetDestinations: Identifiable {
|
|||
case listEdit(list: Models.List)
|
||||
case listAddAccount(account: Account)
|
||||
case addAccount
|
||||
case addRemoteLocalTimeline
|
||||
|
||||
public var id: String {
|
||||
switch self {
|
||||
|
@ -36,6 +37,8 @@ public enum SheetDestinations: Identifiable {
|
|||
return "listAddAccount"
|
||||
case .addAccount:
|
||||
return "addAccount"
|
||||
case .addRemoteLocalTimeline:
|
||||
return "addRemoteLocalTimeline"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ public struct Status: AnyStatus, Codable, Identifiable {
|
|||
|
||||
public static func placeholder() -> Status {
|
||||
.init(id: UUID().uuidString,
|
||||
content: "Some post content\n Some more post content \n Some more",
|
||||
content: "This is a #toot\nWith some @content\nAnd some more content for your #eyes @only",
|
||||
account: .placeholder(),
|
||||
createdAt: "2022-12-16T10:20:54.000Z",
|
||||
editedAt: nil,
|
||||
|
|
|
@ -29,7 +29,10 @@ struct StatusActionsView: View {
|
|||
}
|
||||
}
|
||||
|
||||
func count(viewModel: StatusRowViewModel) -> Int? {
|
||||
func count(viewModel: StatusRowViewModel, theme: Theme) -> Int? {
|
||||
if theme.statusActionsDisplay == .discret {
|
||||
return nil
|
||||
}
|
||||
switch self {
|
||||
case .respond:
|
||||
return viewModel.repliesCount
|
||||
|
@ -71,7 +74,7 @@ struct StatusActionsView: View {
|
|||
HStack(spacing: 2) {
|
||||
Image(systemName: action.iconName(viewModel: viewModel))
|
||||
.foregroundColor(action.tintColor(viewModel: viewModel, theme: theme))
|
||||
if let count = action.count(viewModel: viewModel) {
|
||||
if let count = action.count(viewModel: viewModel, theme: theme) {
|
||||
Text("\(count)")
|
||||
.font(.footnote)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public struct StatusRowView: View {
|
|||
replyView
|
||||
}
|
||||
statusView
|
||||
if !viewModel.isCompact && !viewModel.isRemote {
|
||||
if !viewModel.isCompact && !viewModel.isRemote, theme.statusActionsDisplay != .none {
|
||||
StatusActionsView(viewModel: viewModel)
|
||||
.padding(.vertical, 8)
|
||||
.tint(viewModel.isFocused ? theme.tintColor : .gray)
|
||||
|
|
|
@ -3,7 +3,7 @@ import Models
|
|||
import Network
|
||||
|
||||
public enum TimelineFilter: Hashable, Equatable {
|
||||
case federated, local, home, trending
|
||||
case home, local, federated, trending
|
||||
case hashtag(tag: String, accountId: String?)
|
||||
case list(list: List)
|
||||
case remoteLocal(server: String)
|
||||
|
@ -14,9 +14,9 @@ public enum TimelineFilter: Hashable, Equatable {
|
|||
|
||||
public static func availableTimeline(client: Client) -> [TimelineFilter] {
|
||||
if !client.isAuth {
|
||||
return [.federated, .local, .trending]
|
||||
return [.local, .federated, .trending]
|
||||
}
|
||||
return [.federated, .local, .trending, .home]
|
||||
return [.home, .local, .federated, .trending]
|
||||
}
|
||||
|
||||
public func title() -> String {
|
||||
|
|
Loading…
Reference in New Issue