+
Skip to content

Add settings screen #113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Dec 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions LokiPackage/Sources/Core/UI/Buttons/FloatingActionButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import SwiftUI

public typealias FAB = FloatingActionButton

public struct FloatingActionButton: View {
private let systemName: String
private let backgroundColor: Color
private let foregroundColor: Color
private let action: () -> Void

public var body: some View {
Button(
action: action,
label: {
Image(systemName: systemName)
.resizable()
.frame(width: 24, height: 24)
}
)
.buttonStyle(.floatingAction(
backgroundColor: backgroundColor,
foregroundColor: foregroundColor
))
}

public init(
systemName: String,
backgroundColor: Color = .accentColor,
foregroundColor: Color = .white,
action: @escaping () -> Void
) {
self.systemName = systemName
self.backgroundColor = backgroundColor
self.foregroundColor = foregroundColor
self.action = action
}
}

#if DEBUG
struct FloatingActionButton_Previews: PreviewProvider {
static var previews: some View {
FloatingActionButton(
systemName: "plus",
action: {}
)
}
}
#endif

private extension ButtonStyle where Self == FloatingActionButtonStyle {
static func floatingAction(
backgroundColor: Color,
foregroundColor: Color
) -> FloatingActionButtonStyle {
.init(
backgroundColor: backgroundColor,
foregroundColor: foregroundColor
)
}
}

private struct FloatingActionButtonStyle: ButtonStyle {
let backgroundColor: Color
let foregroundColor: Color

func makeBody(configuration: Configuration) -> some View {
configuration.label
.frame(width: 56, height: 56)
.foregroundColor(foregroundColor)
.background(backgroundColor)
.cornerRadius(16)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@
"Failed to save Sakatsu." = "Failed to save Sakatsu.";
"Detailed cause unknown." = "Detailed cause unknown.";
"Please try again after some time." = "Please try again after some time.";

// SakatsuSettingsScreen
"Settings" = "Settings";
"Version" = "Version";
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@
"Failed to save Sakatsu." = "サ活の保存に失敗しました。";
"Detailed cause unknown." = "詳しい原因はわかりません。";
"Please try again after some time." = "時間をおいて再度お試しください。";

// SakatsuSettingsScreen
"Settings" = "設定";
"Version" = "バージョン";
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,26 @@ public struct SakatsuListScreen: View {
}
)
.navigationTitle(String(localized: "Sakatsu list", bundle: .module))
.overlay(alignment: .bottomTrailing) {
FAB(
systemName: "plus",
action: { viewModel.onAddButtonClick() }
)
.padding(16)
}
.sakatsuListScreenToolbar(
onAddButtonClick: { viewModel.onAddButtonClick() }
onSettingsButtonClick: { viewModel.onSettingsButtonClick() }
)
.sakatsuInputSheet(
shouldShowSheet: viewModel.uiState.shouldShowInputSheet,
shouldShowSheet: viewModel.uiState.shouldShowInputScreen,
selectedSakatsu: viewModel.uiState.selectedSakatsu,
onDismiss: { viewModel.onInputSheetDismiss() },
onDismiss: { viewModel.onInputScreenDismiss() },
onSakatsuSave: { viewModel.onSakatsuSave() }
)
.sakatsuSettingsSheet(
shouldShowSheet: viewModel.uiState.shouldShowSettingsScreen,
onDismiss: { viewModel.onSettingsScreenDismiss() }
)
.copyingSakatsuTextAlert(
sakatsuText: viewModel.uiState.sakatsuText,
onDismiss: { viewModel.onCopyingSakatsuTextAlertDismiss() }
Expand All @@ -45,19 +56,19 @@ public struct SakatsuListScreen: View {

private extension View {
func sakatsuListScreenToolbar(
onAddButtonClick: @escaping () -> Void
onSettingsButtonClick: @escaping () -> Void
) -> some View {
toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem(placement: .navigationBarLeading) {
Button {
onAddButtonClick()
onSettingsButtonClick()
} label: {
Image(systemName: "plus")
Image(systemName: "gearshape")
}
}
ToolbarItem(placement: .navigationBarLeading) {
EditButton()
}
}
}

Expand All @@ -67,18 +78,35 @@ private extension View {
onDismiss: @escaping () -> Void,
onSakatsuSave: @escaping () -> Void
) -> some View {
sheet(isPresented: .init(get: {
shouldShowSheet
}, set: { _ in
onDismiss()
})) {
sheet(
isPresented: .init(get: {
shouldShowSheet
}, set: { _ in
onDismiss()
})
) {
SakatsuInputScreen(
sakatsu: selectedSakatsu,
onSakatsuSave: onSakatsuSave
)
}
}

func sakatsuSettingsSheet(
shouldShowSheet: Bool,
onDismiss: @escaping () -> Void
) -> some View {
sheet(
isPresented: .init(get: {
shouldShowSheet
}, set: { _ in
onDismiss()
})
) {
SakatsuSettingsScreen()
}
}

func copyingSakatsuTextAlert(
sakatsuText: String?,
onDismiss: @escaping () -> Void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ struct SakatsuListUiState {
var sakatsus: [Sakatsu] = []
var selectedSakatsu: Sakatsu? = nil
var sakatsuText: String? = nil
var shouldShowInputSheet: Bool = false
var shouldShowInputScreen: Bool = false
var shouldShowSettingsScreen: Bool = false
var sakatsuListError: SakatsuListError? = nil
}

Expand Down Expand Up @@ -55,26 +56,34 @@ final class SakatsuListViewModel<Repository: SakatsuRepository>: ObservableObjec

extension SakatsuListViewModel {
func onSakatsuSave() {
uiState.shouldShowInputSheet = false
uiState.shouldShowInputScreen = false
refreshSakatsus()
}

func onAddButtonClick() {
uiState.selectedSakatsu = nil
uiState.shouldShowInputSheet = true
uiState.shouldShowInputScreen = true
}

func onEditButtonClick(sakatsuIndex: Int) {
uiState.selectedSakatsu = uiState.sakatsus[sakatsuIndex]
uiState.shouldShowInputSheet = true
uiState.shouldShowInputScreen = true
}

func onSettingsButtonClick() {
uiState.shouldShowSettingsScreen = true
}

func onSettingsScreenDismiss() {
uiState.shouldShowSettingsScreen = false
}

func onCopySakatsuTextButtonClick(sakatsuIndex: Int) {
uiState.sakatsuText = sakatsuText(sakatsu: uiState.sakatsus[sakatsuIndex])
}

func onInputSheetDismiss() {
uiState.shouldShowInputSheet = false
func onInputScreenDismiss() {
uiState.shouldShowInputScreen = false
uiState.selectedSakatsu = nil
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

/// https://github.com/uhooi/UhooiPicBook/blob/43e68a6a4800ebf6180a80e5cc0824b976f18bb2/Sources/AppModule/Extensions/Foundation/Bundle+String.swift
extension Bundle {
var version: String {
guard let version = object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String else {
fatalError("Fail to load Version.")
}
return version
}

var build: String {
guard let build = object(forInfoDictionaryKey: "CFBundleVersion") as? String else {
fatalError("Fail to load Build.")
}
return build
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import SwiftUI

struct SakatsuSettingsScreen: View {
@Environment(\.dismiss) private var dismiss

var body: some View {
NavigationStack {
SakatsuSettingsView()
.navigationTitle(String(localized: "Settings", bundle: .module))
.sakatsuSettingsScreenToolbar(onCloseButtonClick: { dismiss() })
}
}
}

private extension View {
func sakatsuSettingsScreenToolbar(
onCloseButtonClick: @escaping () -> Void
) -> some View {
toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
onCloseButtonClick()
} label: {
Image(systemName: "xmark")
}
}
}
}
}

#if DEBUG
struct SakatsuSettingsScreen_Previews: PreviewProvider {
static var previews: some View {
SakatsuSettingsScreen()
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import SwiftUI

struct SakatsuSettingsView: View {
var body: some View {
Form {
versionSection
}
}

private var versionSection: some View {
Section {
HStack {
Text("Version", bundle: .module)
Spacer()
Text("\(Bundle.main.version) (\(Bundle.main.build))")
}
} footer: {
Text("© 2023 THE Uhooi")
}
}
}

#if DEBUG
struct SakatsuSettingsView_Previews: PreviewProvider {
static var previews: some View {
SakatsuSettingsView()
}
}
#endif
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载