Skip to content

Commit 4845ff3

Browse files
cursoragentsayed44net
andcommitted
Add loading states and skeleton views for community and dashboard
Co-authored-by: sayed44net <sayed44net@gmail.com>
1 parent e855418 commit 4845ff3

File tree

3 files changed

+30
-7
lines changed

3 files changed

+30
-7
lines changed

ios/ResilientMe/Managers/CommunityManager.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ struct CommunityStory: Identifiable, Codable {
2727

2828
final class CommunityManager: ObservableObject {
2929
@Published private(set) var stories: [CommunityStory] = []
30+
@Published var isLoading: Bool = false
3031

3132
func loadStories() async {
3233
#if canImport(FirebaseFirestore)
3334
guard FirebaseManager.shared.isConfigured else { return }
35+
await MainActor.run { self.isLoading = true }
3436
let db = Firestore.firestore()
3537
let snapshot = try? await db.collection("community").whereField("status", isEqualTo: "visible").order(by: "createdAt", descending: true).limit(to: 100).getDocuments()
3638
let list: [CommunityStory] = snapshot?.documents.compactMap { doc in
@@ -44,7 +46,10 @@ final class CommunityManager: ObservableObject {
4446
reactionsMap.forEach { key, value in if let r = Reaction(rawValue: key) { reactions[r] = value } }
4547
return CommunityStory(id: doc.documentID, type: type, content: content, createdAt: ts.dateValue(), reactions: reactions, userReaction: nil)
4648
} ?? []
47-
await MainActor.run { self.stories = list }
49+
await MainActor.run {
50+
self.stories = list
51+
self.isLoading = false
52+
}
4853
#endif
4954
}
5055

@@ -54,6 +59,11 @@ final class CommunityManager: ObservableObject {
5459
}
5560

5661
func addReaction(to story: CommunityStory, reaction: Reaction) {
62+
// optimistic update
63+
if let idx = stories.firstIndex(where: { $0.id == story.id }) {
64+
stories[idx].reactions[reaction, default: 0] += 1
65+
stories[idx].userReaction = reaction
66+
}
5767
#if canImport(FirebaseFunctions)
5868
guard FirebaseManager.shared.isConfigured else { return }
5969
let functions = Functions.functions()

ios/ResilientMe/Views/Community/CommunityView.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ struct CommunityView: View {
1919
.padding(.horizontal)
2020
}
2121

22-
if manager.getStories(filter: selectedFilter).isEmpty {
22+
if manager.isLoading {
23+
VStack(spacing: 12) {
24+
ForEach(0..<6) { _ in SkeletonView(height: 72, cornerRadius: 12) }
25+
}.padding()
26+
} else if manager.getStories(filter: selectedFilter).isEmpty {
2327
VStack(spacing: 12) {
2428
Text("No stories yet.").font(.resilientHeadline)
2529
Text("Share your first story. Someone will relate.").font(.resilientBody).foregroundColor(.secondary)

ios/ResilientMe/Views/DashboardView.swift

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,24 @@ import FirebaseFirestore
66
struct DashboardView: View {
77
@EnvironmentObject private var analyticsManager: AnalyticsManager
88
@State private var patterns: [Pattern] = []
9+
@State private var isLoading: Bool = true
910

1011
var body: some View {
1112
NavigationView {
1213
ScrollView {
1314
VStack(spacing: 16) {
14-
ResilienceRing(score: analyticsManager.currentResilienceScore)
15-
WeeklyStatsCard(stats: analyticsManager.weeklyStats)
16-
if !patterns.isEmpty {
17-
PatternAlertsCard(patterns: patterns)
15+
if isLoading {
16+
SkeletonView(height: 180, cornerRadius: 16)
17+
SkeletonView(height: 110, cornerRadius: 16)
18+
SkeletonView(height: 140, cornerRadius: 16)
19+
} else {
20+
ResilienceRing(score: analyticsManager.currentResilienceScore)
21+
WeeklyStatsCard(stats: analyticsManager.weeklyStats)
22+
if !patterns.isEmpty {
23+
PatternAlertsCard(patterns: patterns)
24+
}
25+
RecoveryTrendsChart(points: analyticsManager.recoveryTrend)
1826
}
19-
RecoveryTrendsChart(points: analyticsManager.recoveryTrend)
2027
}
2128
.padding()
2229
}
@@ -34,9 +41,11 @@ struct DashboardView: View {
3441
}
3542
}
3643
.onAppear {
44+
isLoading = true
3745
analyticsManager.recalculate()
3846
loadPatterns()
3947
AnalyticsManager.trackScreenView("Dashboard")
48+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) { isLoading = false }
4049
}
4150
}
4251
.background(Color.resilientBackground.ignoresSafeArea())

0 commit comments

Comments
 (0)