diff --git a/Campus-iOS.xcodeproj/project.pbxproj b/Campus-iOS.xcodeproj/project.pbxproj index e6dd78ae..65d9e7ac 100644 --- a/Campus-iOS.xcodeproj/project.pbxproj +++ b/Campus-iOS.xcodeproj/project.pbxproj @@ -226,7 +226,6 @@ 36BBE7342798B04D0018FD3F /* NewsSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36BBE7332798B04D0018FD3F /* NewsSource.swift */; }; 36C70FB128538A190097416E /* PanelContentCafeteriasListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C70FB028538A190097416E /* PanelContentCafeteriasListView.swift */; }; 36C70FB32854D2AB0097416E /* PanelContentStudyGroupsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C70FB22854D2AB0097416E /* PanelContentStudyGroupsListView.swift */; }; - 36E964A32774932B0055777F /* CalendarToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E964A22774932B0055777F /* CalendarToolbar.swift */; }; 36E964A5277493D90055777F /* CalendarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E964A4277493D90055777F /* CalendarViewModel.swift */; }; 36E964A7277498540055777F /* CalendarContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E964A6277498540055777F /* CalendarContentView.swift */; }; 36E964AA277498B60055777F /* KVKCalendar in Frameworks */ = {isa = PBXBuildFile; productRef = 36E964A9277498B60055777F /* KVKCalendar */; }; @@ -498,7 +497,6 @@ 36BBE7332798B04D0018FD3F /* NewsSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsSource.swift; sourceTree = ""; }; 36C70FB028538A190097416E /* PanelContentCafeteriasListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PanelContentCafeteriasListView.swift; sourceTree = ""; }; 36C70FB22854D2AB0097416E /* PanelContentStudyGroupsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PanelContentStudyGroupsListView.swift; sourceTree = ""; }; - 36E964A22774932B0055777F /* CalendarToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarToolbar.swift; sourceTree = ""; }; 36E964A4277493D90055777F /* CalendarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewModel.swift; sourceTree = ""; }; 36E964A6277498540055777F /* CalendarContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarContentView.swift; sourceTree = ""; }; 36E964AB277499860055777F /* CalendarDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarDisplayView.swift; sourceTree = ""; }; @@ -1468,7 +1466,6 @@ 36E9649F277492B70055777F /* Views */ = { isa = PBXGroup; children = ( - 36E964A22774932B0055777F /* CalendarToolbar.swift */, 36E964A6277498540055777F /* CalendarContentView.swift */, 36E964AB277499860055777F /* CalendarDisplayView.swift */, 36AD5CF327B8C83500DAE143 /* CalendarSingleEventView.swift */, @@ -1722,7 +1719,6 @@ 36BB6F7027B1197400F224AB /* Profile.swift in Sources */, 08D0703A28776DD6004140B1 /* TextWidgetView.swift in Sources */, 08FAFD15287DC484006A0E27 /* CalendarWidgetView.swift in Sources */, - 36E964A32774932B0055777F /* CalendarToolbar.swift in Sources */, 3654F3762851710E008AD5DC /* RoomFinderViewModel.swift in Sources */, 36108BC427A3046B007DC62D /* LectureDetailsDetailedInfoRowView.swift in Sources */, 36108C1C27A307FA007DC62D /* GradesView.swift in Sources */, @@ -2106,7 +2102,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.0; + MARKETING_VERSION = 4.1; PRODUCT_BUNDLE_IDENTIFIER = de.tum.tca; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -2149,7 +2145,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.0; + MARKETING_VERSION = 4.1; PRODUCT_BUNDLE_IDENTIFIER = de.tum.tca; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; diff --git a/Campus-iOS/App.swift b/Campus-iOS/App.swift index ab8df676..2f42c943 100644 --- a/Campus-iOS/App.swift +++ b/Campus-iOS/App.swift @@ -34,7 +34,7 @@ struct CampusApp: App { NavigationView { LoginView(model: model) .onAppear { - selectedTab = 2 + selectedTab = 0 } } .navigationViewStyle(.stack) @@ -82,19 +82,21 @@ struct CampusApp: App { view.navigationViewStyle(.stack) }) - NavigationView { - WidgetScreen(model: model) - //.navigationTitle("My Widgets") - .toolbar { - ToolbarItemGroup(placement: .navigationBarTrailing) { - ProfileToolbar(model: model) + if UIDevice.current.userInterfaceIdiom == .phone { + NavigationView { + WidgetScreen(model: model) + + .toolbar { + ToolbarItemGroup(placement: .navigationBarTrailing) { + ProfileToolbar(model: model) + } } - } - } - .navigationViewStyle(.stack) - .tag(0) - .tabItem { - Label("My Widgets", systemImage: "rectangle.3.group") + } + .navigationViewStyle(.stack) + .tag(0) + .tabItem { + Label("Home", systemImage: "rectangle.3.group").environment(\.symbolVariants, .none) + } } NavigationView { @@ -108,7 +110,7 @@ struct CampusApp: App { } .tag(2) .tabItem { - Label("Grades", systemImage: "checkmark.shield") + Label("Grades", systemImage: "checkmark.shield").environment(\.symbolVariants, .none) } .if(UIDevice.current.userInterfaceIdiom == .pad, transformT: { view in view.navigationViewStyle(.stack) diff --git a/Campus-iOS/Assets.xcassets/logo-white.imageset/Contents.json b/Campus-iOS/Assets.xcassets/logo-white.imageset/Contents.json index a4e18160..fa8517e3 100644 --- a/Campus-iOS/Assets.xcassets/logo-white.imageset/Contents.json +++ b/Campus-iOS/Assets.xcassets/logo-white.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "TUMLogo_oZ_Vollfl_negativ_RGB.png", + "filename" : "TUM2.png", "idiom" : "universal", "scale" : "1x" }, diff --git a/Campus-iOS/Assets.xcassets/logo-white.imageset/TUM2.png b/Campus-iOS/Assets.xcassets/logo-white.imageset/TUM2.png new file mode 100644 index 00000000..49c0e8d9 Binary files /dev/null and b/Campus-iOS/Assets.xcassets/logo-white.imageset/TUM2.png differ diff --git a/Campus-iOS/Assets.xcassets/logo-white.imageset/TUMLogo_oZ_Vollfl_negativ_RGB.png b/Campus-iOS/Assets.xcassets/logo-white.imageset/TUMLogo_oZ_Vollfl_negativ_RGB.png deleted file mode 100644 index 34d87e42..00000000 Binary files a/Campus-iOS/Assets.xcassets/logo-white.imageset/TUMLogo_oZ_Vollfl_negativ_RGB.png and /dev/null differ diff --git a/Campus-iOS/Assets.xcassets/set-permissions.imageset/Contents.json b/Campus-iOS/Assets.xcassets/set-permissions.imageset/Contents.json new file mode 100644 index 00000000..4c7dc2d4 --- /dev/null +++ b/Campus-iOS/Assets.xcassets/set-permissions.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "set-permissions.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Campus-iOS/Assets.xcassets/set-permissions.imageset/set-permissions.png b/Campus-iOS/Assets.xcassets/set-permissions.imageset/set-permissions.png new file mode 100644 index 00000000..53edded9 Binary files /dev/null and b/Campus-iOS/Assets.xcassets/set-permissions.imageset/set-permissions.png differ diff --git a/Campus-iOS/Assets.xcassets/tower.imageset/image4-94.png b/Campus-iOS/Assets.xcassets/tower.imageset/image4-94.png index c79b3adb..257443f1 100644 Binary files a/Campus-iOS/Assets.xcassets/tower.imageset/image4-94.png and b/Campus-iOS/Assets.xcassets/tower.imageset/image4-94.png differ diff --git a/Campus-iOS/CalendarComponent/ViewModel/CalendarViewModel.swift b/Campus-iOS/CalendarComponent/ViewModel/CalendarViewModel.swift index 9f383763..8a7643f2 100644 --- a/Campus-iOS/CalendarComponent/ViewModel/CalendarViewModel.swift +++ b/Campus-iOS/CalendarComponent/ViewModel/CalendarViewModel.swift @@ -55,7 +55,8 @@ class CalendarViewModel: ObservableObject { var eventsByDate: [Date? : [CalendarEvent]] { let sortedEvents = events.sorted { $0.startDate ?? Date() < $1.startDate ?? Date() } - let dictionary = Dictionary(grouping: sortedEvents, by: { $0.startDate?.removeTimeStamp }) + let filteredEvents = sortedEvents.filter { Date() <= $0.startDate ?? Date() } + let dictionary = Dictionary(grouping: filteredEvents, by: { $0.startDate?.removeTimeStamp }) return dictionary } } diff --git a/Campus-iOS/CalendarComponent/Views/CalendarContentView.swift b/Campus-iOS/CalendarComponent/Views/CalendarContentView.swift index 45371818..54176ec3 100644 --- a/Campus-iOS/CalendarComponent/Views/CalendarContentView.swift +++ b/Campus-iOS/CalendarComponent/Views/CalendarContentView.swift @@ -71,7 +71,12 @@ struct CalendarContentView: View { } .toolbar { ToolbarItemGroup(placement: .navigationBarLeading) { - CalendarToolbar(viewModel: self.viewModel, selectedEventID: self.$selectedEventID, isTodayPressed: self.$isTodayPressed) + Button(action: { + self.isTodayPressed = true + selectedType = .day + }) { + Text("Today") + } } ToolbarItem(placement: .principal) { Picker("Calendar Type", selection: $selectedType) { @@ -88,6 +93,7 @@ struct CalendarContentView: View { } } } + .opacity(1.0) .pickerStyle(.segmented) .onAppear { UISegmentedControl.appearance().backgroundColor = UIColor.systemBlue.withAlphaComponent(0.2) diff --git a/Campus-iOS/CalendarComponent/Views/CalendarToolbar.swift b/Campus-iOS/CalendarComponent/Views/CalendarToolbar.swift deleted file mode 100644 index 73b6339b..00000000 --- a/Campus-iOS/CalendarComponent/Views/CalendarToolbar.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// CalendarToolbar.swift -// Campus-iOS -// -// Created by Milen Vitanov on 23.12.21. -// - -import SwiftUI - -struct CalendarToolbar: View { - @ObservedObject var viewModel: CalendarViewModel - - @Binding var selectedEventID: String? - @Binding var isTodayPressed: Bool - - var body: some View { - HStack() { - NavigationLink(destination: - VStack{ - GeometryReader { geo in - CalendarDisplayView(events: viewModel.events.map({ $0.kvkEvent }), type: .list, selectedEventID: self.$selectedEventID, frame: CalendarContentView.getSafeAreaFrame(geometry: geo), todayPressed: self.$isTodayPressed, calendarWeekDays: 7) - .navigationBarTitle(Text("Events")) - } - } - .edgesIgnoringSafeArea(.all) - .toolbar { - ToolbarItemGroup(placement: .navigationBarTrailing) { - ProfileToolbar(model: self.viewModel.model) - } - } - ) { - Label("", systemImage: "list.bullet") - } - - Spacer().frame(width: 15) - Button(action: { self.isTodayPressed = true }) { - Text("Today") - } - } - } -} - -struct CalendarToolbar_Previews: PreviewProvider { - - @State static var selectedEventId: String? = "test123" - @State static var todayPressed = false - static var model = MockModel() - - static var previews: some View { - CalendarToolbar(viewModel: CalendarViewModel(model: model), selectedEventID: $selectedEventId, isTodayPressed: $todayPressed) - } -} diff --git a/Campus-iOS/CalendarComponent/Views/CalendarWidgetView.swift b/Campus-iOS/CalendarComponent/Views/CalendarWidgetView.swift index dc2c6ef1..bddb41d3 100644 --- a/Campus-iOS/CalendarComponent/Views/CalendarWidgetView.swift +++ b/Campus-iOS/CalendarComponent/Views/CalendarWidgetView.swift @@ -28,7 +28,6 @@ struct CalendarWidgetView: View { var body: some View { // Show the events on the earliest date which is not in the past. let events = viewModel.eventsByDate - .filter { Date() <= $0.key ?? Date() } .min { $0.key ?? Date() < $1.key ?? Date() }?.value ?? [] WidgetFrameView( @@ -52,7 +51,10 @@ struct CalendarWidgetView: View { showDetails.toggle() } .sheet(isPresented: $showDetails) { - CalendarContentView(model: model, refresh: .constant(false)) + VStack { + Spacer().frame(height: 10) + CalendarContentView(model: model, refresh: .constant(false)) + } } .expandable(size: $size, initialSize: initialSize, scale: $scale) } diff --git a/Campus-iOS/Campus-iOS/Base.lproj/Localizable.strings b/Campus-iOS/Campus-iOS/Base.lproj/Localizable.strings index cdfeebd5..ed06fb86 100644 --- a/Campus-iOS/Campus-iOS/Base.lproj/Localizable.strings +++ b/Campus-iOS/Campus-iOS/Base.lproj/Localizable.strings @@ -81,7 +81,6 @@ // TokenConfirmationView "Back" = "Back"; "Log in on " = "Log in on "; -"TUMOnline" = "TUMOnline"; "Select **Token-Management**" = "Select **Token-Management**"; "Activate the newly created token and enable your desired permissions" = "Activate the newly created token and enable your desired permissions"; "Contact Support" = "Contact Support"; @@ -92,16 +91,16 @@ "Leaving now will invalidate the current token!" = "Leaving now will invalidate the current token!"; "Leave" = "Leave"; -"Open TUMOnline" = "Open TUMOnline"; -"Check Token" = "Check Token"; "Token inactive" = "Token inactive"; -"Please activate the latest token on TUMOnline" = "Please activate the latest token on TUMOnline"; "Token active" = "Token active"; // TokenPermissionsView -"You can change your permissions on TUMOnline" = "You can change your permissions on TUMOnline"; +"You can change your permissions in TUMOnline" = "You can change your permissions in TUMOnline"; "Identification (TUM ID and name)" = "Identification (TUM ID and name)"; -"Check Permissions" = "Check Permissions"; +"Permissions" = "Permissions"; +"You have not granted the following permissions: \n\n" = "You have not granted the following permissions: \n\n"; +"\nJust be aware that the app will not fully work without all permissions. You can change the permissions every time in TUMOnline." = "\nJust be aware that the app will not fully work without all permissions. You can change the permissions every time in TUMOnline."; +"Continue anyways" = "Continue anyways"; // ProfileView "Token Permissions" = "Token Permissions"; @@ -176,6 +175,7 @@ // Map "Search ..." = "Search ..."; +"Traffic" = "Traffic"; // Movies "No more movies this semester 😢\nGet excited for the next season!" = "No more movies this semester 😢\nGet excited for the next season!"; diff --git a/Campus-iOS/Campus-iOS/de.lproj/Localizable.strings b/Campus-iOS/Campus-iOS/de.lproj/Localizable.strings index d2bdb252..01d54482 100644 --- a/Campus-iOS/Campus-iOS/de.lproj/Localizable.strings +++ b/Campus-iOS/Campus-iOS/de.lproj/Localizable.strings @@ -75,7 +75,6 @@ // TokenConfirmationView "Back" = "Zurück"; "Log in on " = "Melde dich auf "; -"TUMOnline" = "TUMOnline an"; "Select **Token-Management**" = "Gehe auf **Token Verwaltung**"; "Activate the newly created token and enable your desired permissions" = "Aktiviere den Token und stelle die benötigten Rechte ein"; "Contact Support" = "Support kontaktieren"; @@ -86,16 +85,16 @@ "Leaving now will invalidate the current token!" = "Wenn du jetzt die Seite verlässt wird der aktuelle Token ungültig!"; "Leave" = "Verlassen"; -"Open TUMOnline" = "Öffne TUMOnline"; -"Check Token" = "Token überprüfen"; "Token inactive" = "Token inaktiv"; -"Please activate the latest token on TUMOnline" = "Bitte aktiviere den neusten Token auf TUMOnline"; "Token active" = "Token aktiv"; // TokenPermissionsView -"You can change your permissions on TUMOnline" = "Du kannst deine Rechte auf TUMOnline anpassen"; +"You can change your permissions in TUMOnline" = "Du kannst deine Rechte in TUMOnline anpassen"; "Identification (TUM ID and name)" = "Identifikation (TUM ID und Name)"; -"Check Permissions" = "Rechte überprüfen"; +"Permissions" = "Rechte"; +"You have not granted the following permissions: \n\n" = "Du hast nicht alle Rechte freigegeben: \n\n"; +"\nJust be aware that the app will not fully work without all permissions. You can change the permissions every time in TUMOnline." = "\nDadurch können nicht alle Features der App genutzt werden. Du kannst die Rechte jederzeit auf TUMOnline anpassen."; +"Continue anyways" = "Trotzdem weiter"; // ProfileView "Token Permissions" = "Token Rechte"; @@ -170,6 +169,7 @@ // Map "Search ..." = "Suchen ..."; +"Traffic" = "Auslastung"; // Movies "No more movies this semester 😢\nGet excited for the next season!" = "Keine Filme mehr dieses Semester 😢\nMacht euch ready für die nächste Saison!"; diff --git a/Campus-iOS/GradesComponent/Views/GradeView.swift b/Campus-iOS/GradesComponent/Views/GradeView.swift index 384cbeca..0d4784ab 100644 --- a/Campus-iOS/GradesComponent/Views/GradeView.swift +++ b/Campus-iOS/GradesComponent/Views/GradeView.swift @@ -36,22 +36,22 @@ struct GradeView: View { VStack(alignment: .leading, spacing: 8) { HStack(spacing: 16) { HStack { - Image(systemName: "pencil.circle.fill") + Image(systemName: "pencil.circle") .frame(width: 12, height: 12) .foregroundColor(Color("tumBlue")) Text(grade.modusShort) - .font(.system(size: 12)) + .font(.footnote) .foregroundColor(colorScheme == .dark ? .init(UIColor.lightGray) : .init(UIColor.darkGray)) Spacer() } .frame(minWidth: 0, maxWidth: .infinity) HStack { - Image(systemName: "number.circle.fill") + Image(systemName: "number.circle") .frame(width: 12, height: 12) .foregroundColor(Color("tumBlue")) Text(grade.lvNumber) - .font(.system(size: 12)) + .font(.footnote) .foregroundColor(colorScheme == .dark ? .init(UIColor.lightGray) : .init(UIColor.darkGray)) Spacer() } @@ -59,11 +59,11 @@ struct GradeView: View { }.foregroundColor(.init(.darkGray)) HStack { - Image(systemName: "person.circle.fill") + Image(systemName: "person.circle") .frame(width: 12, height: 12) .foregroundColor(Color("tumBlue")) Text(grade.examiner) - .font(.system(size: 12)) + .font(.footnote) .foregroundColor(colorScheme == .dark ? .init(UIColor.lightGray) : .init(UIColor.darkGray)) .fixedSize(horizontal: false, vertical: true) }.foregroundColor(.init(.darkGray)) diff --git a/Campus-iOS/GradesComponent/Views/GradesView.swift b/Campus-iOS/GradesComponent/Views/GradesView.swift index 11ea6c39..6826efcc 100644 --- a/Campus-iOS/GradesComponent/Views/GradesView.swift +++ b/Campus-iOS/GradesComponent/Views/GradesView.swift @@ -32,12 +32,9 @@ struct GradesView: View { } ForEach(self.vm.gradesByDegreeAndSemester[index].1, id: \.0) { gradesBySemester in - Section( - header: - GroupBoxLabelView( - iconName: "graduationcap.fill", - text: gradesBySemester.0 - ) + Section(header: Text(gradesBySemester.0) + .font(.headline.bold()) + .foregroundColor(Color("tumBlue")) ) { ForEach(gradesBySemester.1) { item in VStack { diff --git a/Campus-iOS/LectureComponent/Views/LectureDetailsViews/LectureDetailsBasicInfoView.swift b/Campus-iOS/LectureComponent/Views/LectureDetailsViews/LectureDetailsBasicInfoView.swift index e938d072..b99a17cc 100644 --- a/Campus-iOS/LectureComponent/Views/LectureDetailsViews/LectureDetailsBasicInfoView.swift +++ b/Campus-iOS/LectureComponent/Views/LectureDetailsViews/LectureDetailsBasicInfoView.swift @@ -62,12 +62,16 @@ struct LectureDetailsBasicInfoView: View { text: lectureDetails.organisation ) Divider() - LectureDetailsBasicInfoRowView( - iconName: "person.fill", - text: lectureDetails.speaker - ) - .onTapGesture { - self.showActionSheet = true + HStack { + LectureDetailsBasicInfoRowView( + iconName: "person.fill", + text: lectureDetails.speaker + ) + Spacer() + Button( + action: { self.showActionSheet = true }, + label: { Image(systemName: "magnifyingglass").foregroundColor(.blue) } + ) } if let firstMeeting = lectureDetails.firstScheduledDate { Divider() diff --git a/Campus-iOS/LectureComponent/Views/LectureView.swift b/Campus-iOS/LectureComponent/Views/LectureView.swift index 02db111a..9359ee02 100644 --- a/Campus-iOS/LectureComponent/Views/LectureView.swift +++ b/Campus-iOS/LectureComponent/Views/LectureView.swift @@ -15,26 +15,25 @@ struct LectureView: View { VStack(alignment: .leading, spacing: 8) { Text(lecture.title) .bold() - .font(.title3) HStack(spacing: 16) { HStack { - Image(systemName: "pencil.circle.fill") + Image(systemName: "pencil.circle") .frame(width: 12, height: 12) .foregroundColor(Color("tumBlue")) Text(lecture.eventType) - .font(.subheadline) + .font(.footnote) .foregroundColor(colorScheme == .dark ? .init(UIColor.lightGray) : .init(UIColor.darkGray)) Spacer() } .frame(minWidth: 0, maxWidth: .infinity) HStack { - Image(systemName: "clock.fill") + Image(systemName: "clock") .frame(width: 12, height: 12) .foregroundColor(Color("tumBlue")) Text(lecture.duration + " SWS") - .font(.subheadline) + .font(.footnote) .foregroundColor(colorScheme == .dark ? .init(UIColor.lightGray) : .init(UIColor.darkGray)) Spacer() } @@ -42,11 +41,11 @@ struct LectureView: View { }.foregroundColor(.init(.darkGray)) HStack { - Image(systemName: "person.circle.fill") + Image(systemName: "person.circle") .frame(width: 12, height: 12) .foregroundColor(Color("tumBlue")) Text(lecture.speaker) - .font(.subheadline) + .font(.footnote) .foregroundColor(colorScheme == .dark ? .init(UIColor.lightGray) : .init(UIColor.darkGray)) .fixedSize(horizontal: false, vertical: true) }.foregroundColor(.init(.darkGray)) diff --git a/Campus-iOS/LectureComponent/Views/LecturesView.swift b/Campus-iOS/LectureComponent/Views/LecturesView.swift index e33d1ab0..b5433da2 100644 --- a/Campus-iOS/LectureComponent/Views/LecturesView.swift +++ b/Campus-iOS/LectureComponent/Views/LecturesView.swift @@ -29,11 +29,9 @@ struct LecturesView: View { var body: some View { List { ForEach(lecturesBySemesterSearchResult, id: \.0) { lecturesBySemester in - Section( - header: GroupBoxLabelView( - iconName: "graduationcap.fill", - text: lecturesBySemester.0 - ) + Section(header: Text(lecturesBySemester.0) + .font(.headline.bold()) + .foregroundColor(Color("tumBlue")) ) { ForEach(lecturesBySemester.1) { item in VStack { diff --git a/Campus-iOS/LoginComponent/ViewModel/TokenPermissionsViewModel+PermissionType.swift b/Campus-iOS/LoginComponent/ViewModel/TokenPermissionsViewModel+PermissionType.swift index 8423ed5e..67f97fa0 100644 --- a/Campus-iOS/LoginComponent/ViewModel/TokenPermissionsViewModel+PermissionType.swift +++ b/Campus-iOS/LoginComponent/ViewModel/TokenPermissionsViewModel+PermissionType.swift @@ -8,11 +8,11 @@ import Foundation extension TokenPermissionsViewModel { - enum PermissionType { - case grades - case calendar - case lectures - case tuitionFees - case identification + enum PermissionType: String { + case grades = "Grades" + case calendar = "Calendar" + case lectures = "Lectures" + case tuitionFees = "Tuition Fees" + case identification = "Identification (TUM ID and name)" } } diff --git a/Campus-iOS/LoginComponent/Views/LoginView.swift b/Campus-iOS/LoginComponent/Views/LoginView.swift index 0ed68447..f171a86d 100644 --- a/Campus-iOS/LoginComponent/Views/LoginView.swift +++ b/Campus-iOS/LoginComponent/Views/LoginView.swift @@ -18,6 +18,8 @@ struct LoginView: View { @ObservedObject var viewModel: LoginViewModel @FocusState private var focusedField: Field? + @State var isActive = true + @State var logInState: LoginViewModel.LoginState = .notChecked @State var showLoginAlert: Bool = false @State var buttonBackgroundColor: Color = .tumBlue @@ -108,7 +110,7 @@ struct LoginView: View { switch result { case .success: withAnimation() { - buttonBackgroundColor = .green + buttonBackgroundColor = .blue logInState = .loggedIn } print("Log in Successfull") @@ -148,20 +150,12 @@ struct LoginView: View { } } case .loggedIn: - HStack { - Image(systemName: "checkmark.circle.fill") - Text("Log in Successfull") - } - .lineLimit(1) - .font(.title3) - .frame(alignment: .center) - .onAppear() { - Task { - try? await Task.sleep(nanoseconds: 1_500_000_000) - withAnimation() { - showLoginButton = false - } - } + NavigationLink(destination: + TokenConfirmationView(viewModel: self.viewModel).navigationBarTitle(Text("Check Token")), isActive: $isActive) { + Text("Log in 🎓") + .lineLimit(1) + .font(.title3) + .frame(alignment: .center) } } @@ -175,21 +169,6 @@ struct LoginView: View { .cornerRadius(10) .buttonStyle(.plain) } - - - if !showLoginButton { - NavigationLink(destination: - TokenConfirmationView(viewModel: self.viewModel).navigationBarTitle(Text("Check Token"))) { - Text("Next") - .font(.body) - .frame(width: 200, height: 48, alignment: .center) - .foregroundColor(.white) - .background(.green) - .cornerRadius(10) - .buttonStyle(.plain) - } - } - Spacer().frame(height: 20) diff --git a/Campus-iOS/LoginComponent/Views/TokenConfirmationView.swift b/Campus-iOS/LoginComponent/Views/TokenConfirmationView.swift index 0dab18ba..23aa8da3 100644 --- a/Campus-iOS/LoginComponent/Views/TokenConfirmationView.swift +++ b/Campus-iOS/LoginComponent/Views/TokenConfirmationView.swift @@ -21,6 +21,8 @@ struct TokenConfirmationView: View { @State var showCheckTokenButton: Bool = true @State var showTUMOnline = false @State var currentStep: Int = 1 + @State var isActive = true + /// The `LoginViewModel` that manages the content of the login screen @ObservedObject var viewModel: LoginViewModel @@ -102,102 +104,101 @@ struct TokenConfirmationView: View { .shadow(radius: 10) // Video is 2532 x 1170 .frame(width: screenWidth*0.109*5, height: screenWidth*0.185*5, alignment: .center) + VStack { Spacer() - Button { - self.showTUMOnline = true - } label: { - HStack { - Image(systemName: "globe") - Text("Open TUMOnline") + HStack { + Spacer() + Button { + self.showTUMOnline = true + } label: { + HStack { + Image(systemName: "globe") + Text("TUMOnline") + } + .lineLimit(1) + .font(.system(size: 15, weight: .bold)) + .frame(width: 150, height: 48, alignment: .center) } - .lineLimit(1).font(.body) - .frame(width: 200, height: 48, alignment: .center) - } - .font(.title) - .foregroundColor(.white) - .background(Color(.tumBlue)) - .cornerRadius(10) - - Spacer() - - if !tokenPermissionButton { - Button(action: { - self.viewModel.checkAuthorization() { result in - switch result { - case .success: - withAnimation { - tokenState = .active - buttonBackgroundColor = .green - showTokenHelp = false - } - case .failure(_): - withAnimation { - tokenState = .inactive - buttonBackgroundColor = .red - showTokenHelp = true + .foregroundColor(.white) + .background(Color(.tumBlue)) + .cornerRadius(10) + .padding() + + Spacer() + + if !tokenPermissionButton { + Button(action: { + self.viewModel.checkAuthorization() { result in + switch result { + case .success: + withAnimation { + tokenState = .active + buttonBackgroundColor = .green + showTokenHelp = false + } + case .failure(_): + withAnimation { + tokenState = .inactive + buttonBackgroundColor = .red + showTokenHelp = true + } } } - } - }) { - switch tokenState { - case .notChecked: - Text("Check Token").lineLimit(1).font(.body) - case .inactive: - VStack { + }) { + switch tokenState { + case .notChecked: HStack { - Image(systemName: "x.circle.fill") - Text("Token inactive").lineLimit(1).font(.body) + Text("Check Token") + .lineLimit(1) + .font(.system(size: 15, weight: .bold)) + Image(systemName: "arrow.right") } - } - .padding() - .onAppear() { - Task { - try? await Task.sleep(nanoseconds: 1_500_000_000) - withAnimation(.easeInOut) { - tokenState = .notChecked - buttonBackgroundColor = .tumBlue + + case .inactive: + VStack { + HStack { + Text("Token inactive") + .lineLimit(1) + .font(.system(size: 14, weight: .bold)) } } - } - case .active: - HStack { - Image(systemName: "checkmark.circle.fill") - Text("Token active").lineLimit(1).font(.body) - } - .padding() - .onAppear() { - Task { - try? await Task.sleep(nanoseconds: 3_000_000_000) - withAnimation() { - tokenPermissionButton = true -// showCheckTokenButton = false + .padding() + .onAppear() { + Task { + try? await Task.sleep(nanoseconds: 1_500_000_000) + withAnimation(.easeInOut) { + tokenState = .notChecked + buttonBackgroundColor = .tumBlue + } + } + } + case .active: + if let model = self.viewModel.model { + NavigationLink(destination: TokenPermissionsView(viewModel: TokenPermissionsViewModel(model: model)).navigationTitle("Check Permissions"), isActive: $isActive) { + Text("Next") + .lineLimit(1) + .font(.body) + .frame(width: 200, height: 48, alignment: .center) + .foregroundColor(.white) + .background(.green) + .cornerRadius(10) + .buttonStyle(.plain) } } } } + .frame(width: 150, height: 48, alignment: .center) + .font(.system(size: 14, weight: .bold)) + .foregroundColor(.white) + .background(buttonBackgroundColor) + .cornerRadius(10) + .buttonStyle(.plain) + .padding() } - .frame(width: 200, height: 48, alignment: .center) - .font(.title) - .foregroundColor(.white) - .background(buttonBackgroundColor) - .cornerRadius(10) - .buttonStyle(.plain) - } - - if tokenPermissionButton, let model = self.viewModel.model { - NavigationLink(destination: TokenPermissionsView(viewModel: TokenPermissionsViewModel(model: model)).navigationTitle("Check Permissions")) { - Text("Next") - .lineLimit(1) - .font(.body) - .frame(width: 200, height: 48, alignment: .center) - .foregroundColor(.white) - .background(.green) - .cornerRadius(10) - .buttonStyle(.plain) - } + Spacer() } if showTokenHelp { @@ -226,9 +227,10 @@ struct TokenConfirmationView: View { self.showBackButtonAlert = true }) { HStack { - Image(systemName: "arrow.left") + Image(systemName: "chevron.left") Text("Back") - }.foregroundColor(Color(.tumBlue)) + } + .foregroundColor(Color(.tumBlue)) } ) .alert(isPresented: $showBackButtonAlert) { diff --git a/Campus-iOS/LoginComponent/Views/TokenPermissionsView.swift b/Campus-iOS/LoginComponent/Views/TokenPermissionsView.swift index 478b7f87..7fc1f9d7 100644 --- a/Campus-iOS/LoginComponent/Views/TokenPermissionsView.swift +++ b/Campus-iOS/LoginComponent/Views/TokenPermissionsView.swift @@ -13,108 +13,115 @@ struct TokenPermissionsView: View { @Environment(\.dismiss) var dismiss @State var doneButton = false @State var showTUMOnline = false + @State var notAllPermissionsGranted = false + @State var permissionsWarning = "" + @State var showHelp = true var dismissWhenDone: Bool = false + let permissionTypes: [TokenPermissionsViewModel.PermissionType] = [.calendar, .lectures, .grades, .tuitionFees, .identification] + var body: some View { - VStack() { - Text("You can change your permissions on TUMOnline") - .foregroundColor(.tumBlue) + VStack(alignment: .center) { HStack { - VStack(alignment: .leading) { - Text("Calendar").padding() - Text("Lectures").padding() - Text("Grades").padding() - Text("Tuition fees").padding() - Text("Identification (TUM ID and name)").padding() - } - Spacer() - VStack { - if let currentState = viewModel.states[.calendar] { - check(state: currentState).padding() - } else { - Image(systemName: "questionmark.circle.fill").foregroundColor(.gray).padding() - } - - if let currentState = viewModel.states[.lectures] { - check(state: currentState).padding() - } else { - Image(systemName: "questionmark.circle.fill").foregroundColor(.gray).padding() - } - - if let currentState = viewModel.states[.grades] { - check(state: currentState).padding() - } else { - Image(systemName: "questionmark.circle.fill").foregroundColor(.gray).padding() - } - - if let currentState = viewModel.states[.tuitionFees] { - check(state: currentState).padding() - } else { - Image(systemName: "questionmark.circle.fill").foregroundColor(.gray).padding() - } - - if let currentState = viewModel.states[.identification] { - check(state: currentState).padding() - } else { - Image(systemName: "questionmark.circle.fill").foregroundColor(.gray).padding() - } - } + Spacer(minLength: 10) + Text("You can change your permissions in TUMOnline") + .foregroundColor(.blue) + .lineLimit(2) + .multilineTextAlignment(.center) + .font(.title2) + Spacer(minLength: 10) } - .font(.system(size: 20)) - .padding() - VStack (){ - Button { - self.showTUMOnline = true - } label: { + VStack(spacing: 0) { + ForEach(permissionTypes, id: \.self) { permissionType in HStack { - Image(systemName: "globe") - Text("Open TUMOnline") + Text(permissionType.rawValue) + Spacer() + if let currentState = viewModel.states[permissionType] { + check(state: currentState).padding() + } else { + Image(systemName: "questionmark.circle.fill").foregroundColor(.gray).padding() + } } - .lineLimit(1).font(.body) - .frame(width: 200, height: 48, alignment: .center) } - .font(.title) - .foregroundColor(.white) - .background(Color(.tumBlue)) - .cornerRadius(10) - - Button { - Task { - await viewModel.checkPermissionFor(types: [.grades, .lectures, .calendar, .identification, .tuitionFees]) - - withAnimation() { - doneButton = true + } + .padding() + + VStack { + HStack (){ + Button { + self.showTUMOnline = true + self.doneButton = false + } label: { + HStack { + Image(systemName: "globe") + Text("TUMOnline") } + .lineLimit(1) + .font(.system(size: 15, weight: .bold)) + .frame(width: 150, height: 48, alignment: .center) } - } label: { - Text("Check Permissions") + .foregroundColor(.white) + .background(Color(.tumBlue)) + .cornerRadius(10) + .padding() + + Spacer() + + Button { + Task { + await viewModel.checkPermissionFor(types: [.grades, .lectures, .calendar, .identification, .tuitionFees]) + + withAnimation() { + doneButton = true + } + } + } label: { + HStack { + Text("Permissions").font(.system(size: 15, weight: .bold)) + Image(systemName: "questionmark.circle").font(.system(size: 15, weight: .bold)) + } .lineLimit(1) - .font(.body) - .frame(width: 200, height: 48, alignment: .center) + .frame(width: 150, height: 48, alignment: .center) .foregroundColor(.white) .background(Color(.tumBlue)) .cornerRadius(10) .buttonStyle(.plain) + } + .padding() } if doneButton { + // Insert the warning string and switch bool + Button { - if dismissWhenDone { - // Dismiss when view is opened from Profile/Settings. - dismiss() + if allPermissionsAreGranted() { + if dismissWhenDone { + // Dismiss when view is opened from Profile/Settings. + dismiss() + } else { + // Used when shown via the login process sheet. + self.viewModel.model.isLoginSheetPresented = false + } } else { - // Used when shown via the login process sheet. - self.viewModel.model.isLoginSheetPresented = false + // Not all permissions were granted + + permissionsWarning = "You have not granted the following permissions: \n\n" + for permission in notGrantedPermissions() { + self.permissionsWarning.append("\(permission.rawValue)\n ") + } + permissionsWarning.append("\nJust be aware that the app will not fully work without all permissions. You can change the permissions every time in TUMOnline.") + + notAllPermissionsGranted = true } } label: { Text("Done") .lineLimit(1) - .font(.body) + .font(.system(size: 17, weight: .bold)) .frame(width: 200, height: 48, alignment: .center) .foregroundColor(.white) - .background(.green) + .background(allPermissionsAreGranted() ? .green : .tumBlue) .cornerRadius(10) .buttonStyle(.plain) } @@ -125,6 +132,43 @@ struct TokenPermissionsView: View { .sheet(isPresented: $showTUMOnline) { SFSafariViewWrapper(url: Constants.tokenManagementTUMOnlineUrl).edgesIgnoringSafeArea(.bottom) } + .alert(isPresented: $notAllPermissionsGranted) { + Alert(title: Text("Permissions Warning"), message: Text(permissionsWarning), primaryButton: Alert.Button.cancel(), secondaryButton: Alert.Button.destructive(Text("Continue anyways"), action: { + if dismissWhenDone { + // Dismiss when view is opened from Profile/Settings. + dismiss() + } else { + // Used when shown via the login process sheet. + self.viewModel.model.isLoginSheetPresented = false + } + })) + } + .task { + await viewModel.checkPermissionFor(types: [.grades, .lectures, .calendar, .identification, .tuitionFees]) + + withAnimation() { + doneButton = true + } + } + } + + func notGrantedPermissions() -> [TokenPermissionsViewModel.PermissionType] { + return permissionTypes.filter { permissionType in + if case .success = viewModel.states[permissionType] { + return false + } else { + return true + } + } + } + + func allPermissionsAreGranted() -> Bool { + for permissionType in permissionTypes { + if case .success = viewModel.states[permissionType] {} else { + return false + } + } + return true } @ViewBuilder diff --git a/Campus-iOS/MapComponent/View/Cafeterias/CafeteriaRowView.swift b/Campus-iOS/MapComponent/View/Cafeterias/CafeteriaRowView.swift index 8d55eb02..2a1e6ec4 100644 --- a/Campus-iOS/MapComponent/View/Cafeterias/CafeteriaRowView.swift +++ b/Campus-iOS/MapComponent/View/Cafeterias/CafeteriaRowView.swift @@ -31,7 +31,7 @@ struct CafeteriaRowView: View { .font(.title3) Spacer() if let queue = cafeteria.queue { - let explainText = explainStatus ? "Auslastung " : "" + let explainText = explainStatus ? "Traffic " : "" Text("\(explainText)\(Int(queue.percent))%") .font(.footnote) .onTapGesture { diff --git a/Campus-iOS/MapComponent/View/MapContentView.swift b/Campus-iOS/MapComponent/View/MapContentView.swift index 09a0e8cd..3ade4103 100644 --- a/Campus-iOS/MapComponent/View/MapContentView.swift +++ b/Campus-iOS/MapComponent/View/MapContentView.swift @@ -39,7 +39,6 @@ struct MapContentView: UIViewRepresentable { handleCafeterias() mapView.showsUserLocation = true - self.locationManager.requestAlwaysAuthorization() self.locationManager.requestWhenInUseAuthorization() return mapView diff --git a/Campus-iOS/MapComponent/View/StudyRooms/StudyRoomGroupView.swift b/Campus-iOS/MapComponent/View/StudyRooms/StudyRoomGroupView.swift index 041602b1..9b179512 100644 --- a/Campus-iOS/MapComponent/View/StudyRooms/StudyRoomGroupView.swift +++ b/Campus-iOS/MapComponent/View/StudyRooms/StudyRoomGroupView.swift @@ -50,6 +50,7 @@ struct StudyRoomGroupView: View { VStack(spacing: 1) { HStack{ VStack(alignment: .leading){ + Spacer().frame(height: 10).gesture(panelDragGesture) Text(group.name ?? "") .bold() .font(.title3) diff --git a/Campus-iOS/MapComponent/View/StudyRooms/StudyRoomWidgetView.swift b/Campus-iOS/MapComponent/View/StudyRooms/StudyRoomWidgetView.swift index 6669bdd1..879bda65 100644 --- a/Campus-iOS/MapComponent/View/StudyRooms/StudyRoomWidgetView.swift +++ b/Campus-iOS/MapComponent/View/StudyRooms/StudyRoomWidgetView.swift @@ -173,7 +173,7 @@ struct StudyRoomWidgetHeaderView: View { VStack(alignment: .leading) { HStack { - Image(systemName: "house") + Image(systemName: "book") Text(studyGroup) .bold() .lineLimit(2) diff --git a/Campus-iOS/MoviesComponent/Views/MovieCard.swift b/Campus-iOS/MoviesComponent/Views/MovieCard.swift index 747cc4bf..bb352d45 100644 --- a/Campus-iOS/MoviesComponent/Views/MovieCard.swift +++ b/Campus-iOS/MoviesComponent/Views/MovieCard.swift @@ -19,11 +19,12 @@ struct MovieCard: View { AsyncImage(url: link) { image in switch image { case .empty: - ProgressView() + ProgressView().padding(UIScreen.main.bounds.width/2) case .success(let image): image .resizable() - .frame(width: 390 * 0.425, height: 390 * 0.525) + .aspectRatio(contentMode: .fill) + .frame(width: 390 * 0.425, height: 390 * 0.6) .clipped() case .failure: Image("movie") @@ -47,20 +48,22 @@ struct MovieCard: View { } // Stack bottom half of card - VStack(alignment: .leading, spacing: 6) { + VStack(alignment: .leading, spacing: 2) { Text(self.movie.title ?? "") .fontWeight(Font.Weight.heavy) .font(.subheadline).foregroundColor(colorScheme == .dark ? .init(UIColor.white) : .init(UIColor.black)) .fixedSize(horizontal: false, vertical: true) - .lineLimit(2) + .lineLimit(1) Text(self.movie.date ?? Date(), style: .date) .font(Font.custom("HelveticaNeue-Bold", size: 12)) .foregroundColor(Color.gray) + Spacer() } - .padding(12) + .padding(EdgeInsets(top: 15, leading: 10, bottom: 10, trailing: 5)) + .frame(height: 55) } - .frame(width: 390 * 0.425, height: 390 * 0.7) + .frame(width: 390 * 0.425, height: 390 * 0.73) .background(Color(.systemGray5)) .cornerRadius(15) .shadow(color: Color.black.opacity(0.2), radius: 7, x: 0, y: 2) diff --git a/Campus-iOS/MoviesComponent/Views/MoviesView.swift b/Campus-iOS/MoviesComponent/Views/MoviesView.swift index 707a5f22..0e20d911 100644 --- a/Campus-iOS/MoviesComponent/Views/MoviesView.swift +++ b/Campus-iOS/MoviesComponent/Views/MoviesView.swift @@ -21,9 +21,9 @@ struct MoviesView: View { Text("No more movies this semester 😢\nGet excited for the next season!") .foregroundColor(Color(UIColor.lightGray)) ScrollView(.vertical) { - LazyVGrid(columns: items, spacing: 6) { + LazyVGrid(columns: items, spacing: 10) { ForEach(self.viewModel.movies, id: \.id ) { movie in - MovieCard(movie: movie).padding(10) + MovieCard(movie: movie).padding(7) .onTapGesture { selectedMovie = movie } @@ -32,7 +32,7 @@ struct MoviesView: View { MovieDetailedView(movie: movie) } } - .padding(15) + .padding(10) .background(Color.systemsBackground) } } diff --git a/Campus-iOS/ProfileComponent/View/ProfileView.swift b/Campus-iOS/ProfileComponent/View/ProfileView.swift index 40eba78f..dbdc579e 100644 --- a/Campus-iOS/ProfileComponent/View/ProfileView.swift +++ b/Campus-iOS/ProfileComponent/View/ProfileView.swift @@ -13,6 +13,8 @@ struct ProfileView: View { @AppStorage("useBuildInWebView") var useBuildInWebView: Bool = true @AppStorage("calendarWeekDays") var calendarWeekDays: Int = 7 @Environment(\.colorScheme) var colorScheme + @State var isWebViewShowed = false + @State var selectedLink: URL? = nil var body: some View { @@ -110,11 +112,25 @@ struct ProfileView: View { } Section("GET IN CONTACT") { - Link(LocalizedStringKey("Join Beta"), destination: URL(string: "https://testflight.apple.com/join/4Ddi6f2f")!) - - Link(LocalizedStringKey("TUM Dev on Github"), destination: URL(string: "https://github.com/TUM-Dev")!) - - Link("TUM Dev Website", destination: URL(string: "https://tum.app")!) + if self.useBuildInWebView { + Button("Join Beta") { + self.selectedLink = URL(string: "https://testflight.apple.com/join/4Ddi6f2f") + } + + Button("TUM Dev on Github") { + self.selectedLink = URL(string: "https://github.com/TUM-Dev") + } + + Button("TUM Dev Website") { + self.selectedLink = URL(string: "https://tum.app") + } + } else { + Link(LocalizedStringKey("Join Beta"), destination: URL(string: "https://testflight.apple.com/join/4Ddi6f2f")!) + + Link(LocalizedStringKey("TUM Dev on Github"), destination: URL(string: "https://github.com/TUM-Dev")!) + + Link("TUM Dev Website", destination: URL(string: "https://tum.app")!) + } Button("Feedback") { let mailToString = "mailto:app@tum.de?subject=[IOS]&body=Hello I have an issue...".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) @@ -156,7 +172,7 @@ struct ProfileView: View { }) { HStack { Spacer() - Text("Version 4.0").foregroundColor(colorScheme == .dark ? .init(UIColor.lightGray) : .init(UIColor.darkGray)) + Text("Version 4.1").foregroundColor(colorScheme == .dark ? .init(UIColor.lightGray) : .init(UIColor.darkGray)) Spacer() } } @@ -186,6 +202,11 @@ struct ProfileView: View { Text("Done").bold() } } + .sheet(item: $selectedLink) { selectedLink in + if let link = selectedLink { + SFSafariViewWrapper(url: link) + } + } } } } diff --git a/Campus-iOS/WidgetComponent/Screen/WidgetScreen.swift b/Campus-iOS/WidgetComponent/Screen/WidgetScreen.swift index 7d38c7b0..9ded3a0c 100644 --- a/Campus-iOS/WidgetComponent/Screen/WidgetScreen.swift +++ b/Campus-iOS/WidgetComponent/Screen/WidgetScreen.swift @@ -11,7 +11,9 @@ import MapKit struct WidgetScreen: View { @StateObject private var recommender: WidgetRecommender + @StateObject var model: Model = Model() @State private var refresh = false + @State private var widgetTitle = String() private let timer = Timer.publish(every: 60, on: .main, in: .common).autoconnect() init(model: Model) { @@ -39,6 +41,8 @@ struct WidgetScreen: View { } .task { try? await recommender.fetchRecommendations() + if let firstName = model.profile.profile?.firstname { widgetTitle = "Hi, " + firstName } + else { widgetTitle = "Welcome"} } .onReceive(timer) { _ in refresh.toggle() @@ -53,6 +57,9 @@ struct WidgetScreen: View { var previousHeight = CGFloat.zero let maxWidth = WidgetSize.bigSquare.dimensions.0 + 2 * WidgetSize.padding + if let firstName = model.profile.profile?.firstname { widgetTitle = "Hi, " + firstName } + else { widgetTitle = "Welcome"} + return ZStack(alignment: .topLeading) { ForEach(0..