Skip to content

Commit 4d844dc

Browse files
authored
Merge pull request #102 from makinosp/develop
Develop
2 parents 241fead + f101c16 commit 4d844dc

File tree

9 files changed

+91
-38
lines changed

9 files changed

+91
-38
lines changed

Harmonie.xcodeproj/project.pbxproj

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
204709A52C7B47E100A56DF8 /* VRCKit in Frameworks */ = {isa = PBXBuildFile; productRef = 2096016C2C4BD30100D905AF /* VRCKit */; };
1717
204709A62C7B47E100A56DF8 /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = 205D81A52C71B9D70047C1CC /* SwiftUIIntrospect */; };
1818
20592B032C45640D00E1C8B8 /* AsyncSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 20592B022C45640D00E1C8B8 /* AsyncSwiftUI */; };
19+
20593CD92C8D4A8000D1305B /* Shimmer in Frameworks */ = {isa = PBXBuildFile; productRef = 20593CD82C8D4A8000D1305B /* Shimmer */; };
1920
206AC9012BFA07AA00E0D5AE /* AsyncSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 206AC9002BFA07AA00E0D5AE /* AsyncSwiftUI */; };
2021
2094C4002C43BBC100F88986 /* VRCKit in Frameworks */ = {isa = PBXBuildFile; productRef = 2094C3FF2C43BBC100F88986 /* VRCKit */; };
2122
20C7F8BB2C480E6800AC7A56 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 20C7F89E2C480E6800AC7A56 /* LICENSE */; };
@@ -78,6 +79,7 @@
7879
20395DC22B945EEF00003921 /* VRCKit in Frameworks */,
7980
2014A0B32C3832690019C638 /* VRCKit in Frameworks */,
8081
20DC3C292C422786001101DB /* VRCKit in Frameworks */,
82+
20593CD92C8D4A8000D1305B /* Shimmer in Frameworks */,
8183
206AC9012BFA07AA00E0D5AE /* AsyncSwiftUI in Frameworks */,
8284
);
8385
runOnlyForDeploymentPostprocessing = 0;
@@ -151,6 +153,7 @@
151153
2052C7AD2C4BC97100B341D1 /* VRCKit */,
152154
2096016C2C4BD30100D905AF /* VRCKit */,
153155
205D81A52C71B9D70047C1CC /* SwiftUIIntrospect */,
156+
20593CD82C8D4A8000D1305B /* Shimmer */,
154157
);
155158
productName = harmonie;
156159
productReference = 20395D912B945CD700003921 /* Harmonie.app */;
@@ -235,6 +238,7 @@
235238
20592B012C45640D00E1C8B8 /* XCRemoteSwiftPackageReference "async-swift-ui" */,
236239
2096016B2C4BD30100D905AF /* XCRemoteSwiftPackageReference "vrckit" */,
237240
205D81A42C71B9D70047C1CC /* XCRemoteSwiftPackageReference "swiftui-introspect" */,
241+
20593CD72C8D4A8000D1305B /* XCRemoteSwiftPackageReference "SwiftUI-Shimmer" */,
238242
);
239243
productRefGroup = 20395D922B945CD700003921 /* Products */;
240244
projectDirPath = "";
@@ -635,6 +639,14 @@
635639
kind = branch;
636640
};
637641
};
642+
20593CD72C8D4A8000D1305B /* XCRemoteSwiftPackageReference "SwiftUI-Shimmer" */ = {
643+
isa = XCRemoteSwiftPackageReference;
644+
repositoryURL = "https://github.com/markiv/SwiftUI-Shimmer";
645+
requirement = {
646+
kind = upToNextMajorVersion;
647+
minimumVersion = 1.5.1;
648+
};
649+
};
638650
205D81A42C71B9D70047C1CC /* XCRemoteSwiftPackageReference "swiftui-introspect" */ = {
639651
isa = XCRemoteSwiftPackageReference;
640652
repositoryURL = "https://github.com/siteline/swiftui-introspect";
@@ -688,6 +700,11 @@
688700
package = 20592B012C45640D00E1C8B8 /* XCRemoteSwiftPackageReference "async-swift-ui" */;
689701
productName = AsyncSwiftUI;
690702
};
703+
20593CD82C8D4A8000D1305B /* Shimmer */ = {
704+
isa = XCSwiftPackageProductDependency;
705+
package = 20593CD72C8D4A8000D1305B /* XCRemoteSwiftPackageReference "SwiftUI-Shimmer" */;
706+
productName = Shimmer;
707+
};
691708
205D81A52C71B9D70047C1CC /* SwiftUIIntrospect */ = {
692709
isa = XCSwiftPackageProductDependency;
693710
package = 205D81A42C71B9D70047C1CC /* XCRemoteSwiftPackageReference "swiftui-introspect" */;

Harmonie.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

harmonie/Components/SquareURLImage.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77

88
import NukeUI
9+
import Shimmer
910
import SwiftUI
1011

1112
struct SquareURLImage: View {
@@ -25,7 +26,10 @@ struct SquareURLImage: View {
2526
}
2627

2728
var placeholder: some View {
28-
rect.fill(.gray).frame(width: frameWidth, height: frameWidth * 3/4)
29+
rect
30+
.frame(width: frameWidth, height: frameWidth * 3/4)
31+
.redacted(reason: .placeholder)
32+
.shimmering()
2933
}
3034

3135
var body: some View {
@@ -40,11 +44,10 @@ struct SquareURLImage: View {
4044
} else {
4145
placeholder
4246
.onDisappear {
43-
isImageLoaded = true
47+
withAnimation { isImageLoaded = true }
4448
}
4549
}
4650
}
4751
.frame(width: frameWidth, height: frameWidth * 3/4)
48-
.redacted(reason: isImageLoaded ? [] : .placeholder)
4952
}
5053
}

harmonie/Utils/Constants.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ enum Constants {
5252
static var setting: some View {
5353
Image(systemName: "gear")
5454
}
55+
static var shield: some View {
56+
Image(systemName: "shield.fill")
57+
}
5558
static var sort: some View {
5659
Image(systemName: "arrow.up.arrow.down")
5760
}

harmonie/ViewModels/FriendViewModel.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ import VRCKit
1212
class FriendViewModel {
1313
var onlineFriends: [Friend] = []
1414
var offlineFriends: [Friend] = []
15+
var friendsLocations: [FriendsLocation] = []
1516
var filterUserStatus: Set<UserStatus> = []
1617
var filterFavoriteGroups: Set<FavoriteGroup> = []
1718
var filterText: String = ""
1819
var sortType: SortType = .default
1920
var sortOrder: SortOrder = .asc
21+
var isRequesting = true
2022
@ObservationIgnored let user: User
2123
@ObservationIgnored private let service: any FriendServiceProtocol
2224

@@ -33,10 +35,6 @@ class FriendViewModel {
3335
allFriends.first { $0.id == id }
3436
}
3537

36-
var friendsLocations: [FriendsLocation] {
37-
service.friendsGroupedByLocation(onlineFriends)
38-
}
39-
4038
/// Returns a list of matches for either `onlineFriends` or `offlineFriends`
4139
/// for each id of reversed order friend list.
4240
/// - Returns a list of recentry friends
@@ -58,5 +56,6 @@ class FriendViewModel {
5856
)
5957
onlineFriends = try await onlineFriendsTask
6058
offlineFriends = try await offlineFriendsTask
59+
friendsLocations = service.friendsGroupedByLocation(onlineFriends)
6160
}
6261
}

harmonie/Views/Location/LocationCardView.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
//
77

88
import NukeUI
9+
import Shimmer
910
import SwiftUI
1011
import VRCKit
1112

1213
struct LocationCardView: View {
1314
@Environment(AppViewModel.self) private var appVM: AppViewModel
1415
@Binding var selected: InstanceLocation?
1516
@State private var instance: Instance?
17+
@State private var isRequesting = true
1618
let service: any InstanceServiceProtocol
1719
let location: FriendsLocation
1820

@@ -29,19 +31,22 @@ struct LocationCardView: View {
2931
ZStack {
3032
RoundedRectangle(cornerRadius: 16)
3133
.foregroundStyle(backGroundColor)
32-
if let instance = instance {
34+
if isRequesting {
35+
locationCardContent(instance: PreviewDataProvider.generateInstance())
36+
.redacted(reason: .placeholder)
37+
.shimmering()
38+
} else if let instance = instance {
3339
locationCardContent(instance: instance)
3440
.onTapGesture {
3541
selected = InstanceLocation(location: location, instance: instance)
3642
}
37-
} else {
38-
ProgressView()
3943
}
4044
}
4145
.frame(minHeight: 120)
4246
.task {
4347
if case let .id(id) = location.location {
4448
do {
49+
defer { withAnimation { isRequesting = false } }
4550
instance = try await service.fetchInstance(location: id)
4651
} catch {
4752
appVM.handleError(error)

harmonie/Views/Location/LocationsView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ struct LocationsView: View {
7070
.padding(.horizontal, 8)
7171
}
7272
.overlay {
73-
if friendVM.friendsLocations.isEmpty {
73+
if friendVM.isRequesting {
74+
ProgressScreen()
75+
} else if friendVM.friendsLocations.isEmpty {
7476
ContentUnavailableView {
7577
Label {
7678
Text("No Friend Location")

harmonie/Views/MainTabView.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ struct MainTabView: View {
2626
}
2727
.task {
2828
do {
29+
defer { friendVM.isRequesting = false }
2930
try await friendVM.fetchAllFriends()
31+
} catch {
32+
appVM.handleError(error)
33+
}
34+
do {
3035
try await favoriteVM.fetchFavorite(friendVM: friendVM)
3136
} catch {
3237
appVM.handleError(error)

harmonie/Views/UserDetail/UserDetailView+ProfileImageContainer.swift

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,42 +22,52 @@ extension UserDetailView {
2222
}
2323

2424
var bottomBar: some View {
25-
HStack {
26-
HStack(alignment: .bottom) {
27-
Label {
28-
Text(user.displayName)
29-
} icon: {
30-
Constants.Icon.circleFilled
31-
.foregroundStyle(statusColor)
32-
}
25+
VStack(alignment: .leading) {
26+
Text(user.displayName)
3327
.font(.headline)
28+
HStack {
3429
statusDescription
30+
Spacer()
31+
trustRankLabel
3532
}
36-
.frame(maxWidth: .infinity, alignment: .leading)
3733
}
34+
.frame(maxWidth: .infinity, alignment: .leading)
3835
.padding(.vertical, 8)
3936
.padding(.horizontal, 12)
4037
}
4138

4239
var statusDescription: some View {
43-
Text(user.statusDescription)
44-
.lineLimit(1)
45-
.font(.subheadline)
40+
Label {
41+
Text(user.statusDescription.isEmpty ? user.status.description : user.statusDescription)
42+
} icon: {
43+
Constants.Icon.circleFilled
44+
.foregroundStyle(statusColor)
45+
}
46+
.lineLimit(1)
47+
.font(.subheadline)
4648
}
4749

48-
var displayStatusAndName: some View {
49-
HStack(alignment: .bottom) {
50-
Label {
51-
Text(user.displayName)
52-
} icon: {
53-
Constants.Icon.circleFilled
54-
.foregroundStyle(user.status.color)
55-
}
56-
.font(.headline)
57-
Text(user.statusDescription)
58-
.font(.subheadline)
59-
Spacer()
50+
var trustRankLabel: some View {
51+
Label {
52+
Text(user.trustRank.description)
53+
} icon: {
54+
Constants.Icon.shield
55+
}
56+
.font(.footnote.bold())
57+
.padding(.horizontal, 12)
58+
.padding(.vertical, 4)
59+
.background(trustRankColor(user.trustRank).opacity(0.5))
60+
.background(.thinMaterial)
61+
.cornerRadius(8)
62+
}
63+
64+
func trustRankColor(_ trustRank: TrustRank) -> Color {
65+
switch trustRank {
66+
case .trusted: .indigo
67+
case .known: .orange
68+
case .user: .green
69+
case .newUser: .blue
70+
case .visitor, .unknown: .gray
6071
}
61-
.padding(8)
6272
}
6373
}

0 commit comments

Comments
 (0)