Merge pull request #11 from rock-n-code/integration/profile
Integration: Profile
This commit is contained in:
commit
f324b59601
@ -7,6 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
02659B192946AA6900C3AD63 /* SheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02659B182946AA6900C3AD63 /* SheetView.swift */; };
|
||||||
026D9825293B6374009FE888 /* Libraries in Frameworks */ = {isa = PBXBuildFile; productRef = 026D9824293B6374009FE888 /* Libraries */; };
|
026D9825293B6374009FE888 /* Libraries in Frameworks */ = {isa = PBXBuildFile; productRef = 026D9824293B6374009FE888 /* Libraries */; };
|
||||||
02AE64EF29363DBF005A4AF3 /* BeRealApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */; };
|
02AE64EF29363DBF005A4AF3 /* BeRealApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */; };
|
||||||
02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64F029363DBF005A4AF3 /* ContentView.swift */; };
|
02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64F029363DBF005A4AF3 /* ContentView.swift */; };
|
||||||
@ -37,6 +38,7 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
02659B182946AA6900C3AD63 /* SheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetView.swift; sourceTree = "<group>"; };
|
||||||
026D9823293B6365009FE888 /* Libraries */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Libraries; sourceTree = "<group>"; };
|
026D9823293B6365009FE888 /* Libraries */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Libraries; sourceTree = "<group>"; };
|
||||||
02784F03293A8331005F839D /* Modules */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Modules; sourceTree = "<group>"; };
|
02784F03293A8331005F839D /* Modules */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Modules; sourceTree = "<group>"; };
|
||||||
02AE64EB29363DBF005A4AF3 /* BeReal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BeReal.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
02AE64EB29363DBF005A4AF3 /* BeReal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BeReal.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@ -80,6 +82,31 @@
|
|||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
02659B152946AA2700C3AD63 /* UI */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
02659B162946AA2E00C3AD63 /* Enumerations */,
|
||||||
|
02659B172946AA4400C3AD63 /* Views */,
|
||||||
|
);
|
||||||
|
path = UI;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
02659B162946AA2E00C3AD63 /* Enumerations */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
02659B182946AA6900C3AD63 /* SheetView.swift */,
|
||||||
|
);
|
||||||
|
path = Enumerations;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
02659B172946AA4400C3AD63 /* Views */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
02AE64F029363DBF005A4AF3 /* ContentView.swift */,
|
||||||
|
);
|
||||||
|
path = Views;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
02AE64E229363DBF005A4AF3 = {
|
02AE64E229363DBF005A4AF3 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -108,8 +135,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */,
|
02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */,
|
||||||
02AE64F029363DBF005A4AF3 /* ContentView.swift */,
|
|
||||||
02AE64F229363DC1005A4AF3 /* Assets.xcassets */,
|
02AE64F229363DC1005A4AF3 /* Assets.xcassets */,
|
||||||
|
02659B152946AA2700C3AD63 /* UI */,
|
||||||
02AE64F429363DC1005A4AF3 /* Preview Content */,
|
02AE64F429363DC1005A4AF3 /* Preview Content */,
|
||||||
);
|
);
|
||||||
path = BeReal;
|
path = BeReal;
|
||||||
@ -284,6 +311,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */,
|
02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */,
|
||||||
|
02659B192946AA6900C3AD63 /* SheetView.swift in Sources */,
|
||||||
02AE64EF29363DBF005A4AF3 /* BeRealApp.swift in Sources */,
|
02AE64EF29363DBF005A4AF3 /* BeRealApp.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
//
|
|
||||||
// ContentView.swift
|
|
||||||
// BeReal
|
|
||||||
//
|
|
||||||
// Created by Javier Cicchelli on 29/11/2022.
|
|
||||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Browse
|
|
||||||
import DataModels
|
|
||||||
import Login
|
|
||||||
import KeychainStorage
|
|
||||||
import Profile
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct ContentView: View {
|
|
||||||
|
|
||||||
// MARK: Storages
|
|
||||||
|
|
||||||
@KeychainStorage(key: .KeychainStorage.account) private var account: Account?
|
|
||||||
|
|
||||||
// MARK: Body
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
NavigationView {
|
|
||||||
BrowseView()
|
|
||||||
}
|
|
||||||
.sheet(isPresented: showLogin) {
|
|
||||||
LoginView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Helpers
|
|
||||||
|
|
||||||
private extension ContentView {
|
|
||||||
var showLogin: Binding<Bool> {
|
|
||||||
.init { account == nil } set: { _ in }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Previews
|
|
||||||
|
|
||||||
struct ContentView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
ContentView()
|
|
||||||
}
|
|
||||||
}
|
|
20
BeReal/UI/Enumerations/SheetView.swift
Normal file
20
BeReal/UI/Enumerations/SheetView.swift
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// SheetView.swift
|
||||||
|
// BeReal
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 12/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// MARK: - Enumerations
|
||||||
|
|
||||||
|
enum SheetView: Int {
|
||||||
|
case login
|
||||||
|
case profile
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Identifiable
|
||||||
|
|
||||||
|
extension SheetView: Identifiable {
|
||||||
|
var id: Int { rawValue }
|
||||||
|
}
|
79
BeReal/UI/Views/ContentView.swift
Normal file
79
BeReal/UI/Views/ContentView.swift
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
//
|
||||||
|
// ContentView.swift
|
||||||
|
// BeReal
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 29/11/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Browse
|
||||||
|
import DataModels
|
||||||
|
import Login
|
||||||
|
import KeychainStorage
|
||||||
|
import Profile
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ContentView: View {
|
||||||
|
|
||||||
|
// MARK: Storages
|
||||||
|
|
||||||
|
@KeychainStorage(key: .KeychainStorage.account) private var account: Account?
|
||||||
|
|
||||||
|
// MARK: States
|
||||||
|
|
||||||
|
@State private var user: User?
|
||||||
|
@State private var showSheet: SheetView?
|
||||||
|
|
||||||
|
// MARK: Body
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationView {
|
||||||
|
BrowseView {
|
||||||
|
// ...
|
||||||
|
} uploadFile: {
|
||||||
|
// ...
|
||||||
|
} showProfile: {
|
||||||
|
showSheet = .profile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
shouldShowLogin()
|
||||||
|
}
|
||||||
|
.onChange(of: account) { _ in
|
||||||
|
shouldShowLogin()
|
||||||
|
}
|
||||||
|
.sheet(item: $showSheet) { sheet in
|
||||||
|
switch sheet {
|
||||||
|
case .login:
|
||||||
|
LoginView {
|
||||||
|
user = $1
|
||||||
|
account = $0
|
||||||
|
}
|
||||||
|
case .profile:
|
||||||
|
ProfileView(user: user) {
|
||||||
|
user = nil
|
||||||
|
account = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private extension ContentView {
|
||||||
|
func shouldShowLogin() {
|
||||||
|
showSheet = account == nil
|
||||||
|
? .login
|
||||||
|
: nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Previews
|
||||||
|
|
||||||
|
struct ContentView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ContentView()
|
||||||
|
}
|
||||||
|
}
|
9
Libraries/Sources/DataModels/Defines/Typealiases.swift
Normal file
9
Libraries/Sources/DataModels/Defines/Typealiases.swift
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
//
|
||||||
|
// Typealiases.swift
|
||||||
|
// DataModels
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 12/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
public typealias ActionClosure = () -> Void
|
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
public struct Account: Codable {
|
public struct Account: Codable, Equatable {
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
|
@ -31,10 +31,18 @@ let package = Package(
|
|||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "Browse",
|
name: "Browse",
|
||||||
|
dependencies: [
|
||||||
|
"Cores",
|
||||||
|
"Libraries"
|
||||||
|
],
|
||||||
resources: [.process("Resources")]
|
resources: [.process("Resources")]
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "Profile",
|
name: "Profile",
|
||||||
|
dependencies: [
|
||||||
|
"Cores",
|
||||||
|
"Libraries"
|
||||||
|
],
|
||||||
resources: [.process("Resources")]
|
resources: [.process("Resources")]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
@ -1,19 +1,29 @@
|
|||||||
//
|
//
|
||||||
// BrowseToolbar.swift
|
// BrowseToolbar.swift
|
||||||
// BeReal
|
// Browse
|
||||||
//
|
//
|
||||||
// Created by Javier Cicchelli on 03/12/2022.
|
// Created by Javier Cicchelli on 03/12/2022.
|
||||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import DataModels
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct BrowseToolbar: ToolbarContent {
|
struct BrowseToolbar: ToolbarContent {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
let createFolder: ActionClosure
|
||||||
|
let uploadFile: ActionClosure
|
||||||
|
let showProfile: ActionClosure
|
||||||
|
|
||||||
|
// MARK: Body
|
||||||
|
|
||||||
var body: some ToolbarContent {
|
var body: some ToolbarContent {
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
Menu {
|
Menu {
|
||||||
Button {
|
Button {
|
||||||
// TODO: Implement the creation of a new folder.
|
createFolder()
|
||||||
} label: {
|
} label: {
|
||||||
Label {
|
Label {
|
||||||
Text(
|
Text(
|
||||||
@ -27,7 +37,7 @@ struct BrowseToolbar: ToolbarContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
// TODO: Implement the upload of a file from the device to the API.
|
uploadFile()
|
||||||
} label: {
|
} label: {
|
||||||
Label {
|
Label {
|
||||||
Text(
|
Text(
|
||||||
@ -55,7 +65,7 @@ struct BrowseToolbar: ToolbarContent {
|
|||||||
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
Button {
|
Button {
|
||||||
// TODO: Implement the show of the user profile.
|
showProfile()
|
||||||
} label: {
|
} label: {
|
||||||
Label {
|
Label {
|
||||||
Text(
|
Text(
|
||||||
@ -70,6 +80,7 @@ struct BrowseToolbar: ToolbarContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Image+Constants
|
// MARK: - Image+Constants
|
||||||
|
@ -6,13 +6,28 @@
|
|||||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import DataModels
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct BrowseView: View {
|
public struct BrowseView: View {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let createFolder: ActionClosure
|
||||||
|
private let uploadFile: ActionClosure
|
||||||
|
private let showProfile: ActionClosure
|
||||||
|
|
||||||
// MARK: Initialisers
|
// MARK: Initialisers
|
||||||
|
|
||||||
public init() {}
|
public init(
|
||||||
|
createFolder: @escaping ActionClosure,
|
||||||
|
uploadFile: @escaping ActionClosure,
|
||||||
|
showProfile: @escaping ActionClosure
|
||||||
|
) {
|
||||||
|
self.createFolder = createFolder
|
||||||
|
self.uploadFile = uploadFile
|
||||||
|
self.showProfile = showProfile
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Body
|
// MARK: Body
|
||||||
|
|
||||||
@ -106,7 +121,11 @@ public struct BrowseView: View {
|
|||||||
.background(Color.red)
|
.background(Color.red)
|
||||||
.navigationTitle("Folder name")
|
.navigationTitle("Folder name")
|
||||||
.toolbar {
|
.toolbar {
|
||||||
BrowseToolbar()
|
BrowseToolbar(
|
||||||
|
createFolder: createFolder,
|
||||||
|
uploadFile: uploadFile,
|
||||||
|
showProfile: showProfile
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +142,13 @@ private extension Image {
|
|||||||
struct BrowseView_Previews: PreviewProvider {
|
struct BrowseView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
BrowseView()
|
BrowseView {
|
||||||
|
// ...
|
||||||
|
} uploadFile: {
|
||||||
|
// ...
|
||||||
|
} showProfile: {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
Modules/Sources/Login/Logic/Defines/Typealiases.swift
Normal file
11
Modules/Sources/Login/Logic/Defines/Typealiases.swift
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
//
|
||||||
|
// Typealiases.swift
|
||||||
|
// Login
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 12/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import DataModels
|
||||||
|
|
||||||
|
public typealias AuthenticatedClosure = (Account, User) -> Void
|
55
Modules/Sources/Login/Logic/Use Cases/GetUserUseCase.swift
Normal file
55
Modules/Sources/Login/Logic/Use Cases/GetUserUseCase.swift
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
//
|
||||||
|
// GetUserUseCase.swift
|
||||||
|
// Login
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 12/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import APIService
|
||||||
|
import DependencyInjection
|
||||||
|
import Dependencies
|
||||||
|
|
||||||
|
struct GetUserUseCase {
|
||||||
|
|
||||||
|
// MARK: Dependencies
|
||||||
|
|
||||||
|
@Dependency(\.apiService) private var apiService
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
let authenticated: AuthenticatedClosure
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
func callAsFunction(
|
||||||
|
username: String,
|
||||||
|
password: String
|
||||||
|
) async throws {
|
||||||
|
let me = try await apiService.getUser(
|
||||||
|
credentials: .init(
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
authenticated(
|
||||||
|
.init(
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
profile: .init(
|
||||||
|
firstName: me.firstName,
|
||||||
|
lastName: me.lastName
|
||||||
|
),
|
||||||
|
rootFolder: .init(
|
||||||
|
id: me.rootItem.id,
|
||||||
|
name: me.rootItem.name,
|
||||||
|
lastModifiedAt: me.rootItem.lastModifiedAt
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,10 +7,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import APIService
|
import APIService
|
||||||
import DataModels
|
|
||||||
import DependencyInjection
|
|
||||||
import Dependencies
|
|
||||||
import KeychainStorage
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct LoginView: View {
|
public struct LoginView: View {
|
||||||
@ -19,9 +15,15 @@ public struct LoginView: View {
|
|||||||
|
|
||||||
@State private var containerTopPadding: CGFloat = 0
|
@State private var containerTopPadding: CGFloat = 0
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let authenticated: AuthenticatedClosure
|
||||||
|
|
||||||
// MARK: Initialisers
|
// MARK: Initialisers
|
||||||
|
|
||||||
public init() {}
|
public init(authenticated: @escaping AuthenticatedClosure) {
|
||||||
|
self.authenticated = authenticated
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Body
|
// MARK: Body
|
||||||
|
|
||||||
@ -30,7 +32,7 @@ public struct LoginView: View {
|
|||||||
.vertical,
|
.vertical,
|
||||||
showsIndicators: false
|
showsIndicators: false
|
||||||
) {
|
) {
|
||||||
LoginContainer()
|
LoginContainer(authenticated: authenticated)
|
||||||
.padding(.horizontal, 24)
|
.padding(.horizontal, 24)
|
||||||
.padding(.top, containerTopPadding)
|
.padding(.top, containerTopPadding)
|
||||||
}
|
}
|
||||||
@ -39,6 +41,7 @@ public struct LoginView: View {
|
|||||||
.onPreferenceChange(ViewHeightPreferenceKey.self) { height in
|
.onPreferenceChange(ViewHeightPreferenceKey.self) { height in
|
||||||
containerTopPadding = height * 0.1
|
containerTopPadding = height * 0.1
|
||||||
}
|
}
|
||||||
|
.interactiveDismissDisabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -47,21 +50,23 @@ public struct LoginView: View {
|
|||||||
|
|
||||||
fileprivate extension LoginView {
|
fileprivate extension LoginView {
|
||||||
struct LoginContainer: View {
|
struct LoginContainer: View {
|
||||||
|
|
||||||
// MARK: Dependencies
|
|
||||||
|
|
||||||
@Dependency(\.apiService) private var apiService
|
|
||||||
|
|
||||||
// MARK: Storages
|
|
||||||
|
|
||||||
@KeychainStorage(key: .KeychainStorage.account) private var account: Account?
|
|
||||||
|
|
||||||
// MARK: States
|
// MARK: States
|
||||||
|
|
||||||
@State private var isAuthenticating: Bool = false
|
@State private var isAuthenticating: Bool = false
|
||||||
@State private var username: String = ""
|
@State private var username: String = ""
|
||||||
@State private var password: String = ""
|
@State private var password: String = ""
|
||||||
@State private var errorMessage: String?
|
@State private var errorMessage: String?
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let getUser: GetUserUseCase
|
||||||
|
|
||||||
|
// MARK: Initialisers
|
||||||
|
|
||||||
|
init(authenticated: @escaping AuthenticatedClosure) {
|
||||||
|
self.getUser = .init(authenticated: authenticated)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Body
|
// MARK: Body
|
||||||
|
|
||||||
@ -133,12 +138,7 @@ private extension LoginView.LoginContainer {
|
|||||||
guard isAuthenticating else { return }
|
guard isAuthenticating else { return }
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await apiService.getUser(credentials: .init(
|
try await getUser(
|
||||||
username: username,
|
|
||||||
password: password
|
|
||||||
))
|
|
||||||
|
|
||||||
account = .init(
|
|
||||||
username: username,
|
username: username,
|
||||||
password: password
|
password: password
|
||||||
)
|
)
|
||||||
@ -157,6 +157,8 @@ private extension LoginView.LoginContainer {
|
|||||||
|
|
||||||
struct LoginView_Previews: PreviewProvider {
|
struct LoginView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
LoginView()
|
LoginView { _, _ in
|
||||||
|
// closure for authenticated action.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
Modules/Sources/Profile/Logic/Adapters/DateAdapter.swift
Normal file
41
Modules/Sources/Profile/Logic/Adapters/DateAdapter.swift
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// DateAdapter.swift
|
||||||
|
// Profile
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 12/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct DateAdapter {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let dateFormatter: DateFormatter = .dateTimeFormatter
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
func callAsFunction(value: Date?) -> String {
|
||||||
|
if let value {
|
||||||
|
return dateFormatter.string(from: value)
|
||||||
|
} else {
|
||||||
|
return .Constants.noValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - DateFormatter+Formats
|
||||||
|
|
||||||
|
private extension DateFormatter {
|
||||||
|
static let dateTimeFormatter = {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
|
||||||
|
formatter.dateStyle = .long
|
||||||
|
formatter.timeStyle = .short
|
||||||
|
formatter.locale = .current
|
||||||
|
|
||||||
|
return formatter
|
||||||
|
}()
|
||||||
|
}
|
13
Modules/Sources/Profile/Logic/Adapters/StringAdapter.swift
Normal file
13
Modules/Sources/Profile/Logic/Adapters/StringAdapter.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// StringAdapter.swift
|
||||||
|
// Profile
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 12/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
struct StringAdapter {
|
||||||
|
func callAsFunction(value: String?) -> String {
|
||||||
|
value ?? .Constants.noValue
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// String+Constants.swift
|
||||||
|
// Profile
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 12/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
enum Constants {
|
||||||
|
static let noValue = "-"
|
||||||
|
}
|
||||||
|
}
|
75
Modules/Sources/Profile/UI/Components/ProfileSection.swift
Normal file
75
Modules/Sources/Profile/UI/Components/ProfileSection.swift
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
//
|
||||||
|
// ProfileSection.swift
|
||||||
|
// Profile
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 12/12/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ProfileSection: View {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
let header: LocalizedStringKey
|
||||||
|
let items: [Item]
|
||||||
|
|
||||||
|
// MARK: Body
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Section {
|
||||||
|
ForEach(items) { item in
|
||||||
|
Label {
|
||||||
|
Text(item.value)
|
||||||
|
} icon: {
|
||||||
|
Text(
|
||||||
|
item.key,
|
||||||
|
bundle: .module
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.labelStyle(.nameAndValue)
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text(
|
||||||
|
header,
|
||||||
|
bundle: .module
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Structs
|
||||||
|
|
||||||
|
extension ProfileSection {
|
||||||
|
struct Item {
|
||||||
|
let key: LocalizedStringKey
|
||||||
|
let value: String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Identifiable
|
||||||
|
|
||||||
|
extension ProfileSection.Item: Identifiable {
|
||||||
|
var id: String { UUID().uuidString }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Previews
|
||||||
|
|
||||||
|
struct ProfileSection_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ProfileSection(
|
||||||
|
header: "some-localised-header-key",
|
||||||
|
items: [
|
||||||
|
.init(
|
||||||
|
key: "some-localized-key",
|
||||||
|
value: "some value"
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
key: "some-other-localised-key",
|
||||||
|
value: "some other value"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -6,18 +6,27 @@
|
|||||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import DataModels
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct ProfileView: View {
|
public struct ProfileView: View {
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
private let logOut: () -> Void
|
private let user: User?
|
||||||
|
private let logout: ActionClosure
|
||||||
|
|
||||||
|
private let stringAdapter = StringAdapter()
|
||||||
|
private let dateAdapter = DateAdapter()
|
||||||
|
|
||||||
// MARK: Initialisers
|
// MARK: Initialisers
|
||||||
|
|
||||||
public init(logOut: @escaping () -> Void) {
|
public init(
|
||||||
self.logOut = logOut
|
user: User?,
|
||||||
|
logout: @escaping ActionClosure
|
||||||
|
) {
|
||||||
|
self.user = user
|
||||||
|
self.logout = logout
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Body
|
// MARK: Body
|
||||||
@ -33,96 +42,45 @@ public struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
.listRowBackground(Color.clear)
|
.listRowBackground(Color.clear)
|
||||||
|
|
||||||
Section {
|
ProfileSection(
|
||||||
Label {
|
header: "profile.sections.names.header.text",
|
||||||
Text("Javier")
|
items: [
|
||||||
} icon: {
|
.init(
|
||||||
Text(
|
key: "profile.sections.names.label.first_name.text",
|
||||||
"profile.sections.names.label.first_name.text",
|
value: stringAdapter(value: user?.profile.firstName)
|
||||||
bundle: .module,
|
),
|
||||||
comment: "First name label text."
|
.init(
|
||||||
|
key: "profile.sections.names.label.last_name.text",
|
||||||
|
value: stringAdapter(value: user?.profile.lastName)
|
||||||
)
|
)
|
||||||
}
|
]
|
||||||
.labelStyle(.nameAndValue)
|
)
|
||||||
|
|
||||||
Label {
|
ProfileSection(
|
||||||
Text("Cicchelli")
|
header: "profile.sections.root_info.header.text",
|
||||||
} icon: {
|
items: [
|
||||||
Text(
|
.init(
|
||||||
"profile.sections.names.label.last_name.text",
|
key: "profile.sections.root_info.label.identifier.text",
|
||||||
bundle: .module,
|
value: stringAdapter(value: user?.rootFolder.id)
|
||||||
comment: "Last name label text."
|
),
|
||||||
|
.init(
|
||||||
|
key: "profile.sections.root_info.label.name.text",
|
||||||
|
value: stringAdapter(value: user?.rootFolder.name)
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
key: "profile.sections.root_info.label.last_modified.text",
|
||||||
|
value: dateAdapter(value: user?.rootFolder.lastModifiedAt)
|
||||||
)
|
)
|
||||||
}
|
]
|
||||||
.labelStyle(.nameAndValue)
|
)
|
||||||
} header: {
|
|
||||||
Text(
|
|
||||||
"profile.sections.names.header.text",
|
|
||||||
bundle: .module,
|
|
||||||
comment: "Names section header text."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Section {
|
|
||||||
Label {
|
|
||||||
Text("71207ee4c0573fde80b03643caafe62731406404")
|
|
||||||
} icon: {
|
|
||||||
Text(
|
|
||||||
"profile.sections.root_info.label.identifier.text",
|
|
||||||
bundle: .module,
|
|
||||||
comment: "Identifier label text."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.labelStyle(.nameAndValue)
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Text("Yes")
|
|
||||||
} icon: {
|
|
||||||
Text(
|
|
||||||
"profile.sections.root_info.label.is_directory.text",
|
|
||||||
bundle: .module,
|
|
||||||
comment: "Is directory label text."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.labelStyle(.nameAndValue)
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Text("3 days ago")
|
|
||||||
} icon: {
|
|
||||||
Text(
|
|
||||||
"profile.sections.root_info.label.last_modified.text",
|
|
||||||
bundle: .module,
|
|
||||||
comment: "Last modified label text."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.labelStyle(.nameAndValue)
|
|
||||||
|
|
||||||
Label {
|
|
||||||
Text("My files")
|
|
||||||
} icon: {
|
|
||||||
Text(
|
|
||||||
"profile.sections.root_info.label.name.text",
|
|
||||||
bundle: .module,
|
|
||||||
comment: "Root name label text."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.labelStyle(.nameAndValue)
|
|
||||||
} header: {
|
|
||||||
Text(
|
|
||||||
"profile.sections.root_info.header.text",
|
|
||||||
bundle: .module,
|
|
||||||
comment: "Root item information header text."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Button {
|
Button {
|
||||||
logOut()
|
logout()
|
||||||
} label: {
|
} label: {
|
||||||
Text(
|
Text(
|
||||||
"profile.button.log_out.text",
|
"profile.button.log_out.text",
|
||||||
bundle: .module,
|
bundle: .module
|
||||||
comment: "Log out button text."
|
|
||||||
)
|
)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
.foregroundColor(.primary)
|
.foregroundColor(.primary)
|
||||||
@ -137,6 +95,7 @@ public struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
.background(Color.red)
|
.background(Color.red)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Images+Constants
|
// MARK: - Images+Constants
|
||||||
@ -149,8 +108,18 @@ private extension Image {
|
|||||||
|
|
||||||
struct ProfileView_Previews: PreviewProvider {
|
struct ProfileView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ProfileView {
|
ProfileView(user: .init(
|
||||||
// closure for log out action.
|
profile: .init(
|
||||||
|
firstName: "Some first name...",
|
||||||
|
lastName: "Some last name..."
|
||||||
|
),
|
||||||
|
rootFolder: .init(
|
||||||
|
id: "1234567890",
|
||||||
|
name: "Some folder name...",
|
||||||
|
lastModifiedAt: .now
|
||||||
|
)
|
||||||
|
)) {
|
||||||
|
// closure for logout action.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user