diff --git a/.github/workflows/deploy_beta.yml b/.github/workflows/deploy_beta.yml index d4b32150..00d939ed 100644 --- a/.github/workflows/deploy_beta.yml +++ b/.github/workflows/deploy_beta.yml @@ -29,6 +29,8 @@ jobs: with: channel: stable cache: true + cache-key: 'flutter-:os:-:channel:-:version:-:arch:' + cache-path: '${{ runner.tool_cache }}/flutter/:channel:-:arch:' - if: matrix.platform == 'android' uses: actions/setup-java@v3 @@ -36,15 +38,10 @@ jobs: distribution: 'corretto' java-version: '17' - - name: Cache pub dependencies - uses: actions/cache@v3 - with: - path: ${{ env.FLUTTER_HOME }}/.pub-cache - key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }} - restore-keys: ${{ runner.os }}-pub- - - name: Install Flutter Packages - run: flutter pub get + run: | + flutter config --no-analytics + flutter pub get - if: matrix.platform == 'ios' name: Install CocoaPods diff --git a/.github/workflows/lint_test_build.yml b/.github/workflows/lint_test_build.yml index 8c2a5432..afadfb02 100644 --- a/.github/workflows/lint_test_build.yml +++ b/.github/workflows/lint_test_build.yml @@ -35,5 +35,5 @@ jobs: - name: Run Tests run: flutter test - - name: Build Project + - name: Build Project for iOS run: flutter build ipa --no-codesign \ No newline at end of file diff --git a/.gitignore b/.gitignore index 502b8b2a..6df88d5f 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,4 @@ app.*.map.json ios/fastlane/report.xml ios/fastlane/README.md -ios/Runner.xcodeproj/project.pbxproj +desiredFileName.txt diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b684550e..89b065fa 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,38 +1,24 @@ - - + + + - + - + - + diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 62b7603b..841e6159 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -182,4 +182,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 7be2f5f74864d463a8ad433546ed1de7e0f29aef -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.0 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index b3b007a3..db042420 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 287374D32B6D5A1900113A88 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; @@ -152,6 +153,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 287374D32B6D5A1900113A88 /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, @@ -415,6 +417,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -434,6 +437,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -466,10 +470,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 7PLLZ436SG; + DEVELOPMENT_TEAM = 2J3C6P6X3N; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -480,8 +485,12 @@ PRODUCT_BUNDLE_IDENTIFIER = de.tum.cit.ase.ios2324.tum; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -540,6 +549,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -559,6 +569,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -594,6 +605,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -613,6 +625,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -647,10 +660,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 7PLLZ436SG; + DEVELOPMENT_TEAM = 2J3C6P6X3N; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -661,9 +675,13 @@ PRODUCT_BUNDLE_IDENTIFIER = de.tum.cit.ase.ios2324.tum; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -674,10 +692,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 7PLLZ436SG; + DEVELOPMENT_TEAM = 2J3C6P6X3N; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -688,8 +707,12 @@ PRODUCT_BUNDLE_IDENTIFIER = de.tum.cit.ase.ios2324.tum; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index fdc5768a..66c8da82 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -1,63 +1,72 @@ - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - gocast - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - gocast_mobile - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLSchemes - - gocast - - - - LSApplicationQueriesSchemes - - https - - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UIApplicationSupportsIndirectInputEvents - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + gocast + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + gocast_mobile + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleURLSchemes + + gocast + + + + LSApplicationQueriesSchemes + + https + + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CFBundleLocalizations + + en + fr + de + es + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + ITSAppUsesNonExemptEncryption + + diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 85bf47c4..167947e7 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -20,7 +20,7 @@ platform :ios do skip_certificate_matching: true ) - previous_build_number = latest_testflight_build_number + previous_build_number = latest_testflight_build_number(version: "#{get_version_number}", initial_build_number: 0, app_identifier: "de.tum.cit.ase.ios2324.tum") current_build_number = previous_build_number + 1 diff --git a/lib/base/networking/api/gocast/api_v2.pb.dart b/lib/base/networking/api/gocast/api_v2.pb.dart index fe76f766..79ebfa3f 100644 --- a/lib/base/networking/api/gocast/api_v2.pb.dart +++ b/lib/base/networking/api/gocast/api_v2.pb.dart @@ -55,14 +55,20 @@ class ChatReaction extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'ChatReaction', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'ChatReaction', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'chatID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'chatID', + $pb.PbFieldType.OU3, protoName: 'chatID', ) - ..a<$core.int>(2, _omitFieldNames ? '' : 'userID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 2, + _omitFieldNames ? '' : 'userID', + $pb.PbFieldType.OU3, protoName: 'userID', ) ..aOS(3, _omitFieldNames ? '' : 'username') @@ -172,8 +178,8 @@ class AddressedUser extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'AddressedUser', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'AddressedUser', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU3) @@ -307,36 +313,50 @@ class ChatMessage extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'ChatMessage', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'ChatMessage', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU3) ..aOS(2, _omitFieldNames ? '' : 'userID', protoName: 'userID') ..aOS(3, _omitFieldNames ? '' : 'username') - ..aOS(4, _omitFieldNames ? '' : 'message')..aOS( - 5, _omitFieldNames ? '' : 'sanitizedMessage', + ..aOS(4, _omitFieldNames ? '' : 'message') + ..aOS( + 5, + _omitFieldNames ? '' : 'sanitizedMessage', protoName: 'sanitizedMessage', ) - ..a<$core.int>(6, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 6, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..aOS(7, _omitFieldNames ? '' : 'color') ..aOB(8, _omitFieldNames ? '' : 'isVisible', protoName: 'isVisible') ..pc( - 9, _omitFieldNames ? '' : 'reactions', $pb.PbFieldType.PM, + 9, + _omitFieldNames ? '' : 'reactions', + $pb.PbFieldType.PM, subBuilder: ChatReaction.create, ) - ..pc(10, _omitFieldNames ? '' : 'replies', $pb.PbFieldType.PM, + ..pc( + 10, + _omitFieldNames ? '' : 'replies', + $pb.PbFieldType.PM, subBuilder: ChatMessage.create, ) ..pc( - 11, _omitFieldNames ? '' : 'addressedUsers', $pb.PbFieldType.PM, + 11, + _omitFieldNames ? '' : 'addressedUsers', + $pb.PbFieldType.PM, protoName: 'addressedUsers', subBuilder: AddressedUser.create, ) ..aOB(12, _omitFieldNames ? '' : 'isResolved', protoName: 'isResolved') - ..aOM<$1.Timestamp>(13, _omitFieldNames ? '' : 'createdAt', + ..aOM<$1.Timestamp>( + 13, + _omitFieldNames ? '' : 'createdAt', protoName: 'createdAt', subBuilder: $1.Timestamp.create, ) @@ -535,11 +555,14 @@ class GetChatMessagesRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetChatMessagesRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetChatMessagesRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -614,12 +637,15 @@ class PostChatMessageRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostChatMessageRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostChatMessageRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOS(1, _omitFieldNames ? '' : 'message') - ..a<$core.int>(2, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 2, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -710,15 +736,21 @@ class PostChatReactionRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostChatReactionRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostChatReactionRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOS(1, _omitFieldNames ? '' : 'emoji') - ..a<$core.int>(2, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 2, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) - ..a<$core.int>(3, _omitFieldNames ? '' : 'chatID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 3, + _omitFieldNames ? '' : 'chatID', + $pb.PbFieldType.OU3, protoName: 'chatID', ) ..hasRequiredFields = false; @@ -821,17 +853,26 @@ class DeleteChatReactionRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'DeleteChatReactionRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'DeleteChatReactionRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) - ..a<$core.int>(2, _omitFieldNames ? '' : 'chatID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 2, + _omitFieldNames ? '' : 'chatID', + $pb.PbFieldType.OU3, protoName: 'chatID', ) - ..a<$core.int>(3, _omitFieldNames ? '' : 'reactionID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 3, + _omitFieldNames ? '' : 'reactionID', + $pb.PbFieldType.OU3, protoName: 'reactionID', ) ..hasRequiredFields = false; @@ -934,15 +975,21 @@ class PostChatReplyRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostChatReplyRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostChatReplyRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOS(1, _omitFieldNames ? '' : 'message') - ..a<$core.int>(2, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 2, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) - ..a<$core.int>(3, _omitFieldNames ? '' : 'chatID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 3, + _omitFieldNames ? '' : 'chatID', + $pb.PbFieldType.OU3, protoName: 'chatID', ) ..hasRequiredFields = false; @@ -1039,14 +1086,20 @@ class MarkChatMessageAsResolvedRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'MarkChatMessageAsResolvedRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'MarkChatMessageAsResolvedRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) - ..a<$core.int>(2, _omitFieldNames ? '' : 'chatID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 2, + _omitFieldNames ? '' : 'chatID', + $pb.PbFieldType.OU3, protoName: 'chatID', ) ..hasRequiredFields = false; @@ -1078,7 +1131,7 @@ class MarkChatMessageAsResolvedRequest extends $pb.GeneratedMessage { $pb.PbList(); @$core.pragma('dart2js:noInline') static MarkChatMessageAsResolvedRequest getDefault() => _defaultInstance ??= - $pb.GeneratedMessage.$_defaultFor( + $pb.GeneratedMessage.$_defaultFor( create, ); static MarkChatMessageAsResolvedRequest? _defaultInstance; @@ -1137,14 +1190,20 @@ class MarkChatMessageAsUnresolvedRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'MarkChatMessageAsUnresolvedRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'MarkChatMessageAsUnresolvedRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) - ..a<$core.int>(2, _omitFieldNames ? '' : 'chatID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 2, + _omitFieldNames ? '' : 'chatID', + $pb.PbFieldType.OU3, protoName: 'chatID', ) ..hasRequiredFields = false; @@ -1176,7 +1235,7 @@ class MarkChatMessageAsUnresolvedRequest extends $pb.GeneratedMessage { $pb.PbList(); @$core.pragma('dart2js:noInline') static MarkChatMessageAsUnresolvedRequest getDefault() => _defaultInstance ??= - $pb.GeneratedMessage.$_defaultFor( + $pb.GeneratedMessage.$_defaultFor( create, ); static MarkChatMessageAsUnresolvedRequest? _defaultInstance; @@ -1231,11 +1290,14 @@ class GetChatMessagesResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetChatMessagesResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetChatMessagesResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..pc(1, _omitFieldNames ? '' : 'messages', $pb.PbFieldType.PM, + ..pc( + 1, + _omitFieldNames ? '' : 'messages', + $pb.PbFieldType.PM, subBuilder: ChatMessage.create, ) ..hasRequiredFields = false; @@ -1297,11 +1359,13 @@ class PostChatMessageResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostChatMessageResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostChatMessageResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..aOM(1, _omitFieldNames ? '' : 'message', + ..aOM( + 1, + _omitFieldNames ? '' : 'message', subBuilder: ChatMessage.create, ) ..hasRequiredFields = false; @@ -1374,11 +1438,13 @@ class PostChatReactionResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostChatReactionResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostChatReactionResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..aOM(1, _omitFieldNames ? '' : 'reaction', + ..aOM( + 1, + _omitFieldNames ? '' : 'reaction', subBuilder: ChatReaction.create, ) ..hasRequiredFields = false; @@ -1443,8 +1509,8 @@ class DeleteChatReactionResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'DeleteChatReactionResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'DeleteChatReactionResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -1503,11 +1569,13 @@ class PostChatReplyResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostChatReplyResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostChatReplyResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..aOM(1, _omitFieldNames ? '' : 'reply', + ..aOM( + 1, + _omitFieldNames ? '' : 'reply', subBuilder: ChatMessage.create, ) ..hasRequiredFields = false; @@ -1580,11 +1648,13 @@ class MarkChatMessageAsResolvedResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'MarkChatMessageAsResolvedResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'MarkChatMessageAsResolvedResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..aOM(1, _omitFieldNames ? '' : 'message', + ..aOM( + 1, + _omitFieldNames ? '' : 'message', subBuilder: ChatMessage.create, ) ..hasRequiredFields = false; @@ -1616,7 +1686,7 @@ class MarkChatMessageAsResolvedResponse extends $pb.GeneratedMessage { $pb.PbList(); @$core.pragma('dart2js:noInline') static MarkChatMessageAsResolvedResponse getDefault() => _defaultInstance ??= - $pb.GeneratedMessage.$_defaultFor( + $pb.GeneratedMessage.$_defaultFor( create, ); static MarkChatMessageAsResolvedResponse? _defaultInstance; @@ -1661,11 +1731,13 @@ class MarkChatMessageAsUnresolvedResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'MarkChatMessageAsUnresolvedResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'MarkChatMessageAsUnresolvedResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..aOM(1, _omitFieldNames ? '' : 'message', + ..aOM( + 1, + _omitFieldNames ? '' : 'message', subBuilder: ChatMessage.create, ) ..hasRequiredFields = false; @@ -1757,18 +1829,23 @@ class Poll extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'Poll', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'Poll', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU3) - ..a<$core.int>(2, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 2, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..aOS(3, _omitFieldNames ? '' : 'question') ..aOB(4, _omitFieldNames ? '' : 'active') ..pc( - 5, _omitFieldNames ? '' : 'pollOptions', $pb.PbFieldType.PM, + 5, + _omitFieldNames ? '' : 'pollOptions', + $pb.PbFieldType.PM, protoName: 'pollOptions', subBuilder: PollOption.create, ) @@ -1884,8 +1961,8 @@ class PollOption extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PollOption', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PollOption', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU3) @@ -1989,11 +2066,14 @@ class GetPollsRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetPollsRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetPollsRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -2063,15 +2143,20 @@ class PostPollVoteRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostPollVoteRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostPollVoteRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..a<$core.int>( - 2, _omitFieldNames ? '' : 'pollOptionID', $pb.PbFieldType.OU3, + 2, + _omitFieldNames ? '' : 'pollOptionID', + $pb.PbFieldType.OU3, protoName: 'pollOptionID', ) ..hasRequiredFields = false; @@ -2149,11 +2234,14 @@ class GetPollsResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetPollsResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetPollsResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..pc(1, _omitFieldNames ? '' : 'polls', $pb.PbFieldType.PM, + ..pc( + 1, + _omitFieldNames ? '' : 'polls', + $pb.PbFieldType.PM, subBuilder: Poll.create, ) ..hasRequiredFields = false; @@ -2202,8 +2290,8 @@ class PostPollVoteResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostPollVoteResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostPollVoteResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -2301,35 +2389,51 @@ class User extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'User', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'User', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU3) ..aOS(2, _omitFieldNames ? '' : 'name') ..aOS(3, _omitFieldNames ? '' : 'lastName', protoName: 'lastName') - ..aOS(4, _omitFieldNames ? '' : 'email')..aOS( - 5, _omitFieldNames ? '' : 'matriculationNumber', + ..aOS(4, _omitFieldNames ? '' : 'email') + ..aOS( + 5, + _omitFieldNames ? '' : 'matriculationNumber', protoName: 'matriculationNumber', ) ..aOS(6, _omitFieldNames ? '' : 'lrzID', protoName: 'lrzID') ..a<$core.int>(7, _omitFieldNames ? '' : 'role', $pb.PbFieldType.OU3) - ..pc(8, _omitFieldNames ? '' : 'courses', $pb.PbFieldType.PM, + ..pc( + 8, + _omitFieldNames ? '' : 'courses', + $pb.PbFieldType.PM, subBuilder: Course.create, ) ..pc( - 9, _omitFieldNames ? '' : 'administeredCourses', $pb.PbFieldType.PM, + 9, + _omitFieldNames ? '' : 'administeredCourses', + $pb.PbFieldType.PM, protoName: 'administeredCourses', subBuilder: Course.create, ) - ..pc(10, _omitFieldNames ? '' : 'pinnedCourses', $pb.PbFieldType.PM, + ..pc( + 10, + _omitFieldNames ? '' : 'pinnedCourses', + $pb.PbFieldType.PM, protoName: 'pinnedCourses', subBuilder: Course.create, ) - ..pc(11, _omitFieldNames ? '' : 'settings', $pb.PbFieldType.PM, + ..pc( + 11, + _omitFieldNames ? '' : 'settings', + $pb.PbFieldType.PM, subBuilder: UserSetting.create, ) - ..pc(12, _omitFieldNames ? '' : 'bookmarks', $pb.PbFieldType.PM, + ..pc( + 12, + _omitFieldNames ? '' : 'bookmarks', + $pb.PbFieldType.PM, subBuilder: Bookmark.create, ) ..hasRequiredFields = false; @@ -2485,13 +2589,16 @@ class UserSetting extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'UserSetting', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'UserSetting', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..e(3, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE, - defaultOrMaker: UserSettingType.PREFERRED_NAME, - valueOf: UserSettingType.valueOf, + ..e( + 3, + _omitFieldNames ? '' : 'type', + $pb.PbFieldType.OE, + defaultOrMaker: UserSettingType.PREFERRED_NAME, + valueOf: UserSettingType.valueOf, enumValues: UserSettingType.values, ) ..aOS(4, _omitFieldNames ? '' : 'value') @@ -2562,8 +2669,8 @@ class GetUserRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetUserRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetUserRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -2630,8 +2737,8 @@ class GetUserCoursesRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetUserCoursesRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetUserCoursesRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'year', $pb.PbFieldType.OU3) @@ -2754,8 +2861,8 @@ class GetUserPinnedRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetUserPinnedRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetUserPinnedRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'year', $pb.PbFieldType.OU3) @@ -2856,8 +2963,8 @@ class GetUserAdminRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetUserAdminRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetUserAdminRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -2904,8 +3011,8 @@ class GetUserSettingsRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetUserSettingsRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetUserSettingsRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -2963,12 +3070,14 @@ class PatchUserSettingsRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PatchUserSettingsRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PatchUserSettingsRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..pc( - 1, _omitFieldNames ? '' : 'userSettings', $pb.PbFieldType.PM, + 1, + _omitFieldNames ? '' : 'userSettings', + $pb.PbFieldType.PM, protoName: 'userSettings', subBuilder: UserSetting.create, ) @@ -3031,12 +3140,14 @@ class PatchUserSettingsResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PatchUserSettingsResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PatchUserSettingsResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..pc( - 1, _omitFieldNames ? '' : 'userSettings', $pb.PbFieldType.PM, + 1, + _omitFieldNames ? '' : 'userSettings', + $pb.PbFieldType.PM, protoName: 'userSettings', subBuilder: UserSetting.create, ) @@ -3099,11 +3210,14 @@ class PostPinnedRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostPinnedRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostPinnedRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'courseID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'courseID', + $pb.PbFieldType.OU3, protoName: 'courseID', ) ..hasRequiredFields = false; @@ -3171,11 +3285,14 @@ class DeletePinnedRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'DeletePinnedRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'DeletePinnedRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'courseID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'courseID', + $pb.PbFieldType.OU3, protoName: 'courseID', ) ..hasRequiredFields = false; @@ -3243,8 +3360,8 @@ class GetUserResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetUserResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetUserResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOM(1, _omitFieldNames ? '' : 'user', subBuilder: User.create) @@ -3315,11 +3432,14 @@ class GetUserCoursesResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetUserCoursesResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetUserCoursesResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..pc(1, _omitFieldNames ? '' : 'courses', $pb.PbFieldType.PM, + ..pc( + 1, + _omitFieldNames ? '' : 'courses', + $pb.PbFieldType.PM, subBuilder: Course.create, ) ..hasRequiredFields = false; @@ -3381,11 +3501,14 @@ class GetUserPinnedResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetUserPinnedResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetUserPinnedResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..pc(1, _omitFieldNames ? '' : 'courses', $pb.PbFieldType.PM, + ..pc( + 1, + _omitFieldNames ? '' : 'courses', + $pb.PbFieldType.PM, subBuilder: Course.create, ) ..hasRequiredFields = false; @@ -3447,11 +3570,14 @@ class GetUserAdminResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetUserAdminResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetUserAdminResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..pc(1, _omitFieldNames ? '' : 'courses', $pb.PbFieldType.PM, + ..pc( + 1, + _omitFieldNames ? '' : 'courses', + $pb.PbFieldType.PM, subBuilder: Course.create, ) ..hasRequiredFields = false; @@ -3511,12 +3637,14 @@ class GetUserSettingsResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetUserSettingsResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetUserSettingsResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..pc( - 1, _omitFieldNames ? '' : 'userSettings', $pb.PbFieldType.PM, + 1, + _omitFieldNames ? '' : 'userSettings', + $pb.PbFieldType.PM, protoName: 'userSettings', subBuilder: UserSetting.create, ) @@ -3571,8 +3699,8 @@ class PostPinnedResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostPinnedResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostPinnedResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -3619,8 +3747,8 @@ class DeletePinnedResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'DeletePinnedResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'DeletePinnedResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -3700,16 +3828,19 @@ class Bookmark extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'Bookmark', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'Bookmark', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU3) ..aOS(2, _omitFieldNames ? '' : 'description') ..a<$core.int>(3, _omitFieldNames ? '' : 'hours', $pb.PbFieldType.OU3) ..a<$core.int>(4, _omitFieldNames ? '' : 'minutes', $pb.PbFieldType.OU3) - ..a<$core.int>(5, _omitFieldNames ? '' : 'seconds', $pb.PbFieldType.OU3)..a< - $core.int>(6, _omitFieldNames ? '' : 'userID', $pb.PbFieldType.OU3, + ..a<$core.int>(5, _omitFieldNames ? '' : 'seconds', $pb.PbFieldType.OU3) + ..a<$core.int>( + 6, + _omitFieldNames ? '' : 'userID', + $pb.PbFieldType.OU3, protoName: 'userID', ) ..a<$core.int>( @@ -3852,11 +3983,14 @@ class GetBookmarksRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetBookmarksRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetBookmarksRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -3940,15 +4074,18 @@ class PutBookmarkRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PutBookmarkRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PutBookmarkRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOS(1, _omitFieldNames ? '' : 'description') ..a<$core.int>(2, _omitFieldNames ? '' : 'hours', $pb.PbFieldType.OU3) ..a<$core.int>(3, _omitFieldNames ? '' : 'minutes', $pb.PbFieldType.OU3) - ..a<$core.int>(4, _omitFieldNames ? '' : 'seconds', $pb.PbFieldType.OU3)..a< - $core.int>(5, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>(4, _omitFieldNames ? '' : 'seconds', $pb.PbFieldType.OU3) + ..a<$core.int>( + 5, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -4080,15 +4217,18 @@ class PatchBookmarkRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PatchBookmarkRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PatchBookmarkRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOS(1, _omitFieldNames ? '' : 'description') ..a<$core.int>(2, _omitFieldNames ? '' : 'hours', $pb.PbFieldType.OU3) ..a<$core.int>(3, _omitFieldNames ? '' : 'minutes', $pb.PbFieldType.OU3) - ..a<$core.int>(4, _omitFieldNames ? '' : 'seconds', $pb.PbFieldType.OU3)..a< - $core.int>(5, _omitFieldNames ? '' : 'bookmarkID', $pb.PbFieldType.OU3, + ..a<$core.int>(4, _omitFieldNames ? '' : 'seconds', $pb.PbFieldType.OU3) + ..a<$core.int>( + 5, + _omitFieldNames ? '' : 'bookmarkID', + $pb.PbFieldType.OU3, protoName: 'bookmarkID', ) ..hasRequiredFields = false; @@ -4205,11 +4345,14 @@ class DeleteBookmarkRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'DeleteBookmarkRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'DeleteBookmarkRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'bookmarkID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'bookmarkID', + $pb.PbFieldType.OU3, protoName: 'bookmarkID', ) ..hasRequiredFields = false; @@ -4280,11 +4423,14 @@ class GetBookmarksResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetBookmarksResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetBookmarksResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..pc(1, _omitFieldNames ? '' : 'bookmarks', $pb.PbFieldType.PM, + ..pc( + 1, + _omitFieldNames ? '' : 'bookmarks', + $pb.PbFieldType.PM, subBuilder: Bookmark.create, ) ..hasRequiredFields = false; @@ -4344,11 +4490,13 @@ class PutBookmarkResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PutBookmarkResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PutBookmarkResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..aOM(1, _omitFieldNames ? '' : 'bookmark', + ..aOM( + 1, + _omitFieldNames ? '' : 'bookmark', subBuilder: Bookmark.create, ) ..hasRequiredFields = false; @@ -4418,11 +4566,13 @@ class PatchBookmarkResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PatchBookmarkResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PatchBookmarkResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..aOM(1, _omitFieldNames ? '' : 'bookmark', + ..aOM( + 1, + _omitFieldNames ? '' : 'bookmark', subBuilder: Bookmark.create, ) ..hasRequiredFields = false; @@ -4487,8 +4637,8 @@ class DeleteBookmarkResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'DeleteBookmarkResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'DeleteBookmarkResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -4562,8 +4712,8 @@ class BannerAlert extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'BannerAlert', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'BannerAlert', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU3) @@ -4694,14 +4844,17 @@ class FeatureNotification extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'FeatureNotification', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'FeatureNotification', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU3) ..aOS(2, _omitFieldNames ? '' : 'Title', protoName: 'Title') ..aOS(3, _omitFieldNames ? '' : 'Body', protoName: 'Body') - ..a<$core.int>(4, _omitFieldNames ? '' : 'Target', $pb.PbFieldType.OU3, + ..a<$core.int>( + 4, + _omitFieldNames ? '' : 'Target', + $pb.PbFieldType.OU3, protoName: 'Target', ) ..hasRequiredFields = false; @@ -4805,8 +4958,8 @@ class PostDeviceTokenRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostDeviceTokenRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostDeviceTokenRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOS(1, _omitFieldNames ? '' : 'deviceToken', protoName: 'deviceToken') @@ -4878,8 +5031,8 @@ class DeleteDeviceTokenRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'DeleteDeviceTokenRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'DeleteDeviceTokenRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOS(1, _omitFieldNames ? '' : 'deviceToken', protoName: 'deviceToken') @@ -4943,8 +5096,8 @@ class GetBannerAlertsRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetBannerAlertsRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetBannerAlertsRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -4994,8 +5147,8 @@ class GetFeatureNotificationsRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetFeatureNotificationsRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetFeatureNotificationsRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -5047,8 +5200,8 @@ class PostDeviceTokenResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PostDeviceTokenResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PostDeviceTokenResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -5098,8 +5251,8 @@ class DeleteDeviceTokenResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'DeleteDeviceTokenResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'DeleteDeviceTokenResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -5157,12 +5310,14 @@ class GetBannerAlertsResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetBannerAlertsResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetBannerAlertsResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..pc( - 1, _omitFieldNames ? '' : 'bannerAlerts', $pb.PbFieldType.PM, + 1, + _omitFieldNames ? '' : 'bannerAlerts', + $pb.PbFieldType.PM, protoName: 'bannerAlerts', subBuilder: BannerAlert.create, ) @@ -5225,13 +5380,15 @@ class GetFeatureNotificationsResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetFeatureNotificationsResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetFeatureNotificationsResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..pc( - 1, _omitFieldNames ? '' : 'featureNotifications', $pb.PbFieldType.PM, - protoName: 'featureNotifications', + 1, + _omitFieldNames ? '' : 'featureNotifications', + $pb.PbFieldType.PM, + protoName: 'featureNotifications', subBuilder: FeatureNotification.create, ) ..hasRequiredFields = false; @@ -5263,7 +5420,7 @@ class GetFeatureNotificationsResponse extends $pb.GeneratedMessage { $pb.PbList(); @$core.pragma('dart2js:noInline') static GetFeatureNotificationsResponse getDefault() => _defaultInstance ??= - $pb.GeneratedMessage.$_defaultFor( + $pb.GeneratedMessage.$_defaultFor( create, ); static GetFeatureNotificationsResponse? _defaultInstance; @@ -5357,25 +5514,33 @@ class Course extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'Course', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'Course', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU3) ..aOS(2, _omitFieldNames ? '' : 'name') ..aOS(3, _omitFieldNames ? '' : 'slug') - ..aOM(4, _omitFieldNames ? '' : 'semester', + ..aOM( + 4, + _omitFieldNames ? '' : 'semester', subBuilder: Semester.create, ) - ..aOS(5, _omitFieldNames ? '' : 'TUMOnlineIdentifier', + ..aOS( + 5, + _omitFieldNames ? '' : 'TUMOnlineIdentifier', protoName: 'TUMOnlineIdentifier', ) - ..aOB(6, _omitFieldNames ? '' : 'VODEnabled', protoName: 'VODEnabled')..aOB( - 7, _omitFieldNames ? '' : 'downloadsEnabled', + ..aOB(6, _omitFieldNames ? '' : 'VODEnabled', protoName: 'VODEnabled') + ..aOB( + 7, + _omitFieldNames ? '' : 'downloadsEnabled', protoName: 'downloadsEnabled', ) - ..aOB(8, _omitFieldNames ? '' : 'chatEnabled', protoName: 'chatEnabled')..aOB( - 9, _omitFieldNames ? '' : 'anonymousChatEnabled', + ..aOB(8, _omitFieldNames ? '' : 'chatEnabled', protoName: 'chatEnabled') + ..aOB( + 9, + _omitFieldNames ? '' : 'anonymousChatEnabled', protoName: 'anonymousChatEnabled', ) ..aOB( @@ -5388,10 +5553,15 @@ class Course extends $pb.GeneratedMessage { _omitFieldNames ? '' : 'vodChatEnabled', protoName: 'vodChatEnabled', ) - ..pc(12, _omitFieldNames ? '' : 'streams', $pb.PbFieldType.PM, + ..pc( + 12, + _omitFieldNames ? '' : 'streams', + $pb.PbFieldType.PM, subBuilder: Stream.create, ) - ..aOS(13, _omitFieldNames ? '' : 'cameraPresetPreferences', + ..aOS( + 13, + _omitFieldNames ? '' : 'cameraPresetPreferences', protoName: 'cameraPresetPreferences', ) ..aOS( @@ -5400,7 +5570,9 @@ class Course extends $pb.GeneratedMessage { protoName: 'sourcePreferences', ) ..a<$core.int>( - 15, _omitFieldNames ? '' : 'lastRecordingID', $pb.PbFieldType.OU3, + 15, + _omitFieldNames ? '' : 'lastRecordingID', + $pb.PbFieldType.OU3, protoName: 'lastRecordingID', ) ..a<$core.int>( @@ -5648,8 +5820,8 @@ class Semester extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'Semester', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'Semester', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOS(1, _omitFieldNames ? '' : 'teachingTerm', protoName: 'teachingTerm') @@ -5740,8 +5912,8 @@ class GetPublicCoursesRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetPublicCoursesRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetPublicCoursesRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'year', $pb.PbFieldType.OU3) @@ -5844,8 +6016,8 @@ class GetSemestersRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetSemestersRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetSemestersRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -5900,11 +6072,14 @@ class GetCourseStreamsRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetCourseStreamsRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetCourseStreamsRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'courseID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'courseID', + $pb.PbFieldType.OU3, protoName: 'courseID', ) ..hasRequiredFields = false; @@ -5975,11 +6150,14 @@ class GetPublicCoursesResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetPublicCoursesResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetPublicCoursesResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..pc(1, _omitFieldNames ? '' : 'courses', $pb.PbFieldType.PM, + ..pc( + 1, + _omitFieldNames ? '' : 'courses', + $pb.PbFieldType.PM, subBuilder: Course.create, ) ..hasRequiredFields = false; @@ -6045,14 +6223,19 @@ class GetSemestersResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetSemestersResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetSemestersResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..aOM(1, _omitFieldNames ? '' : 'current', + ..aOM( + 1, + _omitFieldNames ? '' : 'current', subBuilder: Semester.create, ) - ..pc(2, _omitFieldNames ? '' : 'semesters', $pb.PbFieldType.PM, + ..pc( + 2, + _omitFieldNames ? '' : 'semesters', + $pb.PbFieldType.PM, subBuilder: Semester.create, ) ..hasRequiredFields = false; @@ -6126,11 +6309,14 @@ class GetCourseStreamsResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetCourseStreamsResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetCourseStreamsResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..pc(1, _omitFieldNames ? '' : 'streams', $pb.PbFieldType.PM, + ..pc( + 1, + _omitFieldNames ? '' : 'streams', + $pb.PbFieldType.PM, subBuilder: Stream.create, ) ..hasRequiredFields = false; @@ -6300,37 +6486,52 @@ class Stream extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'Stream', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'Stream', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.int>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU3) ..aOS(2, _omitFieldNames ? '' : 'name') ..aOS(3, _omitFieldNames ? '' : 'description') - ..a<$core.int>(4, _omitFieldNames ? '' : 'courseID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 4, + _omitFieldNames ? '' : 'courseID', + $pb.PbFieldType.OU3, protoName: 'courseID', ) - ..aOM<$1.Timestamp>(5, _omitFieldNames ? '' : 'start', + ..aOM<$1.Timestamp>( + 5, + _omitFieldNames ? '' : 'start', subBuilder: $1.Timestamp.create, ) - ..aOM<$1.Timestamp>(6, _omitFieldNames ? '' : 'end', + ..aOM<$1.Timestamp>( + 6, + _omitFieldNames ? '' : 'end', subBuilder: $1.Timestamp.create, ) ..aOB(7, _omitFieldNames ? '' : 'chatEnabled', protoName: 'chatEnabled') ..aOS(8, _omitFieldNames ? '' : 'roomName', protoName: 'roomName') - ..aOS(9, _omitFieldNames ? '' : 'roomCode', protoName: 'roomCode')..aOS( - 10, _omitFieldNames ? '' : 'eventTypeName', + ..aOS(9, _omitFieldNames ? '' : 'roomCode', protoName: 'roomCode') + ..aOS( + 10, + _omitFieldNames ? '' : 'eventTypeName', protoName: 'eventTypeName', ) ..a<$core.int>( - 11, _omitFieldNames ? '' : 'TUMOnlineEventID', $pb.PbFieldType.OU3, + 11, + _omitFieldNames ? '' : 'TUMOnlineEventID', + $pb.PbFieldType.OU3, protoName: 'TUMOnlineEventID', ) - ..aOS(12, _omitFieldNames ? '' : 'seriesIdentifier', + ..aOS( + 12, + _omitFieldNames ? '' : 'seriesIdentifier', protoName: 'seriesIdentifier', ) - ..aOS(13, _omitFieldNames ? '' : 'playlistUrl', protoName: 'playlistUrl')..aOS( - 14, _omitFieldNames ? '' : 'playlistUrlPRES', + ..aOS(13, _omitFieldNames ? '' : 'playlistUrl', protoName: 'playlistUrl') + ..aOS( + 14, + _omitFieldNames ? '' : 'playlistUrlPRES', protoName: 'playlistUrlPRES', ) ..aOS( @@ -6339,25 +6540,38 @@ class Stream extends $pb.GeneratedMessage { protoName: 'playlistUrlCAM', ) ..aOB(16, _omitFieldNames ? '' : 'liveNow', protoName: 'liveNow') - ..aOM<$1.Timestamp>(17, _omitFieldNames ? '' : 'liveNowTimestamp', + ..aOM<$1.Timestamp>( + 17, + _omitFieldNames ? '' : 'liveNowTimestamp', protoName: 'liveNowTimestamp', subBuilder: $1.Timestamp.create, ) ..aOB(18, _omitFieldNames ? '' : 'recording') ..aOB(19, _omitFieldNames ? '' : 'premiere') ..aOB(20, _omitFieldNames ? '' : 'ended') - ..a<$core.int>(21, _omitFieldNames ? '' : 'vodViews', $pb.PbFieldType.OU3, + ..a<$core.int>( + 21, + _omitFieldNames ? '' : 'vodViews', + $pb.PbFieldType.OU3, protoName: 'vodViews', ) ..a<$core.int>( - 22, _omitFieldNames ? '' : 'startOffset', $pb.PbFieldType.OU3, + 22, + _omitFieldNames ? '' : 'startOffset', + $pb.PbFieldType.OU3, protoName: 'startOffset', ) - ..a<$core.int>(23, _omitFieldNames ? '' : 'endOffset', $pb.PbFieldType.OU3, + ..a<$core.int>( + 23, + _omitFieldNames ? '' : 'endOffset', + $pb.PbFieldType.OU3, protoName: 'endOffset', ) ..a<$core.int>(28, _omitFieldNames ? '' : 'duration', $pb.PbFieldType.OU3) - ..pc(29, _omitFieldNames ? '' : 'downloads', $pb.PbFieldType.PM, + ..pc( + 29, + _omitFieldNames ? '' : 'downloads', + $pb.PbFieldType.PM, subBuilder: Download.create, ) ..aOB(30, _omitFieldNames ? '' : 'isPlanned', protoName: 'isPlanned') @@ -6746,11 +6960,14 @@ class GetStreamRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetStreamRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetStreamRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -6810,8 +7027,8 @@ class GetNowLiveRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetNowLiveRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetNowLiveRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, )..hasRequiredFields = false; @@ -6866,11 +7083,14 @@ class GetThumbsLiveRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetThumbsLiveRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetThumbsLiveRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -6939,11 +7159,14 @@ class GetThumbsVODRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetThumbsVODRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetThumbsVODRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -7011,8 +7234,8 @@ class GetStreamResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetStreamResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetStreamResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOM(1, _omitFieldNames ? '' : 'stream', subBuilder: Stream.create) @@ -7083,11 +7306,14 @@ class GetNowLiveResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetNowLiveResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetNowLiveResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..pc(1, _omitFieldNames ? '' : 'stream', $pb.PbFieldType.PM, + ..pc( + 1, + _omitFieldNames ? '' : 'stream', + $pb.PbFieldType.PM, subBuilder: Stream.create, ) ..hasRequiredFields = false; @@ -7146,8 +7372,8 @@ class GetThumbsVODResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetThumbsVODResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetThumbsVODResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOS(1, _omitFieldNames ? '' : 'path') @@ -7217,8 +7443,8 @@ class GetThumbsLiveResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetThumbsLiveResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetThumbsLiveResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOS(1, _omitFieldNames ? '' : 'path') @@ -7294,8 +7520,8 @@ class Download extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'Download', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'Download', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..aOS(1, _omitFieldNames ? '' : 'friendlyName', protoName: 'friendlyName') @@ -7386,16 +7612,22 @@ class Progress extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'Progress', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'Progress', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.double>(1, _omitFieldNames ? '' : 'progress', $pb.PbFieldType.OF) ..aOB(2, _omitFieldNames ? '' : 'watched') - ..a<$core.int>(3, _omitFieldNames ? '' : 'userID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 3, + _omitFieldNames ? '' : 'userID', + $pb.PbFieldType.OU3, protoName: 'userID', ) - ..a<$core.int>(4, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 4, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -7496,11 +7728,14 @@ class GetProgressRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetProgressRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetProgressRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -7572,12 +7807,15 @@ class PutProgressRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PutProgressRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PutProgressRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) ..a<$core.double>(1, _omitFieldNames ? '' : 'progress', $pb.PbFieldType.OF) - ..a<$core.int>(3, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 3, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -7657,11 +7895,14 @@ class MarkAsWatchedRequest extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'MarkAsWatchedRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'MarkAsWatchedRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..a<$core.int>(1, _omitFieldNames ? '' : 'streamID', $pb.PbFieldType.OU3, + ..a<$core.int>( + 1, + _omitFieldNames ? '' : 'streamID', + $pb.PbFieldType.OU3, protoName: 'streamID', ) ..hasRequiredFields = false; @@ -7730,11 +7971,13 @@ class GetProgressResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'GetProgressResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'GetProgressResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..aOM(1, _omitFieldNames ? '' : 'progress', + ..aOM( + 1, + _omitFieldNames ? '' : 'progress', subBuilder: Progress.create, ) ..hasRequiredFields = false; @@ -7804,11 +8047,13 @@ class PutProgressResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'PutProgressResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'PutProgressResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..aOM(1, _omitFieldNames ? '' : 'progress', + ..aOM( + 1, + _omitFieldNames ? '' : 'progress', subBuilder: Progress.create, ) ..hasRequiredFields = false; @@ -7878,11 +8123,13 @@ class MarkAsWatchedResponse extends $pb.GeneratedMessage { create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'MarkAsWatchedResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), + _omitMessageNames ? '' : 'MarkAsWatchedResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'protobuf'), createEmptyInstance: create, ) - ..aOM(1, _omitFieldNames ? '' : 'progress', + ..aOM( + 1, + _omitFieldNames ? '' : 'progress', subBuilder: Progress.create, ) ..hasRequiredFields = false; diff --git a/lib/base/networking/api/handler/api_handler.dart b/lib/base/networking/api/handler/api_handler.dart index 28da2116..a5b61315 100644 --- a/lib/base/networking/api/handler/api_handler.dart +++ b/lib/base/networking/api/handler/api_handler.dart @@ -37,7 +37,8 @@ class ApiHandler { /// This method takes a [statusCode] and [apiMessage] and throws an [AppError] /// based on the status code. static void handleHttpStatus(int? statusCode, String? apiMessage) { - _logger.i('Handling HTTP status code: $statusCode, API message: $apiMessage'); + _logger + .i('Handling HTTP status code: $statusCode, API message: $apiMessage'); if (statusCode == null) { throw AppError.unknownError("Status code is null"); } diff --git a/lib/base/networking/api/handler/auth_handler.dart b/lib/base/networking/api/handler/auth_handler.dart index 612b54c8..dc6cbfa0 100644 --- a/lib/base/networking/api/handler/auth_handler.dart +++ b/lib/base/networking/api/handler/auth_handler.dart @@ -46,10 +46,12 @@ class AuthHandler { }); Response response; try { - response = await dio.post(url, data: formData); - _logger.i('Received HTTP response with status code: ${response.statusCode}'); + response = await dio.post(url, data: formData); + _logger + .i('Received HTTP response with status code: ${response.statusCode}'); } catch (e) { - _logger.e('Error during basic authentication for user: $username, Error: $e'); + _logger.e( + 'Error during basic authentication for user: $username, Error: $e'); throw AppError.userError(); } try { diff --git a/lib/base/networking/api/handler/bookmarks_handler.dart b/lib/base/networking/api/handler/bookmarks_handler.dart index 40fcf008..9cad48b5 100644 --- a/lib/base/networking/api/handler/bookmarks_handler.dart +++ b/lib/base/networking/api/handler/bookmarks_handler.dart @@ -25,7 +25,6 @@ class BooKMarkHandler { ); } - /// Adds a bookmark for the current user. /// /// Sends a `putUserBookmark` gRPC call with the given [bookmarkData] to add a bookmark. diff --git a/lib/base/networking/api/handler/chat_handler.dart b/lib/base/networking/api/handler/chat_handler.dart index 2ceeb49a..4802ad64 100644 --- a/lib/base/networking/api/handler/chat_handler.dart +++ b/lib/base/networking/api/handler/chat_handler.dart @@ -56,9 +56,11 @@ class ChatHandlers { /// Takes a [emoji] parameter to post a chat reaction. /// /// returns a [ChatReaction] instance that represents the posted chat reaction. - Future postMessageReaction(int messageID, - int streamID, - String emoji,) async { + Future postMessageReaction( + int messageID, + int streamID, + String emoji, + ) async { return _grpcHandler.callGrpcMethod( (client) async { final response = await client.postChatReaction( @@ -80,9 +82,11 @@ class ChatHandlers { /// Takes a [messageID] parameter to delete a chat reaction for a specific message. /// Takes a [streamID] parameter to delete a chat reaction for a specific stream. /// Takes a [reactionID] parameter to delete a chat reaction. - Future deleteMessageReaction(int messageID, - int streamID, - int reactionID,) async { + Future deleteMessageReaction( + int messageID, + int streamID, + int reactionID, + ) async { return _grpcHandler.callGrpcMethod( (client) async { await client.deleteChatReaction( @@ -105,9 +109,11 @@ class ChatHandlers { /// Takes a [message] parameter to post a chat reply. /// /// returns a [ChatMessage] instance that represents the posted chat reply. - Future postChatReply(int messageID, - int streamID, - String message,) async { + Future postChatReply( + int messageID, + int streamID, + String message, + ) async { return _grpcHandler.callGrpcMethod( (client) async { final response = await client.postChatReply( @@ -155,5 +161,4 @@ class ChatHandlers { }, ); } - -} \ No newline at end of file +} diff --git a/lib/base/networking/api/handler/course_handler.dart b/lib/base/networking/api/handler/course_handler.dart index 10b1e4d1..cc412191 100644 --- a/lib/base/networking/api/handler/course_handler.dart +++ b/lib/base/networking/api/handler/course_handler.dart @@ -4,7 +4,6 @@ import 'package:gocast_mobile/base/networking/api/handler/user_handler.dart'; import 'package:logger/logger.dart'; import 'package:tuple/tuple.dart'; - class CourseHandler { static final Logger _logger = Logger(); final GrpcHandler _grpcHandler; @@ -23,7 +22,8 @@ class CourseHandler { }, ); } -/// fetches the semesters and the current semester. + + /// fetches the semesters and the current semester. Future, Semester>> fetchSemesters() async { _logger.i('Fetching semesters'); final response = await _grpcHandler.callGrpcMethod( diff --git a/lib/base/networking/api/handler/grpc_handler.dart b/lib/base/networking/api/handler/grpc_handler.dart index 89ec8f9f..7325d149 100644 --- a/lib/base/networking/api/handler/grpc_handler.dart +++ b/lib/base/networking/api/handler/grpc_handler.dart @@ -9,7 +9,6 @@ import 'package:logger/logger.dart'; /// Handles gRPC communication for the application. class GrpcHandler { static final Logger _logger = Logger(); - final String host; final int port; late ClientChannel _channel; @@ -42,21 +41,16 @@ class GrpcHandler { Future Function(APIClient client) grpcMethod, ) async { _logger.d('callGrpcMethod: Initiating gRPC call'); + final token = await TokenHandler.getToken(); try { - String token = ''; - try { - token = await TokenHandler.loadToken('jwt'); - }catch(e) { - token = ''; - } CallOptions callOptions; - if(token.isNotEmpty) { + if (token.isNotEmpty) { final metadata = { 'grpcgateway-cookie': 'jwt=$token', }; - callOptions = CallOptions(metadata: metadata); - }else { - callOptions = CallOptions(); + callOptions = CallOptions(metadata: metadata); + } else { + callOptions = CallOptions(); } return await grpcMethod(APIClient(_channel, options: callOptions)); } on SocketException catch (socketException) { diff --git a/lib/base/networking/api/handler/poll_handler.dart b/lib/base/networking/api/handler/poll_handler.dart index c43393c9..d942f1dc 100644 --- a/lib/base/networking/api/handler/poll_handler.dart +++ b/lib/base/networking/api/handler/poll_handler.dart @@ -39,7 +39,8 @@ class PollHandlers { ); return _grpcHandler.callGrpcMethod( (client) async { - await client.postPollVote(PostPollVoteRequest( + await client.postPollVote( + PostPollVoteRequest( streamID: streamID, pollOptionID: pollOptionID, ), diff --git a/lib/base/networking/api/handler/settings_handler.dart b/lib/base/networking/api/handler/settings_handler.dart index 81748a51..373fecc7 100644 --- a/lib/base/networking/api/handler/settings_handler.dart +++ b/lib/base/networking/api/handler/settings_handler.dart @@ -5,7 +5,6 @@ import 'package:logger/logger.dart'; import 'grpc_handler.dart'; - class SettingsHandler { static final Logger _logger = Logger(); final GrpcHandler _grpcHandler; diff --git a/lib/base/networking/api/handler/token_handler.dart b/lib/base/networking/api/handler/token_handler.dart index cf970fe8..9e767364 100644 --- a/lib/base/networking/api/handler/token_handler.dart +++ b/lib/base/networking/api/handler/token_handler.dart @@ -11,6 +11,7 @@ import 'package:logger/logger.dart'; class TokenHandler { static final Logger _logger = Logger(); static const _storage = FlutterSecureStorage(); + static String cachedToken = ''; /// Stores a token. /// @@ -84,7 +85,6 @@ class TokenHandler { _logger.w('Token not found for key: $key'); return ""; } - _logger.i('Token successfully loaded for key: $key'); return token; } catch (e) { @@ -103,10 +103,25 @@ class TokenHandler { static Future deleteToken(String key) async { try { await _storage.delete(key: key); + await _invalidateToken(); _logger.i('Token successfully deleted for key: $key'); } catch (e) { _logger.e('Error deleting token: $e'); throw AppError.notFound(); } } + + static Future getToken() async { + if (cachedToken.isNotEmpty) { + _logger.d('Using cached token'); + return cachedToken; + } + _logger.d('Loading token from storage'); + cachedToken = await loadToken('jwt'); + return cachedToken; + } + + static Future _invalidateToken() async { + cachedToken = ''; + } } diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index c984bc54..d1234e99 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -1,7 +1,7 @@ /// AppConfig - Defines the configuration for the application. class AppConfig { static const String appName = 'GoCast Mobile'; - static const String appVersion = '0.1.0'; + static const String appVersion = '0.2.0'; static const String appDescription = 'GoCast Mobile App'; AppConfig._(); // Private constructor diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 08f51e1d..57502ef1 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -5,7 +5,7 @@ }, "my_courses": "Meine Kurse", "public_courses": "Öffentliche Kurse", - "live_now": "Jetzt Live", + "live_now": "Live", "pinned_courses": "Angeheftete Kurse", "pinned_empty": "Sie haben keine angehefteten Kurse.", "pin": "Anheften", @@ -95,5 +95,8 @@ "enter_your_password": "Geben Sie Ihr Passwort ein", "home": "Startseite", "language_selection": "Sprache", - "language_selection_description": "Wählen Sie Ihre bevorzugte Sprache aus" + "language_selection_description": "Wählen Sie Ihre bevorzugte Sprache aus", + "download_completed": "Download abgeschlossen", + "download_failed": "Download fehlgeschlagen", + "download_canceled": "Download abgebrochen" } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index c5da5659..cc6c5369 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -5,7 +5,7 @@ }, "my_courses": "My Courses", "public_courses": "Public Courses", - "live_now": "Live Now", + "live_now": "Live", "pinned_courses": "Pinned Courses", "pinned_empty": "You have no pinned courses.", "pin": "Pin", @@ -95,5 +95,8 @@ "enter_your_password": "Enter your password", "home": "Home", "language_selection": "Language", - "language_selection_description": "Select your preferred language" + "language_selection_description": "Select your preferred language", + "download_completed": "Download Completed", + "download_failed": "Download Failed", + "download_cancelled": "Download Cancelled" } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index f789e0e1..7741a495 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -5,7 +5,7 @@ }, "my_courses": "Mis Cursos", "public_courses": "Cursos Públicos", - "live_now": "En Vivo Ahora", + "live_now": "En Vivo", "pinned_courses": "Cursos Anclados", "pinned_empty": "No tienes cursos anclados.", "pin": "Anclar", @@ -95,5 +95,8 @@ "enter_your_password": "Introduce tu contraseña", "home": "Inicio", "language_selection": "Idioma", - "language_selection_description": "Selecciona tu idioma preferido." + "language_selection_description": "Selecciona tu idioma preferido.", + "download_complete": "Descarga completa", + "download_failed": "Error de descarga", + "download_cancelled": "Descarga cancelada" } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 04907259..a95708b0 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -95,5 +95,8 @@ "enter_your_password": "Entrez votre mot de passe", "home": "Accueil", "language_selection": "Langue", - "language_selection_description": "Choisissez votre langue préférée." + "language_selection_description": "Choisissez votre langue préférée.", + "download_competed": "Téléchargement terminé", + "download_failed": "Échec du téléchargement", + "download_cancelled": "Téléchargement annulé" } diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index 8cd076cc..2ed4e3e8 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -7,4 +7,4 @@ class L10n { const Locale('fr'), const Locale('es'), ]; -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index 45041552..f29f3b54 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/models/user/user_state_model.dart'; @@ -6,6 +7,7 @@ import 'package:gocast_mobile/utils/UserPreferences.dart'; import 'package:gocast_mobile/utils/globals.dart'; import 'package:gocast_mobile/utils/theme.dart'; import 'package:gocast_mobile/navigation_tab.dart'; +import 'package:gocast_mobile/views/course_view/downloaded_courses_view/downloaded_courses_view.dart'; import 'package:gocast_mobile/views/course_view/list_courses_view/public_courses_view.dart'; import 'package:gocast_mobile/views/login_view/internal_login_view.dart'; import 'package:gocast_mobile/views/on_boarding_view/welcome_screen_view.dart'; @@ -25,7 +27,7 @@ Future main() async { await UserPreferences.init(); runApp( - const ProviderScope( + const ProviderScope( child: App(), ), ); @@ -34,23 +36,34 @@ Future main() async { bool isPushNotificationListenerSet = false; class App extends ConsumerWidget { - - const App({ + const App({ super.key, }); - @override Widget build(BuildContext context, WidgetRef ref) { + final connectivityStatus = ref.watch(connectivityProvider); + connectivityStatus.whenData((result) { + if(result == ConnectivityResult.none) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (ModalRoute.of(context)?.settings.name != '/downloads') { + Navigator.of(context).pushNamed('/downloads'); + return; + } + }); + } + }); + final userState = ref.watch(userViewModelProvider); bool isLoggedIn = ref.watch(userViewModelProvider).user != null; - _handleErrors(ref, userState); _setupNotifications(ref, userState); return MaterialApp( + title: 'GoCast', + debugShowCheckedModeBanner: false, localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, @@ -59,15 +72,12 @@ class App extends ConsumerWidget { ], supportedLocales: L10n.all, locale: Locale(UserPreferences.getLanguage()), - theme: appTheme, // Your light theme - darkTheme: darkAppTheme, // Define your dark theme - themeMode: - ref.watch(themeModeProvider), // Use the theme mode from the provider + theme: appTheme, + darkTheme: darkAppTheme, + themeMode: ref.watch(themeModeProvider), navigatorKey: navigatorKey, scaffoldMessengerKey: scaffoldMessengerKey, - home: !isLoggedIn - ? const WelcomeScreen() - : const NavigationTab(), + home: !isLoggedIn ? const WelcomeScreen() : const NavigationTab(), routes: _buildRoutes(), ); } @@ -91,6 +101,7 @@ class App extends ConsumerWidget { '/login': (context) => const InternalLoginScreen(), '/navigationTab': (context) => const NavigationTab(), '/publiccourses': (context) => const PublicCourses(), + '/downloads': (context) => const DownloadedCourses(), }; } diff --git a/lib/models/course/pinned_course_state_model.dart b/lib/models/course/pinned_course_state_model.dart index 63150dee..2dbeddcd 100644 --- a/lib/models/course/pinned_course_state_model.dart +++ b/lib/models/course/pinned_course_state_model.dart @@ -41,7 +41,8 @@ class PinnedCourseState { isLoading: isLoading ?? this.isLoading, userPinned: userPinned ?? this.userPinned, error: error ?? this.error, - displayedPinnedCourses: displayedPinnedCourses ?? this.displayedPinnedCourses, + displayedPinnedCourses: + displayedPinnedCourses ?? this.displayedPinnedCourses, semesters: semesters ?? this.semesters, selectedSemester: selectedSemester ?? this.selectedSemester, semestersAsString: semestersAsString ?? this.semestersAsString, @@ -63,5 +64,4 @@ class PinnedCourseState { currentAsString: currentAsString, ); } - -} \ No newline at end of file +} diff --git a/lib/models/download/download_state_model.dart b/lib/models/download/download_state_model.dart index f01322f8..fd099448 100644 --- a/lib/models/download/download_state_model.dart +++ b/lib/models/download/download_state_model.dart @@ -9,10 +9,10 @@ class DownloadState { }); DownloadState copyWith({ - Map? downloadedVideos, + required Map downloadedVideos, }) { return DownloadState( - downloadedVideos: downloadedVideos ?? this.downloadedVideos, + downloadedVideos: downloadedVideos, ); } } @@ -21,26 +21,46 @@ class DownloadState { class VideoDetails { final String filePath; final String name; - final int duration; // Duration in seconds or your preferred unit + final String duration; final String description; + final String date; const VideoDetails({ required this.filePath, required this.name, required this.duration, required this.description, + required this.date, }); VideoDetails copyWith({ String? filePath, String? name, - int? duration, + String? duration, + String? description, + String? date, }) { return VideoDetails( filePath: filePath ?? this.filePath, name: name ?? this.name, duration: duration ?? this.duration, - description: description, + description: description ?? this.description, + date: date ?? this.date, ); } + + VideoDetails.fromJson(Map json) + : filePath = json['filePath'], + name = json['name'], + duration = json['duration'], + description = json['description'], + date = json['date']; + + Map toJson() => { + 'filePath': filePath, + 'name': name, + 'duration': duration, + 'description': description, + 'date': date, + }; } diff --git a/lib/models/error/error_model.dart b/lib/models/error/error_model.dart index de8ccfcf..3225e50f 100644 --- a/lib/models/error/error_model.dart +++ b/lib/models/error/error_model.dart @@ -73,7 +73,9 @@ class AppError implements Exception { factory AppError.unknownError(String? message) => AppError('❓An unknown error occurred {message: $message}'); - factory AppError.userError() => AppError('🥱Username or password are incorrect'); + factory AppError.userError() => + AppError('🥱Username or password are incorrect'); - factory AppError.notificationNotAvailableYet() => AppError('🔕Notification not available yet, Set the FireBase keys first'); + factory AppError.notificationNotAvailableYet() => + AppError('🔕Notification not available yet, Set the FireBase keys first'); } diff --git a/lib/providers.dart b/lib/providers.dart index 7de218d8..baecf027 100644 --- a/lib/providers.dart +++ b/lib/providers.dart @@ -1,3 +1,4 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/config/app_config.dart'; @@ -78,3 +79,14 @@ final progressProvider = FutureProvider.autoDispose.family( return videoViewModel.fetchProgressForStream(streamId); }, ); + +final isSearchActiveProvider = StateProvider((ref) => false); + +final playbackSpeedsProvider = StateProvider>((ref) { + return [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]; +}); + +final connectivityProvider = StreamProvider((ref) { + return Connectivity().onConnectivityChanged; +}); + diff --git a/lib/utils/UserPreferences.dart b/lib/utils/UserPreferences.dart index d92f5506..28627dc7 100644 --- a/lib/utils/UserPreferences.dart +++ b/lib/utils/UserPreferences.dart @@ -5,9 +5,11 @@ class UserPreferences { static const _keyLanguage = 'language'; - static Future init() async => _preferences = await SharedPreferences.getInstance(); + static Future init() async => + _preferences = await SharedPreferences.getInstance(); - static Future setLanguage(String language) async => await _preferences?.setString(_keyLanguage, language); + static Future setLanguage(String language) async => + await _preferences?.setString(_keyLanguage, language); static String getLanguage() => _preferences?.getString(_keyLanguage) ?? 'en'; diff --git a/lib/utils/tools.dart b/lib/utils/tools.dart new file mode 100644 index 00000000..6d24ccc3 --- /dev/null +++ b/lib/utils/tools.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; + +class Tools { + //private constructor + Tools._(); + + static String extractCourseIds(String title) { + final pattern = RegExp(r'(?:CIT|IN|MA|CH|MW|PH)\d[\w-]*'); + final matches = pattern.allMatches(title); + List ids = []; + for (var match in matches) { + ids.add(match.group(0)!); + } + return ids.join(' , '); + } + + static String formatDuration(int durationInMinutes) { + int hours = durationInMinutes ~/ 60; + int minutes = durationInMinutes % 60; + int seconds = 0; + + String formattedHours = hours < 10 ? '0$hours' : '$hours'; + String formattedMinutes = minutes < 10 ? '0$minutes' : '$minutes'; + String formattedSeconds = seconds < 10 ? '0$seconds' : '$seconds'; + + return '$formattedHours:$formattedMinutes:$formattedSeconds'; + } + + static Color colorPicker(tumID) { + if (tumID.length < 2) return Colors.grey; + switch (tumID.substring(0, 2)) { + case 'IN': + return Colors.blue; + case 'MA': + return Colors.purple; + case 'CH': + return Colors.green; + case 'PH': + return Colors.orange; + case 'MW': + return Colors.red; + case 'EL': + return Colors.black87; + case 'CI': + return Colors.teal; + default: + return Colors.grey; + } + } +} diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index 4ff37e79..be01210d 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -17,7 +17,8 @@ class ChatViewModel extends StateNotifier { try { final messages = await ChatHandlers(_grpcHandler).getChatMessages(streamId); - state = state.copyWith(messages: messages, isLoading: false, accessDenied: false); + state = state.copyWith( + messages: messages, isLoading: false, accessDenied: false); } catch (e) { state = state.copyWith( error: e as AppError, @@ -30,14 +31,17 @@ class ChatViewModel extends StateNotifier { Future updateMessages(int streamId) async { state = state.copyWith(isLoading: true); state = state.clearError(); - if(state.messages == null) { - fetchChatMessages(streamId); - }else { + if (state.messages == null) { + fetchChatMessages(streamId); + } else { try { - final messages = await ChatHandlers(_grpcHandler).getChatMessages(streamId); + final messages = + await ChatHandlers(_grpcHandler).getChatMessages(streamId); final combinedMessages = List.from(state.messages ?? []) - ..addAll(messages.where((newMessage) => !state.messages!.contains(newMessage))); - state = state.copyWith(messages: combinedMessages, isLoading: false, accessDenied: false); + ..addAll(messages + .where((newMessage) => !state.messages!.contains(newMessage))); + state = state.copyWith( + messages: combinedMessages, isLoading: false, accessDenied: false); } catch (e) { state = state.copyWith( error: e as AppError, @@ -73,8 +77,11 @@ class ChatViewModel extends StateNotifier { } } - Future postMessageReaction(int messageId, int streamId, - String emoji,) async { + Future postMessageReaction( + int messageId, + int streamId, + String emoji, + ) async { try { var reaction = await ChatHandlers(_grpcHandler) .postMessageReaction(messageId, streamId, emoji); @@ -84,8 +91,11 @@ class ChatViewModel extends StateNotifier { } } - Future deleteMessageReaction(int messageId, int streamId, - int reactionId,) async { + Future deleteMessageReaction( + int messageId, + int streamId, + int reactionId, + ) async { try { await ChatHandlers(_grpcHandler) .deleteMessageReaction(messageId, streamId, reactionId); @@ -94,8 +104,11 @@ class ChatViewModel extends StateNotifier { } } - Future postChatReply(int messageId, int streamId, - String message,) async { + Future postChatReply( + int messageId, + int streamId, + String message, + ) async { try { var replay = await ChatHandlers(_grpcHandler) .postChatReply(messageId, streamId, message); @@ -127,7 +140,6 @@ class ChatViewModel extends StateNotifier { state = state.clearError(); } - bool _isRateLimitError(dynamic error) { return error.toString().contains("posting too fast"); } diff --git a/lib/view_models/download_view_model.dart b/lib/view_models/download_view_model.dart index 576fa2af..9a805606 100644 --- a/lib/view_models/download_view_model.dart +++ b/lib/view_models/download_view_model.dart @@ -1,11 +1,13 @@ import 'dart:convert'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/models/download/download_state_model.dart'; +import 'package:gocast_mobile/utils/tools.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:path_provider/path_provider.dart'; import 'package:dio/dio.dart'; import 'package:logger/logger.dart'; import 'dart:io'; +import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; class DownloadViewModel extends StateNotifier { final Logger _logger = Logger(); @@ -19,7 +21,6 @@ class DownloadViewModel extends StateNotifier { return state.downloadedVideos.containsKey(streamIdInt); } - Future initDownloads() async { final prefs = await SharedPreferences.getInstance(); final jsonString = prefs.getString('downloadedVideos'); @@ -34,6 +35,7 @@ class DownloadViewModel extends StateNotifier { name: videoDetailsMap['name'], duration: videoDetailsMap['duration'], description: videoDetailsMap['description'], + date: videoDetailsMap['date'], ); return MapEntry(int.parse(key), videoDetails); }).cast(); // Ensure the map has the correct type @@ -41,47 +43,54 @@ class DownloadViewModel extends StateNotifier { } } - Future downloadVideo(String videoUrl, int streamId, String fileName, - String streamName, int streamDuration, String description,) async { + Future downloadVideo(String videoUrl, Stream stream, + String streamName, String streamDate) async { try { final directory = await getApplicationDocumentsDirectory(); - final filePath = '${directory.path}/$fileName'; + final filePath = + '${directory.path}/${streamName.replaceAll(' ', '_')}.mp4'; Dio dio = Dio(); await dio.download(videoUrl, filePath); _logger.d('Downloaded video to: $filePath'); final prefs = await SharedPreferences.getInstance(); - final int streamIdInt = streamId.toInt(); + final int streamIdInt = stream.id.toInt(); // Create a map for the video details final videoDetailsMap = { 'filePath': filePath, 'name': streamName, - 'duration': streamDuration, - 'description': description, + 'duration': Tools.formatDuration(stream.end + .toDateTime() + .difference(stream.start.toDateTime()) + .inMinutes), + 'description': stream.description, + 'date': streamDate, }; - // Convert video details map to JSON string - final videoDetailsJson = json.encode(videoDetailsMap); - // Save the JSON string in your SharedPreferences - final downloadedVideosJson = Map.from(state.downloadedVideos) - ..[streamIdInt] = videoDetailsJson; + final downloadedVideosJson = + Map.from(state.downloadedVideos) + ..[streamIdInt] = VideoDetails.fromJson(videoDetailsMap); await prefs.setString( 'downloadedVideos', - json.encode(downloadedVideosJson.map((key, value) => - MapEntry(key.toString(), value),),), + json.encode( + downloadedVideosJson.map( + (key, value) => MapEntry(key.toString(), value), + ), + ), ); // Convert the JSON strings back to VideoDetails objects for the state final downloadedVideos = downloadedVideosJson.map((key, value) { - final videoDetailsMap = json.decode(value); + final videoDetailsMap = value.toJson(); final videoDetails = VideoDetails( filePath: videoDetailsMap['filePath'], name: videoDetailsMap['name'], duration: videoDetailsMap['duration'], description: videoDetailsMap['description'], + date: videoDetailsMap['date'], ); return MapEntry(key, videoDetails); }).cast(); @@ -122,25 +131,27 @@ class DownloadViewModel extends StateNotifier { final prefs = await SharedPreferences.getInstance(); final updatedDownloads = Map.from( - state.downloadedVideos,); + state.downloadedVideos, + ); updatedDownloads.remove(videoId); // Save updated list to SharedPreferences // Convert VideoDetails objects to JSON strings before saving await prefs.setString( 'downloadedVideos', - json.encode(updatedDownloads.map((key, value) => - MapEntry(key.toString(), json.encode(value)),),) - ,); + json.encode( + updatedDownloads.map( + (key, value) => MapEntry(key.toString(), json.encode(value)), + ), + ), + ); state = state.copyWith(downloadedVideos: updatedDownloads); } - } - catch (e) { - _logger.e('Error deleting video with ID $videoId: $e'); + } catch (e) { + _logger.e('Error deleting video with ID $videoId: $e'); } } - Future deleteAllDownloads() async { _logger.i('Deleting all downloaded videos'); @@ -165,7 +176,4 @@ class DownloadViewModel extends StateNotifier { _logger.e('Error deleting all videos: $e'); } } - - } - diff --git a/lib/view_models/notification_view_model.dart b/lib/view_models/notification_view_model.dart index 1b653549..cadb83d2 100644 --- a/lib/view_models/notification_view_model.dart +++ b/lib/view_models/notification_view_model.dart @@ -30,7 +30,7 @@ class NotificationViewModel extends StateNotifier { String? deviceToken; try { deviceToken = await _firebaseMessaging.getToken(); - }catch(e){ + } catch (e) { throw AppError.notificationNotAvailableYet(); } // Send device_token to API diff --git a/lib/view_models/pinned_view_model.dart b/lib/view_models/pinned_view_model.dart index 34baae90..1f348bc6 100644 --- a/lib/view_models/pinned_view_model.dart +++ b/lib/view_models/pinned_view_model.dart @@ -26,7 +26,6 @@ class PinnedViewModel extends StateNotifier { } } - Future pinCourse(int courseID) async { state = state.copyWith(isLoading: true); try { @@ -45,7 +44,6 @@ class PinnedViewModel extends StateNotifier { } } - Future unpinCourse(int courseID) async { state = state.copyWith(isLoading: true); try { @@ -113,6 +111,4 @@ class PinnedViewModel extends StateNotifier { void setSelectedSemester(String choice) { state = state.copyWith(selectedSemester: choice); } - - -} \ No newline at end of file +} diff --git a/lib/view_models/setting_view_model.dart b/lib/view_models/setting_view_model.dart index 4df36444..ab87ce5f 100644 --- a/lib/view_models/setting_view_model.dart +++ b/lib/view_models/setting_view_model.dart @@ -14,20 +14,19 @@ class SettingViewModel extends StateNotifier { SettingViewModel(this._grpcHandler) : super(const SettingState()); Future fetchUserSettings() async { - final userSettings = - await SettingsHandler(_grpcHandler).fetchUserSettings(); - state = state.copyWith(userSettings: userSettings); + final userSettings = + await SettingsHandler(_grpcHandler).fetchUserSettings(); + state = state.copyWith(userSettings: userSettings); } Future updateUserSettings(List updatedSettings) async { - - final success = await SettingsHandler(_grpcHandler) - .updateUserSettings(updatedSettings); - if (success) { - state = state.copyWith(userSettings: updatedSettings); - } else { - Logger().e('Failed to update user settings'); - } + final success = + await SettingsHandler(_grpcHandler).updateUserSettings(updatedSettings); + if (success) { + state = state.copyWith(userSettings: updatedSettings); + } else { + Logger().e('Failed to update user settings'); + } } Future loadPreferences() async { @@ -88,8 +87,8 @@ class SettingViewModel extends StateNotifier { } Future updatePreferredGreeting(String newGreeting) async { - await SettingsHandler(_grpcHandler).updateGreeting(newGreeting); - await fetchUserSettings(); + await SettingsHandler(_grpcHandler).updateGreeting(newGreeting); + await fetchUserSettings(); } Future updatePreferredName(String newName) async { @@ -120,5 +119,4 @@ class SettingViewModel extends StateNotifier { void setLoading(bool isLoading) { state = state.copyWith(isLoading: isLoading); } - } diff --git a/lib/view_models/stream_view_model.dart b/lib/view_models/stream_view_model.dart index c399c47c..c155f0dc 100644 --- a/lib/view_models/stream_view_model.dart +++ b/lib/view_models/stream_view_model.dart @@ -25,8 +25,6 @@ class StreamViewModel extends StateNotifier { } } - - void updatedDisplayedStreams(List> allStreams) { state = state.copyWith(displayedStreams: allStreams); } diff --git a/lib/view_models/user_view_model.dart b/lib/view_models/user_view_model.dart index 7be382ad..f6d70d65 100644 --- a/lib/view_models/user_view_model.dart +++ b/lib/view_models/user_view_model.dart @@ -20,12 +20,10 @@ class UserViewModel extends StateNotifier { final GrpcHandler _grpcHandler; - UserViewModel(this._grpcHandler) : super(const UserState()){ - // Check if the user is already logged in + UserViewModel(this._grpcHandler) : super(const UserState()) { _checkToken(); } - /// Handles basic authentication. /// If the authentication is successful, it navigates to the courses screen. /// If the authentication fails, it shows an error message. @@ -68,12 +66,11 @@ class UserViewModel extends StateNotifier { } } - Future logout() async { await TokenHandler.deleteToken('jwt'); await TokenHandler.deleteToken('device_token'); await TokenHandler.deleteToken('jwt_token'); - state=state.reset(); + state = state.reset(); _logger.i('Logged out user and cleared tokens.'); } @@ -85,7 +82,6 @@ class UserViewModel extends StateNotifier { state = state.copyWith(isLoading: true); try { var semesters = await CourseHandler(_grpcHandler).fetchSemesters(); - semesters.item1.add(semesters.item2); state = state.copyWith(current: semesters.item2, isLoading: false); state = state.copyWith(semesters: semesters.item1, isLoading: false); @@ -142,7 +138,6 @@ class UserViewModel extends StateNotifier { state = state.copyWith(displayedCourses: displayedCourses); } - void setUpDisplayedCourses(List allCourses) { CourseUtils.sortCourses(allCourses, 'Newest First'); updatedDisplayedCourses( @@ -159,10 +154,10 @@ class UserViewModel extends StateNotifier { Future _checkToken() async { String token = await _getToken(); - if(token.isNotEmpty && !Jwt.isExpired(token)) { + if (token.isNotEmpty && !Jwt.isExpired(token)) { _logger.i('Token found, fetching user: $token'); fetchUser(); - }else { + } else { _logger.i('Token not found or expired'); } } @@ -170,10 +165,9 @@ class UserViewModel extends StateNotifier { Future _getToken() async { try { return await TokenHandler.loadToken('jwt'); - } catch(e){ + } catch (e) { Logger().w("Token not found"); return ''; } } - } diff --git a/lib/views/chat_view/chat_view.dart b/lib/views/chat_view/chat_view.dart index 5abf8a7d..e7c67790 100644 --- a/lib/views/chat_view/chat_view.dart +++ b/lib/views/chat_view/chat_view.dart @@ -1,14 +1,12 @@ - import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/views/chat_view/chat_view_state.dart'; - class ChatView extends ConsumerStatefulWidget { final int? streamID; const ChatView({ super.key, - this.streamID, + this.streamID, }); @override diff --git a/lib/views/chat_view/chat_view_state.dart b/lib/views/chat_view/chat_view_state.dart index d11eb412..bb6a1a4b 100644 --- a/lib/views/chat_view/chat_view_state.dart +++ b/lib/views/chat_view/chat_view_state.dart @@ -8,14 +8,12 @@ import 'package:gocast_mobile/providers.dart'; import 'package:gocast_mobile/views/chat_view/chat_view.dart'; import 'package:logger/logger.dart'; - class ChatViewState extends ConsumerState { late ScrollController _scrollController; Timer? _updateTimer; bool _isCooldownActive = false; bool _isInitialScrollDone = false; - @override void initState() { super.initState(); @@ -63,7 +61,8 @@ class ChatViewState extends ConsumerState { WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('You are sending messages too fast. Please wait a 60 seconds.'), + content: Text( + 'You are sending messages too fast. Please wait a 60 seconds.'), ), ); }); @@ -89,7 +88,6 @@ class ChatViewState extends ConsumerState { ); } - BoxDecoration getChatDecoration(bool isIOS) { return BoxDecoration( color: Theme.of(context).appBarTheme.backgroundColor, @@ -142,7 +140,9 @@ class ChatViewState extends ConsumerState { BoxDecoration getMessageBubbleStyle(bool isSentByMe, bool isIOS) { return BoxDecoration( - color: isSentByMe ? (isIOS ? CupertinoColors.activeBlue : Colors.blue) : (isIOS ? CupertinoColors.systemGrey6 : Colors.grey[300]), + color: isSentByMe + ? (isIOS ? CupertinoColors.activeBlue : Colors.blue) + : (isIOS ? CupertinoColors.systemGrey6 : Colors.grey[300]), borderRadius: BorderRadius.circular(18), ); } @@ -151,20 +151,30 @@ class ChatViewState extends ConsumerState { TextEditingController controller = TextEditingController(); return Padding( padding: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 15.0), - child: isIOS ? buildIOSMessageInputField(controller) : buildNonIOSMessageInputField(controller), + child: isIOS + ? buildIOSMessageInputField(controller) + : buildNonIOSMessageInputField(controller), ); } Widget buildIOSMessageInputField(TextEditingController controller) { return CupertinoTextField( controller: controller, - placeholder: _isCooldownActive ? 'Wait 30 seconds before sending another message' : 'Type a message...', + placeholder: _isCooldownActive + ? 'Wait 30 seconds before sending another message' + : 'Type a message...', enabled: !_isCooldownActive, suffix: GestureDetector( onTap: () => postMessage(context, ref, controller.text), child: _isCooldownActive - ? const Icon(CupertinoIcons.arrow_up_circle_fill, color: CupertinoColors.systemGrey,) - : const Icon(CupertinoIcons.arrow_up_circle_fill, color: CupertinoColors.activeBlue,), + ? const Icon( + CupertinoIcons.arrow_up_circle_fill, + color: CupertinoColors.systemGrey, + ) + : const Icon( + CupertinoIcons.arrow_up_circle_fill, + color: CupertinoColors.activeBlue, + ), ), decoration: BoxDecoration( color: CupertinoColors.systemGrey6, @@ -178,7 +188,9 @@ class ChatViewState extends ConsumerState { return TextField( controller: controller, decoration: InputDecoration( - hintText: _isCooldownActive ? 'Wait 30 seconds before sending another message' : 'Type a message...', + hintText: _isCooldownActive + ? 'Wait 30 seconds before sending another message' + : 'Type a message...', enabled: !_isCooldownActive, suffixIcon: GestureDetector( onTap: () => postMessage(context, ref, controller.text), @@ -199,7 +211,9 @@ class ChatViewState extends ConsumerState { void postMessage(BuildContext context, WidgetRef ref, String message) { if (!_isCooldownActive && message.isNotEmpty && message.trim().isNotEmpty) { final int? streamId = widget.streamID; - ref.read(chatViewModelProvider.notifier).postChatMessage(streamId!, message); + ref + .read(chatViewModelProvider.notifier) + .postChatMessage(streamId!, message); // Start cooldown Logger().i('Cooldown started'); setState(() { @@ -216,7 +230,6 @@ class ChatViewState extends ConsumerState { } } - void _scrollToBottom() { if (!_isInitialScrollDone && mounted && _scrollController.hasClients) { _scrollController.animateTo( @@ -227,6 +240,4 @@ class ChatViewState extends ConsumerState { _isInitialScrollDone = true; } } - - } diff --git a/lib/views/chat_view/inactive_view.dart b/lib/views/chat_view/inactive_view.dart index 7cf7f08d..ca3c9455 100644 --- a/lib/views/chat_view/inactive_view.dart +++ b/lib/views/chat_view/inactive_view.dart @@ -7,7 +7,6 @@ import 'package:gocast_mobile/views/video_view/video_player.dart'; import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class InactiveView extends ConsumerStatefulWidget { final int? streamID; @@ -64,7 +63,8 @@ class InactiveViewState extends ConsumerState { ), child: Text( chatState.accessDenied - ? AppLocalizations.of(context)!.chat_is_disabled_for_this_lecture + ? AppLocalizations.of(context)! + .chat_is_disabled_for_this_lecture : AppLocalizations.of(context)!.chat_is_hidden, textAlign: TextAlign.center, style: const TextStyle( diff --git a/lib/views/chat_view/poll_view_state.dart b/lib/views/chat_view/poll_view_state.dart index c71b7e4c..95ae1787 100644 --- a/lib/views/chat_view/poll_view_state.dart +++ b/lib/views/chat_view/poll_view_state.dart @@ -5,7 +5,6 @@ import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; import 'package:gocast_mobile/providers.dart'; import 'package:gocast_mobile/views/chat_view/poll_view.dart'; - class PollViewState extends ConsumerState { Timer? _updateTimer; Map selectedOptions = {}; @@ -66,7 +65,10 @@ class PollViewState extends ConsumerState { } Widget _buildPollCard( - BuildContext context, Poll poll, Map answeredPolls,) { + BuildContext context, + Poll poll, + Map answeredPolls, + ) { bool isAnswered = answeredPolls.containsKey(poll.id); return isAnswered ? _buildAnsweredPollCard(context, poll, answeredPolls[poll.id]) @@ -74,7 +76,10 @@ class PollViewState extends ConsumerState { } Widget _buildAnsweredPollCard( - BuildContext context, Poll poll, int? answeredOptionId,) { + BuildContext context, + Poll poll, + int? answeredOptionId, + ) { ThemeData themeData = Theme.of(context); return Opacity( opacity: 0.5, @@ -105,13 +110,16 @@ class PollViewState extends ConsumerState { physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, - childAspectRatio: - 3, + childAspectRatio: 3, ), itemCount: poll.pollOptions.length, itemBuilder: (context, index) { - return _buildInactivePollOption(context, poll, - poll.pollOptions[index], answeredOptionId,); + return _buildInactivePollOption( + context, + poll, + poll.pollOptions[index], + answeredOptionId, + ); }, ), ], @@ -122,23 +130,25 @@ class PollViewState extends ConsumerState { ); } - Widget _buildInactivePollOption(BuildContext context, Poll poll, - PollOption option, int? selectedOptionId,) { + Widget _buildInactivePollOption( + BuildContext context, + Poll poll, + PollOption option, + int? selectedOptionId, + ) { bool isSelected = option.id == selectedOptionId; return Container( margin: const EdgeInsets.all(4.0), decoration: BoxDecoration( color: isSelected ? Colors.grey : Colors.white, borderRadius: BorderRadius.circular(8.0), - border: - Border.all(color: Colors.grey), + border: Border.all(color: Colors.grey), ), child: Center( child: Text( option.answer, style: const TextStyle( - color: - Colors.black, + color: Colors.black, ), ), ), @@ -173,13 +183,15 @@ class PollViewState extends ConsumerState { physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, - childAspectRatio: - 3, + childAspectRatio: 3, ), itemCount: poll.pollOptions.length, itemBuilder: (context, index) { return _buildActivePollOption( - context, poll, poll.pollOptions[index],); + context, + poll, + poll.pollOptions[index], + ); }, ), _buildSubmitButton(poll), @@ -191,7 +203,10 @@ class PollViewState extends ConsumerState { } Widget _buildActivePollOption( - BuildContext context, Poll poll, PollOption option,) { + BuildContext context, + Poll poll, + PollOption option, + ) { bool isSelected = selectedOptions[poll.id] == option.id; return GestureDetector( onTap: () { @@ -200,7 +215,8 @@ class PollViewState extends ConsumerState { }); }, child: Container( - margin: const EdgeInsets.all(4.0), // Add some spacing around each button + margin: + const EdgeInsets.all(4.0), // Add some spacing around each button decoration: BoxDecoration( color: isSelected ? Colors.blue : Colors.white, // Change color based on selection @@ -231,13 +247,11 @@ class PollViewState extends ConsumerState { poll.question, style: const TextStyle( fontSize: 16.0, - fontWeight: FontWeight - .bold, + fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, maxLines: 2, - overflow: TextOverflow - .ellipsis, + overflow: TextOverflow.ellipsis, ), ), ], @@ -249,18 +263,19 @@ class PollViewState extends ConsumerState { final pollViewModel = ref.read(pollViewModelProvider.notifier); return Padding( - padding: const EdgeInsets.all(8.0), // Consistent padding with the rest of the layout + padding: const EdgeInsets.all( + 8.0), // Consistent padding with the rest of the layout child: ElevatedButton( onPressed: selectedOptions.containsKey(poll.id) ? () { - final int? pollOptionId = selectedOptions[poll.id]; - if (pollOptionId != null) { - setState(() { - pollViewModel.postPollVote(poll.streamID, pollOptionId); - pollViewModel.postAnsweredPoll(poll.id, pollOptionId); - }); - } - } + final int? pollOptionId = selectedOptions[poll.id]; + if (pollOptionId != null) { + setState(() { + pollViewModel.postPollVote(poll.streamID, pollOptionId); + pollViewModel.postAnsweredPoll(poll.id, pollOptionId); + }); + } + } : null, style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, @@ -279,5 +294,4 @@ class PollViewState extends ConsumerState { ), ); } - } diff --git a/lib/views/chat_view/suggested_streams_list.dart b/lib/views/chat_view/suggested_streams_list.dart index 4f4891a3..5e7dbf49 100644 --- a/lib/views/chat_view/suggested_streams_list.dart +++ b/lib/views/chat_view/suggested_streams_list.dart @@ -21,7 +21,9 @@ class SuggestedStreamsWidget extends StatelessWidget { final stream = suggestedStreams[index]; return ListTile( leading: const Icon(Icons.play_circle_outline), - title: Text(stream.name), + title: Text(stream.name != '' + ? stream.name + : 'Lecture: ${DateFormat('EEEE. dd', Localizations.localeOf(context).toString()).format(stream.start.toDateTime())}'), subtitle: Text( DateFormat('dd MMMM yyyy').format(stream.start.toDateTime()), ), diff --git a/lib/views/components/custom_bottom_nav_bar.dart b/lib/views/components/custom_bottom_nav_bar.dart index b3b70486..38294458 100644 --- a/lib/views/components/custom_bottom_nav_bar.dart +++ b/lib/views/components/custom_bottom_nav_bar.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/providers.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class CustomBottomNavBar extends ConsumerWidget { const CustomBottomNavBar({super.key}); diff --git a/lib/views/components/custom_search_filter_top_nav_bar.dart b/lib/views/components/custom_search_filter_top_nav_bar.dart deleted file mode 100644 index 78cbf464..00000000 --- a/lib/views/components/custom_search_filter_top_nav_bar.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - - -class CustomSearchFilterTopNavBar extends StatelessWidget - implements PreferredSizeWidget { - final TextEditingController searchController; - final VoidCallback? onBackButtonPressed; - final VoidCallback? onFilterButtonPressed; - - const CustomSearchFilterTopNavBar({ - super.key, - required this.searchController, - this.onBackButtonPressed, - this.onFilterButtonPressed, - }); - - @override - Widget build(BuildContext context) { - return AppBar( - leading: IconButton( - icon: const Icon(Icons.arrow_back_ios), - onPressed: onBackButtonPressed ?? - () { - // TODO: Define back button functionality - }, - ), - title: Container( - height: 40, - margin: const EdgeInsets.only(right: 8, left: 8), - decoration: BoxDecoration( - color: const Color(0xEDFAFAFA), - borderRadius: BorderRadius.circular(10), - ), - child: TextField( - controller: searchController, - decoration: InputDecoration( - border: InputBorder.none, - hintText: AppLocalizations.of(context)!.search, - prefixIcon: const Icon(Icons.search, color: Color(0x993C3C43)), - hintStyle: const TextStyle( - color: Color(0x993C3C43), - fontSize: 17, - fontFamily: 'SF Pro Text', - fontWeight: FontWeight.w400, - letterSpacing: -0.41, - ), - ), - ), - ), - actions: [ - IconButton( - icon: const Icon(Icons.filter_list_rounded), - onPressed: onFilterButtonPressed ?? - () { - // TODO: Define filter button functionality - }, - ), - ], - titleSpacing: 0.0, - ); - } - - @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); -} diff --git a/lib/views/components/custom_search_top_nav_bar.dart b/lib/views/components/custom_search_top_nav_bar.dart index 4411a5b5..6e07f3f2 100644 --- a/lib/views/components/custom_search_top_nav_bar.dart +++ b/lib/views/components/custom_search_top_nav_bar.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:gocast_mobile/providers.dart'; import 'package:gocast_mobile/views/components/filter_popup_menu_button.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class CustomSearchTopNavBar extends ConsumerWidget implements PreferredSizeWidget { final TextEditingController searchController; @@ -117,5 +117,3 @@ class CustomSearchTopNavBar extends ConsumerWidget @override Size get preferredSize => const Size.fromHeight(50); } - -final isSearchActiveProvider = StateProvider((ref) => false); diff --git a/lib/views/components/custom_search_top_nav_bar_back_button.dart b/lib/views/components/custom_search_top_nav_bar_back_button.dart index 097603bf..a62111a5 100644 --- a/lib/views/components/custom_search_top_nav_bar_back_button.dart +++ b/lib/views/components/custom_search_top_nav_bar_back_button.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/views/components/filter_popup_menu_button.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class CustomSearchTopNavBarWithBackButton extends ConsumerWidget implements PreferredSizeWidget { final TextEditingController searchController; diff --git a/lib/views/components/filter_popup_menu_button.dart b/lib/views/components/filter_popup_menu_button.dart index 7e22ce19..376464ca 100644 --- a/lib/views/components/filter_popup_menu_button.dart +++ b/lib/views/components/filter_popup_menu_button.dart @@ -17,19 +17,18 @@ class FilterPopupMenuButton extends ConsumerWidget { // Assuming both selectedSemester and pinnedSelectedSemester are always the same, // we can just use one of them for the check mark logic. final selectedSemester = ref.read(userViewModelProvider).selectedSemester; - final selectedSortedOption = ref.read(videoViewModelProvider).selectedFilterOption; + final selectedSortedOption = + ref.read(videoViewModelProvider).selectedFilterOption; return PopupMenuButton( onSelected: (choice) { - // When an item is selected, update both providers to ensure they stay in sync - ref.read(userViewModelProvider.notifier).setSelectedSemester(choice); - ref.read(pinnedCourseViewModelProvider.notifier).setSelectedSemester(choice); onClick(choice); }, itemBuilder: (BuildContext context) { return filterOptions.map((choice) { // Check if the item is selected - bool isSelected = selectedSemester == choice || selectedSortedOption == choice; + bool isSelected = + selectedSemester == choice || selectedSortedOption == choice; return PopupMenuItem( value: choice, @@ -38,7 +37,8 @@ class FilterPopupMenuButton extends ConsumerWidget { title: Text(choice), trailing: Opacity( opacity: isSelected ? 1.0 : 0.0, - child: Icon(Icons.check, color: Theme.of(context).iconTheme.color), + child: + Icon(Icons.check, color: Theme.of(context).iconTheme.color), ), ), ); @@ -47,7 +47,4 @@ class FilterPopupMenuButton extends ConsumerWidget { icon: Icon(Icons.filter_list, color: Theme.of(context).iconTheme.color), ); } - - - } diff --git a/lib/views/course_view/components/course_card.dart b/lib/views/course_view/components/course_card.dart index fb24f71e..125cfc7e 100644 --- a/lib/views/course_view/components/course_card.dart +++ b/lib/views/course_view/components/course_card.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:gocast_mobile/utils/tools.dart'; class CourseCard extends StatelessWidget { final String title; @@ -9,7 +10,6 @@ class CourseCard extends StatelessWidget { final VoidCallback onTap; final int courseId; - //for displaying courses final bool? live; @@ -21,20 +21,49 @@ class CourseCard extends StatelessWidget { //for displaying livestreams final String? subtitle; - const CourseCard({ + const CourseCard._({ super.key, required this.title, - this.subtitle, required this.tumID, - required this.courseId, required this.onTap, + required this.courseId, + // Pass other fields as before this.live, this.course, this.onPinUnpin, this.isPinned, required this.isLoggedIn, + this.subtitle, }); + factory CourseCard({ + Key? key, + required String title, + String? subtitle, + required int courseId, + required VoidCallback onTap, + bool? live, + Course? course, + Function(Course)? onPinUnpin, + bool? isPinned, + required bool isLoggedIn, + }) { + final tumID = Tools.extractCourseIds(title); + return CourseCard._( + key: key, + title: title, + tumID: tumID, + courseId: courseId, + onTap: onTap, + live: live, + course: course, + onPinUnpin: onPinUnpin, + isPinned: isPinned, + isLoggedIn: isLoggedIn, + subtitle: subtitle, + ); + } + @override Widget build(BuildContext context) { ThemeData themeData = Theme.of(context); @@ -61,15 +90,15 @@ class CourseCard extends StatelessWidget { ), child: ClipRRect( borderRadius: BorderRadius.circular(8.0), - child: _buildCourseCard( - themeData, - cardWidth, - context, - course!, - onPinUnpin!, - isPinned!, - isLoggedIn, - ), + child: _buildCourseCard( + themeData, + cardWidth, + context, + course!, + onPinUnpin!, + isPinned!, + isLoggedIn, + ), ), ), ); @@ -82,39 +111,40 @@ class CourseCard extends StatelessWidget { Course course, Function(Course) onPinUnpin, bool isPinned, - bool isLoggedIn, + bool isLoggedIn, ) { - return Slidable( key: ValueKey(course.id), closeOnScroll: true, - endActionPane: isLoggedIn ? ActionPane( - motion: const DrawerMotion(), - dragDismissible: true, - children: [ - if (isPinned) - SlidableAction( - autoClose: true, - onPressed: (_) async { - bool confirmUnpin = await _confirmUnpin(context); - if (confirmUnpin) onPinUnpin(course); - }, - backgroundColor: Colors.red, - foregroundColor: Colors.white, - icon: Icons.push_pin_outlined, - label: AppLocalizations.of(context)!.unpin , - ), - if (!isPinned) - SlidableAction( - autoClose: true, - onPressed: (_) => onPinUnpin(course), - backgroundColor: Colors.blue, - foregroundColor: Colors.white, - icon: Icons.push_pin, - label: AppLocalizations.of(context)!.pin, - ), - ], - ) : null, + endActionPane: isLoggedIn + ? ActionPane( + motion: const DrawerMotion(), + dragDismissible: true, + children: [ + if (isPinned) + SlidableAction( + autoClose: true, + onPressed: (_) async { + bool confirmUnpin = await _confirmUnpin(context); + if (confirmUnpin) onPinUnpin(course); + }, + backgroundColor: Colors.red, + foregroundColor: Colors.white, + icon: Icons.push_pin_outlined, + label: AppLocalizations.of(context)!.unpin, + ), + if (!isPinned) + SlidableAction( + autoClose: true, + onPressed: (_) => onPinUnpin(course), + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + icon: Icons.push_pin, + label: AppLocalizations.of(context)!.pin, + ), + ], + ) + : null, child: IntrinsicHeight( child: Row( children: [ @@ -135,7 +165,16 @@ class CourseCard extends StatelessWidget { ), Padding( padding: const EdgeInsets.symmetric(vertical: 3.0), - child: _buildCourseTitle(themeData.textTheme), + child: Row( + children: [ + Expanded( + child: _buildCourseTitle(themeData.textTheme), + ), + if (isPinned) + Icon(Icons.push_pin, + color: themeData.primaryColor, size: 16), + ], + ), ), ], ), @@ -153,7 +192,8 @@ class CourseCard extends StatelessWidget { builder: (BuildContext context) { return AlertDialog( title: Text(AppLocalizations.of(context)!.confirm_unpin_title), - content: Text(AppLocalizations.of(context)!.confirm_unpin_message), + content: + Text(AppLocalizations.of(context)!.confirm_unpin_message), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), @@ -170,14 +210,10 @@ class CourseCard extends StatelessWidget { false; } - - - - Widget _buildCourseIsLive(BuildContext context) { if (live == null) return const SizedBox(); return live! - ? Row( + ? Row( children: [ const Icon( Icons.circle, @@ -200,30 +236,10 @@ class CourseCard extends StatelessWidget { Widget _buildCourseColor() { return Container( width: 5, - color: _colorPicker(), + color: Tools.colorPicker(tumID), ); } - Color _colorPicker() { - if (tumID.length < 2) return Colors.grey; - switch (tumID.substring(0, 2)) { - case 'IN': - return Colors.blue; - case 'MA': - return Colors.purple; - case 'CH': - return Colors.green; - case 'PH': - return Colors.orange; - case 'MW': - return Colors.red; - case 'EL': - return Colors.black87; - default: - return Colors.grey; - } - } - Widget _buildCourseTitle(TextTheme textTheme) { return Text( title, diff --git a/lib/views/course_view/components/course_section.dart b/lib/views/course_view/components/course_section.dart index aa5d35e9..28e0c398 100644 --- a/lib/views/course_view/components/course_section.dart +++ b/lib/views/course_view/components/course_section.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; @@ -9,7 +8,6 @@ import 'package:gocast_mobile/views/course_view/components/course_card.dart'; import 'package:gocast_mobile/views/course_view/course_detail_view/course_detail_view.dart'; import 'dart:math' as math; - /// CourseSection /// /// A reusable stateless widget to display a specific course section. @@ -27,8 +25,7 @@ import 'dart:math' as math; /// different titles, courses and onViewAll actions. class CourseSection extends StatelessWidget { final String sectionTitle; - final SectionKind - sectionKind; + final SectionKind sectionKind; final List courses; final List streams; final VoidCallback? onViewAll; @@ -77,10 +74,12 @@ class CourseSection extends StatelessWidget { Widget _buildCourseList(BuildContext context) { bool isTablet = MediaQuery.of(context).size.width >= 600 ? true : false; int displayCount = math.min(courses.length, 3); - double cardHeight = 75; + double cardHeight = 95; return ConstrainedBox( - constraints: BoxConstraints(maxHeight: isTablet ? double.infinity : cardHeight * displayCount,), + constraints: BoxConstraints( + maxHeight: isTablet ? double.infinity : cardHeight * displayCount, + ), child: ListView.builder( physics: const ClampingScrollPhysics(), shrinkWrap: true, @@ -98,7 +97,6 @@ class CourseSection extends StatelessWidget { isPinned: isPinned, onPinUnpin: (course) => _togglePin(course, isPinned), title: course.name, - tumID: course.tUMOnlineIdentifier, live: streams.any((stream) => stream.courseID == course.id), courseId: course.id, onTap: () { diff --git a/lib/views/course_view/components/live_stream_section.dart b/lib/views/course_view/components/live_stream_section.dart index 7195934a..81c0a042 100644 --- a/lib/views/course_view/components/live_stream_section.dart +++ b/lib/views/course_view/components/live_stream_section.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; +import 'package:gocast_mobile/utils/tools.dart'; import 'package:gocast_mobile/views/course_view/components/pulse_background.dart'; import 'package:gocast_mobile/views/course_view/components/small_stream_card.dart'; @@ -85,10 +86,11 @@ class LiveStreamSection extends StatelessWidget { return SmallStreamCard( title: stream.item1.name, subtitle: course.name, - tumID: course.tUMOnlineIdentifier, + tumID: Tools.extractCourseIds(course.name), roomName: stream.item1.roomName, roomNumber: stream.item1.roomCode, path: imagePath, + isDownloaded: false, courseId: course.id, onTap: () { Navigator.push( diff --git a/lib/views/course_view/components/pin_button.dart b/lib/views/course_view/components/pin_button.dart index 0c4ab797..2bf81701 100644 --- a/lib/views/course_view/components/pin_button.dart +++ b/lib/views/course_view/components/pin_button.dart @@ -20,7 +20,8 @@ class PinButton extends ConsumerWidget { if (!isLoggedIn) { return const Icon( Icons.push_pin_outlined, - color: Colors.transparent,); + color: Colors.transparent, + ); } return StatefulBuilder( builder: (context, setState) { diff --git a/lib/views/course_view/components/pulse_background.dart b/lib/views/course_view/components/pulse_background.dart index 6cfca0d7..23559f32 100644 --- a/lib/views/course_view/components/pulse_background.dart +++ b/lib/views/course_view/components/pulse_background.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class PulsingBackground extends StatefulWidget { const PulsingBackground({super.key}); diff --git a/lib/views/course_view/components/small_stream_card.dart b/lib/views/course_view/components/small_stream_card.dart index 70c20b0c..a7315e24 100644 --- a/lib/views/course_view/components/small_stream_card.dart +++ b/lib/views/course_view/components/small_stream_card.dart @@ -4,6 +4,7 @@ import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; import 'package:gocast_mobile/utils/constants.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +//import 'package:http/http.dart' as http; class SmallStreamCard extends StatelessWidget { final String title; @@ -75,29 +76,33 @@ class SmallStreamCard extends StatelessWidget { ); } - Widget _buildStreamCard(BuildContext context, ThemeData themeData, double cardWidth) { - return (isDownloaded!=null && showDeleteConfirmationDialog!=null) ? _buildDownloadedCard(context, themeData, cardWidth) : _buildLiveCard(themeData, cardWidth); + Widget _buildStreamCard( + BuildContext context, ThemeData themeData, double cardWidth) { + return (isDownloaded != null && showDeleteConfirmationDialog != null) + ? _buildDownloadedCard(context, themeData, cardWidth) + : _buildLiveCard(themeData, cardWidth); } - Widget _buildDownloadedCard (BuildContext context, ThemeData themeData, double cardWidth) { + Widget _buildDownloadedCard( + BuildContext context, ThemeData themeData, double cardWidth) { return Slidable( - key: Key(courseId.toString()), - closeOnScroll: true, - endActionPane: ActionPane( - motion: const DrawerMotion(), - dragDismissible: true, - children: [ - SlidableAction( - onPressed: (_) => showDeleteConfirmationDialog!(courseId!), - autoClose: true, - backgroundColor: Colors.red, - foregroundColor: Colors.white, - icon: Icons.delete_rounded, - label: AppLocalizations.of(context)!.delete, - ), - ], - ), - child: _buildLiveCard(themeData, cardWidth*1.3), + key: Key(courseId.toString()), + closeOnScroll: true, + endActionPane: ActionPane( + motion: const DrawerMotion(), + dragDismissible: true, + children: [ + SlidableAction( + onPressed: (_) => showDeleteConfirmationDialog!(courseId!), + autoClose: true, + backgroundColor: Colors.red, + foregroundColor: Colors.white, + icon: Icons.delete_rounded, + label: AppLocalizations.of(context)!.delete, + ), + ], + ), + child: _buildLiveCard(themeData, cardWidth * 1.3), ); } @@ -131,7 +136,7 @@ class SmallStreamCard extends StatelessWidget { _buildCourseTitle(themeData.textTheme), _buildCourseSubtitle(themeData.textTheme), const SizedBox(height: 15), - _buildLocation(themeData), + if (!isDownloaded!) _buildLocation(themeData), ], ), ), @@ -154,30 +159,29 @@ class SmallStreamCard extends StatelessWidget { AppImages.course1, fit: BoxFit.cover, ) - : - Image.network( - path!, // Use the image URL - fit: BoxFit.cover, // Maintain the cover fit - loadingBuilder: (context, child, loadingProgress) { - if (loadingProgress == null) { - return child; // Image is fully loaded - } - return Center( - child: CircularProgressIndicator( - value: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded / - (loadingProgress.expectedTotalBytes ?? 1) - : null, + : Image.network( + path!, // Use the image URL + fit: BoxFit.cover, // Maintain the cover fit + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) { + return child; // Image is fully loaded + } + return Center( + child: CircularProgressIndicator( + value: loadingProgress.expectedTotalBytes != null + ? loadingProgress.cumulativeBytesLoaded / + (loadingProgress.expectedTotalBytes ?? 1) + : null, + ), + ); + }, + errorBuilder: (context, error, stackTrace) { + return Image.asset( + AppImages.course1, + fit: BoxFit.cover, + ); + }, ), - ); - }, - errorBuilder: (context, error, stackTrace) { - return Image.asset( - AppImages.course1, - fit: BoxFit.cover, - ); - }, - ), ), ), ], @@ -185,12 +189,26 @@ class SmallStreamCard extends StatelessWidget { } Widget _buildLocation(ThemeData themeData) { - if (roomNumber == null) return const SizedBox(); + final isLocationEmpty = + (roomName?.isEmpty ?? true) && (roomNumber?.isEmpty ?? true); + + return isLocationEmpty + ? _buildInactiveLocation(themeData) + : _buildActiveLocation(themeData); + } - final Uri url = Uri.parse('https://nav.tum.de/room/$roomNumber'); + Widget _buildActiveLocation(ThemeData themeData) { + // Determine the URL based on the availability of roomNumber + final Uri url = roomNumber?.isNotEmpty ?? false + ? Uri.parse( + 'https://nav.tum.de/room/$roomNumber', + ) // Use roomNumber in URL if available + : Uri.parse( + 'https://nav.tum.de/search?q=${Uri.encodeComponent(roomName ?? '')}', + ); // Fall back to search URL using roomName return Align( - alignment: Alignment.centerRight, // Align to the right + alignment: Alignment.centerRight, child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( @@ -204,18 +222,19 @@ class SmallStreamCard extends StatelessWidget { ), child: InkWell( onTap: () async { - if (await canLaunchUrl(url)) { - await launchUrl(url); - } else { - throw 'Could not launch $url'; + try { + if (await canLaunchUrl(url)) { + await launchUrl(url); + } + } catch (e) { + // Handle exceptions or errors here } }, child: const Row( mainAxisSize: MainAxisSize.min, - // Constrain row size to its children children: [ Icon(Icons.room, size: 24), - SizedBox(width: 8), // Spacing before the arrow icon + SizedBox(width: 8), Icon(Icons.arrow_forward_ios, size: 16), ], ), @@ -224,6 +243,27 @@ class SmallStreamCard extends StatelessWidget { ); } + Widget _buildInactiveLocation(ThemeData themeData) { + return Align( + alignment: Alignment.centerRight, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.withOpacity(0.4)), + borderRadius: BorderRadius.circular(5), + ), + child: const Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.room, size: 24, color: Colors.grey), + SizedBox(width: 8), + Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey), + ], + ), + ), + ); + } + Widget _buildCourseTumID() { return Text( tumID, diff --git a/lib/views/course_view/components/stream_card.dart b/lib/views/course_view/components/stream_card.dart index 21868822..cf1ffc18 100644 --- a/lib/views/course_view/components/stream_card.dart +++ b/lib/views/course_view/components/stream_card.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; import 'package:gocast_mobile/providers.dart'; import 'package:gocast_mobile/utils/constants.dart'; +import 'package:gocast_mobile/utils/tools.dart'; import 'package:gocast_mobile/views/video_view/video_player.dart'; import 'package:intl/intl.dart'; @@ -23,9 +24,6 @@ class StreamCard extends ConsumerStatefulWidget { } class StreamCardState extends ConsumerState { - String imageName = - 'https://live.rbg.tum.de/thumb-fallback.png'; // Replace with your image URL - @override void initState() { super.initState(); @@ -71,7 +69,9 @@ class StreamCardState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildHeader( - title: widget.stream.name, + title: widget.stream.name != '' + ? widget.stream.name + : 'Lecture: ${DateFormat('EEEE. dd', Localizations.localeOf(context).toString()).format(widget.stream.start.toDateTime())}', subtitle: widget.stream.description, length: widget.stream.duration, themeData: themeData, @@ -102,7 +102,7 @@ class StreamCardState extends ConsumerState { AspectRatio( aspectRatio: 16 / 9, child: Image.network( - imageName, + widget.imageName, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) { @@ -170,18 +170,6 @@ class StreamCardState extends ConsumerState { ); } - String formatDuration(int durationInMinutes) { - int hours = durationInMinutes ~/ 60; - int minutes = durationInMinutes % 60; - int seconds = 0; - - String formattedHours = hours < 10 ? '0$hours' : '$hours'; - String formattedMinutes = minutes < 10 ? '0$minutes' : '$minutes'; - String formattedSeconds = seconds < 10 ? '0$seconds' : '$seconds'; - - return '$formattedHours:$formattedMinutes:$formattedSeconds'; - } - Widget _buildStreamDate(ThemeData themeData) { return Container( decoration: BoxDecoration( @@ -207,7 +195,10 @@ class StreamCardState extends ConsumerState { ), padding: const EdgeInsets.all(5), child: Text( - formatDuration(widget.stream.duration), + Tools.formatDuration(widget.stream.end + .toDateTime() + .difference(widget.stream.start.toDateTime()) + .inMinutes), style: themeData.textTheme.labelSmall?.copyWith( fontSize: 12, color: Colors.white, diff --git a/lib/views/course_view/course_detail_view/course_detail_view.dart b/lib/views/course_view/course_detail_view/course_detail_view.dart index a9e8078b..57d40041 100644 --- a/lib/views/course_view/course_detail_view/course_detail_view.dart +++ b/lib/views/course_view/course_detail_view/course_detail_view.dart @@ -71,7 +71,7 @@ class CourseDetailState extends ConsumerState { videoViewModelNotifier.updatedDisplayedStreams(temp); isSearchInitialized = false; } else { - displayedStreams = displayedStreams.where((stream) { + displayedStreams = temp.where((stream) { return stream.item1.name.toLowerCase().contains(searchInput) || stream.item1.description.toLowerCase().contains(searchInput); }).toList(); @@ -88,7 +88,7 @@ class CourseDetailState extends ConsumerState { appBar: CustomSearchTopNavBarWithBackButton( searchController: searchController, onClick: _handleSortOptionSelected, - filterOptions: [AppLocalizations.of(context)!.newest_first, AppLocalizations.of(context)!.oldest_first], + filterOptions: const ['Newest First', 'Oldest First'], ), body: RefreshIndicator( onRefresh: () => _refreshStreams(widget.courseId), @@ -167,7 +167,7 @@ class CourseDetailState extends ConsumerState { ), ), ) - : Center(child: Text(AppLocalizations.of(context)!.no_courses_found)), + : Center(child: Text(AppLocalizations.of(context)!.no_courses_found)), ); } @@ -181,7 +181,6 @@ class CourseDetailState extends ConsumerState { final streamWithThumb = streamsWithThumb[index]; final stream = streamWithThumb.item1; final thumbnail = _getThumbnailUrl(streamWithThumb.item2); - return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: StreamCard( @@ -251,7 +250,8 @@ class CourseDetailState extends ConsumerState { } bool _checkPinStatus() { - final userPinned = ref.watch(pinnedCourseViewModelProvider).userPinned ?? []; + final userPinned = + ref.watch(pinnedCourseViewModelProvider).userPinned ?? []; // Iterate over the userPinned list and check if courseId matches for (var course in userPinned) { if (course.id == widget.courseId) { diff --git a/lib/views/course_view/courses_overview.dart b/lib/views/course_view/courses_overview.dart index d95aadee..fa1d82ac 100644 --- a/lib/views/course_view/courses_overview.dart +++ b/lib/views/course_view/courses_overview.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/providers.dart'; import 'package:gocast_mobile/utils/section_kind.dart'; +import 'package:gocast_mobile/utils/sort_utils.dart'; import 'package:gocast_mobile/views/components/base_view.dart'; import 'package:gocast_mobile/views/course_view/components/course_section.dart'; import 'package:gocast_mobile/views/course_view/components/live_stream_section.dart'; @@ -49,6 +50,12 @@ class CourseOverviewState extends ConsumerState { } final userCourses = ref.watch(userViewModelProvider).userCourses ?? []; final publicCourses = ref.watch(userViewModelProvider).publicCourses ?? []; + final currentSemester = + ref.watch(userViewModelProvider).currentAsString ?? "All"; + final userCoursesCurrent = + CourseUtils.filterCoursesBySemester(userCourses, currentSemester); + final publicCoursesCurrent = + CourseUtils.filterCoursesBySemester(publicCourses, currentSemester); final liveStreams = ref.watch(videoViewModelProvider).liveStreams; final liveStreamWithThumb = ref.watch(videoViewModelProvider).liveStreamsWithThumb ?? []; @@ -72,11 +79,11 @@ class CourseOverviewState extends ConsumerState { onRefresh: _refreshData, child: ListView( children: [ - Center( - child: LiveStreamSection( + Center( + child: LiveStreamSection( ref: ref, sectionTitle: AppLocalizations.of(context)!.live_now, - courses: (userCourses) + (publicCourses), + courses: (userCoursesCurrent) + (publicCoursesCurrent), streams: liveStreamWithThumb, ), ), @@ -88,7 +95,7 @@ class CourseOverviewState extends ConsumerState { child: _buildSection( AppLocalizations.of(context)!.my_courses, SectionKind.myCourses, - userCourses, + userCoursesCurrent, liveStreams, ), ), @@ -96,7 +103,7 @@ class CourseOverviewState extends ConsumerState { child: _buildSection( AppLocalizations.of(context)!.public_courses, SectionKind.publicCourses, - publicCourses, + publicCoursesCurrent, liveStreams, ), ), @@ -106,13 +113,13 @@ class CourseOverviewState extends ConsumerState { _buildSection( AppLocalizations.of(context)!.my_courses, SectionKind.myCourses, - userCourses, + userCoursesCurrent, liveStreams, ), _buildSection( AppLocalizations.of(context)!.public_courses, SectionKind.publicCourses, - publicCourses, + publicCoursesCurrent, liveStreams, ), ], @@ -123,10 +130,12 @@ class CourseOverviewState extends ConsumerState { ); } - Widget _buildSection(String title, - SectionKind sectionKind, - courses, - streams,) { + Widget _buildSection( + String title, + SectionKind sectionKind, + courses, + streams, + ) { return CourseSection( ref: ref, sectionTitle: title, @@ -167,7 +176,8 @@ class CourseOverviewState extends ConsumerState { Future _refreshData() async { setState( - () => isLoading = true,); // Set loading to true at the start of refresh + () => isLoading = true, + ); // Set loading to true at the start of refresh final userViewModelNotifier = ref.read(userViewModelProvider.notifier); await userViewModelNotifier.fetchUserCourses(); @@ -175,7 +185,8 @@ class CourseOverviewState extends ConsumerState { await ref.read(videoViewModelProvider.notifier).fetchLiveNowStreams(); await ref.read(videoViewModelProvider.notifier).fetchLiveThumbnails(); - setState(() => - isLoading = false,); // Set loading to false once refresh is complete + setState( + () => isLoading = false, + ); // Set loading to false once refresh is complete } } diff --git a/lib/views/course_view/downloaded_courses_view/download_content_view.dart b/lib/views/course_view/downloaded_courses_view/download_content_view.dart index b0ac65e8..117f0593 100644 --- a/lib/views/course_view/downloaded_courses_view/download_content_view.dart +++ b/lib/views/course_view/downloaded_courses_view/download_content_view.dart @@ -6,8 +6,6 @@ import 'package:gocast_mobile/views/components/custom_search_top_nav_bar.dart'; import 'package:gocast_mobile/views/course_view/components/small_stream_card.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - - /// CourseListScreen /// /// This screen displays a list of courses. @@ -43,13 +41,14 @@ class DownloadCoursesContentView extends ConsumerWidget { itemCount: videoCards.isEmpty ? 1 : videoCards.length, itemBuilder: (BuildContext context, int index) { if (videoCards.isEmpty) { - return Center( + return Center( child: Padding( padding: AppPadding.sectionPadding, child: Center( child: Padding( padding: const EdgeInsets.symmetric(vertical: 295.0), - child: Text(AppLocalizations.of(context)!.no_downloaded_courses), + child: Text(AppLocalizations.of(context)! + .no_downloaded_courses), ), ), ), diff --git a/lib/views/course_view/downloaded_courses_view/downloaded_courses_view.dart b/lib/views/course_view/downloaded_courses_view/downloaded_courses_view.dart index 7c1bbb09..aad5ea33 100644 --- a/lib/views/course_view/downloaded_courses_view/downloaded_courses_view.dart +++ b/lib/views/course_view/downloaded_courses_view/downloaded_courses_view.dart @@ -8,7 +8,6 @@ import 'package:gocast_mobile/views/course_view/downloaded_courses_view/download import 'package:gocast_mobile/views/video_view/offline_video_player/offline_video_player.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class DownloadedCourses extends ConsumerStatefulWidget { const DownloadedCourses({super.key}); @@ -24,7 +23,7 @@ class DownloadedCoursesState extends ConsumerState { context: context, builder: (BuildContext context) { return AlertDialog( - title: Text(AppLocalizations.of(context)!.confirm_delete), + title: Text(AppLocalizations.of(context)!.confirm_delete), content: Text(AppLocalizations.of(context)!.confirm_delete_message), actions: [ TextButton( @@ -66,7 +65,10 @@ class DownloadedCoursesState extends ConsumerState { customAppBar: CustomSearchTopNavBar( searchController: searchController, title: AppLocalizations.of(context)!.download, - filterOptions: [AppLocalizations.of(context)!.newest_first, AppLocalizations.of(context)!.oldest_first], + filterOptions: [ + AppLocalizations.of(context)!.newest_first, + AppLocalizations.of(context)!.oldest_first + ], onClick: (String choice) { // Handle filter option click }, @@ -74,30 +76,26 @@ class DownloadedCoursesState extends ConsumerState { videoCards: downloadedVideos.entries.map((entry) { final int videoId = entry.key; final VideoDetails videoDetails = entry.value; - final String localPath = videoDetails.filePath; - final String videoName = videoDetails.name; - final int durationSeconds = videoDetails.duration; - final String formattedDuration = "${(durationSeconds ~/ 3600).toString().padLeft(2, '0')}:${((durationSeconds % 3600) ~/ 60).toString().padLeft(2, '0')}:${(durationSeconds % 60).toString().padLeft(2, '0')}"; - return SmallStreamCard( + return SmallStreamCard( isDownloaded: true, - courseId: videoId, - title: videoName, - subtitle: formattedDuration, - tumID: "TUMID", - showDeleteConfirmationDialog: _showDeleteConfirmationDialog, - onTap: () { + courseId: videoId, + title: videoDetails.name, + subtitle: videoDetails.duration, + tumID: videoDetails.date, + showDeleteConfirmationDialog: _showDeleteConfirmationDialog, + onTap: () { Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => - OfflineVideoPlayerPage(localPath: localPath), - ), - ); + MaterialPageRoute( + builder: (context) => OfflineVideoPlayerPage( + videoDetails: videoDetails, + ), + ), + ); }, - ); + ); }).toList(), ), ), ); - } -} \ No newline at end of file +} diff --git a/lib/views/course_view/list_courses_view/courses_list_view.dart b/lib/views/course_view/list_courses_view/courses_list_view.dart index 2b5b24ea..4266f258 100644 --- a/lib/views/course_view/list_courses_view/courses_list_view.dart +++ b/lib/views/course_view/list_courses_view/courses_list_view.dart @@ -37,7 +37,8 @@ class CoursesList extends ConsumerWidget { Padding _buildPlaceholder(BuildContext context) { return Padding( padding: AppPadding.sectionPadding, - child: Center(child: Text(AppLocalizations.of(context)!.no_courses_found)), + child: + Center(child: Text(AppLocalizations.of(context)!.no_courses_found)), ); } @@ -48,46 +49,47 @@ class CoursesList extends ConsumerWidget { ) { final liveStreams = ref.watch(videoViewModelProvider).liveStreams ?? []; var liveCourseIds = liveStreams.map((stream) => stream.courseID).toSet(); - final userPinned = ref.watch(pinnedCourseViewModelProvider).userPinned ?? []; + final userPinned = + ref.watch(pinnedCourseViewModelProvider).userPinned ?? []; List liveCourses = courses.where((course) => liveCourseIds.contains(course.id)).toList(); return ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - scrollDirection: Axis.vertical, - itemCount: courses.length, - itemBuilder: (BuildContext context, int index) { - final course = courses[index]; - final isPinned = userPinned.contains(course); - return CourseCard( - isLoggedIn: ref.read(userViewModelProvider).user != null, - course: course, - isPinned: isPinned, - onPinUnpin: (course) { - final pinnedViewModelNotifier = - ref.read(pinnedCourseViewModelProvider.notifier); - if (isPinned) { - pinnedViewModelNotifier.unpinCourse(course.id); - } else { - pinnedViewModelNotifier.pinCourse(course.id); - } - }, - title: course.name, - tumID: course.tUMOnlineIdentifier, - live: liveCourses.contains(course), - courseId: course.id, - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => CourseDetail( - title: course.name, - courseId: course.id, - ), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + scrollDirection: Axis.vertical, + itemCount: courses.length, + itemBuilder: (BuildContext context, int index) { + final course = courses[index]; + final isPinned = userPinned.contains(course); + return CourseCard( + isLoggedIn: ref.read(userViewModelProvider).user != null, + course: course, + isPinned: isPinned, + onPinUnpin: (course) { + final pinnedViewModelNotifier = + ref.read(pinnedCourseViewModelProvider.notifier); + if (isPinned) { + pinnedViewModelNotifier.unpinCourse(course.id); + } else { + pinnedViewModelNotifier.pinCourse(course.id); + } + }, + title: course.name, + live: liveCourses.contains(course), + courseId: course.id, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CourseDetail( + title: course.name, + courseId: course.id, ), - ); - }, - ); - },); + ), + ); + }, + ); + }, + ); } } diff --git a/lib/views/course_view/list_courses_view/my_courses_view.dart b/lib/views/course_view/list_courses_view/my_courses_view.dart index bab0da0e..01b77c14 100644 --- a/lib/views/course_view/list_courses_view/my_courses_view.dart +++ b/lib/views/course_view/list_courses_view/my_courses_view.dart @@ -40,6 +40,9 @@ class MyCoursesState extends ConsumerState { void filterCoursesBySemester(String selectedSemester) { var allUserCourses = ref.watch(userViewModelProvider).userCourses ?? []; + ref + .read(pinnedCourseViewModelProvider.notifier) + .setSelectedSemester(selectedSemester); ref .read(userViewModelProvider.notifier) .updateSelectedSemester(selectedSemester, allUserCourses); @@ -59,7 +62,7 @@ class MyCoursesState extends ConsumerState { userViewModelNotifier.updatedDisplayedCourses(temp); isSearchInitialized = false; } else { - displayedCourses = displayedCourses.where((course) { + displayedCourses = temp.where((course) { return course.name.toLowerCase().contains(searchInput) || course.slug.toLowerCase().contains(searchInput); }).toList(); diff --git a/lib/views/course_view/list_courses_view/public_courses_view.dart b/lib/views/course_view/list_courses_view/public_courses_view.dart index 31c913d9..a636cb53 100644 --- a/lib/views/course_view/list_courses_view/public_courses_view.dart +++ b/lib/views/course_view/list_courses_view/public_courses_view.dart @@ -30,8 +30,8 @@ class PublicCoursesState extends ConsumerState { void _initializeCourses() { final userViewModelNotifier = ref.read(userViewModelProvider.notifier); Future.microtask(() async { - await userViewModelNotifier.fetchPublicCourses(); await userViewModelNotifier.fetchSemesters(); + await userViewModelNotifier.fetchPublicCourses(); }); } @@ -41,6 +41,9 @@ class PublicCoursesState extends ConsumerState { void filterCoursesBySemester(String selectedSemester) { var allUserCourses = ref.watch(userViewModelProvider).publicCourses ?? []; + ref + .read(pinnedCourseViewModelProvider.notifier) + .setSelectedSemester(selectedSemester); ref .read(userViewModelProvider.notifier) .updateSelectedSemester(selectedSemester, allUserCourses); @@ -60,7 +63,7 @@ class PublicCoursesState extends ConsumerState { userViewModelNotifier.updatedDisplayedCourses(temp); isSearchInitialized = false; } else { - displayedCourses = displayedCourses.where((course) { + displayedCourses = temp.where((course) { return course.name.toLowerCase().contains(searchInput) || course.slug.toLowerCase().contains(searchInput); }).toList(); diff --git a/lib/views/course_view/pinned_courses_view/pinned_courses_content_view.dart b/lib/views/course_view/pinned_courses_view/pinned_courses_content_view.dart index 5c44ad92..5cd23e97 100644 --- a/lib/views/course_view/pinned_courses_view/pinned_courses_content_view.dart +++ b/lib/views/course_view/pinned_courses_view/pinned_courses_content_view.dart @@ -51,8 +51,11 @@ class PinnedCoursesContentView extends ConsumerWidget { padding: AppPadding.sectionPadding, child: Center( child: Padding( - padding: const EdgeInsets.symmetric(vertical: 295.0), - child: Text(AppLocalizations.of(context)!.pinned_empty,), + padding: + const EdgeInsets.symmetric(vertical: 295.0), + child: Text( + AppLocalizations.of(context)!.pinned_empty, + ), ), ), ), diff --git a/lib/views/course_view/pinned_courses_view/pinned_courses_view.dart b/lib/views/course_view/pinned_courses_view/pinned_courses_view.dart index d03e8835..5dda26da 100644 --- a/lib/views/course_view/pinned_courses_view/pinned_courses_view.dart +++ b/lib/views/course_view/pinned_courses_view/pinned_courses_view.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; @@ -42,6 +41,9 @@ class PinnedCoursesState extends ConsumerState { } void filterCoursesBySemester(String selectedSemester) { + ref + .read(userViewModelProvider.notifier) + .setSelectedSemester(selectedSemester); var userPinned = ref.watch(pinnedCourseViewModelProvider).userPinned ?? []; ref .read(pinnedCourseViewModelProvider.notifier) @@ -62,7 +64,7 @@ class PinnedCoursesState extends ConsumerState { pinnedViewModelNotifier.updatedDisplayedPinnedCourses(temp); isSearchInitialized = false; } else { - displayedCourses = displayedCourses.where((course) { + displayedCourses = temp.where((course) { return course.name.toLowerCase().contains(searchInput) || course.slug.toLowerCase().contains(searchInput); }).toList(); @@ -103,7 +105,6 @@ class PinnedCoursesState extends ConsumerState { title: course.name, courseId: course.id, subtitle: course.tUMOnlineIdentifier, - tumID: course.tUMOnlineIdentifier, onTap: () => _handleCourseTap(course, context), ); }).toList(), diff --git a/lib/views/login_view/internal_login_view.dart b/lib/views/login_view/internal_login_view.dart index 61ba4fad..7fd487be 100644 --- a/lib/views/login_view/internal_login_view.dart +++ b/lib/views/login_view/internal_login_view.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/providers.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - /// Internal login screen view. /// /// This screen is used to login with a username and password. @@ -132,7 +131,7 @@ class InternalLoginScreenState extends ConsumerState { usernameController.text, passwordController.text, ); - if ( userState.user != null) { + if (userState.user != null) { ref.read(settingViewModelProvider.notifier).fetchUserSettings(); } else { userState.copyWith(isLoading: false); diff --git a/lib/views/notifications_view/notifications_overview.dart b/lib/views/notifications_view/notifications_overview.dart index 2a053b47..f657fccd 100644 --- a/lib/views/notifications_view/notifications_overview.dart +++ b/lib/views/notifications_view/notifications_overview.dart @@ -4,7 +4,6 @@ import 'package:gocast_mobile/providers.dart'; import 'notifications_screen_view.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class MyNotifications extends ConsumerStatefulWidget { const MyNotifications({super.key}); diff --git a/lib/views/notifications_view/notifications_screen_view.dart b/lib/views/notifications_view/notifications_screen_view.dart index 00ce7c03..f92427e9 100644 --- a/lib/views/notifications_view/notifications_screen_view.dart +++ b/lib/views/notifications_view/notifications_screen_view.dart @@ -7,7 +7,6 @@ import 'package:gocast_mobile/utils/constants.dart'; import 'package:gocast_mobile/views/settings_view/settings_screen_view.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class NotificationsScreen extends ConsumerWidget { final String title; final List pushNotifications; @@ -41,12 +40,15 @@ class NotificationsScreen extends ConsumerWidget { physics: const AlwaysScrollableScrollPhysics(), scrollDirection: Axis.vertical, children: [ - _buildSectionHeader(AppLocalizations.of(context)!.banner_notification), + _buildSectionHeader( + AppLocalizations.of(context)!.banner_notification), for (var alert in bannerAlerts) _buildBannerAlert(alert), - _buildSectionHeader(AppLocalizations.of(context)!.feature_notifications), + _buildSectionHeader( + AppLocalizations.of(context)!.feature_notifications), for (var notification in featureNotifications) _buildFeatureNotification(notification), - _buildSectionHeader(AppLocalizations.of(context)!.recent_uploads), + _buildSectionHeader( + AppLocalizations.of(context)!.recent_uploads), for (var notification in pushNotifications) _buildPushNotification(notification), ], @@ -73,7 +75,9 @@ class NotificationsScreen extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( - child: Center(child: Text(AppLocalizations.of(context)!.no_notifications_found)), + child: Center( + child: Text(AppLocalizations.of(context)! + .no_notifications_found)), ), ], ), diff --git a/lib/views/on_boarding_view/enable_notification_view.dart b/lib/views/on_boarding_view/enable_notification_view.dart index e6b62868..9c84edec 100644 --- a/lib/views/on_boarding_view/enable_notification_view.dart +++ b/lib/views/on_boarding_view/enable_notification_view.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class EnableNotificationscreen extends StatelessWidget { const EnableNotificationscreen({super.key}); diff --git a/lib/views/on_boarding_view/welcome_screen_view.dart b/lib/views/on_boarding_view/welcome_screen_view.dart index bfb12ba9..b7d59ad7 100644 --- a/lib/views/on_boarding_view/welcome_screen_view.dart +++ b/lib/views/on_boarding_view/welcome_screen_view.dart @@ -144,7 +144,8 @@ class WelcomeScreen extends ConsumerWidget { valueColor: AlwaysStoppedAnimation(Colors.white), ), ) - : Text(AppLocalizations.of(context)!.tum_login, style: const TextStyle(fontSize: 18)), + : Text(AppLocalizations.of(context)!.tum_login, + style: const TextStyle(fontSize: 18)), onPressed: () => handleSSOLogin(context, ref), ); } @@ -160,7 +161,8 @@ class WelcomeScreen extends ConsumerWidget { shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), ), - child: Text(AppLocalizations.of(context)!.continue_without, style: const TextStyle(fontSize: 18)), + child: Text(AppLocalizations.of(context)!.continue_without, + style: const TextStyle(fontSize: 18)), onPressed: () { Navigator.pushNamed(context, '/publiccourses'); }, @@ -173,7 +175,7 @@ class WelcomeScreen extends ConsumerWidget { context, MaterialPageRoute(builder: (context) => const InternalLoginScreen()), ), - child: Center( + child: Center( child: Text( AppLocalizations.of(context)!.use_an_internal_account, style: const TextStyle( diff --git a/lib/views/settings_view/custom_playback_speed_view.dart b/lib/views/settings_view/custom_playback_speed_view.dart index ce39776f..af05e25b 100644 --- a/lib/views/settings_view/custom_playback_speed_view.dart +++ b/lib/views/settings_view/custom_playback_speed_view.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - void showAddCustomSpeedDialog( BuildContext context, Function(double) onSpeedAdded, diff --git a/lib/views/settings_view/edit_profile_screen_view.dart b/lib/views/settings_view/edit_profile_screen_view.dart index 91da3ef3..4d470b5e 100644 --- a/lib/views/settings_view/edit_profile_screen_view.dart +++ b/lib/views/settings_view/edit_profile_screen_view.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/providers.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class EditProfileScreen extends ConsumerStatefulWidget { const EditProfileScreen({super.key}); @@ -43,7 +42,7 @@ class EditProfileScreenState extends ConsumerState { title: Padding( padding: isLandscape ? const EdgeInsets.only(top: 16.0) : EdgeInsets.zero, - child: Text(AppLocalizations.of(context)!.edit_profile), + child: Text(AppLocalizations.of(context)!.edit_profile), ), ), body: Padding( @@ -55,9 +54,10 @@ class EditProfileScreenState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( + Text( AppLocalizations.of(context)!.preferred_name, - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + style: + const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), TextField( @@ -76,9 +76,10 @@ class EditProfileScreenState extends ConsumerState { color: Theme.of(context).colorScheme.scrim.withOpacity(0.50), ), - children: [ + children: [ TextSpan( - text: AppLocalizations.of(context)!.name_change_limitation, + text: + AppLocalizations.of(context)!.name_change_limitation, style: const TextStyle( fontStyle: FontStyle.italic, fontWeight: FontWeight.bold, @@ -95,7 +96,7 @@ class EditProfileScreenState extends ConsumerState { ), ), onPressed: () => _onSaveButtonPressed(context), - child: Padding( + child: Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Text(AppLocalizations.of(context)!.save), ), @@ -151,7 +152,9 @@ class EditProfileScreenState extends ConsumerState { return AlertDialog( backgroundColor: Theme.of(context).colorScheme.onPrimary, title: const Text("Error"), - content: Text(errorMessage== '3 months' ? AppLocalizations.of(context)!.name_change_limitation : errorMessage), + content: Text(errorMessage == '3 months' + ? AppLocalizations.of(context)!.name_change_limitation + : errorMessage), actions: [ TextButton( child: const Text("OK"), diff --git a/lib/views/settings_view/playback_speed_picker_view.dart b/lib/views/settings_view/playback_speed_picker_view.dart index b13e0f82..c6b2c0ec 100644 --- a/lib/views/settings_view/playback_speed_picker_view.dart +++ b/lib/views/settings_view/playback_speed_picker_view.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - void showPlaybackSpeedsPicker( BuildContext context, WidgetRef ref, @@ -45,7 +44,7 @@ void showPlaybackSpeedsPicker( if (selectedSpeeds .any((speed) => !defaultSpeeds.contains(speed))) ...[ const Divider(), - Padding( + Padding( padding: const EdgeInsets.all(8.0), child: Text( AppLocalizations.of(context)!.custom_playback_speed, diff --git a/lib/views/settings_view/playback_speed_settings_view.dart b/lib/views/settings_view/playback_speed_settings_view.dart index 5a96a4ac..86f21072 100644 --- a/lib/views/settings_view/playback_speed_settings_view.dart +++ b/lib/views/settings_view/playback_speed_settings_view.dart @@ -13,8 +13,15 @@ class PlaybackSpeedSettings extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final settingState = ref.watch(settingViewModelProvider); - final selectedPlaybackSpeeds = - _parsePlaybackSpeeds(settingState.userSettings, ref); + + WidgetsBinding.instance.addPostFrameCallback((_) { + final speeds = _initPlaybackSpeeds(settingState.userSettings, ref); + // Only update if necessary to avoid infinite loops + if (ref.read(playbackSpeedsProvider.notifier).state != speeds) { + ref.read(playbackSpeedsProvider.notifier).state = speeds; + } + }); + final selectedPlaybackSpeeds = ref.watch(playbackSpeedsProvider); return Column( children: [ @@ -30,7 +37,7 @@ class PlaybackSpeedSettings extends ConsumerWidget { List playbackSpeeds, ) { return ListTile( - title: Text(AppLocalizations.of(context)!.playback_speed), + title: Text(AppLocalizations.of(context)!.playback_speed), trailing: const Icon(Icons.arrow_forward_ios), onTap: () => showPlaybackSpeedsPicker( context, @@ -68,10 +75,13 @@ class PlaybackSpeedSettings extends ConsumerWidget { .updateSelectedSpeeds(speed, isSelected); } - List _parsePlaybackSpeeds( + List _initPlaybackSpeeds( List? userSettings, WidgetRef ref, ) { - return ref.read(settingViewModelProvider.notifier).parsePlaybackSpeeds(); + final settingViewModel = ref.read(settingViewModelProvider.notifier); + final speeds = settingViewModel.parsePlaybackSpeeds(); + ref.read(playbackSpeedsProvider.notifier).state = speeds; + return ref.read(playbackSpeedsProvider); } } diff --git a/lib/views/settings_view/preferred_greeting_view.dart b/lib/views/settings_view/preferred_greeting_view.dart index 2c67a654..3b569a17 100644 --- a/lib/views/settings_view/preferred_greeting_view.dart +++ b/lib/views/settings_view/preferred_greeting_view.dart @@ -5,7 +5,6 @@ import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; import 'package:gocast_mobile/views/settings_view/authentication_error_card_view.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class PreferredGreetingView extends ConsumerWidget { const PreferredGreetingView({super.key}); diff --git a/lib/views/settings_view/settings_screen_view.dart b/lib/views/settings_view/settings_screen_view.dart index 37491cc1..27352804 100644 --- a/lib/views/settings_view/settings_screen_view.dart +++ b/lib/views/settings_view/settings_screen_view.dart @@ -11,7 +11,6 @@ import 'package:gocast_mobile/views/settings_view/authentication_error_card_view import 'package:url_launcher/url_launcher.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class SettingsScreen extends ConsumerStatefulWidget { const SettingsScreen({super.key}); @@ -48,8 +47,10 @@ class _SettingsScreenState extends ConsumerState { children: [ _buildProfileTile(userState), const Divider(), - _buildSectionTitle(AppLocalizations.of(context)!.account_settings), - _buildEditableListTile(AppLocalizations.of(context)!.edit_profile, () async { + _buildSectionTitle( + AppLocalizations.of(context)!.account_settings), + _buildEditableListTile(AppLocalizations.of(context)!.edit_profile, + () async { bool isAuthenticated = await showAuthenticationErrorCard(context, ref); if (isAuthenticated && mounted) { @@ -88,7 +89,8 @@ class _SettingsScreenState extends ConsumerState { _buildLogoutTile(context), const Divider(), _buildSectionTitle(AppLocalizations.of(context)!.more), - _buildNavigableListTile(AppLocalizations.of(context)!.about_us, ""), + _buildNavigableListTile( + AppLocalizations.of(context)!.about_us, ""), _buildNavigableListTile( AppLocalizations.of(context)!.privacy_policy, "https://live.rbg.tum.de/privacy", @@ -129,7 +131,7 @@ class _SettingsScreenState extends ConsumerState { themeModeText = AppLocalizations.of(context)!.system_default; } return ListTile( - title: Text(AppLocalizations.of(context)!.choose_theme), + title: Text(AppLocalizations.of(context)!.choose_theme), subtitle: Text(themeModeText), trailing: const Icon(Icons.arrow_forward_ios), onTap: () => _showThemeSelectionSheet(context, ref), @@ -139,7 +141,8 @@ class _SettingsScreenState extends ConsumerState { ListTile _buildLanguageSelectionTile(BuildContext context, WidgetRef ref) { return ListTile( title: Text(AppLocalizations.of(context)!.language_selection), - subtitle: Text(UserPreferences.getLanguageName(UserPreferences.getLanguage())), + subtitle: + Text(UserPreferences.getLanguageName(UserPreferences.getLanguage())), trailing: const Icon(Icons.arrow_forward_ios), onTap: () async { final selectedLanguage = await showModalBottomSheet( @@ -149,10 +152,12 @@ class _SettingsScreenState extends ConsumerState { child: Column( mainAxisSize: MainAxisSize.min, children: ['en', 'fr', 'de', 'es'] - .map((String lang) => ListTile( - title: Text(UserPreferences.getLanguageName(lang)), - onTap: () => Navigator.pop(context, lang), - ),) + .map( + (String lang) => ListTile( + title: Text(UserPreferences.getLanguageName(lang)), + onTap: () => Navigator.pop(context, lang), + ), + ) .toList(), ), ); @@ -168,7 +173,6 @@ class _SettingsScreenState extends ConsumerState { ); } - void _showThemeSelectionSheet(BuildContext context, WidgetRef ref) { showModalBottomSheet( context: context, @@ -177,7 +181,7 @@ class _SettingsScreenState extends ConsumerState { child: Wrap( children: [ ListTile( - title: Text(AppLocalizations.of(context)!.system_default), + title: Text(AppLocalizations.of(context)!.system_default), onTap: () { ref .read(settingViewModelProvider.notifier) @@ -288,7 +292,7 @@ class _SettingsScreenState extends ConsumerState { context: context, builder: (BuildContext context) { return AlertDialog( - title: Text(AppLocalizations.of(context)!.logout), + title: Text(AppLocalizations.of(context)!.logout), content: Text(AppLocalizations.of(context)!.logout_message), actions: [ TextButton( diff --git a/lib/views/video_view/offline_video_player/offline_video_player.dart b/lib/views/video_view/offline_video_player/offline_video_player.dart index bc1c477b..efb00b31 100644 --- a/lib/views/video_view/offline_video_player/offline_video_player.dart +++ b/lib/views/video_view/offline_video_player/offline_video_player.dart @@ -2,18 +2,18 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:gocast_mobile/models/download/download_state_model.dart'; import 'package:gocast_mobile/models/error/error_model.dart'; import 'package:gocast_mobile/providers.dart'; -import 'package:gocast_mobile/views/chat_view/inactive_view.dart'; import 'package:gocast_mobile/views/video_view/offline_video_player/offline_video_player_controller.dart'; import 'package:shared_preferences/shared_preferences.dart'; class OfflineVideoPlayerPage extends ConsumerStatefulWidget { - final String localPath; + final VideoDetails videoDetails; const OfflineVideoPlayerPage({ super.key, - required this.localPath, + required this.videoDetails, }); @override @@ -30,8 +30,16 @@ class OfflineVideoPlayerPageState Widget _buildVideoLayout() { return Column( children: [ - Expanded(child: _controllerManager.buildVideoPlayer()), - const Expanded(child: InactiveView()), + Expanded( + child: Center( + // Center the player + child: AspectRatio( + aspectRatio: + _controllerManager.videoPlayerController.value.aspectRatio, + child: _controllerManager.buildVideoPlayer(), + ), + ), + ), ], ); } @@ -56,7 +64,7 @@ class OfflineVideoPlayerPageState @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text("Replace me with video Title")), + appBar: AppBar(title: Text(widget.videoDetails.name)), body: ref.read(videoViewModelProvider).isLoading ? const Center(child: CircularProgressIndicator()) : _buildVideoLayout(), @@ -70,8 +78,8 @@ class OfflineVideoPlayerPageState // Initialize the controller manager. void _initializeControllerManager() { - _controllerManager = - OfflineVideoPlayerControllerManager(localPath: widget.localPath); + _controllerManager = OfflineVideoPlayerControllerManager( + localPath: widget.videoDetails.filePath); } // Initialize the video player and seek to the last progress. @@ -89,7 +97,8 @@ class OfflineVideoPlayerPageState // Seek to the last progress. Future _seekToLastProgress() async { final prefs = await SharedPreferences.getInstance(); - final progress = prefs.getDouble('progress_${widget.localPath}') ?? 0.0; + final progress = + prefs.getDouble('progress_${widget.videoDetails.name}') ?? 0.0; final position = Duration( seconds: (progress * _controllerManager.videoPlayerController.value.duration.inSeconds) @@ -140,17 +149,17 @@ class OfflineVideoPlayerPageState Future _updateProgress(double progress) async { final prefs = await SharedPreferences.getInstance(); - await prefs.setDouble('progress_${widget.localPath}', progress); + await prefs.setDouble('progress_${widget.videoDetails.name}', progress); } bool _shouldMarkAsWatched(double progress) { - const watchedThreshold = 0.9; // 90% + const watchedThreshold = 0.8; return progress >= watchedThreshold; } Future _markStreamAsWatched() async { final prefs = await SharedPreferences.getInstance(); - await prefs.setBool('watched_${widget.localPath}', true); + await prefs.setBool('watched_${widget.videoDetails.name}', true); } // Simplified loading state management diff --git a/lib/views/video_view/offline_video_player/offline_video_player_controller.dart b/lib/views/video_view/offline_video_player/offline_video_player_controller.dart index 26182257..7925cf97 100644 --- a/lib/views/video_view/offline_video_player/offline_video_player_controller.dart +++ b/lib/views/video_view/offline_video_player/offline_video_player_controller.dart @@ -45,10 +45,12 @@ class OfflineVideoPlayerControllerManager { cupertinoProgressColors: _getCupertinoProgressColors(), materialProgressColors: _getMaterialProgressColors(), placeholder: Container(color: Colors.black), + allowMuting: true, autoInitialize: true, allowFullScreen: true, fullScreenByDefault: false, showOptions: true, + playbackSpeeds: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2], ); } diff --git a/lib/views/video_view/utils/custom_video_control_bar.dart b/lib/views/video_view/utils/custom_video_control_bar.dart index 6b9f2369..980b18b4 100644 --- a/lib/views/video_view/utils/custom_video_control_bar.dart +++ b/lib/views/video_view/utils/custom_video_control_bar.dart @@ -81,7 +81,8 @@ class CustomVideoControlBar extends StatelessWidget { return Consumer( builder: (context, ref, child) { - final pinnedViewModel = ref.read(pinnedCourseViewModelProvider.notifier); + final pinnedViewModel = + ref.read(pinnedCourseViewModelProvider.notifier); final downloadViewModel = ref.read(downloadViewModelProvider.notifier); final isPinned = pinnedViewModel.isCoursePinned(currentStream.courseID); final isDownloaded = @@ -103,7 +104,8 @@ class CustomVideoControlBar extends StatelessWidget { ), IconButton( icon: isPollVisible - ? Icon(Icons.quiz_outlined, + ? Icon( + Icons.quiz_outlined, color: themeData.primaryColor, ) : const Icon(Icons.quiz_outlined), diff --git a/lib/views/video_view/utils/download_service.dart b/lib/views/video_view/utils/download_service.dart new file mode 100644 index 00000000..83c2c9ca --- /dev/null +++ b/lib/views/video_view/utils/download_service.dart @@ -0,0 +1,86 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:gocast_mobile/providers.dart'; + +class DownloadService { + final WidgetRef ref; + final bool Function() isWidgetMounted; + final Function(String message) onShowSnackBar; + final Function() showDownloadConfirmationDialog; + final Function() showMobileDataNotAllowedDialog; + final String startingDownloadMessage; + final String downloadNotAvailableMessage; + final String downloadCompletedMessage; + final String downloadFailedMessage; + final String donwloadCancelledMessage; + + DownloadService({ + required this.ref, + required this.isWidgetMounted, + required this.onShowSnackBar, + required this.startingDownloadMessage, + required this.downloadNotAvailableMessage, + required this.downloadCompletedMessage, + required this.downloadFailedMessage, + required this.donwloadCancelledMessage, + required this.showDownloadConfirmationDialog, + required this.showMobileDataNotAllowedDialog, + }); + + Future downloadVideo( + Stream stream, String type, String streamName, String streamDate) async { + bool canDownload = await _handleDownloadConnectivity(stream, type); + if (!canDownload) return; + + String? downloadUrl; + for (var download in stream.downloads) { + if (download.friendlyName == type) { + downloadUrl = download.downloadURL; + break; + } + } + + if (downloadUrl == null) { + if (!isWidgetMounted()) return; + onShowSnackBar(downloadNotAvailableMessage); + return; + } + + onShowSnackBar(startingDownloadMessage); + + ref + .read(downloadViewModelProvider.notifier) + .downloadVideo(downloadUrl, stream, streamName, streamDate) + .then((localPath) { + if (!isWidgetMounted()) return; + onShowSnackBar(localPath.isNotEmpty + ? downloadCompletedMessage + : downloadFailedMessage); + }); + } + + Future _handleDownloadConnectivity(Stream stream, String type) async { + final isDownloadWithWifiOnly = + ref.watch(settingViewModelProvider).isDownloadWithWifiOnly; + var connectivityResult = await (Connectivity().checkConnectivity()); + // If 'Download Over Wi-Fi Only' is enabled and connected to mobile, show a dialog + if (connectivityResult == ConnectivityResult.mobile && + isDownloadWithWifiOnly) { + if (!isWidgetMounted()) return false; + showMobileDataNotAllowedDialog(); + return false; + } + // If on mobile data and 'Download Over Wi-Fi Only' is disabled, ask for confirmation + if (connectivityResult == ConnectivityResult.mobile && + !isDownloadWithWifiOnly) { + bool shouldProceed = await showDownloadConfirmationDialog(); + if (!isWidgetMounted()) return false; + if (!shouldProceed) { + onShowSnackBar(donwloadCancelledMessage); + return false; + } + } + return true; + } +} diff --git a/lib/views/video_view/video_player.dart b/lib/views/video_view/video_player.dart index a79dd557..302ab602 100644 --- a/lib/views/video_view/video_player.dart +++ b/lib/views/video_view/video_player.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gocast_mobile/base/networking/api/gocast/api_v2.pb.dart'; @@ -10,12 +9,13 @@ import 'package:gocast_mobile/views/chat_view/chat_view.dart'; import 'package:gocast_mobile/views/chat_view/inactive_view.dart'; import 'package:gocast_mobile/views/chat_view/poll_view.dart'; import 'package:gocast_mobile/views/video_view/utils/custom_video_control_bar.dart'; +import 'package:gocast_mobile/views/video_view/utils/download_service.dart'; import 'package:gocast_mobile/views/video_view/utils/video_player_handler.dart'; import 'package:gocast_mobile/views/video_view/video_player_controller.dart'; +import 'package:intl/intl.dart'; import 'package:logger/logger.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - class VideoPlayerPage extends ConsumerStatefulWidget { final Stream stream; @@ -76,12 +76,13 @@ class VideoPlayerPageState extends ConsumerState { await ref .read(courseViewModelProvider.notifier) .getCourseWithID(widget.stream.courseID); - await ref.read(chatViewModelProvider.notifier).fetchChatMessages(widget.stream.id); - Course? course = ref - .read(courseViewModelProvider) - .course; + await ref + .read(chatViewModelProvider.notifier) + .fetchChatMessages(widget.stream.id); + Course? course = ref.read(courseViewModelProvider).course; if (course != null) { - if (course.chatEnabled && course.vodChatEnabled && widget.stream.chatEnabled) { + if ((course.chatEnabled && widget.stream.chatEnabled) || + (course.vodChatEnabled && widget.stream.chatEnabled)) { setState(() { _isChatActive = true; _isPollActive = true; @@ -107,9 +108,7 @@ class VideoPlayerPageState extends ConsumerState { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.stream.name)), - body: ref - .read(videoViewModelProvider) - .isLoading + body: ref.read(videoViewModelProvider).isLoading ? const Center(child: CircularProgressIndicator()) : _buildVideoLayout(), ); @@ -151,12 +150,10 @@ class VideoPlayerPageState extends ConsumerState { // Seek to the last progress. Future _seekToLastProgress() async { Progress progress = - ref - .read(videoViewModelProvider) - .progress ?? Progress(progress: 0.0); + ref.read(videoViewModelProvider).progress ?? Progress(progress: 0.0); final position = Duration( seconds: (progress.progress * - _controllerManager.videoPlayerController.value.duration.inSeconds) + _controllerManager.videoPlayerController.value.duration.inSeconds) .round(), ); await _controllerManager.videoPlayerController.seekTo(position); @@ -174,18 +171,17 @@ class VideoPlayerPageState extends ConsumerState { void _setupProgressListener() { _progressTimer = - Timer.periodic(const Duration(seconds: 5), _progressUpdateCallback); + Timer.periodic(const Duration(seconds: 15), _progressUpdateCallback); } void _progressUpdateCallback(Timer timer) async { if (!_isPlayerInitialized()) return; - if (_controllerManager.videoPlayerController.value.isPlaying) { - final position = _getCurrentPosition(); - final progress = _calculateProgress(position); - await _updateProgress(progress); - if (_shouldMarkAsWatched(progress)) { - await _markStreamAsWatched(); - } + if (!_controllerManager.videoPlayerController.value.isPlaying) return; + final position = _getCurrentPosition(); + final progress = _calculateProgress(position); + await _updateProgress(progress); + if (_shouldMarkAsWatched(progress)) { + await _markStreamAsWatched(); } } @@ -210,7 +206,7 @@ class VideoPlayerPageState extends ConsumerState { } bool _shouldMarkAsWatched(double progress) { - const watchedThreshold = 0.9; + const watchedThreshold = 0.8; return progress >= watchedThreshold; } @@ -221,9 +217,7 @@ class VideoPlayerPageState extends ConsumerState { } void _switchPlaylist(String newPlaylistUrl) async { - if (ref - .read(videoViewModelProvider) - .videoSource == newPlaylistUrl) { + if (ref.read(videoViewModelProvider).videoSource == newPlaylistUrl) { Logger().i("Already displaying $newPlaylistUrl"); return; } @@ -268,90 +262,75 @@ class VideoPlayerPageState extends ConsumerState { } Future _downloadVideo(Stream stream, String type) async { - // Extract the "Combined" download URL from the Stream object - bool canDownload = await _handleDownloadConnectivity(stream, type); - if (!canDownload) return; // Exit if download should not proceed - - String? downloadUrl; - for (var download in stream.downloads) { - if (download.friendlyName == type) { - downloadUrl = download.downloadURL; - break; - } - } - //downloadUrl="https://file-examples.com/storage/fed61549c865b2b5c9768b5/2017/04/file_example_MP4_480_1_5MG.mp4"; - // Check if the Combined URL is found - if (downloadUrl == null) { - if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text( - 'Download type "$type" not available for this lecture',),), - ); - return; - } - - // Use the extracted URL for downloading - String fileName = "stream.mp4"; - if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(AppLocalizations.of(context)!.starting_download)), - ); - // Call the download function from the StreamViewModel - - ref - .read(downloadViewModelProvider.notifier) - .downloadVideo(downloadUrl, stream.id, fileName,stream.name,stream.duration,stream.description,) - .then((localPath) { - if (localPath.isNotEmpty) { - // Download successful - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Video Downloaded')), - ); - } else { - // Download failed, but not due to Wi-Fi (since it would throw an exception) + final downloadService = DownloadService( + ref: ref, + isWidgetMounted: () => mounted, + onShowSnackBar: (message) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Download failed')), + SnackBar(content: Text(message)), ); - } - }); + }, + startingDownloadMessage: AppLocalizations.of(context)!.starting_download, + downloadNotAvailableMessage: + AppLocalizations.of(context)!.download_not_allowed, + downloadCompletedMessage: + AppLocalizations.of(context)!.download_completed, + downloadFailedMessage: AppLocalizations.of(context)!.download_failed, + donwloadCancelledMessage: + AppLocalizations.of(context)!.download_cancelled, + showDownloadConfirmationDialog: _showDownloadConfirmationDialog, + showMobileDataNotAllowedDialog: _showMobileDataNotAllowedDialog, + ); + String streamName = stream.name != '' + ? stream.name + : 'Lecture: ${DateFormat('EEEE. dd', Localizations.localeOf(context).toString()).format(stream.start.toDateTime())}'; + String streamDate = + DateFormat('dd MMMM yyyy', Localizations.localeOf(context).toString()) + .format(stream.start.toDateTime()); + downloadService.downloadVideo(stream, type, streamName, streamDate); } Future _showDownloadConfirmationDialog() async { return await showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext dialogContext) { - return AlertDialog( - title: const Text("Download Video"), - content: const Text( - "You are on mobile data. Would you like to download the video over mobile data?",), - actions: [ - TextButton( - child: const Text("No"), - onPressed: () { - Navigator.of(dialogContext).pop(false); - }, - ), - TextButton( - child: const Text("Yes"), - onPressed: () { - Navigator.of(dialogContext).pop(true); - }, - ), - ], - ); - }, - ) ?? false; // If dialog is dismissed, return false + context: context, + barrierDismissible: false, + builder: (BuildContext dialogContext) { + return AlertDialog( + title: const Text("Download Video"), + content: const Text( + "You are on mobile data. Would you like to download the video over mobile data?", + ), + actions: [ + TextButton( + child: const Text("No"), + onPressed: () { + Navigator.of(dialogContext).pop(false); + }, + ), + TextButton( + child: const Text("Yes"), + onPressed: () { + Navigator.of(dialogContext).pop(true); + }, + ), + ], + ); + }, + ) ?? + false; // If dialog is dismissed, return false } + void _showMobileDataNotAllowedDialog() { showDialog( context: context, - barrierDismissible: false, // User must tap a button for the dialog to close + barrierDismissible: + false, // User must tap a button for the dialog to close builder: (BuildContext dialogContext) { return AlertDialog( title: const Text("Download Not Allowed"), content: const Text( - "You are currently on mobile data. Video cannot be downloaded over mobile data due to your settings.",), + "You are currently on mobile data. Video cannot be downloaded over mobile data due to your settings.", + ), actions: [ TextButton( child: const Text("OK"), @@ -364,34 +343,4 @@ class VideoPlayerPageState extends ConsumerState { }, ); } - - Future _handleDownloadConnectivity(Stream stream, String type) async { - final isDownloadWithWifiOnly = ref - .watch(settingViewModelProvider) - .isDownloadWithWifiOnly; - - var connectivityResult = await (Connectivity().checkConnectivity()); - - // If 'Download Over Wi-Fi Only' is enabled and connected to mobile, show a dialog - if (connectivityResult == ConnectivityResult.mobile && isDownloadWithWifiOnly) { - if (!mounted) return false; - _showMobileDataNotAllowedDialog(); - return false; - } - - // If on mobile data and 'Download Over Wi-Fi Only' is disabled, ask for confirmation - if (connectivityResult == ConnectivityResult.mobile && !isDownloadWithWifiOnly) { - bool shouldProceed = await _showDownloadConfirmationDialog(); - if (!mounted) return false; - if (!shouldProceed) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Download cancelled')), - ); - return false; - } - } - - return true; // Proceed with download if all checks pass - } - } diff --git a/lib/views/video_view/video_player_controller.dart b/lib/views/video_view/video_player_controller.dart index 94cf3028..e6856d8e 100644 --- a/lib/views/video_view/video_player_controller.dart +++ b/lib/views/video_view/video_player_controller.dart @@ -47,7 +47,6 @@ class VideoPlayerControllerManager { aspectRatio: videoPlayerController.value.aspectRatio, autoPlay: true, looping: false, - zoomAndPan: true, additionalOptions: (context) => _getAdditionalOptions(), errorBuilder: (context, errorMessage) { return Center( @@ -62,17 +61,12 @@ class VideoPlayerControllerManager { ), ); }, - isLive: currentStream.liveNow, allowMuting: true, - useRootNavigator: true, cupertinoProgressColors: _getCupertinoProgressColors(), materialProgressColors: _getMaterialProgressColors(), placeholder: Container(color: Colors.black), - autoInitialize: true, allowFullScreen: true, - fullScreenByDefault: false, playbackSpeeds: _filteredPlaybackSpeeds(), - allowPlaybackSpeedChanging: true, ); } @@ -106,16 +100,6 @@ class VideoPlayerControllerManager { ), ); } - if (currentStream.hasPlaylistUrlCAM() && - currentStream.hasPlaylistUrlPRES()) { - items.add( - OptionItem( - onTap: () => onMenuSelection?.call('Split view', currentStream), - iconData: Icons.vertical_split_sharp, - title: "Split view", - ), - ); - } return items; } @@ -176,16 +160,7 @@ class VideoPlayerControllerManager { } } - List _getPlaybackSpeeds() { - final settingViewModel = ref.read(settingViewModelProvider.notifier); - return settingViewModel.parsePlaybackSpeeds(); - } - List _filteredPlaybackSpeeds() { - final playbackSpeeds = _getPlaybackSpeeds(); - var filteredSpeeds = playbackSpeeds.where((speed) => speed <= 2.0).toList(); - return filteredSpeeds.isEmpty - ? [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2] - : filteredSpeeds; + return ref.read(playbackSpeedsProvider); } }