Skip to content

Commit dda478a

Browse files
authored
fix: emptyclasroom error
2 parents 60c892c + 025eda0 commit dda478a

4 files changed

Lines changed: 200 additions & 69 deletions

File tree

VITTY/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Google Services configuration file
2+
GoogleService-Info.plist

VITTY/VITTY.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
4B183EEA2D7C793800C9D801 /* RemindersData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B183EE92D7C791400C9D801 /* RemindersData.swift */; };
2929
4B183EEC2D7CB15800C9D801 /* CourseRefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B183EEB2D7CB11500C9D801 /* CourseRefs.swift */; };
3030
4B1BDBCC2E1396B1008C2DE9 /* ToolTip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1BDBCB2E1396A9008C2DE9 /* ToolTip.swift */; };
31+
4B2D1F0F2E26060C002AFD25 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4B2D1F0E2E26060C002AFD25 /* GoogleService-Info.plist */; };
3132
4B2D648F2E20BA6300412CB7 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2D648E2E20BA5A00412CB7 /* NetworkMonitor.swift */; };
3233
4B2D64902E20BA6300412CB7 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2D648E2E20BA5A00412CB7 /* NetworkMonitor.swift */; };
3334
4B2D64922E20C1AC00412CB7 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4B2D64912E20C1AC00412CB7 /* GoogleService-Info.plist */; };
@@ -192,6 +193,7 @@
192193
4B183EE92D7C791400C9D801 /* RemindersData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemindersData.swift; sourceTree = "<group>"; };
193194
4B183EEB2D7CB11500C9D801 /* CourseRefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseRefs.swift; sourceTree = "<group>"; };
194195
4B1BDBCB2E1396A9008C2DE9 /* ToolTip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolTip.swift; sourceTree = "<group>"; };
196+
4B2D1F0E2E26060C002AFD25 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
195197
4B2D648E2E20BA5A00412CB7 /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = "<group>"; };
196198
4B2D64912E20C1AC00412CB7 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
197199
4B2DD6942E0A702D00BC3B67 /* CircleRequests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleRequests.swift; sourceTree = "<group>"; };
@@ -406,6 +408,7 @@
406408
4BC853C52DF6F71B0092B2E2 /* VittyWidgetExtension.entitlements */,
407409
4B2D64912E20C1AC00412CB7 /* GoogleService-Info.plist */,
408410
52EE849D2CB9CD1F00CD864C /* GoogleService-Info.plist */,
411+
4B2D1F0E2E26060C002AFD25 /* GoogleService-Info.plist */,
409412
5251A7FF2B46E3C000D44CFE /* .swift-format */,
410413
314A408E27383BEC0058082F /* VITTYApp.swift */,
411414
314A409027383BEC0058082F /* ContentView.swift */,
@@ -1097,6 +1100,7 @@
10971100
31128CFE2772F57E0084C9EA /* Poppins-MediumItalic.ttf in Resources */,
10981101
31128CFD2772F57E0084C9EA /* Poppins-SemiBold.ttf in Resources */,
10991102
31128CF92772F57E0084C9EA /* Poppins-Medium.ttf in Resources */,
1103+
4B2D1F0F2E26060C002AFD25 /* GoogleService-Info.plist in Resources */,
11001104
31128CFA2772F57E0084C9EA /* Poppins-SemiBoldItalic.ttf in Resources */,
11011105
31128CFC2772F57E0084C9EA /* Poppins-Regular.ttf in Resources */,
11021106
52EE849E2CB9CD1F00CD864C /* GoogleService-Info.plist in Resources */,

VITTY/VITTY/EmptyClassroom/View/EmptyClass.swift

Lines changed: 163 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
//
2+
// EmptyClassAPIService.swift
3+
// VITTY
4+
//
5+
// Created by Rujin Devkota on 2/27/25.
6+
//
7+
18
import SwiftUI
29

310
struct EmptyClassRoom: View {
@@ -88,83 +95,172 @@ struct EmptyClassRoom: View {
8895
private var contentView: some View {
8996
Group {
9097
if viewModel.isLoading {
91-
VStack(spacing: 16) {
92-
ProgressView()
93-
.scaleEffect(1.2)
94-
.tint(.white)
95-
Text("Loading classrooms...")
96-
.foregroundColor(.white.opacity(0.8))
97-
.font(.subheadline)
98-
}
99-
.frame(maxWidth: .infinity, maxHeight: .infinity)
100-
.padding()
98+
loadingView
99+
} else if viewModel.isGenerating {
100+
generatingView
101101
} else if let errorMessage = viewModel.errorMessage {
102-
VStack(spacing: 16) {
103-
Image(systemName: "exclamationmark.triangle")
104-
.font(.system(size: 40))
105-
.foregroundColor(.red.opacity(0.8))
106-
Text("Error")
107-
.font(.headline)
108-
.foregroundColor(.white)
109-
Text(errorMessage)
110-
.foregroundColor(.red.opacity(0.8))
111-
.multilineTextAlignment(.center)
112-
.font(.subheadline)
113-
Button("Retry") {
114-
Task {
115-
await viewModel.fetchEmptyClassrooms(slot: selectedSlot, authToken: authViewModel.loggedInBackendUser?.token ?? "")
116-
}
117-
}
102+
errorView(errorMessage)
103+
} else if viewModel.emptyClassrooms.isEmpty {
104+
emptyStateView
105+
} else if filteredClassrooms.isEmpty && !searchText.isEmpty {
106+
noResultsView
107+
} else {
108+
classroomsGrid
109+
}
110+
}
111+
}
112+
113+
private var loadingView: some View {
114+
VStack(spacing: 16) {
115+
ProgressView()
116+
.scaleEffect(1.2)
117+
.tint(.white)
118+
Text("Loading classrooms...")
119+
.foregroundColor(.white.opacity(0.8))
120+
.font(.subheadline)
121+
}
122+
.frame(maxWidth: .infinity, maxHeight: .infinity)
123+
.padding()
124+
}
125+
126+
private var generatingView: some View {
127+
VStack(spacing: 24) {
128+
// Animated hourglass icon
129+
Image(systemName: "hourglass")
130+
.font(.system(size: 50))
131+
.foregroundColor(.blue.opacity(0.7))
132+
.rotationEffect(.degrees(viewModel.isGenerating ? 180 : 0))
133+
.animation(.easeInOut(duration: 1.5).repeatForever(autoreverses: true), value: viewModel.isGenerating)
134+
135+
VStack(spacing: 16) {
136+
Text("Preparing Your Data")
137+
.font(.title2)
138+
.fontWeight(.semibold)
118139
.foregroundColor(.white)
140+
141+
Text("Our system is currently generating the latest classroom information for slot \(selectedSlot).")
142+
.foregroundColor(.white.opacity(0.8))
143+
.multilineTextAlignment(.center)
144+
.font(.body)
119145
.padding(.horizontal, 20)
120-
.padding(.vertical, 8)
121-
.background(Color.blue.opacity(0.7))
122-
.cornerRadius(8)
123-
}
124-
.frame(maxWidth: .infinity, maxHeight: .infinity)
125-
.padding()
126-
} else if viewModel.emptyClassrooms.isEmpty {
127-
VStack(spacing: 16) {
128-
Image(systemName: "building.2")
129-
.font(.system(size: 40))
146+
147+
VStack(spacing: 8) {
148+
Text("This process may take a few moments")
149+
.foregroundColor(.blue.opacity(0.8))
150+
.font(.subheadline)
151+
152+
Text("Please try reloading in a moment")
130153
.foregroundColor(.white.opacity(0.6))
131-
Text("No Classrooms Available")
132-
.font(.headline)
133-
.foregroundColor(.white)
134-
Text("There are no empty classrooms for slot \(selectedSlot) at this time.")
135-
.foregroundColor(.white.opacity(0.8))
136-
.multilineTextAlignment(.center)
154+
.font(.caption)
155+
}
156+
.padding(.top, 8)
157+
}
158+
159+
Button(action: {
160+
viewModel.reload(slot: selectedSlot, authToken: authViewModel.loggedInBackendUser?.token ?? "")
161+
}) {
162+
HStack(spacing: 8) {
163+
Image(systemName: "arrow.clockwise")
164+
.font(.system(size: 14, weight: .medium))
165+
Text("Reload")
137166
.font(.subheadline)
167+
.fontWeight(.medium)
138168
}
139-
.frame(maxWidth: .infinity, maxHeight: .infinity)
140-
.padding()
141-
} else if filteredClassrooms.isEmpty && !searchText.isEmpty {
142-
VStack(spacing: 16) {
143-
Image(systemName: "magnifyingglass")
144-
.font(.system(size: 40))
145-
.foregroundColor(.white.opacity(0.6))
146-
Text("No Results Found")
147-
.font(.headline)
148-
.foregroundColor(.white)
149-
Text("No classrooms match '\(searchText)'")
150-
.foregroundColor(.white.opacity(0.8))
151-
.multilineTextAlignment(.center)
169+
.foregroundColor(.white)
170+
.padding(.horizontal, 24)
171+
.padding(.vertical, 12)
172+
.background(
173+
RoundedRectangle(cornerRadius: 10)
174+
.fill(Color.blue.opacity(0.7))
175+
.shadow(color: .black.opacity(0.2), radius: 4, x: 0, y: 2)
176+
)
177+
}
178+
.padding(.top, 8)
179+
}
180+
.frame(maxWidth: .infinity, maxHeight: .infinity)
181+
.padding()
182+
}
183+
184+
private func errorView(_ errorMessage: String) -> some View {
185+
VStack(spacing: 16) {
186+
Image(systemName: "exclamationmark.triangle")
187+
.font(.system(size: 40))
188+
.foregroundColor(.orange.opacity(0.8))
189+
190+
Text("Oops!")
191+
.font(.headline)
192+
.foregroundColor(.white)
193+
194+
Text(errorMessage)
195+
.foregroundColor(.white.opacity(0.8))
196+
.multilineTextAlignment(.center)
197+
.font(.subheadline)
198+
.padding(.horizontal)
199+
200+
Button(action: {
201+
viewModel.reload(slot: selectedSlot, authToken: authViewModel.loggedInBackendUser?.token ?? "")
202+
}) {
203+
HStack(spacing: 8) {
204+
Image(systemName: "arrow.clockwise")
205+
.font(.system(size: 14, weight: .medium))
206+
Text("Reload")
152207
.font(.subheadline)
153-
Button("Clear Search") {
154-
searchText = ""
155-
}
156-
.foregroundColor(.white)
157-
.padding(.horizontal, 20)
158-
.padding(.vertical, 8)
159-
.background(Color.blue.opacity(0.7))
160-
.cornerRadius(8)
208+
.fontWeight(.medium)
161209
}
162-
.frame(maxWidth: .infinity, maxHeight: .infinity)
163-
.padding()
164-
} else {
165-
classroomsGrid
210+
.foregroundColor(.white)
211+
.padding(.horizontal, 24)
212+
.padding(.vertical, 12)
213+
.background(
214+
RoundedRectangle(cornerRadius: 10)
215+
.fill(Color.orange.opacity(0.7))
216+
.shadow(color: .black.opacity(0.2), radius: 4, x: 0, y: 2)
217+
)
218+
}
219+
}
220+
.frame(maxWidth: .infinity, maxHeight: .infinity)
221+
.padding()
222+
}
223+
224+
private var emptyStateView: some View {
225+
VStack(spacing: 16) {
226+
Image(systemName: "building.2")
227+
.font(.system(size: 40))
228+
.foregroundColor(.white.opacity(0.6))
229+
Text("No Classrooms Available")
230+
.font(.headline)
231+
.foregroundColor(.white)
232+
Text("There are no empty classrooms for slot \(selectedSlot) at this time.")
233+
.foregroundColor(.white.opacity(0.8))
234+
.multilineTextAlignment(.center)
235+
.font(.subheadline)
236+
}
237+
.frame(maxWidth: .infinity, maxHeight: .infinity)
238+
.padding()
239+
}
240+
241+
private var noResultsView: some View {
242+
VStack(spacing: 16) {
243+
Image(systemName: "magnifyingglass")
244+
.font(.system(size: 40))
245+
.foregroundColor(.white.opacity(0.6))
246+
Text("No Results Found")
247+
.font(.headline)
248+
.foregroundColor(.white)
249+
Text("No classrooms match '\(searchText)'")
250+
.foregroundColor(.white.opacity(0.8))
251+
.multilineTextAlignment(.center)
252+
.font(.subheadline)
253+
Button("Clear Search") {
254+
searchText = ""
166255
}
256+
.foregroundColor(.white)
257+
.padding(.horizontal, 20)
258+
.padding(.vertical, 8)
259+
.background(Color.blue.opacity(0.7))
260+
.cornerRadius(8)
167261
}
262+
.frame(maxWidth: .infinity, maxHeight: .infinity)
263+
.padding()
168264
}
169265

170266
private var classroomsGrid: some View {

VITTY/VITTY/EmptyClassroom/ViewModel/EmptyClassRoomViewModel.swift

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,52 @@ class EmptyClassroomViewModel: ObservableObject {
1212
@Published var emptyClassrooms: [String] = []
1313
@Published var isLoading: Bool = false
1414
@Published var errorMessage: String?
15+
@Published var isGenerating: Bool = false
1516
private var currentSlot: String = "A1"
1617

1718
func fetchEmptyClassrooms(slot: String, authToken: String) async {
1819
guard !slot.isEmpty else { return }
1920

2021
isLoading = true
2122
errorMessage = nil
23+
isGenerating = false
2224
currentSlot = slot
2325

2426
do {
2527
let classrooms = try await EmptyClassRoomAPIService.shared.getEmptyClassrooms(slot: slot, authToken: authToken)
2628
emptyClassrooms = classrooms
29+
isGenerating = false
2730
} catch {
28-
errorMessage = "Failed to load empty classrooms: \(error.localizedDescription)"
31+
handleError(error)
2932
}
3033

3134
isLoading = false
3235
}
36+
37+
private func handleError(_ error: Error) {
38+
let errorDescription = error.localizedDescription.lowercased()
39+
40+
// Check if this is a server error (likely generation in progress)
41+
if let nsError = error as NSError?,
42+
nsError.code >= 500 ||
43+
errorDescription.contains("contact vitty support") ||
44+
errorDescription.contains("failed to parse error response") ||
45+
errorDescription.contains("generating") ||
46+
errorDescription.contains("server error") {
47+
48+
// Show generation state instead of error
49+
isGenerating = true
50+
errorMessage = nil
51+
} else {
52+
// Only show actual errors for client-side issues
53+
isGenerating = false
54+
errorMessage = "Failed to load empty classrooms: \(error.localizedDescription)"
55+
}
56+
}
57+
58+
func reload(slot: String, authToken: String) {
59+
Task {
60+
await fetchEmptyClassrooms(slot: slot, authToken: authToken)
61+
}
62+
}
3363
}
34-

0 commit comments

Comments
 (0)