From 3669c585045f9a97fdc4448f8386333c1df5436d Mon Sep 17 00:00:00 2001 From: makinosp Date: Sat, 21 Sep 2024 23:03:58 +0900 Subject: [PATCH 1/7] fix: layout in LocationsView --- harmonie/Views/Location/LocationsView.swift | 52 +++++++++++++-------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/harmonie/Views/Location/LocationsView.swift b/harmonie/Views/Location/LocationsView.swift index 9e384f8..3c0ae38 100644 --- a/harmonie/Views/Location/LocationsView.swift +++ b/harmonie/Views/Location/LocationsView.swift @@ -11,37 +11,41 @@ import VRCKit struct LocationsView: View, FriendServicePresentable, InstanceServicePresentable { @Environment(AppViewModel.self) var appVM: AppViewModel @Environment(FriendViewModel.self) var friendVM: FriendViewModel + @State private var columnVisibility: NavigationSplitViewVisibility = .all @State private var selectedInstance: InstanceLocation? @State private var selectedUser: Selected? var body: some View { - NavigationSplitView(columnVisibility: .constant(.all)) { + NavigationSplitView(columnVisibility: $columnVisibility) { locationList .navigationTitle("Locations") - .navigationSplitViewColumnWidth( - min: 300, - ideal: WindowUtil.width / 2, - max: WindowUtil.width / 2 - ) + .setColumn() } content: { - if let selectedInstance = selectedInstance { - LocationDetailView( - instanceLocation: selectedInstance, - selectedUser: $selectedUser - ) - } else { - ContentUnavailableView { - Label { - Text("Select a location") - } icon: { - Constants.Icon.location + Group { + if let selectedInstance = selectedInstance { + LocationDetailView( + instanceLocation: selectedInstance, + selectedUser: $selectedUser + ) + } else { + ContentUnavailableView { + Label { + Text("Select a location") + } icon: { + Constants.Icon.location + } } } } + .setColumn() } detail: { - if let selectedUser = selectedUser { - UserDetailPresentationView(selected: selectedUser) + Group { + if let selectedUser = selectedUser { + UserDetailPresentationView(selected: selectedUser) + .id(selectedUser) + } } + .setColumn() } .navigationSplitViewStyle(.balanced) .refreshable { @@ -77,3 +81,13 @@ struct LocationsView: View, FriendServicePresentable, InstanceServicePresentable } } } + +fileprivate extension View { + func setColumn() -> some View { + self.navigationSplitViewColumnWidth( + min: WindowUtil.width * 1 / 3, + ideal: WindowUtil.width * 1 / 3, + max: WindowUtil.width / 2 + ) + } +} From f32628705216740e5d64567927635565bc59a983 Mon Sep 17 00:00:00 2001 From: makinosp Date: Sat, 21 Sep 2024 23:04:59 +0900 Subject: [PATCH 2/7] fix: warning of getting last activity in UserDetailView --- .../UserDetail/UserDetailView+ProfileImageContainer.swift | 2 +- harmonie/Views/UserDetail/UserDetailView.swift | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/harmonie/Views/UserDetail/UserDetailView+ProfileImageContainer.swift b/harmonie/Views/UserDetail/UserDetailView+ProfileImageContainer.swift index a7f8ecc..f9e844a 100644 --- a/harmonie/Views/UserDetail/UserDetailView+ProfileImageContainer.swift +++ b/harmonie/Views/UserDetail/UserDetailView+ProfileImageContainer.swift @@ -17,7 +17,7 @@ extension UserDetailView { HStack { Spacer() Label { - Text(DateUtil.shared.formatRelative(from: user.lastActivity)) + Text(lastActivity) } icon: { Image(systemName: "stopwatch") } diff --git a/harmonie/Views/UserDetail/UserDetailView.swift b/harmonie/Views/UserDetail/UserDetailView.swift index 9d8e96e..82dc283 100644 --- a/harmonie/Views/UserDetail/UserDetailView.swift +++ b/harmonie/Views/UserDetail/UserDetailView.swift @@ -19,6 +19,7 @@ struct UserDetailView: View, FavoriteServicePresentable, InstanceServicePresenta @State var instance: Instance? @State var note: String @State var isRequesting = false + @State var lastActivity = "" init(user: UserDetail) { _user = State(initialValue: user) @@ -47,6 +48,9 @@ struct UserDetailView: View, FavoriteServicePresentable, InstanceServicePresenta await fetchInstance(id: id) } } + .task { + lastActivity = await DateUtil.shared.formatRelative(from: user.lastActivity) + } } private var contentStacks: some View { From b9c67fce9f79d84fd8f4685638ab34a694def46c Mon Sep 17 00:00:00 2001 From: makinosp Date: Sat, 21 Sep 2024 23:20:20 +0900 Subject: [PATCH 3/7] refact: refine model for views --- harmonie/Models/SegmentModel.swift | 29 +++++++++++++++++++++ harmonie/Models/SelectionModel.swift | 19 ++++++++++++++ harmonie/Utils/Selected.swift | 10 ------- harmonie/ViewModels/FavoriteViewModel.swift | 23 ---------------- 4 files changed, 48 insertions(+), 33 deletions(-) create mode 100644 harmonie/Models/SegmentModel.swift create mode 100644 harmonie/Models/SelectionModel.swift delete mode 100644 harmonie/Utils/Selected.swift diff --git a/harmonie/Models/SegmentModel.swift b/harmonie/Models/SegmentModel.swift new file mode 100644 index 0000000..51a28ad --- /dev/null +++ b/harmonie/Models/SegmentModel.swift @@ -0,0 +1,29 @@ +// +// SegmentModel.swift +// Harmonie +// +// Created by makinosp on 2024/09/21. +// + +enum Segment { + case friend, world +} + +extension Segment: Identifiable { + var id: Int { hashValue } +} + +extension Segment: CaseIterable { + var allCases: [Segment] { + [.friend, .world] + } +} + +extension Segment: CustomStringConvertible { + var description: String { + switch self { + case .friend: "Friend" + case .world: "World" + } + } +} diff --git a/harmonie/Models/SelectionModel.swift b/harmonie/Models/SelectionModel.swift new file mode 100644 index 0000000..10f48d2 --- /dev/null +++ b/harmonie/Models/SelectionModel.swift @@ -0,0 +1,19 @@ +// +// SelectionModel.swift +// Harmonie +// +// Created by makinosp on 2024/07/28. +// + +struct Selected: Hashable, Identifiable { + let id: String +} + +struct SegmentIdSelection: Hashable, Sendable { + let selected: Selected + let segment: Segment +} + +extension SegmentIdSelection: Identifiable { + var id: Int { hashValue } +} diff --git a/harmonie/Utils/Selected.swift b/harmonie/Utils/Selected.swift deleted file mode 100644 index 0256fb5..0000000 --- a/harmonie/Utils/Selected.swift +++ /dev/null @@ -1,10 +0,0 @@ -// -// Selected.swift -// Harmonie -// -// Created by makinosp on 2024/07/28. -// - -struct Selected: Hashable, Identifiable { - let id: String -} diff --git a/harmonie/ViewModels/FavoriteViewModel.swift b/harmonie/ViewModels/FavoriteViewModel.swift index 4f651f1..6ee0458 100644 --- a/harmonie/ViewModels/FavoriteViewModel.swift +++ b/harmonie/ViewModels/FavoriteViewModel.swift @@ -17,10 +17,6 @@ final class FavoriteViewModel { var favoriteWorlds: [World] = [] var segment: Segment = .friend - enum Segment { - case friend, world - } - /// A filtered list of favorite groups that contain only friend-type groups. /// It retrieves friend-type groups from the `favoriteGroups` property. var favoriteFriendGroups: [FavoriteGroup] { @@ -126,22 +122,3 @@ final class FavoriteViewModel { favoriteWorlds = try await service.fetchFavoritedWorlds() } } - -extension FavoriteViewModel.Segment: Identifiable { - var id: Int { hashValue } -} - -extension FavoriteViewModel.Segment: CaseIterable { - var allCases: [FavoriteViewModel.Segment] { - [.friend, .world] - } -} - -extension FavoriteViewModel.Segment: CustomStringConvertible { - var description: String { - switch self { - case .friend: "Friend" - case .world: "World" - } - } -} From 062916f484e3d035590a6685ffa5ac41a80ff135 Mon Sep 17 00:00:00 2001 From: makinosp Date: Sat, 21 Sep 2024 23:20:41 +0900 Subject: [PATCH 4/7] refact: LocationCardView tag --- harmonie/Views/Location/LocationCardView.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/harmonie/Views/Location/LocationCardView.swift b/harmonie/Views/Location/LocationCardView.swift index 094aaa9..c41d54b 100644 --- a/harmonie/Views/Location/LocationCardView.swift +++ b/harmonie/Views/Location/LocationCardView.swift @@ -68,11 +68,15 @@ struct LocationCardView: View, InstanceServicePresentable { } } .onTapGesture { - selected = InstanceLocation(location: location, instance: instance) + selected = tag(instance) } } } - .tag(InstanceLocation(location: location, instance: instance)) + .tag(tag(instance)) + } + + private func tag(_ instance: Instance) -> InstanceLocation { + InstanceLocation(location: location, instance: instance) } private func personAmount(_ instance: Instance) -> String { From be223f7c835fa8ad9528b8c37f66462228f4a088 Mon Sep 17 00:00:00 2001 From: makinosp Date: Sat, 21 Sep 2024 23:36:12 +0900 Subject: [PATCH 5/7] fix: scope --- harmonie/Models/SelectionModel.swift | 2 +- harmonie/Views/Favorite/FavoritesView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/harmonie/Models/SelectionModel.swift b/harmonie/Models/SelectionModel.swift index 10f48d2..e2d2677 100644 --- a/harmonie/Models/SelectionModel.swift +++ b/harmonie/Models/SelectionModel.swift @@ -5,7 +5,7 @@ // Created by makinosp on 2024/07/28. // -struct Selected: Hashable, Identifiable { +struct Selected: Hashable, Identifiable, Sendable { let id: String } diff --git a/harmonie/Views/Favorite/FavoritesView.swift b/harmonie/Views/Favorite/FavoritesView.swift index abd4979..919a6b4 100644 --- a/harmonie/Views/Favorite/FavoritesView.swift +++ b/harmonie/Views/Favorite/FavoritesView.swift @@ -25,7 +25,7 @@ struct FavoritesView: View { ToolbarItem(placement: .status) { @Bindable var favoriteVM = favoriteVM Picker("", selection: $favoriteVM.segment) { - ForEach(FavoriteViewModel.Segment.allCases) { segment in + ForEach(Segment.allCases) { segment in Text(segment.description).tag(segment) } } From 7a76b2fd365d414eb58e0d4158c48f7100121038 Mon Sep 17 00:00:00 2001 From: makinosp Date: Sat, 21 Sep 2024 23:41:31 +0900 Subject: [PATCH 6/7] feat: selectable both friends and worlds in LocationDetailView --- harmonie/Models/SelectionModel.swift | 14 ++++++++ .../Views/Location/LocationDetailView.swift | 34 +++++++++++++------ harmonie/Views/Location/LocationsView.swift | 17 +++++++--- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/harmonie/Models/SelectionModel.swift b/harmonie/Models/SelectionModel.swift index e2d2677..5827148 100644 --- a/harmonie/Models/SelectionModel.swift +++ b/harmonie/Models/SelectionModel.swift @@ -17,3 +17,17 @@ struct SegmentIdSelection: Hashable, Sendable { extension SegmentIdSelection: Identifiable { var id: Int { hashValue } } + +extension SegmentIdSelection { + init(friendId: String) { + selected = Selected(id: friendId) + segment = .friend + } +} + +extension SegmentIdSelection { + init(worldId: String) { + selected = Selected(id: worldId) + segment = .world + } +} diff --git a/harmonie/Views/Location/LocationDetailView.swift b/harmonie/Views/Location/LocationDetailView.swift index a48f319..c686f8b 100644 --- a/harmonie/Views/Location/LocationDetailView.swift +++ b/harmonie/Views/Location/LocationDetailView.swift @@ -10,25 +10,37 @@ import NukeUI import VRCKit struct LocationDetailView: View { - @Binding var selectedUser: Selected? + @Binding var selection: SegmentIdSelection? private let instance: Instance private let location: FriendsLocation - init(instanceLocation: InstanceLocation, selectedUser: Binding) { + init(instanceLocation: InstanceLocation, selection: Binding) { instance = instanceLocation.instance location = instanceLocation.location - _selectedUser = selectedUser + _selection = selection } var body: some View { - List(selection: $selectedUser) { + List(selection: $selection) { Section("World") { - GradientOverlayImageView( - imageUrl: instance.world.imageUrl(.x1024), - thumbnailImageUrl: instance.world.imageUrl(.x256), - height: 80 - ) { bottomBar } - .listRowInsets(EdgeInsets()) + HStack(spacing: 12) { + SquareURLImage( + imageUrl: instance.world.imageUrl(.x1024), + thumbnailImageUrl: instance.world.imageUrl(.x256) + ) + VStack(alignment: .leading) { + Text(instance.world.name) + .font(.body) + .lineLimit(1) + Text(instance.world.description ?? "") + .font(.footnote) + .foregroundStyle(Color.gray) + .lineLimit(2) + } + .frame(maxWidth: .infinity, alignment: .leading) + Constants.Icon.forward + } + .tag(SegmentIdSelection(worldId: instance.world.id)) } Section("Friends") { friendList @@ -95,7 +107,7 @@ struct LocationDetailView: View { } icon: { UserIcon(user: friend, size: Constants.IconSize.thumbnail) } - .tag(Selected(id: friend.id)) + .tag(SegmentIdSelection(friendId: friend.id)) } } } diff --git a/harmonie/Views/Location/LocationsView.swift b/harmonie/Views/Location/LocationsView.swift index 3c0ae38..fbdabad 100644 --- a/harmonie/Views/Location/LocationsView.swift +++ b/harmonie/Views/Location/LocationsView.swift @@ -13,7 +13,7 @@ struct LocationsView: View, FriendServicePresentable, InstanceServicePresentable @Environment(FriendViewModel.self) var friendVM: FriendViewModel @State private var columnVisibility: NavigationSplitViewVisibility = .all @State private var selectedInstance: InstanceLocation? - @State private var selectedUser: Selected? + @State private var selection: SegmentIdSelection? var body: some View { NavigationSplitView(columnVisibility: $columnVisibility) { @@ -25,7 +25,7 @@ struct LocationsView: View, FriendServicePresentable, InstanceServicePresentable if let selectedInstance = selectedInstance { LocationDetailView( instanceLocation: selectedInstance, - selectedUser: $selectedUser + selection: $selection ) } else { ContentUnavailableView { @@ -40,9 +40,16 @@ struct LocationsView: View, FriendServicePresentable, InstanceServicePresentable .setColumn() } detail: { Group { - if let selectedUser = selectedUser { - UserDetailPresentationView(selected: selectedUser) - .id(selectedUser) + if let selection = selection { + Group { + switch selection.segment { + case .friend: + UserDetailPresentationView(selected: selection.selected) + case .world: + WorldDetailPresentationView(id: selection.selected.id) + } + } + .id(selection.selected.id) } } .setColumn() From 6c5075eb8e31187ded7e9b598d21ac63312a9352 Mon Sep 17 00:00:00 2001 From: makinosp Date: Sat, 21 Sep 2024 23:42:04 +0900 Subject: [PATCH 7/7] refact: navigationTitle in loading in WorldDetailPresentationView --- harmonie/Views/WorldDetail/WorldDetailPresentationView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/harmonie/Views/WorldDetail/WorldDetailPresentationView.swift b/harmonie/Views/WorldDetail/WorldDetailPresentationView.swift index 92d08f5..943222e 100644 --- a/harmonie/Views/WorldDetail/WorldDetailPresentationView.swift +++ b/harmonie/Views/WorldDetail/WorldDetailPresentationView.swift @@ -24,6 +24,8 @@ struct WorldDetailPresentationView: View, WorldServicePresentable { .task(id: id) { await fetchWorld(id: id) } + .navigationTitle("Loading...") + .navigationBarTitleDisplayMode(.inline) } }