Skip to content

Commit

Permalink
Merge pull request #104 from makinosp/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
makinosp authored Sep 8, 2024
2 parents 4d844dc + 536fcef commit ecd43bb
Show file tree
Hide file tree
Showing 34 changed files with 273 additions and 215 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,7 @@ playground.xcworkspace
# fastlane/report.xml
# fastlane/Preview.html
# fastlane/screenshots/**/*.png
# fastlane/test_output
# fastlane/test_output

# macOS
.DS_Store
29 changes: 27 additions & 2 deletions Harmonie.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
20395D8D2B945CD700003921 /* Sources */,
20395D8E2B945CD700003921 /* Frameworks */,
20395D8F2B945CD700003921 /* Resources */,
20A26A662C8D6DF0004310E6 /* Swiftlint */,
);
buildRules = (
);
Expand Down Expand Up @@ -276,6 +277,28 @@
};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
20A26A662C8D6DF0004310E6 /* Swiftlint */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = Swiftlint;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nSWIFT_PACKAGE_DIR=\"${BUILD_DIR%Build/*}SourcePackages/artifacts\"\nSWIFTLINT_CMD=$(ls \"$SWIFT_PACKAGE_DIR\"/swiftlintplugins/SwiftLintBinary/SwiftLintBinary.artifactbundle/swiftlint-*/bin/swiftlint | head -n 1)\n\nif test -f \"$SWIFTLINT_CMD\" 2>&1\nthen\n \"$SWIFTLINT_CMD\"\nelse\n echo \"warning: `swiftlint` command not found - See https://github.com/realm/SwiftLint#installation for installation instructions.\"\nfi\n";
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
20395D8D2B945CD700003921 /* Sources */ = {
isa = PBXSourcesBuildPhase;
Expand Down Expand Up @@ -445,6 +468,7 @@
DEVELOPMENT_ASSET_PATHS = "\"harmonie/Preview Content\"";
DEVELOPMENT_TEAM = CJHK4FP72G;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Harmonie/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Harmonie;
Expand All @@ -459,7 +483,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.4.2;
MARKETING_VERSION = 0.5.0;
PRODUCT_BUNDLE_IDENTIFIER = jp.mknn.harmonie;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -479,6 +503,7 @@
DEVELOPMENT_ASSET_PATHS = "\"harmonie/Preview Content\"";
DEVELOPMENT_TEAM = CJHK4FP72G;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Harmonie/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Harmonie;
Expand All @@ -493,7 +518,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.4.2;
MARKETING_VERSION = 0.5.0;
PRODUCT_BUNDLE_IDENTIFIER = jp.mknn.harmonie;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"location" : "https://github.com/makinosp/vrckit",
"state" : {
"branch" : "develop",
"revision" : "2371372b625c197e928215e4c9ae912de24e5835"
"revision" : "bf2facec1f9c338ee67c6469c5098b7e5c2ebd09"
}
}
],
Expand Down
15 changes: 15 additions & 0 deletions harmonie/Extensions/NSError+isCancelled.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// NSError+isCancelled.swift
// Harmonie
//
// Created by makinosp on 2024/09/08.
//

import Foundation

extension NSError {
/// A Boolean value indicating whether the error represents a cancelled network request.
var isCancelled: Bool {
self.domain == NSURLErrorDomain && self.code == NSURLErrorCancelled
}
}
21 changes: 21 additions & 0 deletions harmonie/Protocols/AuthenticationServicePresentable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// AuthenticationServicePresentable.swift
// Harmonie
//
// Created by makinosp on 2024/09/08.
//

import VRCKit

protocol AuthenticationServicePresentable {
var appVM: AppViewModel { get }
}

extension AuthenticationServicePresentable {
@MainActor
var authenticationService: AuthenticationServiceProtocol {
appVM.isDemoMode
? AuthenticationPreviewService(client: appVM.client)
: AuthenticationService(client: appVM.client)
}
}
21 changes: 21 additions & 0 deletions harmonie/Protocols/FavoriteServicePresentable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// FavoriteServicePresentable.swift
// Harmonie
//
// Created by makinosp on 2024/09/08.
//

import VRCKit

protocol FavoriteServicePresentable {
var appVM: AppViewModel { get }
}

extension FavoriteServicePresentable {
@MainActor
var favoriteService: FavoriteServiceProtocol {
appVM.isDemoMode
? FavoritePreviewService(client: appVM.client)
: FavoriteService(client: appVM.client)
}
}
21 changes: 21 additions & 0 deletions harmonie/Protocols/FriendServicePresentable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// FriendServicePresentable.swift
// Harmonie
//
// Created by makinosp on 2024/09/08.
//

import VRCKit

protocol FriendServicePresentable {
var appVM: AppViewModel { get }
}

extension FriendServicePresentable {
@MainActor
var friendService: FriendServiceProtocol {
appVM.isDemoMode
? FriendPreviewService(client: appVM.client)
: FriendService(client: appVM.client)
}
}
2 changes: 1 addition & 1 deletion harmonie/Utils/DateUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

class DateUtil {
final class DateUtil {
static let shared = DateUtil()

private let yyyyMMddDateFormatter: DateFormatter = {
Expand Down
78 changes: 33 additions & 45 deletions harmonie/ViewModels/AppViewModel.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// UserData.swift
// AppViewModel.swift
// Harmonie
//
// Created by makinosp on 2024/03/09.
Expand All @@ -9,39 +9,23 @@ import Foundation
import Observation
import VRCKit

@MainActor @Observable
class AppViewModel {
@Observable
final class AppViewModel {
var user: User?
var step: Step = .initializing
var isPresentedAlert = false
var vrckError: VRCKitError?
var isDemoMode = false
@ObservationIgnored var client = APIClient()
@ObservationIgnored var service: any AuthenticationServiceProtocol

init() {
self.service = AuthenticationService(client: client)
}

enum Step: Equatable {
case initializing, loggingIn, done(User)
}

func setDemoMode() {
isDemoMode = true
service = AuthenticationPreviewService(client: client)
}

func reset() {
user = nil
step = .initializing
client = APIClient()
}

/// Check the authentication status of the user,
/// fetch the user information, and perform the initialization process.
/// - Returns: Depending on the status, either `loggingIn` or `done` is returned.
func setup() async -> Step {
func setup(service: any AuthenticationServiceProtocol) async -> Step {
// check local data
guard !client.cookieManager.cookies.isEmpty else {
return .loggingIn
Expand All @@ -60,9 +44,14 @@ class AppViewModel {
}
}

func login(username: String, password: String, isSavedOnKeyChain: Bool) async -> VerifyType? {
func login(
service: any AuthenticationServiceProtocol,
username: String,
password: String,
isSavedOnKeyChain: Bool
) async -> VerifyType? {
if username == "demo" && password == "demo" {
setDemoMode()
isDemoMode = true
} else {
client.setCledentials(username: username, password: password)
}
Expand All @@ -84,7 +73,11 @@ class AppViewModel {
return nil
}

func verifyTwoFA(_ verifyType: VerifyType?, _ code: String) async {
func verifyTwoFA(
service: any AuthenticationServiceProtocol,
verifyType: VerifyType?,
code: String
) async {
do {
defer {
reset()
Expand All @@ -103,17 +96,7 @@ class AppViewModel {
}
}

func generateFriendVM(user: User) -> FriendViewModel {
let service = isDemoMode ? FriendPreviewService(client: client) : FriendService(client: client)
return FriendViewModel(user: user, service: service)
}

func generateFavoriteVM(friendVM: FriendViewModel) -> FavoriteViewModel {
let service = isDemoMode ? FavoritePreviewService(client: client) : FavoriteService(client: client)
return FavoriteViewModel(service: service)
}

func logout() async {
func logout(service: any AuthenticationServiceProtocol) async {
do {
try await service.logout()
reset()
Expand All @@ -122,21 +105,26 @@ class AppViewModel {
}
}

private func reset() {
user = nil
step = .initializing
client = APIClient()
}

func handleError(_ error: Error) {
if let error = error as? VRCKitError {
vrckError = error
} else if (error as NSError?)?.isCancelled ?? false {
return
} else {
vrckError = .unexpectedError
if error == .unauthorized {
step = .loggingIn
} else {
vrckError = error
}
} else if !isCancelled(error) {
vrckError = .unexpected
}
isPresentedAlert = true
isPresentedAlert = vrckError != nil
}
}

fileprivate extension NSError {
/// A Boolean value indicating whether the error represents a cancelled network request.
var isCancelled: Bool {
self.domain == NSURLErrorDomain && self.code == NSURLErrorCancelled
private func isCancelled(_ error: Error) -> Bool {
(error as NSError?)?.isCancelled ?? false
}
}
20 changes: 9 additions & 11 deletions harmonie/ViewModels/FavoriteViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,18 @@ import VRCKit

/// View model for managing favorite groups and their associated details.
/// Acts as an interface between the UI and backend services for handling favorite group operations.
@MainActor @Observable
class FavoriteViewModel {
@Observable
final class FavoriteViewModel {
typealias FavoriteFriend = (favoriteGroupId: String, friends: [Friend])
var favoriteGroups: [FavoriteGroup] = []
var favoriteFriends: [FavoriteFriend] = []
var favoriteWorlds: [World] = []
var segment: Segment = .friend
@ObservationIgnored let service: any FavoriteServiceProtocol

enum Segment {
case friend, world
}

/// Initializes the view model with the specified HTTP client.
/// - Parameter client: The `APIClient` instance used for making network requests.
init(service: any FavoriteServiceProtocol) {
self.service = service
}

/// A filtered list of favorite groups that contain only friend-type groups.
/// It retrieves friend-type groups from the `favoriteGroups` property.
var favoriteFriendGroups: [FavoriteGroup] {
Expand All @@ -36,7 +30,7 @@ class FavoriteViewModel {

/// Asynchronously fetches and updates the favorite groups and their details.
/// - Throws: An error if any network request or decoding operation fails.
func fetchFavorite(friendVM: FriendViewModel) async throws {
func fetchFavorite(service: FavoriteServiceProtocol, friendVM: FriendViewModel) async throws {
favoriteGroups = try await service.listFavoriteGroups()
let favoriteDetails = try await service.fetchFavoriteGroupDetails(favoriteGroups: favoriteGroups)
let favoriteDetailsOfFriends = favoriteDetails.filter { $0.allFavoritesAre(.friend) }
Expand Down Expand Up @@ -113,7 +107,7 @@ class FavoriteViewModel {
/// - Parameters:
/// - friendId: The friend's id whose favorite status is being updated.
/// - targetGroup: The `FavoriteGroup` object representing the target group for the favorite status.
func updateFavorite(friend: Friend, targetGroup: FavoriteGroup) async throws {
func updateFavorite(service: FavoriteServiceProtocol, friend: Friend, targetGroup: FavoriteGroup) async throws {
let sourceGroupId = findFavoriteGroupIdForFriend(friendId: friend.id)
if let sourceGroupId = sourceGroupId {
_ = try await service.removeFavorite(favoriteId: friend.id)
Expand All @@ -128,6 +122,10 @@ class FavoriteViewModel {
addFriendToFavoriteGroup(friend: friend, groupId: targetGroup.id)
}
}

func fetchFavoritedWorlds(service: any WorldServiceProtocol) async throws {
favoriteWorlds = try await service.fetchFavoritedWorlds()
}
}

extension FavoriteViewModel.Segment: Identifiable {
Expand Down
5 changes: 5 additions & 0 deletions harmonie/ViewModels/FriendViewModel+Filters.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ extension FriendViewModel {
case asc, desc
}

func clearFilters() {
filterUserStatus = []
filterFavoriteGroups = []
}

/// Filters the list of friends based on the specified list type.
/// - Parameter favoriteFriends: Favorite friends information.
/// - Returns: A filtered list of friends whose display names meet the criteria defined by `isIncluded`.
Expand Down
Loading

0 comments on commit ecd43bb

Please sign in to comment.