From 11c6e270d7dcda4b0a9707238ca7f22866414698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20K=C3=B6rber?= <56073945+jakobkoerber@users.noreply.github.com> Date: Sun, 21 Apr 2024 22:43:37 +0200 Subject: [PATCH] Improve Routing, Feedback, Settings & Calendar (#246) --- android/app/build.gradle | 10 +- android/app/proguard-rules.pro | 42 ----- .../de/tum/in/tumcampus/MainActivity.kt | 2 +- .../in/tumcampus/util/DateTimeSerializer.kt | 45 +++--- .../de/tum/in/tumcampus/util/DateTimeUtils.kt | 150 +++--------------- .../widgets/calendar/CalendarEventType.kt | 8 - .../widgets/calendar/CalendarWidget.kt | 56 ++++--- .../widgets/calendar/CalendarWidgetService.kt | 61 ++++--- .../widgets/calendar/WidgetCalendarItem.kt | 48 ++---- .../app/src/main/res/values-de/strings.xml | 24 ++- android/app/src/main/res/values/strings.xml | 24 ++- android/settings.gradle | 1 + ios/CalendarWidget/CalendarEntry.swift | 32 +--- ios/CalendarWidget/CalendarEventView.swift | 16 +- ios/Podfile.lock | 40 ++--- lib/base/enums/calendar_event_type.dart | 6 - .../campus_exception_router.dart | 9 +- .../errorHandling/default_error_router.dart | 10 +- .../errorHandling/dio_exception_router.dart | 40 +++-- .../errorHandling/error_handling_router.dart | 11 +- .../errorHandling/error_handling_view.dart | 68 ++++---- lib/base/errorHandling/grpc_error_router.dart | 9 +- .../search_exception_router.dart | 9 +- .../tum_online_api_exception_router.dart | 12 +- lib/base/errorHandling/type_error_router.dart | 10 +- lib/base/localization/app_de.arb | 45 ++++-- lib/base/localization/app_en.arb | 49 ++++-- .../tum_online_api_exception.dart | 66 +++++--- .../networking/protocols/api_exception.dart | 11 +- lib/base/routing/router.dart | 10 +- ...ctions_launcher.dart => map_launcher.dart} | 7 +- .../model/calendar_data_source.dart | 2 +- .../model/calendar_event.dart | 29 +--- .../viewModels/calendar_viewmodel.dart | 1 + .../views/calendars_view.dart | 18 ++- .../views/custom_event_view.dart | 2 +- .../views/event_creation_form_field.dart | 2 + .../views/event_creation_view.dart | 3 +- .../calendar_widget_event_view.dart | 2 +- .../homeWidget/calendar_widget_view.dart | 2 +- .../views/departures_details_view.dart | 4 +- .../homeWidget/departures_widget_view.dart | 2 +- .../viewModels/feedback_viewmodel.dart | 25 ++- .../views/feedback_form_view.dart | 22 +++ lib/gradeComponent/views/grades_view.dart | 2 +- .../views/unauthorized_view.dart | 34 ++-- .../views/lecture_details_view.dart | 12 +- lib/lectureComponent/views/lecture_view.dart | 2 +- lib/lectureComponent/views/lectures_view.dart | 2 +- lib/main.dart | 10 +- .../views/homeWidget/movies_widget_view.dart | 2 +- .../model/navigatum_navigation_details.dart | 3 + .../views/navigatum_room_view.dart | 77 ++++++--- lib/navigation_service.dart | 2 +- .../views/homeWidget/news_widget_view.dart | 2 +- .../viewModels/onboarding_viewmodel.dart | 3 +- .../views/confirm_view.dart | 2 +- lib/onboardingComponent/views/login_view.dart | 5 +- .../views/cafeterias/cafeteria_view.dart | 8 +- .../views/cafeterias/cafeterias_view.dart | 2 +- .../views/directions_button.dart | 54 ------- .../homeWidget/cafeteria_widget_view.dart | 5 +- lib/placesComponent/views/places_screen.dart | 2 +- .../study_room_group_scaffold.dart | 24 ++- .../studyGroups/study_room_group_view.dart | 21 +-- .../views/studyGroups/study_rooms_view.dart | 2 +- .../calendar_search_result_view.dart | 2 +- .../appWideSearch/search_textfield_view.dart | 1 + .../views/personRoomSearch/search_view.dart | 3 +- .../views/settings_view.dart | 11 +- .../views/student_card_view.dart | 2 +- pubspec.lock | 135 ++++++++-------- pubspec.yaml | 9 +- 73 files changed, 702 insertions(+), 782 deletions(-) delete mode 100644 android/app/proguard-rules.pro delete mode 100644 android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarEventType.kt delete mode 100644 lib/base/enums/calendar_event_type.dart rename lib/base/util/{directions_launcher.dart => map_launcher.dart} (90%) delete mode 100644 lib/placesComponent/views/directions_button.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index c4a68ae2..6b4a4a43 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,6 +1,7 @@ plugins { id "com.android.application" id "kotlin-android" + id "org.jetbrains.kotlin.plugin.serialization" id "dev.flutter.flutter-gradle-plugin" id "com.google.gms.google-services" id "com.google.firebase.crashlytics" @@ -71,7 +72,7 @@ android { signingConfig signingConfigs.release minifyEnabled true shrinkResources true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFile getDefaultProguardFile('proguard-android.txt') } else { signingConfig signingConfigs.debug } @@ -89,12 +90,7 @@ flutter { dependencies { implementation 'com.android.support:multidex' - implementation 'joda-time:joda-time:2.12.7' - def appcompat_version = "1.6.1" - implementation("androidx.appcompat:appcompat:$appcompat_version") - implementation("androidx.appcompat:appcompat-resources:$appcompat_version") - implementation 'com.google.code.gson:gson:2.10.1' - implementation 'org.ocpsoft.prettytime:prettytime:5.0.4.Final' + implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0' } apply plugin: 'com.google.firebase.crashlytics' diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro deleted file mode 100644 index 49b0275e..00000000 --- a/android/app/proguard-rules.pro +++ /dev/null @@ -1,42 +0,0 @@ -# joda --keep class org.joda.** {*;} --dontwarn org.joda.** - -# prettytime --keep class org.ocpsoft.pretty.time.i18n.** --keep class org.ocpsoft.prettytime.i18n.** --keep class org.ocpsoft.prettytime.units.** --keepnames class ** implements org.ocpsoft.prettytime.TimeUnit - -# gson --keepclassmembers,allowobfuscation class * { - @com.google.gson.annotations.SerializedName ; -} --keep class * { - @com.google.gson.annotations.SerializedName ; -} --if class * --keepclasseswithmembers class <1> { - (...); - @com.google.gson.annotations.SerializedName ; -} --keepnames class com.fasterxml.jackson.databind.** { *; } --dontwarn com.fasterxml.jackson.databind.** - -# entires recommended by Android Studio --dontwarn com.google.android.play.core.splitcompat.SplitCompatApplication --dontwarn com.google.android.play.core.splitinstall.SplitInstallManager --dontwarn com.google.android.play.core.splitinstall.SplitInstallRequest$Builder --dontwarn com.google.android.play.core.splitinstall.SplitInstallRequest --dontwarn com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener --dontwarn com.google.android.play.core.tasks.OnFailureListener --dontwarn com.google.android.play.core.tasks.OnSuccessListener --dontwarn com.google.android.play.core.tasks.Task --dontwarn com.google.api.client.http.GenericUrl --dontwarn com.google.api.client.http.HttpHeaders --dontwarn com.google.api.client.http.HttpRequest --dontwarn com.google.api.client.http.HttpRequestFactory --dontwarn com.google.api.client.http.HttpResponse --dontwarn com.google.api.client.http.HttpTransport --dontwarn com.google.api.client.http.javanet.NetHttpTransport$Builder --dontwarn com.google.api.client.http.javanet.NetHttpTransport diff --git a/android/app/src/main/kotlin/de/tum/in/tumcampus/MainActivity.kt b/android/app/src/main/kotlin/de/tum/in/tumcampus/MainActivity.kt index c76a21de..291b5ec8 100644 --- a/android/app/src/main/kotlin/de/tum/in/tumcampus/MainActivity.kt +++ b/android/app/src/main/kotlin/de/tum/in/tumcampus/MainActivity.kt @@ -2,4 +2,4 @@ package de.tum.`in`.tumcampus import io.flutter.embedding.android.FlutterActivity -class MainActivity : FlutterActivity() {} +class MainActivity : FlutterActivity() diff --git a/android/app/src/main/kotlin/de/tum/in/tumcampus/util/DateTimeSerializer.kt b/android/app/src/main/kotlin/de/tum/in/tumcampus/util/DateTimeSerializer.kt index 9b09e035..ac9618d9 100644 --- a/android/app/src/main/kotlin/de/tum/in/tumcampus/util/DateTimeSerializer.kt +++ b/android/app/src/main/kotlin/de/tum/in/tumcampus/util/DateTimeSerializer.kt @@ -1,27 +1,32 @@ package de.tum.`in`.tumcampus.util -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import org.joda.time.DateTime -import org.joda.time.format.DateTimeFormat -import java.lang.reflect.Type +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter -class LocalDateTimeDeserializer : JsonDeserializer { - override fun deserialize( - json: JsonElement?, - typeOfT: Type?, - context: JsonDeserializationContext? - ): DateTime? { - return deserializeStringToDate(json?.asString) +@OptIn(ExperimentalSerializationApi::class) +@Serializer(forClass = LocalDateTime::class) +object DateTimeSerializer : KSerializer { + private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS") + + override fun serialize(encoder: Encoder, value: LocalDateTime) { + encoder.encodeString(value.format(formatter)) + } + + override fun deserialize(decoder: Decoder): LocalDateTime { + return LocalDateTime.parse(decoder.decodeString(), formatter) } -} -fun deserializeStringToDate(dateString: String?): DateTime? { - return try { - val formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS") - DateTime.parse(dateString, formatter) - } catch (_: Exception) { - null + fun deserializeStringToDate(dateString: String?): LocalDateTime? { + return try { + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS") + LocalDateTime.parse(dateString, formatter) + } catch (_: Exception) { + null + } } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/de/tum/in/tumcampus/util/DateTimeUtils.kt b/android/app/src/main/kotlin/de/tum/in/tumcampus/util/DateTimeUtils.kt index 52717f51..254edd8a 100644 --- a/android/app/src/main/kotlin/de/tum/in/tumcampus/util/DateTimeUtils.kt +++ b/android/app/src/main/kotlin/de/tum/in/tumcampus/util/DateTimeUtils.kt @@ -1,139 +1,37 @@ package de.tum.`in`.tumcampus.util import android.content.Context -import android.text.format.DateUtils.* import de.tum.`in`.tumcampus.R -import org.joda.time.DateTime -import org.joda.time.DateTimeZone -import org.joda.time.format.DateTimeFormat -import org.joda.time.format.DateTimeFormatter -import java.text.ParseException -import java.util.* +import java.time.Duration +import java.time.LocalDateTime object DateTimeUtils { /** - * Format an upcoming string nicely by being more precise as time comes closer - * E.g.: - * in 30 minutes - * in 2 h 4 min - * in 6 hours - * tomorrow - * @see getRelativeTimeSpanString() - */ - private fun formatFutureTime(time: DateTime, context: Context): String { - val timeInMillis = time.millis - val now = DateTime.now().millis - - // Catch future dates: current clock might be running behind - if (timeInMillis < now || timeInMillis <= 0) return formatTimeOrDay(time, context) - - val diff = timeInMillis - now - return when { - diff < 60 * MINUTE_IN_MILLIS -> { - val formatter = DateTimeFormat.forPattern("m") - .withLocale(Locale.ENGLISH) - "${context.getString(R.string.IN)} ${ - formatter.print( - DateTime( - diff, - DateTimeZone.UTC - ) - ) - } " + - context.getString(R.string.MINUTES) - } - - diff < 5 * HOUR_IN_MILLIS -> { - val formatter = DateTimeFormat.forPattern("h 'h' m 'min'") - .withLocale(Locale.ENGLISH) - "${context.getString(R.string.IN)} ${ - formatter.print( - DateTime( - diff, - DateTimeZone.UTC - ) - ) - }" - } - - else -> getRelativeTimeSpanString( - timeInMillis, now, MINUTE_IN_MILLIS, - FORMAT_ABBREV_RELATIVE - ).toString() - } - } - - /** - * @Deprecated use formatTimeOrDay(DateTime, Context) - */ - @Deprecated("Use the version with a proper DateTime object, there's really no reason to pass datetimes as strings") - fun formatTimeOrDayFromISO(datetime: String, context: Context): String { - val d = parseIsoDate(datetime) ?: return "" - return formatTimeOrDay(d, context) - } - - /** - * Format a *past* ISO string timestamp with degrading granularity as time goes by - * E.g.: - * Just now - * 18:20 - * Yesterday - * 12.03.2016 + * Checks whether two DateTime contain the same day * - * Please note, that this does *not* use getRelativeTimeSpanString(), because lectures scheduled - * at 12:00 and starting at 12:15 get a bit annoying nagging you with "12 minutes ago", when - * they actually only start in a couple of minutes - * This is similar to formatFutureTime(), but not specialized on future dates - * When in doubt, use formatFutureTime() - * @see formatFutureTime() + * @return true if both dates are on the same day */ - private fun formatTimeOrDay(time: DateTime, context: Context): String { - val timeInMillis = time.millis - val now = DateTime.now().millis - - // Catch future dates: current clock might be running behind - if (timeInMillis > now || timeInMillis <= 0) { - return context.getString(R.string.just_now) - } + fun isSameDay(first: LocalDateTime, second: LocalDateTime) = + first.year == second.year && first.dayOfYear == second.dayOfYear - val diff = now - timeInMillis - return when { - diff < MINUTE_IN_MILLIS -> - context.getString(R.string.just_now) - - diff < 24 * HOUR_IN_MILLIS -> - DateTimeFormat.forPattern("HH:mm") - .withLocale(Locale.ENGLISH) - .print(time) - - diff < 48 * HOUR_IN_MILLIS -> - context.getString(R.string.yesterday) - - else -> - DateTimeFormat.forPattern("dd.MM.yyyy") - .withLocale(Locale.ENGLISH) - .print(time) - } - } - - /** - * 2014-06-30T16:31:57Z - */ - private val isoDateFormatter: DateTimeFormatter = - DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'") +} - private fun parseIsoDate(datetime: String) = try { - isoDateFormatter.parseDateTime(datetime) - } catch (e: ParseException) { - //Utils.log(e) - null +fun LocalDateTime.timeAgo(context: Context): String { + val now = LocalDateTime.now() + val duration = Duration.between(this, now) + + val years = duration.toDays() / 365 + val months = duration.toDays() / 30 + val days = duration.toDays() + val hours = duration.toHours() + val minutes = duration.toMinutes() + + return when { + years > 0 -> context.resources.getQuantityString(R.plurals.yearsAgo, years.toInt()) + months > 0 -> context.resources.getQuantityString(R.plurals.monthsAgo, months.toInt()) + days > 0 -> context.resources.getQuantityString(R.plurals.daysAgo, days.toInt()) + hours > 0 -> context.resources.getQuantityString(R.plurals.hoursAgo, hours.toInt()) + minutes > 0 -> context.resources.getQuantityString(R.plurals.minutesAgo, minutes.toInt()) + else -> context.resources.getString(R.string.just_now) } - - /** - * Checks whether two DateTime contain the same day - * - * @return true if both dates are on the same day - */ - fun isSameDay(first: DateTime, second: DateTime) = - first.year() == second.year() && first.dayOfYear() == second.dayOfYear() } diff --git a/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarEventType.kt b/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarEventType.kt deleted file mode 100644 index e60bf345..00000000 --- a/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarEventType.kt +++ /dev/null @@ -1,8 +0,0 @@ -package de.tum.`in`.tumcampus.widgets.calendar - -enum class CalendarEventType { - CANCELED, - LECTURE, - EXERCISE, - OTHER -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarWidget.kt b/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarWidget.kt index 1075639a..8e8c4b93 100644 --- a/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarWidget.kt +++ b/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarWidget.kt @@ -9,22 +9,24 @@ import android.view.View import android.widget.RemoteViews import de.tum.`in`.tumcampus.MainActivity import de.tum.`in`.tumcampus.R -import de.tum.`in`.tumcampus.util.deserializeStringToDate +import de.tum.`in`.tumcampus.util.DateTimeSerializer +import de.tum.`in`.tumcampus.util.timeAgo import es.antonborri.home_widget.HomeWidgetLaunchIntent import es.antonborri.home_widget.HomeWidgetPlugin -import org.joda.time.DateTime -import org.joda.time.Days -import org.joda.time.LocalDate -import org.joda.time.format.DateTimeFormat -import org.ocpsoft.prettytime.PrettyTime +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.Period +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle +import java.time.format.TextStyle import java.util.Locale class CalendarWidget : AppWidgetProvider() { override fun onUpdate( - context: Context, - appWidgetManager: AppWidgetManager, - appWidgetIds: IntArray + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray ) { // There may be multiple widgets active, so update all of them for (appWidgetId in appWidgetIds) { @@ -34,34 +36,41 @@ class CalendarWidget : AppWidgetProvider() { } private fun updateAppWidget( - context: Context, - appWidgetManager: AppWidgetManager, - appWidgetId: Int + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetId: Int ) { // Instantiate the RemoteViews object for the app widget layout. val remoteViews = RemoteViews(context.packageName, R.layout.calendar_widget) // Set formatted date in the header - val localDate = DateTime.now().toLocalDate() - val date = DateTimeFormat.longDate().print(localDate) - val weekday = localDate.dayOfWeek().getAsText(Locale.getDefault()) + val localDate = LocalDate.now() + val dateFormatter = + DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.getDefault()) + val date = localDate.format(dateFormatter) + val weekday = localDate.dayOfWeek.getDisplayName(TextStyle.FULL, Locale.getDefault()) remoteViews.setTextViewText(R.id.calendar_widget_date, "$weekday, $date") // Set last saved date in the header - val p = PrettyTime() val widgetData = HomeWidgetPlugin.getData(context) val lastSaved = widgetData.getString("calendar_save", null) - val lastSavedDate = deserializeStringToDate(lastSaved)?.toLocalDate() - val lastSavedDateString = p.format( deserializeStringToDate(lastSaved)?.toDate()) + + val lastSavedDate = DateTimeSerializer.deserializeStringToDate(lastSaved) + val lastSavedDateString = (lastSavedDate ?: LocalDateTime.now()).timeAgo(context) remoteViews.setTextViewText(R.id.calendar_widget_updated_on, lastSavedDateString) val pendingIntentWithData = HomeWidgetLaunchIntent.getActivity( - context, - MainActivity::class.java, - Uri.parse("tumCampusApp://message?homeWidget=calendar")) + context, + MainActivity::class.java, + Uri.parse("tumCampusApp://message?homeWidget=calendar") + ) remoteViews.setOnClickPendingIntent(R.id.calendar_widget, pendingIntentWithData) - if (Days.daysBetween(lastSavedDate, LocalDate.now()).days < 14) { + if (lastSavedDate != null && Period.between( + LocalDate.now(), + lastSavedDate.toLocalDate(), + ).days < 14 + ) { // Set up the intent that starts the calendarWidgetService val intent = Intent(context, CalendarWidgetService::class.java) intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) @@ -72,9 +81,10 @@ private fun updateAppWidget( // It should be in the same layout used to instantiate the RemoteViews // object above. remoteViews.setEmptyView(R.id.calendar_widget_listview, R.id.empty_list_item) + remoteViews.setViewVisibility(R.id.old_data_item, View.INVISIBLE) } else { - remoteViews.setViewVisibility(R.id.calendar_widget_listview, View.INVISIBLE) remoteViews.setViewVisibility(R.id.old_data_item, View.VISIBLE) + remoteViews.setViewVisibility(R.id.calendar_widget_listview, View.INVISIBLE) } // Instruct the widget manager to update the widget diff --git a/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarWidgetService.kt b/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarWidgetService.kt index 65081714..06d79d82 100644 --- a/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarWidgetService.kt +++ b/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/CalendarWidgetService.kt @@ -4,14 +4,13 @@ import android.content.Context import android.content.Intent import android.widget.RemoteViews import android.widget.RemoteViewsService -import com.google.gson.GsonBuilder import de.tum.`in`.tumcampus.util.DateTimeUtils -import de.tum.`in`.tumcampus.util.LocalDateTimeDeserializer import de.tum.`in`.tumcampus.R import de.tum.`in`.tumcampus.util.Const import es.antonborri.home_widget.HomeWidgetPlugin -import org.joda.time.DateTime -import org.joda.time.format.DateTimeFormat +import kotlinx.serialization.json.Json +import java.time.format.DateTimeFormatter +import java.time.format.TextStyle import java.util.* import kotlin.collections.ArrayList @@ -22,7 +21,7 @@ class CalendarWidgetService : RemoteViewsService() { } private class CalendarRemoteViewFactory( - private val applicationContext: Context + private val applicationContext: Context ) : RemoteViewsFactory { private var calendarEvents: List = ArrayList() @@ -31,12 +30,9 @@ class CalendarWidgetService : RemoteViewsService() { override fun onDataSetChanged() { val widgetData = HomeWidgetPlugin.getData(applicationContext) val data = widgetData.getString("calendar", null) - val gson = GsonBuilder() - .registerTypeAdapter(DateTime::class.java, LocalDateTimeDeserializer()) - .create() - val parsedEvents = gson.fromJson(data, Array::class.java).asList() - - calendarEvents = parsedEvents + if (data != null) { + calendarEvents = Json.decodeFromString>(data).asList() + } // Set isFirstOnDay flags if (calendarEvents.isNotEmpty()) { @@ -44,15 +40,13 @@ class CalendarWidgetService : RemoteViewsService() { } for (index in 1 until calendarEvents.size) { - val (_, _, startTime) = calendarEvents[index - 1] - val lastTime = DateTime(startTime) + val startTime = calendarEvents[index - 1].startDate val thisEvent = calendarEvents[index] - val thisTime = DateTime(thisEvent.startDate.millis) + val thisTime = thisEvent.startDate - if (!DateTimeUtils.isSameDay(lastTime, thisTime)) { + if (!DateTimeUtils.isSameDay(startTime, thisTime)) { thisEvent.isFirstOnDay = true - } } } @@ -67,23 +61,23 @@ class CalendarWidgetService : RemoteViewsService() { } val remoteViews = RemoteViews( - applicationContext.packageName, - R.layout.calendar_widget_item + applicationContext.packageName, + R.layout.calendar_widget_item ) // Get the lecture for this view val currentItem = this.calendarEvents[position] - val startTime = DateTime(currentItem.startDate.millis) + val startTime = currentItem.startDate // Setup the date if (currentItem.isFirstOnDay) { remoteViews.setTextViewText( - R.id.calendar_widget_date_day, - startTime.dayOfMonth.toString() + R.id.calendar_widget_date_day, + startTime.dayOfMonth.toString() ) remoteViews.setTextViewText( - R.id.calendar_widget_date_weekday, startTime.monthOfYear() - .getAsShortText(Locale.getDefault()) + R.id.calendar_widget_date_weekday, + startTime.month.getDisplayName(TextStyle.SHORT, Locale.getDefault()) ) remoteViews.setViewPadding(R.id.calendar_widget_item, 0, 15, 0, 0) } else { @@ -94,20 +88,23 @@ class CalendarWidgetService : RemoteViewsService() { } // Setup event color - remoteViews.setInt(R.id.calendar_widget_event, "setBackgroundColor", currentItem.getEventColor(applicationContext)) + remoteViews.setInt( + R.id.calendar_widget_event, + "setBackgroundColor", + currentItem.getEventColor(applicationContext) + ) // Setup event title remoteViews.setTextViewText(R.id.calendar_widget_event_title, currentItem.title) // Setup event time - val formatter = DateTimeFormat.shortTime() - val startTimeText = formatter.print(startTime) - val endTime = DateTime(currentItem.endDate.millis) - val endTimeText = formatter.print(endTime) + val formatter = DateTimeFormatter.ofPattern("hh:mm a") + val startTimeText = formatter.format(startTime) + val endTimeText = formatter.format(currentItem.endDate) val eventTime = applicationContext.getString( - R.string.event_start_end_format_string, - startTimeText, - endTimeText + R.string.event_start_end_format_string, + startTimeText, + endTimeText ) remoteViews.setTextViewText(R.id.calendar_widget_event_time, eventTime) @@ -116,7 +113,7 @@ class CalendarWidgetService : RemoteViewsService() { // Setup action to open calendar val fillInIntent = Intent().apply { - putExtra(Const.EVENT_TIME, currentItem.startDate.millis) + putExtra(Const.EVENT_TIME, currentItem.startDate) } remoteViews.setOnClickFillInIntent(R.id.calendar_widget_event, fillInIntent) diff --git a/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/WidgetCalendarItem.kt b/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/WidgetCalendarItem.kt index 3aea69c2..256ae273 100644 --- a/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/WidgetCalendarItem.kt +++ b/android/app/src/main/kotlin/de/tum/in/tumcampus/widgets/calendar/WidgetCalendarItem.kt @@ -1,51 +1,37 @@ package de.tum.`in`.tumcampus.widgets.calendar import android.content.Context -import android.graphics.Color import androidx.core.content.ContextCompat -import com.google.gson.annotations.SerializedName import de.tum.`in`.tumcampus.R +import de.tum.`in`.tumcampus.util.DateTimeSerializer import de.tum.`in`.tumcampus.util.argbToColor -import org.joda.time.DateTime +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import java.time.LocalDateTime +@Serializable data class WidgetCalendarItem( - @SerializedName("nr") + @SerialName("nr") val id: String, - val title: String, - @SerializedName("dtstart") - val startDate: DateTime, - @SerializedName("dtend") - val endDate: DateTime, - val location: String, val status: String, + val url: String?, + val title: String, + val description: String?, + @Serializable(with = DateTimeSerializer::class) + @SerialName("dtstart") + val startDate: LocalDateTime, + @Serializable(with = DateTimeSerializer::class) + @SerialName("dtend") + val endDate: LocalDateTime, + val location: String?, val color: Long?, var isFirstOnDay: Boolean = false ) { fun getEventColor(context: Context): Int { return if (color == null) { - when (type) { - CalendarEventType.CANCELED -> Color.parseColor("#F44336") - CalendarEventType.LECTURE -> Color.parseColor("#4CAF50") - CalendarEventType.EXERCISE -> Color.parseColor("#9800FF") - else -> ContextCompat.getColor(context, R.color.color_primary) - } + ContextCompat.getColor(context, R.color.color_primary) } else { argbToColor(color) } } - - private val type: CalendarEventType - get() { - return when { - isCanceled -> CalendarEventType.CANCELED - title.endsWith("VO") || title.endsWith("VU") || title.endsWith("VI") -> CalendarEventType.LECTURE - title.endsWith("UE") -> CalendarEventType.EXERCISE - else -> CalendarEventType.OTHER - } - } - - private val isCanceled: Boolean - get() { - return status == "CANCEL" - } } diff --git a/android/app/src/main/res/values-de/strings.xml b/android/app/src/main/res/values-de/strings.xml index eb820c3e..65e6ecc8 100644 --- a/android/app/src/main/res/values-de/strings.xml +++ b/android/app/src/main/res/values-de/strings.xml @@ -2,13 +2,33 @@ EXAMPLE Widget hinzufügen - Das ist eine Widget Beschreibung + Ein Überblick über Deine bevorstehenden Veranstaltungen. Calendar Widget TUM Vorlesungen auswählen Keine Vorlesungen geplannt :) Dieses Widget wurde zuletzt vor über 2 Wochen aktualisiert. Öffne die Applikation, um die Daten zu aktualisieren in Minuten - Jetzt + Gerade eben Gestern + + Vor 1 Jahr + Vor %d Jahren + + + Vor 1 Monaten + Vor %d Monaten + + + Vor 1 Tag + Vor %d Tagen + + + Vor 1 Stunde + Vor %d Stunden + + + Vor 1 Minute + Vor %d Minuten + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index d108e6ab..07ec7f99 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -2,14 +2,34 @@ EXAMPLE Add widget - This is an app widget description + An Overview About Your Upcoming Events. calendar Widget TUM Select lectures No lectures planned :) This widget was last updated over 2 weeks ago.Open the application to update the data in minutes - Just now + moments ago Yesterday %1$s–%2$s + + 1 year ago + %d years ago + + + 1 month ago + %d months ago + + + 1 day ago + %d days ago + + + 1 hour ago + %d hours ago + + + 1 minute ago + %d minutes ago + \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle index de7a1747..d183927c 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -20,6 +20,7 @@ plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version '8.3.2' apply false id "org.jetbrains.kotlin.android" version "1.9.20" apply false + id "org.jetbrains.kotlin.plugin.serialization" version "1.9.20" apply false id "com.google.gms.google-services" version "4.4.0" apply false id "com.google.firebase.crashlytics" version "2.9.9" apply false } diff --git a/ios/CalendarWidget/CalendarEntry.swift b/ios/CalendarWidget/CalendarEntry.swift index bb928f84..f266c91f 100644 --- a/ios/CalendarWidget/CalendarEntry.swift +++ b/ios/CalendarWidget/CalendarEntry.swift @@ -24,37 +24,7 @@ struct CalendarEntry: Codable, Identifiable { case title, location, status, color } - var isCanceled: Bool { - return status == "CANCEL" - } - - var type: CalendarEventType { - if isCanceled { - return .canceled - } else if title.hasSuffix("VO") || title.hasSuffix("VU") || title.hasSuffix("VI") { - return .lecture - } else if title.hasSuffix("UE") { - return .exercise - } else { - return .other - } - } - var eventColor: Color { - if (color == nil) { - switch type { - case .canceled: - return Color(red: 244 / 255, green: 67 / 255, blue: 54 / 255) - case .lecture: - return Color(red: 76 / 255, green: 175 / 255, blue: 80 / 255) - case .exercise: - return Color(red: 255 / 255, green: 152 / 255, blue: 0 / 255) - default: - return .accent - } - } - else { - return Color(argb: UInt32(color!)) - } + return color == nil ? .accent : Color(argb: UInt32(color!)) } } diff --git a/ios/CalendarWidget/CalendarEventView.swift b/ios/CalendarWidget/CalendarEventView.swift index ba46c68a..b97464da 100644 --- a/ios/CalendarWidget/CalendarEventView.swift +++ b/ios/CalendarWidget/CalendarEventView.swift @@ -8,29 +8,29 @@ import SwiftUI struct CalendarEventView: View { - + let event: CalendarEntry let color: Color let isFirst: Bool - + private var timeFormatter: DateFormatter { let formatter = DateFormatter() formatter.dateFormat = "HH:mm" return formatter } - + private var dayNumberFormatter: DateFormatter { let formatter = DateFormatter() formatter.dateFormat = "dd" return formatter } - + private var monthNameFormatter: DateFormatter { let formatter = DateFormatter() formatter.dateFormat = "MMM" return formatter } - + var body: some View { HStack { VStack(alignment: .leading) { @@ -42,15 +42,15 @@ struct CalendarEventView: View { } .frame(width: 30) .opacity(isFirst ? 1 : 0) - + VStack(alignment: .leading, spacing: 3) { Text(event.title) .font(.caption) .bold() .lineLimit(1) - + let timeText = "\(timeFormatter.string(from: event.startDate)) - \(timeFormatter.string(from: event.endDate))" - + if (event.location != nil) { Text("\(timeText) | \(event.location!)") .font(.caption2) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2552816b..ef9bc1d5 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,19 +1,19 @@ PODS: - device_info_plus (0.0.1): - Flutter - - Firebase/CoreOnly (10.22.0): - - FirebaseCore (= 10.22.0) - - Firebase/Crashlytics (10.22.0): + - Firebase/CoreOnly (10.24.0): + - FirebaseCore (= 10.24.0) + - Firebase/Crashlytics (10.24.0): - Firebase/CoreOnly - - FirebaseCrashlytics (~> 10.22.0) - - firebase_core (2.29.0): - - Firebase/CoreOnly (= 10.22.0) + - FirebaseCrashlytics (~> 10.24.0) + - firebase_core (2.30.0): + - Firebase/CoreOnly (= 10.24.0) - Flutter - - firebase_crashlytics (3.5.1): - - Firebase/Crashlytics (= 10.22.0) + - firebase_crashlytics (3.5.2): + - Firebase/Crashlytics (= 10.24.0) - firebase_core - Flutter - - FirebaseCore (10.22.0): + - FirebaseCore (10.24.0): - FirebaseCoreInternal (~> 10.0) - GoogleUtilities/Environment (~> 7.12) - GoogleUtilities/Logger (~> 7.12) @@ -21,9 +21,10 @@ PODS: - FirebaseCore (~> 10.0) - FirebaseCoreInternal (10.24.0): - "GoogleUtilities/NSData+zlib (~> 7.8)" - - FirebaseCrashlytics (10.22.0): + - FirebaseCrashlytics (10.24.0): - FirebaseCore (~> 10.5) - FirebaseInstallations (~> 10.0) + - FirebaseRemoteConfigInterop (~> 10.23) - FirebaseSessions (~> 10.5) - GoogleDataTransport (~> 9.2) - GoogleUtilities/Environment (~> 7.8) @@ -34,6 +35,7 @@ PODS: - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/UserDefaults (~> 7.8) - PromisesObjC (~> 2.1) + - FirebaseRemoteConfigInterop (10.24.0) - FirebaseSessions (10.24.0): - FirebaseCore (~> 10.5) - FirebaseCoreExtension (~> 10.0) @@ -51,7 +53,7 @@ PODS: - Flutter - google_maps_flutter_ios (0.0.1): - Flutter - - GoogleMaps (< 9.0) + - GoogleMaps (< 9.0, >= 8.4) - GoogleDataTransport (9.4.1): - GoogleUtilities/Environment (~> 7.7) - nanopb (< 2.30911.0, >= 2.30908.0) @@ -137,6 +139,7 @@ SPEC REPOS: - FirebaseCoreInternal - FirebaseCrashlytics - FirebaseInstallations + - FirebaseRemoteConfigInterop - FirebaseSessions - GoogleDataTransport - GoogleMaps @@ -187,20 +190,21 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d - Firebase: 797fd7297b7e1be954432743a0b3f90038e45a71 - firebase_core: aaadbddb3cb2ee3792b9804f9dbb63e5f6f7b55c - firebase_crashlytics: 4271b5bb77f6169ac7c2a9d62ad0e6aa5f84c2fe - FirebaseCore: 0326ec9b05fbed8f8716cddbf0e36894a13837f7 + Firebase: 91fefd38712feb9186ea8996af6cbdef41473442 + firebase_core: 66b99b4fb4e5d7cc4e88d4c195fe986681f3466a + firebase_crashlytics: 7ccf6bc9a3310a5c53bca1150203f9b91b42732a + FirebaseCore: 11dc8a16dfb7c5e3c3f45ba0e191a33ac4f50894 FirebaseCoreExtension: af5fd85e817ea9d19f9a2659a376cf9cf99f03c0 FirebaseCoreInternal: bcb5acffd4ea05e12a783ecf835f2210ce3dc6af - FirebaseCrashlytics: e568d68ce89117c80cddb04073ab9018725fbb8c + FirebaseCrashlytics: af38ea4adfa606f6e63fcc22091b61e7938fcf66 FirebaseInstallations: 8f581fca6478a50705d2bd2abd66d306e0f5736e + FirebaseRemoteConfigInterop: 6c349a466490aeace3ce9c091c86be1730711634 FirebaseSessions: 2651b464e241c93fd44112f995d5ab663c970487 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778 flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be geolocator_apple: 6cbaf322953988e009e5ecb481f07efece75c450 - google_maps_flutter_ios: d9b9a308a1f14275c2967aefa639018a02bd44e8 + google_maps_flutter_ios: c454f18e0e22df6ac0e9f2a4df340858f5a3680c GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d GoogleUtilities: d053d902a8edaa9904e1bd00c37535385b8ed152 @@ -217,7 +221,7 @@ SPEC CHECKSUMS: shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586 - video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579 + video_player_avfoundation: 2b4384f3b157206b5e150a0083cdc0c905d260d3 PODFILE CHECKSUM: 2dec3f67b1fa3eb41982ef2f40e0d15f34b91a30 diff --git a/lib/base/enums/calendar_event_type.dart b/lib/base/enums/calendar_event_type.dart deleted file mode 100644 index 3a2a51e2..00000000 --- a/lib/base/enums/calendar_event_type.dart +++ /dev/null @@ -1,6 +0,0 @@ -enum CalendarEventType { - canceled, - lecture, - exercise, - other; -} diff --git a/lib/base/errorHandling/campus_exception_router.dart b/lib/base/errorHandling/campus_exception_router.dart index ccc87f91..84765f7c 100644 --- a/lib/base/errorHandling/campus_exception_router.dart +++ b/lib/base/errorHandling/campus_exception_router.dart @@ -8,14 +8,12 @@ class CampusExceptionRouter extends StatelessWidget with ErrorHandlingView { super.key, required this.campusException, required ErrorHandlingViewType errorHandlingViewType, - Future Function(bool)? retry, - Future Function(bool, BuildContext)? retryWithContext, + Function()? retry, Color? titleColor, Color? bodyColor, }) { this.errorHandlingViewType = errorHandlingViewType; this.retry = retry; - this.retryWithContext = retryWithContext; this.titleColor = titleColor; this.bodyColor = bodyColor; } @@ -24,6 +22,9 @@ class CampusExceptionRouter extends StatelessWidget with ErrorHandlingView { @override Widget build(BuildContext context) { - return exceptionMessage(context, campusException.message, null); + return exceptionMessage( + errorMessage: campusException.message, + context: context, + ); } } diff --git a/lib/base/errorHandling/default_error_router.dart b/lib/base/errorHandling/default_error_router.dart index 479e0b89..ef41cbfe 100644 --- a/lib/base/errorHandling/default_error_router.dart +++ b/lib/base/errorHandling/default_error_router.dart @@ -8,14 +8,12 @@ class DefaultErrorRouter extends StatelessWidget with ErrorHandlingView { super.key, required this.exception, required ErrorHandlingViewType errorHandlingViewType, - Future Function(bool)? retry, - Future Function(bool, BuildContext)? retryWithContext, + Function()? retry, Color? titleColor, Color? bodyColor, }) { this.errorHandlingViewType = errorHandlingViewType; this.retry = retry; - this.retryWithContext = retryWithContext; this.titleColor = titleColor; this.bodyColor = bodyColor; } @@ -25,9 +23,9 @@ class DefaultErrorRouter extends StatelessWidget with ErrorHandlingView { @override Widget build(BuildContext context) { return exceptionMessage( - context, - context.localizations.unknownError, - context.localizations.pleaseReport, + errorMessage: context.localizations.unknownError, + fixMessage: context.localizations.pleaseReport, + context: context, ); } } diff --git a/lib/base/errorHandling/dio_exception_router.dart b/lib/base/errorHandling/dio_exception_router.dart index f5991d63..60113385 100644 --- a/lib/base/errorHandling/dio_exception_router.dart +++ b/lib/base/errorHandling/dio_exception_router.dart @@ -9,14 +9,12 @@ class DioExceptionRouter extends StatelessWidget with ErrorHandlingView { super.key, required this.dioException, required ErrorHandlingViewType errorHandlingViewType, - Future Function(bool)? retry, - Future Function(bool, BuildContext)? retryWithContext, + Function()? retry, Color? titleColor, Color? bodyColor, }) { this.errorHandlingViewType = errorHandlingViewType; this.retry = retry; - this.retryWithContext = retryWithContext; this.titleColor = titleColor; this.bodyColor = bodyColor; } @@ -28,42 +26,42 @@ class DioExceptionRouter extends StatelessWidget with ErrorHandlingView { switch (dioException.type) { case DioExceptionType.badResponse: return exceptionMessage( - context, - context.localizations.badResponse, - context.localizations.pleaseTryAgain, + errorMessage: context.localizations.badResponse, + fixMessage: context.localizations.pleaseTryAgain, + context: context, ); case DioExceptionType.connectionError: return exceptionMessage( - context, - context.localizations.connectionError, - context.localizations.makeSureInternetConnection, + errorMessage: context.localizations.connectionError, + fixMessage: context.localizations.makeSureInternetConnection, + context: context, ); case DioExceptionType.cancel: return exceptionMessage( - context, - context.localizations.requestCancelled, - context.localizations.pleaseReport, + errorMessage: context.localizations.requestCancelled, + fixMessage: context.localizations.pleaseReport, + context: context, ); case DioExceptionType.connectionTimeout: case DioExceptionType.sendTimeout: case DioExceptionType.receiveTimeout: return exceptionMessage( - context, - context.localizations.connectionTimeout, - context.localizations.makeSureInternetConnection, + errorMessage: context.localizations.connectionTimeout, + fixMessage: context.localizations.makeSureInternetConnection, + context: context, ); default: if (dioException.error.toString().contains("SocketException")) { return exceptionMessage( - context, - context.localizations.connectionError, - context.localizations.makeSureInternetConnection, + fixMessage: context.localizations.connectionError, + errorMessage: context.localizations.makeSureInternetConnection, + context: context, ); } else { return exceptionMessage( - context, - context.localizations.unknownError, - context.localizations.pleaseReport, + fixMessage: context.localizations.unknownError, + errorMessage: context.localizations.pleaseReport, + context: context, ); } } diff --git a/lib/base/errorHandling/error_handling_router.dart b/lib/base/errorHandling/error_handling_router.dart index 7481819e..754e8a85 100644 --- a/lib/base/errorHandling/error_handling_router.dart +++ b/lib/base/errorHandling/error_handling_router.dart @@ -24,15 +24,13 @@ class ErrorHandlingRouter extends ConsumerWidget { required this.error, required this.errorHandlingViewType, this.retry, - this.retryWithContext, this.titleColor, this.bodyColor, }); final Object? error; final ErrorHandlingViewType errorHandlingViewType; - final Future Function(bool)? retry; - final Future Function(bool, BuildContext)? retryWithContext; + final Function()? retry; final Color? titleColor; final Color? bodyColor; @@ -50,7 +48,6 @@ class ErrorHandlingRouter extends ConsumerWidget { dioException: dioException, errorHandlingViewType: errorHandlingViewType, retry: retry, - retryWithContext: retryWithContext, titleColor: titleColor, bodyColor: bodyColor, ); @@ -76,7 +73,6 @@ class ErrorHandlingRouter extends ConsumerWidget { tumOnlineApiException: tumOnlineApiException, errorHandlingViewType: errorHandlingViewType, retry: retry, - retryWithContext: retryWithContext, titleColor: titleColor, bodyColor: bodyColor, ); @@ -90,7 +86,6 @@ class ErrorHandlingRouter extends ConsumerWidget { searchException: searchException, errorHandlingViewType: errorHandlingViewType, retry: retry, - retryWithContext: retryWithContext, titleColor: titleColor, bodyColor: bodyColor, ); @@ -104,7 +99,6 @@ class ErrorHandlingRouter extends ConsumerWidget { campusException: campusException, errorHandlingViewType: errorHandlingViewType, retry: retry, - retryWithContext: retryWithContext, titleColor: titleColor, bodyColor: bodyColor, ); @@ -119,7 +113,6 @@ class ErrorHandlingRouter extends ConsumerWidget { typeError: typeError, errorHandlingViewType: errorHandlingViewType, retry: retry, - retryWithContext: retryWithContext, titleColor: titleColor, bodyColor: bodyColor, ); @@ -128,7 +121,6 @@ class ErrorHandlingRouter extends ConsumerWidget { grpcError: grpcError, errorHandlingViewType: errorHandlingViewType, retry: retry, - retryWithContext: retryWithContext, titleColor: titleColor, bodyColor: bodyColor, ); @@ -144,7 +136,6 @@ class ErrorHandlingRouter extends ConsumerWidget { exception: error, errorHandlingViewType: errorHandlingViewType, retry: retry, - retryWithContext: retryWithContext, titleColor: titleColor, bodyColor: bodyColor, ); diff --git a/lib/base/errorHandling/error_handling_view.dart b/lib/base/errorHandling/error_handling_view.dart index 165d62e0..b4a03835 100644 --- a/lib/base/errorHandling/error_handling_view.dart +++ b/lib/base/errorHandling/error_handling_view.dart @@ -5,16 +5,17 @@ import 'package:flutter_svg/flutter_svg.dart'; mixin ErrorHandlingView { late final ErrorHandlingViewType errorHandlingViewType; - late final Future Function(bool)? retry; - late final Future Function(bool, BuildContext)? retryWithContext; + late final Function()? retry; late final Color? titleColor; late final Color? bodyColor; - Widget exceptionMessage( - BuildContext context, - String errorMessage, + Widget exceptionMessage({ + required String errorMessage, String? fixMessage, - ) { + Function()? retry, + String? retryMessage, + required BuildContext context, + }) { switch (errorHandlingViewType) { case ErrorHandlingViewType.fullScreen: case ErrorHandlingViewType.fullScreenNoImage: @@ -36,41 +37,44 @@ mixin ErrorHandlingView { flex: 0, child: Column( children: [ - _errorMessageText( - errorMessage, - context, - style: Theme.of(context).textTheme.titleLarge?.copyWith( - color: titleColor ?? context.primaryColor, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - if (fixMessage != null) - Text( - fixMessage, + Padding( + padding: EdgeInsets.only(bottom: context.padding), + child: _errorMessageText( + errorMessage, + context, style: Theme.of(context) .textTheme - .bodyLarge - ?.copyWith(color: bodyColor), - textAlign: TextAlign.center, - maxLines: 3, + .headlineMedium + ?.copyWith( + color: titleColor ?? context.primaryColor, + ), + maxLines: 1, overflow: TextOverflow.ellipsis, ), + ), + if (fixMessage != null) + Padding( + padding: + EdgeInsets.symmetric(horizontal: context.padding), + child: Text( + fixMessage, + style: Theme.of(context) + .textTheme + .bodyLarge + ?.copyWith(color: bodyColor), + textAlign: TextAlign.center, + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ), ], ), ), const Spacer(), - if (retry != null && retryWithContext == null) ...[ - ElevatedButton( - onPressed: retry != null ? () => retry!(true) : null, - child: Text(context.localizations.tryAgain), - ), - const Spacer(), - ], - if (retryWithContext != null && retry == null) ...[ + if (retry != null || this.retry != null) ...[ ElevatedButton( - onPressed: () => retry!(true), - child: Text(context.localizations.tryAgain), + onPressed: () => retry != null ? retry() : this.retry!(), + child: Text(retryMessage ?? context.localizations.tryAgain), ), const Spacer(), ], diff --git a/lib/base/errorHandling/grpc_error_router.dart b/lib/base/errorHandling/grpc_error_router.dart index 52c0cf88..53969495 100644 --- a/lib/base/errorHandling/grpc_error_router.dart +++ b/lib/base/errorHandling/grpc_error_router.dart @@ -9,14 +9,12 @@ class GrpcErrorRouter extends StatelessWidget with ErrorHandlingView { super.key, required this.grpcError, required ErrorHandlingViewType errorHandlingViewType, - Future Function(bool)? retry, - Future Function(bool, BuildContext)? retryWithContext, + Function()? retry, Color? titleColor, Color? bodyColor, }) { this.errorHandlingViewType = errorHandlingViewType; this.retry = retry; - this.retryWithContext = retryWithContext; this.titleColor = titleColor; this.bodyColor = bodyColor; } @@ -26,9 +24,8 @@ class GrpcErrorRouter extends StatelessWidget with ErrorHandlingView { @override Widget build(BuildContext context) { return exceptionMessage( - context, - grpcError.message ?? context.localizations.unknownError, - null, + errorMessage: grpcError.message ?? context.localizations.unknownError, + context: context, ); } } diff --git a/lib/base/errorHandling/search_exception_router.dart b/lib/base/errorHandling/search_exception_router.dart index d05d4c34..bbe6d5e0 100644 --- a/lib/base/errorHandling/search_exception_router.dart +++ b/lib/base/errorHandling/search_exception_router.dart @@ -8,14 +8,12 @@ class SearchExceptionRouter extends StatelessWidget with ErrorHandlingView { super.key, required this.searchException, required ErrorHandlingViewType errorHandlingViewType, - Future Function(bool)? retry, - Future Function(bool, BuildContext)? retryWithContext, + Function()? retry, Color? titleColor, Color? bodyColor, }) { this.errorHandlingViewType = errorHandlingViewType; this.retry = retry; - this.retryWithContext = retryWithContext; this.titleColor = titleColor; this.bodyColor = bodyColor; } @@ -24,6 +22,9 @@ class SearchExceptionRouter extends StatelessWidget with ErrorHandlingView { @override Widget build(BuildContext context) { - return exceptionMessage(context, searchException.message, null); + return exceptionMessage( + errorMessage: searchException.message, + context: context, + ); } } diff --git a/lib/base/errorHandling/tum_online_api_exception_router.dart b/lib/base/errorHandling/tum_online_api_exception_router.dart index f2f6e041..69ab9e29 100644 --- a/lib/base/errorHandling/tum_online_api_exception_router.dart +++ b/lib/base/errorHandling/tum_online_api_exception_router.dart @@ -9,14 +9,12 @@ class TumOnlineApiExceptionRouter extends StatelessWidget super.key, required this.tumOnlineApiException, required ErrorHandlingViewType errorHandlingViewType, - Future Function(bool)? retry, - Future Function(bool, BuildContext)? retryWithContext, + Function()? retry, Color? titleColor, Color? bodyColor, }) { this.errorHandlingViewType = errorHandlingViewType; this.retry = retry; - this.retryWithContext = retryWithContext; this.titleColor = titleColor; this.bodyColor = bodyColor; } @@ -26,9 +24,11 @@ class TumOnlineApiExceptionRouter extends StatelessWidget @override Widget build(BuildContext context) { return exceptionMessage( - context, - tumOnlineApiException.message, - tumOnlineApiException.recoverySuggestion, + errorMessage: tumOnlineApiException.message(context), + fixMessage: tumOnlineApiException.recoverySuggestion(context), + retry: tumOnlineApiException.overwriteRetry(context) ?? retry, + retryMessage: tumOnlineApiException.overwriteRetryMessage(context), + context: context, ); } } diff --git a/lib/base/errorHandling/type_error_router.dart b/lib/base/errorHandling/type_error_router.dart index 78a7b0de..9ea66588 100644 --- a/lib/base/errorHandling/type_error_router.dart +++ b/lib/base/errorHandling/type_error_router.dart @@ -8,14 +8,12 @@ class TypeErrorRouter extends StatelessWidget with ErrorHandlingView { super.key, required this.typeError, required ErrorHandlingViewType errorHandlingViewType, - Future Function(bool)? retry, - Future Function(bool, BuildContext)? retryWithContext, + Function()? retry, Color? titleColor, Color? bodyColor, }) { this.errorHandlingViewType = errorHandlingViewType; this.retry = retry; - this.retryWithContext = retryWithContext; this.titleColor = titleColor; this.bodyColor = bodyColor; } @@ -25,9 +23,9 @@ class TypeErrorRouter extends StatelessWidget with ErrorHandlingView { @override Widget build(BuildContext context) { return exceptionMessage( - context, - context.localizations.decodingError, - context.localizations.pleaseReport, + errorMessage: context.localizations.decodingError, + fixMessage: context.localizations.pleaseReport, + context: context, ); } } diff --git a/lib/base/localization/app_de.arb b/lib/base/localization/app_de.arb index 144c25d0..530c7179 100644 --- a/lib/base/localization/app_de.arb +++ b/lib/base/localization/app_de.arb @@ -49,13 +49,17 @@ "tuitionPaid":"Bezahlt", "tuitionDueDate":"Fälligkeitsdatum", "tuitionOpenAmount":"Offener Betrag", - "versionNumber":"Version {number}", + "versionNumber":"Version {number} ({build})", "@versionNumber":{ "description":"Aktuelle Version der App", "placeholders":{ "number":{ "type":"String", "example":"-.-.-" + }, + "build":{ + "type":"String", + "example":"1" } } }, @@ -253,14 +257,14 @@ "closed":"Geschlossen", "closedOn":"{day} geschlossen", "@closedOn":{ - "description":"Day", - "placeholders":{ - "day":{ - "type":"String", - "example":"Montag" - } + "description":"Day", + "placeholders":{ + "day":{ + "type":"String", + "example":"Montag" } - }, + } + }, "closedToday":"Heute geschlossen", "submitFeedback":"Feedback einreichen", "name":"Name", @@ -270,8 +274,11 @@ "submit":"Senden", "yourMessage":"Deine Nachricht...", "yourName":"Dein Name...", + "yourEmailTitle":"Deine E-Mail-Adresse", + "yourEmail":"deineemail@tum.de", "invalidName":"Ungültiger Name", "invalidMessage":"Ungültige Nachricht", + "invalidEmail":"Ungültige E-Mail-Adresse", "unableToSend":"Nachricht kann nicht gesendet werden!", "successfullySent":"Nachricht erfolgreich gesendet!\n Danke für Dein Feedback!", "signInToTumOnline":"1. Melden Dich bei TUMonline an\n", @@ -337,7 +344,25 @@ "unknownDirection":"Unbekannte Richtung", "showWeekends":"Wochenenden anzeigen", "color":"Farbe", - "resetAll":"Alles zurücksetzen", + "resetLogin":"Zurücksetzen & Anmelden", "resetPreferences":"Einstellungen zurücksetzen", - "mostSearchedRooms":"meistgesuche Räume" + "noPermission":"Keine Berechtigung", + "tokenNotConfirmed":"Token nicht bestätigt", + "loginNeeded":"Login benötigt", + "rateExceeded":"Anfragequote überschritten", + "limitReached":"Token-Limit erreicht, maximal 10 Token pro Benutzer erlaubt", + "noUserSpecified":"Kein Benutzer spezifiziert", + "noUserFound":"Kein Benutzer gefunden", + "personNotFound":"Person nicht gefunden", + "invalidSearch":"Ungültige Suche", + "unknownException":"Unbekannter Fehler", + "noPermissionRecovery":"Stelle sicher, dass du die richtigen Berechtigungen für deinen Token aktivierst!", + "tokenNotConfirmedRecovery":"Gehe zu TumOnline und bestätige deinen Token!", + "loginNeededRecovery":"Diese Funktion kann nur genutzt werden, wenn du dich anmeldest!", + "rateExceededRecovery":"Bitte versuche es später noch einmal!", + "limitReachedRecovery":"Bitte lösche einen deiner Token!", + "noUserSpecifiedRecovery":"Bitte gib deine Tum ID ein!", + "noUserFoundRecovery":"Überprüfe, ob du deine Tum ID richtig eingegeben hast!", + "personNotFoundRecovery":"Überprüfe, ob du den Namen richtig eingegeben hast!", + "invalidSearchRecovery":"Ein Suchbegriff mit weniger als 4 Zeichen darf keine Wildcards oder Sonderzeichen enthalten!" } diff --git a/lib/base/localization/app_en.arb b/lib/base/localization/app_en.arb index aa7e7a44..e01b0b15 100644 --- a/lib/base/localization/app_en.arb +++ b/lib/base/localization/app_en.arb @@ -49,13 +49,17 @@ "tuitionPaid":"Tuition Paid", "tuitionDueDate":"Due Date", "tuitionOpenAmount":"Open Amount", - "versionNumber":"Version {number}", + "versionNumber":"Version {number} ({build})", "@versionNumber":{ "description":"What version number the app is currently on", "placeholders":{ "number":{ "type":"String", "example":"-.-.-" + }, + "build":{ + "type":"String", + "example":"1" } } }, @@ -252,15 +256,15 @@ }, "closed":"Closed", "closedOn":"Closed on {day}", - "@closedOn":{ - "description":"Day", - "placeholders":{ - "day":{ - "type":"String", - "example":"Monday" - } - } - }, + "@closedOn":{ + "description":"Day", + "placeholders":{ + "day":{ + "type":"String", + "example":"Monday" + } + } + }, "closedToday":"Closed Today", "submitFeedback":"Submit Feedback", "name":"Name", @@ -270,8 +274,11 @@ "submit":"Submit", "yourMessage":"Your Message...", "yourName":"Your Name...", + "yourEmailTitle":"Your Email Address", + "yourEmail":"youremail@tum.de", "invalidName":"Invalid Name", "invalidMessage":"Invalid Message", + "invalidEmail":"Invalid Email Address", "unableToSend":"Unable to Send Message!", "successfullySent":"Message Successfully Sent!\n Thanks for Your Feedback!", "signInToTumOnline":"1. Sign in to TUMonline\n", @@ -337,7 +344,25 @@ "unknownDirection":"Unknown Direction", "showWeekends":"Show Weekends", "color":"Color", - "resetAll":"Reset All", + "resetLogin":"Reset & Login", "resetPreferences":"Reset Preferences", - "mostSearchedRooms":"Most Searched Rooms" + "noPermission":"No Permission", + "tokenNotConfirmed":"Token Not Confirmed", + "loginNeeded":"Login Needed", + "rateExceeded":"Request Rate Exceeded", + "limitReached":"Token limit Reached, Maximum 10 Tokens per User Allowed", + "noUserSpecified":"No User Specified", + "noUserFound":"No User Found", + "personNotFound":"Person Not Found", + "invalidSearch":"Invalid Search", + "unknownException":"Unknown Exception", + "noPermissionRecovery":"Make sure to enable the right permissions for your token!", + "tokenNotConfirmedRecovery":"Go to TumOnline and confirm your token!", + "loginNeededRecovery":"This feature can only be used if you log in!", + "rateExceededRecovery":"Please try again later!", + "limitReachedRecovery":"Please delete one of your tokens!", + "noUserSpecifiedRecovery":"Please enter your Tum ID!", + "noUserFoundRecovery":"Make sure you entered your Tum ID correctly!", + "personNotFoundRecovery":"Make sure you entered the name correctly!", + "invalidSearchRecovery":"A search string with less than 4 characters must not contain wildcards or special characters!" } diff --git a/lib/base/networking/apis/tumOnlineApi/tum_online_api_exception.dart b/lib/base/networking/apis/tumOnlineApi/tum_online_api_exception.dart index 3b2a178e..70511857 100644 --- a/lib/base/networking/apis/tumOnlineApi/tum_online_api_exception.dart +++ b/lib/base/networking/apis/tumOnlineApi/tum_online_api_exception.dart @@ -1,4 +1,8 @@ +import 'package:campus_flutter/base/extensions/context.dart'; import 'package:campus_flutter/base/networking/protocols/api_exception.dart'; +import 'package:campus_flutter/base/routing/routes.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:go_router/go_router.dart'; class TumOnlineApiException implements ApiException { final TumOnlineApiExceptionType tumOnlineApiExceptionType; @@ -42,52 +46,52 @@ class TumOnlineApiException implements ApiException { } @override - String get message { + String message(BuildContext context) { switch (tumOnlineApiExceptionType) { case TumOnlineApiExceptionNoPermission _: - return "No Permission"; + return context.localizations.noPermission; case TumOnlineApiExceptionTokenNotConfirmed _: - return "Token Not Confirmed"; + return context.localizations.tokenNotConfirmed; case TumOnlineApiExceptionInvalidToken _: - return "Token Invalid"; + return context.localizations.loginNeeded; case TumOnlineApiExceptionRequestRate _: - return "Request Rate Exceeded"; + return context.localizations.rateExceeded; case TumOnlineApiExceptionTokenLimitReached _: - return "Token limit Reached, Maximum 10 Tokens per User Allowed"; + return context.localizations.limitReached; case TumOnlineApiExceptionNoUserSpecified _: - return "No User Specified"; + return context.localizations.noUserSpecified; case TumOnlineApiExceptionNoUserFound _: - return "No User Found"; + return context.localizations.noUserFound; case TumOnlineApiExceptionPersonNotFound _: - return "Person Not Found"; + return context.localizations.personNotFound; case TumOnlineApiExceptionInvalidSearchString _: - return "Invalid Search"; + return context.localizations.invalidSearch; case TumOnlineApiExceptionUnknown _: - return "Unknown Exception"; + return context.localizations.unknownException; } } @override - String? get recoverySuggestion { + String? recoverySuggestion(BuildContext context) { switch (tumOnlineApiExceptionType) { case TumOnlineApiExceptionNoPermission _: - return "Make sure to enable the right permissions for your token!"; + return context.localizations.noPermissionRecovery; case TumOnlineApiExceptionTokenNotConfirmed _: - return "Go to TumOnline and confirm your token!"; + return context.localizations.tokenNotConfirmedRecovery; case TumOnlineApiExceptionInvalidToken _: - return "Try creating a new token!"; + return context.localizations.loginNeededRecovery; case TumOnlineApiExceptionRequestRate _: - return "Please try again later!"; + return context.localizations.rateExceededRecovery; case TumOnlineApiExceptionTokenLimitReached _: - return "Please delete one of your tokens!"; + return context.localizations.limitReachedRecovery; case TumOnlineApiExceptionNoUserSpecified _: - return "Please enter your Tum ID!"; + return context.localizations.noUserSpecifiedRecovery; case TumOnlineApiExceptionNoUserFound _: - return "Make sure you entered your Tum ID correctly!"; + return context.localizations.noUserFoundRecovery; case TumOnlineApiExceptionPersonNotFound _: - return "Make sure you entered the name correctly!"; + return context.localizations.personNotFoundRecovery; case TumOnlineApiExceptionInvalidSearchString _: - return "A search string with less than 4 characters must not contain wildcards or special characters!"; + return context.localizations.invalidSearchRecovery; case TumOnlineApiExceptionUnknown unknown: return unknown.message; } @@ -97,6 +101,26 @@ class TumOnlineApiException implements ApiException { String toString() { return "TumOnlineException: $message"; } + + @override + Function()? overwriteRetry(BuildContext context) { + switch (tumOnlineApiExceptionType) { + case TumOnlineApiExceptionInvalidToken _: + return (() => context.go(onboarding)); + default: + return null; + } + } + + @override + String? overwriteRetryMessage(BuildContext context) { + switch (tumOnlineApiExceptionType) { + case TumOnlineApiExceptionInvalidToken _: + return context.localizations.login; + default: + return null; + } + } } /// exception types diff --git a/lib/base/networking/protocols/api_exception.dart b/lib/base/networking/protocols/api_exception.dart index 4e23fb62..6d5700a6 100644 --- a/lib/base/networking/protocols/api_exception.dart +++ b/lib/base/networking/protocols/api_exception.dart @@ -1,13 +1,16 @@ +import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; part 'api_exception.g.dart'; abstract class ApiException implements Exception { - final String message; - final String? recoverySuggestion; + String message(BuildContext context); + + String? recoverySuggestion(BuildContext context); + + Function()? overwriteRetry(BuildContext context); - @pragma("vm:entry-point") - ApiException([this.message = "", this.recoverySuggestion]); + String? overwriteRetryMessage(BuildContext context); } @JsonSerializable() diff --git a/lib/base/routing/router.dart b/lib/base/routing/router.dart index 4c2e17c8..375ec242 100644 --- a/lib/base/routing/router.dart +++ b/lib/base/routing/router.dart @@ -152,13 +152,9 @@ final _router = GoRouter( ), GoRoute( path: lectureDetails, - builder: (context, state) { - final data = state as (Lecture?, CalendarEvent?); - return LectureDetailsScaffold( - lecture: data.$1, - event: data.$2, - ); - }, + builder: (context, state) => LectureDetailsScaffold( + lecture: state.extra as Lecture?, + ), ), GoRoute( path: calendarDetails, diff --git a/lib/base/util/directions_launcher.dart b/lib/base/util/map_launcher.dart similarity index 90% rename from lib/base/util/directions_launcher.dart rename to lib/base/util/map_launcher.dart index e4e9a8a1..4e5aef1e 100644 --- a/lib/base/util/directions_launcher.dart +++ b/lib/base/util/map_launcher.dart @@ -43,11 +43,10 @@ Future showDirectionsDialog( title: Text( context.localizations.openIn(value[index].mapName), ), - onTap: () => MapLauncher.showDirections( + onTap: () => MapLauncher.showMarker( mapType: value[index].mapType, - directionsMode: DirectionsMode.walking, - destinationTitle: name, - destination: Coords(location.latitude, location.longitude), + title: name, + coords: Coords(location.latitude, location.longitude), ), ), separatorBuilder: (context, index) => const PaddedDivider(), diff --git a/lib/calendarComponent/model/calendar_data_source.dart b/lib/calendarComponent/model/calendar_data_source.dart index 14428b8f..42397435 100644 --- a/lib/calendarComponent/model/calendar_data_source.dart +++ b/lib/calendarComponent/model/calendar_data_source.dart @@ -34,6 +34,6 @@ class MeetingDataSource extends CalendarDataSource { @override Color getColor(int index) { final appointment = appointments![index]! as CalendarEvent; - return appointment.getColor(context); + return appointment.getColor(); } } diff --git a/lib/calendarComponent/model/calendar_event.dart b/lib/calendarComponent/model/calendar_event.dart index 7d374f63..a09cb28f 100644 --- a/lib/calendarComponent/model/calendar_event.dart +++ b/lib/calendarComponent/model/calendar_event.dart @@ -1,4 +1,4 @@ -import 'package:campus_flutter/base/enums/calendar_event_type.dart'; +import 'package:campus_flutter/base/theme/constants.dart'; import 'package:campus_flutter/searchComponent/model/comparison_token.dart'; import 'package:campus_flutter/searchComponent/protocols/searchable.dart'; import 'package:campus_flutter/base/extensions/context.dart'; @@ -65,36 +65,13 @@ class CalendarEvent extends Searchable { return status == "CANCEL"; } - CalendarEventType get type { - if (isCanceled) { - return CalendarEventType.canceled; - } else if (title.endsWith("VO") || - title.endsWith("VU") || - title.endsWith("VI")) { - return CalendarEventType.lecture; - } else if (title.endsWith("UE")) { - return CalendarEventType.exercise; - } else { - return CalendarEventType.other; - } - } - void setColor(Color? color) { this.color = color?.value; } - Color getColor(BuildContext context) { + Color getColor() { if (color == null) { - switch (type) { - case CalendarEventType.canceled: - return Colors.red; - case CalendarEventType.lecture: - return Colors.green; - case CalendarEventType.exercise: - return Colors.orange; - default: - return context.primaryColor; - } + return primaryLightColor; } else { return Color(color!); } diff --git a/lib/calendarComponent/viewModels/calendar_viewmodel.dart b/lib/calendarComponent/viewModels/calendar_viewmodel.dart index fcb19fa3..d2726202 100644 --- a/lib/calendarComponent/viewModels/calendar_viewmodel.dart +++ b/lib/calendarComponent/viewModels/calendar_viewmodel.dart @@ -32,6 +32,7 @@ class CalendarViewModel { element.setColor(eventColor); } } + response.$2.removeWhere((element) => element.isCanceled); events.add(response.$2); updateHomeWidget(response.$2); }, diff --git a/lib/calendarComponent/views/calendars_view.dart b/lib/calendarComponent/views/calendars_view.dart index ad0a709a..33628b37 100644 --- a/lib/calendarComponent/views/calendars_view.dart +++ b/lib/calendarComponent/views/calendars_view.dart @@ -59,12 +59,10 @@ class _CalendarsViewState extends ConsumerState child: Row( children: [ TextButton( - onPressed: () { - setState(() { - _selectedCalendarTab = 0; - dayController.displayDate = DateTime.now(); - }); - }, + onPressed: () => ref.read(selectedDate.notifier).state = ( + DateTime.now(), + null, + ), child: Text( context.localizations.calendarViewToday, style: Theme.of(context).textTheme.titleMedium, @@ -126,7 +124,7 @@ class _CalendarsViewState extends ConsumerState return ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.fullScreen, - retry: ref.read(calendarViewModel).fetch, + retry: (() => ref.read(calendarViewModel).fetch(true)), ); } else { return DelayedLoadingIndicator( @@ -158,6 +156,12 @@ class _CalendarsViewState extends ConsumerState weekController.selectedDate = state.$1; weekController.displayDate = state.$1; default: + dayController.selectedDate = state.$1; + dayController.displayDate = state.$1; + weekController.selectedDate = state.$1; + weekController.displayDate = state.$1; + monthController.selectedDate = state.$1; + monthController.displayDate = state.$1; } } } diff --git a/lib/calendarComponent/views/custom_event_view.dart b/lib/calendarComponent/views/custom_event_view.dart index 61a53f71..32b2a41c 100644 --- a/lib/calendarComponent/views/custom_event_view.dart +++ b/lib/calendarComponent/views/custom_event_view.dart @@ -37,7 +37,7 @@ class CustomEventView extends ConsumerWidget { _infoEntry( context.localizations.color, ColorPickerView( - color: calendarEvent.getColor(context), + color: calendarEvent.getColor(), onColorChanged: (color) { ref.read(calendarViewModel).setEventColor( calendarEvent.lvNr ?? calendarEvent.id, diff --git a/lib/calendarComponent/views/event_creation_form_field.dart b/lib/calendarComponent/views/event_creation_form_field.dart index 8514830d..95307d21 100644 --- a/lib/calendarComponent/views/event_creation_form_field.dart +++ b/lib/calendarComponent/views/event_creation_form_field.dart @@ -48,6 +48,8 @@ class EventCreationFormField extends ConsumerWidget { onChanged: (value) => ref .read(calendarAdditionViewModel(calendarEvent)) .checkValidity(), + onTapOutside: (_) => + FocusManager.instance.primaryFocus?.unfocus(), ), ), ), diff --git a/lib/calendarComponent/views/event_creation_view.dart b/lib/calendarComponent/views/event_creation_view.dart index aefb5e2c..a2e55b0b 100644 --- a/lib/calendarComponent/views/event_creation_view.dart +++ b/lib/calendarComponent/views/event_creation_view.dart @@ -1,4 +1,5 @@ import 'package:campus_flutter/base/extensions/context.dart'; +import 'package:campus_flutter/base/routing/routes.dart'; import 'package:campus_flutter/base/util/custom_back_button.dart'; import 'package:campus_flutter/calendarComponent/model/calendar_event.dart'; import 'package:campus_flutter/calendarComponent/viewModels/calendar_addition_viewmodel.dart'; @@ -106,7 +107,7 @@ class EventCreationView extends ConsumerWidget { .saveEvent() .then((value) { ref.invalidate(calendarAdditionViewModel); - context.pop(); + context.canPop() ? context.pop() : context.go(calendar); }) : null, child: Text(context.localizations.submit), diff --git a/lib/calendarComponent/views/homeWidget/calendar_widget_event_view.dart b/lib/calendarComponent/views/homeWidget/calendar_widget_event_view.dart index 12cd9516..328e5166 100644 --- a/lib/calendarComponent/views/homeWidget/calendar_widget_event_view.dart +++ b/lib/calendarComponent/views/homeWidget/calendar_widget_event_view.dart @@ -54,7 +54,7 @@ class CalendarHomeWidgetEventView extends ConsumerWidget { decoration: BoxDecoration( border: Border( left: BorderSide( - color: calendarEvent.getColor(context), + color: calendarEvent.getColor(), width: 2.0, ), ), diff --git a/lib/calendarComponent/views/homeWidget/calendar_widget_view.dart b/lib/calendarComponent/views/homeWidget/calendar_widget_view.dart index 15c42814..ef4c6ae4 100644 --- a/lib/calendarComponent/views/homeWidget/calendar_widget_view.dart +++ b/lib/calendarComponent/views/homeWidget/calendar_widget_view.dart @@ -45,7 +45,7 @@ class _CalendarHomeWidgetView extends ConsumerState { child: ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.textOnly, - retry: ref.read(calendarViewModel).fetch, + retry: (() => ref.read(calendarViewModel).fetch(true)), ), ); } else { diff --git a/lib/departuresComponent/views/departures_details_view.dart b/lib/departuresComponent/views/departures_details_view.dart index 9869f94e..faaaa45b 100644 --- a/lib/departuresComponent/views/departures_details_view.dart +++ b/lib/departuresComponent/views/departures_details_view.dart @@ -2,7 +2,7 @@ import 'package:campus_flutter/base/enums/error_handling_view_type.dart'; import 'package:campus_flutter/base/util/card_with_padding.dart'; import 'package:campus_flutter/base/util/custom_back_button.dart'; import 'package:campus_flutter/base/util/delayed_loading_indicator.dart'; -import 'package:campus_flutter/base/util/directions_launcher.dart'; +import 'package:campus_flutter/base/util/map_launcher.dart'; import 'package:campus_flutter/base/util/icon_text.dart'; import 'package:campus_flutter/base/util/last_updated_text.dart'; import 'package:campus_flutter/base/errorHandling/error_handling_router.dart'; @@ -140,7 +140,7 @@ class _DeparturesDetailsViewState extends ConsumerState { return ErrorHandlingRouter( error: widget.snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.fullScreen, - retry: ref.read(departureViewModel).fetch, + retry: (() => ref.read(departureViewModel).fetch(true)), ); } else { return DelayedLoadingIndicator(name: context.localizations.departures); diff --git a/lib/departuresComponent/views/homeWidget/departures_widget_view.dart b/lib/departuresComponent/views/homeWidget/departures_widget_view.dart index e448e0b6..c6330ab6 100644 --- a/lib/departuresComponent/views/homeWidget/departures_widget_view.dart +++ b/lib/departuresComponent/views/homeWidget/departures_widget_view.dart @@ -84,7 +84,7 @@ class _DeparturesHomeWidgetState extends ConsumerState { return ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.textOnly, - retry: ref.read(departureViewModel).fetch, + retry: (() => ref.read(departureViewModel).fetch(true)), ); } else { return DelayedLoadingIndicator( diff --git a/lib/feedbackComponent/viewModels/feedback_viewmodel.dart b/lib/feedbackComponent/viewModels/feedback_viewmodel.dart index 9f2a764d..773cdc94 100644 --- a/lib/feedbackComponent/viewModels/feedback_viewmodel.dart +++ b/lib/feedbackComponent/viewModels/feedback_viewmodel.dart @@ -19,7 +19,9 @@ final feedbackViewModel = Provider((ref) => FeedbackViewModel(ref)); class FeedbackViewModel { BehaviorSubject shareLocation = BehaviorSubject.seeded(false); BehaviorSubject activeButton = BehaviorSubject.seeded(false); + BehaviorSubject showEmailTextField = BehaviorSubject.seeded(false); BehaviorSubject validName = BehaviorSubject.seeded(null); + BehaviorSubject validEmail = BehaviorSubject.seeded(null); BehaviorSubject validMessage = BehaviorSubject.seeded(null); final TextEditingController name = TextEditingController(); final TextEditingController emailAddress = TextEditingController(); @@ -35,6 +37,8 @@ class FeedbackViewModel { name.text = personDetails.fullName; emailAddress.text = personDetails.email; validName.add(true); + } else { + showEmailTextField.add(true); } } @@ -113,9 +117,24 @@ class FeedbackViewModel { validName.add(name.value.text.isNotEmpty); } + void checkEmailValidity() { + final RegExp validEmailRegex = RegExp(r"^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$"); + if (emailAddress.value.text.isNotEmpty) { + if (validEmailRegex.hasMatch(emailAddress.value.text)) { + validEmail.add(true); + } else { + validEmail.add(false); + } + } + checkButton(); + } + void checkButton() { - activeButton - .add((validName.value ?? false) && (validMessage.value ?? false)); + activeButton.add( + (validName.value ?? false) && + (validEmail.value ?? false) && + (validMessage.value ?? false), + ); } void clearForm() { @@ -124,7 +143,9 @@ class FeedbackViewModel { message.text = ""; shareLocation.add(false); activeButton.add(false); + showEmailTextField.add(false); validMessage.add(null); validName.add(null); + validEmail.add(null); } } diff --git a/lib/feedbackComponent/views/feedback_form_view.dart b/lib/feedbackComponent/views/feedback_form_view.dart index 33f5b8e1..73a8fe2d 100644 --- a/lib/feedbackComponent/views/feedback_form_view.dart +++ b/lib/feedbackComponent/views/feedback_form_view.dart @@ -58,6 +58,7 @@ class _FeedbackFormViewState extends ConsumerState { invalidMessage: context.localizations.invalidName, decorationMessage: context.localizations.yourName, ), + optionalEmailTextField(), FeedbackTextField( title: context.localizations.message, textEditingController: ref.read(feedbackViewModel).message, @@ -98,4 +99,25 @@ class _FeedbackFormViewState extends ConsumerState { ), ); } + + Widget optionalEmailTextField() { + return StreamBuilder( + stream: ref.watch(feedbackViewModel).showEmailTextField, + builder: (context, snapshot) { + if (snapshot.hasData && snapshot.data!) { + return FeedbackTextField( + title: context.localizations.yourEmailTitle, + textEditingController: ref.read(feedbackViewModel).emailAddress, + validInput: ref.watch(feedbackViewModel).validEmail, + onChanged: (text) => + ref.read(feedbackViewModel).checkEmailValidity(), + invalidMessage: context.localizations.invalidEmail, + decorationMessage: context.localizations.yourEmail, + ); + } else { + return const SizedBox.shrink(); + } + }, + ); + } } diff --git a/lib/gradeComponent/views/grades_view.dart b/lib/gradeComponent/views/grades_view.dart index 3773f8cf..7a5d8cf4 100644 --- a/lib/gradeComponent/views/grades_view.dart +++ b/lib/gradeComponent/views/grades_view.dart @@ -62,7 +62,7 @@ class _GradesViewState extends ConsumerState return ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.fullScreen, - retry: ref.read(gradeViewModel).fetch, + retry: (() => ref.read(gradeViewModel).fetch(true)), ); } else { return DelayedLoadingIndicator(name: context.localizations.grades); diff --git a/lib/homeComponent/contactComponent/views/unauthorized_view.dart b/lib/homeComponent/contactComponent/views/unauthorized_view.dart index 8b792eef..2bc63967 100644 --- a/lib/homeComponent/contactComponent/views/unauthorized_view.dart +++ b/lib/homeComponent/contactComponent/views/unauthorized_view.dart @@ -1,25 +1,31 @@ +import 'package:campus_flutter/base/routing/routes.dart'; import 'package:flutter/material.dart'; import 'package:campus_flutter/base/extensions/context.dart'; +import 'package:go_router/go_router.dart'; class UnauthorizedView extends StatelessWidget { const UnauthorizedView({super.key}); @override Widget build(BuildContext context) { - return Row( - children: [ - const CircleAvatar( - backgroundImage: - AssetImage('assets/images/placeholders/portrait_placeholder.png'), - radius: 50, - ), - const Spacer(), - Text( - context.localizations.notLoggedIn, - style: Theme.of(context).textTheme.titleLarge, - ), - const Spacer(flex: 2), - ], + return InkWell( + onTap: () => context.push(onboarding), + child: Row( + children: [ + const CircleAvatar( + backgroundImage: AssetImage( + 'assets/images/placeholders/portrait_placeholder.png', + ), + radius: 50, + ), + const Spacer(), + Text( + context.localizations.notLoggedIn, + style: Theme.of(context).textTheme.titleLarge, + ), + const Spacer(flex: 2), + ], + ), ); } } diff --git a/lib/lectureComponent/views/lecture_details_view.dart b/lib/lectureComponent/views/lecture_details_view.dart index 3cb66e71..5117f02c 100644 --- a/lib/lectureComponent/views/lecture_details_view.dart +++ b/lib/lectureComponent/views/lecture_details_view.dart @@ -37,14 +37,6 @@ class LectureDetailsScaffold extends ConsumerStatefulWidget { class _LectureDetailsScaffoldState extends ConsumerState { - late Color selectedColor; - - @override - void didChangeDependencies() { - selectedColor = widget.event?.getColor(context) ?? Colors.red; - super.didChangeDependencies(); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -54,7 +46,7 @@ class _LectureDetailsScaffoldState Padding( padding: EdgeInsets.symmetric(horizontal: context.padding), child: ColorPickerView( - color: widget.event?.getColor(context) ?? context.primaryColor, + color: widget.event?.getColor() ?? context.primaryColor, onColorChanged: (color) { if (widget.event != null) { ref.read(calendarViewModel).setEventColor( @@ -114,7 +106,7 @@ class _LectureDetailsViewState extends ConsumerState { return ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.fullScreen, - retry: ref.read(viewModel).fetch, + retry: (() => ref.read(viewModel).fetch(true)), ); } else { return DelayedLoadingIndicator( diff --git a/lib/lectureComponent/views/lecture_view.dart b/lib/lectureComponent/views/lecture_view.dart index 29ef2121..23dfcf22 100644 --- a/lib/lectureComponent/views/lecture_view.dart +++ b/lib/lectureComponent/views/lecture_view.dart @@ -38,7 +38,7 @@ class LectureView extends ConsumerWidget { _subtitle(lecture.speaker!, Icons.person, context), ], ), - onTap: () => context.push(lectureDetails, extra: (lecture, null)), + onTap: () => context.push(lectureDetails, extra: lecture), ); } diff --git a/lib/lectureComponent/views/lectures_view.dart b/lib/lectureComponent/views/lectures_view.dart index 54f5cb6b..9b42fe43 100644 --- a/lib/lectureComponent/views/lectures_view.dart +++ b/lib/lectureComponent/views/lectures_view.dart @@ -64,7 +64,7 @@ class _LecturesViewState extends ConsumerState return ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.fullScreen, - retry: ref.read(lectureViewModel).fetch, + retry: (() => ref.read(lectureViewModel).fetch(true)), ); } else { return DelayedLoadingIndicator(name: context.localizations.lectures); diff --git a/lib/main.dart b/lib/main.dart index 9b1d0873..7595077f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:campus_flutter/base/enums/appearance.dart'; import 'package:campus_flutter/base/enums/shortcut_item.dart'; +import 'package:campus_flutter/base/networking/cache/cache_entry.dart'; import 'package:campus_flutter/base/util/enum_parser.dart'; import 'package:campus_flutter/base/networking/base/grpc_client.dart'; import 'package:campus_flutter/base/networking/base/connection_checker.dart'; @@ -31,8 +32,6 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:quick_actions/quick_actions.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'base/networking/cache/cache_entry.dart'; - final getIt = GetIt.instance; final customLocale = StateProvider((ref) => null); final appearance = StateProvider((ref) => Appearance.system); @@ -63,12 +62,7 @@ Future _initializeFirebase() async { Future _initializeNetworkingClients() async { final directory = await getApplicationDocumentsDirectory(); - final isar = await Isar.open( - [ - CacheEntrySchema, - ], - directory: directory.path, - ); + final isar = await Isar.open([CacheEntrySchema], directory: directory.path); getIt.registerSingleton(RestClient(isar)); getIt.registerSingleton(await GrpcClient.createGrpcClient(isar)); } diff --git a/lib/movieComponent/views/homeWidget/movies_widget_view.dart b/lib/movieComponent/views/homeWidget/movies_widget_view.dart index e6a19423..ff405880 100644 --- a/lib/movieComponent/views/homeWidget/movies_widget_view.dart +++ b/lib/movieComponent/views/homeWidget/movies_widget_view.dart @@ -54,7 +54,7 @@ class _MoviesHomeWidgetState extends ConsumerState { child: ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.textOnly, - retry: ref.read(movieViewModel).fetch, + retry: (() => ref.read(movieViewModel).fetch(true)), ), ); } else { diff --git a/lib/navigaTumComponent/model/navigatum_navigation_details.dart b/lib/navigaTumComponent/model/navigatum_navigation_details.dart index b9da59b4..5cb76f3c 100644 --- a/lib/navigaTumComponent/model/navigatum_navigation_details.dart +++ b/lib/navigaTumComponent/model/navigatum_navigation_details.dart @@ -20,6 +20,9 @@ class NavigaTumNavigationDetails { final NavigaTumNavigationCoordinates coordinates; final NavigaTumNavigationMaps maps; + bool get hasCoordinates => + coordinates.latitude != null && coordinates.longitude != null; + NavigaTumNavigationDetails( this.id, this.name, diff --git a/lib/navigaTumComponent/views/navigatum_room_view.dart b/lib/navigaTumComponent/views/navigatum_room_view.dart index 1c6c5232..bca309f6 100644 --- a/lib/navigaTumComponent/views/navigatum_room_view.dart +++ b/lib/navigaTumComponent/views/navigatum_room_view.dart @@ -1,7 +1,9 @@ +import 'package:campus_flutter/base/classes/location.dart'; import 'package:campus_flutter/base/enums/error_handling_view_type.dart'; import 'package:campus_flutter/base/util/custom_back_button.dart'; import 'package:campus_flutter/base/util/delayed_loading_indicator.dart'; import 'package:campus_flutter/base/errorHandling/error_handling_router.dart'; +import 'package:campus_flutter/base/util/map_launcher.dart'; import 'package:campus_flutter/navigaTumComponent/model/navigatum_navigation_details.dart'; import 'package:campus_flutter/navigaTumComponent/viewModels/navigatum_details_viewmodel.dart'; import 'package:campus_flutter/navigaTumComponent/views/navigatum_room_details_view.dart'; @@ -11,23 +13,55 @@ import 'package:campus_flutter/base/extensions/context.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -class NavigaTumRoomScaffold extends StatelessWidget { +class NavigaTumRoomScaffold extends ConsumerWidget { const NavigaTumRoomScaffold({super.key, required this.id}); final String id; @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar( leading: const CustomBackButton(), title: Text(context.localizations.roomDetails), + actions: [ + StreamBuilder( + stream: ref.watch(navigaTumDetailsViewModel(id)).details, + builder: (context, snapshot) { + if (snapshot.hasData && snapshot.data!.hasCoordinates) { + return _directionsButton(snapshot.data!, context); + } else { + return const SizedBox.shrink(); + } + }, + ), + ], ), body: NavigaTumRoomView( id: id, ), ); } + + Widget _directionsButton( + NavigaTumNavigationDetails details, + BuildContext context, + ) { + return IconButton( + onPressed: () => showDirectionsDialog( + details.name, + Location( + latitude: details.coordinates.latitude!, + longitude: details.coordinates.longitude!, + ), + context, + ), + icon: Icon( + Icons.directions, + color: context.theme.primaryColor, + ), + ); + } } class NavigaTumRoomView extends ConsumerStatefulWidget { @@ -70,8 +104,9 @@ class _NavigaTumRoomState extends ConsumerState { return ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.fullScreen, - retryWithContext: - ref.read(navigaTumDetailsViewModel(widget.id)).fetchDetails, + retry: (() => ref + .read(navigaTumDetailsViewModel(widget.id)) + .fetchDetails(true, context)), ); } else { return DelayedLoadingIndicator( @@ -110,23 +145,25 @@ class _NavigaTumRoomState extends ConsumerState { NavigaTumNavigationDetails details, { bool isPortrait = true, }) { - return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (isPortrait) ...[ - _name(details.name), - _type(details.typeCommonName), + return Scrollbar( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (isPortrait) ...[ + _name(details.name), + _type(details.typeCommonName), + ], + NavigaTumRoomDetailsView( + id: details.id, + properties: details.additionalProperties.properties, + ), + if (isPortrait) + NavigaTumRoomBuildingView(coordinates: details.coordinates), + if ((details.maps.roomfinder?.available ?? []).isNotEmpty) + NavigaTumRoomMapsView(maps: ref.read(viewModel).getMaps()), ], - NavigaTumRoomDetailsView( - id: details.id, - properties: details.additionalProperties.properties, - ), - if (isPortrait) - NavigaTumRoomBuildingView(coordinates: details.coordinates), - if ((details.maps.roomfinder?.available ?? []).isNotEmpty) - NavigaTumRoomMapsView(maps: ref.read(viewModel).getMaps()), - ], + ), ), ); } diff --git a/lib/navigation_service.dart b/lib/navigation_service.dart index 3a9cfdc9..c0950e0f 100644 --- a/lib/navigation_service.dart +++ b/lib/navigation_service.dart @@ -100,7 +100,7 @@ class NavigationService { ), IconButton( onPressed: () => context.push(menuSettings), - icon: const Icon(Icons.menu), + icon: const Icon(Icons.settings), ), ]; } diff --git a/lib/newsComponent/views/homeWidget/news_widget_view.dart b/lib/newsComponent/views/homeWidget/news_widget_view.dart index ecd43fb7..065fd100 100644 --- a/lib/newsComponent/views/homeWidget/news_widget_view.dart +++ b/lib/newsComponent/views/homeWidget/news_widget_view.dart @@ -68,7 +68,7 @@ class _NewsWidgetViewState extends ConsumerState { child: ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.textOnly, - retry: ref.read(newsViewModel).fetch, + retry: (() => ref.read(newsViewModel).fetch(true)), ), ), ); diff --git a/lib/onboardingComponent/viewModels/onboarding_viewmodel.dart b/lib/onboardingComponent/viewModels/onboarding_viewmodel.dart index 28edba56..f9c8ba42 100644 --- a/lib/onboardingComponent/viewModels/onboarding_viewmodel.dart +++ b/lib/onboardingComponent/viewModels/onboarding_viewmodel.dart @@ -84,11 +84,12 @@ class OnboardingViewModel { Future checkLogin() async { return _storage.read(key: "token").then( (value) async { + final status = getIt().getOnboardingStatus(); if (value != null) { Api.tumToken = value; credentials.add(Credentials.tumId); return true; - } else if (getIt().getOnboardingStatus() != null) { + } else if (status != null && status) { credentials.add(Credentials.noTumId); return true; } else { diff --git a/lib/onboardingComponent/views/confirm_view.dart b/lib/onboardingComponent/views/confirm_view.dart index 9030c26e..72db2d99 100644 --- a/lib/onboardingComponent/views/confirm_view.dart +++ b/lib/onboardingComponent/views/confirm_view.dart @@ -225,7 +225,7 @@ class _ConfirmViewState extends ConsumerState { ); }, ).catchError((error) { - ScaffoldMessenger.of(context).showSnackBar( + ScaffoldMessenger.maybeOf(context)?.showSnackBar( SnackBar( duration: const Duration(seconds: 10), content: ErrorHandlingRouter( diff --git a/lib/onboardingComponent/views/login_view.dart b/lib/onboardingComponent/views/login_view.dart index 41392c5a..6e378c77 100644 --- a/lib/onboardingComponent/views/login_view.dart +++ b/lib/onboardingComponent/views/login_view.dart @@ -148,6 +148,7 @@ class _LoginViewState extends ConsumerState { } }, enableSuggestions: false, + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), ), ), const Padding(padding: EdgeInsets.symmetric(horizontal: 4.0)), @@ -169,6 +170,7 @@ class _LoginViewState extends ConsumerState { } }, enableSuggestions: false, + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), ), ), const Padding(padding: EdgeInsets.symmetric(horizontal: 4.0)), @@ -187,6 +189,7 @@ class _LoginViewState extends ConsumerState { } }, enableSuggestions: false, + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), ), ), const Spacer(), @@ -207,7 +210,7 @@ class _LoginViewState extends ConsumerState { ref.read(onboardingViewModel).requestLogin().then( (value) => context.push(confirm), onError: (error) { - ScaffoldMessenger.of(context).showSnackBar( + ScaffoldMessenger.maybeOf(context)?.showSnackBar( SnackBar( duration: const Duration(seconds: 10), content: ErrorHandlingRouter( diff --git a/lib/placesComponent/views/cafeterias/cafeteria_view.dart b/lib/placesComponent/views/cafeterias/cafeteria_view.dart index 0b606b3a..55ccbf54 100644 --- a/lib/placesComponent/views/cafeterias/cafeteria_view.dart +++ b/lib/placesComponent/views/cafeterias/cafeteria_view.dart @@ -2,7 +2,7 @@ import 'package:campus_flutter/base/classes/location.dart' as location; import 'package:campus_flutter/base/enums/error_handling_view_type.dart'; import 'package:campus_flutter/base/util/custom_back_button.dart'; import 'package:campus_flutter/base/util/delayed_loading_indicator.dart'; -import 'package:campus_flutter/base/util/directions_launcher.dart'; +import 'package:campus_flutter/base/util/map_launcher.dart'; import 'package:campus_flutter/base/util/info_row.dart'; import 'package:campus_flutter/base/errorHandling/error_handling_router.dart'; import 'package:campus_flutter/base/util/places_util.dart'; @@ -10,7 +10,6 @@ import 'package:campus_flutter/placesComponent/model/cafeterias/cafeteria.dart'; import 'package:campus_flutter/placesComponent/model/cafeterias/opening_hours.dart'; import 'package:campus_flutter/placesComponent/viewModels/cafeterias_viewmodel.dart'; import 'package:campus_flutter/placesComponent/views/cafeterias/dish_grid_view.dart'; -import 'package:campus_flutter/placesComponent/views/directions_button.dart'; import 'package:campus_flutter/placesComponent/views/map_widget.dart'; import 'package:campus_flutter/base/extensions/context.dart'; import 'package:collection/collection.dart'; @@ -189,11 +188,6 @@ class _CafeteriaViewState extends ConsumerState { zoom: 15, aspectRatio: 2, ), - DirectionsButton.latLng( - name: widget.cafeteria.name, - latitude: widget.cafeteria.location.latitude, - longitude: widget.cafeteria.location.longitude, - ), ]; } diff --git a/lib/placesComponent/views/cafeterias/cafeterias_view.dart b/lib/placesComponent/views/cafeterias/cafeterias_view.dart index 506a035f..be5285b5 100644 --- a/lib/placesComponent/views/cafeterias/cafeterias_view.dart +++ b/lib/placesComponent/views/cafeterias/cafeterias_view.dart @@ -94,7 +94,7 @@ class _CafeteriasState extends ConsumerState { return ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.fullScreen, - retry: ref.read(cafeteriasViewModel).fetch, + retry: (() => ref.read(cafeteriasViewModel).fetch(true)), ); } else { return DelayedLoadingIndicator( diff --git a/lib/placesComponent/views/directions_button.dart b/lib/placesComponent/views/directions_button.dart deleted file mode 100644 index e3bd241e..00000000 --- a/lib/placesComponent/views/directions_button.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:campus_flutter/base/classes/location.dart'; -import 'package:campus_flutter/base/util/directions_launcher.dart'; -import 'package:campus_flutter/base/util/icon_text.dart'; -import 'package:campus_flutter/base/extensions/context.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class DirectionsButton extends ConsumerWidget { - const DirectionsButton.latLng({ - super.key, - this.location, - this.name, - required this.latitude, - required this.longitude, - }); - - const DirectionsButton.location({ - super.key, - required this.location, - this.name, - this.latitude, - this.longitude, - }); - - final Location? location; - final String? name; - final double? latitude; - final double? longitude; - - @override - Widget build(BuildContext context, WidgetRef ref) { - return Padding( - padding: EdgeInsets.symmetric(horizontal: context.padding), - child: SizedBox( - height: 50, - width: double.infinity, - child: ElevatedButton( - onPressed: () => showDirectionsDialog( - name ?? "Destination", - Location( - latitude: location?.latitude ?? latitude!, - longitude: location?.longitude ?? longitude!, - ), - context, - ), - child: IconText( - iconData: Icons.directions, - label: context.localizations.showDirections, - ), - ), - ), - ); - } -} diff --git a/lib/placesComponent/views/homeWidget/cafeteria_widget_view.dart b/lib/placesComponent/views/homeWidget/cafeteria_widget_view.dart index e3b9c367..c083b770 100644 --- a/lib/placesComponent/views/homeWidget/cafeteria_widget_view.dart +++ b/lib/placesComponent/views/homeWidget/cafeteria_widget_view.dart @@ -108,8 +108,9 @@ class _CafeteriaWidgetViewState extends ConsumerState { height: 150, child: ErrorHandlingRouter( error: snapshot.error!, - errorHandlingViewType: ErrorHandlingViewType.descriptionOnly, - retry: ref.read(cafeteriasViewModel).fetchWidgetCafeteria, + errorHandlingViewType: ErrorHandlingViewType.textOnly, + retry: (() => + ref.read(cafeteriasViewModel).fetchWidgetCafeteria(true)), ), ), ); diff --git a/lib/placesComponent/views/places_screen.dart b/lib/placesComponent/views/places_screen.dart index d0d5ce46..73198304 100644 --- a/lib/placesComponent/views/places_screen.dart +++ b/lib/placesComponent/views/places_screen.dart @@ -40,7 +40,7 @@ class _PlacesScreenState extends ConsumerState return ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.fullScreen, - retry: ref.read(placesViewModel).fetch, + retry: (() => ref.read(placesViewModel).fetch(true)), ); } else { return DelayedLoadingIndicator( diff --git a/lib/placesComponent/views/studyGroups/study_room_group_scaffold.dart b/lib/placesComponent/views/studyGroups/study_room_group_scaffold.dart index 48d97d90..447e3bf0 100644 --- a/lib/placesComponent/views/studyGroups/study_room_group_scaffold.dart +++ b/lib/placesComponent/views/studyGroups/study_room_group_scaffold.dart @@ -1,4 +1,7 @@ +import 'package:campus_flutter/base/classes/location.dart'; +import 'package:campus_flutter/base/extensions/context.dart'; import 'package:campus_flutter/base/util/custom_back_button.dart'; +import 'package:campus_flutter/base/util/map_launcher.dart'; import 'package:campus_flutter/placesComponent/model/studyRooms/study_room_group.dart'; import 'package:campus_flutter/placesComponent/views/studyGroups/study_room_group_view.dart'; import 'package:flutter/material.dart'; @@ -33,7 +36,26 @@ class StudyRoomGroupScaffold extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( - appBar: AppBar(leading: const CustomBackButton()), + appBar: AppBar( + leading: const CustomBackButton(), + actions: [ + if (studyRoomGroup != null && studyRoomGroup?.coordinate != null) + IconButton( + onPressed: () => showDirectionsDialog( + studyRoomGroup!.name, + Location( + latitude: studyRoomGroup!.coordinate!.latitude, + longitude: studyRoomGroup!.coordinate!.longitude, + ), + context, + ), + icon: Icon( + Icons.directions, + color: context.theme.primaryColor, + ), + ), + ], + ), body: studyRoomGroup != null ? StudyRoomGroupView(studyRoomGroup, isSplitView) : StudyRoomGroupView(null, isSplitView), diff --git a/lib/placesComponent/views/studyGroups/study_room_group_view.dart b/lib/placesComponent/views/studyGroups/study_room_group_view.dart index 455a7cca..2ed61682 100644 --- a/lib/placesComponent/views/studyGroups/study_room_group_view.dart +++ b/lib/placesComponent/views/studyGroups/study_room_group_view.dart @@ -7,7 +7,6 @@ import 'package:campus_flutter/homeComponent/widgetComponent/views/widget_frame_ import 'package:campus_flutter/placesComponent/model/studyRooms/study_room.dart'; import 'package:campus_flutter/placesComponent/model/studyRooms/study_room_group.dart'; import 'package:campus_flutter/placesComponent/viewModels/study_rooms_viewmodel.dart'; -import 'package:campus_flutter/placesComponent/views/directions_button.dart'; import 'package:campus_flutter/placesComponent/views/map_widget.dart'; import 'package:campus_flutter/placesComponent/views/studyGroups/study_room_row_view.dart'; import 'package:collection/collection.dart'; @@ -82,7 +81,7 @@ class StudyRoomGroupView extends ConsumerWidget { return ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.fullScreen, - retry: ref.read(studyRoomsViewModel).fetch, + retry: (() => ref.read(studyRoomsViewModel).fetch(true)), ); } else { return const DelayedLoadingIndicator(name: "Study Rooms"); @@ -115,8 +114,6 @@ class StudyRoomGroupView extends ConsumerWidget { if (orientation == Orientation.portrait) ...[ if (studyRoomGroup?.coordinate != null) _portraitMap(studyRoomGroup, context), - if (studyRoomGroup?.coordinate != null) - _directionsButton(studyRoomGroup!), ], WidgetFrameView( title: context.localizations.rooms, @@ -147,13 +144,7 @@ class StudyRoomGroupView extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: Column( - children: [ - _landscapeMap(studyRoomGroup, context), - if (studyRoomGroup?.coordinate != null) - _directionsButton(studyRoomGroup!), - ], - ), + child: _landscapeMap(studyRoomGroup, context), ), Expanded( child: _body( @@ -218,12 +209,4 @@ class StudyRoomGroupView extends ConsumerWidget { aspectRatio: 2, ); } - - Widget _directionsButton(StudyRoomGroup studyRoomGroup) { - return DirectionsButton.latLng( - latitude: studyRoomGroup.coordinate!.latitude, - longitude: studyRoomGroup.coordinate!.longitude, - name: studyRoomGroup.name, - ); - } } diff --git a/lib/placesComponent/views/studyGroups/study_rooms_view.dart b/lib/placesComponent/views/studyGroups/study_rooms_view.dart index 59d56854..b79a5333 100644 --- a/lib/placesComponent/views/studyGroups/study_rooms_view.dart +++ b/lib/placesComponent/views/studyGroups/study_rooms_view.dart @@ -92,7 +92,7 @@ class _StudyRoomsViewState extends ConsumerState { return ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.fullScreen, - retry: ref.read(studyRoomsViewModel).fetch, + retry: (() => ref.read(studyRoomsViewModel).fetch(true)), ); } else { return DelayedLoadingIndicator( diff --git a/lib/searchComponent/views/appWideSearch/resultViews/calendar_search_result_view.dart b/lib/searchComponent/views/appWideSearch/resultViews/calendar_search_result_view.dart index 3b3c768c..e6103b90 100644 --- a/lib/searchComponent/views/appWideSearch/resultViews/calendar_search_result_view.dart +++ b/lib/searchComponent/views/appWideSearch/resultViews/calendar_search_result_view.dart @@ -40,7 +40,7 @@ class CalendarSearchResultView extends ConsumerWidget { ), ], ), - onTap: () => context.push(lectureDetails, extra: (null, calendarEvent)), + onTap: () => context.push(calendarDetails, extra: calendarEvent), ), ); } diff --git a/lib/searchComponent/views/appWideSearch/search_textfield_view.dart b/lib/searchComponent/views/appWideSearch/search_textfield_view.dart index 4e7ff113..3575ac9b 100644 --- a/lib/searchComponent/views/appWideSearch/search_textfield_view.dart +++ b/lib/searchComponent/views/appWideSearch/search_textfield_view.dart @@ -43,6 +43,7 @@ class _SearchTextFieldState extends ConsumerState { ) : null, ), + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), ), ); } diff --git a/lib/searchComponent/views/personRoomSearch/search_view.dart b/lib/searchComponent/views/personRoomSearch/search_view.dart index e2f22eb3..ca9d6dcc 100644 --- a/lib/searchComponent/views/personRoomSearch/search_view.dart +++ b/lib/searchComponent/views/personRoomSearch/search_view.dart @@ -29,7 +29,7 @@ class PersonRoomSearchScaffold extends ConsumerWidget { appBar: AppBar( leading: CustomBackButton( onPressed: () { - context.pop(); + context.canPop() ? context.pop() : context.go(search); if (isRoomSearch) { ref.read(navigaTumSearchViewModel).searchResults.add([]); } else { @@ -150,6 +150,7 @@ class _SearchViewState extends ConsumerState { ) : null, ), + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), ), ); } diff --git a/lib/settingsComponent/views/settings_view.dart b/lib/settingsComponent/views/settings_view.dart index c0ce5f62..c5e47f3a 100644 --- a/lib/settingsComponent/views/settings_view.dart +++ b/lib/settingsComponent/views/settings_view.dart @@ -68,7 +68,7 @@ class SettingsView extends ConsumerWidget { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) => widgets[index], - separatorBuilder: (context, index) => const PaddedDivider(), + separatorBuilder: (context, index) => const PaddedDivider(height: 0), itemCount: widgets.length, ), ), @@ -104,7 +104,7 @@ class SettingsView extends ConsumerWidget { textAlign: TextAlign.center, ) : Text( - context.localizations.resetAll, + context.localizations.resetLogin, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.red, fontWeight: FontWeight.w500, @@ -127,10 +127,13 @@ class SettingsView extends ConsumerWidget { builder: (context, snapshot) { if (snapshot.hasData) { return Text( - context.localizations.versionNumber(snapshot.data!.version), + context.localizations.versionNumber( + snapshot.data!.version, + snapshot.data!.buildNumber, + ), ); } else { - return Text(context.localizations.versionNumber("-.-.-")); + return Text(context.localizations.versionNumber("-.-.-", "-")); } }, ), diff --git a/lib/studentCardComponent/views/student_card_view.dart b/lib/studentCardComponent/views/student_card_view.dart index 980e1713..114141e9 100644 --- a/lib/studentCardComponent/views/student_card_view.dart +++ b/lib/studentCardComponent/views/student_card_view.dart @@ -45,7 +45,7 @@ class StudentCardView extends ConsumerWidget { return ErrorHandlingRouter( error: snapshot.error!, errorHandlingViewType: ErrorHandlingViewType.fullScreen, - retry: ref.read(studentCardViewModel).fetch, + retry: (() => ref.read(studentCardViewModel).fetch(true)), ); } else { return const DelayedLoadingIndicator(name: "StudentCard"); diff --git a/pubspec.lock b/pubspec.lock index ff266fb2..7a02cee4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "0cb43f83f36ba8cb20502dee0c205e3f3aafb751732d724aeac3f2e044212cc2" + sha256: "99b5dec989287c1aca71bf27339e0022b4dc3679225f442fb75790ef44535bf8" url: "https://pub.dev" source: hosted - version: "1.3.29" + version: "1.3.30" analyzer: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" async: dependency: transitive description: @@ -69,10 +69,10 @@ packages: dependency: transitive description: name: barcode - sha256: "91b143666f7bb13636f716b6d4e412e372ab15ff7969799af8c9e30a382e9385" + sha256: "1fe4a55344505850517ce72d4a3a7b9ccf51b0dc1631ee7e552f6eacc4947f96" url: "https://pub.dev" source: hosted - version: "2.2.6" + version: "2.2.7" barcode_widget: dependency: "direct main" description: @@ -285,10 +285,10 @@ packages: dependency: "direct main" description: name: dio - sha256: "0978e9a3e45305a80a7210dbeaf79d6ee8bee33f70c8e542dc654c952070217f" + sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5" url: "https://pub.dev" source: hosted - version: "5.4.2+1" + version: "5.4.3+1" fake_async: dependency: transitive description: @@ -317,10 +317,10 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: a864d1b6afd25497a3b57b016886d1763df52baaa69758a46723164de8d187fe + sha256: "6b1152a5af3b1cfe7e45309e96fc1aa14873f410f7aadb3878aa7812acfa7531" url: "https://pub.dev" source: hosted - version: "2.29.0" + version: "2.30.0" firebase_core_platform_interface: dependency: transitive description: @@ -341,18 +341,18 @@ packages: dependency: "direct main" description: name: firebase_crashlytics - sha256: a4e2c0ef8a595d2fbbaa325ffb15179583db3a01da8ffc2ecd97a46d76ae7546 + sha256: c39ad1df819130dbb3468e1ad4e6fcd09fa37f8bc1581e8881d515a54a181757 url: "https://pub.dev" source: hosted - version: "3.5.1" + version: "3.5.2" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: "897e8814773b83848ce8967b94d1ad5e780671253d3cd7862bf4a442f29db811" + sha256: "22492a34fa1405b57a6eb37c251434c38f780b88c9e87a9f35fa41189b2b2b61" url: "https://pub.dev" source: hosted - version: "3.6.29" + version: "3.6.30" fixnum: dependency: transitive description: @@ -416,10 +416,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "592dc01a18961a51c24ae5d963b724b2b7fa4a95c100fe8eb6ca8a5a4732cadf" + sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" url: "https://pub.dev" source: hosted - version: "2.0.18" + version: "2.0.19" flutter_riverpod: dependency: "direct main" description: @@ -562,10 +562,10 @@ packages: dependency: "direct main" description: name: get_it - sha256: "36524bfb3f0b4ec952c3202466fdd69ad1f7ac1dd9b0a7564177707e45bfaeb9" + sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 url: "https://pub.dev" source: hosted - version: "7.6.8" + version: "7.7.0" glob: dependency: transitive description: @@ -578,10 +578,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: f6ba8eed5fa831e461122de577d4a26674a1d836e2956abe6c0f6c4d952e6673 + sha256: "771c8feb40ad0ef639973d7ecf1b43d55ffcedb2207fd43fab030f5639e40446" url: "https://pub.dev" source: hosted - version: "13.2.3" + version: "13.2.4" google_identity_services_web: dependency: transitive description: @@ -602,10 +602,10 @@ packages: dependency: "direct main" description: name: google_maps_flutter - sha256: "982c2e22ec78d32701bfbd351311e8579a4952035381ac66462c0af11003107b" + sha256: c1972cbad779bc5346c49045f26ae45550a0958b1cbca5b524dd3c8954995d28 url: "https://pub.dev" source: hosted - version: "2.6.0" + version: "2.6.1" google_maps_flutter_android: dependency: transitive description: @@ -615,14 +615,13 @@ packages: source: hosted version: "2.7.0" google_maps_flutter_ios: - dependency: "direct overridden" + dependency: transitive description: - path: "packages/google_maps_flutter/google_maps_flutter_ios" - ref: main - resolved-ref: "33c1f4f32a84058e49f0eef62a7219405bccf398" - url: "https://github.com/jakobkoerber/packages.git" - source: git - version: "2.5.2" + name: google_maps_flutter_ios + sha256: e5132d17f051600d90d79d9f574b177c24231da702453a036db2490f9ced4646 + url: "https://pub.dev" + source: hosted + version: "2.6.0" google_maps_flutter_platform_interface: dependency: transitive description: @@ -643,10 +642,10 @@ packages: dependency: transitive description: name: googleapis_auth - sha256: "1401a9e55f9e0f565d3eebb18d990290f53a12d38a5f7f0230b112895778a85b" + sha256: befd71383a955535060acde8792e7efc11d2fccd03dd1d3ec434e85b68775938 url: "https://pub.dev" source: hosted - version: "1.5.1" + version: "1.6.0" graphs: dependency: transitive description: @@ -660,7 +659,7 @@ packages: description: path: "." ref: master - resolved-ref: "547b8cdc1cf3b799079ccca1349a781deb7e6c64" + resolved-ref: e631635424e41a5ed5bd993cd75ce8967d87772a url: "https://github.com/jakobkoerber/grpc-dart.git" source: git version: "3.2.4" @@ -668,10 +667,10 @@ packages: dependency: "direct main" description: name: home_widget - sha256: c58a9e6d3b94490f1a8d5ddcbeeeeebc79abd0befe5889c26b0713fb09be6857 + sha256: "29565bfee4b32eaf9e7e8b998d504618b779a74b2b1ac62dd4dac7468e66f1a3" url: "https://pub.dev" source: hosted - version: "0.4.1" + version: "0.5.0" html: dependency: transitive description: @@ -948,18 +947,18 @@ packages: dependency: "direct main" description: name: path_provider - sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "51f0d2c554cfbc9d6a312ab35152fc77e2f0b758ce9f1a444a3a1e5b8f3c6b7f" + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.4" path_provider_foundation: dependency: transitive description: @@ -1068,10 +1067,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333" + sha256: "79fbafed02cfdbe85ef3fd06c7f4bc2cbcba0177e61b765264853d4253b21744" url: "https://pub.dev" source: hosted - version: "3.8.0" + version: "3.9.0" pool: dependency: transitive description: @@ -1124,10 +1123,10 @@ packages: dependency: transitive description: name: quick_actions_android - sha256: adb42f20a46b22fee4caef421c00ff9eb209f9d441010bc5d6e9afa824288cf6 + sha256: "58b9b36cad2fc5c5a6752e5eafdb20ab00697f11278ca7f206ecd7b85ec6c101" url: "https://pub.dev" source: hosted - version: "1.0.10" + version: "1.0.11" quick_actions_ios: dependency: transitive description: @@ -1172,18 +1171,18 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_foundation: dependency: transitive description: @@ -1377,34 +1376,34 @@ packages: dependency: "direct main" description: name: syncfusion_flutter_calendar - sha256: "74a0ea2350dec01b14a9e2df511d7bfc079f3546c3b623c9bfe02b9af9061d8b" + sha256: "24070f4ca1558d994e8232b250a2ce3edc920845c8b0f3597895afc8338c901e" url: "https://pub.dev" source: hosted - version: "25.1.39" + version: "25.1.40" syncfusion_flutter_charts: dependency: "direct main" description: name: syncfusion_flutter_charts - sha256: d80656b44dfc24e42affae9e2ad0741c7480454ca414df7541c3def387299d27 + sha256: f3327077c4816573895b15545ea85b12c6a6cab7f8c2cde1a7eec05c36bfb432 url: "https://pub.dev" source: hosted - version: "25.1.39+1" + version: "25.1.40" syncfusion_flutter_core: dependency: "direct main" description: name: syncfusion_flutter_core - sha256: "159b125f55d95534c0c39a5efd644f50a310821191946e15eac50a49032fb42c" + sha256: "8e19260d292ed77e1cbd4ee1baafb3c9486079cad856e7891478131d25076039" url: "https://pub.dev" source: hosted - version: "25.1.39" + version: "25.1.40" syncfusion_flutter_datepicker: dependency: "direct main" description: name: syncfusion_flutter_datepicker - sha256: "742bbe6aba9df10553a177e3f6e4a3c07bbea39244e3af3f898864823b003de9" + sha256: "04e7cc46b2c0ebfcd76e6082284f7daaf4731df37ddcc876d3073159a6e55d25" url: "https://pub.dev" source: hosted - version: "25.1.39" + version: "25.1.40" synchronized: dependency: transitive description: @@ -1497,18 +1496,18 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" + sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e" url: "https://pub.dev" source: hosted - version: "6.2.5" + version: "6.2.6" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 + sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775" url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.3.1" url_launcher_ios: dependency: transitive description: @@ -1545,10 +1544,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d" + sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" url_launcher_windows: dependency: transitive description: @@ -1601,26 +1600,26 @@ packages: dependency: "direct main" description: name: video_player - sha256: efa2e24042166906ddf836dd131258d0371d0009cdf0476f6a83fd992a17f5d0 + sha256: db6a72d8f4fd155d0189845678f55ad2fd54b02c10dcafd11c068dbb631286c0 url: "https://pub.dev" source: hosted - version: "2.8.5" + version: "2.8.6" video_player_android: dependency: transitive description: name: video_player_android - sha256: "4dd9b8b86d70d65eecf3dcabfcdfbb9c9115d244d022654aba49a00336d540c2" + sha256: "134e1ad410d67e18a19486ed9512c72dfc6d8ffb284d0e8f2e99e903d1ba8fa3" url: "https://pub.dev" source: hosted - version: "2.4.12" + version: "2.4.14" video_player_avfoundation: dependency: transitive description: name: video_player_avfoundation - sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed" + sha256: "00c49b1d68071341397cf760b982c1e26ed9232464c8506ee08378a5cca5070d" url: "https://pub.dev" source: hosted - version: "2.5.6" + version: "2.5.7" video_player_platform_interface: dependency: transitive description: @@ -1689,10 +1688,10 @@ packages: dependency: transitive description: name: win32_registry - sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" + sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 37af1336..3cd39a4f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: campus_flutter description: "A new Flutter project." publish_to: 'none' -version: 5.0.2+1 +version: 5.0.3+1 environment: sdk: '>=3.2.3 <4.0.0' @@ -37,7 +37,7 @@ dependencies: flutter_staggered_grid_view: ^0.7.0 flutter_svg: ^2.0.9 flutter_linkify: ^6.0.0 - home_widget: "0.4.1" + home_widget: ^0.5.0 auto_size_text: ^3.0.0 quick_actions: ^1.0.7 flutter_native_splash: ^2.2.19 @@ -86,11 +86,6 @@ dependency_overrides: git: url: https://github.com/jakobkoerber/grpc-dart.git ref: master - google_maps_flutter_ios: - git: - url: https://github.com/jakobkoerber/packages.git - path: packages/google_maps_flutter/google_maps_flutter_ios - ref: main dev_dependencies: # code generation