Skip to content

Commit

Permalink
feat: re Feat/login (#4)
Browse files Browse the repository at this point in the history
* feat: basic login

* feat: successful login

* feat: save credentials with keychain-swift

* feat: adjust log

* feat: switch instances

* feat: lowercase server address

* fix: non-neodb.social instance cannot login

* feat: user api

* feat: make llm knows keychain-swift is already added

* feat: custom ContentUnavailableView

* feat: profile view

* feat: feat inject

* feat: new profile view design

* feat: injection

* feat: new profile design

* feat: injection next

* feat: redesigned profile

* refactor: replace @StateObject with @EnvironmentObject for AuthService in ContentView, LoginView, and NeoDBApp

* feat: implement user caching and refresh functionality in UserService and ProfileViewModel

- Added caching mechanism for user data in UserService to improve performance.
- Updated getCurrentUser method to accept a forceRefresh parameter for optional cache bypass.
- Implemented clearCache method to allow cache clearing on logout.
- Modified loadUserProfile method in ProfileViewModel to support force refresh.
- Enhanced ProfileView to show loading indicators and support pull-to-refresh for user profile loading.

* refactor: enhance ProfileView layout and loading state handling

- Introduced a new private variable for avatar size to standardize avatar dimensions.
- Refactored profile content display logic to improve readability and maintainability.
- Added a placeholder for the avatar while loading user data.
- Updated loading indicators and error handling for a better user experience.
- Ensured the logout button is disabled when no user is present.

* chore: remove outdated Project Structure documentation and update logout button in ProfileView

- Deleted the Project Structure.md file as it was no longer relevant.
- Updated the logout button in ProfileView to use a text label instead of an icon, enhancing clarity for users.
  • Loading branch information
lcandy2 authored Jan 5, 2025
1 parent 27e3a20 commit 726b388
Show file tree
Hide file tree
Showing 17 changed files with 1,870 additions and 14 deletions.
46 changes: 46 additions & 0 deletions NeoDB/NeoDB.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
objects = {

/* Begin PBXBuildFile section */
242688402D2A9E0D00DFAAC1 /* InjectionNext in Frameworks */ = {isa = PBXBuildFile; productRef = 2426883F2D2A9E0D00DFAAC1 /* InjectionNext */; };
245FD2132D2A817A005B55B3 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 245FD2122D2A817A005B55B3 /* KeychainSwift */; };
24D1BE412D2A9F340063B530 /* Inject in Frameworks */ = {isa = PBXBuildFile; productRef = 24D1BE402D2A9F340063B530 /* Inject */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -70,6 +72,8 @@
buildActionMask = 2147483647;
files = (
245FD2132D2A817A005B55B3 /* KeychainSwift in Frameworks */,
242688402D2A9E0D00DFAAC1 /* InjectionNext in Frameworks */,
24D1BE412D2A9F340063B530 /* Inject in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -90,12 +94,20 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
245FD27F2D2A9AF0005B55B3 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
24CEACE62D0DA6200083D4BA = {
isa = PBXGroup;
children = (
24CEACF12D0DA6200083D4BA /* NeoDB */,
24CEAD032D0DA6210083D4BA /* NeoDBTests */,
24CEAD0D2D0DA6210083D4BA /* NeoDBUITests */,
245FD27F2D2A9AF0005B55B3 /* Frameworks */,
24CEACF02D0DA6200083D4BA /* Products */,
);
sourceTree = "<group>";
Expand Down Expand Up @@ -131,6 +143,8 @@
name = NeoDB;
packageProductDependencies = (
245FD2122D2A817A005B55B3 /* KeychainSwift */,
2426883F2D2A9E0D00DFAAC1 /* InjectionNext */,
24D1BE402D2A9F340063B530 /* Inject */,
);
productName = NeoDB;
productReference = 24CEACEF2D0DA6200083D4BA /* NeoDB.app */;
Expand Down Expand Up @@ -216,6 +230,8 @@
minimizedProjectReferenceProxies = 1;
packageReferences = (
245FD2112D2A817A005B55B3 /* XCRemoteSwiftPackageReference "keychain-swift" */,
2415DF2F2D2A9DCC00DAC07F /* XCRemoteSwiftPackageReference "InjectionNext" */,
24AFFC8E2D2A9F040085A6D0 /* XCRemoteSwiftPackageReference "Inject" */,
);
preferredProjectObjectVersion = 77;
productRefGroup = 24CEACF02D0DA6200083D4BA /* Products */;
Expand Down Expand Up @@ -432,6 +448,10 @@
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 12;
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"-Xlinker",
"-interposable",
);
PRODUCT_BUNDLE_IDENTIFIER = app.neodb;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
Expand Down Expand Up @@ -611,6 +631,14 @@
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
2415DF2F2D2A9DCC00DAC07F /* XCRemoteSwiftPackageReference "InjectionNext" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/johnno1962/InjectionNext";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.2.6;
};
};
245FD2112D2A817A005B55B3 /* XCRemoteSwiftPackageReference "keychain-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/evgenyneu/keychain-swift.git";
Expand All @@ -619,14 +647,32 @@
minimumVersion = 24.0.0;
};
};
24AFFC8E2D2A9F040085A6D0 /* XCRemoteSwiftPackageReference "Inject" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/krzysztofzablocki/Inject.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.5.2;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
2426883F2D2A9E0D00DFAAC1 /* InjectionNext */ = {
isa = XCSwiftPackageProductDependency;
package = 2415DF2F2D2A9DCC00DAC07F /* XCRemoteSwiftPackageReference "InjectionNext" */;
productName = InjectionNext;
};
245FD2122D2A817A005B55B3 /* KeychainSwift */ = {
isa = XCSwiftPackageProductDependency;
package = 245FD2112D2A817A005B55B3 /* XCRemoteSwiftPackageReference "keychain-swift" */;
productName = KeychainSwift;
};
24D1BE402D2A9F340063B530 /* Inject */ = {
isa = XCSwiftPackageProductDependency;
package = 24AFFC8E2D2A9F040085A6D0 /* XCRemoteSwiftPackageReference "Inject" */;
productName = Inject;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 24CEACE72D0DA6200083D4BA /* Project object */;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
{
"originHash" : "61d0f8bddcbf3b93ae7ce1d42c4f6929e95bfcb66319ddeb36d0dda55337047e",
"originHash" : "657466b00a4cf16eda52a23bd963af27f6da71d69047ed11c12b2a367c93a6ba",
"pins" : [
{
"identity" : "inject",
"kind" : "remoteSourceControl",
"location" : "https://github.com/krzysztofzablocki/Inject.git",
"state" : {
"revision" : "728c56639ecb3df441d51d5bc6747329afabcfc9",
"version" : "1.5.2"
}
},
{
"identity" : "injectionnext",
"kind" : "remoteSourceControl",
"location" : "https://github.com/johnno1962/InjectionNext",
"state" : {
"revision" : "dc3474dc65e0a8d6661932733950810bfca180a9",
"version" : "1.2.6"
}
},
{
"identity" : "keychain-swift",
"kind" : "remoteSourceControl",
Expand Down
47 changes: 41 additions & 6 deletions NeoDB/NeoDB/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,52 @@
import SwiftUI

struct ContentView: View {
@EnvironmentObject var authService: AuthService

var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
TabView {
NavigationStack {
Text("Home Feed")
.navigationTitle("Home")
}
.tabItem {
Label("Home", systemImage: "house.fill")
}

NavigationStack {
Text("Search")
.navigationTitle("Search")
}
.tabItem {
Label("Search", systemImage: "magnifyingglass")
}

NavigationStack {
Text("Library")
.navigationTitle("Library")
}
.tabItem {
Label("Library", systemImage: "books.vertical.fill")
}


NavigationStack {
ProfileView(authService: authService)
}
.tabItem {
Label("Profile", systemImage: "person.fill")
}
}
.padding()
.tint(.accentColor)
.enableInjection()
}

#if DEBUG
@ObserveInjection var forceRedraw
#endif
}

#Preview {
ContentView()
.environmentObject(AuthService())
}
94 changes: 93 additions & 1 deletion NeoDB/NeoDB/NeoDBApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,101 @@ import SwiftUI

@main
struct NeoDBApp: App {
@StateObject private var authService = AuthService()

var body: some Scene {
WindowGroup {
ContentView()
Group {
if authService.isAuthenticated {
ContentView()
.environmentObject(authService)
} else {
LoginView()
.environmentObject(authService)
}
}
.onOpenURL { url in
Task {
do {
try await authService.handleCallback(url: url)
} catch {
print("Authentication error: \(error)")
}
}
}
}
}
}

#if canImport(HotSwiftUI)
@_exported import HotSwiftUI
#elseif canImport(Inject)
@_exported import Inject
#else
// This code can be found in the Swift package:
// https://github.com/johnno1962/HotSwiftUI or
// https://github.com/krzysztofzablocki/Inject

#if DEBUG
import Combine

public class InjectionObserver: ObservableObject {
public static let shared = InjectionObserver()
@Published var injectionNumber = 0
var cancellable: AnyCancellable? = nil
let publisher = PassthroughSubject<Void, Never>()
init() {
cancellable = NotificationCenter.default.publisher(for:
Notification.Name("INJECTION_BUNDLE_NOTIFICATION"))
.sink { [weak self] change in
self?.injectionNumber += 1
self?.publisher.send()
}
}
}

extension SwiftUI.View {
public func eraseToAnyView() -> some SwiftUI.View {
return AnyView(self)
}
public func enableInjection() -> some SwiftUI.View {
return eraseToAnyView()
}
public func onInjection(bumpState: @escaping () -> ()) -> some SwiftUI.View {
return self
.onReceive(InjectionObserver.shared.publisher, perform: bumpState)
.eraseToAnyView()
}
}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@propertyWrapper
public struct ObserveInjection: DynamicProperty {
@ObservedObject private var iO = InjectionObserver.shared
public init() {}
public private(set) var wrappedValue: Int {
get {0} set {}
}
}
#else
extension SwiftUI.View {
@inline(__always)
public func eraseToAnyView() -> some SwiftUI.View { return self }
@inline(__always)
public func enableInjection() -> some SwiftUI.View { return self }
@inline(__always)
public func onInjection(bumpState: @escaping () -> ()) -> some SwiftUI.View {
return self
}
}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@propertyWrapper
public struct ObserveInjection {
public init() {}
public private(set) var wrappedValue: Int {
get {0} set {}
}
}
#endif
#endif
Loading

0 comments on commit 726b388

Please sign in to comment.