From 9575f6decc75c4956d498f2231a5e0dc127288b9 Mon Sep 17 00:00:00 2001 From: Erick Date: Sun, 15 Dec 2024 16:12:00 +0300 Subject: [PATCH 01/24] feat: added local authentication on the application --- lib/features/auth/cubit/auth_cubit.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/features/auth/cubit/auth_cubit.dart b/lib/features/auth/cubit/auth_cubit.dart index feaf058..f8fedeb 100644 --- a/lib/features/auth/cubit/auth_cubit.dart +++ b/lib/features/auth/cubit/auth_cubit.dart @@ -8,6 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:academia/features/auth/cubit/auth_states.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; +/// TODO: erick figure out a way to automatically log in user when connection is established class AuthCubit extends Cubit { final UserRepository _userRepository = UserRepository(); @@ -25,7 +26,14 @@ class AuthCubit extends Cubit { result.fold((error) { emit(AuthErrorState(error)); return; - }, (creds) { + }, (creds) async { + final List connectivityResult = + await (Connectivity().checkConnectivity()); + // Authenticate locally if no internet connection was found + if (connectivityResult.contains(ConnectivityResult.none)) { + emit(AuthenticatedState(user: users.first, localAuth: true)); + return; + } authenticate(creds).then((auth) { auth.fold((error) { emit(AuthErrorState(error)); From 2be151efc69713b1a563a514fa0280895219ea81 Mon Sep 17 00:00:00 2001 From: Erick Date: Mon, 16 Dec 2024 18:05:21 +0300 Subject: [PATCH 02/24] feat: alerting the user incase of network connection changes --- lib/features/home/views/layout.dart | 42 ++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/lib/features/home/views/layout.dart b/lib/features/home/views/layout.dart index be4e2e5..82d79f4 100644 --- a/lib/features/home/views/layout.dart +++ b/lib/features/home/views/layout.dart @@ -1,6 +1,8 @@ +import 'dart:async'; + import 'package:academia/features/features.dart'; -import 'package:academia/features/profile/profile_page_desktop.dart'; import 'package:academia/utils/responsive/responsive.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:icons_plus/icons_plus.dart'; @@ -18,16 +20,50 @@ class _LayoutState extends State { child: Text("Statistics"), ), Center( - child: Text("Statistics"), + child: Text("Courses"), ), Center( - child: Text("Statistics"), + child: Text("Social"), ), Center( child: Text("Statistics"), ), ProfilePage() ]; + + // Stream subscription for network notification + @override + void initState() { + super.initState(); + StreamSubscription> subscription = Connectivity() + .onConnectivityChanged + .listen((List result) { + // Received changes in available connectivity types! + if (result.contains(ConnectivityResult.none)) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + duration: Duration(days: 30), + showCloseIcon: true, + content: Text( + "Your internet connection has been lost some features may not work"), + ), + ); + return; + } + if (!result.contains(ConnectivityResult.none)) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + showCloseIcon: true, + content: Text("Your internet connection has been restored"), + ), + ); + return; + } + }); + } + @override Widget build(BuildContext context) { return LayoutBuilder( From 10e2fc9cbab19710e4809e0cbe4fe6465eb5eb94 Mon Sep 17 00:00:00 2001 From: Erick Date: Tue, 17 Dec 2024 17:25:23 +0300 Subject: [PATCH 03/24] fix: poping previous scaffold messages --- lib/features/home/views/layout.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/features/home/views/layout.dart b/lib/features/home/views/layout.dart index 82d79f4..fc84e57 100644 --- a/lib/features/home/views/layout.dart +++ b/lib/features/home/views/layout.dart @@ -41,6 +41,7 @@ class _LayoutState extends State { // Received changes in available connectivity types! if (result.contains(ConnectivityResult.none)) { if (!mounted) return; + ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( duration: Duration(days: 30), @@ -53,6 +54,8 @@ class _LayoutState extends State { } if (!result.contains(ConnectivityResult.none)) { if (!mounted) return; + + ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( showCloseIcon: true, From 2c6ff5a1b495c51c0fd35394d032095d0ccdb601 Mon Sep 17 00:00:00 2001 From: Erick Date: Tue, 17 Dec 2024 05:34:00 +0300 Subject: [PATCH 04/24] fix: first time app launch leading to retrieved users --- lib/app.dart | 10 +- lib/database/database.dart | 17 -- lib/database/database.g.dart | 95 +++------- lib/features/auth/auth.dart | 2 + lib/features/auth/cubit/auth_cubit.dart | 14 +- lib/features/auth/models/user_profile.dart | 5 +- .../repository/user_local_repository.dart | 23 ++- .../repository/user_remote_repository.dart | 22 ++- .../auth/repository/user_repository.dart | 42 ++++- lib/features/home/views/layout.dart | 62 +++++-- lib/features/profile/cubit/profile_cubit.dart | 42 +++++ lib/features/profile/cubit/profile_state.dart | 22 +++ lib/features/profile/profile.dart | 4 +- lib/features/profile/profile_page_mobile.dart | 92 --------- .../profile/{ => views}/profile_page.dart | 4 +- .../{ => views}/profile_page_desktop.dart | 0 .../profile/views/profile_page_mobile.dart | 175 ++++++++++++++++++ lib/utils/network/dio_client.dart | 4 +- lib/utils/router/default_route.dart | 2 - lib/utils/router/router.dart | 3 - 20 files changed, 414 insertions(+), 226 deletions(-) create mode 100644 lib/features/profile/cubit/profile_cubit.dart create mode 100644 lib/features/profile/cubit/profile_state.dart delete mode 100644 lib/features/profile/profile_page_mobile.dart rename lib/features/profile/{ => views}/profile_page.dart (80%) rename lib/features/profile/{ => views}/profile_page_desktop.dart (100%) create mode 100644 lib/features/profile/views/profile_page_mobile.dart diff --git a/lib/app.dart b/lib/app.dart index 4e85e0e..da48d7a 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,8 +1,10 @@ -import 'package:academia/features/auth/cubit/auth_cubit.dart'; +import 'package:academia/database/database.dart'; +import 'package:academia/features/features.dart'; import 'package:academia/utils/router/router.dart'; import 'package:flutter/material.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:get_it/get_it.dart'; import 'package:google_fonts/google_fonts.dart'; class Academia extends StatelessWidget { @@ -14,9 +16,15 @@ class Academia extends StatelessWidget { @override Widget build(BuildContext context) { + // Inject the application database + GetIt.instance.registerSingletonIfAbsent( + () => AppDatabase(), + instanceName: "cacheDB", + ); return MultiBlocProvider( providers: [ BlocProvider(create: (_) => AuthCubit()), + BlocProvider(create: (_) => ProfileCubit()), ], child: DynamicColorBuilder( builder: (lightscheme, darkscheme) => MaterialApp.router( diff --git a/lib/database/database.dart b/lib/database/database.dart index a3a42fc..a0c858c 100644 --- a/lib/database/database.dart +++ b/lib/database/database.dart @@ -61,20 +61,3 @@ class AppDatabase extends _$AppDatabase { return driftDatabase(name: 'academia'); } } - -/// A singleton class to reference the local database. -/// Use this instead of AppDatabase to ensure you always -/// have an initialized instance -final class LocalDatabase { - static final LocalDatabase _instance = LocalDatabase._internal(); - - LocalDatabase._internal(); - - factory LocalDatabase() { - return _instance; - } - - AppDatabase getInstance() { - return AppDatabase(); - } -} diff --git a/lib/database/database.g.dart b/lib/database/database.g.dart index 4083e81..c566a91 100644 --- a/lib/database/database.g.dart +++ b/lib/database/database.g.dart @@ -579,15 +579,6 @@ class $UserProfileTable extends UserProfile final GeneratedDatabase attachedDatabase; final String? _alias; $UserProfileTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - hasAutoIncrement: true, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); static const VerificationMeta _userIdMeta = const VerificationMeta('userId'); @override late final GeneratedColumn userId = GeneratedColumn( @@ -660,7 +651,6 @@ class $UserProfileTable extends UserProfile type: DriftSqlType.dateTime, requiredDuringInsert: true); @override List get $columns => [ - id, userId, bio, vibePoints, @@ -682,9 +672,6 @@ class $UserProfileTable extends UserProfile {bool isInserting = false}) { final context = VerificationContext(); final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } if (data.containsKey('user_id')) { context.handle(_userIdMeta, userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); @@ -743,13 +730,11 @@ class $UserProfileTable extends UserProfile } @override - Set get $primaryKey => {id}; + Set get $primaryKey => {userId}; @override UserProfileData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserProfileData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}id'])!, userId: attachedDatabase.typeMapping .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, bio: attachedDatabase.typeMapping @@ -780,7 +765,6 @@ class $UserProfileTable extends UserProfile } class UserProfileData extends DataClass implements Insertable { - final int id; final String userId; final String? bio; final int vibePoints; @@ -792,8 +776,7 @@ class UserProfileData extends DataClass implements Insertable { final String campus; final DateTime dateOfBirth; const UserProfileData( - {required this.id, - required this.userId, + {required this.userId, this.bio, required this.vibePoints, this.profilePictureUrl, @@ -806,7 +789,6 @@ class UserProfileData extends DataClass implements Insertable { @override Map toColumns(bool nullToAbsent) { final map = {}; - map['id'] = Variable(id); map['user_id'] = Variable(userId); if (!nullToAbsent || bio != null) { map['bio'] = Variable(bio); @@ -828,7 +810,6 @@ class UserProfileData extends DataClass implements Insertable { UserProfileCompanion toCompanion(bool nullToAbsent) { return UserProfileCompanion( - id: Value(id), userId: Value(userId), bio: bio == null && nullToAbsent ? const Value.absent() : Value(bio), vibePoints: Value(vibePoints), @@ -850,7 +831,6 @@ class UserProfileData extends DataClass implements Insertable { {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserProfileData( - id: serializer.fromJson(json['id']), userId: serializer.fromJson(json['user_id']), bio: serializer.fromJson(json['bio']), vibePoints: serializer.fromJson(json['vibe_points']), @@ -868,7 +848,6 @@ class UserProfileData extends DataClass implements Insertable { Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { - 'id': serializer.toJson(id), 'user_id': serializer.toJson(userId), 'bio': serializer.toJson(bio), 'vibe_points': serializer.toJson(vibePoints), @@ -883,8 +862,7 @@ class UserProfileData extends DataClass implements Insertable { } UserProfileData copyWith( - {int? id, - String? userId, + {String? userId, Value bio = const Value.absent(), int? vibePoints, Value profilePictureUrl = const Value.absent(), @@ -895,7 +873,6 @@ class UserProfileData extends DataClass implements Insertable { String? campus, DateTime? dateOfBirth}) => UserProfileData( - id: id ?? this.id, userId: userId ?? this.userId, bio: bio.present ? bio.value : this.bio, vibePoints: vibePoints ?? this.vibePoints, @@ -913,7 +890,6 @@ class UserProfileData extends DataClass implements Insertable { ); UserProfileData copyWithCompanion(UserProfileCompanion data) { return UserProfileData( - id: data.id.present ? data.id.value : this.id, userId: data.userId.present ? data.userId.value : this.userId, bio: data.bio.present ? data.bio.value : this.bio, vibePoints: @@ -937,7 +913,6 @@ class UserProfileData extends DataClass implements Insertable { @override String toString() { return (StringBuffer('UserProfileData(') - ..write('id: $id, ') ..write('userId: $userId, ') ..write('bio: $bio, ') ..write('vibePoints: $vibePoints, ') @@ -953,23 +928,12 @@ class UserProfileData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash( - id, - userId, - bio, - vibePoints, - profilePictureUrl, - lastSeen, - createdAt, - modifiedAt, - admissionNumber, - campus, - dateOfBirth); + int get hashCode => Object.hash(userId, bio, vibePoints, profilePictureUrl, + lastSeen, createdAt, modifiedAt, admissionNumber, campus, dateOfBirth); @override bool operator ==(Object other) => identical(this, other) || (other is UserProfileData && - other.id == this.id && other.userId == this.userId && other.bio == this.bio && other.vibePoints == this.vibePoints && @@ -983,7 +947,6 @@ class UserProfileData extends DataClass implements Insertable { } class UserProfileCompanion extends UpdateCompanion { - final Value id; final Value userId; final Value bio; final Value vibePoints; @@ -994,8 +957,8 @@ class UserProfileCompanion extends UpdateCompanion { final Value admissionNumber; final Value campus; final Value dateOfBirth; + final Value rowid; const UserProfileCompanion({ - this.id = const Value.absent(), this.userId = const Value.absent(), this.bio = const Value.absent(), this.vibePoints = const Value.absent(), @@ -1006,9 +969,9 @@ class UserProfileCompanion extends UpdateCompanion { this.admissionNumber = const Value.absent(), this.campus = const Value.absent(), this.dateOfBirth = const Value.absent(), + this.rowid = const Value.absent(), }); UserProfileCompanion.insert({ - this.id = const Value.absent(), required String userId, this.bio = const Value.absent(), this.vibePoints = const Value.absent(), @@ -1019,10 +982,10 @@ class UserProfileCompanion extends UpdateCompanion { this.admissionNumber = const Value.absent(), this.campus = const Value.absent(), required DateTime dateOfBirth, + this.rowid = const Value.absent(), }) : userId = Value(userId), dateOfBirth = Value(dateOfBirth); static Insertable custom({ - Expression? id, Expression? userId, Expression? bio, Expression? vibePoints, @@ -1033,9 +996,9 @@ class UserProfileCompanion extends UpdateCompanion { Expression? admissionNumber, Expression? campus, Expression? dateOfBirth, + Expression? rowid, }) { return RawValuesInsertable({ - if (id != null) 'id': id, if (userId != null) 'user_id': userId, if (bio != null) 'bio': bio, if (vibePoints != null) 'vibe_points': vibePoints, @@ -1046,12 +1009,12 @@ class UserProfileCompanion extends UpdateCompanion { if (admissionNumber != null) 'admission_number': admissionNumber, if (campus != null) 'campus': campus, if (dateOfBirth != null) 'date_of_birth': dateOfBirth, + if (rowid != null) 'rowid': rowid, }); } UserProfileCompanion copyWith( - {Value? id, - Value? userId, + {Value? userId, Value? bio, Value? vibePoints, Value? profilePictureUrl, @@ -1060,9 +1023,9 @@ class UserProfileCompanion extends UpdateCompanion { Value? modifiedAt, Value? admissionNumber, Value? campus, - Value? dateOfBirth}) { + Value? dateOfBirth, + Value? rowid}) { return UserProfileCompanion( - id: id ?? this.id, userId: userId ?? this.userId, bio: bio ?? this.bio, vibePoints: vibePoints ?? this.vibePoints, @@ -1073,15 +1036,13 @@ class UserProfileCompanion extends UpdateCompanion { admissionNumber: admissionNumber ?? this.admissionNumber, campus: campus ?? this.campus, dateOfBirth: dateOfBirth ?? this.dateOfBirth, + rowid: rowid ?? this.rowid, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } if (userId.present) { map['user_id'] = Variable(userId.value); } @@ -1112,13 +1073,15 @@ class UserProfileCompanion extends UpdateCompanion { if (dateOfBirth.present) { map['date_of_birth'] = Variable(dateOfBirth.value); } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } return map; } @override String toString() { return (StringBuffer('UserProfileCompanion(') - ..write('id: $id, ') ..write('userId: $userId, ') ..write('bio: $bio, ') ..write('vibePoints: $vibePoints, ') @@ -1128,7 +1091,8 @@ class UserProfileCompanion extends UpdateCompanion { ..write('modifiedAt: $modifiedAt, ') ..write('admissionNumber: $admissionNumber, ') ..write('campus: $campus, ') - ..write('dateOfBirth: $dateOfBirth') + ..write('dateOfBirth: $dateOfBirth, ') + ..write('rowid: $rowid') ..write(')')) .toString(); } @@ -1888,7 +1852,6 @@ typedef $$UserTableProcessedTableManager = ProcessedTableManager< PrefetchHooks Function({bool userProfileRefs})>; typedef $$UserProfileTableCreateCompanionBuilder = UserProfileCompanion Function({ - Value id, required String userId, Value bio, Value vibePoints, @@ -1899,10 +1862,10 @@ typedef $$UserProfileTableCreateCompanionBuilder = UserProfileCompanion Value admissionNumber, Value campus, required DateTime dateOfBirth, + Value rowid, }); typedef $$UserProfileTableUpdateCompanionBuilder = UserProfileCompanion Function({ - Value id, Value userId, Value bio, Value vibePoints, @@ -1913,6 +1876,7 @@ typedef $$UserProfileTableUpdateCompanionBuilder = UserProfileCompanion Value admissionNumber, Value campus, Value dateOfBirth, + Value rowid, }); final class $$UserProfileTableReferences @@ -1942,9 +1906,6 @@ class $$UserProfileTableFilterComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnFilters(column)); - ColumnFilters get bio => $composableBuilder( column: $table.bio, builder: (column) => ColumnFilters(column)); @@ -2004,9 +1965,6 @@ class $$UserProfileTableOrderingComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get bio => $composableBuilder( column: $table.bio, builder: (column) => ColumnOrderings(column)); @@ -2066,9 +2024,6 @@ class $$UserProfileTableAnnotationComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - GeneratedColumn get bio => $composableBuilder(column: $table.bio, builder: (column) => column); @@ -2140,7 +2095,6 @@ class $$UserProfileTableTableManager extends RootTableManager< createComputedFieldComposer: () => $$UserProfileTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ - Value id = const Value.absent(), Value userId = const Value.absent(), Value bio = const Value.absent(), Value vibePoints = const Value.absent(), @@ -2151,9 +2105,9 @@ class $$UserProfileTableTableManager extends RootTableManager< Value admissionNumber = const Value.absent(), Value campus = const Value.absent(), Value dateOfBirth = const Value.absent(), + Value rowid = const Value.absent(), }) => UserProfileCompanion( - id: id, userId: userId, bio: bio, vibePoints: vibePoints, @@ -2164,9 +2118,9 @@ class $$UserProfileTableTableManager extends RootTableManager< admissionNumber: admissionNumber, campus: campus, dateOfBirth: dateOfBirth, + rowid: rowid, ), createCompanionCallback: ({ - Value id = const Value.absent(), required String userId, Value bio = const Value.absent(), Value vibePoints = const Value.absent(), @@ -2177,9 +2131,9 @@ class $$UserProfileTableTableManager extends RootTableManager< Value admissionNumber = const Value.absent(), Value campus = const Value.absent(), required DateTime dateOfBirth, + Value rowid = const Value.absent(), }) => UserProfileCompanion.insert( - id: id, userId: userId, bio: bio, vibePoints: vibePoints, @@ -2190,6 +2144,7 @@ class $$UserProfileTableTableManager extends RootTableManager< admissionNumber: admissionNumber, campus: campus, dateOfBirth: dateOfBirth, + rowid: rowid, ), withReferenceMapper: (p0) => p0 .map((e) => ( diff --git a/lib/features/auth/auth.dart b/lib/features/auth/auth.dart index fd4af2f..9617e38 100644 --- a/lib/features/auth/auth.dart +++ b/lib/features/auth/auth.dart @@ -1,2 +1,4 @@ +export 'cubit/auth_cubit.dart'; +export 'cubit/auth_states.dart'; export './views/login_page.dart'; export './views/user_selection_page.dart'; diff --git a/lib/features/auth/cubit/auth_cubit.dart b/lib/features/auth/cubit/auth_cubit.dart index f8fedeb..3e61191 100644 --- a/lib/features/auth/cubit/auth_cubit.dart +++ b/lib/features/auth/cubit/auth_cubit.dart @@ -21,19 +21,21 @@ class AuthCubit extends Cubit { }, (users) { if (users.isEmpty) { emit(AuthFirstAppLaunch()); + return true; } else if (users.length == 1) { return fetchUserCredsFromCache(users.first).then((result) { result.fold((error) { emit(AuthErrorState(error)); return; }, (creds) async { + // TODO: erick allow checking for connection result final List connectivityResult = await (Connectivity().checkConnectivity()); // Authenticate locally if no internet connection was found - if (connectivityResult.contains(ConnectivityResult.none)) { - emit(AuthenticatedState(user: users.first, localAuth: true)); - return; - } + // if (connectivityResult.contains(ConnectivityResult.none)) { + // emit(AuthenticatedState(user: users.first, localAuth: true)); + // return; + // } authenticate(creds).then((auth) { auth.fold((error) { emit(AuthErrorState(error)); @@ -76,8 +78,8 @@ class AuthCubit extends Cubit { } Future fetchUserProfile(UserData user) async { - final result = await _userRepository.fetchUserProfile(user); - result.fold((l) { + final result = await _userRepository.fetchUserProfileFromCache(user); + return result.fold((l) { debugPrint(l); return null; }, (r) { diff --git a/lib/features/auth/models/user_profile.dart b/lib/features/auth/models/user_profile.dart index 147ef0b..4e749d2 100644 --- a/lib/features/auth/models/user_profile.dart +++ b/lib/features/auth/models/user_profile.dart @@ -2,8 +2,6 @@ import 'package:drift/drift.dart'; import './user.dart'; class UserProfile extends Table { - IntColumn get id => integer().autoIncrement()(); - @JsonKey("user_id") TextColumn get userId => text().references(User, #id)(); @@ -27,4 +25,7 @@ class UserProfile extends Table { TextColumn get campus => text().withDefault(const Constant("athi"))(); @JsonKey("date_of_birth") DateTimeColumn get dateOfBirth => dateTime()(); + + @override + Set>? get primaryKey => {userId}; } diff --git a/lib/features/auth/repository/user_local_repository.dart b/lib/features/auth/repository/user_local_repository.dart index 2a13621..e1dd6bb 100644 --- a/lib/features/auth/repository/user_local_repository.dart +++ b/lib/features/auth/repository/user_local_repository.dart @@ -1,12 +1,13 @@ import 'package:academia/database/database.dart'; import 'package:dartz/dartz.dart'; import 'package:drift/drift.dart'; +import 'package:get_it/get_it.dart'; /// Repository for manipulation of user related information /// in the application's local cache final class UserLocalRepository { // the db's instance - final AppDatabase _localDb = LocalDatabase().getInstance(); + final AppDatabase _localDb = GetIt.instance.get(instanceName: "cacheDB"); /// Fetches all users from the local cache /// incase of an error it will return a [String] to the left @@ -101,6 +102,26 @@ final class UserLocalRepository { } } + /// Sets a user's profile to the cache + Future> addUserProfile(UserProfileData profile) async { + try { + final ok = + await _localDb.into(_localDb.userProfile).insertOnConflictUpdate( + profile.toCompanion(true), + ); + if (ok != 0) { + return right(true); + } + return left( + "The specified user profile data was not inserted since it exists and confliced", + ); + } catch (e) { + return left( + "Failed to append user profile to cache with error description ${e.toString()}", + ); + } + } + /// Retrieves a user's profile from the cache Future> fetchUserProfile( UserData user) async { diff --git a/lib/features/auth/repository/user_remote_repository.dart b/lib/features/auth/repository/user_remote_repository.dart index 6b34c6d..6b072c3 100644 --- a/lib/features/auth/repository/user_remote_repository.dart +++ b/lib/features/auth/repository/user_remote_repository.dart @@ -26,7 +26,27 @@ final class UserRemoteRepository with DioErrorHandler { } on DioException catch (de) { return handleDioError(de); } catch (e) { - return left("Please check your internet connection and try that again!"); + return left("Something went terribly wrong please try that later"); + } + } + + /// The function attempts to fetch a user's profile from verisafe + /// In the event of success it retuns the [UserProfileData] + /// and in case of failure it retuns a string indicating what exactly + /// went wrong + Future> fetchUserProfile() async { + try { + final response = await _client.dio.get("/users/profile"); + if (response.statusCode == 200) { + return right(UserProfileData.fromJson(response.data)); + } + + return left(response.data["error"] ?? response.statusMessage); + } on DioException catch (de) { + return handleDioError(de); + } catch (e) { + rethrow; + return left("Something went terribly wrong please try that later"); } } } diff --git a/lib/features/auth/repository/user_repository.dart b/lib/features/auth/repository/user_repository.dart index 0dca726..df203b2 100644 --- a/lib/features/auth/repository/user_repository.dart +++ b/lib/features/auth/repository/user_repository.dart @@ -46,15 +46,16 @@ final class UserRepository { Future> authenticateRemotely( UserCredentialData credentials) async { // Register a magnet singleton instance - GetIt.instance.registerSingletonIfAbsent( - () => Magnet(credentials.admno, credentials.password), - instanceName: "magnet", - ); + // GetIt.instance.registerSingletonIfAbsent( + // () => Magnet(credentials.admno, credentials.password), + // instanceName: "magnet", + // ); + // TODO: (erick) enable auth with magnet // authenticate with magnet - final magnetResult = - await (GetIt.instance.get(instanceName: "magnet").login()); - + const magnetResult = Right(Object()); + // await (GetIt.instance.get(instanceName: "magnet").login()); + // return magnetResult.fold((error) { return left(error.toString()); }, (session) async { @@ -80,9 +81,32 @@ final class UserRepository { } /// Retrieves a user's profile from the cache - Future> fetchUserProfile( + Future> fetchUserProfileFromCache( UserData user, ) async { - return await _userLocalRepository.fetchUserProfile(user); + final localResult = await _userLocalRepository.fetchUserProfile(user); + if (localResult.isRight()) { + final profile = (localResult as Right).value; + if (profile == null) { + return await refreshUserProfile(user); + } + return right((localResult as Right).value); + } + + return Left((localResult as Left).value); + } + + Future> refreshUserProfile( + UserData user, + ) async { + // Fetch from the remote db + final remoteResult = await _userRemoteRepository.fetchUserProfile(); + if (remoteResult.isRight()) { + final profile = (remoteResult as Right).value; + await _userLocalRepository.addUserProfile(profile); + return right(profile); + } + + return (left((remoteResult as Left).value)); } } diff --git a/lib/features/home/views/layout.dart b/lib/features/home/views/layout.dart index fc84e57..310ae91 100644 --- a/lib/features/home/views/layout.dart +++ b/lib/features/home/views/layout.dart @@ -15,21 +15,21 @@ class Layout extends StatefulWidget { class _LayoutState extends State { int _selectedIndex = 0; - final pages = const [ - Center( - child: Text("Statistics"), - ), - Center( - child: Text("Courses"), - ), - Center( - child: Text("Social"), - ), - Center( - child: Text("Statistics"), - ), - ProfilePage() - ]; + // final pages = const [ + // Center( + // child: Text("Statistics"), + // ), + // Center( + // child: Text("Courses"), + // ), + // Center( + // child: Text("Social"), + // ), + // Center( + // child: Text("Statistics"), + // ), + // ProfilePage() + // ]; // Stream subscription for network notification @override @@ -74,7 +74,21 @@ class _LayoutState extends State { body: constraints.maxWidth < ScreenDimension.mobileWidth ? IndexedStack( index: _selectedIndex, - children: pages, + children: const [ + Center( + child: Text("Statistics"), + ), + Center( + child: Text("Courses"), + ), + Center( + child: Text("Social"), + ), + Center( + child: Text("Statistics"), + ), + ProfilePage() + ], ) : Row( children: [ @@ -119,7 +133,21 @@ class _LayoutState extends State { Expanded( child: IndexedStack( index: _selectedIndex, - children: pages, + children: const [ + Center( + child: Text("Statistics"), + ), + Center( + child: Text("Courses"), + ), + Center( + child: Text("Social"), + ), + Center( + child: Text("Statistics"), + ), + ProfilePage() + ], ), ) ], diff --git a/lib/features/profile/cubit/profile_cubit.dart b/lib/features/profile/cubit/profile_cubit.dart new file mode 100644 index 0000000..c11def0 --- /dev/null +++ b/lib/features/profile/cubit/profile_cubit.dart @@ -0,0 +1,42 @@ +import 'package:academia/database/database.dart'; +import 'package:academia/features/auth/repository/user_repository.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import './profile_state.dart'; + +final class ProfileCubit extends Cubit { + final UserRepository _userRepository = UserRepository(); + ProfileCubit() : super(ProfileInitalState()); + + /// Fetches the profile for a [User] specified by [user] from + /// the local cache + Future> fetchUserProfileFromCache( + UserData user, + ) async { + emit(ProfileLoadingState()); + final result = await _userRepository.fetchUserProfileFromCache(user); + return result.fold((l) { + emit(ProfileErrorState(error: l)); + return left(l); + }, (r) { + emit(ProfileLoadedState(userProfiles: r)); + return right(r); + }); + } + + /// Fetches the profile for a [User] specified by [user] from + /// the remote repo + Future> fetchUserProfileFromRemote( + UserData user, + ) async { + emit(ProfileLoadingState()); + final result = await _userRepository.refreshUserProfile(user); + return result.fold((l) { + emit(ProfileErrorState(error: l)); + return left(l); + }, (r) { + emit(ProfileLoadedState(userProfiles: r)); + return right(r); + }); + } +} diff --git a/lib/features/profile/cubit/profile_state.dart b/lib/features/profile/cubit/profile_state.dart new file mode 100644 index 0000000..4470bf3 --- /dev/null +++ b/lib/features/profile/cubit/profile_state.dart @@ -0,0 +1,22 @@ +// The base profile state +import 'package:academia/database/database.dart'; + +class ProfileState {} + +// Profile initial +class ProfileInitalState extends ProfileState {} + +/// Profile loading state +class ProfileLoadingState extends ProfileState {} + +/// Profile error state +class ProfileErrorState extends ProfileState { + final String error; + ProfileErrorState({required this.error}); +} + +/// Profile loaded state +class ProfileLoadedState extends ProfileState { + final UserProfileData userProfiles; + ProfileLoadedState({required this.userProfiles}); +} diff --git a/lib/features/profile/profile.dart b/lib/features/profile/profile.dart index 988ef68..a9c445a 100644 --- a/lib/features/profile/profile.dart +++ b/lib/features/profile/profile.dart @@ -1 +1,3 @@ -export 'profile_page.dart'; +export 'views/profile_page.dart'; +export 'cubit/profile_cubit.dart'; +export 'cubit/profile_state.dart'; diff --git a/lib/features/profile/profile_page_mobile.dart b/lib/features/profile/profile_page_mobile.dart deleted file mode 100644 index 26db170..0000000 --- a/lib/features/profile/profile_page_mobile.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:icons_plus/icons_plus.dart'; -import 'package:sliver_tools/sliver_tools.dart'; - -class ProfilePageMobile extends StatefulWidget { - const ProfilePageMobile({super.key}); - - @override - State createState() => _ProfilePageMobileState(); -} - -class _ProfilePageMobileState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - body: CustomScrollView( - slivers: [ - SliverAppBar( - pinned: true, - floating: true, - snap: true, - title: const Text("My Profile"), - actions: [ - IconButton( - onPressed: () {}, - icon: const Icon(Bootstrap.pen), - ), - ], - ), - SliverPinnedHeader( - child: Container( - width: MediaQuery.of(context).size.width, - padding: const EdgeInsets.all(12), - child: Column( - spacing: 12, - children: [ - const CircleAvatar( - radius: 80, - ), - Text( - "John Doe Sang Kipkemboi", - style: Theme.of(context).textTheme.headlineSmall, - overflow: TextOverflow.ellipsis, - ), - const Text( - "Some long bio here to be here", - overflow: TextOverflow.visible, - ), - FilledButton( - onPressed: () {}, - child: const Text("Preview my school ID"), - ), - ], - ), - ), - ), - SliverPadding( - padding: const EdgeInsets.all(12), - sliver: SliverClip( - child: MultiSliver( - children: const [ - ListTile( - leading: Icon(Bootstrap.hash), - title: Text("Admission Number"), - subtitle: Text("21-1000"), - ), - ListTile( - leading: Icon(Bootstrap.compass), - title: Text("Campus"), - subtitle: Text("Athi River"), - ), - ListTile( - leading: Icon(Bootstrap.phone), - title: Text("Phone Number"), - subtitle: Text("078277272"), - ), - ListTile( - leading: Icon(Bootstrap.envelope_heart), - title: Text("Email"), - subtitle: Text("someone@example.com"), - ) - ], - ), - ), - ), - ], - ), - ); - } -} diff --git a/lib/features/profile/profile_page.dart b/lib/features/profile/views/profile_page.dart similarity index 80% rename from lib/features/profile/profile_page.dart rename to lib/features/profile/views/profile_page.dart index b72d985..aedc1c4 100644 --- a/lib/features/profile/profile_page.dart +++ b/lib/features/profile/views/profile_page.dart @@ -1,7 +1,7 @@ -import 'package:academia/features/profile/profile_page_desktop.dart'; -import 'package:academia/features/profile/profile_page_mobile.dart'; import 'package:academia/utils/responsive/responsive.dart'; import 'package:flutter/material.dart'; +import 'profile_page_mobile.dart'; +import 'profile_page_desktop.dart'; class ProfilePage extends StatefulWidget { const ProfilePage({super.key}); diff --git a/lib/features/profile/profile_page_desktop.dart b/lib/features/profile/views/profile_page_desktop.dart similarity index 100% rename from lib/features/profile/profile_page_desktop.dart rename to lib/features/profile/views/profile_page_desktop.dart diff --git a/lib/features/profile/views/profile_page_mobile.dart b/lib/features/profile/views/profile_page_mobile.dart new file mode 100644 index 0000000..996cfa1 --- /dev/null +++ b/lib/features/profile/views/profile_page_mobile.dart @@ -0,0 +1,175 @@ +import 'package:academia/database/database.dart'; +import 'package:academia/features/features.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:icons_plus/icons_plus.dart'; +import 'package:skeletonizer/skeletonizer.dart'; +import 'package:sliver_tools/sliver_tools.dart'; + +class ProfilePageMobile extends StatefulWidget { + const ProfilePageMobile({super.key}); + + @override + State createState() => _ProfilePageMobileState(); +} + +class _ProfilePageMobileState extends State { + late UserData user; + late AuthCubit authCubit = BlocProvider.of(context); + late ProfileCubit profileCubit = BlocProvider.of(context); + + @override + void initState() { + super.initState(); + if (authCubit.state is AuthenticatedState) { + user = (authCubit.state as AuthenticatedState).user; + profileCubit.fetchUserProfileFromCache(user); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: RefreshIndicator( + onRefresh: () async { + return Future.delayed(const Duration(seconds: 10)); + }, + child: CustomScrollView( + slivers: [ + SliverAppBar( + actions: [ + IconButton( + onPressed: () {}, + icon: const Icon(Bootstrap.pencil), + ), + IconButton( + onPressed: () {}, + icon: const Icon(Bootstrap.person_add), + ) + ], + ), + SliverPinnedHeader( + child: Container( + width: MediaQuery.of(context).size.width, + padding: const EdgeInsets.all(12), + child: Row( + spacing: 12, + children: [ + const CircleAvatar( + radius: 60, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 2, + children: [ + Text( + "${user.firstname} ${user.othernames}", + style: Theme.of(context).textTheme.headlineSmall, + overflow: TextOverflow.ellipsis, + ), + TextButton( + onPressed: () {}, + child: const Text("Preview my school ID"), + ), + ], + ), + ), + ], + ), + ), + ), + SliverPadding( + padding: const EdgeInsets.all(12), + sliver: SliverClip( + child: BlocBuilder( + builder: (context, state) { + print(state.runtimeType.toString()); + if (state is ProfileErrorState) { + print((state as ProfileErrorState).error); + } + if (profileCubit.state is! ProfileLoadedState) { + return SliverFillRemaining( + child: Skeletonizer( + enabled: true, + child: ListView.builder( + itemCount: 8, + itemBuilder: (context, index) => Card( + elevation: 0, + shape: index == 0 + ? const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), + ) + : index == 7 + ? const RoundedRectangleBorder( + borderRadius: + BorderRadius.vertical( + bottom: Radius.circular(12), + ), + ) + : const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.zero)), + child: const ListTile(), + )), + ), + ); + } + final profile = (state as ProfileLoadedState).userProfiles; + return MultiSliver( + children: [ + Text( + "Some long bio here to be here", + overflow: TextOverflow.visible, + style: Theme.of(context).textTheme.bodyLarge, + ), + const Card( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), + ), + child: ListTile( + leading: Icon(Bootstrap.hash), + title: Text("Admission Number"), + subtitle: Text("21-1000"), + ), + ), + const Card( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.zero)), + child: ListTile( + leading: Icon(Bootstrap.hash), + title: Text("Admission Number"), + subtitle: Text("21-1000"), + ), + ), + const Card( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12), + ), + ), + child: ListTile( + leading: Icon(Bootstrap.hash), + title: Text("Admission Number"), + subtitle: Text("21-1000"), + ), + ), + ], + ); + }, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/utils/network/dio_client.dart b/lib/utils/network/dio_client.dart index d1fbff2..96c191b 100644 --- a/lib/utils/network/dio_client.dart +++ b/lib/utils/network/dio_client.dart @@ -4,8 +4,8 @@ import './auth_interceptor.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; class DioClient { - static const String _baseUrl = "http://192.168.2.115:8000/v2"; - // static const String _baseUrl = "http://192.168.26.183:8000/v2"; + // static const String _baseUrl = "http://192.168.2.115:8000/v2"; + static const String _baseUrl = "http://127.0.0.1:8000/v2"; DioClient() { dio.interceptors.add( diff --git a/lib/utils/router/default_route.dart b/lib/utils/router/default_route.dart index 96b42cf..fef345d 100644 --- a/lib/utils/router/default_route.dart +++ b/lib/utils/router/default_route.dart @@ -1,5 +1,3 @@ -import 'package:academia/features/auth/cubit/auth_cubit.dart'; -import 'package:academia/features/auth/cubit/auth_states.dart'; import 'package:academia/features/features.dart'; import 'package:academia/utils/router/router.dart'; import 'package:flutter/material.dart'; diff --git a/lib/utils/router/router.dart b/lib/utils/router/router.dart index 8126d78..15bfc26 100644 --- a/lib/utils/router/router.dart +++ b/lib/utils/router/router.dart @@ -1,8 +1,5 @@ -import 'package:academia/features/auth/cubit/auth_cubit.dart'; -import 'package:academia/features/auth/cubit/auth_states.dart'; import 'package:academia/utils/router/default_route.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:academia/features/features.dart'; From a03adcb7a8285a39af00b03df914957594289324 Mon Sep 17 00:00:00 2001 From: Erick Date: Wed, 18 Dec 2024 18:47:00 +0300 Subject: [PATCH 05/24] fix: login offline mode issues --- lib/features/auth/cubit/auth_cubit.dart | 19 +++++++---------- lib/features/auth/views/login_page.dart | 21 +------------------ .../profile/views/profile_page_mobile.dart | 1 + 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/lib/features/auth/cubit/auth_cubit.dart b/lib/features/auth/cubit/auth_cubit.dart index 3e61191..40d1674 100644 --- a/lib/features/auth/cubit/auth_cubit.dart +++ b/lib/features/auth/cubit/auth_cubit.dart @@ -21,21 +21,22 @@ class AuthCubit extends Cubit { }, (users) { if (users.isEmpty) { emit(AuthFirstAppLaunch()); - return true; + return null; } else if (users.length == 1) { return fetchUserCredsFromCache(users.first).then((result) { result.fold((error) { emit(AuthErrorState(error)); return; }, (creds) async { - // TODO: erick allow checking for connection result final List connectivityResult = await (Connectivity().checkConnectivity()); + // Authenticate locally if no internet connection was found - // if (connectivityResult.contains(ConnectivityResult.none)) { - // emit(AuthenticatedState(user: users.first, localAuth: true)); - // return; - // } + if (connectivityResult.contains(ConnectivityResult.none)) { + emit(AuthenticatedState(user: users.first, localAuth: true)); + return; + } + authenticate(creds).then((auth) { auth.fold((error) { emit(AuthErrorState(error)); @@ -54,12 +55,6 @@ class AuthCubit extends Cubit { ); } - StreamSubscription> subscription = Connectivity() - .onConnectivityChanged - .listen((List result) { - // Received changes in available connectivity types! - }); - /// Authenticate performs authentication mechanisms with both verisafe /// and magnet to authenticate a user Future> authenticate( diff --git a/lib/features/auth/views/login_page.dart b/lib/features/auth/views/login_page.dart index 517fc50..78eb3f8 100644 --- a/lib/features/auth/views/login_page.dart +++ b/lib/features/auth/views/login_page.dart @@ -35,25 +35,6 @@ class _LoginPageState extends State { bool validateForm() { return _formState.currentState!.validate(); } - // - // /// Shows a dialog with [title] and [content] - // void _showMessageDialog(String title, String content) { - // showDialog( - // context: context, - // builder: (context) => AlertDialog( - // title: Text(title), - // content: Text(content), - // actions: [ - // TextButton( - // onPressed: () { - // context.pop(); - // }, - // child: const Text("Ok"), - // ), - // ], - // ), - // ); - // } @override Widget build(BuildContext context) { @@ -198,7 +179,7 @@ class _LoginPageState extends State { ); }, (r) { HapticFeedback.heavyImpact(); - GoRouter.of(context).pushNamed( + GoRouter.of(context).pushReplacementNamed( AcademiaRouter.home, ); }); diff --git a/lib/features/profile/views/profile_page_mobile.dart b/lib/features/profile/views/profile_page_mobile.dart index 996cfa1..b6875f6 100644 --- a/lib/features/profile/views/profile_page_mobile.dart +++ b/lib/features/profile/views/profile_page_mobile.dart @@ -37,6 +37,7 @@ class _ProfilePageMobileState extends State { child: CustomScrollView( slivers: [ SliverAppBar( + automaticallyImplyLeading: false, actions: [ IconButton( onPressed: () {}, From 9d419c77d16ddda10f10479d329b6d8d260de7ff Mon Sep 17 00:00:00 2001 From: Erick Date: Thu, 19 Dec 2024 04:27:00 +0300 Subject: [PATCH 06/24] chore: user profile design semi done --- .../profile/views/profile_page_mobile.dart | 151 +++++++++++++++--- 1 file changed, 131 insertions(+), 20 deletions(-) diff --git a/lib/features/profile/views/profile_page_mobile.dart b/lib/features/profile/views/profile_page_mobile.dart index b6875f6..bdd775c 100644 --- a/lib/features/profile/views/profile_page_mobile.dart +++ b/lib/features/profile/views/profile_page_mobile.dart @@ -1,5 +1,6 @@ import 'package:academia/database/database.dart'; import 'package:academia/features/features.dart'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:icons_plus/icons_plus.dart'; @@ -32,12 +33,32 @@ class _ProfilePageMobileState extends State { return Scaffold( body: RefreshIndicator( onRefresh: () async { - return Future.delayed(const Duration(seconds: 10)); + final localAuth = (authCubit.state as AuthenticatedState).localAuth; + if (localAuth) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + "Please check your internet connection and try again!", + ), + ), + ); + return; + } + final response = await profileCubit.fetchUserProfileFromRemote(user); + response.fold((error) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(error)), + ); + }, (profile) { + setState(() {}); + }); }, child: CustomScrollView( slivers: [ SliverAppBar( automaticallyImplyLeading: false, + pinned: true, + floating: true, actions: [ IconButton( onPressed: () {}, @@ -56,8 +77,38 @@ class _ProfilePageMobileState extends State { child: Row( spacing: 12, children: [ - const CircleAvatar( + CircleAvatar( radius: 60, + child: ClipRRect( + borderRadius: BorderRadius.circular(50), + child: BlocBuilder( + buildWhen: (previous, current) { + if (current is ProfileLoadingState) { + return true; + } else if (current is ProfileLoadedState) { + return true; + } + return false; + }, + builder: (context, state) { + if (state is ProfileLoadingState) { + return const CircularProgressIndicator.adaptive(); + } + final profile = + (state as ProfileLoadedState).userProfiles; + + return CachedNetworkImage( + imageUrl: profile.profilePictureUrl ?? "", + fit: BoxFit.fill, + errorWidget: (context, error, message) { + return Image.asset( + "assets/icons/academia_prod.png", + ); + }, + ); + }, + ), + ), ), Expanded( child: Column( @@ -85,10 +136,6 @@ class _ProfilePageMobileState extends State { sliver: SliverClip( child: BlocBuilder( builder: (context, state) { - print(state.runtimeType.toString()); - if (state is ProfileErrorState) { - print((state as ProfileErrorState).error); - } if (profileCubit.state is! ProfileLoadedState) { return SliverFillRemaining( child: Skeletonizer( @@ -120,33 +167,97 @@ class _ProfilePageMobileState extends State { } final profile = (state as ProfileLoadedState).userProfiles; return MultiSliver( + pushPinnedChildren: true, children: [ - Text( - "Some long bio here to be here", - overflow: TextOverflow.visible, - style: Theme.of(context).textTheme.bodyLarge, + SliverPinnedHeader( + child: Container( + color: Theme.of(context).colorScheme.surface, + child: Text( + "Student information", + style: Theme.of(context).textTheme.titleLarge, + ), + ), ), - const Card( + Card( elevation: 0, - shape: RoundedRectangleBorder( + shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(12), ), ), child: ListTile( - leading: Icon(Bootstrap.hash), - title: Text("Admission Number"), - subtitle: Text("21-1000"), + leading: const Icon(Bootstrap.hash), + title: const Text("Admission Number"), + subtitle: Text("${profile.admissionNumber}"), ), ), - const Card( + Card( elevation: 0, - shape: RoundedRectangleBorder( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.at), + title: const Text("Academia Username"), + subtitle: Text("@${user.username}"), + ), + ), + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.chat_dots), + title: const Text("Bio"), + subtitle: Text( + "${profile.bio}", + overflow: TextOverflow.visible, + ), + ), + ), + Card( + elevation: 0, + shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.zero)), child: ListTile( - leading: Icon(Bootstrap.hash), - title: Text("Admission Number"), - subtitle: Text("21-1000"), + leading: const Icon(Bootstrap.gender_ambiguous), + title: const Text("Gender"), + subtitle: Text(user.gender), + ), + ), + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.zero)), + child: ListTile( + leading: const Icon(Icons.card_giftcard), + title: const Text("National ID"), + subtitle: Text(user.nationalId), + ), + ), + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.zero)), + child: ListTile( + leading: const Icon(Bootstrap.phone), + title: const Text("Phone"), + subtitle: Text(user.phone), + ), + ), + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.zero)), + child: ListTile( + leading: const Icon(Bootstrap.envelope), + title: const Text("Email"), + subtitle: Text(user.email ?? "unknown"), ), ), const Card( From fb2e52dd663b9734d13aa682806fd8272bf299a2 Mon Sep 17 00:00:00 2001 From: Erick Date: Sat, 21 Dec 2024 15:34:00 +0300 Subject: [PATCH 07/24] fix: central content profile cards being rounded --- lib/features/profile/views/profile_page_mobile.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/features/profile/views/profile_page_mobile.dart b/lib/features/profile/views/profile_page_mobile.dart index bdd775c..145a7d9 100644 --- a/lib/features/profile/views/profile_page_mobile.dart +++ b/lib/features/profile/views/profile_page_mobile.dart @@ -195,7 +195,7 @@ class _ProfilePageMobileState extends State { elevation: 0, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( - top: Radius.circular(12), + top: Radius.zero, ), ), child: ListTile( @@ -208,7 +208,7 @@ class _ProfilePageMobileState extends State { elevation: 0, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( - top: Radius.circular(12), + top: Radius.zero, ), ), child: ListTile( From 5f63a9e116ca4457465fde248b51ad12b1975a90 Mon Sep 17 00:00:00 2001 From: Erick Date: Sun, 22 Dec 2024 14:04:00 +0300 Subject: [PATCH 08/24] feat: added multiple accounts sign in --- lib/features/profile/views/profile_page_mobile.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/features/profile/views/profile_page_mobile.dart b/lib/features/profile/views/profile_page_mobile.dart index 145a7d9..259489d 100644 --- a/lib/features/profile/views/profile_page_mobile.dart +++ b/lib/features/profile/views/profile_page_mobile.dart @@ -1,8 +1,11 @@ import 'package:academia/database/database.dart'; import 'package:academia/features/features.dart'; +import 'package:academia/utils/router/router.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import 'package:icons_plus/icons_plus.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:sliver_tools/sliver_tools.dart'; @@ -65,7 +68,12 @@ class _ProfilePageMobileState extends State { icon: const Icon(Bootstrap.pencil), ), IconButton( - onPressed: () {}, + onPressed: () { + HapticFeedback.heavyImpact().then((val) { + if (!context.mounted) return; + GoRouter.of(context).pushNamed(AcademiaRouter.auth); + }); + }, icon: const Icon(Bootstrap.person_add), ) ], From 160ce2108403bf61326f653a6ed4122e9a87179a Mon Sep 17 00:00:00 2001 From: Erick Date: Mon, 23 Dec 2024 12:12:00 +0300 Subject: [PATCH 09/24] chore: essentials page ui design --- lib/features/essentials/essentials.dart | 1 + .../views/essentials_mobile_page.dart | 154 ++++++++++++++++++ .../essentials/views/essentials_page.dart | 19 +++ lib/features/home/views/layout.dart | 9 +- 4 files changed, 177 insertions(+), 6 deletions(-) create mode 100644 lib/features/essentials/essentials.dart create mode 100644 lib/features/essentials/views/essentials_mobile_page.dart create mode 100644 lib/features/essentials/views/essentials_page.dart diff --git a/lib/features/essentials/essentials.dart b/lib/features/essentials/essentials.dart new file mode 100644 index 0000000..8c408fb --- /dev/null +++ b/lib/features/essentials/essentials.dart @@ -0,0 +1 @@ +export 'views/essentials_page.dart'; diff --git a/lib/features/essentials/views/essentials_mobile_page.dart b/lib/features/essentials/views/essentials_mobile_page.dart new file mode 100644 index 0000000..191cf95 --- /dev/null +++ b/lib/features/essentials/views/essentials_mobile_page.dart @@ -0,0 +1,154 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:icons_plus/icons_plus.dart'; +import 'package:sliver_tools/sliver_tools.dart'; + +class EssentialsMobilePage extends StatefulWidget { + const EssentialsMobilePage({super.key}); + + @override + State createState() => _EssentialsMobilePageState(); +} + +class _EssentialsMobilePageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: CustomScrollView( + slivers: [ + SliverAppBar( + expandedHeight: 250, + flexibleSpace: FlexibleSpaceBar( + title: Text( + "Essentials", + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + fontFamily: GoogleFonts.libreBaskerville().fontFamily, + ), + ), + ), + ), + SliverPadding( + padding: const EdgeInsets.all(12), + sliver: MultiSliver( + children: const [ + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.bell), + title: const Text("Todos & Assigments"), + subtitle: Text("Keep track of your assignments and todos"), + ), + ), + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.clock), + title: const Text("Exam timetable"), + subtitle: Text("Psst.. Never miss an exam"), + ), + ), + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.filetype_pdf), + title: const Text("Past Revision Papers"), + subtitle: Text("You want them? You get them.."), + ), + ), + + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.file_ppt), + title: const Text("Ask Me"), + subtitle: Text("Boring notes? We'll help you revise"), + ), + ), + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12), + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.play), + title: const Text("Flash Cards"), + subtitle: Text( + "Curious if you really understood? Try our flashcards", + ), + ), + ), + + //Page Break for student performance + SizedBox(height: 18), + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.bell), + title: const Text("Student Audit"), + subtitle: Text("Keep track of your assignments and todos"), + ), + ), + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.activity), + title: const Text("GPA Calculator"), + subtitle: Text("Watch out for those grades!"), + ), + ), + + Card( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12), + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.play), + title: const Text("Flash Cards"), + subtitle: Text( + "Curious if you really understood? Try our flashcards", + ), + ), + ), + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/features/essentials/views/essentials_page.dart b/lib/features/essentials/views/essentials_page.dart new file mode 100644 index 0000000..51774cf --- /dev/null +++ b/lib/features/essentials/views/essentials_page.dart @@ -0,0 +1,19 @@ +import 'package:academia/features/essentials/views/essentials_mobile_page.dart'; +import 'package:academia/utils/responsive/responsive.dart'; +import 'package:flutter/material.dart'; + +class EssentialsPage extends StatelessWidget { + const EssentialsPage({super.key}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) => + constraints.maxWidth < ScreenDimension.mobileWidth + ? const EssentialsMobilePage() + : const Center( + child: Text("Essentials coming soon"), + ), + ); + } +} diff --git a/lib/features/home/views/layout.dart b/lib/features/home/views/layout.dart index 310ae91..3658369 100644 --- a/lib/features/home/views/layout.dart +++ b/lib/features/home/views/layout.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:academia/features/essentials/essentials.dart'; import 'package:academia/features/features.dart'; import 'package:academia/utils/responsive/responsive.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; @@ -84,9 +85,7 @@ class _LayoutState extends State { Center( child: Text("Social"), ), - Center( - child: Text("Statistics"), - ), + EssentialsPage(), ProfilePage() ], ) @@ -143,9 +142,7 @@ class _LayoutState extends State { Center( child: Text("Social"), ), - Center( - child: Text("Statistics"), - ), + EssentialsPage(), ProfilePage() ], ), From 88a56a8aca1dbfc0f0a6186c7ff78fd24a11b8b5 Mon Sep 17 00:00:00 2001 From: Erick Date: Tue, 24 Dec 2024 01:51:23 +0300 Subject: [PATCH 10/24] chore: added header background color --- lib/features/essentials/views/essentials_mobile_page.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/features/essentials/views/essentials_mobile_page.dart b/lib/features/essentials/views/essentials_mobile_page.dart index 191cf95..9bca1a5 100644 --- a/lib/features/essentials/views/essentials_mobile_page.dart +++ b/lib/features/essentials/views/essentials_mobile_page.dart @@ -18,6 +18,7 @@ class _EssentialsMobilePageState extends State { slivers: [ SliverAppBar( expandedHeight: 250, + backgroundColor: Theme.of(context).colorScheme.errorContainer, flexibleSpace: FlexibleSpaceBar( title: Text( "Essentials", From 66abdd5233c5a45eb1a5875e53186fdde3dbb160 Mon Sep 17 00:00:00 2001 From: Erick Date: Tue, 24 Dec 2024 02:03:51 +0300 Subject: [PATCH 11/24] chore: introduced courses to the application --- lib/features/courses/courses.dart | 1 + lib/features/courses/views/courses_page.dart | 18 ++++++++++ .../courses/views/courses_page_desktop.dart | 12 +++++++ .../courses/views/courses_page_mobile.dart | 35 +++++++++++++++++++ lib/features/features.dart | 1 + lib/features/home/views/layout.dart | 8 ++--- 6 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 lib/features/courses/courses.dart create mode 100644 lib/features/courses/views/courses_page.dart create mode 100644 lib/features/courses/views/courses_page_desktop.dart create mode 100644 lib/features/courses/views/courses_page_mobile.dart diff --git a/lib/features/courses/courses.dart b/lib/features/courses/courses.dart new file mode 100644 index 0000000..f9e167c --- /dev/null +++ b/lib/features/courses/courses.dart @@ -0,0 +1 @@ +export 'views/courses_page.dart'; diff --git a/lib/features/courses/views/courses_page.dart b/lib/features/courses/views/courses_page.dart new file mode 100644 index 0000000..29b9616 --- /dev/null +++ b/lib/features/courses/views/courses_page.dart @@ -0,0 +1,18 @@ +import 'package:academia/features/courses/views/courses_page_desktop.dart'; +import 'package:academia/features/courses/views/courses_page_mobile.dart'; +import 'package:academia/utils/utils.dart'; +import 'package:flutter/material.dart'; + +class CoursesPage extends StatelessWidget { + const CoursesPage({super.key}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) => + constraints.maxWidth < ScreenDimension.mobileWidth + ? const CoursesPageMobile() + : const CoursesPageDesktop(), + ); + } +} diff --git a/lib/features/courses/views/courses_page_desktop.dart b/lib/features/courses/views/courses_page_desktop.dart new file mode 100644 index 0000000..bb6c95f --- /dev/null +++ b/lib/features/courses/views/courses_page_desktop.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; + +class CoursesPageDesktop extends StatelessWidget { + const CoursesPageDesktop({super.key}); + + @override + Widget build(BuildContext context) { + return Center( + child: Text("Course page desktop"), + ); + } +} diff --git a/lib/features/courses/views/courses_page_mobile.dart b/lib/features/courses/views/courses_page_mobile.dart new file mode 100644 index 0000000..e7118c3 --- /dev/null +++ b/lib/features/courses/views/courses_page_mobile.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class CoursesPageMobile extends StatefulWidget { + const CoursesPageMobile({super.key}); + + @override + State createState() => _CoursesPageMobileState(); +} + +class _CoursesPageMobileState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: RefreshIndicator( + onRefresh: () async {}, + child: CustomScrollView( + slivers: [ + SliverAppBar( + expandedHeight: 250, + flexibleSpace: FlexibleSpaceBar( + title: Text( + "Courses", + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + fontFamily: GoogleFonts.libreBaskerville().fontFamily, + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/features.dart b/lib/features/features.dart index 6a4ff23..5c34560 100644 --- a/lib/features/features.dart +++ b/lib/features/features.dart @@ -2,3 +2,4 @@ export 'onboarding/onboarding.dart'; export 'auth/auth.dart'; export 'home/home.dart'; export 'profile/profile.dart'; +export 'courses/courses.dart'; diff --git a/lib/features/home/views/layout.dart b/lib/features/home/views/layout.dart index 3658369..859655e 100644 --- a/lib/features/home/views/layout.dart +++ b/lib/features/home/views/layout.dart @@ -79,9 +79,7 @@ class _LayoutState extends State { Center( child: Text("Statistics"), ), - Center( - child: Text("Courses"), - ), + CoursesPage(), Center( child: Text("Social"), ), @@ -136,9 +134,7 @@ class _LayoutState extends State { Center( child: Text("Statistics"), ), - Center( - child: Text("Courses"), - ), + CoursesPage(), Center( child: Text("Social"), ), From ddabd3d57c3ceffb16138eba54e468cd1701b616 Mon Sep 17 00:00:00 2001 From: Erick Date: Wed, 25 Dec 2024 08:34:10 +0300 Subject: [PATCH 12/24] chore: courses page ui --- lib/features/courses/views/courses_page_mobile.dart | 13 +++++++++++++ .../essentials/views/essentials_mobile_page.dart | 9 +++++++++ lib/utils/network/dio_client.dart | 4 ++-- pubspec.yaml | 1 + 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/features/courses/views/courses_page_mobile.dart b/lib/features/courses/views/courses_page_mobile.dart index e7118c3..8aca866 100644 --- a/lib/features/courses/views/courses_page_mobile.dart +++ b/lib/features/courses/views/courses_page_mobile.dart @@ -18,6 +18,9 @@ class _CoursesPageMobileState extends State { slivers: [ SliverAppBar( expandedHeight: 250, + pinned: true, + floating: true, + snap: true, flexibleSpace: FlexibleSpaceBar( title: Text( "Courses", @@ -27,6 +30,16 @@ class _CoursesPageMobileState extends State { ), ), ), + SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 12), + sliver: SliverList.separated( + itemBuilder: (context, index) => ListTile( + leading: CircleAvatar(), + title: Text("ACS 200"), + subtitle: Text("PLAB * 10:00 - 13:00 * Fredrick Ogore")), + separatorBuilder: (context, index) => SizedBox(), + ), + ), ], ), ), diff --git a/lib/features/essentials/views/essentials_mobile_page.dart b/lib/features/essentials/views/essentials_mobile_page.dart index 9bca1a5..b31056a 100644 --- a/lib/features/essentials/views/essentials_mobile_page.dart +++ b/lib/features/essentials/views/essentials_mobile_page.dart @@ -17,6 +17,9 @@ class _EssentialsMobilePageState extends State { body: CustomScrollView( slivers: [ SliverAppBar( + pinned: true, + snap: true, + floating: true, expandedHeight: 250, backgroundColor: Theme.of(context).colorScheme.errorContainer, flexibleSpace: FlexibleSpaceBar( @@ -27,6 +30,12 @@ class _EssentialsMobilePageState extends State { ), ), ), + actions: [ + IconButton( + onPressed: () {}, + icon: const Icon(Bootstrap.qr_code_scan), + ), + ], ), SliverPadding( padding: const EdgeInsets.all(12), diff --git a/lib/utils/network/dio_client.dart b/lib/utils/network/dio_client.dart index 96c191b..b8c39c6 100644 --- a/lib/utils/network/dio_client.dart +++ b/lib/utils/network/dio_client.dart @@ -4,8 +4,8 @@ import './auth_interceptor.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; class DioClient { - // static const String _baseUrl = "http://192.168.2.115:8000/v2"; - static const String _baseUrl = "http://127.0.0.1:8000/v2"; + static const String _baseUrl = "http://192.168.167.218:8000/v2"; + // static const String _baseUrl = "http://127.0.0.1:8000/v2"; DioClient() { dio.interceptors.add( diff --git a/pubspec.yaml b/pubspec.yaml index 606a5ba..7d55ba8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -64,6 +64,7 @@ dependencies: get_it: ^8.0.2 pretty_dio_logger: ^1.4.0 connectivity_plus: ^6.1.1 + flex_color_picker: ^3.6.0 dev_dependencies: flutter_test: From fc523f7ae37225263430e7f05eca0416f327bae9 Mon Sep 17 00:00:00 2001 From: Erick Date: Wed, 25 Dec 2024 08:43:06 +0300 Subject: [PATCH 13/24] fix: homescreen having option to go back to login screen # FIX - Popped all the previous routes --- lib/features/auth/views/login_page.dart | 9 ++++++--- .../auth/views/user_selection_page.dart | 20 ------------------- .../views/widgets/user_selection_tile.dart | 16 +++++++++------ 3 files changed, 16 insertions(+), 29 deletions(-) diff --git a/lib/features/auth/views/login_page.dart b/lib/features/auth/views/login_page.dart index 78eb3f8..299aa7f 100644 --- a/lib/features/auth/views/login_page.dart +++ b/lib/features/auth/views/login_page.dart @@ -1,6 +1,7 @@ import 'package:academia/database/database.dart'; import 'package:academia/features/auth/cubit/auth_cubit.dart'; import 'package:academia/features/auth/cubit/auth_states.dart'; +import 'package:academia/features/features.dart'; import 'package:academia/utils/router/router.dart'; import 'package:academia/utils/validator/validator.dart'; import 'package:flutter/material.dart'; @@ -179,9 +180,11 @@ class _LoginPageState extends State { ); }, (r) { HapticFeedback.heavyImpact(); - GoRouter.of(context).pushReplacementNamed( - AcademiaRouter.home, - ); + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => const Layout(), + ), + (Route route) => false); }); }, child: state is AuthLoadingState diff --git a/lib/features/auth/views/user_selection_page.dart b/lib/features/auth/views/user_selection_page.dart index d8a0713..6c6866e 100644 --- a/lib/features/auth/views/user_selection_page.dart +++ b/lib/features/auth/views/user_selection_page.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:icons_plus/icons_plus.dart'; -import 'package:skeletonizer/skeletonizer.dart'; import 'package:sliver_tools/sliver_tools.dart'; class UserSelectionPage extends StatefulWidget { @@ -20,25 +19,6 @@ class UserSelectionPage extends StatefulWidget { class _UserSelectionPageState extends State { late AuthCubit authCubit = BlocProvider.of(context); - /// Shows a dialog with [title] and [content] - void _showMessageDialog(String title, String content) { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text(title), - content: Text(content), - actions: [ - TextButton( - onPressed: () { - context.pop(); - }, - child: const Text("Ok"), - ), - ], - ), - ); - } - @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/features/auth/views/widgets/user_selection_tile.dart b/lib/features/auth/views/widgets/user_selection_tile.dart index a46abda..27ee103 100644 --- a/lib/features/auth/views/widgets/user_selection_tile.dart +++ b/lib/features/auth/views/widgets/user_selection_tile.dart @@ -1,6 +1,7 @@ import 'package:academia/database/database.dart'; import 'package:academia/features/auth/cubit/auth_cubit.dart'; import 'package:academia/features/auth/cubit/auth_states.dart'; +import 'package:academia/features/home/views/layout.dart'; import 'package:academia/utils/router/router.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; @@ -22,7 +23,7 @@ class UserSelectionTile extends StatefulWidget { } class _UserSelectionTileState extends State { - late AuthCubit _authCubit = BlocProvider.of(context); + late AuthCubit authCubit = BlocProvider.of(context); UserProfileData? profile; UserCredentialData? creds; @@ -46,11 +47,11 @@ class _UserSelectionTileState extends State { } Future _fetchUserProfile(UserData user) async { - return await _authCubit.fetchUserProfile(user); + return await authCubit.fetchUserProfile(user); } Future _fetchUserCredentials(UserData user) async { - final result = await _authCubit.fetchUserCredsFromCache(user); + final result = await authCubit.fetchUserCredsFromCache(user); return result.fold((l) { _showMessageDialog("Error", l); return null; @@ -73,15 +74,18 @@ class _UserSelectionTileState extends State { enabled: snapshot.connectionState != ConnectionState.done, child: ListTile( onTap: () async { - final result = await _authCubit.authenticate(creds!); + final result = await authCubit.authenticate(creds!); result.fold((l) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(l), )); }, (r) { if (!context.mounted) return; - GoRouter.of(context) - .pushReplacementNamed(AcademiaRouter.home); + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => const Layout(), + ), + (Route route) => false); }); }, title: Text("@${widget.user.username}"), From 08c3be6c8639742deb785d1fff17149045acaa1b Mon Sep 17 00:00:00 2001 From: Erick Date: Wed, 25 Dec 2024 18:48:01 +0300 Subject: [PATCH 14/24] chore: added courses models --- lib/database/database.dart | 8 +- lib/database/database.g.dart | 683 +++++++++++++++++++++- lib/features/courses/models/course.dart | 17 + lib/features/courses/models/semester.dart | 18 + lib/utils/network/dio_client.dart | 2 +- 5 files changed, 725 insertions(+), 3 deletions(-) create mode 100644 lib/features/courses/models/course.dart create mode 100644 lib/features/courses/models/semester.dart diff --git a/lib/database/database.dart b/lib/database/database.dart index a0c858c..a63eed1 100644 --- a/lib/database/database.dart +++ b/lib/database/database.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:academia/features/auth/models/user.dart'; import 'package:academia/features/auth/models/user_credentials.dart'; import 'package:academia/features/auth/models/user_profile.dart'; +import 'package:academia/features/courses/models/course.dart'; import 'package:drift/drift.dart'; import 'package:drift/native.dart'; import 'package:drift_flutter/drift_flutter.dart'; @@ -43,7 +44,12 @@ Future _getDatabaseDirectory() async { } } -@DriftDatabase(tables: [User, UserProfile, UserCredential]) +@DriftDatabase(tables: [ + User, + UserProfile, + UserCredential, + Course, +]) class AppDatabase extends _$AppDatabase { // After generating code, this class needs to define a schemaVersion getter // and a constructor telling drift where the database should be stored. diff --git a/lib/database/database.g.dart b/lib/database/database.g.dart index c566a91..7d38e02 100644 --- a/lib/database/database.g.dart +++ b/lib/database/database.g.dart @@ -1496,18 +1496,472 @@ class UserCredentialCompanion extends UpdateCompanion { } } +class $CourseTable extends Course with TableInfo<$CourseTable, CourseData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $CourseTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + static const VerificationMeta _unitMeta = const VerificationMeta('unit'); + @override + late final GeneratedColumn unit = GeneratedColumn( + 'unit', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE')); + static const VerificationMeta _sectionMeta = + const VerificationMeta('section'); + @override + late final GeneratedColumn section = GeneratedColumn( + 'section', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _weekDayMeta = + const VerificationMeta('weekDay'); + @override + late final GeneratedColumn weekDay = GeneratedColumn( + 'week_day', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _campusMeta = const VerificationMeta('campus'); + @override + late final GeneratedColumn campus = GeneratedColumn( + 'campus', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _roomMeta = const VerificationMeta('room'); + @override + late final GeneratedColumn room = GeneratedColumn( + 'room', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _lecturerMeta = + const VerificationMeta('lecturer'); + @override + late final GeneratedColumn lecturer = GeneratedColumn( + 'lecturer', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _periodMeta = const VerificationMeta('period'); + @override + late final GeneratedColumn period = GeneratedColumn( + 'period', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _colorMeta = const VerificationMeta('color'); + @override + late final GeneratedColumn color = GeneratedColumn( + 'color', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + @override + List get $columns => + [id, unit, section, weekDay, campus, room, lecturer, period, color]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'course'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('unit')) { + context.handle( + _unitMeta, unit.isAcceptableOrUnknown(data['unit']!, _unitMeta)); + } else if (isInserting) { + context.missing(_unitMeta); + } + if (data.containsKey('section')) { + context.handle(_sectionMeta, + section.isAcceptableOrUnknown(data['section']!, _sectionMeta)); + } else if (isInserting) { + context.missing(_sectionMeta); + } + if (data.containsKey('week_day')) { + context.handle(_weekDayMeta, + weekDay.isAcceptableOrUnknown(data['week_day']!, _weekDayMeta)); + } else if (isInserting) { + context.missing(_weekDayMeta); + } + if (data.containsKey('campus')) { + context.handle(_campusMeta, + campus.isAcceptableOrUnknown(data['campus']!, _campusMeta)); + } else if (isInserting) { + context.missing(_campusMeta); + } + if (data.containsKey('room')) { + context.handle( + _roomMeta, room.isAcceptableOrUnknown(data['room']!, _roomMeta)); + } else if (isInserting) { + context.missing(_roomMeta); + } + if (data.containsKey('lecturer')) { + context.handle(_lecturerMeta, + lecturer.isAcceptableOrUnknown(data['lecturer']!, _lecturerMeta)); + } else if (isInserting) { + context.missing(_lecturerMeta); + } + if (data.containsKey('period')) { + context.handle(_periodMeta, + period.isAcceptableOrUnknown(data['period']!, _periodMeta)); + } else if (isInserting) { + context.missing(_periodMeta); + } + if (data.containsKey('color')) { + context.handle( + _colorMeta, color.isAcceptableOrUnknown(data['color']!, _colorMeta)); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + CourseData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return CourseData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}id']), + unit: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}unit'])!, + section: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}section'])!, + weekDay: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}week_day'])!, + campus: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}campus'])!, + room: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}room'])!, + lecturer: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}lecturer'])!, + period: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}period'])!, + color: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}color']), + ); + } + + @override + $CourseTable createAlias(String alias) { + return $CourseTable(attachedDatabase, alias); + } +} + +class CourseData extends DataClass implements Insertable { + final String? id; + final String unit; + final String section; + final String weekDay; + final String campus; + final String room; + final String lecturer; + final String period; + final int? color; + const CourseData( + {this.id, + required this.unit, + required this.section, + required this.weekDay, + required this.campus, + required this.room, + required this.lecturer, + required this.period, + this.color}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || id != null) { + map['id'] = Variable(id); + } + map['unit'] = Variable(unit); + map['section'] = Variable(section); + map['week_day'] = Variable(weekDay); + map['campus'] = Variable(campus); + map['room'] = Variable(room); + map['lecturer'] = Variable(lecturer); + map['period'] = Variable(period); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + return map; + } + + CourseCompanion toCompanion(bool nullToAbsent) { + return CourseCompanion( + id: id == null && nullToAbsent ? const Value.absent() : Value(id), + unit: Value(unit), + section: Value(section), + weekDay: Value(weekDay), + campus: Value(campus), + room: Value(room), + lecturer: Value(lecturer), + period: Value(period), + color: + color == null && nullToAbsent ? const Value.absent() : Value(color), + ); + } + + factory CourseData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return CourseData( + id: serializer.fromJson(json['id']), + unit: serializer.fromJson(json['unit']), + section: serializer.fromJson(json['section']), + weekDay: serializer.fromJson(json['day_of_the_week']), + campus: serializer.fromJson(json['campus']), + room: serializer.fromJson(json['room']), + lecturer: serializer.fromJson(json['lecturer']), + period: serializer.fromJson(json['period']), + color: serializer.fromJson(json['color']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'unit': serializer.toJson(unit), + 'section': serializer.toJson(section), + 'day_of_the_week': serializer.toJson(weekDay), + 'campus': serializer.toJson(campus), + 'room': serializer.toJson(room), + 'lecturer': serializer.toJson(lecturer), + 'period': serializer.toJson(period), + 'color': serializer.toJson(color), + }; + } + + CourseData copyWith( + {Value id = const Value.absent(), + String? unit, + String? section, + String? weekDay, + String? campus, + String? room, + String? lecturer, + String? period, + Value color = const Value.absent()}) => + CourseData( + id: id.present ? id.value : this.id, + unit: unit ?? this.unit, + section: section ?? this.section, + weekDay: weekDay ?? this.weekDay, + campus: campus ?? this.campus, + room: room ?? this.room, + lecturer: lecturer ?? this.lecturer, + period: period ?? this.period, + color: color.present ? color.value : this.color, + ); + CourseData copyWithCompanion(CourseCompanion data) { + return CourseData( + id: data.id.present ? data.id.value : this.id, + unit: data.unit.present ? data.unit.value : this.unit, + section: data.section.present ? data.section.value : this.section, + weekDay: data.weekDay.present ? data.weekDay.value : this.weekDay, + campus: data.campus.present ? data.campus.value : this.campus, + room: data.room.present ? data.room.value : this.room, + lecturer: data.lecturer.present ? data.lecturer.value : this.lecturer, + period: data.period.present ? data.period.value : this.period, + color: data.color.present ? data.color.value : this.color, + ); + } + + @override + String toString() { + return (StringBuffer('CourseData(') + ..write('id: $id, ') + ..write('unit: $unit, ') + ..write('section: $section, ') + ..write('weekDay: $weekDay, ') + ..write('campus: $campus, ') + ..write('room: $room, ') + ..write('lecturer: $lecturer, ') + ..write('period: $period, ') + ..write('color: $color') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, unit, section, weekDay, campus, room, lecturer, period, color); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is CourseData && + other.id == this.id && + other.unit == this.unit && + other.section == this.section && + other.weekDay == this.weekDay && + other.campus == this.campus && + other.room == this.room && + other.lecturer == this.lecturer && + other.period == this.period && + other.color == this.color); +} + +class CourseCompanion extends UpdateCompanion { + final Value id; + final Value unit; + final Value section; + final Value weekDay; + final Value campus; + final Value room; + final Value lecturer; + final Value period; + final Value color; + final Value rowid; + const CourseCompanion({ + this.id = const Value.absent(), + this.unit = const Value.absent(), + this.section = const Value.absent(), + this.weekDay = const Value.absent(), + this.campus = const Value.absent(), + this.room = const Value.absent(), + this.lecturer = const Value.absent(), + this.period = const Value.absent(), + this.color = const Value.absent(), + this.rowid = const Value.absent(), + }); + CourseCompanion.insert({ + this.id = const Value.absent(), + required String unit, + required String section, + required String weekDay, + required String campus, + required String room, + required String lecturer, + required String period, + this.color = const Value.absent(), + this.rowid = const Value.absent(), + }) : unit = Value(unit), + section = Value(section), + weekDay = Value(weekDay), + campus = Value(campus), + room = Value(room), + lecturer = Value(lecturer), + period = Value(period); + static Insertable custom({ + Expression? id, + Expression? unit, + Expression? section, + Expression? weekDay, + Expression? campus, + Expression? room, + Expression? lecturer, + Expression? period, + Expression? color, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (unit != null) 'unit': unit, + if (section != null) 'section': section, + if (weekDay != null) 'week_day': weekDay, + if (campus != null) 'campus': campus, + if (room != null) 'room': room, + if (lecturer != null) 'lecturer': lecturer, + if (period != null) 'period': period, + if (color != null) 'color': color, + if (rowid != null) 'rowid': rowid, + }); + } + + CourseCompanion copyWith( + {Value? id, + Value? unit, + Value? section, + Value? weekDay, + Value? campus, + Value? room, + Value? lecturer, + Value? period, + Value? color, + Value? rowid}) { + return CourseCompanion( + id: id ?? this.id, + unit: unit ?? this.unit, + section: section ?? this.section, + weekDay: weekDay ?? this.weekDay, + campus: campus ?? this.campus, + room: room ?? this.room, + lecturer: lecturer ?? this.lecturer, + period: period ?? this.period, + color: color ?? this.color, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (unit.present) { + map['unit'] = Variable(unit.value); + } + if (section.present) { + map['section'] = Variable(section.value); + } + if (weekDay.present) { + map['week_day'] = Variable(weekDay.value); + } + if (campus.present) { + map['campus'] = Variable(campus.value); + } + if (room.present) { + map['room'] = Variable(room.value); + } + if (lecturer.present) { + map['lecturer'] = Variable(lecturer.value); + } + if (period.present) { + map['period'] = Variable(period.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('CourseCompanion(') + ..write('id: $id, ') + ..write('unit: $unit, ') + ..write('section: $section, ') + ..write('weekDay: $weekDay, ') + ..write('campus: $campus, ') + ..write('room: $room, ') + ..write('lecturer: $lecturer, ') + ..write('period: $period, ') + ..write('color: $color, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + abstract class _$AppDatabase extends GeneratedDatabase { _$AppDatabase(QueryExecutor e) : super(e); $AppDatabaseManager get managers => $AppDatabaseManager(this); late final $UserTable user = $UserTable(this); late final $UserProfileTable userProfile = $UserProfileTable(this); late final $UserCredentialTable userCredential = $UserCredentialTable(this); + late final $CourseTable course = $CourseTable(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => - [user, userProfile, userCredential]; + [user, userProfile, userCredential, course]; @override DriftDatabaseOptions get options => const DriftDatabaseOptions(storeDateTimeAsText: true); @@ -2657,6 +3111,231 @@ typedef $$UserCredentialTableProcessedTableManager = ProcessedTableManager< (UserCredentialData, $$UserCredentialTableReferences), UserCredentialData, PrefetchHooks Function({bool userId, bool username, bool email})>; +typedef $$CourseTableCreateCompanionBuilder = CourseCompanion Function({ + Value id, + required String unit, + required String section, + required String weekDay, + required String campus, + required String room, + required String lecturer, + required String period, + Value color, + Value rowid, +}); +typedef $$CourseTableUpdateCompanionBuilder = CourseCompanion Function({ + Value id, + Value unit, + Value section, + Value weekDay, + Value campus, + Value room, + Value lecturer, + Value period, + Value color, + Value rowid, +}); + +class $$CourseTableFilterComposer + extends Composer<_$AppDatabase, $CourseTable> { + $$CourseTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get id => $composableBuilder( + column: $table.id, builder: (column) => ColumnFilters(column)); + + ColumnFilters get unit => $composableBuilder( + column: $table.unit, builder: (column) => ColumnFilters(column)); + + ColumnFilters get section => $composableBuilder( + column: $table.section, builder: (column) => ColumnFilters(column)); + + ColumnFilters get weekDay => $composableBuilder( + column: $table.weekDay, builder: (column) => ColumnFilters(column)); + + ColumnFilters get campus => $composableBuilder( + column: $table.campus, builder: (column) => ColumnFilters(column)); + + ColumnFilters get room => $composableBuilder( + column: $table.room, builder: (column) => ColumnFilters(column)); + + ColumnFilters get lecturer => $composableBuilder( + column: $table.lecturer, builder: (column) => ColumnFilters(column)); + + ColumnFilters get period => $composableBuilder( + column: $table.period, builder: (column) => ColumnFilters(column)); + + ColumnFilters get color => $composableBuilder( + column: $table.color, builder: (column) => ColumnFilters(column)); +} + +class $$CourseTableOrderingComposer + extends Composer<_$AppDatabase, $CourseTable> { + $$CourseTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get unit => $composableBuilder( + column: $table.unit, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get section => $composableBuilder( + column: $table.section, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get weekDay => $composableBuilder( + column: $table.weekDay, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get campus => $composableBuilder( + column: $table.campus, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get room => $composableBuilder( + column: $table.room, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get lecturer => $composableBuilder( + column: $table.lecturer, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get period => $composableBuilder( + column: $table.period, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get color => $composableBuilder( + column: $table.color, builder: (column) => ColumnOrderings(column)); +} + +class $$CourseTableAnnotationComposer + extends Composer<_$AppDatabase, $CourseTable> { + $$CourseTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get unit => + $composableBuilder(column: $table.unit, builder: (column) => column); + + GeneratedColumn get section => + $composableBuilder(column: $table.section, builder: (column) => column); + + GeneratedColumn get weekDay => + $composableBuilder(column: $table.weekDay, builder: (column) => column); + + GeneratedColumn get campus => + $composableBuilder(column: $table.campus, builder: (column) => column); + + GeneratedColumn get room => + $composableBuilder(column: $table.room, builder: (column) => column); + + GeneratedColumn get lecturer => + $composableBuilder(column: $table.lecturer, builder: (column) => column); + + GeneratedColumn get period => + $composableBuilder(column: $table.period, builder: (column) => column); + + GeneratedColumn get color => + $composableBuilder(column: $table.color, builder: (column) => column); +} + +class $$CourseTableTableManager extends RootTableManager< + _$AppDatabase, + $CourseTable, + CourseData, + $$CourseTableFilterComposer, + $$CourseTableOrderingComposer, + $$CourseTableAnnotationComposer, + $$CourseTableCreateCompanionBuilder, + $$CourseTableUpdateCompanionBuilder, + (CourseData, BaseReferences<_$AppDatabase, $CourseTable, CourseData>), + CourseData, + PrefetchHooks Function()> { + $$CourseTableTableManager(_$AppDatabase db, $CourseTable table) + : super(TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$CourseTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$CourseTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$CourseTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + Value id = const Value.absent(), + Value unit = const Value.absent(), + Value section = const Value.absent(), + Value weekDay = const Value.absent(), + Value campus = const Value.absent(), + Value room = const Value.absent(), + Value lecturer = const Value.absent(), + Value period = const Value.absent(), + Value color = const Value.absent(), + Value rowid = const Value.absent(), + }) => + CourseCompanion( + id: id, + unit: unit, + section: section, + weekDay: weekDay, + campus: campus, + room: room, + lecturer: lecturer, + period: period, + color: color, + rowid: rowid, + ), + createCompanionCallback: ({ + Value id = const Value.absent(), + required String unit, + required String section, + required String weekDay, + required String campus, + required String room, + required String lecturer, + required String period, + Value color = const Value.absent(), + Value rowid = const Value.absent(), + }) => + CourseCompanion.insert( + id: id, + unit: unit, + section: section, + weekDay: weekDay, + campus: campus, + room: room, + lecturer: lecturer, + period: period, + color: color, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + )); +} + +typedef $$CourseTableProcessedTableManager = ProcessedTableManager< + _$AppDatabase, + $CourseTable, + CourseData, + $$CourseTableFilterComposer, + $$CourseTableOrderingComposer, + $$CourseTableAnnotationComposer, + $$CourseTableCreateCompanionBuilder, + $$CourseTableUpdateCompanionBuilder, + (CourseData, BaseReferences<_$AppDatabase, $CourseTable, CourseData>), + CourseData, + PrefetchHooks Function()>; class $AppDatabaseManager { final _$AppDatabase _db; @@ -2666,4 +3345,6 @@ class $AppDatabaseManager { $$UserProfileTableTableManager(_db, _db.userProfile); $$UserCredentialTableTableManager get userCredential => $$UserCredentialTableTableManager(_db, _db.userCredential); + $$CourseTableTableManager get course => + $$CourseTableTableManager(_db, _db.course); } diff --git a/lib/features/courses/models/course.dart b/lib/features/courses/models/course.dart new file mode 100644 index 0000000..76de6e0 --- /dev/null +++ b/lib/features/courses/models/course.dart @@ -0,0 +1,17 @@ +import 'package:drift/drift.dart'; + +class Course extends Table { + TextColumn get id => text().nullable()(); + TextColumn get unit => text().unique()(); + TextColumn get section => text()(); + @JsonKey("day_of_the_week") + TextColumn get weekDay => text()(); + TextColumn get campus => text()(); + TextColumn get room => text()(); + TextColumn get lecturer => text()(); + TextColumn get period => text()(); + IntColumn get color => integer().nullable()(); + + @override + Set>? get primaryKey => {id}; +} diff --git a/lib/features/courses/models/semester.dart b/lib/features/courses/models/semester.dart new file mode 100644 index 0000000..2e96da6 --- /dev/null +++ b/lib/features/courses/models/semester.dart @@ -0,0 +1,18 @@ +import 'package:drift/drift.dart'; + +class Semester extends Table { + TextColumn get id => text()(); + TextColumn get name => text()(); + TextColumn get description => text()(); + @JsonKey("begins_at") + DateTimeColumn get beginsAt => dateTime()(); + @JsonKey("ends_at") + DateTimeColumn get endsAt => dateTime()(); + @JsonKey("created_at") + DateTimeColumn get createdAt => dateTime()(); + @JsonKey("modified_at") + DateTimeColumn get modifiedAt => dateTime()(); + + @override + Set>? get primaryKey => {id}; +} diff --git a/lib/utils/network/dio_client.dart b/lib/utils/network/dio_client.dart index b8c39c6..1fb3318 100644 --- a/lib/utils/network/dio_client.dart +++ b/lib/utils/network/dio_client.dart @@ -4,7 +4,7 @@ import './auth_interceptor.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; class DioClient { - static const String _baseUrl = "http://192.168.167.218:8000/v2"; + static const String _baseUrl = "http://192.168.43.218:8000/v2"; // static const String _baseUrl = "http://127.0.0.1:8000/v2"; DioClient() { From 796629a3710fc9d13de7ec16e27cd9b3af62ef80 Mon Sep 17 00:00:00 2001 From: Erick Date: Thu, 26 Dec 2024 08:00:43 +0300 Subject: [PATCH 15/24] fix: profile page card padding # FIXES - Reduced padding size on the profile content cards - Removed dio content fetch - Auth screens now navigate the user without an option to o back --- .../auth/repository/user_repository.dart | 6 ++++-- lib/features/auth/views/login_page.dart | 11 ++++------- .../views/widgets/user_selection_tile.dart | 9 ++++----- .../profile/views/profile_page_mobile.dart | 18 +++++++++++++----- lib/utils/network/auth_interceptor.dart | 12 +++++++----- lib/utils/network/dio_client.dart | 4 ++-- 6 files changed, 34 insertions(+), 26 deletions(-) diff --git a/lib/features/auth/repository/user_repository.dart b/lib/features/auth/repository/user_repository.dart index df203b2..5e1d10c 100644 --- a/lib/features/auth/repository/user_repository.dart +++ b/lib/features/auth/repository/user_repository.dart @@ -53,7 +53,7 @@ final class UserRepository { // TODO: (erick) enable auth with magnet // authenticate with magnet - const magnetResult = Right(Object()); + const magnetResult = Right(Object()); // await (GetIt.instance.get(instanceName: "magnet").login()); // return magnetResult.fold((error) { @@ -88,7 +88,9 @@ final class UserRepository { if (localResult.isRight()) { final profile = (localResult as Right).value; if (profile == null) { - return await refreshUserProfile(user); + return left( + "Failed to fetch your profile from cache please connect to the internet and refresh", + ); } return right((localResult as Right).value); } diff --git a/lib/features/auth/views/login_page.dart b/lib/features/auth/views/login_page.dart index 299aa7f..1046520 100644 --- a/lib/features/auth/views/login_page.dart +++ b/lib/features/auth/views/login_page.dart @@ -1,6 +1,4 @@ import 'package:academia/database/database.dart'; -import 'package:academia/features/auth/cubit/auth_cubit.dart'; -import 'package:academia/features/auth/cubit/auth_states.dart'; import 'package:academia/features/features.dart'; import 'package:academia/utils/router/router.dart'; import 'package:academia/utils/validator/validator.dart'; @@ -180,11 +178,10 @@ class _LoginPageState extends State { ); }, (r) { HapticFeedback.heavyImpact(); - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (context) => const Layout(), - ), - (Route route) => false); + context.pop(); + GoRouter.of(context).pushReplacementNamed( + AcademiaRouter.home, + ); }); }, child: state is AuthLoadingState diff --git a/lib/features/auth/views/widgets/user_selection_tile.dart b/lib/features/auth/views/widgets/user_selection_tile.dart index 27ee103..a5f793d 100644 --- a/lib/features/auth/views/widgets/user_selection_tile.dart +++ b/lib/features/auth/views/widgets/user_selection_tile.dart @@ -81,11 +81,10 @@ class _UserSelectionTileState extends State { )); }, (r) { if (!context.mounted) return; - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (context) => const Layout(), - ), - (Route route) => false); + context.pop(); + GoRouter.of(context).pushReplacementNamed( + AcademiaRouter.home, + ); }); }, title: Text("@${widget.user.username}"), diff --git a/lib/features/profile/views/profile_page_mobile.dart b/lib/features/profile/views/profile_page_mobile.dart index 259489d..cf77b76 100644 --- a/lib/features/profile/views/profile_page_mobile.dart +++ b/lib/features/profile/views/profile_page_mobile.dart @@ -188,6 +188,7 @@ class _ProfilePageMobileState extends State { ), Card( elevation: 0, + margin: const EdgeInsets.only(bottom: 2), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(12), @@ -201,6 +202,7 @@ class _ProfilePageMobileState extends State { ), Card( elevation: 0, + margin: const EdgeInsets.only(bottom: 2), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.zero, @@ -214,6 +216,7 @@ class _ProfilePageMobileState extends State { ), Card( elevation: 0, + margin: const EdgeInsets.only(bottom: 2), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.zero, @@ -230,6 +233,7 @@ class _ProfilePageMobileState extends State { ), Card( elevation: 0, + margin: const EdgeInsets.only(bottom: 2), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.zero)), child: ListTile( @@ -240,6 +244,7 @@ class _ProfilePageMobileState extends State { ), Card( elevation: 0, + margin: const EdgeInsets.only(bottom: 2), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.zero)), child: ListTile( @@ -250,6 +255,7 @@ class _ProfilePageMobileState extends State { ), Card( elevation: 0, + margin: const EdgeInsets.only(bottom: 2), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.zero)), child: ListTile( @@ -260,6 +266,7 @@ class _ProfilePageMobileState extends State { ), Card( elevation: 0, + margin: const EdgeInsets.only(bottom: 2), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.zero)), child: ListTile( @@ -268,17 +275,18 @@ class _ProfilePageMobileState extends State { subtitle: Text(user.email ?? "unknown"), ), ), - const Card( + Card( + margin: EdgeInsets.zero, elevation: 0, - shape: RoundedRectangleBorder( + shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( bottom: Radius.circular(12), ), ), child: ListTile( - leading: Icon(Bootstrap.hash), - title: Text("Admission Number"), - subtitle: Text("21-1000"), + leading: const Icon(Bootstrap.house_heart), + title: const Text("Campus"), + subtitle: Text(profile.campus), ), ), ], diff --git a/lib/utils/network/auth_interceptor.dart b/lib/utils/network/auth_interceptor.dart index c143e47..b06dc49 100644 --- a/lib/utils/network/auth_interceptor.dart +++ b/lib/utils/network/auth_interceptor.dart @@ -35,11 +35,13 @@ class AuthInterceptor extends Interceptor { @override void onError(DioException err, ErrorInterceptorHandler handler) async { - // TODO: erick Add automatic token refreshing - if (err.response?.statusCode == 401) { - return handler - .resolve(await dio.fetch(err.requestOptions)); // Repeat the request. - } + // TODO: erick Add automatic token refreshing and code retrial + // if (err.response?.statusCode == 401) { + // print("Some really bad error"); + // + // return handler + // .resolve(await dio.fetch(err.requestOptions)); // Repeat the request. + // } return handler.reject(DioException( requestOptions: err.requestOptions, diff --git a/lib/utils/network/dio_client.dart b/lib/utils/network/dio_client.dart index 96c191b..1fb3318 100644 --- a/lib/utils/network/dio_client.dart +++ b/lib/utils/network/dio_client.dart @@ -4,8 +4,8 @@ import './auth_interceptor.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; class DioClient { - // static const String _baseUrl = "http://192.168.2.115:8000/v2"; - static const String _baseUrl = "http://127.0.0.1:8000/v2"; + static const String _baseUrl = "http://192.168.43.218:8000/v2"; + // static const String _baseUrl = "http://127.0.0.1:8000/v2"; DioClient() { dio.interceptors.add( From 7dc9f9f7c1585330e2a8964e4284ba2d8b0e0eec Mon Sep 17 00:00:00 2001 From: Erick Date: Thu, 26 Dec 2024 08:08:53 +0300 Subject: [PATCH 16/24] fix: reduced content padding on essentials page cards --- .../views/essentials_mobile_page.dart | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/lib/features/essentials/views/essentials_mobile_page.dart b/lib/features/essentials/views/essentials_mobile_page.dart index 9bca1a5..eb20658 100644 --- a/lib/features/essentials/views/essentials_mobile_page.dart +++ b/lib/features/essentials/views/essentials_mobile_page.dart @@ -34,67 +34,72 @@ class _EssentialsMobilePageState extends State { children: const [ Card( elevation: 0, - shape: const RoundedRectangleBorder( + margin: EdgeInsets.only(bottom: 2), + shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(12), ), ), child: ListTile( - leading: const Icon(Bootstrap.bell), - title: const Text("Todos & Assigments"), + leading: Icon(Bootstrap.bell), + title: Text("Todos & Assigments"), subtitle: Text("Keep track of your assignments and todos"), ), ), Card( elevation: 0, - shape: const RoundedRectangleBorder( + margin: EdgeInsets.only(bottom: 2), + shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.zero, ), ), child: ListTile( - leading: const Icon(Bootstrap.clock), - title: const Text("Exam timetable"), + leading: Icon(Bootstrap.clock), + title: Text("Exam timetable"), subtitle: Text("Psst.. Never miss an exam"), ), ), Card( elevation: 0, - shape: const RoundedRectangleBorder( + margin: EdgeInsets.only(bottom: 2), + shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.zero, ), ), child: ListTile( - leading: const Icon(Bootstrap.filetype_pdf), - title: const Text("Past Revision Papers"), + leading: Icon(Bootstrap.filetype_pdf), + title: Text("Past Revision Papers"), subtitle: Text("You want them? You get them.."), ), ), Card( elevation: 0, - shape: const RoundedRectangleBorder( + margin: EdgeInsets.only(bottom: 2), + shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( bottom: Radius.zero, ), ), child: ListTile( - leading: const Icon(Bootstrap.file_ppt), - title: const Text("Ask Me"), + leading: Icon(Bootstrap.file_ppt), + title: Text("Ask Me"), subtitle: Text("Boring notes? We'll help you revise"), ), ), Card( elevation: 0, - shape: const RoundedRectangleBorder( + margin: EdgeInsets.only(bottom: 2), + shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( bottom: Radius.circular(12), ), ), child: ListTile( - leading: const Icon(Bootstrap.play), - title: const Text("Flash Cards"), + leading: Icon(Bootstrap.play), + title: Text("Flash Cards"), subtitle: Text( "Curious if you really understood? Try our flashcards", ), @@ -104,42 +109,45 @@ class _EssentialsMobilePageState extends State { //Page Break for student performance SizedBox(height: 18), Card( + margin: EdgeInsets.only(bottom: 2), elevation: 0, - shape: const RoundedRectangleBorder( + shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(12), ), ), child: ListTile( - leading: const Icon(Bootstrap.bell), - title: const Text("Student Audit"), + leading: Icon(Bootstrap.bell), + title: Text("Student Audit"), subtitle: Text("Keep track of your assignments and todos"), ), ), Card( elevation: 0, - shape: const RoundedRectangleBorder( + margin: EdgeInsets.only(bottom: 2), + shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( bottom: Radius.zero, ), ), child: ListTile( - leading: const Icon(Bootstrap.activity), - title: const Text("GPA Calculator"), + leading: Icon(Bootstrap.activity), + title: Text("GPA Calculator"), subtitle: Text("Watch out for those grades!"), ), ), Card( elevation: 0, - shape: const RoundedRectangleBorder( + margin: EdgeInsets.only(bottom: 2), + shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( bottom: Radius.circular(12), ), ), child: ListTile( - leading: const Icon(Bootstrap.play), - title: const Text("Flash Cards"), + leading: Icon(Bootstrap.play), + title: Text("Flash Cards"), subtitle: Text( "Curious if you really understood? Try our flashcards", ), From 0d08636229c0d7cc2e7744ab177243a4656e1384 Mon Sep 17 00:00:00 2001 From: Erick Date: Thu, 26 Dec 2024 11:13:51 +0300 Subject: [PATCH 17/24] feat: got courses to display on the courses page --- lib/app.dart | 1 + lib/database/database.g.dart | 85 ++++++++++++++++--- .../repository/user_remote_repository.dart | 1 - .../auth/repository/user_repository.dart | 9 +- lib/features/courses/courses.dart | 2 + lib/features/courses/cubit/course_cubit.dart | 28 ++++++ lib/features/courses/cubit/course_state.dart | 37 ++++++++ lib/features/courses/models/course.dart | 5 +- .../repository/course_local_repository.dart | 71 ++++++++++++++++ .../repository/course_remote_repository.dart | 17 ++++ .../courses/repository/course_repository.dart | 35 ++++++++ .../courses/views/courses_page_mobile.dart | 73 +++++++++++++--- 12 files changed, 336 insertions(+), 28 deletions(-) create mode 100644 lib/features/courses/cubit/course_cubit.dart create mode 100644 lib/features/courses/cubit/course_state.dart create mode 100644 lib/features/courses/repository/course_local_repository.dart create mode 100644 lib/features/courses/repository/course_remote_repository.dart create mode 100644 lib/features/courses/repository/course_repository.dart diff --git a/lib/app.dart b/lib/app.dart index da48d7a..f270c3f 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -25,6 +25,7 @@ class Academia extends StatelessWidget { providers: [ BlocProvider(create: (_) => AuthCubit()), BlocProvider(create: (_) => ProfileCubit()), + BlocProvider(create: (_) => CourseCubit()), ], child: DynamicColorBuilder( builder: (lightscheme, darkscheme) => MaterialApp.router( diff --git a/lib/database/database.g.dart b/lib/database/database.g.dart index 7d38e02..092b45e 100644 --- a/lib/database/database.g.dart +++ b/lib/database/database.g.dart @@ -1510,9 +1510,7 @@ class $CourseTable extends Course with TableInfo<$CourseTable, CourseData> { @override late final GeneratedColumn unit = GeneratedColumn( 'unit', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE')); + type: DriftSqlType.string, requiredDuringInsert: true); static const VerificationMeta _sectionMeta = const VerificationMeta('section'); @override @@ -1551,9 +1549,27 @@ class $CourseTable extends Course with TableInfo<$CourseTable, CourseData> { late final GeneratedColumn color = GeneratedColumn( 'color', aliasedName, true, type: DriftSqlType.int, requiredDuringInsert: false); + static const VerificationMeta _createdAtMeta = + const VerificationMeta('createdAt'); @override - List get $columns => - [id, unit, section, weekDay, campus, room, lecturer, period, color]; + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', aliasedName, true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: Constant(DateTime.now())); + @override + List get $columns => [ + id, + unit, + section, + weekDay, + campus, + room, + lecturer, + period, + color, + createdAt + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1613,6 +1629,10 @@ class $CourseTable extends Course with TableInfo<$CourseTable, CourseData> { context.handle( _colorMeta, color.isAcceptableOrUnknown(data['color']!, _colorMeta)); } + if (data.containsKey('created_at')) { + context.handle(_createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + } return context; } @@ -1640,6 +1660,8 @@ class $CourseTable extends Course with TableInfo<$CourseTable, CourseData> { .read(DriftSqlType.string, data['${effectivePrefix}period'])!, color: attachedDatabase.typeMapping .read(DriftSqlType.int, data['${effectivePrefix}color']), + createdAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at']), ); } @@ -1659,6 +1681,7 @@ class CourseData extends DataClass implements Insertable { final String lecturer; final String period; final int? color; + final DateTime? createdAt; const CourseData( {this.id, required this.unit, @@ -1668,7 +1691,8 @@ class CourseData extends DataClass implements Insertable { required this.room, required this.lecturer, required this.period, - this.color}); + this.color, + this.createdAt}); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1685,6 +1709,9 @@ class CourseData extends DataClass implements Insertable { if (!nullToAbsent || color != null) { map['color'] = Variable(color); } + if (!nullToAbsent || createdAt != null) { + map['created_at'] = Variable(createdAt); + } return map; } @@ -1700,6 +1727,9 @@ class CourseData extends DataClass implements Insertable { period: Value(period), color: color == null && nullToAbsent ? const Value.absent() : Value(color), + createdAt: createdAt == null && nullToAbsent + ? const Value.absent() + : Value(createdAt), ); } @@ -1716,6 +1746,7 @@ class CourseData extends DataClass implements Insertable { lecturer: serializer.fromJson(json['lecturer']), period: serializer.fromJson(json['period']), color: serializer.fromJson(json['color']), + createdAt: serializer.fromJson(json['created_at']), ); } @override @@ -1731,6 +1762,7 @@ class CourseData extends DataClass implements Insertable { 'lecturer': serializer.toJson(lecturer), 'period': serializer.toJson(period), 'color': serializer.toJson(color), + 'created_at': serializer.toJson(createdAt), }; } @@ -1743,7 +1775,8 @@ class CourseData extends DataClass implements Insertable { String? room, String? lecturer, String? period, - Value color = const Value.absent()}) => + Value color = const Value.absent(), + Value createdAt = const Value.absent()}) => CourseData( id: id.present ? id.value : this.id, unit: unit ?? this.unit, @@ -1754,6 +1787,7 @@ class CourseData extends DataClass implements Insertable { lecturer: lecturer ?? this.lecturer, period: period ?? this.period, color: color.present ? color.value : this.color, + createdAt: createdAt.present ? createdAt.value : this.createdAt, ); CourseData copyWithCompanion(CourseCompanion data) { return CourseData( @@ -1766,6 +1800,7 @@ class CourseData extends DataClass implements Insertable { lecturer: data.lecturer.present ? data.lecturer.value : this.lecturer, period: data.period.present ? data.period.value : this.period, color: data.color.present ? data.color.value : this.color, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, ); } @@ -1780,14 +1815,15 @@ class CourseData extends DataClass implements Insertable { ..write('room: $room, ') ..write('lecturer: $lecturer, ') ..write('period: $period, ') - ..write('color: $color') + ..write('color: $color, ') + ..write('createdAt: $createdAt') ..write(')')) .toString(); } @override - int get hashCode => Object.hash( - id, unit, section, weekDay, campus, room, lecturer, period, color); + int get hashCode => Object.hash(id, unit, section, weekDay, campus, room, + lecturer, period, color, createdAt); @override bool operator ==(Object other) => identical(this, other) || @@ -1800,7 +1836,8 @@ class CourseData extends DataClass implements Insertable { other.room == this.room && other.lecturer == this.lecturer && other.period == this.period && - other.color == this.color); + other.color == this.color && + other.createdAt == this.createdAt); } class CourseCompanion extends UpdateCompanion { @@ -1813,6 +1850,7 @@ class CourseCompanion extends UpdateCompanion { final Value lecturer; final Value period; final Value color; + final Value createdAt; final Value rowid; const CourseCompanion({ this.id = const Value.absent(), @@ -1824,6 +1862,7 @@ class CourseCompanion extends UpdateCompanion { this.lecturer = const Value.absent(), this.period = const Value.absent(), this.color = const Value.absent(), + this.createdAt = const Value.absent(), this.rowid = const Value.absent(), }); CourseCompanion.insert({ @@ -1836,6 +1875,7 @@ class CourseCompanion extends UpdateCompanion { required String lecturer, required String period, this.color = const Value.absent(), + this.createdAt = const Value.absent(), this.rowid = const Value.absent(), }) : unit = Value(unit), section = Value(section), @@ -1854,6 +1894,7 @@ class CourseCompanion extends UpdateCompanion { Expression? lecturer, Expression? period, Expression? color, + Expression? createdAt, Expression? rowid, }) { return RawValuesInsertable({ @@ -1866,6 +1907,7 @@ class CourseCompanion extends UpdateCompanion { if (lecturer != null) 'lecturer': lecturer, if (period != null) 'period': period, if (color != null) 'color': color, + if (createdAt != null) 'created_at': createdAt, if (rowid != null) 'rowid': rowid, }); } @@ -1880,6 +1922,7 @@ class CourseCompanion extends UpdateCompanion { Value? lecturer, Value? period, Value? color, + Value? createdAt, Value? rowid}) { return CourseCompanion( id: id ?? this.id, @@ -1891,6 +1934,7 @@ class CourseCompanion extends UpdateCompanion { lecturer: lecturer ?? this.lecturer, period: period ?? this.period, color: color ?? this.color, + createdAt: createdAt ?? this.createdAt, rowid: rowid ?? this.rowid, ); } @@ -1925,6 +1969,9 @@ class CourseCompanion extends UpdateCompanion { if (color.present) { map['color'] = Variable(color.value); } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } if (rowid.present) { map['rowid'] = Variable(rowid.value); } @@ -1943,6 +1990,7 @@ class CourseCompanion extends UpdateCompanion { ..write('lecturer: $lecturer, ') ..write('period: $period, ') ..write('color: $color, ') + ..write('createdAt: $createdAt, ') ..write('rowid: $rowid') ..write(')')) .toString(); @@ -3121,6 +3169,7 @@ typedef $$CourseTableCreateCompanionBuilder = CourseCompanion Function({ required String lecturer, required String period, Value color, + Value createdAt, Value rowid, }); typedef $$CourseTableUpdateCompanionBuilder = CourseCompanion Function({ @@ -3133,6 +3182,7 @@ typedef $$CourseTableUpdateCompanionBuilder = CourseCompanion Function({ Value lecturer, Value period, Value color, + Value createdAt, Value rowid, }); @@ -3171,6 +3221,9 @@ class $$CourseTableFilterComposer ColumnFilters get color => $composableBuilder( column: $table.color, builder: (column) => ColumnFilters(column)); + + ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, builder: (column) => ColumnFilters(column)); } class $$CourseTableOrderingComposer @@ -3208,6 +3261,9 @@ class $$CourseTableOrderingComposer ColumnOrderings get color => $composableBuilder( column: $table.color, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, builder: (column) => ColumnOrderings(column)); } class $$CourseTableAnnotationComposer @@ -3245,6 +3301,9 @@ class $$CourseTableAnnotationComposer GeneratedColumn get color => $composableBuilder(column: $table.color, builder: (column) => column); + + GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); } class $$CourseTableTableManager extends RootTableManager< @@ -3279,6 +3338,7 @@ class $$CourseTableTableManager extends RootTableManager< Value lecturer = const Value.absent(), Value period = const Value.absent(), Value color = const Value.absent(), + Value createdAt = const Value.absent(), Value rowid = const Value.absent(), }) => CourseCompanion( @@ -3291,6 +3351,7 @@ class $$CourseTableTableManager extends RootTableManager< lecturer: lecturer, period: period, color: color, + createdAt: createdAt, rowid: rowid, ), createCompanionCallback: ({ @@ -3303,6 +3364,7 @@ class $$CourseTableTableManager extends RootTableManager< required String lecturer, required String period, Value color = const Value.absent(), + Value createdAt = const Value.absent(), Value rowid = const Value.absent(), }) => CourseCompanion.insert( @@ -3315,6 +3377,7 @@ class $$CourseTableTableManager extends RootTableManager< lecturer: lecturer, period: period, color: color, + createdAt: createdAt, rowid: rowid, ), withReferenceMapper: (p0) => p0 diff --git a/lib/features/auth/repository/user_remote_repository.dart b/lib/features/auth/repository/user_remote_repository.dart index 6b072c3..59a6821 100644 --- a/lib/features/auth/repository/user_remote_repository.dart +++ b/lib/features/auth/repository/user_remote_repository.dart @@ -45,7 +45,6 @@ final class UserRemoteRepository with DioErrorHandler { } on DioException catch (de) { return handleDioError(de); } catch (e) { - rethrow; return left("Something went terribly wrong please try that later"); } } diff --git a/lib/features/auth/repository/user_repository.dart b/lib/features/auth/repository/user_repository.dart index 5e1d10c..4598a17 100644 --- a/lib/features/auth/repository/user_repository.dart +++ b/lib/features/auth/repository/user_repository.dart @@ -46,16 +46,17 @@ final class UserRepository { Future> authenticateRemotely( UserCredentialData credentials) async { // Register a magnet singleton instance + + // TODO: (erick) enable auth with magnet // GetIt.instance.registerSingletonIfAbsent( // () => Magnet(credentials.admno, credentials.password), // instanceName: "magnet", // ); - // TODO: (erick) enable auth with magnet // authenticate with magnet - const magnetResult = Right(Object()); - // await (GetIt.instance.get(instanceName: "magnet").login()); - // + const magnetResult = + // await (GetIt.instance.get(instanceName: "magnet").login()); + Right(Object()); return magnetResult.fold((error) { return left(error.toString()); }, (session) async { diff --git a/lib/features/courses/courses.dart b/lib/features/courses/courses.dart index f9e167c..08a758e 100644 --- a/lib/features/courses/courses.dart +++ b/lib/features/courses/courses.dart @@ -1 +1,3 @@ export 'views/courses_page.dart'; +export 'cubit/course_cubit.dart'; +export 'cubit/course_state.dart'; diff --git a/lib/features/courses/cubit/course_cubit.dart b/lib/features/courses/cubit/course_cubit.dart new file mode 100644 index 0000000..9d0c84e --- /dev/null +++ b/lib/features/courses/cubit/course_cubit.dart @@ -0,0 +1,28 @@ +import 'package:academia/features/courses/cubit/course_state.dart'; +import 'package:academia/features/courses/repository/course_repository.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class CourseCubit extends Cubit { + final CourseRepository _courseRepository = CourseRepository(); + CourseCubit() : super(CourseStateInitial()) { + fetchCachedCourses().then((value) {}); + } + + Future syncCourses() async { + final res = await _courseRepository.syncCoursesWithMagnet(); + if (res.isLeft()) { + emit(CourseStateError(error: (res as Left).value)); + } + emit(CourseStateLoaded(courses: (res as Right).value)); + } + + Future fetchCachedCourses() async { + final result = await _courseRepository.fetchAllCachedCourses(); + result.fold((error) { + emit(CourseStateError(error: error)); + }, (courses) { + emit(CourseStateLoaded(courses: courses)); + }); + } +} diff --git a/lib/features/courses/cubit/course_state.dart b/lib/features/courses/cubit/course_state.dart new file mode 100644 index 0000000..9251005 --- /dev/null +++ b/lib/features/courses/cubit/course_state.dart @@ -0,0 +1,37 @@ +import 'package:academia/database/database.dart'; + +/// The base course state +/// note that the [busy] flag will be inherited by all +/// states to indicate that the state is busy and might +/// change +abstract class CourseState { + CourseState({this.busy = false}); + bool busy; +} + +/// The [CourseStateInitial] represents the first state +/// of courses +final class CourseStateInitial extends CourseState {} + +/// The [CourseStateLoading] indicates that the courses +/// are loading either from remote or cache and they're +/// bound to change +final class CourseStateLoading extends CourseState {} + +/// [CourseStateLoaded] indicates that the courses are loaded +/// The [courses] member contains a list of all courses +final class CourseStateLoaded extends CourseState { + final List courses; + CourseStateLoaded({required this.courses}); +} + +/// The [CourseStateError] represents the courses error state +/// It should be emitted if for example fetching was an error +/// or parsing the course data was an error +/// +/// The [error] member contains a [String] message of +/// what went wrong +final class CourseStateError extends CourseState { + final String error; + CourseStateError({required this.error}); +} diff --git a/lib/features/courses/models/course.dart b/lib/features/courses/models/course.dart index 76de6e0..2884d2e 100644 --- a/lib/features/courses/models/course.dart +++ b/lib/features/courses/models/course.dart @@ -2,7 +2,7 @@ import 'package:drift/drift.dart'; class Course extends Table { TextColumn get id => text().nullable()(); - TextColumn get unit => text().unique()(); + TextColumn get unit => text()(); TextColumn get section => text()(); @JsonKey("day_of_the_week") TextColumn get weekDay => text()(); @@ -11,6 +11,9 @@ class Course extends Table { TextColumn get lecturer => text()(); TextColumn get period => text()(); IntColumn get color => integer().nullable()(); + @JsonKey("created_at") + DateTimeColumn get createdAt => + dateTime().nullable().withDefault(Constant(DateTime.now()))(); @override Set>? get primaryKey => {id}; diff --git a/lib/features/courses/repository/course_local_repository.dart b/lib/features/courses/repository/course_local_repository.dart new file mode 100644 index 0000000..8d774d7 --- /dev/null +++ b/lib/features/courses/repository/course_local_repository.dart @@ -0,0 +1,71 @@ +import 'package:academia/database/database.dart'; +import 'package:dartz/dartz.dart'; +import 'package:drift/drift.dart'; +import 'package:get_it/get_it.dart'; + +/// [CourseLocalRepository] +/// A Helper class to manipulate course related information +/// on the device local storage +final class CourseLocalRepository { + // the db's instance + final AppDatabase _localDb = GetIt.instance.get(instanceName: "cacheDB"); + + /// Fetches all cached courses + /// Incase of an error a message of type [String] is returned + /// On success, a [List] of [CourseData] is returned + Future>> fetchAllCachedCourses() async { + try { + final users = await (_localDb.course.select() + ..orderBy([ + (c) => OrderingTerm( + expression: c.createdAt, + mode: OrderingMode.desc, + ), + ])) + .get(); + return right(users); + } catch (e) { + return left("Failed to retrieve users with message ${e.toString()}"); + } + } + + /// Adds a [CourseData] specified by [course] to [_localDb] cache + /// This method can also be used to update courses since it also updates the + /// information on conflict + Future> addCourseToCache(CourseData course) async { + try { + final ok = await _localDb.into(_localDb.course).insertOnConflictUpdate( + course.toCompanion(true), + ); + if (ok != 0) { + return right(true); + } + return left( + "The specified course data was not inserted since it exists and conflicted", + ); + } catch (e) { + return left( + "Failed to append course to cache with error description ${e.toString()}", + ); + } + } + + /// Delete the [CourseData] specified by [course] from local cache + /// It wil return an instance of [String] describing the error that it might have + /// encountered or a boolean [true] incase it was a success + Future> deleteCourseFromCache(CourseData course) async { + try { + final ok = await _localDb.delete(_localDb.course).delete(course); + if (ok != 0) { + return right(true); + } + return left( + "The specified course was not deleted because it does not exist", + ); + } catch (e) { + return left( + "Failed to delete course from cache with error description ${e.toString()}", + ); + } + } +} diff --git a/lib/features/courses/repository/course_remote_repository.dart b/lib/features/courses/repository/course_remote_repository.dart new file mode 100644 index 0000000..67d8379 --- /dev/null +++ b/lib/features/courses/repository/course_remote_repository.dart @@ -0,0 +1,17 @@ +import 'package:academia/database/database.dart'; +import 'package:dartz/dartz.dart'; +import 'package:get_it/get_it.dart'; +import 'package:magnet/magnet.dart'; + +final class CourseRemoteRepository { + /// Fetches courses from magnet. + Future>> fetchCoursesFromMagnet() async { + final magnetInstance = GetIt.instance.get(instanceName: "magnet"); + final magnetResult = await magnetInstance.fetchUserTimeTable(); + return magnetResult.fold((error) { + return left(error.toString()); + }, (courses) { + return right(courses.map((c) => CourseData.fromJson(c)).toList()); + }); + } +} diff --git a/lib/features/courses/repository/course_repository.dart b/lib/features/courses/repository/course_repository.dart new file mode 100644 index 0000000..38da255 --- /dev/null +++ b/lib/features/courses/repository/course_repository.dart @@ -0,0 +1,35 @@ +import 'package:academia/database/database.dart'; +import 'package:dartz/dartz.dart'; + +import 'course_local_repository.dart'; +import 'course_remote_repository.dart'; + +final class CourseRepository { + final CourseLocalRepository _localRepository = CourseLocalRepository(); + final CourseRemoteRepository _courseRemoteRepository = + CourseRemoteRepository(); + + /// Fetches all cached courses + /// Incase of an error a message of type [String] is returned + /// On success, a [List] of [CourseData] is returned + Future>> fetchAllCachedCourses() async { + return await _localRepository.fetchAllCachedCourses(); + } + + Future>> syncCoursesWithMagnet() async { + final result = await _courseRemoteRepository.fetchCoursesFromMagnet(); + + return result.fold((error) { + return left(error); + }, (courses) async { + // Cache them to local db + for (final course in courses) { + final res = await _localRepository.addCourseToCache(course); + if (res.isLeft()) { + return left((res as Left).value); + } + } + return right(courses); + }); + } +} diff --git a/lib/features/courses/views/courses_page_mobile.dart b/lib/features/courses/views/courses_page_mobile.dart index 8aca866..b0b6781 100644 --- a/lib/features/courses/views/courses_page_mobile.dart +++ b/lib/features/courses/views/courses_page_mobile.dart @@ -1,5 +1,8 @@ +import 'package:academia/features/features.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:skeletonizer/skeletonizer.dart'; class CoursesPageMobile extends StatefulWidget { const CoursesPageMobile({super.key}); @@ -9,11 +12,20 @@ class CoursesPageMobile extends StatefulWidget { } class _CoursesPageMobileState extends State { + late CourseCubit courseCubit = BlocProvider.of(context); + + @override + void initState() { + super.initState(); + } + @override Widget build(BuildContext context) { return Scaffold( body: RefreshIndicator( - onRefresh: () async {}, + onRefresh: () async { + await courseCubit.syncCourses(); + }, child: CustomScrollView( slivers: [ SliverAppBar( @@ -30,16 +42,55 @@ class _CoursesPageMobileState extends State { ), ), ), - SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 12), - sliver: SliverList.separated( - itemBuilder: (context, index) => ListTile( - leading: CircleAvatar(), - title: Text("ACS 200"), - subtitle: Text("PLAB * 10:00 - 13:00 * Fredrick Ogore")), - separatorBuilder: (context, index) => SizedBox(), - ), - ), + BlocBuilder(builder: (context, state) { + if (state is CourseStateLoaded) { + if (state.courses.isNotEmpty) { + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 12), + sliver: SliverList.separated( + itemBuilder: (context, index) { + final course = state.courses[index]; + return ListTile( + leading: const CircleAvatar(), + title: Text("${course.unit} ${course.section}"), + subtitle: Text( + "${course.room} * ${course.period} * ${course.lecturer}", + ), + ); + }, + separatorBuilder: (context, index) => const SizedBox(), + itemCount: state.courses.length, + ), + ); + } + + return const SliverFillRemaining( + // TODO: erick add an illustration or animation + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text("You have no courses yet, please pull to refresh"), + ], + ), + ); + } + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 12), + sliver: SliverList.separated( + itemCount: 8, + itemBuilder: (context, index) => const Skeletonizer( + enabled: true, + child: ListTile( + leading: CircleAvatar(), + title: Text("Some Couse"), + subtitle: Text("PLAB * 10:00 - 13:00 * Awesome Lecturer"), + ), + ), + separatorBuilder: (context, index) => const SizedBox(), + ), + ); + }), ], ), ), From c9736cb738d9c35f3d2c6064d0fb0e5c51649975 Mon Sep 17 00:00:00 2001 From: Erick Date: Thu, 26 Dec 2024 11:52:30 +0300 Subject: [PATCH 18/24] fix: courses page stuck on loading state on app restart --- lib/database/database.g.dart | 307 ++++++++++++++---- .../auth/repository/user_repository.dart | 14 +- lib/features/courses/cubit/course_cubit.dart | 15 +- lib/features/courses/models/course.dart | 5 +- .../repository/course_local_repository.dart | 4 +- .../courses/repository/course_repository.dart | 13 +- .../courses/views/courses_page_mobile.dart | 8 +- 7 files changed, 279 insertions(+), 87 deletions(-) diff --git a/lib/database/database.g.dart b/lib/database/database.g.dart index 092b45e..dd699cb 100644 --- a/lib/database/database.g.dart +++ b/lib/database/database.g.dart @@ -1501,16 +1501,19 @@ class $CourseTable extends Course with TableInfo<$CourseTable, CourseData> { final GeneratedDatabase attachedDatabase; final String? _alias; $CourseTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); static const VerificationMeta _unitMeta = const VerificationMeta('unit'); @override late final GeneratedColumn unit = GeneratedColumn( 'unit', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _userMeta = const VerificationMeta('user'); + @override + late final GeneratedColumn user = GeneratedColumn( + 'user', aliasedName, true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('REFERENCES user (id)')); static const VerificationMeta _sectionMeta = const VerificationMeta('section'); @override @@ -1559,8 +1562,8 @@ class $CourseTable extends Course with TableInfo<$CourseTable, CourseData> { defaultValue: Constant(DateTime.now())); @override List get $columns => [ - id, unit, + user, section, weekDay, campus, @@ -1580,15 +1583,16 @@ class $CourseTable extends Course with TableInfo<$CourseTable, CourseData> { {bool isInserting = false}) { final context = VerificationContext(); final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } if (data.containsKey('unit')) { context.handle( _unitMeta, unit.isAcceptableOrUnknown(data['unit']!, _unitMeta)); } else if (isInserting) { context.missing(_unitMeta); } + if (data.containsKey('user')) { + context.handle( + _userMeta, user.isAcceptableOrUnknown(data['user']!, _userMeta)); + } if (data.containsKey('section')) { context.handle(_sectionMeta, section.isAcceptableOrUnknown(data['section']!, _sectionMeta)); @@ -1637,15 +1641,15 @@ class $CourseTable extends Course with TableInfo<$CourseTable, CourseData> { } @override - Set get $primaryKey => {id}; + Set get $primaryKey => {unit}; @override CourseData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return CourseData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id']), unit: attachedDatabase.typeMapping .read(DriftSqlType.string, data['${effectivePrefix}unit'])!, + user: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}user']), section: attachedDatabase.typeMapping .read(DriftSqlType.string, data['${effectivePrefix}section'])!, weekDay: attachedDatabase.typeMapping @@ -1672,8 +1676,8 @@ class $CourseTable extends Course with TableInfo<$CourseTable, CourseData> { } class CourseData extends DataClass implements Insertable { - final String? id; final String unit; + final String? user; final String section; final String weekDay; final String campus; @@ -1683,8 +1687,8 @@ class CourseData extends DataClass implements Insertable { final int? color; final DateTime? createdAt; const CourseData( - {this.id, - required this.unit, + {required this.unit, + this.user, required this.section, required this.weekDay, required this.campus, @@ -1696,10 +1700,10 @@ class CourseData extends DataClass implements Insertable { @override Map toColumns(bool nullToAbsent) { final map = {}; - if (!nullToAbsent || id != null) { - map['id'] = Variable(id); - } map['unit'] = Variable(unit); + if (!nullToAbsent || user != null) { + map['user'] = Variable(user); + } map['section'] = Variable(section); map['week_day'] = Variable(weekDay); map['campus'] = Variable(campus); @@ -1717,8 +1721,8 @@ class CourseData extends DataClass implements Insertable { CourseCompanion toCompanion(bool nullToAbsent) { return CourseCompanion( - id: id == null && nullToAbsent ? const Value.absent() : Value(id), unit: Value(unit), + user: user == null && nullToAbsent ? const Value.absent() : Value(user), section: Value(section), weekDay: Value(weekDay), campus: Value(campus), @@ -1737,8 +1741,8 @@ class CourseData extends DataClass implements Insertable { {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return CourseData( - id: serializer.fromJson(json['id']), unit: serializer.fromJson(json['unit']), + user: serializer.fromJson(json['user']), section: serializer.fromJson(json['section']), weekDay: serializer.fromJson(json['day_of_the_week']), campus: serializer.fromJson(json['campus']), @@ -1753,8 +1757,8 @@ class CourseData extends DataClass implements Insertable { Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { - 'id': serializer.toJson(id), 'unit': serializer.toJson(unit), + 'user': serializer.toJson(user), 'section': serializer.toJson(section), 'day_of_the_week': serializer.toJson(weekDay), 'campus': serializer.toJson(campus), @@ -1767,8 +1771,8 @@ class CourseData extends DataClass implements Insertable { } CourseData copyWith( - {Value id = const Value.absent(), - String? unit, + {String? unit, + Value user = const Value.absent(), String? section, String? weekDay, String? campus, @@ -1778,8 +1782,8 @@ class CourseData extends DataClass implements Insertable { Value color = const Value.absent(), Value createdAt = const Value.absent()}) => CourseData( - id: id.present ? id.value : this.id, unit: unit ?? this.unit, + user: user.present ? user.value : this.user, section: section ?? this.section, weekDay: weekDay ?? this.weekDay, campus: campus ?? this.campus, @@ -1791,8 +1795,8 @@ class CourseData extends DataClass implements Insertable { ); CourseData copyWithCompanion(CourseCompanion data) { return CourseData( - id: data.id.present ? data.id.value : this.id, unit: data.unit.present ? data.unit.value : this.unit, + user: data.user.present ? data.user.value : this.user, section: data.section.present ? data.section.value : this.section, weekDay: data.weekDay.present ? data.weekDay.value : this.weekDay, campus: data.campus.present ? data.campus.value : this.campus, @@ -1807,8 +1811,8 @@ class CourseData extends DataClass implements Insertable { @override String toString() { return (StringBuffer('CourseData(') - ..write('id: $id, ') ..write('unit: $unit, ') + ..write('user: $user, ') ..write('section: $section, ') ..write('weekDay: $weekDay, ') ..write('campus: $campus, ') @@ -1822,14 +1826,14 @@ class CourseData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, unit, section, weekDay, campus, room, + int get hashCode => Object.hash(unit, user, section, weekDay, campus, room, lecturer, period, color, createdAt); @override bool operator ==(Object other) => identical(this, other) || (other is CourseData && - other.id == this.id && other.unit == this.unit && + other.user == this.user && other.section == this.section && other.weekDay == this.weekDay && other.campus == this.campus && @@ -1841,8 +1845,8 @@ class CourseData extends DataClass implements Insertable { } class CourseCompanion extends UpdateCompanion { - final Value id; final Value unit; + final Value user; final Value section; final Value weekDay; final Value campus; @@ -1853,8 +1857,8 @@ class CourseCompanion extends UpdateCompanion { final Value createdAt; final Value rowid; const CourseCompanion({ - this.id = const Value.absent(), this.unit = const Value.absent(), + this.user = const Value.absent(), this.section = const Value.absent(), this.weekDay = const Value.absent(), this.campus = const Value.absent(), @@ -1866,8 +1870,8 @@ class CourseCompanion extends UpdateCompanion { this.rowid = const Value.absent(), }); CourseCompanion.insert({ - this.id = const Value.absent(), required String unit, + this.user = const Value.absent(), required String section, required String weekDay, required String campus, @@ -1885,8 +1889,8 @@ class CourseCompanion extends UpdateCompanion { lecturer = Value(lecturer), period = Value(period); static Insertable custom({ - Expression? id, Expression? unit, + Expression? user, Expression? section, Expression? weekDay, Expression? campus, @@ -1898,8 +1902,8 @@ class CourseCompanion extends UpdateCompanion { Expression? rowid, }) { return RawValuesInsertable({ - if (id != null) 'id': id, if (unit != null) 'unit': unit, + if (user != null) 'user': user, if (section != null) 'section': section, if (weekDay != null) 'week_day': weekDay, if (campus != null) 'campus': campus, @@ -1913,8 +1917,8 @@ class CourseCompanion extends UpdateCompanion { } CourseCompanion copyWith( - {Value? id, - Value? unit, + {Value? unit, + Value? user, Value? section, Value? weekDay, Value? campus, @@ -1925,8 +1929,8 @@ class CourseCompanion extends UpdateCompanion { Value? createdAt, Value? rowid}) { return CourseCompanion( - id: id ?? this.id, unit: unit ?? this.unit, + user: user ?? this.user, section: section ?? this.section, weekDay: weekDay ?? this.weekDay, campus: campus ?? this.campus, @@ -1942,12 +1946,12 @@ class CourseCompanion extends UpdateCompanion { @override Map toColumns(bool nullToAbsent) { final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } if (unit.present) { map['unit'] = Variable(unit.value); } + if (user.present) { + map['user'] = Variable(user.value); + } if (section.present) { map['section'] = Variable(section.value); } @@ -1981,8 +1985,8 @@ class CourseCompanion extends UpdateCompanion { @override String toString() { return (StringBuffer('CourseCompanion(') - ..write('id: $id, ') ..write('unit: $unit, ') + ..write('user: $user, ') ..write('section: $section, ') ..write('weekDay: $weekDay, ') ..write('campus: $campus, ') @@ -2061,6 +2065,20 @@ final class $$UserTableReferences return ProcessedTableManager( manager.$state.copyWith(prefetchedData: cache)); } + + static MultiTypedResultKey<$CourseTable, List> _courseRefsTable( + _$AppDatabase db) => + MultiTypedResultKey.fromTable(db.course, + aliasName: $_aliasNameGenerator(db.user.id, db.course.user)); + + $$CourseTableProcessedTableManager get courseRefs { + final manager = $$CourseTableTableManager($_db, $_db.course) + .filter((f) => f.user.id($_item.id)); + + final cache = $_typedResult.readTableOrNull(_courseRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache)); + } } class $$UserTableFilterComposer extends Composer<_$AppDatabase, $UserTable> { @@ -2124,6 +2142,27 @@ class $$UserTableFilterComposer extends Composer<_$AppDatabase, $UserTable> { )); return f(composer); } + + Expression courseRefs( + Expression Function($$CourseTableFilterComposer f) f) { + final $$CourseTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.course, + getReferencedColumn: (t) => t.user, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$CourseTableFilterComposer( + $db: $db, + $table: $db.course, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } } class $$UserTableOrderingComposer extends Composer<_$AppDatabase, $UserTable> { @@ -2230,6 +2269,27 @@ class $$UserTableAnnotationComposer )); return f(composer); } + + Expression courseRefs( + Expression Function($$CourseTableAnnotationComposer a) f) { + final $$CourseTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.course, + getReferencedColumn: (t) => t.user, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$CourseTableAnnotationComposer( + $db: $db, + $table: $db.course, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } } class $$UserTableTableManager extends RootTableManager< @@ -2243,7 +2303,7 @@ class $$UserTableTableManager extends RootTableManager< $$UserTableUpdateCompanionBuilder, (UserData, $$UserTableReferences), UserData, - PrefetchHooks Function({bool userProfileRefs})> { + PrefetchHooks Function({bool userProfileRefs, bool courseRefs})> { $$UserTableTableManager(_$AppDatabase db, $UserTable table) : super(TableManagerState( db: db, @@ -2314,10 +2374,14 @@ class $$UserTableTableManager extends RootTableManager< .map((e) => (e.readTable(table), $$UserTableReferences(db, table, e))) .toList(), - prefetchHooksCallback: ({userProfileRefs = false}) { + prefetchHooksCallback: ( + {userProfileRefs = false, courseRefs = false}) { return PrefetchHooks( db: db, - explicitlyWatchedTables: [if (userProfileRefs) db.userProfile], + explicitlyWatchedTables: [ + if (userProfileRefs) db.userProfile, + if (courseRefs) db.course + ], addJoins: null, getPrefetchedDataCallback: (items) async { return [ @@ -2332,6 +2396,17 @@ class $$UserTableTableManager extends RootTableManager< referencedItemsForCurrentItem: (item, referencedItems) => referencedItems.where((e) => e.userId == item.id), + typedResults: items), + if (courseRefs) + await $_getPrefetchedData( + currentTable: table, + referencedTable: + $$UserTableReferences._courseRefsTable(db), + managerFromTypedResult: (p0) => + $$UserTableReferences(db, table, p0).courseRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => + referencedItems.where((e) => e.user == item.id), typedResults: items) ]; }, @@ -2351,7 +2426,7 @@ typedef $$UserTableProcessedTableManager = ProcessedTableManager< $$UserTableUpdateCompanionBuilder, (UserData, $$UserTableReferences), UserData, - PrefetchHooks Function({bool userProfileRefs})>; + PrefetchHooks Function({bool userProfileRefs, bool courseRefs})>; typedef $$UserProfileTableCreateCompanionBuilder = UserProfileCompanion Function({ required String userId, @@ -3160,8 +3235,8 @@ typedef $$UserCredentialTableProcessedTableManager = ProcessedTableManager< UserCredentialData, PrefetchHooks Function({bool userId, bool username, bool email})>; typedef $$CourseTableCreateCompanionBuilder = CourseCompanion Function({ - Value id, required String unit, + Value user, required String section, required String weekDay, required String campus, @@ -3173,8 +3248,8 @@ typedef $$CourseTableCreateCompanionBuilder = CourseCompanion Function({ Value rowid, }); typedef $$CourseTableUpdateCompanionBuilder = CourseCompanion Function({ - Value id, Value unit, + Value user, Value section, Value weekDay, Value campus, @@ -3186,6 +3261,24 @@ typedef $$CourseTableUpdateCompanionBuilder = CourseCompanion Function({ Value rowid, }); +final class $$CourseTableReferences + extends BaseReferences<_$AppDatabase, $CourseTable, CourseData> { + $$CourseTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static $UserTable _userTable(_$AppDatabase db) => + db.user.createAlias($_aliasNameGenerator(db.course.user, db.user.id)); + + $$UserTableProcessedTableManager? get user { + if ($_item.user == null) return null; + final manager = $$UserTableTableManager($_db, $_db.user) + .filter((f) => f.id($_item.user!)); + final item = $_typedResult.readTableOrNull(_userTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } +} + class $$CourseTableFilterComposer extends Composer<_$AppDatabase, $CourseTable> { $$CourseTableFilterComposer({ @@ -3195,9 +3288,6 @@ class $$CourseTableFilterComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnFilters(column)); - ColumnFilters get unit => $composableBuilder( column: $table.unit, builder: (column) => ColumnFilters(column)); @@ -3224,6 +3314,26 @@ class $$CourseTableFilterComposer ColumnFilters get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnFilters(column)); + + $$UserTableFilterComposer get user { + final $$UserTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.user, + referencedTable: $db.user, + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserTableFilterComposer( + $db: $db, + $table: $db.user, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } } class $$CourseTableOrderingComposer @@ -3235,9 +3345,6 @@ class $$CourseTableOrderingComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get unit => $composableBuilder( column: $table.unit, builder: (column) => ColumnOrderings(column)); @@ -3264,6 +3371,26 @@ class $$CourseTableOrderingComposer ColumnOrderings get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnOrderings(column)); + + $$UserTableOrderingComposer get user { + final $$UserTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.user, + referencedTable: $db.user, + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserTableOrderingComposer( + $db: $db, + $table: $db.user, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } } class $$CourseTableAnnotationComposer @@ -3275,9 +3402,6 @@ class $$CourseTableAnnotationComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - GeneratedColumn get unit => $composableBuilder(column: $table.unit, builder: (column) => column); @@ -3304,6 +3428,26 @@ class $$CourseTableAnnotationComposer GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); + + $$UserTableAnnotationComposer get user { + final $$UserTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.user, + referencedTable: $db.user, + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserTableAnnotationComposer( + $db: $db, + $table: $db.user, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } } class $$CourseTableTableManager extends RootTableManager< @@ -3315,9 +3459,9 @@ class $$CourseTableTableManager extends RootTableManager< $$CourseTableAnnotationComposer, $$CourseTableCreateCompanionBuilder, $$CourseTableUpdateCompanionBuilder, - (CourseData, BaseReferences<_$AppDatabase, $CourseTable, CourseData>), + (CourseData, $$CourseTableReferences), CourseData, - PrefetchHooks Function()> { + PrefetchHooks Function({bool user})> { $$CourseTableTableManager(_$AppDatabase db, $CourseTable table) : super(TableManagerState( db: db, @@ -3329,8 +3473,8 @@ class $$CourseTableTableManager extends RootTableManager< createComputedFieldComposer: () => $$CourseTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ - Value id = const Value.absent(), Value unit = const Value.absent(), + Value user = const Value.absent(), Value section = const Value.absent(), Value weekDay = const Value.absent(), Value campus = const Value.absent(), @@ -3342,8 +3486,8 @@ class $$CourseTableTableManager extends RootTableManager< Value rowid = const Value.absent(), }) => CourseCompanion( - id: id, unit: unit, + user: user, section: section, weekDay: weekDay, campus: campus, @@ -3355,8 +3499,8 @@ class $$CourseTableTableManager extends RootTableManager< rowid: rowid, ), createCompanionCallback: ({ - Value id = const Value.absent(), required String unit, + Value user = const Value.absent(), required String section, required String weekDay, required String campus, @@ -3368,8 +3512,8 @@ class $$CourseTableTableManager extends RootTableManager< Value rowid = const Value.absent(), }) => CourseCompanion.insert( - id: id, unit: unit, + user: user, section: section, weekDay: weekDay, campus: campus, @@ -3381,9 +3525,42 @@ class $$CourseTableTableManager extends RootTableManager< rowid: rowid, ), withReferenceMapper: (p0) => p0 - .map((e) => (e.readTable(table), BaseReferences(db, table, e))) + .map((e) => + (e.readTable(table), $$CourseTableReferences(db, table, e))) .toList(), - prefetchHooksCallback: null, + prefetchHooksCallback: ({user = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (user) { + state = state.withJoin( + currentTable: table, + currentColumn: table.user, + referencedTable: $$CourseTableReferences._userTable(db), + referencedColumn: $$CourseTableReferences._userTable(db).id, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, )); } @@ -3396,9 +3573,9 @@ typedef $$CourseTableProcessedTableManager = ProcessedTableManager< $$CourseTableAnnotationComposer, $$CourseTableCreateCompanionBuilder, $$CourseTableUpdateCompanionBuilder, - (CourseData, BaseReferences<_$AppDatabase, $CourseTable, CourseData>), + (CourseData, $$CourseTableReferences), CourseData, - PrefetchHooks Function()>; + PrefetchHooks Function({bool user})>; class $AppDatabaseManager { final _$AppDatabase _db; diff --git a/lib/features/auth/repository/user_repository.dart b/lib/features/auth/repository/user_repository.dart index 4598a17..d974027 100644 --- a/lib/features/auth/repository/user_repository.dart +++ b/lib/features/auth/repository/user_repository.dart @@ -48,15 +48,15 @@ final class UserRepository { // Register a magnet singleton instance // TODO: (erick) enable auth with magnet - // GetIt.instance.registerSingletonIfAbsent( - // () => Magnet(credentials.admno, credentials.password), - // instanceName: "magnet", - // ); + GetIt.instance.registerSingletonIfAbsent( + () => Magnet(credentials.admno, credentials.password), + instanceName: "magnet", + ); // authenticate with magnet - const magnetResult = - // await (GetIt.instance.get(instanceName: "magnet").login()); - Right(Object()); + final magnetResult = + await (GetIt.instance.get(instanceName: "magnet").login()); + // Right(Object()); return magnetResult.fold((error) { return left(error.toString()); }, (session) async { diff --git a/lib/features/courses/cubit/course_cubit.dart b/lib/features/courses/cubit/course_cubit.dart index 9d0c84e..5b83ed2 100644 --- a/lib/features/courses/cubit/course_cubit.dart +++ b/lib/features/courses/cubit/course_cubit.dart @@ -1,3 +1,4 @@ +import 'package:academia/database/database.dart'; import 'package:academia/features/courses/cubit/course_state.dart'; import 'package:academia/features/courses/repository/course_repository.dart'; import 'package:dartz/dartz.dart'; @@ -5,20 +6,20 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class CourseCubit extends Cubit { final CourseRepository _courseRepository = CourseRepository(); - CourseCubit() : super(CourseStateInitial()) { - fetchCachedCourses().then((value) {}); - } + CourseCubit() : super(CourseStateInitial()); - Future syncCourses() async { - final res = await _courseRepository.syncCoursesWithMagnet(); + Future syncCourses(UserData user) async { + emit(CourseStateLoading()); + final res = await _courseRepository.syncCoursesWithMagnet(user); if (res.isLeft()) { emit(CourseStateError(error: (res as Left).value)); } emit(CourseStateLoaded(courses: (res as Right).value)); } - Future fetchCachedCourses() async { - final result = await _courseRepository.fetchAllCachedCourses(); + Future fetchCachedCourses(UserData user) async { + emit(CourseStateLoading()); + final result = await _courseRepository.fetchAllCachedCourses(user); result.fold((error) { emit(CourseStateError(error: error)); }, (courses) { diff --git a/lib/features/courses/models/course.dart b/lib/features/courses/models/course.dart index 2884d2e..91d9ac3 100644 --- a/lib/features/courses/models/course.dart +++ b/lib/features/courses/models/course.dart @@ -1,8 +1,9 @@ +import 'package:academia/features/auth/models/user.dart'; import 'package:drift/drift.dart'; class Course extends Table { - TextColumn get id => text().nullable()(); TextColumn get unit => text()(); + TextColumn get user => text().references(User, #id).nullable()(); TextColumn get section => text()(); @JsonKey("day_of_the_week") TextColumn get weekDay => text()(); @@ -16,5 +17,5 @@ class Course extends Table { dateTime().nullable().withDefault(Constant(DateTime.now()))(); @override - Set>? get primaryKey => {id}; + Set>? get primaryKey => {unit}; } diff --git a/lib/features/courses/repository/course_local_repository.dart b/lib/features/courses/repository/course_local_repository.dart index 8d774d7..7cd596a 100644 --- a/lib/features/courses/repository/course_local_repository.dart +++ b/lib/features/courses/repository/course_local_repository.dart @@ -13,7 +13,8 @@ final class CourseLocalRepository { /// Fetches all cached courses /// Incase of an error a message of type [String] is returned /// On success, a [List] of [CourseData] is returned - Future>> fetchAllCachedCourses() async { + Future>> fetchAllCachedCourses( + UserData user) async { try { final users = await (_localDb.course.select() ..orderBy([ @@ -23,6 +24,7 @@ final class CourseLocalRepository { ), ])) .get(); + users.removeWhere((course) => course.user == user.id); return right(users); } catch (e) { return left("Failed to retrieve users with message ${e.toString()}"); diff --git a/lib/features/courses/repository/course_repository.dart b/lib/features/courses/repository/course_repository.dart index 38da255..f1eb9a1 100644 --- a/lib/features/courses/repository/course_repository.dart +++ b/lib/features/courses/repository/course_repository.dart @@ -1,5 +1,6 @@ import 'package:academia/database/database.dart'; import 'package:dartz/dartz.dart'; +import 'package:drift/drift.dart'; import 'course_local_repository.dart'; import 'course_remote_repository.dart'; @@ -12,11 +13,13 @@ final class CourseRepository { /// Fetches all cached courses /// Incase of an error a message of type [String] is returned /// On success, a [List] of [CourseData] is returned - Future>> fetchAllCachedCourses() async { - return await _localRepository.fetchAllCachedCourses(); + Future>> fetchAllCachedCourses( + UserData user) async { + return await _localRepository.fetchAllCachedCourses(user); } - Future>> syncCoursesWithMagnet() async { + Future>> syncCoursesWithMagnet( + UserData user) async { final result = await _courseRemoteRepository.fetchCoursesFromMagnet(); return result.fold((error) { @@ -24,7 +27,9 @@ final class CourseRepository { }, (courses) async { // Cache them to local db for (final course in courses) { - final res = await _localRepository.addCourseToCache(course); + final res = await _localRepository.addCourseToCache( + course.copyWith(user: Value(user.id)), + ); if (res.isLeft()) { return left((res as Left).value); } diff --git a/lib/features/courses/views/courses_page_mobile.dart b/lib/features/courses/views/courses_page_mobile.dart index b0b6781..e9f8ebd 100644 --- a/lib/features/courses/views/courses_page_mobile.dart +++ b/lib/features/courses/views/courses_page_mobile.dart @@ -13,9 +13,13 @@ class CoursesPageMobile extends StatefulWidget { class _CoursesPageMobileState extends State { late CourseCubit courseCubit = BlocProvider.of(context); + late AuthCubit authCubit = BlocProvider.of(context); @override void initState() { + courseCubit.fetchCachedCourses( + (authCubit.state as AuthenticatedState).user, + ); super.initState(); } @@ -24,7 +28,9 @@ class _CoursesPageMobileState extends State { return Scaffold( body: RefreshIndicator( onRefresh: () async { - await courseCubit.syncCourses(); + await courseCubit.syncCourses( + (authCubit.state as AuthenticatedState).user, + ); }, child: CustomScrollView( slivers: [ From b65916199754c9f3c4e4bf97eefccb2d834f735c Mon Sep 17 00:00:00 2001 From: Erick Date: Thu, 26 Dec 2024 13:05:56 +0300 Subject: [PATCH 19/24] feat: course view page --- lib/features/courses/courses.dart | 1 + .../repository/course_local_repository.dart | 3 +- .../courses/repository/course_repository.dart | 1 + .../views/course_mobile_view_page.dart | 183 ++++++++++++++++++ .../courses/views/courses_page_mobile.dart | 6 + lib/utils/router/router.dart | 9 + 6 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 lib/features/courses/views/course_mobile_view_page.dart diff --git a/lib/features/courses/courses.dart b/lib/features/courses/courses.dart index 08a758e..f2a958a 100644 --- a/lib/features/courses/courses.dart +++ b/lib/features/courses/courses.dart @@ -1,3 +1,4 @@ export 'views/courses_page.dart'; +export 'views/course_mobile_view_page.dart'; export 'cubit/course_cubit.dart'; export 'cubit/course_state.dart'; diff --git a/lib/features/courses/repository/course_local_repository.dart b/lib/features/courses/repository/course_local_repository.dart index 7cd596a..f4cebec 100644 --- a/lib/features/courses/repository/course_local_repository.dart +++ b/lib/features/courses/repository/course_local_repository.dart @@ -36,8 +36,9 @@ final class CourseLocalRepository { /// information on conflict Future> addCourseToCache(CourseData course) async { try { - final ok = await _localDb.into(_localDb.course).insertOnConflictUpdate( + final ok = await _localDb.into(_localDb.course).insert( course.toCompanion(true), + mode: InsertMode.insertOrReplace, ); if (ok != 0) { return right(true); diff --git a/lib/features/courses/repository/course_repository.dart b/lib/features/courses/repository/course_repository.dart index f1eb9a1..f4b5631 100644 --- a/lib/features/courses/repository/course_repository.dart +++ b/lib/features/courses/repository/course_repository.dart @@ -31,6 +31,7 @@ final class CourseRepository { course.copyWith(user: Value(user.id)), ); if (res.isLeft()) { + print((res as Left).value); return left((res as Left).value); } } diff --git a/lib/features/courses/views/course_mobile_view_page.dart b/lib/features/courses/views/course_mobile_view_page.dart new file mode 100644 index 0000000..fecd5e7 --- /dev/null +++ b/lib/features/courses/views/course_mobile_view_page.dart @@ -0,0 +1,183 @@ +import 'package:academia/database/database.dart'; +import 'package:flutter/material.dart'; +import 'package:icons_plus/icons_plus.dart'; +import 'package:sliver_tools/sliver_tools.dart'; + +class CourseMobileViewPage extends StatefulWidget { + const CourseMobileViewPage({ + super.key, + required this.course, + }); + + final CourseData course; + @override + State createState() => _CourseMobileViewPageState(); +} + +class _CourseMobileViewPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: CustomScrollView( + slivers: [ + SliverAppBar( + pinned: true, + floating: true, + snap: true, + expandedHeight: 128, + flexibleSpace: FlexibleSpaceBar( + title: Text(widget.course.unit), + ), + + // actions + actions: [ + IconButton( + onPressed: () {}, + icon: const Icon(Bootstrap.trash), + ), + ], + ), + SliverPadding( + padding: const EdgeInsets.all(12), + sliver: MultiSliver( + children: [ + SliverPinnedHeader( + child: Container( + color: Theme.of(context).colorScheme.surface, + padding: const EdgeInsets.symmetric(vertical: 16), + child: Text( + "Course Details", + style: Theme.of(context).textTheme.headlineSmall, + ), + ), + ), + + // Graph + SliverList.builder( + itemCount: 10, + itemBuilder: (context, index) => ListTile( + leading: CircleAvatar( + child: Text(index.toString()), + ), + title: Text("Hi"), + subtitle: Text("Some really nice content"), + ), + ), + ], + ), + ), + SliverPadding( + padding: const EdgeInsets.all(12), + sliver: MultiSliver( + children: [ + SliverPinnedHeader( + child: Container( + color: Theme.of(context).colorScheme.surface, + padding: const EdgeInsets.symmetric(vertical: 16), + child: Text( + "Course Details", + style: Theme.of(context).textTheme.headlineSmall, + ), + ), + ), + + // Information cards + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.hash), + title: const Text("Course Unit Code"), + subtitle: Text(widget.course.unit), + ), + ), + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.nut), + title: const Text("Course Section"), + subtitle: Text(widget.course.section), + ), + ), + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.person_badge), + title: const Text("Lecturer"), + subtitle: Text(widget.course.lecturer), + ), + ), + + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.calendar2_date), + title: const Text("Every"), + subtitle: Text(widget.course.weekDay), + ), + ), + + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.clock), + title: const Text("Period"), + subtitle: Text(widget.course.period), + ), + ), + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12), + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.house_check), + title: const Text("Venue"), + subtitle: Text(widget.course.room), + ), + ), + ], + ), + ), + ], + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () {}, + label: const Text("New Reminder"), + icon: const Icon(Bootstrap.alarm), + ), + ); + } +} diff --git a/lib/features/courses/views/courses_page_mobile.dart b/lib/features/courses/views/courses_page_mobile.dart index e9f8ebd..131d509 100644 --- a/lib/features/courses/views/courses_page_mobile.dart +++ b/lib/features/courses/views/courses_page_mobile.dart @@ -1,6 +1,8 @@ import 'package:academia/features/features.dart'; +import 'package:academia/utils/router/router.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:skeletonizer/skeletonizer.dart'; @@ -57,6 +59,10 @@ class _CoursesPageMobileState extends State { itemBuilder: (context, index) { final course = state.courses[index]; return ListTile( + onTap: () => context.pushNamed( + AcademiaRouter.courseView, + extra: course, + ), leading: const CircleAvatar(), title: Text("${course.unit} ${course.section}"), subtitle: Text( diff --git a/lib/utils/router/router.dart b/lib/utils/router/router.dart index 15bfc26..fe56ce0 100644 --- a/lib/utils/router/router.dart +++ b/lib/utils/router/router.dart @@ -1,3 +1,4 @@ +import 'package:academia/database/database.dart'; import 'package:academia/utils/router/default_route.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -14,6 +15,7 @@ class AcademiaRouter { static const String home = "home"; static const String userSelection = "user-selection"; static const String onboarding = "onboarding"; + static const String courseView = "course-view"; static final GoRouter _router = GoRouter( initialLocation: "/", @@ -49,6 +51,13 @@ class AcademiaRouter { path: "/$userSelection", builder: (context, state) => const UserSelectionPage(), ), + GoRoute( + name: courseView, + path: "/$courseView", + builder: (context, state) { + final CourseData course = state.extra as CourseData; + return CourseMobileViewPage(course: course); + }), ], ); } From f0dcd8e34b17f8472697a13abb373f4dbab29a1a Mon Sep 17 00:00:00 2001 From: Erick Date: Sun, 29 Dec 2024 08:53:28 +0300 Subject: [PATCH 20/24] fix: cached courses not loading --- .../auth/repository/user_remote_repository.dart | 4 ++-- lib/features/auth/repository/user_repository.dart | 14 +++++++------- lib/features/courses/cubit/course_cubit.dart | 1 + .../repository/course_local_repository.dart | 14 ++++---------- .../courses/views/courses_page_mobile.dart | 3 ++- lib/utils/network/dio_client.dart | 4 ++-- lib/utils/utils.dart | 6 ++++++ 7 files changed, 24 insertions(+), 22 deletions(-) diff --git a/lib/features/auth/repository/user_remote_repository.dart b/lib/features/auth/repository/user_remote_repository.dart index 59a6821..e5e08ce 100644 --- a/lib/features/auth/repository/user_remote_repository.dart +++ b/lib/features/auth/repository/user_remote_repository.dart @@ -14,7 +14,7 @@ final class UserRemoteRepository with DioErrorHandler { ) async { try { final response = await _client.dio.post( - "/auth/authenticate", + "/verisafe/v2/auth/authenticate", data: credentials.toJson(), ); @@ -36,7 +36,7 @@ final class UserRemoteRepository with DioErrorHandler { /// went wrong Future> fetchUserProfile() async { try { - final response = await _client.dio.get("/users/profile"); + final response = await _client.dio.get("verisafe/v2/users/profile"); if (response.statusCode == 200) { return right(UserProfileData.fromJson(response.data)); } diff --git a/lib/features/auth/repository/user_repository.dart b/lib/features/auth/repository/user_repository.dart index d974027..4598a17 100644 --- a/lib/features/auth/repository/user_repository.dart +++ b/lib/features/auth/repository/user_repository.dart @@ -48,15 +48,15 @@ final class UserRepository { // Register a magnet singleton instance // TODO: (erick) enable auth with magnet - GetIt.instance.registerSingletonIfAbsent( - () => Magnet(credentials.admno, credentials.password), - instanceName: "magnet", - ); + // GetIt.instance.registerSingletonIfAbsent( + // () => Magnet(credentials.admno, credentials.password), + // instanceName: "magnet", + // ); // authenticate with magnet - final magnetResult = - await (GetIt.instance.get(instanceName: "magnet").login()); - // Right(Object()); + const magnetResult = + // await (GetIt.instance.get(instanceName: "magnet").login()); + Right(Object()); return magnetResult.fold((error) { return left(error.toString()); }, (session) async { diff --git a/lib/features/courses/cubit/course_cubit.dart b/lib/features/courses/cubit/course_cubit.dart index 5b83ed2..5d77cac 100644 --- a/lib/features/courses/cubit/course_cubit.dart +++ b/lib/features/courses/cubit/course_cubit.dart @@ -21,6 +21,7 @@ class CourseCubit extends Cubit { emit(CourseStateLoading()); final result = await _courseRepository.fetchAllCachedCourses(user); result.fold((error) { + print(error); emit(CourseStateError(error: error)); }, (courses) { emit(CourseStateLoaded(courses: courses)); diff --git a/lib/features/courses/repository/course_local_repository.dart b/lib/features/courses/repository/course_local_repository.dart index f4cebec..f7e6927 100644 --- a/lib/features/courses/repository/course_local_repository.dart +++ b/lib/features/courses/repository/course_local_repository.dart @@ -16,16 +16,10 @@ final class CourseLocalRepository { Future>> fetchAllCachedCourses( UserData user) async { try { - final users = await (_localDb.course.select() - ..orderBy([ - (c) => OrderingTerm( - expression: c.createdAt, - mode: OrderingMode.desc, - ), - ])) - .get(); - users.removeWhere((course) => course.user == user.id); - return right(users); + final courses = await _localDb.managers.course + .filter((c) => c.user.id.equals(user.id)) + .get(distinct: true); + return right(courses); } catch (e) { return left("Failed to retrieve users with message ${e.toString()}"); } diff --git a/lib/features/courses/views/courses_page_mobile.dart b/lib/features/courses/views/courses_page_mobile.dart index 131d509..dd8112a 100644 --- a/lib/features/courses/views/courses_page_mobile.dart +++ b/lib/features/courses/views/courses_page_mobile.dart @@ -1,5 +1,6 @@ import 'package:academia/features/features.dart'; import 'package:academia/utils/router/router.dart'; +import 'package:academia/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; @@ -66,7 +67,7 @@ class _CoursesPageMobileState extends State { leading: const CircleAvatar(), title: Text("${course.unit} ${course.section}"), subtitle: Text( - "${course.room} * ${course.period} * ${course.lecturer}", + "${course.room} * ${course.period} * ${course.lecturer.capitalize()}", ), ); }, diff --git a/lib/utils/network/dio_client.dart b/lib/utils/network/dio_client.dart index 1fb3318..a1d58c7 100644 --- a/lib/utils/network/dio_client.dart +++ b/lib/utils/network/dio_client.dart @@ -4,8 +4,8 @@ import './auth_interceptor.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; class DioClient { - static const String _baseUrl = "http://192.168.43.218:8000/v2"; - // static const String _baseUrl = "http://127.0.0.1:8000/v2"; + // static const String _baseUrl = "http://192.168.43.218:8000/v2"; + static const String _baseUrl = "http://192.168.43.218:8000"; DioClient() { dio.interceptors.add( diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index ac1e339..fee0649 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -1 +1,7 @@ export 'responsive/responsive.dart'; + +extension StringExtension on String { + String capitalize() { + return "${this[0].toUpperCase()}${substring(1).toLowerCase()}"; + } +} From 2268f2e0497a95d64646831c164f3aac5b10dfda Mon Sep 17 00:00:00 2001 From: Erick Date: Sun, 29 Dec 2024 09:48:43 +0300 Subject: [PATCH 21/24] feat: tagging courses by colors --- lib/features/courses/cubit/course_cubit.dart | 20 ++ .../courses/repository/course_repository.dart | 7 +- .../views/course_mobile_view_page.dart | 317 ++++++++++-------- .../courses/views/courses_page_mobile.dart | 6 +- 4 files changed, 213 insertions(+), 137 deletions(-) diff --git a/lib/features/courses/cubit/course_cubit.dart b/lib/features/courses/cubit/course_cubit.dart index 5d77cac..05654b5 100644 --- a/lib/features/courses/cubit/course_cubit.dart +++ b/lib/features/courses/cubit/course_cubit.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:academia/database/database.dart'; import 'package:academia/features/courses/cubit/course_state.dart'; import 'package:academia/features/courses/repository/course_repository.dart'; @@ -27,4 +29,22 @@ class CourseCubit extends Cubit { emit(CourseStateLoaded(courses: courses)); }); } + + Future saveCouse(CourseData course) async { + if (state is! CourseStateLoaded) { + return; + } + var courses = []; + courses = (state as CourseStateLoaded).courses; + courses.removeWhere((c) => c.unit == course.unit); + emit(CourseStateLoading()); + final result = await _courseRepository.saveCourseToCache(course); + result.fold((error) { + emit(CourseStateError(error: error)); + }, (ok) { + courses.add(course); + emit(CourseStateLoaded(courses: courses)); + log("Data saved to localdb sucessfully"); + }); + } } diff --git a/lib/features/courses/repository/course_repository.dart b/lib/features/courses/repository/course_repository.dart index f4b5631..ddaa700 100644 --- a/lib/features/courses/repository/course_repository.dart +++ b/lib/features/courses/repository/course_repository.dart @@ -31,11 +31,16 @@ final class CourseRepository { course.copyWith(user: Value(user.id)), ); if (res.isLeft()) { - print((res as Left).value); return left((res as Left).value); } } return right(courses); }); } + + /// Saves [course] to the local cache + /// Useful for updating information on a course + Future> saveCourseToCache(CourseData course) async { + return await _localRepository.addCourseToCache(course); + } } diff --git a/lib/features/courses/views/course_mobile_view_page.dart b/lib/features/courses/views/course_mobile_view_page.dart index fecd5e7..51d503c 100644 --- a/lib/features/courses/views/course_mobile_view_page.dart +++ b/lib/features/courses/views/course_mobile_view_page.dart @@ -1,5 +1,9 @@ import 'package:academia/database/database.dart'; +import 'package:academia/features/courses/courses.dart'; +import 'package:drift/drift.dart'; +import 'package:flex_color_picker/flex_color_picker.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:icons_plus/icons_plus.dart'; import 'package:sliver_tools/sliver_tools.dart'; @@ -15,168 +19,211 @@ class CourseMobileViewPage extends StatefulWidget { } class _CourseMobileViewPageState extends State { + late CourseCubit courseCubit = BlocProvider.of(context); + late Color? color; + + @override + void initState() { + super.initState(); + color = widget.course.color == null ? null : Color(widget.course.color!); + } + @override Widget build(BuildContext context) { - return Scaffold( - body: CustomScrollView( - slivers: [ - SliverAppBar( - pinned: true, - floating: true, - snap: true, - expandedHeight: 128, - flexibleSpace: FlexibleSpaceBar( - title: Text(widget.course.unit), + return Theme( + data: color == null + ? Theme.of(context) + : Theme.of(context).copyWith( + colorScheme: ColorScheme.fromSeed(seedColor: color!), ), - - // actions - actions: [ - IconButton( - onPressed: () {}, - icon: const Icon(Bootstrap.trash), + child: Scaffold( + body: CustomScrollView( + slivers: [ + SliverAppBar( + pinned: true, + floating: true, + snap: true, + expandedHeight: 128, + flexibleSpace: FlexibleSpaceBar( + title: Text(widget.course.unit), ), - ], - ), - SliverPadding( - padding: const EdgeInsets.all(12), - sliver: MultiSliver( - children: [ - SliverPinnedHeader( - child: Container( - color: Theme.of(context).colorScheme.surface, - padding: const EdgeInsets.symmetric(vertical: 16), - child: Text( - "Course Details", - style: Theme.of(context).textTheme.headlineSmall, - ), - ), - ), - // Graph - SliverList.builder( - itemCount: 10, - itemBuilder: (context, index) => ListTile( - leading: CircleAvatar( - child: Text(index.toString()), - ), - title: Text("Hi"), - subtitle: Text("Some really nice content"), - ), + // actions + actions: [ + IconButton( + onPressed: () {}, + icon: const Icon(Bootstrap.trash), ), ], ), - ), - SliverPadding( - padding: const EdgeInsets.all(12), - sliver: MultiSliver( - children: [ - SliverPinnedHeader( - child: Container( - color: Theme.of(context).colorScheme.surface, - padding: const EdgeInsets.symmetric(vertical: 16), - child: Text( - "Course Details", - style: Theme.of(context).textTheme.headlineSmall, + SliverPadding( + padding: const EdgeInsets.all(12), + sliver: MultiSliver( + children: [ + SliverPinnedHeader( + child: Container( + color: Theme.of(context).colorScheme.surface, + padding: const EdgeInsets.symmetric(vertical: 16), + child: Text( + "Course Information", + style: Theme.of(context).textTheme.headlineSmall, + ), ), ), - ), - - // Information cards - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.circular(12), + // Information cards + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), ), - ), - child: ListTile( - leading: const Icon(Bootstrap.hash), - title: const Text("Course Unit Code"), - subtitle: Text(widget.course.unit), - ), - ), - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.zero, + child: ListTile( + leading: const Icon(Bootstrap.hash), + title: const Text("Course Unit Code"), + subtitle: Text(widget.course.unit), ), ), - child: ListTile( - leading: const Icon(Bootstrap.nut), - title: const Text("Course Section"), - subtitle: Text(widget.course.section), - ), - ), - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.zero, + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.nut), + title: const Text("Course Section"), + subtitle: Text(widget.course.section), ), ), - child: ListTile( - leading: const Icon(Bootstrap.person_badge), - title: const Text("Lecturer"), - subtitle: Text(widget.course.lecturer), + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.person_badge), + title: const Text("Lecturer"), + subtitle: Text(widget.course.lecturer), + ), ), - ), - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.zero, + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.calendar2_date), + title: const Text("Every"), + subtitle: Text(widget.course.weekDay), ), ), - child: ListTile( - leading: const Icon(Bootstrap.calendar2_date), - title: const Text("Every"), - subtitle: Text(widget.course.weekDay), - ), - ), - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.zero, + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.clock), + title: const Text("Period"), + subtitle: Text(widget.course.period), ), ), - child: ListTile( - leading: const Icon(Bootstrap.clock), - title: const Text("Period"), - subtitle: Text(widget.course.period), + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12), + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.house_check), + title: const Text("Venue"), + subtitle: Text(widget.course.room), + ), ), - ), - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - bottom: Radius.circular(12), + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12), + ), + ), + child: ListTile( + onTap: () { + final result = showColorPickerDialog( + context, + Theme.of(context).colorScheme.primary, + ); + result.then((color) { + setState(() { + this.color = color; + }); + courseCubit.saveCouse(widget.course.copyWith( + color: Value(color.value), + )); + }); + }, + leading: CircleAvatar(backgroundColor: color), + title: const Text("Course Color"), + subtitle: + const Text("Pick a color to identify this course"), ), ), - child: ListTile( - leading: const Icon(Bootstrap.house_check), - title: const Text("Venue"), - subtitle: Text(widget.course.room), + ], + ), + ), + SliverPadding( + padding: const EdgeInsets.all(12), + sliver: MultiSliver( + children: [ + SliverPinnedHeader( + child: Container( + color: Theme.of(context).colorScheme.surface, + padding: const EdgeInsets.symmetric(vertical: 16), + child: Text( + "Course Notes & Reminders", + style: Theme.of(context).textTheme.headlineSmall, + ), + ), ), - ), - ], + // Graph + SliverList.builder( + itemCount: 10, + itemBuilder: (context, index) => ListTile( + leading: CircleAvatar( + child: Text(index.toString()), + ), + title: const Text("Hi"), + subtitle: const Text("Some really nice content"), + ), + ), + ], + ), ), - ), - ], - ), - floatingActionButton: FloatingActionButton.extended( - onPressed: () {}, - label: const Text("New Reminder"), - icon: const Icon(Bootstrap.alarm), + ], + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () {}, + label: const Text("New Reminder"), + icon: const Icon(Bootstrap.alarm), + ), ), ); } diff --git a/lib/features/courses/views/courses_page_mobile.dart b/lib/features/courses/views/courses_page_mobile.dart index dd8112a..1257f8c 100644 --- a/lib/features/courses/views/courses_page_mobile.dart +++ b/lib/features/courses/views/courses_page_mobile.dart @@ -64,7 +64,11 @@ class _CoursesPageMobileState extends State { AcademiaRouter.courseView, extra: course, ), - leading: const CircleAvatar(), + leading: CircleAvatar( + backgroundColor: course.color == null + ? null + : Color(course.color!), + ), title: Text("${course.unit} ${course.section}"), subtitle: Text( "${course.room} * ${course.period} * ${course.lecturer.capitalize()}", From cfd201c9e08e65320abc47b15a9a725e8733016f Mon Sep 17 00:00:00 2001 From: Erick Date: Sun, 29 Dec 2024 10:18:06 +0300 Subject: [PATCH 22/24] feat: syncing courses with remote wookie server --- .../repository/course_remote_repository.dart | 24 +- .../courses/repository/course_repository.dart | 5 + .../views/course_mobile_view_page.dart | 328 +++++++++--------- 3 files changed, 193 insertions(+), 164 deletions(-) diff --git a/lib/features/courses/repository/course_remote_repository.dart b/lib/features/courses/repository/course_remote_repository.dart index 67d8379..8439e47 100644 --- a/lib/features/courses/repository/course_remote_repository.dart +++ b/lib/features/courses/repository/course_remote_repository.dart @@ -1,9 +1,13 @@ import 'package:academia/database/database.dart'; +import 'package:academia/utils/network/network.dart'; import 'package:dartz/dartz.dart'; +import 'package:dio/dio.dart'; import 'package:get_it/get_it.dart'; import 'package:magnet/magnet.dart'; -final class CourseRemoteRepository { +final class CourseRemoteRepository with DioErrorHandler { + final DioClient _dioClient = DioClient(); + /// Fetches courses from magnet. Future>> fetchCoursesFromMagnet() async { final magnetInstance = GetIt.instance.get(instanceName: "magnet"); @@ -14,4 +18,22 @@ final class CourseRemoteRepository { return right(courses.map((c) => CourseData.fromJson(c)).toList()); }); } + + /// Sync courses with wookie + Future> syncCourseWithWookie(CourseData course) async { + try { + final response = await _dioClient.dio.post( + "/wookie/courses/create", + data: course.toJson(), + ); + if (response.statusCode == 201) { + return right(true); + } + return right(false); + } on DioException catch (de) { + return handleDioError(de); + } catch (e) { + return left("Something went terribly wrong please try that later"); + } + } } diff --git a/lib/features/courses/repository/course_repository.dart b/lib/features/courses/repository/course_repository.dart index ddaa700..94b4b6e 100644 --- a/lib/features/courses/repository/course_repository.dart +++ b/lib/features/courses/repository/course_repository.dart @@ -27,6 +27,7 @@ final class CourseRepository { }, (courses) async { // Cache them to local db for (final course in courses) { + syncCourseWithWookie(course); final res = await _localRepository.addCourseToCache( course.copyWith(user: Value(user.id)), ); @@ -43,4 +44,8 @@ final class CourseRepository { Future> saveCourseToCache(CourseData course) async { return await _localRepository.addCourseToCache(course); } + + Future> syncCourseWithWookie(CourseData course) async { + return await _courseRemoteRepository.syncCourseWithWookie(course); + } } diff --git a/lib/features/courses/views/course_mobile_view_page.dart b/lib/features/courses/views/course_mobile_view_page.dart index 51d503c..6605531 100644 --- a/lib/features/courses/views/course_mobile_view_page.dart +++ b/lib/features/courses/views/course_mobile_view_page.dart @@ -33,196 +33,198 @@ class _CourseMobileViewPageState extends State { return Theme( data: color == null ? Theme.of(context) - : Theme.of(context).copyWith( + : ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: color!), ), - child: Scaffold( - body: CustomScrollView( - slivers: [ - SliverAppBar( - pinned: true, - floating: true, - snap: true, - expandedHeight: 128, - flexibleSpace: FlexibleSpaceBar( - title: Text(widget.course.unit), - ), - - // actions - actions: [ - IconButton( - onPressed: () {}, - icon: const Icon(Bootstrap.trash), + child: Builder( + builder: (context) => Scaffold( + body: CustomScrollView( + slivers: [ + SliverAppBar( + pinned: true, + floating: true, + snap: true, + expandedHeight: 128, + flexibleSpace: FlexibleSpaceBar( + title: Text(widget.course.unit), ), - ], - ), - SliverPadding( - padding: const EdgeInsets.all(12), - sliver: MultiSliver( - children: [ - SliverPinnedHeader( - child: Container( - color: Theme.of(context).colorScheme.surface, - padding: const EdgeInsets.symmetric(vertical: 16), - child: Text( - "Course Information", - style: Theme.of(context).textTheme.headlineSmall, - ), - ), + + // actions + actions: [ + IconButton( + onPressed: () {}, + icon: const Icon(Bootstrap.trash), ), - // Information cards - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.circular(12), + ], + ), + SliverPadding( + padding: const EdgeInsets.all(12), + sliver: MultiSliver( + children: [ + SliverPinnedHeader( + child: Container( + color: Theme.of(context).colorScheme.surface, + padding: const EdgeInsets.symmetric(vertical: 16), + child: Text( + "Course Information", + style: Theme.of(context).textTheme.headlineSmall, + ), ), ), - child: ListTile( - leading: const Icon(Bootstrap.hash), - title: const Text("Course Unit Code"), - subtitle: Text(widget.course.unit), - ), - ), - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.zero, + // Information cards + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.hash), + title: const Text("Course Unit Code"), + subtitle: Text(widget.course.unit), ), ), - child: ListTile( - leading: const Icon(Bootstrap.nut), - title: const Text("Course Section"), - subtitle: Text(widget.course.section), - ), - ), - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.zero, + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.nut), + title: const Text("Course Section"), + subtitle: Text(widget.course.section), ), ), - child: ListTile( - leading: const Icon(Bootstrap.person_badge), - title: const Text("Lecturer"), - subtitle: Text(widget.course.lecturer), + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.person_badge), + title: const Text("Lecturer"), + subtitle: Text(widget.course.lecturer), + ), ), - ), - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.zero, + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.calendar2_date), + title: const Text("Every"), + subtitle: Text(widget.course.weekDay), ), ), - child: ListTile( - leading: const Icon(Bootstrap.calendar2_date), - title: const Text("Every"), - subtitle: Text(widget.course.weekDay), - ), - ), - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.zero, + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.zero, + ), ), - ), - child: ListTile( - leading: const Icon(Bootstrap.clock), - title: const Text("Period"), - subtitle: Text(widget.course.period), - ), - ), - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - bottom: Radius.circular(12), + child: ListTile( + leading: const Icon(Bootstrap.clock), + title: const Text("Period"), + subtitle: Text(widget.course.period), ), ), - child: ListTile( - leading: const Icon(Bootstrap.house_check), - title: const Text("Venue"), - subtitle: Text(widget.course.room), - ), - ), - Card( - elevation: 0, - margin: const EdgeInsets.only(bottom: 2), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - bottom: Radius.circular(12), + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12), + ), + ), + child: ListTile( + leading: const Icon(Bootstrap.house_check), + title: const Text("Venue"), + subtitle: Text(widget.course.room), ), ), - child: ListTile( - onTap: () { - final result = showColorPickerDialog( - context, - Theme.of(context).colorScheme.primary, - ); - result.then((color) { - setState(() { - this.color = color; + Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12), + ), + ), + child: ListTile( + onTap: () { + final result = showColorPickerDialog( + context, + Theme.of(context).colorScheme.primary, + ); + result.then((color) { + setState(() { + this.color = color; + }); + courseCubit.saveCouse(widget.course.copyWith( + color: Value(color.value), + )); }); - courseCubit.saveCouse(widget.course.copyWith( - color: Value(color.value), - )); - }); - }, - leading: CircleAvatar(backgroundColor: color), - title: const Text("Course Color"), - subtitle: - const Text("Pick a color to identify this course"), + }, + leading: CircleAvatar(backgroundColor: color), + title: const Text("Course Color"), + subtitle: + const Text("Pick a color to identify this course"), + ), ), - ), - ], + ], + ), ), - ), - SliverPadding( - padding: const EdgeInsets.all(12), - sliver: MultiSliver( - children: [ - SliverPinnedHeader( - child: Container( - color: Theme.of(context).colorScheme.surface, - padding: const EdgeInsets.symmetric(vertical: 16), - child: Text( - "Course Notes & Reminders", - style: Theme.of(context).textTheme.headlineSmall, + SliverPadding( + padding: const EdgeInsets.all(12), + sliver: MultiSliver( + children: [ + SliverPinnedHeader( + child: Container( + color: Theme.of(context).colorScheme.surface, + padding: const EdgeInsets.symmetric(vertical: 16), + child: Text( + "Course Notes & Reminders", + style: Theme.of(context).textTheme.headlineSmall, + ), ), ), - ), - // Graph - SliverList.builder( - itemCount: 10, - itemBuilder: (context, index) => ListTile( - leading: CircleAvatar( - child: Text(index.toString()), - ), - title: const Text("Hi"), - subtitle: const Text("Some really nice content"), + // Graph + SliverList.builder( + itemCount: 10, + itemBuilder: (context, index) => ListTile( + leading: CircleAvatar( + child: Text(index.toString()), + ), + title: const Text("Hi"), + subtitle: const Text("Some really nice content"), + ), ), - ), - ], + ], + ), ), - ), - ], - ), - floatingActionButton: FloatingActionButton.extended( - onPressed: () {}, - label: const Text("New Reminder"), - icon: const Icon(Bootstrap.alarm), + ], + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () {}, + label: const Text("New Reminder"), + icon: const Icon(Bootstrap.alarm), + ), ), ), ); From c76f488d7a17e8a7fee2ee699c76eaa339771ea4 Mon Sep 17 00:00:00 2001 From: Erick Date: Mon, 30 Dec 2024 13:46:36 +0300 Subject: [PATCH 23/24] chore: todo view page ui screen --- lib/database/database.dart | 2 + lib/database/database.g.dart | 3257 ++++++++++++----- .../views/essentials_mobile_page.dart | 34 +- lib/features/features.dart | 1 + lib/features/todo/models/todo.dart | 27 + lib/features/todo/todo.dart | 2 + lib/features/todo/views/todo_page.dart | 37 + lib/features/todo/views/todo_view_page.dart | 364 ++ lib/utils/router/router.dart | 28 +- 9 files changed, 2772 insertions(+), 980 deletions(-) create mode 100644 lib/features/todo/models/todo.dart create mode 100644 lib/features/todo/todo.dart create mode 100644 lib/features/todo/views/todo_page.dart create mode 100644 lib/features/todo/views/todo_view_page.dart diff --git a/lib/database/database.dart b/lib/database/database.dart index a63eed1..adb2caf 100644 --- a/lib/database/database.dart +++ b/lib/database/database.dart @@ -4,6 +4,7 @@ import 'package:academia/features/auth/models/user.dart'; import 'package:academia/features/auth/models/user_credentials.dart'; import 'package:academia/features/auth/models/user_profile.dart'; import 'package:academia/features/courses/models/course.dart'; +import 'package:academia/features/todo/models/todo.dart'; import 'package:drift/drift.dart'; import 'package:drift/native.dart'; import 'package:drift_flutter/drift_flutter.dart'; @@ -49,6 +50,7 @@ Future _getDatabaseDirectory() async { UserProfile, UserCredential, Course, + Todo, ]) class AppDatabase extends _$AppDatabase { // After generating code, this class needs to define a schemaVersion getter diff --git a/lib/database/database.g.dart b/lib/database/database.g.dart index dd699cb..643d699 100644 --- a/lib/database/database.g.dart +++ b/lib/database/database.g.dart @@ -2001,117 +2001,1285 @@ class CourseCompanion extends UpdateCompanion { } } -abstract class _$AppDatabase extends GeneratedDatabase { - _$AppDatabase(QueryExecutor e) : super(e); - $AppDatabaseManager get managers => $AppDatabaseManager(this); - late final $UserTable user = $UserTable(this); - late final $UserProfileTable userProfile = $UserProfileTable(this); - late final $UserCredentialTable userCredential = $UserCredentialTable(this); - late final $CourseTable course = $CourseTable(this); +class $TodoTable extends Todo with TableInfo<$TodoTable, TodoData> { @override - Iterable> get allTables => - allSchemaEntities.whereType>(); + final GeneratedDatabase attachedDatabase; + final String? _alias; + $TodoTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); @override - List get allSchemaEntities => - [user, userProfile, userCredential, course]; + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); + static const VerificationMeta _userIdMeta = const VerificationMeta('userId'); @override - DriftDatabaseOptions get options => - const DriftDatabaseOptions(storeDateTimeAsText: true); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', aliasedName, false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: + GeneratedColumn.constraintIsAlways('REFERENCES user (id)')); + static const VerificationMeta _unitMeta = const VerificationMeta('unit'); + @override + late final GeneratedColumn unit = GeneratedColumn( + 'unit', aliasedName, true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('REFERENCES course (unit)')); + static const VerificationMeta _titleMeta = const VerificationMeta('title'); + @override + late final GeneratedColumn title = GeneratedColumn( + 'title', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _bodyMeta = const VerificationMeta('body'); + @override + late final GeneratedColumn body = GeneratedColumn( + 'body', aliasedName, false, + type: DriftSqlType.string, requiredDuringInsert: true); + static const VerificationMeta _subtasksMeta = + const VerificationMeta('subtasks'); + @override + late final GeneratedColumn subtasks = GeneratedColumn( + 'subtasks', aliasedName, true, + type: DriftSqlType.string, requiredDuringInsert: false); + static const VerificationMeta _colorMeta = const VerificationMeta('color'); + @override + late final GeneratedColumn color = GeneratedColumn( + 'color', aliasedName, true, + type: DriftSqlType.int, requiredDuringInsert: false); + static const VerificationMeta _notifyMeta = const VerificationMeta('notify'); + @override + late final GeneratedColumn notify = GeneratedColumn( + 'notify', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("notify" IN (0, 1))'), + defaultValue: const Constant(false)); + static const VerificationMeta _completeMeta = + const VerificationMeta('complete'); + @override + late final GeneratedColumn complete = GeneratedColumn( + 'complete', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: + GeneratedColumn.constraintIsAlways('CHECK ("complete" IN (0, 1))'), + defaultValue: const Constant(false)); + static const VerificationMeta _autocompleteMeta = + const VerificationMeta('autocomplete'); + @override + late final GeneratedColumn autocomplete = GeneratedColumn( + 'autocomplete', aliasedName, false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("autocomplete" IN (0, 1))'), + defaultValue: const Constant(false)); + static const VerificationMeta _notifyAtMeta = + const VerificationMeta('notifyAt'); + @override + late final GeneratedColumn notifyAt = GeneratedColumn( + 'notify_at', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + static const VerificationMeta _duedateMeta = + const VerificationMeta('duedate'); + @override + late final GeneratedColumn duedate = GeneratedColumn( + 'duedate', aliasedName, true, + type: DriftSqlType.dateTime, requiredDuringInsert: false); + static const VerificationMeta _dateCreatedMeta = + const VerificationMeta('dateCreated'); + @override + late final GeneratedColumn dateCreated = GeneratedColumn( + 'date_created', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: Constant(DateTime.now())); + static const VerificationMeta _dateModifiedMeta = + const VerificationMeta('dateModified'); + @override + late final GeneratedColumn dateModified = GeneratedColumn( + 'date_modified', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: Constant(DateTime.now())); + static const VerificationMeta _completedAtMeta = + const VerificationMeta('completedAt'); + @override + late final GeneratedColumn completedAt = GeneratedColumn( + 'completed_at', aliasedName, false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: Constant(DateTime.now())); + @override + List get $columns => [ + id, + userId, + unit, + title, + body, + subtasks, + color, + notify, + complete, + autocomplete, + notifyAt, + duedate, + dateCreated, + dateModified, + completedAt + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'todo'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('user_id')) { + context.handle(_userIdMeta, + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); + } else if (isInserting) { + context.missing(_userIdMeta); + } + if (data.containsKey('unit')) { + context.handle( + _unitMeta, unit.isAcceptableOrUnknown(data['unit']!, _unitMeta)); + } + if (data.containsKey('title')) { + context.handle( + _titleMeta, title.isAcceptableOrUnknown(data['title']!, _titleMeta)); + } else if (isInserting) { + context.missing(_titleMeta); + } + if (data.containsKey('body')) { + context.handle( + _bodyMeta, body.isAcceptableOrUnknown(data['body']!, _bodyMeta)); + } else if (isInserting) { + context.missing(_bodyMeta); + } + if (data.containsKey('subtasks')) { + context.handle(_subtasksMeta, + subtasks.isAcceptableOrUnknown(data['subtasks']!, _subtasksMeta)); + } + if (data.containsKey('color')) { + context.handle( + _colorMeta, color.isAcceptableOrUnknown(data['color']!, _colorMeta)); + } + if (data.containsKey('notify')) { + context.handle(_notifyMeta, + notify.isAcceptableOrUnknown(data['notify']!, _notifyMeta)); + } + if (data.containsKey('complete')) { + context.handle(_completeMeta, + complete.isAcceptableOrUnknown(data['complete']!, _completeMeta)); + } + if (data.containsKey('autocomplete')) { + context.handle( + _autocompleteMeta, + autocomplete.isAcceptableOrUnknown( + data['autocomplete']!, _autocompleteMeta)); + } + if (data.containsKey('notify_at')) { + context.handle(_notifyAtMeta, + notifyAt.isAcceptableOrUnknown(data['notify_at']!, _notifyAtMeta)); + } + if (data.containsKey('duedate')) { + context.handle(_duedateMeta, + duedate.isAcceptableOrUnknown(data['duedate']!, _duedateMeta)); + } + if (data.containsKey('date_created')) { + context.handle( + _dateCreatedMeta, + dateCreated.isAcceptableOrUnknown( + data['date_created']!, _dateCreatedMeta)); + } + if (data.containsKey('date_modified')) { + context.handle( + _dateModifiedMeta, + dateModified.isAcceptableOrUnknown( + data['date_modified']!, _dateModifiedMeta)); + } + if (data.containsKey('completed_at')) { + context.handle( + _completedAtMeta, + completedAt.isAcceptableOrUnknown( + data['completed_at']!, _completedAtMeta)); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + TodoData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return TodoData( + id: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + userId: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, + unit: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}unit']), + title: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}title'])!, + body: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}body'])!, + subtasks: attachedDatabase.typeMapping + .read(DriftSqlType.string, data['${effectivePrefix}subtasks']), + color: attachedDatabase.typeMapping + .read(DriftSqlType.int, data['${effectivePrefix}color']), + notify: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}notify'])!, + complete: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}complete'])!, + autocomplete: attachedDatabase.typeMapping + .read(DriftSqlType.bool, data['${effectivePrefix}autocomplete'])!, + notifyAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}notify_at']), + duedate: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}duedate']), + dateCreated: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}date_created'])!, + dateModified: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, data['${effectivePrefix}date_modified'])!, + completedAt: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}completed_at'])!, + ); + } + + @override + $TodoTable createAlias(String alias) { + return $TodoTable(attachedDatabase, alias); + } } -typedef $$UserTableCreateCompanionBuilder = UserCompanion Function({ - required String id, - required String username, - required String firstname, - Value othernames, - required String phone, - Value email, - required String gender, - Value active, - required DateTime createdAt, - required DateTime modifiedAt, - required String nationalId, +class TodoData extends DataClass implements Insertable { + final int id; + final String userId; + final String? unit; + final String title; + final String body; + final String? subtasks; + final int? color; + final bool notify; + final bool complete; + final bool autocomplete; + final DateTime? notifyAt; + final DateTime? duedate; + final DateTime dateCreated; + final DateTime dateModified; + final DateTime completedAt; + const TodoData( + {required this.id, + required this.userId, + this.unit, + required this.title, + required this.body, + this.subtasks, + this.color, + required this.notify, + required this.complete, + required this.autocomplete, + this.notifyAt, + this.duedate, + required this.dateCreated, + required this.dateModified, + required this.completedAt}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['user_id'] = Variable(userId); + if (!nullToAbsent || unit != null) { + map['unit'] = Variable(unit); + } + map['title'] = Variable(title); + map['body'] = Variable(body); + if (!nullToAbsent || subtasks != null) { + map['subtasks'] = Variable(subtasks); + } + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + map['notify'] = Variable(notify); + map['complete'] = Variable(complete); + map['autocomplete'] = Variable(autocomplete); + if (!nullToAbsent || notifyAt != null) { + map['notify_at'] = Variable(notifyAt); + } + if (!nullToAbsent || duedate != null) { + map['duedate'] = Variable(duedate); + } + map['date_created'] = Variable(dateCreated); + map['date_modified'] = Variable(dateModified); + map['completed_at'] = Variable(completedAt); + return map; + } + + TodoCompanion toCompanion(bool nullToAbsent) { + return TodoCompanion( + id: Value(id), + userId: Value(userId), + unit: unit == null && nullToAbsent ? const Value.absent() : Value(unit), + title: Value(title), + body: Value(body), + subtasks: subtasks == null && nullToAbsent + ? const Value.absent() + : Value(subtasks), + color: + color == null && nullToAbsent ? const Value.absent() : Value(color), + notify: Value(notify), + complete: Value(complete), + autocomplete: Value(autocomplete), + notifyAt: notifyAt == null && nullToAbsent + ? const Value.absent() + : Value(notifyAt), + duedate: duedate == null && nullToAbsent + ? const Value.absent() + : Value(duedate), + dateCreated: Value(dateCreated), + dateModified: Value(dateModified), + completedAt: Value(completedAt), + ); + } + + factory TodoData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return TodoData( + id: serializer.fromJson(json['id']), + userId: serializer.fromJson(json['userId']), + unit: serializer.fromJson(json['unit']), + title: serializer.fromJson(json['title']), + body: serializer.fromJson(json['body']), + subtasks: serializer.fromJson(json['subtasks']), + color: serializer.fromJson(json['color']), + notify: serializer.fromJson(json['notify']), + complete: serializer.fromJson(json['complete']), + autocomplete: serializer.fromJson(json['autocomplete']), + notifyAt: serializer.fromJson(json['notifyAt']), + duedate: serializer.fromJson(json['duedate']), + dateCreated: serializer.fromJson(json['dateCreated']), + dateModified: serializer.fromJson(json['dateModified']), + completedAt: serializer.fromJson(json['completedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'userId': serializer.toJson(userId), + 'unit': serializer.toJson(unit), + 'title': serializer.toJson(title), + 'body': serializer.toJson(body), + 'subtasks': serializer.toJson(subtasks), + 'color': serializer.toJson(color), + 'notify': serializer.toJson(notify), + 'complete': serializer.toJson(complete), + 'autocomplete': serializer.toJson(autocomplete), + 'notifyAt': serializer.toJson(notifyAt), + 'duedate': serializer.toJson(duedate), + 'dateCreated': serializer.toJson(dateCreated), + 'dateModified': serializer.toJson(dateModified), + 'completedAt': serializer.toJson(completedAt), + }; + } + + TodoData copyWith( + {int? id, + String? userId, + Value unit = const Value.absent(), + String? title, + String? body, + Value subtasks = const Value.absent(), + Value color = const Value.absent(), + bool? notify, + bool? complete, + bool? autocomplete, + Value notifyAt = const Value.absent(), + Value duedate = const Value.absent(), + DateTime? dateCreated, + DateTime? dateModified, + DateTime? completedAt}) => + TodoData( + id: id ?? this.id, + userId: userId ?? this.userId, + unit: unit.present ? unit.value : this.unit, + title: title ?? this.title, + body: body ?? this.body, + subtasks: subtasks.present ? subtasks.value : this.subtasks, + color: color.present ? color.value : this.color, + notify: notify ?? this.notify, + complete: complete ?? this.complete, + autocomplete: autocomplete ?? this.autocomplete, + notifyAt: notifyAt.present ? notifyAt.value : this.notifyAt, + duedate: duedate.present ? duedate.value : this.duedate, + dateCreated: dateCreated ?? this.dateCreated, + dateModified: dateModified ?? this.dateModified, + completedAt: completedAt ?? this.completedAt, + ); + TodoData copyWithCompanion(TodoCompanion data) { + return TodoData( + id: data.id.present ? data.id.value : this.id, + userId: data.userId.present ? data.userId.value : this.userId, + unit: data.unit.present ? data.unit.value : this.unit, + title: data.title.present ? data.title.value : this.title, + body: data.body.present ? data.body.value : this.body, + subtasks: data.subtasks.present ? data.subtasks.value : this.subtasks, + color: data.color.present ? data.color.value : this.color, + notify: data.notify.present ? data.notify.value : this.notify, + complete: data.complete.present ? data.complete.value : this.complete, + autocomplete: data.autocomplete.present + ? data.autocomplete.value + : this.autocomplete, + notifyAt: data.notifyAt.present ? data.notifyAt.value : this.notifyAt, + duedate: data.duedate.present ? data.duedate.value : this.duedate, + dateCreated: + data.dateCreated.present ? data.dateCreated.value : this.dateCreated, + dateModified: data.dateModified.present + ? data.dateModified.value + : this.dateModified, + completedAt: + data.completedAt.present ? data.completedAt.value : this.completedAt, + ); + } + + @override + String toString() { + return (StringBuffer('TodoData(') + ..write('id: $id, ') + ..write('userId: $userId, ') + ..write('unit: $unit, ') + ..write('title: $title, ') + ..write('body: $body, ') + ..write('subtasks: $subtasks, ') + ..write('color: $color, ') + ..write('notify: $notify, ') + ..write('complete: $complete, ') + ..write('autocomplete: $autocomplete, ') + ..write('notifyAt: $notifyAt, ') + ..write('duedate: $duedate, ') + ..write('dateCreated: $dateCreated, ') + ..write('dateModified: $dateModified, ') + ..write('completedAt: $completedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + userId, + unit, + title, + body, + subtasks, + color, + notify, + complete, + autocomplete, + notifyAt, + duedate, + dateCreated, + dateModified, + completedAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is TodoData && + other.id == this.id && + other.userId == this.userId && + other.unit == this.unit && + other.title == this.title && + other.body == this.body && + other.subtasks == this.subtasks && + other.color == this.color && + other.notify == this.notify && + other.complete == this.complete && + other.autocomplete == this.autocomplete && + other.notifyAt == this.notifyAt && + other.duedate == this.duedate && + other.dateCreated == this.dateCreated && + other.dateModified == this.dateModified && + other.completedAt == this.completedAt); +} + +class TodoCompanion extends UpdateCompanion { + final Value id; + final Value userId; + final Value unit; + final Value title; + final Value body; + final Value subtasks; + final Value color; + final Value notify; + final Value complete; + final Value autocomplete; + final Value notifyAt; + final Value duedate; + final Value dateCreated; + final Value dateModified; + final Value completedAt; + const TodoCompanion({ + this.id = const Value.absent(), + this.userId = const Value.absent(), + this.unit = const Value.absent(), + this.title = const Value.absent(), + this.body = const Value.absent(), + this.subtasks = const Value.absent(), + this.color = const Value.absent(), + this.notify = const Value.absent(), + this.complete = const Value.absent(), + this.autocomplete = const Value.absent(), + this.notifyAt = const Value.absent(), + this.duedate = const Value.absent(), + this.dateCreated = const Value.absent(), + this.dateModified = const Value.absent(), + this.completedAt = const Value.absent(), + }); + TodoCompanion.insert({ + this.id = const Value.absent(), + required String userId, + this.unit = const Value.absent(), + required String title, + required String body, + this.subtasks = const Value.absent(), + this.color = const Value.absent(), + this.notify = const Value.absent(), + this.complete = const Value.absent(), + this.autocomplete = const Value.absent(), + this.notifyAt = const Value.absent(), + this.duedate = const Value.absent(), + this.dateCreated = const Value.absent(), + this.dateModified = const Value.absent(), + this.completedAt = const Value.absent(), + }) : userId = Value(userId), + title = Value(title), + body = Value(body); + static Insertable custom({ + Expression? id, + Expression? userId, + Expression? unit, + Expression? title, + Expression? body, + Expression? subtasks, + Expression? color, + Expression? notify, + Expression? complete, + Expression? autocomplete, + Expression? notifyAt, + Expression? duedate, + Expression? dateCreated, + Expression? dateModified, + Expression? completedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (userId != null) 'user_id': userId, + if (unit != null) 'unit': unit, + if (title != null) 'title': title, + if (body != null) 'body': body, + if (subtasks != null) 'subtasks': subtasks, + if (color != null) 'color': color, + if (notify != null) 'notify': notify, + if (complete != null) 'complete': complete, + if (autocomplete != null) 'autocomplete': autocomplete, + if (notifyAt != null) 'notify_at': notifyAt, + if (duedate != null) 'duedate': duedate, + if (dateCreated != null) 'date_created': dateCreated, + if (dateModified != null) 'date_modified': dateModified, + if (completedAt != null) 'completed_at': completedAt, + }); + } + + TodoCompanion copyWith( + {Value? id, + Value? userId, + Value? unit, + Value? title, + Value? body, + Value? subtasks, + Value? color, + Value? notify, + Value? complete, + Value? autocomplete, + Value? notifyAt, + Value? duedate, + Value? dateCreated, + Value? dateModified, + Value? completedAt}) { + return TodoCompanion( + id: id ?? this.id, + userId: userId ?? this.userId, + unit: unit ?? this.unit, + title: title ?? this.title, + body: body ?? this.body, + subtasks: subtasks ?? this.subtasks, + color: color ?? this.color, + notify: notify ?? this.notify, + complete: complete ?? this.complete, + autocomplete: autocomplete ?? this.autocomplete, + notifyAt: notifyAt ?? this.notifyAt, + duedate: duedate ?? this.duedate, + dateCreated: dateCreated ?? this.dateCreated, + dateModified: dateModified ?? this.dateModified, + completedAt: completedAt ?? this.completedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (unit.present) { + map['unit'] = Variable(unit.value); + } + if (title.present) { + map['title'] = Variable(title.value); + } + if (body.present) { + map['body'] = Variable(body.value); + } + if (subtasks.present) { + map['subtasks'] = Variable(subtasks.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (notify.present) { + map['notify'] = Variable(notify.value); + } + if (complete.present) { + map['complete'] = Variable(complete.value); + } + if (autocomplete.present) { + map['autocomplete'] = Variable(autocomplete.value); + } + if (notifyAt.present) { + map['notify_at'] = Variable(notifyAt.value); + } + if (duedate.present) { + map['duedate'] = Variable(duedate.value); + } + if (dateCreated.present) { + map['date_created'] = Variable(dateCreated.value); + } + if (dateModified.present) { + map['date_modified'] = Variable(dateModified.value); + } + if (completedAt.present) { + map['completed_at'] = Variable(completedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('TodoCompanion(') + ..write('id: $id, ') + ..write('userId: $userId, ') + ..write('unit: $unit, ') + ..write('title: $title, ') + ..write('body: $body, ') + ..write('subtasks: $subtasks, ') + ..write('color: $color, ') + ..write('notify: $notify, ') + ..write('complete: $complete, ') + ..write('autocomplete: $autocomplete, ') + ..write('notifyAt: $notifyAt, ') + ..write('duedate: $duedate, ') + ..write('dateCreated: $dateCreated, ') + ..write('dateModified: $dateModified, ') + ..write('completedAt: $completedAt') + ..write(')')) + .toString(); + } +} + +abstract class _$AppDatabase extends GeneratedDatabase { + _$AppDatabase(QueryExecutor e) : super(e); + $AppDatabaseManager get managers => $AppDatabaseManager(this); + late final $UserTable user = $UserTable(this); + late final $UserProfileTable userProfile = $UserProfileTable(this); + late final $UserCredentialTable userCredential = $UserCredentialTable(this); + late final $CourseTable course = $CourseTable(this); + late final $TodoTable todo = $TodoTable(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => + [user, userProfile, userCredential, course, todo]; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} + +typedef $$UserTableCreateCompanionBuilder = UserCompanion Function({ + required String id, + required String username, + required String firstname, + Value othernames, + required String phone, + Value email, + required String gender, + Value active, + required DateTime createdAt, + required DateTime modifiedAt, + required String nationalId, + Value rowid, +}); +typedef $$UserTableUpdateCompanionBuilder = UserCompanion Function({ + Value id, + Value username, + Value firstname, + Value othernames, + Value phone, + Value email, + Value gender, + Value active, + Value createdAt, + Value modifiedAt, + Value nationalId, + Value rowid, +}); + +final class $$UserTableReferences + extends BaseReferences<_$AppDatabase, $UserTable, UserData> { + $$UserTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static MultiTypedResultKey<$UserProfileTable, List> + _userProfileRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( + db.userProfile, + aliasName: $_aliasNameGenerator(db.user.id, db.userProfile.userId)); + + $$UserProfileTableProcessedTableManager get userProfileRefs { + final manager = $$UserProfileTableTableManager($_db, $_db.userProfile) + .filter((f) => f.userId.id($_item.id)); + + final cache = $_typedResult.readTableOrNull(_userProfileRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache)); + } + + static MultiTypedResultKey<$CourseTable, List> _courseRefsTable( + _$AppDatabase db) => + MultiTypedResultKey.fromTable(db.course, + aliasName: $_aliasNameGenerator(db.user.id, db.course.user)); + + $$CourseTableProcessedTableManager get courseRefs { + final manager = $$CourseTableTableManager($_db, $_db.course) + .filter((f) => f.user.id($_item.id)); + + final cache = $_typedResult.readTableOrNull(_courseRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache)); + } + + static MultiTypedResultKey<$TodoTable, List> _todoRefsTable( + _$AppDatabase db) => + MultiTypedResultKey.fromTable(db.todo, + aliasName: $_aliasNameGenerator(db.user.id, db.todo.userId)); + + $$TodoTableProcessedTableManager get todoRefs { + final manager = $$TodoTableTableManager($_db, $_db.todo) + .filter((f) => f.userId.id($_item.id)); + + final cache = $_typedResult.readTableOrNull(_todoRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache)); + } +} + +class $$UserTableFilterComposer extends Composer<_$AppDatabase, $UserTable> { + $$UserTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get id => $composableBuilder( + column: $table.id, builder: (column) => ColumnFilters(column)); + + ColumnFilters get username => $composableBuilder( + column: $table.username, builder: (column) => ColumnFilters(column)); + + ColumnFilters get firstname => $composableBuilder( + column: $table.firstname, builder: (column) => ColumnFilters(column)); + + ColumnFilters get othernames => $composableBuilder( + column: $table.othernames, builder: (column) => ColumnFilters(column)); + + ColumnFilters get phone => $composableBuilder( + column: $table.phone, builder: (column) => ColumnFilters(column)); + + ColumnFilters get email => $composableBuilder( + column: $table.email, builder: (column) => ColumnFilters(column)); + + ColumnFilters get gender => $composableBuilder( + column: $table.gender, builder: (column) => ColumnFilters(column)); + + ColumnFilters get active => $composableBuilder( + column: $table.active, builder: (column) => ColumnFilters(column)); + + ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, builder: (column) => ColumnFilters(column)); + + ColumnFilters get modifiedAt => $composableBuilder( + column: $table.modifiedAt, builder: (column) => ColumnFilters(column)); + + ColumnFilters get nationalId => $composableBuilder( + column: $table.nationalId, builder: (column) => ColumnFilters(column)); + + Expression userProfileRefs( + Expression Function($$UserProfileTableFilterComposer f) f) { + final $$UserProfileTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.userProfile, + getReferencedColumn: (t) => t.userId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserProfileTableFilterComposer( + $db: $db, + $table: $db.userProfile, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } + + Expression courseRefs( + Expression Function($$CourseTableFilterComposer f) f) { + final $$CourseTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.course, + getReferencedColumn: (t) => t.user, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$CourseTableFilterComposer( + $db: $db, + $table: $db.course, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } + + Expression todoRefs( + Expression Function($$TodoTableFilterComposer f) f) { + final $$TodoTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.todo, + getReferencedColumn: (t) => t.userId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$TodoTableFilterComposer( + $db: $db, + $table: $db.todo, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } +} + +class $$UserTableOrderingComposer extends Composer<_$AppDatabase, $UserTable> { + $$UserTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get username => $composableBuilder( + column: $table.username, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get firstname => $composableBuilder( + column: $table.firstname, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get othernames => $composableBuilder( + column: $table.othernames, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get phone => $composableBuilder( + column: $table.phone, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get email => $composableBuilder( + column: $table.email, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get gender => $composableBuilder( + column: $table.gender, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get active => $composableBuilder( + column: $table.active, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get modifiedAt => $composableBuilder( + column: $table.modifiedAt, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get nationalId => $composableBuilder( + column: $table.nationalId, builder: (column) => ColumnOrderings(column)); +} + +class $$UserTableAnnotationComposer + extends Composer<_$AppDatabase, $UserTable> { + $$UserTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get username => + $composableBuilder(column: $table.username, builder: (column) => column); + + GeneratedColumn get firstname => + $composableBuilder(column: $table.firstname, builder: (column) => column); + + GeneratedColumn get othernames => $composableBuilder( + column: $table.othernames, builder: (column) => column); + + GeneratedColumn get phone => + $composableBuilder(column: $table.phone, builder: (column) => column); + + GeneratedColumn get email => + $composableBuilder(column: $table.email, builder: (column) => column); + + GeneratedColumn get gender => + $composableBuilder(column: $table.gender, builder: (column) => column); + + GeneratedColumn get active => + $composableBuilder(column: $table.active, builder: (column) => column); + + GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + GeneratedColumn get modifiedAt => $composableBuilder( + column: $table.modifiedAt, builder: (column) => column); + + GeneratedColumn get nationalId => $composableBuilder( + column: $table.nationalId, builder: (column) => column); + + Expression userProfileRefs( + Expression Function($$UserProfileTableAnnotationComposer a) f) { + final $$UserProfileTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.userProfile, + getReferencedColumn: (t) => t.userId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserProfileTableAnnotationComposer( + $db: $db, + $table: $db.userProfile, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } + + Expression courseRefs( + Expression Function($$CourseTableAnnotationComposer a) f) { + final $$CourseTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.course, + getReferencedColumn: (t) => t.user, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$CourseTableAnnotationComposer( + $db: $db, + $table: $db.course, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } + + Expression todoRefs( + Expression Function($$TodoTableAnnotationComposer a) f) { + final $$TodoTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.todo, + getReferencedColumn: (t) => t.userId, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$TodoTableAnnotationComposer( + $db: $db, + $table: $db.todo, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return f(composer); + } +} + +class $$UserTableTableManager extends RootTableManager< + _$AppDatabase, + $UserTable, + UserData, + $$UserTableFilterComposer, + $$UserTableOrderingComposer, + $$UserTableAnnotationComposer, + $$UserTableCreateCompanionBuilder, + $$UserTableUpdateCompanionBuilder, + (UserData, $$UserTableReferences), + UserData, + PrefetchHooks Function( + {bool userProfileRefs, bool courseRefs, bool todoRefs})> { + $$UserTableTableManager(_$AppDatabase db, $UserTable table) + : super(TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$UserTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$UserTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$UserTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + Value id = const Value.absent(), + Value username = const Value.absent(), + Value firstname = const Value.absent(), + Value othernames = const Value.absent(), + Value phone = const Value.absent(), + Value email = const Value.absent(), + Value gender = const Value.absent(), + Value active = const Value.absent(), + Value createdAt = const Value.absent(), + Value modifiedAt = const Value.absent(), + Value nationalId = const Value.absent(), + Value rowid = const Value.absent(), + }) => + UserCompanion( + id: id, + username: username, + firstname: firstname, + othernames: othernames, + phone: phone, + email: email, + gender: gender, + active: active, + createdAt: createdAt, + modifiedAt: modifiedAt, + nationalId: nationalId, + rowid: rowid, + ), + createCompanionCallback: ({ + required String id, + required String username, + required String firstname, + Value othernames = const Value.absent(), + required String phone, + Value email = const Value.absent(), + required String gender, + Value active = const Value.absent(), + required DateTime createdAt, + required DateTime modifiedAt, + required String nationalId, + Value rowid = const Value.absent(), + }) => + UserCompanion.insert( + id: id, + username: username, + firstname: firstname, + othernames: othernames, + phone: phone, + email: email, + gender: gender, + active: active, + createdAt: createdAt, + modifiedAt: modifiedAt, + nationalId: nationalId, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map((e) => + (e.readTable(table), $$UserTableReferences(db, table, e))) + .toList(), + prefetchHooksCallback: ( + {userProfileRefs = false, courseRefs = false, todoRefs = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [ + if (userProfileRefs) db.userProfile, + if (courseRefs) db.course, + if (todoRefs) db.todo + ], + addJoins: null, + getPrefetchedDataCallback: (items) async { + return [ + if (userProfileRefs) + await $_getPrefetchedData( + currentTable: table, + referencedTable: + $$UserTableReferences._userProfileRefsTable(db), + managerFromTypedResult: (p0) => + $$UserTableReferences(db, table, p0) + .userProfileRefs, + referencedItemsForCurrentItem: (item, + referencedItems) => + referencedItems.where((e) => e.userId == item.id), + typedResults: items), + if (courseRefs) + await $_getPrefetchedData( + currentTable: table, + referencedTable: + $$UserTableReferences._courseRefsTable(db), + managerFromTypedResult: (p0) => + $$UserTableReferences(db, table, p0).courseRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => + referencedItems.where((e) => e.user == item.id), + typedResults: items), + if (todoRefs) + await $_getPrefetchedData( + currentTable: table, + referencedTable: + $$UserTableReferences._todoRefsTable(db), + managerFromTypedResult: (p0) => + $$UserTableReferences(db, table, p0).todoRefs, + referencedItemsForCurrentItem: (item, + referencedItems) => + referencedItems.where((e) => e.userId == item.id), + typedResults: items) + ]; + }, + ); + }, + )); +} + +typedef $$UserTableProcessedTableManager = ProcessedTableManager< + _$AppDatabase, + $UserTable, + UserData, + $$UserTableFilterComposer, + $$UserTableOrderingComposer, + $$UserTableAnnotationComposer, + $$UserTableCreateCompanionBuilder, + $$UserTableUpdateCompanionBuilder, + (UserData, $$UserTableReferences), + UserData, + PrefetchHooks Function( + {bool userProfileRefs, bool courseRefs, bool todoRefs})>; +typedef $$UserProfileTableCreateCompanionBuilder = UserProfileCompanion + Function({ + required String userId, + Value bio, + Value vibePoints, + Value profilePictureUrl, + Value lastSeen, + Value createdAt, + Value modifiedAt, + Value admissionNumber, + Value campus, + required DateTime dateOfBirth, Value rowid, }); -typedef $$UserTableUpdateCompanionBuilder = UserCompanion Function({ - Value id, - Value username, - Value firstname, - Value othernames, - Value phone, - Value email, - Value gender, - Value active, +typedef $$UserProfileTableUpdateCompanionBuilder = UserProfileCompanion + Function({ + Value userId, + Value bio, + Value vibePoints, + Value profilePictureUrl, + Value lastSeen, Value createdAt, Value modifiedAt, - Value nationalId, + Value admissionNumber, + Value campus, + Value dateOfBirth, Value rowid, }); -final class $$UserTableReferences - extends BaseReferences<_$AppDatabase, $UserTable, UserData> { - $$UserTableReferences(super.$_db, super.$_table, super.$_typedResult); - - static MultiTypedResultKey<$UserProfileTable, List> - _userProfileRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( - db.userProfile, - aliasName: $_aliasNameGenerator(db.user.id, db.userProfile.userId)); - - $$UserProfileTableProcessedTableManager get userProfileRefs { - final manager = $$UserProfileTableTableManager($_db, $_db.userProfile) - .filter((f) => f.userId.id($_item.id)); - - final cache = $_typedResult.readTableOrNull(_userProfileRefsTable($_db)); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache)); - } - - static MultiTypedResultKey<$CourseTable, List> _courseRefsTable( - _$AppDatabase db) => - MultiTypedResultKey.fromTable(db.course, - aliasName: $_aliasNameGenerator(db.user.id, db.course.user)); +final class $$UserProfileTableReferences + extends BaseReferences<_$AppDatabase, $UserProfileTable, UserProfileData> { + $$UserProfileTableReferences(super.$_db, super.$_table, super.$_typedResult); - $$CourseTableProcessedTableManager get courseRefs { - final manager = $$CourseTableTableManager($_db, $_db.course) - .filter((f) => f.user.id($_item.id)); + static $UserTable _userIdTable(_$AppDatabase db) => db.user + .createAlias($_aliasNameGenerator(db.userProfile.userId, db.user.id)); - final cache = $_typedResult.readTableOrNull(_courseRefsTable($_db)); + $$UserTableProcessedTableManager? get userId { + if ($_item.userId == null) return null; + final manager = $$UserTableTableManager($_db, $_db.user) + .filter((f) => f.id($_item.userId!)); + final item = $_typedResult.readTableOrNull(_userIdTable($_db)); + if (item == null) return manager; return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache)); + manager.$state.copyWith(prefetchedData: [item])); } } -class $$UserTableFilterComposer extends Composer<_$AppDatabase, $UserTable> { - $$UserTableFilterComposer({ +class $$UserProfileTableFilterComposer + extends Composer<_$AppDatabase, $UserProfileTable> { + $$UserProfileTableFilterComposer({ required super.$db, required super.$table, super.joinBuilder, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnFilters(column)); - - ColumnFilters get username => $composableBuilder( - column: $table.username, builder: (column) => ColumnFilters(column)); - - ColumnFilters get firstname => $composableBuilder( - column: $table.firstname, builder: (column) => ColumnFilters(column)); - - ColumnFilters get othernames => $composableBuilder( - column: $table.othernames, builder: (column) => ColumnFilters(column)); - - ColumnFilters get phone => $composableBuilder( - column: $table.phone, builder: (column) => ColumnFilters(column)); + ColumnFilters get bio => $composableBuilder( + column: $table.bio, builder: (column) => ColumnFilters(column)); - ColumnFilters get email => $composableBuilder( - column: $table.email, builder: (column) => ColumnFilters(column)); + ColumnFilters get vibePoints => $composableBuilder( + column: $table.vibePoints, builder: (column) => ColumnFilters(column)); - ColumnFilters get gender => $composableBuilder( - column: $table.gender, builder: (column) => ColumnFilters(column)); + ColumnFilters get profilePictureUrl => $composableBuilder( + column: $table.profilePictureUrl, + builder: (column) => ColumnFilters(column)); - ColumnFilters get active => $composableBuilder( - column: $table.active, builder: (column) => ColumnFilters(column)); + ColumnFilters get lastSeen => $composableBuilder( + column: $table.lastSeen, builder: (column) => ColumnFilters(column)); ColumnFilters get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnFilters(column)); @@ -2119,83 +3287,58 @@ class $$UserTableFilterComposer extends Composer<_$AppDatabase, $UserTable> { ColumnFilters get modifiedAt => $composableBuilder( column: $table.modifiedAt, builder: (column) => ColumnFilters(column)); - ColumnFilters get nationalId => $composableBuilder( - column: $table.nationalId, builder: (column) => ColumnFilters(column)); + ColumnFilters get admissionNumber => $composableBuilder( + column: $table.admissionNumber, + builder: (column) => ColumnFilters(column)); - Expression userProfileRefs( - Expression Function($$UserProfileTableFilterComposer f) f) { - final $$UserProfileTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.userProfile, - getReferencedColumn: (t) => t.userId, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$UserProfileTableFilterComposer( - $db: $db, - $table: $db.userProfile, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } + ColumnFilters get campus => $composableBuilder( + column: $table.campus, builder: (column) => ColumnFilters(column)); - Expression courseRefs( - Expression Function($$CourseTableFilterComposer f) f) { - final $$CourseTableFilterComposer composer = $composerBuilder( + ColumnFilters get dateOfBirth => $composableBuilder( + column: $table.dateOfBirth, builder: (column) => ColumnFilters(column)); + + $$UserTableFilterComposer get userId { + final $$UserTableFilterComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.course, - getReferencedColumn: (t) => t.user, + getCurrentColumn: (t) => t.userId, + referencedTable: $db.user, + getReferencedColumn: (t) => t.id, builder: (joinBuilder, {$addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer}) => - $$CourseTableFilterComposer( + $$UserTableFilterComposer( $db: $db, - $table: $db.course, + $table: $db.user, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, joinBuilder: joinBuilder, $removeJoinBuilderFromRootComposer: $removeJoinBuilderFromRootComposer, )); - return f(composer); + return composer; } } -class $$UserTableOrderingComposer extends Composer<_$AppDatabase, $UserTable> { - $$UserTableOrderingComposer({ +class $$UserProfileTableOrderingComposer + extends Composer<_$AppDatabase, $UserProfileTable> { + $$UserProfileTableOrderingComposer({ required super.$db, required super.$table, super.joinBuilder, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get username => $composableBuilder( - column: $table.username, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get firstname => $composableBuilder( - column: $table.firstname, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get othernames => $composableBuilder( - column: $table.othernames, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get phone => $composableBuilder( - column: $table.phone, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get bio => $composableBuilder( + column: $table.bio, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get email => $composableBuilder( - column: $table.email, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get vibePoints => $composableBuilder( + column: $table.vibePoints, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get gender => $composableBuilder( - column: $table.gender, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get profilePictureUrl => $composableBuilder( + column: $table.profilePictureUrl, + builder: (column) => ColumnOrderings(column)); - ColumnOrderings get active => $composableBuilder( - column: $table.active, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get lastSeen => $composableBuilder( + column: $table.lastSeen, builder: (column) => ColumnOrderings(column)); ColumnOrderings get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnOrderings(column)); @@ -2203,42 +3346,57 @@ class $$UserTableOrderingComposer extends Composer<_$AppDatabase, $UserTable> { ColumnOrderings get modifiedAt => $composableBuilder( column: $table.modifiedAt, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get nationalId => $composableBuilder( - column: $table.nationalId, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get admissionNumber => $composableBuilder( + column: $table.admissionNumber, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get campus => $composableBuilder( + column: $table.campus, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get dateOfBirth => $composableBuilder( + column: $table.dateOfBirth, builder: (column) => ColumnOrderings(column)); + + $$UserTableOrderingComposer get userId { + final $$UserTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: $db.user, + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserTableOrderingComposer( + $db: $db, + $table: $db.user, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } } -class $$UserTableAnnotationComposer - extends Composer<_$AppDatabase, $UserTable> { - $$UserTableAnnotationComposer({ +class $$UserProfileTableAnnotationComposer + extends Composer<_$AppDatabase, $UserProfileTable> { + $$UserProfileTableAnnotationComposer({ required super.$db, required super.$table, super.joinBuilder, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - - GeneratedColumn get username => - $composableBuilder(column: $table.username, builder: (column) => column); - - GeneratedColumn get firstname => - $composableBuilder(column: $table.firstname, builder: (column) => column); - - GeneratedColumn get othernames => $composableBuilder( - column: $table.othernames, builder: (column) => column); - - GeneratedColumn get phone => - $composableBuilder(column: $table.phone, builder: (column) => column); + GeneratedColumn get bio => + $composableBuilder(column: $table.bio, builder: (column) => column); - GeneratedColumn get email => - $composableBuilder(column: $table.email, builder: (column) => column); + GeneratedColumn get vibePoints => $composableBuilder( + column: $table.vibePoints, builder: (column) => column); - GeneratedColumn get gender => - $composableBuilder(column: $table.gender, builder: (column) => column); + GeneratedColumn get profilePictureUrl => $composableBuilder( + column: $table.profilePictureUrl, builder: (column) => column); - GeneratedColumn get active => - $composableBuilder(column: $table.active, builder: (column) => column); + GeneratedColumn get lastSeen => + $composableBuilder(column: $table.lastSeen, builder: (column) => column); GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); @@ -2246,222 +3404,196 @@ class $$UserTableAnnotationComposer GeneratedColumn get modifiedAt => $composableBuilder( column: $table.modifiedAt, builder: (column) => column); - GeneratedColumn get nationalId => $composableBuilder( - column: $table.nationalId, builder: (column) => column); + GeneratedColumn get admissionNumber => $composableBuilder( + column: $table.admissionNumber, builder: (column) => column); - Expression userProfileRefs( - Expression Function($$UserProfileTableAnnotationComposer a) f) { - final $$UserProfileTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.userProfile, - getReferencedColumn: (t) => t.userId, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$UserProfileTableAnnotationComposer( - $db: $db, - $table: $db.userProfile, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } + GeneratedColumn get campus => + $composableBuilder(column: $table.campus, builder: (column) => column); - Expression courseRefs( - Expression Function($$CourseTableAnnotationComposer a) f) { - final $$CourseTableAnnotationComposer composer = $composerBuilder( + GeneratedColumn get dateOfBirth => $composableBuilder( + column: $table.dateOfBirth, builder: (column) => column); + + $$UserTableAnnotationComposer get userId { + final $$UserTableAnnotationComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.course, - getReferencedColumn: (t) => t.user, + getCurrentColumn: (t) => t.userId, + referencedTable: $db.user, + getReferencedColumn: (t) => t.id, builder: (joinBuilder, {$addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer}) => - $$CourseTableAnnotationComposer( + $$UserTableAnnotationComposer( $db: $db, - $table: $db.course, + $table: $db.user, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, joinBuilder: joinBuilder, $removeJoinBuilderFromRootComposer: $removeJoinBuilderFromRootComposer, )); - return f(composer); + return composer; } } -class $$UserTableTableManager extends RootTableManager< +class $$UserProfileTableTableManager extends RootTableManager< _$AppDatabase, - $UserTable, - UserData, - $$UserTableFilterComposer, - $$UserTableOrderingComposer, - $$UserTableAnnotationComposer, - $$UserTableCreateCompanionBuilder, - $$UserTableUpdateCompanionBuilder, - (UserData, $$UserTableReferences), - UserData, - PrefetchHooks Function({bool userProfileRefs, bool courseRefs})> { - $$UserTableTableManager(_$AppDatabase db, $UserTable table) + $UserProfileTable, + UserProfileData, + $$UserProfileTableFilterComposer, + $$UserProfileTableOrderingComposer, + $$UserProfileTableAnnotationComposer, + $$UserProfileTableCreateCompanionBuilder, + $$UserProfileTableUpdateCompanionBuilder, + (UserProfileData, $$UserProfileTableReferences), + UserProfileData, + PrefetchHooks Function({bool userId})> { + $$UserProfileTableTableManager(_$AppDatabase db, $UserProfileTable table) : super(TableManagerState( db: db, table: table, createFilteringComposer: () => - $$UserTableFilterComposer($db: db, $table: table), + $$UserProfileTableFilterComposer($db: db, $table: table), createOrderingComposer: () => - $$UserTableOrderingComposer($db: db, $table: table), + $$UserProfileTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => - $$UserTableAnnotationComposer($db: db, $table: table), + $$UserProfileTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ - Value id = const Value.absent(), - Value username = const Value.absent(), - Value firstname = const Value.absent(), - Value othernames = const Value.absent(), - Value phone = const Value.absent(), - Value email = const Value.absent(), - Value gender = const Value.absent(), - Value active = const Value.absent(), + Value userId = const Value.absent(), + Value bio = const Value.absent(), + Value vibePoints = const Value.absent(), + Value profilePictureUrl = const Value.absent(), + Value lastSeen = const Value.absent(), Value createdAt = const Value.absent(), Value modifiedAt = const Value.absent(), - Value nationalId = const Value.absent(), + Value admissionNumber = const Value.absent(), + Value campus = const Value.absent(), + Value dateOfBirth = const Value.absent(), Value rowid = const Value.absent(), }) => - UserCompanion( - id: id, - username: username, - firstname: firstname, - othernames: othernames, - phone: phone, - email: email, - gender: gender, - active: active, + UserProfileCompanion( + userId: userId, + bio: bio, + vibePoints: vibePoints, + profilePictureUrl: profilePictureUrl, + lastSeen: lastSeen, createdAt: createdAt, modifiedAt: modifiedAt, - nationalId: nationalId, + admissionNumber: admissionNumber, + campus: campus, + dateOfBirth: dateOfBirth, rowid: rowid, ), createCompanionCallback: ({ - required String id, - required String username, - required String firstname, - Value othernames = const Value.absent(), - required String phone, - Value email = const Value.absent(), - required String gender, - Value active = const Value.absent(), - required DateTime createdAt, - required DateTime modifiedAt, - required String nationalId, + required String userId, + Value bio = const Value.absent(), + Value vibePoints = const Value.absent(), + Value profilePictureUrl = const Value.absent(), + Value lastSeen = const Value.absent(), + Value createdAt = const Value.absent(), + Value modifiedAt = const Value.absent(), + Value admissionNumber = const Value.absent(), + Value campus = const Value.absent(), + required DateTime dateOfBirth, Value rowid = const Value.absent(), }) => - UserCompanion.insert( - id: id, - username: username, - firstname: firstname, - othernames: othernames, - phone: phone, - email: email, - gender: gender, - active: active, + UserProfileCompanion.insert( + userId: userId, + bio: bio, + vibePoints: vibePoints, + profilePictureUrl: profilePictureUrl, + lastSeen: lastSeen, createdAt: createdAt, modifiedAt: modifiedAt, - nationalId: nationalId, + admissionNumber: admissionNumber, + campus: campus, + dateOfBirth: dateOfBirth, rowid: rowid, ), withReferenceMapper: (p0) => p0 - .map((e) => - (e.readTable(table), $$UserTableReferences(db, table, e))) + .map((e) => ( + e.readTable(table), + $$UserProfileTableReferences(db, table, e) + )) .toList(), - prefetchHooksCallback: ( - {userProfileRefs = false, courseRefs = false}) { + prefetchHooksCallback: ({userId = false}) { return PrefetchHooks( db: db, - explicitlyWatchedTables: [ - if (userProfileRefs) db.userProfile, - if (courseRefs) db.course - ], - addJoins: null, + explicitlyWatchedTables: [], + addJoins: < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic>>(state) { + if (userId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.userId, + referencedTable: + $$UserProfileTableReferences._userIdTable(db), + referencedColumn: + $$UserProfileTableReferences._userIdTable(db).id, + ) as T; + } + + return state; + }, getPrefetchedDataCallback: (items) async { - return [ - if (userProfileRefs) - await $_getPrefetchedData( - currentTable: table, - referencedTable: - $$UserTableReferences._userProfileRefsTable(db), - managerFromTypedResult: (p0) => - $$UserTableReferences(db, table, p0) - .userProfileRefs, - referencedItemsForCurrentItem: (item, - referencedItems) => - referencedItems.where((e) => e.userId == item.id), - typedResults: items), - if (courseRefs) - await $_getPrefetchedData( - currentTable: table, - referencedTable: - $$UserTableReferences._courseRefsTable(db), - managerFromTypedResult: (p0) => - $$UserTableReferences(db, table, p0).courseRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => - referencedItems.where((e) => e.user == item.id), - typedResults: items) - ]; + return []; }, ); }, )); } -typedef $$UserTableProcessedTableManager = ProcessedTableManager< +typedef $$UserProfileTableProcessedTableManager = ProcessedTableManager< _$AppDatabase, - $UserTable, - UserData, - $$UserTableFilterComposer, - $$UserTableOrderingComposer, - $$UserTableAnnotationComposer, - $$UserTableCreateCompanionBuilder, - $$UserTableUpdateCompanionBuilder, - (UserData, $$UserTableReferences), - UserData, - PrefetchHooks Function({bool userProfileRefs, bool courseRefs})>; -typedef $$UserProfileTableCreateCompanionBuilder = UserProfileCompanion - Function({ - required String userId, - Value bio, - Value vibePoints, - Value profilePictureUrl, - Value lastSeen, - Value createdAt, - Value modifiedAt, - Value admissionNumber, - Value campus, - required DateTime dateOfBirth, + $UserProfileTable, + UserProfileData, + $$UserProfileTableFilterComposer, + $$UserProfileTableOrderingComposer, + $$UserProfileTableAnnotationComposer, + $$UserProfileTableCreateCompanionBuilder, + $$UserProfileTableUpdateCompanionBuilder, + (UserProfileData, $$UserProfileTableReferences), + UserProfileData, + PrefetchHooks Function({bool userId})>; +typedef $$UserCredentialTableCreateCompanionBuilder = UserCredentialCompanion + Function({ + Value userId, + required String admno, + Value accessToken, + required String username, + required String email, + required String password, + Value lastLogin, Value rowid, }); -typedef $$UserProfileTableUpdateCompanionBuilder = UserProfileCompanion +typedef $$UserCredentialTableUpdateCompanionBuilder = UserCredentialCompanion Function({ - Value userId, - Value bio, - Value vibePoints, - Value profilePictureUrl, - Value lastSeen, - Value createdAt, - Value modifiedAt, - Value admissionNumber, - Value campus, - Value dateOfBirth, + Value userId, + Value admno, + Value accessToken, + Value username, + Value email, + Value password, + Value lastLogin, Value rowid, }); -final class $$UserProfileTableReferences - extends BaseReferences<_$AppDatabase, $UserProfileTable, UserProfileData> { - $$UserProfileTableReferences(super.$_db, super.$_table, super.$_typedResult); +final class $$UserCredentialTableReferences extends BaseReferences< + _$AppDatabase, $UserCredentialTable, UserCredentialData> { + $$UserCredentialTableReferences( + super.$_db, super.$_table, super.$_typedResult); static $UserTable _userIdTable(_$AppDatabase db) => db.user - .createAlias($_aliasNameGenerator(db.userProfile.userId, db.user.id)); + .createAlias($_aliasNameGenerator(db.userCredential.userId, db.user.id)); $$UserTableProcessedTableManager? get userId { if ($_item.userId == null) return null; @@ -2472,48 +3604,221 @@ final class $$UserProfileTableReferences return ProcessedTableManager( manager.$state.copyWith(prefetchedData: [item])); } + + static $UserTable _usernameTable(_$AppDatabase db) => db.user.createAlias( + $_aliasNameGenerator(db.userCredential.username, db.user.username)); + + $$UserTableProcessedTableManager? get username { + if ($_item.username == null) return null; + final manager = $$UserTableTableManager($_db, $_db.user) + .filter((f) => f.username($_item.username!)); + final item = $_typedResult.readTableOrNull(_usernameTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } + + static $UserTable _emailTable(_$AppDatabase db) => db.user.createAlias( + $_aliasNameGenerator(db.userCredential.email, db.user.email)); + + $$UserTableProcessedTableManager? get email { + if ($_item.email == null) return null; + final manager = $$UserTableTableManager($_db, $_db.user) + .filter((f) => f.email($_item.email!)); + final item = $_typedResult.readTableOrNull(_emailTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } } -class $$UserProfileTableFilterComposer - extends Composer<_$AppDatabase, $UserProfileTable> { - $$UserProfileTableFilterComposer({ +class $$UserCredentialTableFilterComposer + extends Composer<_$AppDatabase, $UserCredentialTable> { + $$UserCredentialTableFilterComposer({ required super.$db, required super.$table, super.joinBuilder, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get bio => $composableBuilder( - column: $table.bio, builder: (column) => ColumnFilters(column)); + ColumnFilters get admno => $composableBuilder( + column: $table.admno, builder: (column) => ColumnFilters(column)); - ColumnFilters get vibePoints => $composableBuilder( - column: $table.vibePoints, builder: (column) => ColumnFilters(column)); + ColumnFilters get accessToken => $composableBuilder( + column: $table.accessToken, builder: (column) => ColumnFilters(column)); - ColumnFilters get profilePictureUrl => $composableBuilder( - column: $table.profilePictureUrl, - builder: (column) => ColumnFilters(column)); + ColumnFilters get password => $composableBuilder( + column: $table.password, builder: (column) => ColumnFilters(column)); - ColumnFilters get lastSeen => $composableBuilder( - column: $table.lastSeen, builder: (column) => ColumnFilters(column)); + ColumnFilters get lastLogin => $composableBuilder( + column: $table.lastLogin, builder: (column) => ColumnFilters(column)); - ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnFilters(column)); + $$UserTableFilterComposer get userId { + final $$UserTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: $db.user, + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserTableFilterComposer( + $db: $db, + $table: $db.user, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } - ColumnFilters get modifiedAt => $composableBuilder( - column: $table.modifiedAt, builder: (column) => ColumnFilters(column)); + $$UserTableFilterComposer get username { + final $$UserTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.username, + referencedTable: $db.user, + getReferencedColumn: (t) => t.username, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserTableFilterComposer( + $db: $db, + $table: $db.user, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } - ColumnFilters get admissionNumber => $composableBuilder( - column: $table.admissionNumber, - builder: (column) => ColumnFilters(column)); + $$UserTableFilterComposer get email { + final $$UserTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.email, + referencedTable: $db.user, + getReferencedColumn: (t) => t.email, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserTableFilterComposer( + $db: $db, + $table: $db.user, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} - ColumnFilters get campus => $composableBuilder( - column: $table.campus, builder: (column) => ColumnFilters(column)); +class $$UserCredentialTableOrderingComposer + extends Composer<_$AppDatabase, $UserCredentialTable> { + $$UserCredentialTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get admno => $composableBuilder( + column: $table.admno, builder: (column) => ColumnOrderings(column)); - ColumnFilters get dateOfBirth => $composableBuilder( - column: $table.dateOfBirth, builder: (column) => ColumnFilters(column)); + ColumnOrderings get accessToken => $composableBuilder( + column: $table.accessToken, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get password => $composableBuilder( + column: $table.password, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get lastLogin => $composableBuilder( + column: $table.lastLogin, builder: (column) => ColumnOrderings(column)); + + $$UserTableOrderingComposer get userId { + final $$UserTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: $db.user, + getReferencedColumn: (t) => t.id, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserTableOrderingComposer( + $db: $db, + $table: $db.user, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + $$UserTableOrderingComposer get username { + final $$UserTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.username, + referencedTable: $db.user, + getReferencedColumn: (t) => t.username, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserTableOrderingComposer( + $db: $db, + $table: $db.user, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } + + $$UserTableOrderingComposer get email { + final $$UserTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.email, + referencedTable: $db.user, + getReferencedColumn: (t) => t.email, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$UserTableOrderingComposer( + $db: $db, + $table: $db.user, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } +} + +class $$UserCredentialTableAnnotationComposer + extends Composer<_$AppDatabase, $UserCredentialTable> { + $$UserCredentialTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get admno => + $composableBuilder(column: $table.admno, builder: (column) => column); + + GeneratedColumn get accessToken => $composableBuilder( + column: $table.accessToken, builder: (column) => column); + + GeneratedColumn get password => + $composableBuilder(column: $table.password, builder: (column) => column); - $$UserTableFilterComposer get userId { - final $$UserTableFilterComposer composer = $composerBuilder( + GeneratedColumn get lastLogin => + $composableBuilder(column: $table.lastLogin, builder: (column) => column); + + $$UserTableAnnotationComposer get userId { + final $$UserTableAnnotationComposer composer = $composerBuilder( composer: this, getCurrentColumn: (t) => t.userId, referencedTable: $db.user, @@ -2521,7 +3826,7 @@ class $$UserProfileTableFilterComposer builder: (joinBuilder, {$addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer}) => - $$UserTableFilterComposer( + $$UserTableAnnotationComposer( $db: $db, $table: $db.user, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, @@ -2531,56 +3836,17 @@ class $$UserProfileTableFilterComposer )); return composer; } -} - -class $$UserProfileTableOrderingComposer - extends Composer<_$AppDatabase, $UserProfileTable> { - $$UserProfileTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get bio => $composableBuilder( - column: $table.bio, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get vibePoints => $composableBuilder( - column: $table.vibePoints, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get profilePictureUrl => $composableBuilder( - column: $table.profilePictureUrl, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get lastSeen => $composableBuilder( - column: $table.lastSeen, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get modifiedAt => $composableBuilder( - column: $table.modifiedAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get admissionNumber => $composableBuilder( - column: $table.admissionNumber, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get campus => $composableBuilder( - column: $table.campus, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get dateOfBirth => $composableBuilder( - column: $table.dateOfBirth, builder: (column) => ColumnOrderings(column)); - - $$UserTableOrderingComposer get userId { - final $$UserTableOrderingComposer composer = $composerBuilder( + $$UserTableAnnotationComposer get username { + final $$UserTableAnnotationComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.userId, + getCurrentColumn: (t) => t.username, referencedTable: $db.user, - getReferencedColumn: (t) => t.id, + getReferencedColumn: (t) => t.username, builder: (joinBuilder, {$addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer}) => - $$UserTableOrderingComposer( + $$UserTableAnnotationComposer( $db: $db, $table: $db.user, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, @@ -2590,50 +3856,13 @@ class $$UserProfileTableOrderingComposer )); return composer; } -} - -class $$UserProfileTableAnnotationComposer - extends Composer<_$AppDatabase, $UserProfileTable> { - $$UserProfileTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get bio => - $composableBuilder(column: $table.bio, builder: (column) => column); - - GeneratedColumn get vibePoints => $composableBuilder( - column: $table.vibePoints, builder: (column) => column); - - GeneratedColumn get profilePictureUrl => $composableBuilder( - column: $table.profilePictureUrl, builder: (column) => column); - - GeneratedColumn get lastSeen => - $composableBuilder(column: $table.lastSeen, builder: (column) => column); - GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); - - GeneratedColumn get modifiedAt => $composableBuilder( - column: $table.modifiedAt, builder: (column) => column); - - GeneratedColumn get admissionNumber => $composableBuilder( - column: $table.admissionNumber, builder: (column) => column); - - GeneratedColumn get campus => - $composableBuilder(column: $table.campus, builder: (column) => column); - - GeneratedColumn get dateOfBirth => $composableBuilder( - column: $table.dateOfBirth, builder: (column) => column); - - $$UserTableAnnotationComposer get userId { + $$UserTableAnnotationComposer get email { final $$UserTableAnnotationComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.userId, + getCurrentColumn: (t) => t.email, referencedTable: $db.user, - getReferencedColumn: (t) => t.id, + getReferencedColumn: (t) => t.email, builder: (joinBuilder, {$addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer}) => @@ -2649,87 +3878,77 @@ class $$UserProfileTableAnnotationComposer } } -class $$UserProfileTableTableManager extends RootTableManager< +class $$UserCredentialTableTableManager extends RootTableManager< _$AppDatabase, - $UserProfileTable, - UserProfileData, - $$UserProfileTableFilterComposer, - $$UserProfileTableOrderingComposer, - $$UserProfileTableAnnotationComposer, - $$UserProfileTableCreateCompanionBuilder, - $$UserProfileTableUpdateCompanionBuilder, - (UserProfileData, $$UserProfileTableReferences), - UserProfileData, - PrefetchHooks Function({bool userId})> { - $$UserProfileTableTableManager(_$AppDatabase db, $UserProfileTable table) + $UserCredentialTable, + UserCredentialData, + $$UserCredentialTableFilterComposer, + $$UserCredentialTableOrderingComposer, + $$UserCredentialTableAnnotationComposer, + $$UserCredentialTableCreateCompanionBuilder, + $$UserCredentialTableUpdateCompanionBuilder, + (UserCredentialData, $$UserCredentialTableReferences), + UserCredentialData, + PrefetchHooks Function({bool userId, bool username, bool email})> { + $$UserCredentialTableTableManager( + _$AppDatabase db, $UserCredentialTable table) : super(TableManagerState( db: db, table: table, createFilteringComposer: () => - $$UserProfileTableFilterComposer($db: db, $table: table), + $$UserCredentialTableFilterComposer($db: db, $table: table), createOrderingComposer: () => - $$UserProfileTableOrderingComposer($db: db, $table: table), + $$UserCredentialTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => - $$UserProfileTableAnnotationComposer($db: db, $table: table), + $$UserCredentialTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ - Value userId = const Value.absent(), - Value bio = const Value.absent(), - Value vibePoints = const Value.absent(), - Value profilePictureUrl = const Value.absent(), - Value lastSeen = const Value.absent(), - Value createdAt = const Value.absent(), - Value modifiedAt = const Value.absent(), - Value admissionNumber = const Value.absent(), - Value campus = const Value.absent(), - Value dateOfBirth = const Value.absent(), + Value userId = const Value.absent(), + Value admno = const Value.absent(), + Value accessToken = const Value.absent(), + Value username = const Value.absent(), + Value email = const Value.absent(), + Value password = const Value.absent(), + Value lastLogin = const Value.absent(), Value rowid = const Value.absent(), }) => - UserProfileCompanion( + UserCredentialCompanion( userId: userId, - bio: bio, - vibePoints: vibePoints, - profilePictureUrl: profilePictureUrl, - lastSeen: lastSeen, - createdAt: createdAt, - modifiedAt: modifiedAt, - admissionNumber: admissionNumber, - campus: campus, - dateOfBirth: dateOfBirth, + admno: admno, + accessToken: accessToken, + username: username, + email: email, + password: password, + lastLogin: lastLogin, rowid: rowid, ), createCompanionCallback: ({ - required String userId, - Value bio = const Value.absent(), - Value vibePoints = const Value.absent(), - Value profilePictureUrl = const Value.absent(), - Value lastSeen = const Value.absent(), - Value createdAt = const Value.absent(), - Value modifiedAt = const Value.absent(), - Value admissionNumber = const Value.absent(), - Value campus = const Value.absent(), - required DateTime dateOfBirth, + Value userId = const Value.absent(), + required String admno, + Value accessToken = const Value.absent(), + required String username, + required String email, + required String password, + Value lastLogin = const Value.absent(), Value rowid = const Value.absent(), }) => - UserProfileCompanion.insert( + UserCredentialCompanion.insert( userId: userId, - bio: bio, - vibePoints: vibePoints, - profilePictureUrl: profilePictureUrl, - lastSeen: lastSeen, - createdAt: createdAt, - modifiedAt: modifiedAt, - admissionNumber: admissionNumber, - campus: campus, - dateOfBirth: dateOfBirth, + admno: admno, + accessToken: accessToken, + username: username, + email: email, + password: password, + lastLogin: lastLogin, rowid: rowid, ), withReferenceMapper: (p0) => p0 .map((e) => ( e.readTable(table), - $$UserProfileTableReferences(db, table, e) + $$UserCredentialTableReferences(db, table, e) )) .toList(), - prefetchHooksCallback: ({userId = false}) { + prefetchHooksCallback: ( + {userId = false, username = false, email = false}) { return PrefetchHooks( db: db, explicitlyWatchedTables: [], @@ -2751,9 +3970,30 @@ class $$UserProfileTableTableManager extends RootTableManager< currentTable: table, currentColumn: table.userId, referencedTable: - $$UserProfileTableReferences._userIdTable(db), + $$UserCredentialTableReferences._userIdTable(db), referencedColumn: - $$UserProfileTableReferences._userIdTable(db).id, + $$UserCredentialTableReferences._userIdTable(db).id, + ) as T; + } + if (username) { + state = state.withJoin( + currentTable: table, + currentColumn: table.username, + referencedTable: + $$UserCredentialTableReferences._usernameTable(db), + referencedColumn: $$UserCredentialTableReferences + ._usernameTable(db) + .username, + ) as T; + } + if (email) { + state = state.withJoin( + currentTable: table, + currentColumn: table.email, + referencedTable: + $$UserCredentialTableReferences._emailTable(db), + referencedColumn: + $$UserCredentialTableReferences._emailTable(db).email, ) as T; } @@ -2767,133 +4007,119 @@ class $$UserProfileTableTableManager extends RootTableManager< )); } -typedef $$UserProfileTableProcessedTableManager = ProcessedTableManager< +typedef $$UserCredentialTableProcessedTableManager = ProcessedTableManager< _$AppDatabase, - $UserProfileTable, - UserProfileData, - $$UserProfileTableFilterComposer, - $$UserProfileTableOrderingComposer, - $$UserProfileTableAnnotationComposer, - $$UserProfileTableCreateCompanionBuilder, - $$UserProfileTableUpdateCompanionBuilder, - (UserProfileData, $$UserProfileTableReferences), - UserProfileData, - PrefetchHooks Function({bool userId})>; -typedef $$UserCredentialTableCreateCompanionBuilder = UserCredentialCompanion - Function({ - Value userId, - required String admno, - Value accessToken, - required String username, - required String email, - required String password, - Value lastLogin, + $UserCredentialTable, + UserCredentialData, + $$UserCredentialTableFilterComposer, + $$UserCredentialTableOrderingComposer, + $$UserCredentialTableAnnotationComposer, + $$UserCredentialTableCreateCompanionBuilder, + $$UserCredentialTableUpdateCompanionBuilder, + (UserCredentialData, $$UserCredentialTableReferences), + UserCredentialData, + PrefetchHooks Function({bool userId, bool username, bool email})>; +typedef $$CourseTableCreateCompanionBuilder = CourseCompanion Function({ + required String unit, + Value user, + required String section, + required String weekDay, + required String campus, + required String room, + required String lecturer, + required String period, + Value color, + Value createdAt, Value rowid, }); -typedef $$UserCredentialTableUpdateCompanionBuilder = UserCredentialCompanion - Function({ - Value userId, - Value admno, - Value accessToken, - Value username, - Value email, - Value password, - Value lastLogin, +typedef $$CourseTableUpdateCompanionBuilder = CourseCompanion Function({ + Value unit, + Value user, + Value section, + Value weekDay, + Value campus, + Value room, + Value lecturer, + Value period, + Value color, + Value createdAt, Value rowid, }); -final class $$UserCredentialTableReferences extends BaseReferences< - _$AppDatabase, $UserCredentialTable, UserCredentialData> { - $$UserCredentialTableReferences( - super.$_db, super.$_table, super.$_typedResult); +final class $$CourseTableReferences + extends BaseReferences<_$AppDatabase, $CourseTable, CourseData> { + $$CourseTableReferences(super.$_db, super.$_table, super.$_typedResult); - static $UserTable _userIdTable(_$AppDatabase db) => db.user - .createAlias($_aliasNameGenerator(db.userCredential.userId, db.user.id)); + static $UserTable _userTable(_$AppDatabase db) => + db.user.createAlias($_aliasNameGenerator(db.course.user, db.user.id)); - $$UserTableProcessedTableManager? get userId { - if ($_item.userId == null) return null; + $$UserTableProcessedTableManager? get user { + if ($_item.user == null) return null; final manager = $$UserTableTableManager($_db, $_db.user) - .filter((f) => f.id($_item.userId!)); - final item = $_typedResult.readTableOrNull(_userIdTable($_db)); + .filter((f) => f.id($_item.user!)); + final item = $_typedResult.readTableOrNull(_userTable($_db)); if (item == null) return manager; return ProcessedTableManager( manager.$state.copyWith(prefetchedData: [item])); } - static $UserTable _usernameTable(_$AppDatabase db) => db.user.createAlias( - $_aliasNameGenerator(db.userCredential.username, db.user.username)); - - $$UserTableProcessedTableManager? get username { - if ($_item.username == null) return null; - final manager = $$UserTableTableManager($_db, $_db.user) - .filter((f) => f.username($_item.username!)); - final item = $_typedResult.readTableOrNull(_usernameTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); - } + static MultiTypedResultKey<$TodoTable, List> _todoRefsTable( + _$AppDatabase db) => + MultiTypedResultKey.fromTable(db.todo, + aliasName: $_aliasNameGenerator(db.course.unit, db.todo.unit)); - static $UserTable _emailTable(_$AppDatabase db) => db.user.createAlias( - $_aliasNameGenerator(db.userCredential.email, db.user.email)); + $$TodoTableProcessedTableManager get todoRefs { + final manager = $$TodoTableTableManager($_db, $_db.todo) + .filter((f) => f.unit.unit($_item.unit)); - $$UserTableProcessedTableManager? get email { - if ($_item.email == null) return null; - final manager = $$UserTableTableManager($_db, $_db.user) - .filter((f) => f.email($_item.email!)); - final item = $_typedResult.readTableOrNull(_emailTable($_db)); - if (item == null) return manager; + final cache = $_typedResult.readTableOrNull(_todoRefsTable($_db)); return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: cache)); } } -class $$UserCredentialTableFilterComposer - extends Composer<_$AppDatabase, $UserCredentialTable> { - $$UserCredentialTableFilterComposer({ +class $$CourseTableFilterComposer + extends Composer<_$AppDatabase, $CourseTable> { + $$CourseTableFilterComposer({ required super.$db, required super.$table, super.joinBuilder, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get admno => $composableBuilder( - column: $table.admno, builder: (column) => ColumnFilters(column)); + ColumnFilters get unit => $composableBuilder( + column: $table.unit, builder: (column) => ColumnFilters(column)); - ColumnFilters get accessToken => $composableBuilder( - column: $table.accessToken, builder: (column) => ColumnFilters(column)); + ColumnFilters get section => $composableBuilder( + column: $table.section, builder: (column) => ColumnFilters(column)); - ColumnFilters get password => $composableBuilder( - column: $table.password, builder: (column) => ColumnFilters(column)); + ColumnFilters get weekDay => $composableBuilder( + column: $table.weekDay, builder: (column) => ColumnFilters(column)); - ColumnFilters get lastLogin => $composableBuilder( - column: $table.lastLogin, builder: (column) => ColumnFilters(column)); + ColumnFilters get campus => $composableBuilder( + column: $table.campus, builder: (column) => ColumnFilters(column)); - $$UserTableFilterComposer get userId { - final $$UserTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: $db.user, - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$UserTableFilterComposer( - $db: $db, - $table: $db.user, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } + ColumnFilters get room => $composableBuilder( + column: $table.room, builder: (column) => ColumnFilters(column)); - $$UserTableFilterComposer get username { + ColumnFilters get lecturer => $composableBuilder( + column: $table.lecturer, builder: (column) => ColumnFilters(column)); + + ColumnFilters get period => $composableBuilder( + column: $table.period, builder: (column) => ColumnFilters(column)); + + ColumnFilters get color => $composableBuilder( + column: $table.color, builder: (column) => ColumnFilters(column)); + + ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, builder: (column) => ColumnFilters(column)); + + $$UserTableFilterComposer get user { final $$UserTableFilterComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.username, + getCurrentColumn: (t) => t.user, referencedTable: $db.user, - getReferencedColumn: (t) => t.username, + getReferencedColumn: (t) => t.id, builder: (joinBuilder, {$addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer}) => @@ -2908,140 +4134,74 @@ class $$UserCredentialTableFilterComposer return composer; } - $$UserTableFilterComposer get email { - final $$UserTableFilterComposer composer = $composerBuilder( + Expression todoRefs( + Expression Function($$TodoTableFilterComposer f) f) { + final $$TodoTableFilterComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.email, - referencedTable: $db.user, - getReferencedColumn: (t) => t.email, + getCurrentColumn: (t) => t.unit, + referencedTable: $db.todo, + getReferencedColumn: (t) => t.unit, builder: (joinBuilder, {$addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer}) => - $$UserTableFilterComposer( + $$TodoTableFilterComposer( $db: $db, - $table: $db.user, + $table: $db.todo, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, joinBuilder: joinBuilder, $removeJoinBuilderFromRootComposer: $removeJoinBuilderFromRootComposer, )); - return composer; + return f(composer); } } -class $$UserCredentialTableOrderingComposer - extends Composer<_$AppDatabase, $UserCredentialTable> { - $$UserCredentialTableOrderingComposer({ +class $$CourseTableOrderingComposer + extends Composer<_$AppDatabase, $CourseTable> { + $$CourseTableOrderingComposer({ required super.$db, required super.$table, super.joinBuilder, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get admno => $composableBuilder( - column: $table.admno, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get accessToken => $composableBuilder( - column: $table.accessToken, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get password => $composableBuilder( - column: $table.password, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get lastLogin => $composableBuilder( - column: $table.lastLogin, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get unit => $composableBuilder( + column: $table.unit, builder: (column) => ColumnOrderings(column)); - $$UserTableOrderingComposer get userId { - final $$UserTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: $db.user, - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$UserTableOrderingComposer( - $db: $db, - $table: $db.user, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } + ColumnOrderings get section => $composableBuilder( + column: $table.section, builder: (column) => ColumnOrderings(column)); - $$UserTableOrderingComposer get username { - final $$UserTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.username, - referencedTable: $db.user, - getReferencedColumn: (t) => t.username, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$UserTableOrderingComposer( - $db: $db, - $table: $db.user, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } + ColumnOrderings get weekDay => $composableBuilder( + column: $table.weekDay, builder: (column) => ColumnOrderings(column)); - $$UserTableOrderingComposer get email { - final $$UserTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.email, - referencedTable: $db.user, - getReferencedColumn: (t) => t.email, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$UserTableOrderingComposer( - $db: $db, - $table: $db.user, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} + ColumnOrderings get campus => $composableBuilder( + column: $table.campus, builder: (column) => ColumnOrderings(column)); -class $$UserCredentialTableAnnotationComposer - extends Composer<_$AppDatabase, $UserCredentialTable> { - $$UserCredentialTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get admno => - $composableBuilder(column: $table.admno, builder: (column) => column); + ColumnOrderings get room => $composableBuilder( + column: $table.room, builder: (column) => ColumnOrderings(column)); - GeneratedColumn get accessToken => $composableBuilder( - column: $table.accessToken, builder: (column) => column); + ColumnOrderings get lecturer => $composableBuilder( + column: $table.lecturer, builder: (column) => ColumnOrderings(column)); - GeneratedColumn get password => - $composableBuilder(column: $table.password, builder: (column) => column); + ColumnOrderings get period => $composableBuilder( + column: $table.period, builder: (column) => ColumnOrderings(column)); - GeneratedColumn get lastLogin => - $composableBuilder(column: $table.lastLogin, builder: (column) => column); + ColumnOrderings get color => $composableBuilder( + column: $table.color, builder: (column) => ColumnOrderings(column)); - $$UserTableAnnotationComposer get userId { - final $$UserTableAnnotationComposer composer = $composerBuilder( + ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, builder: (column) => ColumnOrderings(column)); + + $$UserTableOrderingComposer get user { + final $$UserTableOrderingComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.userId, + getCurrentColumn: (t) => t.user, referencedTable: $db.user, getReferencedColumn: (t) => t.id, builder: (joinBuilder, {$addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer}) => - $$UserTableAnnotationComposer( + $$UserTableOrderingComposer( $db: $db, $table: $db.user, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, @@ -3051,13 +4211,50 @@ class $$UserCredentialTableAnnotationComposer )); return composer; } +} - $$UserTableAnnotationComposer get username { +class $$CourseTableAnnotationComposer + extends Composer<_$AppDatabase, $CourseTable> { + $$CourseTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get unit => + $composableBuilder(column: $table.unit, builder: (column) => column); + + GeneratedColumn get section => + $composableBuilder(column: $table.section, builder: (column) => column); + + GeneratedColumn get weekDay => + $composableBuilder(column: $table.weekDay, builder: (column) => column); + + GeneratedColumn get campus => + $composableBuilder(column: $table.campus, builder: (column) => column); + + GeneratedColumn get room => + $composableBuilder(column: $table.room, builder: (column) => column); + + GeneratedColumn get lecturer => + $composableBuilder(column: $table.lecturer, builder: (column) => column); + + GeneratedColumn get period => + $composableBuilder(column: $table.period, builder: (column) => column); + + GeneratedColumn get color => + $composableBuilder(column: $table.color, builder: (column) => column); + + GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + $$UserTableAnnotationComposer get user { final $$UserTableAnnotationComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.username, + getCurrentColumn: (t) => t.user, referencedTable: $db.user, - getReferencedColumn: (t) => t.username, + getReferencedColumn: (t) => t.id, builder: (joinBuilder, {$addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer}) => @@ -3072,101 +4269,110 @@ class $$UserCredentialTableAnnotationComposer return composer; } - $$UserTableAnnotationComposer get email { - final $$UserTableAnnotationComposer composer = $composerBuilder( + Expression todoRefs( + Expression Function($$TodoTableAnnotationComposer a) f) { + final $$TodoTableAnnotationComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.email, - referencedTable: $db.user, - getReferencedColumn: (t) => t.email, + getCurrentColumn: (t) => t.unit, + referencedTable: $db.todo, + getReferencedColumn: (t) => t.unit, builder: (joinBuilder, {$addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer}) => - $$UserTableAnnotationComposer( + $$TodoTableAnnotationComposer( $db: $db, - $table: $db.user, + $table: $db.todo, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, joinBuilder: joinBuilder, $removeJoinBuilderFromRootComposer: $removeJoinBuilderFromRootComposer, )); - return composer; + return f(composer); } } -class $$UserCredentialTableTableManager extends RootTableManager< +class $$CourseTableTableManager extends RootTableManager< _$AppDatabase, - $UserCredentialTable, - UserCredentialData, - $$UserCredentialTableFilterComposer, - $$UserCredentialTableOrderingComposer, - $$UserCredentialTableAnnotationComposer, - $$UserCredentialTableCreateCompanionBuilder, - $$UserCredentialTableUpdateCompanionBuilder, - (UserCredentialData, $$UserCredentialTableReferences), - UserCredentialData, - PrefetchHooks Function({bool userId, bool username, bool email})> { - $$UserCredentialTableTableManager( - _$AppDatabase db, $UserCredentialTable table) + $CourseTable, + CourseData, + $$CourseTableFilterComposer, + $$CourseTableOrderingComposer, + $$CourseTableAnnotationComposer, + $$CourseTableCreateCompanionBuilder, + $$CourseTableUpdateCompanionBuilder, + (CourseData, $$CourseTableReferences), + CourseData, + PrefetchHooks Function({bool user, bool todoRefs})> { + $$CourseTableTableManager(_$AppDatabase db, $CourseTable table) : super(TableManagerState( db: db, table: table, createFilteringComposer: () => - $$UserCredentialTableFilterComposer($db: db, $table: table), + $$CourseTableFilterComposer($db: db, $table: table), createOrderingComposer: () => - $$UserCredentialTableOrderingComposer($db: db, $table: table), + $$CourseTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => - $$UserCredentialTableAnnotationComposer($db: db, $table: table), + $$CourseTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ - Value userId = const Value.absent(), - Value admno = const Value.absent(), - Value accessToken = const Value.absent(), - Value username = const Value.absent(), - Value email = const Value.absent(), - Value password = const Value.absent(), - Value lastLogin = const Value.absent(), + Value unit = const Value.absent(), + Value user = const Value.absent(), + Value section = const Value.absent(), + Value weekDay = const Value.absent(), + Value campus = const Value.absent(), + Value room = const Value.absent(), + Value lecturer = const Value.absent(), + Value period = const Value.absent(), + Value color = const Value.absent(), + Value createdAt = const Value.absent(), Value rowid = const Value.absent(), }) => - UserCredentialCompanion( - userId: userId, - admno: admno, - accessToken: accessToken, - username: username, - email: email, - password: password, - lastLogin: lastLogin, + CourseCompanion( + unit: unit, + user: user, + section: section, + weekDay: weekDay, + campus: campus, + room: room, + lecturer: lecturer, + period: period, + color: color, + createdAt: createdAt, rowid: rowid, ), createCompanionCallback: ({ - Value userId = const Value.absent(), - required String admno, - Value accessToken = const Value.absent(), - required String username, - required String email, - required String password, - Value lastLogin = const Value.absent(), + required String unit, + Value user = const Value.absent(), + required String section, + required String weekDay, + required String campus, + required String room, + required String lecturer, + required String period, + Value color = const Value.absent(), + Value createdAt = const Value.absent(), Value rowid = const Value.absent(), }) => - UserCredentialCompanion.insert( - userId: userId, - admno: admno, - accessToken: accessToken, - username: username, - email: email, - password: password, - lastLogin: lastLogin, + CourseCompanion.insert( + unit: unit, + user: user, + section: section, + weekDay: weekDay, + campus: campus, + room: room, + lecturer: lecturer, + period: period, + color: color, + createdAt: createdAt, rowid: rowid, ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - $$UserCredentialTableReferences(db, table, e) - )) + .map((e) => + (e.readTable(table), $$CourseTableReferences(db, table, e))) .toList(), - prefetchHooksCallback: ( - {userId = false, username = false, email = false}) { + prefetchHooksCallback: ({user = false, todoRefs = false}) { return PrefetchHooks( db: db, - explicitlyWatchedTables: [], + explicitlyWatchedTables: [if (todoRefs) db.todo], addJoins: < T extends TableManagerState< dynamic, @@ -3180,145 +4386,166 @@ class $$UserCredentialTableTableManager extends RootTableManager< dynamic, dynamic, dynamic>>(state) { - if (userId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.userId, - referencedTable: - $$UserCredentialTableReferences._userIdTable(db), - referencedColumn: - $$UserCredentialTableReferences._userIdTable(db).id, - ) as T; - } - if (username) { - state = state.withJoin( - currentTable: table, - currentColumn: table.username, - referencedTable: - $$UserCredentialTableReferences._usernameTable(db), - referencedColumn: $$UserCredentialTableReferences - ._usernameTable(db) - .username, - ) as T; - } - if (email) { + if (user) { state = state.withJoin( currentTable: table, - currentColumn: table.email, - referencedTable: - $$UserCredentialTableReferences._emailTable(db), - referencedColumn: - $$UserCredentialTableReferences._emailTable(db).email, + currentColumn: table.user, + referencedTable: $$CourseTableReferences._userTable(db), + referencedColumn: $$CourseTableReferences._userTable(db).id, ) as T; } return state; }, getPrefetchedDataCallback: (items) async { - return []; + return [ + if (todoRefs) + await $_getPrefetchedData( + currentTable: table, + referencedTable: + $$CourseTableReferences._todoRefsTable(db), + managerFromTypedResult: (p0) => + $$CourseTableReferences(db, table, p0).todoRefs, + referencedItemsForCurrentItem: (item, + referencedItems) => + referencedItems.where((e) => e.unit == item.unit), + typedResults: items) + ]; }, ); }, )); } -typedef $$UserCredentialTableProcessedTableManager = ProcessedTableManager< +typedef $$CourseTableProcessedTableManager = ProcessedTableManager< _$AppDatabase, - $UserCredentialTable, - UserCredentialData, - $$UserCredentialTableFilterComposer, - $$UserCredentialTableOrderingComposer, - $$UserCredentialTableAnnotationComposer, - $$UserCredentialTableCreateCompanionBuilder, - $$UserCredentialTableUpdateCompanionBuilder, - (UserCredentialData, $$UserCredentialTableReferences), - UserCredentialData, - PrefetchHooks Function({bool userId, bool username, bool email})>; -typedef $$CourseTableCreateCompanionBuilder = CourseCompanion Function({ - required String unit, - Value user, - required String section, - required String weekDay, - required String campus, - required String room, - required String lecturer, - required String period, - Value color, - Value createdAt, - Value rowid, -}); -typedef $$CourseTableUpdateCompanionBuilder = CourseCompanion Function({ - Value unit, - Value user, - Value section, - Value weekDay, - Value campus, - Value room, - Value lecturer, - Value period, + $CourseTable, + CourseData, + $$CourseTableFilterComposer, + $$CourseTableOrderingComposer, + $$CourseTableAnnotationComposer, + $$CourseTableCreateCompanionBuilder, + $$CourseTableUpdateCompanionBuilder, + (CourseData, $$CourseTableReferences), + CourseData, + PrefetchHooks Function({bool user, bool todoRefs})>; +typedef $$TodoTableCreateCompanionBuilder = TodoCompanion Function({ + Value id, + required String userId, + Value unit, + required String title, + required String body, + Value subtasks, Value color, - Value createdAt, - Value rowid, + Value notify, + Value complete, + Value autocomplete, + Value notifyAt, + Value duedate, + Value dateCreated, + Value dateModified, + Value completedAt, +}); +typedef $$TodoTableUpdateCompanionBuilder = TodoCompanion Function({ + Value id, + Value userId, + Value unit, + Value title, + Value body, + Value subtasks, + Value color, + Value notify, + Value complete, + Value autocomplete, + Value notifyAt, + Value duedate, + Value dateCreated, + Value dateModified, + Value completedAt, }); -final class $$CourseTableReferences - extends BaseReferences<_$AppDatabase, $CourseTable, CourseData> { - $$CourseTableReferences(super.$_db, super.$_table, super.$_typedResult); +final class $$TodoTableReferences + extends BaseReferences<_$AppDatabase, $TodoTable, TodoData> { + $$TodoTableReferences(super.$_db, super.$_table, super.$_typedResult); - static $UserTable _userTable(_$AppDatabase db) => - db.user.createAlias($_aliasNameGenerator(db.course.user, db.user.id)); + static $UserTable _userIdTable(_$AppDatabase db) => + db.user.createAlias($_aliasNameGenerator(db.todo.userId, db.user.id)); - $$UserTableProcessedTableManager? get user { - if ($_item.user == null) return null; + $$UserTableProcessedTableManager? get userId { + if ($_item.userId == null) return null; final manager = $$UserTableTableManager($_db, $_db.user) - .filter((f) => f.id($_item.user!)); - final item = $_typedResult.readTableOrNull(_userTable($_db)); + .filter((f) => f.id($_item.userId!)); + final item = $_typedResult.readTableOrNull(_userIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item])); + } + + static $CourseTable _unitTable(_$AppDatabase db) => + db.course.createAlias($_aliasNameGenerator(db.todo.unit, db.course.unit)); + + $$CourseTableProcessedTableManager? get unit { + if ($_item.unit == null) return null; + final manager = $$CourseTableTableManager($_db, $_db.course) + .filter((f) => f.unit($_item.unit!)); + final item = $_typedResult.readTableOrNull(_unitTable($_db)); if (item == null) return manager; return ProcessedTableManager( manager.$state.copyWith(prefetchedData: [item])); } } -class $$CourseTableFilterComposer - extends Composer<_$AppDatabase, $CourseTable> { - $$CourseTableFilterComposer({ +class $$TodoTableFilterComposer extends Composer<_$AppDatabase, $TodoTable> { + $$TodoTableFilterComposer({ required super.$db, required super.$table, super.joinBuilder, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get unit => $composableBuilder( - column: $table.unit, builder: (column) => ColumnFilters(column)); + ColumnFilters get id => $composableBuilder( + column: $table.id, builder: (column) => ColumnFilters(column)); - ColumnFilters get section => $composableBuilder( - column: $table.section, builder: (column) => ColumnFilters(column)); + ColumnFilters get title => $composableBuilder( + column: $table.title, builder: (column) => ColumnFilters(column)); - ColumnFilters get weekDay => $composableBuilder( - column: $table.weekDay, builder: (column) => ColumnFilters(column)); + ColumnFilters get body => $composableBuilder( + column: $table.body, builder: (column) => ColumnFilters(column)); - ColumnFilters get campus => $composableBuilder( - column: $table.campus, builder: (column) => ColumnFilters(column)); + ColumnFilters get subtasks => $composableBuilder( + column: $table.subtasks, builder: (column) => ColumnFilters(column)); - ColumnFilters get room => $composableBuilder( - column: $table.room, builder: (column) => ColumnFilters(column)); + ColumnFilters get color => $composableBuilder( + column: $table.color, builder: (column) => ColumnFilters(column)); - ColumnFilters get lecturer => $composableBuilder( - column: $table.lecturer, builder: (column) => ColumnFilters(column)); + ColumnFilters get notify => $composableBuilder( + column: $table.notify, builder: (column) => ColumnFilters(column)); - ColumnFilters get period => $composableBuilder( - column: $table.period, builder: (column) => ColumnFilters(column)); + ColumnFilters get complete => $composableBuilder( + column: $table.complete, builder: (column) => ColumnFilters(column)); - ColumnFilters get color => $composableBuilder( - column: $table.color, builder: (column) => ColumnFilters(column)); + ColumnFilters get autocomplete => $composableBuilder( + column: $table.autocomplete, builder: (column) => ColumnFilters(column)); - ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnFilters(column)); + ColumnFilters get notifyAt => $composableBuilder( + column: $table.notifyAt, builder: (column) => ColumnFilters(column)); - $$UserTableFilterComposer get user { + ColumnFilters get duedate => $composableBuilder( + column: $table.duedate, builder: (column) => ColumnFilters(column)); + + ColumnFilters get dateCreated => $composableBuilder( + column: $table.dateCreated, builder: (column) => ColumnFilters(column)); + + ColumnFilters get dateModified => $composableBuilder( + column: $table.dateModified, builder: (column) => ColumnFilters(column)); + + ColumnFilters get completedAt => $composableBuilder( + column: $table.completedAt, builder: (column) => ColumnFilters(column)); + + $$UserTableFilterComposer get userId { final $$UserTableFilterComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.user, + getCurrentColumn: (t) => t.userId, referencedTable: $db.user, getReferencedColumn: (t) => t.id, builder: (joinBuilder, @@ -3334,48 +4561,81 @@ class $$CourseTableFilterComposer )); return composer; } + + $$CourseTableFilterComposer get unit { + final $$CourseTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.unit, + referencedTable: $db.course, + getReferencedColumn: (t) => t.unit, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$CourseTableFilterComposer( + $db: $db, + $table: $db.course, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } } -class $$CourseTableOrderingComposer - extends Composer<_$AppDatabase, $CourseTable> { - $$CourseTableOrderingComposer({ +class $$TodoTableOrderingComposer extends Composer<_$AppDatabase, $TodoTable> { + $$TodoTableOrderingComposer({ required super.$db, required super.$table, super.joinBuilder, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get unit => $composableBuilder( - column: $table.unit, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get id => $composableBuilder( + column: $table.id, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get section => $composableBuilder( - column: $table.section, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get title => $composableBuilder( + column: $table.title, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get weekDay => $composableBuilder( - column: $table.weekDay, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get body => $composableBuilder( + column: $table.body, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get campus => $composableBuilder( - column: $table.campus, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get subtasks => $composableBuilder( + column: $table.subtasks, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get room => $composableBuilder( - column: $table.room, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get color => $composableBuilder( + column: $table.color, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get lecturer => $composableBuilder( - column: $table.lecturer, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get notify => $composableBuilder( + column: $table.notify, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get period => $composableBuilder( - column: $table.period, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get complete => $composableBuilder( + column: $table.complete, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get color => $composableBuilder( - column: $table.color, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get autocomplete => $composableBuilder( + column: $table.autocomplete, + builder: (column) => ColumnOrderings(column)); - ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnOrderings(column)); + ColumnOrderings get notifyAt => $composableBuilder( + column: $table.notifyAt, builder: (column) => ColumnOrderings(column)); - $$UserTableOrderingComposer get user { + ColumnOrderings get duedate => $composableBuilder( + column: $table.duedate, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get dateCreated => $composableBuilder( + column: $table.dateCreated, builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get dateModified => $composableBuilder( + column: $table.dateModified, + builder: (column) => ColumnOrderings(column)); + + ColumnOrderings get completedAt => $composableBuilder( + column: $table.completedAt, builder: (column) => ColumnOrderings(column)); + + $$UserTableOrderingComposer get userId { final $$UserTableOrderingComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.user, + getCurrentColumn: (t) => t.userId, referencedTable: $db.user, getReferencedColumn: (t) => t.id, builder: (joinBuilder, @@ -3391,48 +4651,80 @@ class $$CourseTableOrderingComposer )); return composer; } + + $$CourseTableOrderingComposer get unit { + final $$CourseTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.unit, + referencedTable: $db.course, + getReferencedColumn: (t) => t.unit, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$CourseTableOrderingComposer( + $db: $db, + $table: $db.course, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } } -class $$CourseTableAnnotationComposer - extends Composer<_$AppDatabase, $CourseTable> { - $$CourseTableAnnotationComposer({ +class $$TodoTableAnnotationComposer + extends Composer<_$AppDatabase, $TodoTable> { + $$TodoTableAnnotationComposer({ required super.$db, required super.$table, super.joinBuilder, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get unit => - $composableBuilder(column: $table.unit, builder: (column) => column); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); - GeneratedColumn get section => - $composableBuilder(column: $table.section, builder: (column) => column); + GeneratedColumn get title => + $composableBuilder(column: $table.title, builder: (column) => column); - GeneratedColumn get weekDay => - $composableBuilder(column: $table.weekDay, builder: (column) => column); + GeneratedColumn get body => + $composableBuilder(column: $table.body, builder: (column) => column); - GeneratedColumn get campus => - $composableBuilder(column: $table.campus, builder: (column) => column); + GeneratedColumn get subtasks => + $composableBuilder(column: $table.subtasks, builder: (column) => column); - GeneratedColumn get room => - $composableBuilder(column: $table.room, builder: (column) => column); + GeneratedColumn get color => + $composableBuilder(column: $table.color, builder: (column) => column); - GeneratedColumn get lecturer => - $composableBuilder(column: $table.lecturer, builder: (column) => column); + GeneratedColumn get notify => + $composableBuilder(column: $table.notify, builder: (column) => column); - GeneratedColumn get period => - $composableBuilder(column: $table.period, builder: (column) => column); + GeneratedColumn get complete => + $composableBuilder(column: $table.complete, builder: (column) => column); - GeneratedColumn get color => - $composableBuilder(column: $table.color, builder: (column) => column); + GeneratedColumn get autocomplete => $composableBuilder( + column: $table.autocomplete, builder: (column) => column); - GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); + GeneratedColumn get notifyAt => + $composableBuilder(column: $table.notifyAt, builder: (column) => column); - $$UserTableAnnotationComposer get user { + GeneratedColumn get duedate => + $composableBuilder(column: $table.duedate, builder: (column) => column); + + GeneratedColumn get dateCreated => $composableBuilder( + column: $table.dateCreated, builder: (column) => column); + + GeneratedColumn get dateModified => $composableBuilder( + column: $table.dateModified, builder: (column) => column); + + GeneratedColumn get completedAt => $composableBuilder( + column: $table.completedAt, builder: (column) => column); + + $$UserTableAnnotationComposer get userId { final $$UserTableAnnotationComposer composer = $composerBuilder( composer: this, - getCurrentColumn: (t) => t.user, + getCurrentColumn: (t) => t.userId, referencedTable: $db.user, getReferencedColumn: (t) => t.id, builder: (joinBuilder, @@ -3448,87 +4740,123 @@ class $$CourseTableAnnotationComposer )); return composer; } + + $$CourseTableAnnotationComposer get unit { + final $$CourseTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.unit, + referencedTable: $db.course, + getReferencedColumn: (t) => t.unit, + builder: (joinBuilder, + {$addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer}) => + $$CourseTableAnnotationComposer( + $db: $db, + $table: $db.course, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + )); + return composer; + } } -class $$CourseTableTableManager extends RootTableManager< +class $$TodoTableTableManager extends RootTableManager< _$AppDatabase, - $CourseTable, - CourseData, - $$CourseTableFilterComposer, - $$CourseTableOrderingComposer, - $$CourseTableAnnotationComposer, - $$CourseTableCreateCompanionBuilder, - $$CourseTableUpdateCompanionBuilder, - (CourseData, $$CourseTableReferences), - CourseData, - PrefetchHooks Function({bool user})> { - $$CourseTableTableManager(_$AppDatabase db, $CourseTable table) + $TodoTable, + TodoData, + $$TodoTableFilterComposer, + $$TodoTableOrderingComposer, + $$TodoTableAnnotationComposer, + $$TodoTableCreateCompanionBuilder, + $$TodoTableUpdateCompanionBuilder, + (TodoData, $$TodoTableReferences), + TodoData, + PrefetchHooks Function({bool userId, bool unit})> { + $$TodoTableTableManager(_$AppDatabase db, $TodoTable table) : super(TableManagerState( db: db, table: table, createFilteringComposer: () => - $$CourseTableFilterComposer($db: db, $table: table), + $$TodoTableFilterComposer($db: db, $table: table), createOrderingComposer: () => - $$CourseTableOrderingComposer($db: db, $table: table), + $$TodoTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => - $$CourseTableAnnotationComposer($db: db, $table: table), + $$TodoTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ - Value unit = const Value.absent(), - Value user = const Value.absent(), - Value section = const Value.absent(), - Value weekDay = const Value.absent(), - Value campus = const Value.absent(), - Value room = const Value.absent(), - Value lecturer = const Value.absent(), - Value period = const Value.absent(), + Value id = const Value.absent(), + Value userId = const Value.absent(), + Value unit = const Value.absent(), + Value title = const Value.absent(), + Value body = const Value.absent(), + Value subtasks = const Value.absent(), Value color = const Value.absent(), - Value createdAt = const Value.absent(), - Value rowid = const Value.absent(), + Value notify = const Value.absent(), + Value complete = const Value.absent(), + Value autocomplete = const Value.absent(), + Value notifyAt = const Value.absent(), + Value duedate = const Value.absent(), + Value dateCreated = const Value.absent(), + Value dateModified = const Value.absent(), + Value completedAt = const Value.absent(), }) => - CourseCompanion( + TodoCompanion( + id: id, + userId: userId, unit: unit, - user: user, - section: section, - weekDay: weekDay, - campus: campus, - room: room, - lecturer: lecturer, - period: period, + title: title, + body: body, + subtasks: subtasks, color: color, - createdAt: createdAt, - rowid: rowid, + notify: notify, + complete: complete, + autocomplete: autocomplete, + notifyAt: notifyAt, + duedate: duedate, + dateCreated: dateCreated, + dateModified: dateModified, + completedAt: completedAt, ), createCompanionCallback: ({ - required String unit, - Value user = const Value.absent(), - required String section, - required String weekDay, - required String campus, - required String room, - required String lecturer, - required String period, + Value id = const Value.absent(), + required String userId, + Value unit = const Value.absent(), + required String title, + required String body, + Value subtasks = const Value.absent(), Value color = const Value.absent(), - Value createdAt = const Value.absent(), - Value rowid = const Value.absent(), + Value notify = const Value.absent(), + Value complete = const Value.absent(), + Value autocomplete = const Value.absent(), + Value notifyAt = const Value.absent(), + Value duedate = const Value.absent(), + Value dateCreated = const Value.absent(), + Value dateModified = const Value.absent(), + Value completedAt = const Value.absent(), }) => - CourseCompanion.insert( + TodoCompanion.insert( + id: id, + userId: userId, unit: unit, - user: user, - section: section, - weekDay: weekDay, - campus: campus, - room: room, - lecturer: lecturer, - period: period, + title: title, + body: body, + subtasks: subtasks, color: color, - createdAt: createdAt, - rowid: rowid, + notify: notify, + complete: complete, + autocomplete: autocomplete, + notifyAt: notifyAt, + duedate: duedate, + dateCreated: dateCreated, + dateModified: dateModified, + completedAt: completedAt, ), withReferenceMapper: (p0) => p0 .map((e) => - (e.readTable(table), $$CourseTableReferences(db, table, e))) + (e.readTable(table), $$TodoTableReferences(db, table, e))) .toList(), - prefetchHooksCallback: ({user = false}) { + prefetchHooksCallback: ({userId = false, unit = false}) { return PrefetchHooks( db: db, explicitlyWatchedTables: [], @@ -3545,12 +4873,20 @@ class $$CourseTableTableManager extends RootTableManager< dynamic, dynamic, dynamic>>(state) { - if (user) { + if (userId) { state = state.withJoin( currentTable: table, - currentColumn: table.user, - referencedTable: $$CourseTableReferences._userTable(db), - referencedColumn: $$CourseTableReferences._userTable(db).id, + currentColumn: table.userId, + referencedTable: $$TodoTableReferences._userIdTable(db), + referencedColumn: $$TodoTableReferences._userIdTable(db).id, + ) as T; + } + if (unit) { + state = state.withJoin( + currentTable: table, + currentColumn: table.unit, + referencedTable: $$TodoTableReferences._unitTable(db), + referencedColumn: $$TodoTableReferences._unitTable(db).unit, ) as T; } @@ -3564,18 +4900,18 @@ class $$CourseTableTableManager extends RootTableManager< )); } -typedef $$CourseTableProcessedTableManager = ProcessedTableManager< +typedef $$TodoTableProcessedTableManager = ProcessedTableManager< _$AppDatabase, - $CourseTable, - CourseData, - $$CourseTableFilterComposer, - $$CourseTableOrderingComposer, - $$CourseTableAnnotationComposer, - $$CourseTableCreateCompanionBuilder, - $$CourseTableUpdateCompanionBuilder, - (CourseData, $$CourseTableReferences), - CourseData, - PrefetchHooks Function({bool user})>; + $TodoTable, + TodoData, + $$TodoTableFilterComposer, + $$TodoTableOrderingComposer, + $$TodoTableAnnotationComposer, + $$TodoTableCreateCompanionBuilder, + $$TodoTableUpdateCompanionBuilder, + (TodoData, $$TodoTableReferences), + TodoData, + PrefetchHooks Function({bool userId, bool unit})>; class $AppDatabaseManager { final _$AppDatabase _db; @@ -3587,4 +4923,5 @@ class $AppDatabaseManager { $$UserCredentialTableTableManager(_db, _db.userCredential); $$CourseTableTableManager get course => $$CourseTableTableManager(_db, _db.course); + $$TodoTableTableManager get todo => $$TodoTableTableManager(_db, _db.todo); } diff --git a/lib/features/essentials/views/essentials_mobile_page.dart b/lib/features/essentials/views/essentials_mobile_page.dart index 2bd8cb1..6c2291d 100644 --- a/lib/features/essentials/views/essentials_mobile_page.dart +++ b/lib/features/essentials/views/essentials_mobile_page.dart @@ -1,4 +1,6 @@ +import 'package:academia/utils/router/router.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:icons_plus/icons_plus.dart'; import 'package:sliver_tools/sliver_tools.dart'; @@ -40,22 +42,26 @@ class _EssentialsMobilePageState extends State { SliverPadding( padding: const EdgeInsets.all(12), sliver: MultiSliver( - children: const [ + children: [ Card( elevation: 0, - margin: EdgeInsets.only(bottom: 2), - shape: RoundedRectangleBorder( + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(12), ), ), child: ListTile( - leading: Icon(Bootstrap.bell), - title: Text("Todos & Assigments"), - subtitle: Text("Keep track of your assignments and todos"), + onTap: () { + context.pushNamed(AcademiaRouter.todo); + }, + leading: const Icon(Bootstrap.bell), + title: const Text("Todos & Assigments"), + subtitle: + const Text("Keep track of your assignments and todos"), ), ), - Card( + const Card( elevation: 0, margin: EdgeInsets.only(bottom: 2), shape: RoundedRectangleBorder( @@ -69,7 +75,7 @@ class _EssentialsMobilePageState extends State { subtitle: Text("Psst.. Never miss an exam"), ), ), - Card( + const Card( elevation: 0, margin: EdgeInsets.only(bottom: 2), shape: RoundedRectangleBorder( @@ -84,7 +90,7 @@ class _EssentialsMobilePageState extends State { ), ), - Card( + const Card( elevation: 0, margin: EdgeInsets.only(bottom: 2), shape: RoundedRectangleBorder( @@ -98,7 +104,7 @@ class _EssentialsMobilePageState extends State { subtitle: Text("Boring notes? We'll help you revise"), ), ), - Card( + const Card( elevation: 0, margin: EdgeInsets.only(bottom: 2), shape: RoundedRectangleBorder( @@ -116,8 +122,8 @@ class _EssentialsMobilePageState extends State { ), //Page Break for student performance - SizedBox(height: 18), - Card( + const SizedBox(height: 18), + const Card( margin: EdgeInsets.only(bottom: 2), elevation: 0, shape: RoundedRectangleBorder( @@ -131,7 +137,7 @@ class _EssentialsMobilePageState extends State { subtitle: Text("Keep track of your assignments and todos"), ), ), - Card( + const Card( elevation: 0, margin: EdgeInsets.only(bottom: 2), shape: RoundedRectangleBorder( @@ -146,7 +152,7 @@ class _EssentialsMobilePageState extends State { ), ), - Card( + const Card( elevation: 0, margin: EdgeInsets.only(bottom: 2), shape: RoundedRectangleBorder( diff --git a/lib/features/features.dart b/lib/features/features.dart index 5c34560..def19d8 100644 --- a/lib/features/features.dart +++ b/lib/features/features.dart @@ -3,3 +3,4 @@ export 'auth/auth.dart'; export 'home/home.dart'; export 'profile/profile.dart'; export 'courses/courses.dart'; +export 'todo/todo.dart'; diff --git a/lib/features/todo/models/todo.dart b/lib/features/todo/models/todo.dart new file mode 100644 index 0000000..80b0c08 --- /dev/null +++ b/lib/features/todo/models/todo.dart @@ -0,0 +1,27 @@ +import 'package:academia/features/auth/models/user.dart'; +import 'package:academia/features/courses/models/course.dart'; +import 'package:drift/drift.dart'; + +class Todo extends Table { + IntColumn get id => integer().autoIncrement()(); + TextColumn get userId => text().references(User, #id)(); + TextColumn get unit => text().references(Course, #unit).nullable()(); + TextColumn get title => text()(); + TextColumn get body => text()(); + TextColumn get subtasks => text().nullable()(); + IntColumn get color => integer().nullable()(); + BoolColumn get notify => boolean().withDefault(const Constant(false))(); + BoolColumn get complete => boolean().withDefault(const Constant(false))(); + BoolColumn get autocomplete => boolean().withDefault(const Constant(false))(); + DateTimeColumn get notifyAt => dateTime().nullable()(); + DateTimeColumn get duedate => dateTime().nullable()(); + DateTimeColumn get dateCreated => + dateTime().withDefault(Constant(DateTime.now()))(); + DateTimeColumn get dateModified => + dateTime().withDefault(Constant(DateTime.now()))(); + DateTimeColumn get completedAt => + dateTime().withDefault(Constant(DateTime.now()))(); + + @override + Set>? get primaryKey => {id}; +} diff --git a/lib/features/todo/todo.dart b/lib/features/todo/todo.dart new file mode 100644 index 0000000..3f3c55b --- /dev/null +++ b/lib/features/todo/todo.dart @@ -0,0 +1,2 @@ +export 'views/todo_page.dart'; +export 'views/todo_view_page.dart'; diff --git a/lib/features/todo/views/todo_page.dart b/lib/features/todo/views/todo_page.dart new file mode 100644 index 0000000..a8700bc --- /dev/null +++ b/lib/features/todo/views/todo_page.dart @@ -0,0 +1,37 @@ +import 'package:academia/utils/router/router.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:icons_plus/icons_plus.dart'; + +class TodoPage extends StatelessWidget { + const TodoPage({super.key}); + @override + Widget build(BuildContext context) { + return Scaffold( + body: const CustomScrollView( + slivers: [ + SliverAppBar( + pinned: true, + snap: true, + floating: true, + expandedHeight: 128, + flexibleSpace: FlexibleSpaceBar( + title: Text("Todos & Assignments"), + ), + ), + SliverFillRemaining( + child: Center( + child: Text("You have no todos added yet"), + ), + ) + ], + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + context.pushNamed(AcademiaRouter.todoView); + }, + child: const Icon(Bootstrap.plus_circle_dotted), + ), + ); + } +} diff --git a/lib/features/todo/views/todo_view_page.dart b/lib/features/todo/views/todo_view_page.dart new file mode 100644 index 0000000..7f290c0 --- /dev/null +++ b/lib/features/todo/views/todo_view_page.dart @@ -0,0 +1,364 @@ +import 'dart:convert'; + +import 'package:academia/database/database.dart'; +import 'package:drift/drift.dart' as drift; +import 'package:flex_color_picker/flex_color_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:go_router/go_router.dart'; +import 'package:icons_plus/icons_plus.dart'; +import 'package:sliver_tools/sliver_tools.dart'; + +enum TodoEditState { + create, + modify, +} + +class TodoViewPage extends StatefulWidget { + const TodoViewPage({super.key, this.todoData}); + final TodoData? todoData; + + @override + State createState() => _TodoViewPageState(); +} + +class _TodoViewPageState extends State { + late TodoEditState editState = + widget.todoData == null ? TodoEditState.create : TodoEditState.modify; + + late TodoData todo; + final TextEditingController _titleController = TextEditingController(); + final TextEditingController _descriptionController = TextEditingController(); + Map? subtasks; + + @override + void initState() { + todo = widget.todoData == null + ? TodoData( + userId: "", + title: "", + body: "", + notify: false, + complete: false, + autocomplete: false, + dateCreated: DateTime.now(), + dateModified: DateTime.now(), + completedAt: DateTime.now()) + : widget.todoData!; + + _titleController.text = todo.title; + _descriptionController.text = todo.body; + subtasks = todo.subtasks == null ? {} : json.decode(todo.subtasks!); + + super.initState(); + } + + void _showAddSubtaskDialog() { + final TextEditingController subTaskController = TextEditingController(); + showModalBottomSheet( + context: context, + builder: (context) => Container( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + spacing: 16, + children: [ + Text( + "Please add a subtask to your todo", + style: Theme.of(context).textTheme.headlineSmall, + ), + TextFormField( + controller: subTaskController, + decoration: InputDecoration( + hintText: "Remember to...", + hintStyle: Theme.of(context).textTheme.titleMedium, + border: const OutlineInputBorder( + borderSide: BorderSide(width: 2), + ), + ), + ), + FilledButton( + onPressed: () { + setState(() { + subtasks![subTaskController.text.capitalize] = false; + }); + context.pop(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Subtask sucessfully added to task"), + ), + ); + }, + child: const Text( + "Add sub task", + ), + ) + ], + ), + ), + ); + } + + Future _configureDueDate() async { + final picked = await showDatePicker( + context: context, + firstDate: DateTime.now(), + lastDate: DateTime.now().add( + const Duration(days: 365), + ), + ); + if (picked != null) { + setState(() { + todo = todo.copyWith(duedate: drift.Value(picked)); + }); + } + } + + Future _configureNotificationTime() async { + final picked = await showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ); + + if (picked != null) { + setState(() { + todo = todo.copyWith( + notifyAt: drift.Value( + DateTime.now().copyWith( + hour: picked.hour, + minute: picked.minute, + ), + ), + ); + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: CustomScrollView( + slivers: [ + SliverAppBar( + floating: true, + snap: true, + pinned: true, + expandedHeight: 128, + flexibleSpace: FlexibleSpaceBar( + title: Text( + editState == TodoEditState.create + ? "Create a todo item" + : "Modify todo", + ), + ), + ), + SliverPadding( + padding: const EdgeInsets.all(12), + sliver: MultiSliver( + children: [ + SliverPinnedHeader( + child: Container( + color: Theme.of(context).colorScheme.surface, + child: TextFormField( + controller: _titleController, + maxLength: 30, + decoration: InputDecoration( + hintText: "Tap to add a title", + hintStyle: + Theme.of(context).textTheme.headlineSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + border: const OutlineInputBorder( + borderSide: BorderSide.none, + ), + ), + ), + ), + ), + SliverPinnedHeader( + child: Text( + "Add some sub-tasks 🫠", + style: Theme.of(context).textTheme.headlineSmall, + ), + ), + SliverList.builder( + itemBuilder: (context, index) { + if (index == subtasks!.keys.length) { + return TextButton.icon( + onPressed: _showAddSubtaskDialog, + icon: const Icon(Bootstrap.journal_plus), + label: const Text("Add a subtask"), + ); + } + var item = subtasks!.entries.elementAt(index); + return CheckboxListTile( + onChanged: (value) { + setState(() { + subtasks![item.key] = value!; + }); + }, + value: item.value, + title: Text(item.key), + ); + }, + itemCount: subtasks!.keys.length + 1, + ), + const SizedBox(height: 16), + SliverPinnedHeader( + child: Container( + margin: const EdgeInsets.symmetric(vertical: 16), + color: Theme.of(context).colorScheme.surface, + child: Text( + "More task configurations", + style: Theme.of(context).textTheme.headlineSmall, + ), + ), + ), + SliverToBoxAdapter( + child: Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), + ), + child: ListTile( + onTap: () async { + final color = await showColorPickerDialog( + context, + Theme.of(context).colorScheme.primary, + ); + + setState(() { + todo = todo.copyWith(color: drift.Value(color.value)); + }); + }, + title: const Text("Pick a color for this task"), + leading: CircleAvatar( + backgroundColor: + todo.color == null ? null : Color(todo.color!), + ), + ), + ), + ), + SliverToBoxAdapter( + child: Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.zero, + ), + child: ListTile( + leading: const Icon(Bootstrap.calendar_day), + enableFeedback: true, + onTap: () async { + await _configureDueDate(); + await HapticFeedback.heavyImpact(); + }, + title: const Text("Pick due date for task"), + subtitle: Text( + todo.duedate == null + ? "Not configured yet" + : todo.duedate.toString(), + ), + ), + ), + ), + SliverToBoxAdapter( + child: Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.zero, + ), + child: SwitchListTile( + enableFeedback: true, + onChanged: (val) { + HapticFeedback.heavyImpact().then((value) { + setState(() { + todo = todo.copyWith(complete: val); + }); + }); + }, + value: todo.complete, + title: const Text("Mark task as complete"), + ), + ), + ), + SliverToBoxAdapter( + child: Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.zero, + ), + child: SwitchListTile( + enableFeedback: true, + onChanged: (val) { + HapticFeedback.heavyImpact().then((value) { + setState(() { + todo = todo.copyWith(autocomplete: val); + }); + }); + }, + value: todo.autocomplete, + title: const Text("Automatically close this task"), + ), + ), + ), + SliverToBoxAdapter( + child: Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.zero, + ), + child: SwitchListTile( + enableFeedback: false, + onChanged: (val) { + HapticFeedback.heavyImpact().then((value) { + setState(() { + todo = todo.copyWith(notify: val); + }); + }); + }, + value: todo.notify, + title: const Text("Notify me on this task"), + ), + ), + ), + SliverToBoxAdapter( + child: Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12), + ), + ), + child: ListTile( + enabled: todo.notify, + onTap: () async { + await _configureNotificationTime(); + }, + title: const Text("Configure task's notification"), + leading: const Icon(Bootstrap.bell), + subtitle: const Text( + "Notifications will automatically repeat on the specified time until completed or deleted", + ), + ), + ), + ), + ], + ), + ), + ], + ), + floatingActionButton: FloatingActionButton( + onPressed: () {}, + child: const Icon(Bootstrap.check), + ), + ); + } +} diff --git a/lib/utils/router/router.dart b/lib/utils/router/router.dart index fe56ce0..7f7efa0 100644 --- a/lib/utils/router/router.dart +++ b/lib/utils/router/router.dart @@ -16,6 +16,8 @@ class AcademiaRouter { static const String userSelection = "user-selection"; static const String onboarding = "onboarding"; static const String courseView = "course-view"; + static const String todo = "todo"; + static const String todoView = "todo-view"; static final GoRouter _router = GoRouter( initialLocation: "/", @@ -52,12 +54,26 @@ class AcademiaRouter { builder: (context, state) => const UserSelectionPage(), ), GoRoute( - name: courseView, - path: "/$courseView", - builder: (context, state) { - final CourseData course = state.extra as CourseData; - return CourseMobileViewPage(course: course); - }), + name: todo, + path: "/$todo", + builder: (context, state) => const TodoPage(), + ), + GoRoute( + name: todoView, + path: "/$todoView", + builder: (context, state) { + TodoData? todo = state.extra as TodoData?; + return TodoViewPage(todoData: todo); + }, + ), + GoRoute( + name: courseView, + path: "/$courseView", + builder: (context, state) { + final CourseData course = state.extra as CourseData; + return CourseMobileViewPage(course: course); + }, + ), ], ); } From 6beb84ad424a3e64e304c7bdf7d066d6321e2a8f Mon Sep 17 00:00:00 2001 From: Erick Date: Mon, 30 Dec 2024 14:17:37 +0300 Subject: [PATCH 24/24] feat: course linking to tasks --- lib/database/database.g.dart | 34 ++++++------ lib/features/todo/models/todo.dart | 2 +- lib/features/todo/views/todo_view_page.dart | 60 +++++++++++++++++++++ 3 files changed, 79 insertions(+), 17 deletions(-) diff --git a/lib/database/database.g.dart b/lib/database/database.g.dart index 643d699..21ecb0c 100644 --- a/lib/database/database.g.dart +++ b/lib/database/database.g.dart @@ -2009,7 +2009,7 @@ class $TodoTable extends Todo with TableInfo<$TodoTable, TodoData> { static const VerificationMeta _idMeta = const VerificationMeta('id'); @override late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, + 'id', aliasedName, true, hasAutoIncrement: true, type: DriftSqlType.int, requiredDuringInsert: false, @@ -2228,7 +2228,7 @@ class $TodoTable extends Todo with TableInfo<$TodoTable, TodoData> { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return TodoData( id: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}id'])!, + .read(DriftSqlType.int, data['${effectivePrefix}id']), userId: attachedDatabase.typeMapping .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, unit: attachedDatabase.typeMapping @@ -2267,7 +2267,7 @@ class $TodoTable extends Todo with TableInfo<$TodoTable, TodoData> { } class TodoData extends DataClass implements Insertable { - final int id; + final int? id; final String userId; final String? unit; final String title; @@ -2283,7 +2283,7 @@ class TodoData extends DataClass implements Insertable { final DateTime dateModified; final DateTime completedAt; const TodoData( - {required this.id, + {this.id, required this.userId, this.unit, required this.title, @@ -2301,7 +2301,9 @@ class TodoData extends DataClass implements Insertable { @override Map toColumns(bool nullToAbsent) { final map = {}; - map['id'] = Variable(id); + if (!nullToAbsent || id != null) { + map['id'] = Variable(id); + } map['user_id'] = Variable(userId); if (!nullToAbsent || unit != null) { map['unit'] = Variable(unit); @@ -2331,7 +2333,7 @@ class TodoData extends DataClass implements Insertable { TodoCompanion toCompanion(bool nullToAbsent) { return TodoCompanion( - id: Value(id), + id: id == null && nullToAbsent ? const Value.absent() : Value(id), userId: Value(userId), unit: unit == null && nullToAbsent ? const Value.absent() : Value(unit), title: Value(title), @@ -2360,7 +2362,7 @@ class TodoData extends DataClass implements Insertable { {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return TodoData( - id: serializer.fromJson(json['id']), + id: serializer.fromJson(json['id']), userId: serializer.fromJson(json['userId']), unit: serializer.fromJson(json['unit']), title: serializer.fromJson(json['title']), @@ -2381,7 +2383,7 @@ class TodoData extends DataClass implements Insertable { Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { - 'id': serializer.toJson(id), + 'id': serializer.toJson(id), 'userId': serializer.toJson(userId), 'unit': serializer.toJson(unit), 'title': serializer.toJson(title), @@ -2400,7 +2402,7 @@ class TodoData extends DataClass implements Insertable { } TodoData copyWith( - {int? id, + {Value id = const Value.absent(), String? userId, Value unit = const Value.absent(), String? title, @@ -2416,7 +2418,7 @@ class TodoData extends DataClass implements Insertable { DateTime? dateModified, DateTime? completedAt}) => TodoData( - id: id ?? this.id, + id: id.present ? id.value : this.id, userId: userId ?? this.userId, unit: unit.present ? unit.value : this.unit, title: title ?? this.title, @@ -2519,7 +2521,7 @@ class TodoData extends DataClass implements Insertable { } class TodoCompanion extends UpdateCompanion { - final Value id; + final Value id; final Value userId; final Value unit; final Value title; @@ -2607,7 +2609,7 @@ class TodoCompanion extends UpdateCompanion { } TodoCompanion copyWith( - {Value? id, + {Value? id, Value? userId, Value? unit, Value? title, @@ -4430,7 +4432,7 @@ typedef $$CourseTableProcessedTableManager = ProcessedTableManager< CourseData, PrefetchHooks Function({bool user, bool todoRefs})>; typedef $$TodoTableCreateCompanionBuilder = TodoCompanion Function({ - Value id, + Value id, required String userId, Value unit, required String title, @@ -4447,7 +4449,7 @@ typedef $$TodoTableCreateCompanionBuilder = TodoCompanion Function({ Value completedAt, }); typedef $$TodoTableUpdateCompanionBuilder = TodoCompanion Function({ - Value id, + Value id, Value userId, Value unit, Value title, @@ -4785,7 +4787,7 @@ class $$TodoTableTableManager extends RootTableManager< createComputedFieldComposer: () => $$TodoTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ - Value id = const Value.absent(), + Value id = const Value.absent(), Value userId = const Value.absent(), Value unit = const Value.absent(), Value title = const Value.absent(), @@ -4819,7 +4821,7 @@ class $$TodoTableTableManager extends RootTableManager< completedAt: completedAt, ), createCompanionCallback: ({ - Value id = const Value.absent(), + Value id = const Value.absent(), required String userId, Value unit = const Value.absent(), required String title, diff --git a/lib/features/todo/models/todo.dart b/lib/features/todo/models/todo.dart index 80b0c08..290882f 100644 --- a/lib/features/todo/models/todo.dart +++ b/lib/features/todo/models/todo.dart @@ -3,7 +3,7 @@ import 'package:academia/features/courses/models/course.dart'; import 'package:drift/drift.dart'; class Todo extends Table { - IntColumn get id => integer().autoIncrement()(); + IntColumn get id => integer().autoIncrement().nullable()(); TextColumn get userId => text().references(User, #id)(); TextColumn get unit => text().references(Course, #unit).nullable()(); TextColumn get title => text()(); diff --git a/lib/features/todo/views/todo_view_page.dart b/lib/features/todo/views/todo_view_page.dart index 7f290c0..8487ced 100644 --- a/lib/features/todo/views/todo_view_page.dart +++ b/lib/features/todo/views/todo_view_page.dart @@ -1,10 +1,12 @@ import 'dart:convert'; import 'package:academia/database/database.dart'; +import 'package:academia/features/features.dart'; import 'package:drift/drift.dart' as drift; import 'package:flex_color_picker/flex_color_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:icons_plus/icons_plus.dart'; import 'package:sliver_tools/sliver_tools.dart'; @@ -23,6 +25,7 @@ class TodoViewPage extends StatefulWidget { } class _TodoViewPageState extends State { + late CourseCubit courseCubit = BlocProvider.of(context); late TodoEditState editState = widget.todoData == null ? TodoEditState.create : TodoEditState.modify; @@ -134,6 +137,44 @@ class _TodoViewPageState extends State { } } + Future _showCoursePickerDialog() async { + showModalBottomSheet( + context: context, + builder: (context) => Container( + padding: const EdgeInsets.all(16), + child: BlocBuilder(builder: (context, state) { + if (state is! CourseStateLoaded) { + return const Center( + child: Text("Please refresh courses to continue"), + ); + } + + final courses = (state).courses; + return ListView.builder( + itemCount: courses.length, + itemBuilder: (context, index) { + return CheckboxListTile( + title: Text(courses[index].unit), + value: false, + onChanged: (val) { + setState(() { + todo = todo.copyWith( + unit: drift.Value(courses[index].unit)); + }); + context.pop(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + "Successfully linked task to ${courses[index].unit}"), + ), + ); + }); + }); + }), + ), + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -265,6 +306,25 @@ class _TodoViewPageState extends State { ), ), ), + SliverToBoxAdapter( + child: Card( + elevation: 0, + margin: const EdgeInsets.only(bottom: 2), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.zero, + ), + child: ListTile( + leading: const Icon(Bootstrap.vinyl), + enableFeedback: false, + onTap: () { + _showCoursePickerDialog(); + HapticFeedback.heavyImpact().then((value) {}); + }, + title: const Text("Pick a course for this task"), + subtitle: todo.unit == null ? null : Text(todo.unit!), + ), + ), + ), SliverToBoxAdapter( child: Card( elevation: 0,