DAO #4

Merged
jshbrmn merged 9 commits from dao into main 2024-12-24 14:00:49 -08:00
47 changed files with 10138 additions and 1007 deletions

View File

@ -9,6 +9,10 @@
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
- "**/*.g.dart"
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`

9
build.yaml Normal file
View File

@ -0,0 +1,9 @@
targets:
$default:
builders:
drift_dev:
options:
schema_dir: lib/database/drift_schemas/
databases:
# Required: A name for the database and it's path
sendtrain: lib/database/database.dart

35
lib/daos/actions_dao.dart Normal file
View File

@ -0,0 +1,35 @@
import 'package:drift/drift.dart';
import 'package:sendtrain/database/database.dart';
part 'actions_dao.g.dart';
@DriftAccessor(tables: [Actions])
class ActionsDao extends DatabaseAccessor<AppDatabase> with _$ActionsDaoMixin {
ActionsDao(super.db);
Future<List<Action>> all() async {
return await select(actions).get();
}
Future<Action> find(int id) async {
return await (select(actions)..where((action) => action.id.equals(id) )).getSingle();
}
Future<List<Action>> fromActivity(Activity activity) async {
final result = select(db.activityActions).join(
[
innerJoin(
db.actions,
db.actions.id.equalsExp(db.activityActions.actionId),
),
],
)
..where(db.activityActions.activityId.equals(activity.id));
final actions = (await result.get())
.map((e) => e.readTable(db.actions))
.toList();
return actions;
}
}

View File

@ -0,0 +1,8 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'actions_dao.dart';
// ignore_for_file: type=lint
mixin _$ActionsDaoMixin on DatabaseAccessor<AppDatabase> {
$ActionsTable get actions => attachedDatabase.actions;
}

View File

@ -0,0 +1,35 @@
import 'package:drift/drift.dart';
import 'package:sendtrain/database/database.dart';
part 'activities_dao.g.dart';
@DriftAccessor(tables: [Activities])
class ActivitiesDao extends DatabaseAccessor<AppDatabase> with _$ActivitiesDaoMixin {
ActivitiesDao(super.db);
Future<List<Activity>> all() async {
return await select(activities).get();
}
Future<Activity> find(int id) async {
return await (select(activities)..where((activity) => activity.id.equals(id) )).getSingle();
}
Future<List<Activity>> sessionActivities(int id) async {
final result = select(db.sessionActivities).join(
[
innerJoin(
db.activities,
db.activities.id
.equalsExp(db.sessionActivities.activityId),
),
],
)..where(db.sessionActivities.sessionId.equals(id));
final activities = (await result.get())
.map((e) => e.readTable(db.activities))
.toList();
return activities;
}
}

View File

@ -0,0 +1,8 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'activities_dao.dart';
// ignore_for_file: type=lint
mixin _$ActivitiesDaoMixin on DatabaseAccessor<AppDatabase> {
$ActivitiesTable get activities => attachedDatabase.activities;
}

View File

@ -0,0 +1,30 @@
import 'package:drift/drift.dart';
import 'package:sendtrain/database/database.dart';
part 'activity_actions_dao.g.dart';
@DriftAccessor(tables: [ActivityActions])
class ActivityActionsDao extends DatabaseAccessor<AppDatabase> with _$ActivityActionsDaoMixin {
ActivityActionsDao(super.db);
Future<List<ActivityAction>> all() => select(activityActions).get();
Stream<List<ActivityAction>> watch() => select(activityActions).watch();
Future insert(ActivityAction activityAction) => into(activityActions).insert(activityAction);
Future replace(ActivityAction activityAction) => update(activityActions).replace(activityAction);
Future remove(ActivityAction activityAction) => delete(activityActions).delete(activityAction);
// Future<List<ActivityAction>> all() async {
// return await select(activityActions).get();
// }
Future<ActivityAction> find(int id) async {
return await (select(activityActions)..where((activityAction) => activityAction.id.equals(id) )).getSingle();
}
Future<List<ActivityAction>> fromActivityId(int id) async {
final result = db.managers.activityActions
.filter((activityAction) => activityAction.activityId.id(id));
return result.get();
}
}

View File

@ -0,0 +1,10 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'activity_actions_dao.dart';
// ignore_for_file: type=lint
mixin _$ActivityActionsDaoMixin on DatabaseAccessor<AppDatabase> {
$ActivitiesTable get activities => attachedDatabase.activities;
$ActionsTable get actions => attachedDatabase.actions;
$ActivityActionsTable get activityActions => attachedDatabase.activityActions;
}

View File

@ -0,0 +1,57 @@
import 'package:drift/drift.dart';
import 'package:sendtrain/database/database.dart';
part 'media_items_dao.g.dart';
@DriftAccessor(tables: [MediaItems])
class MediaItemsDao extends DatabaseAccessor<AppDatabase> with _$MediaItemsDaoMixin {
MediaItemsDao(super.db);
Future<List<MediaItem>> all() async {
return await select(mediaItems).get();
}
Future<MediaItem> find(int id) async {
return await (select(mediaItems)..where((mediaItem) => mediaItem.id.equals(id) )).getSingle();
}
Future<List<MediaItem>> fromActivity(Activity activity) async {
final result = select(db.objectMediaItems).join(
[
innerJoin(
db.mediaItems,
db.mediaItems.id.equalsExp(db.objectMediaItems.mediaId),
),
],
)
..where(
db.objectMediaItems.objectType.equals(ObjectType.activities.name))
..where(db.objectMediaItems.objectId.equals(activity.id));
final mediaItems = (await result.get())
.map((e) => e.readTable(db.mediaItems))
.toList();
return mediaItems;
}
Future<List<MediaItem>> fromSession(Session session) async {
final result = select(db.objectMediaItems).join(
[
innerJoin(
db.mediaItems,
db.mediaItems.id.equalsExp(db.objectMediaItems.mediaId),
),
],
)
..where(
db.objectMediaItems.objectType.equals(ObjectType.sessions.name))
..where(db.objectMediaItems.objectId.equals(session.id));
final mediaItems = (await result.get())
.map((e) => e.readTable(db.mediaItems))
.toList();
return mediaItems;
}
}

View File

@ -0,0 +1,8 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'media_items_dao.dart';
// ignore_for_file: type=lint
mixin _$MediaItemsDaoMixin on DatabaseAccessor<AppDatabase> {
$MediaItemsTable get mediaItems => attachedDatabase.mediaItems;
}

View File

@ -0,0 +1,24 @@
import 'package:drift/drift.dart';
import 'package:sendtrain/database/database.dart';
part 'session_activities_dao.g.dart';
@DriftAccessor(tables: [SessionActivities])
class SessionActivitiesDao extends DatabaseAccessor<AppDatabase> with _$SessionActivitiesDaoMixin {
SessionActivitiesDao(super.db);
Future<List<SessionActivity>> all() async {
return await select(sessionActivities).get();
}
Future<SessionActivity> find(int id) async {
return await (select(sessionActivities)..where((sessionActivity) => sessionActivity.id.equals(id) )).getSingle();
}
Future<List<SessionActivity>> fromSessionId(int id) async {
final result = db.managers.sessionActivities
.filter((sessionActivity) => sessionActivity.sessionId.id(id));
return result.get();
}
}

View File

@ -0,0 +1,11 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'session_activities_dao.dart';
// ignore_for_file: type=lint
mixin _$SessionActivitiesDaoMixin on DatabaseAccessor<AppDatabase> {
$SessionsTable get sessions => attachedDatabase.sessions;
$ActivitiesTable get activities => attachedDatabase.activities;
$SessionActivitiesTable get sessionActivities =>
attachedDatabase.sessionActivities;
}

View File

@ -0,0 +1,17 @@
import 'package:drift/drift.dart';
import 'package:sendtrain/database/database.dart';
part 'sessions_dao.g.dart';
@DriftAccessor(tables: [Sessions])
class SessionsDao extends DatabaseAccessor<AppDatabase> with _$SessionsDaoMixin {
SessionsDao(super.db);
Future<List<Session>> all() async {
return await select(sessions).get();
}
Future<Session> find(int id) async {
return await (select(sessions)..where((session) => session.id.equals(id) )).getSingle();
}
}

View File

@ -0,0 +1,8 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'sessions_dao.dart';
// ignore_for_file: type=lint
mixin _$SessionsDaoMixin on DatabaseAccessor<AppDatabase> {
$SessionsTable get sessions => attachedDatabase.sessions;
}

View File

@ -1,41 +1,86 @@
import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart';
import 'package:sendtrain/daos/actions_dao.dart';
import 'package:sendtrain/daos/activities_dao.dart';
import 'package:sendtrain/daos/activity_actions_dao.dart';
import 'package:sendtrain/daos/media_items_dao.dart';
import 'package:sendtrain/daos/session_activities_dao.dart';
import 'package:sendtrain/daos/sessions_dao.dart';
import 'package:sendtrain/database/seed.dart';
part 'database.g.dart';
@DriftDatabase(tables: [
Sessions,
SessionActivities,
Activities,
ActivityActions,
Actions,
ObjectMediaItems,
MediaItems
], daos: [
SessionsDao,
ActivitiesDao,
MediaItemsDao,
SessionActivitiesDao,
ActivityActionsDao,
ActionsDao
])
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.
// These are described in the getting started guide: https://drift.simonbinder.eu/setup/
AppDatabase() : super(_openConnection());
enum SessionStatus {
pending,
started,
completed,
missed
@override
int get schemaVersion => 4;
@override
MigrationStrategy get migration {
return MigrationStrategy(
onCreate: (m) async {
await m.createAll().then((r) async {
await seedDb(this);
}); // create all tables
},
beforeOpen: (details) async {
/// Enable foreign_keys
await customStatement('PRAGMA foreign_keys = ON');
},
);
}
static QueryExecutor _openConnection() {
// `driftDatabase` from `package:drift_flutter` stores the database in
// `getApplicationDocumentsDirectory()`.
return driftDatabase(name: 'sendtrain');
}
}
enum SessionStatus { pending, started, completed, missed }
class Sessions extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 3, max: 32)();
TextColumn get content => text().named('body')();
TextColumn get status => textEnum<SessionStatus>()();
DateTimeColumn get date => dateTime().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
DateTimeColumn get createdAt =>
dateTime().withDefault(Variable(DateTime.now()))();
}
class SessionActivities extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get sessionId => integer().references(Sessions, #id)();
IntColumn get activityId => integer().references(Activities, #id)();
IntColumn get position => integer()();
TextColumn get results => text().nullable()();
TextColumn get achievements => text().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
DateTimeColumn get createdAt =>
dateTime().withDefault(Variable(DateTime.now()))();
}
enum ActivityCategories {
fundamentals,
conditioning,
advanced,
custom,
pro
}
enum ActivityCategories { fundamentals, conditioning, advanced, custom, pro }
enum ActivityType {
strength,
@ -56,14 +101,17 @@ class Activities extends Table {
TextColumn get type => textEnum<ActivityType>()();
TextColumn get description => text().named('body')();
TextColumn get category => textEnum<ActivityCategories>()();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
DateTimeColumn get createdAt =>
dateTime().withDefault(Variable(DateTime.now()))();
}
class ActivityActions extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get activityId => integer().references(Activities, #id)();
IntColumn get actionId => integer().references(Actions, #id)();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
IntColumn get position => integer()();
DateTimeColumn get createdAt =>
dateTime().withDefault(Variable(DateTime.now()))();
}
class Actions extends Table {
@ -71,51 +119,33 @@ class Actions extends Table {
TextColumn get title => text().withLength(min: 3, max: 32)();
TextColumn get description => text().named('body')();
TextColumn get set => text()();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
DateTimeColumn get createdAt =>
dateTime().withDefault(Variable(DateTime.now()))();
}
enum ObjectType {
actions,
activities,
sessions,
}
class ObjectMediaItems extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get objectId => integer().references(Actions, #id)();
IntColumn get objectId => integer()();
TextColumn get objectType => textEnum<ObjectType>()();
IntColumn get mediaId => integer().references(MediaItems, #id)();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
DateTimeColumn get createdAt =>
dateTime().withDefault(Variable(DateTime.now()))();
}
enum MediaType {
youtube,
image
}
enum MediaType { youtube, image }
class MediaItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 3, max: 32)();
TextColumn get description => text().named('body')();
TextColumn get reference => text().withLength(min: 3, max: 32)();
TextColumn get reference => text().withLength(min: 3, max: 256)();
TextColumn get type => textEnum<MediaType>()();
DateTimeColumn get createdAt => dateTime().withDefault(Variable(DateTime.now()))();
}
@DriftDatabase(tables: [
Sessions,
SessionActivities,
Activities,
ActivityActions,
Actions,
ObjectMediaItems,
MediaItems
])
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.
// These are described in the getting started guide: https://drift.simonbinder.eu/setup/
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
static QueryExecutor _openConnection() {
// `driftDatabase` from `package:drift_flutter` stores the database in
// `getApplicationDocumentsDirectory()`.
return driftDatabase(name: 'sendtrain');
}
DateTimeColumn get createdAt =>
dateTime().withDefault(Variable(DateTime.now()))();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,688 @@
// dart format width=80
import 'package:drift/internal/versioned_schema.dart' as i0;
import 'package:drift/drift.dart' as i1;
import 'package:drift/drift.dart'; // ignore_for_file: type=lint,unused_import
// GENERATED BY drift_dev, DO NOT MODIFY.
final class Schema2 extends i0.VersionedSchema {
Schema2({required super.database}) : super(version: 2);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
sessions,
activities,
sessionActivities,
actions,
activityActions,
mediaItems,
objectMediaItems,
];
late final Shape0 sessions = Shape0(
source: i0.VersionedTable(
entityName: 'sessions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_2,
_column_3,
_column_4,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape1 activities = Shape1(
source: i0.VersionedTable(
entityName: 'activities',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_6,
_column_2,
_column_7,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape2 sessionActivities = Shape2(
source: i0.VersionedTable(
entityName: 'session_activities',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_8,
_column_9,
_column_10,
_column_11,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape3 actions = Shape3(
source: i0.VersionedTable(
entityName: 'actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_2,
_column_12,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape4 activityActions = Shape4(
source: i0.VersionedTable(
entityName: 'activity_actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_9,
_column_13,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape5 mediaItems = Shape5(
source: i0.VersionedTable(
entityName: 'media_items',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_2,
_column_14,
_column_6,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape6 objectMediaItems = Shape6(
source: i0.VersionedTable(
entityName: 'object_media_items',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_15,
_column_16,
_column_17,
_column_5,
],
attachedDatabase: database,
),
alias: null);
}
class Shape0 extends i0.VersionedTable {
Shape0({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get id =>
columnsByName['id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get title =>
columnsByName['title']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get content =>
columnsByName['body']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get status =>
columnsByName['status']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<DateTime> get date =>
columnsByName['date']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<int> _column_0(String aliasedName) =>
i1.GeneratedColumn<int>('id', aliasedName, false,
hasAutoIncrement: true,
type: i1.DriftSqlType.int,
defaultConstraints:
i1.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
i1.GeneratedColumn<String> _column_1(String aliasedName) =>
i1.GeneratedColumn<String>('title', aliasedName, false,
additionalChecks: i1.GeneratedColumn.checkTextLength(
minTextLength: 3, maxTextLength: 32),
type: i1.DriftSqlType.string);
i1.GeneratedColumn<String> _column_2(String aliasedName) =>
i1.GeneratedColumn<String>('body', aliasedName, false,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<String> _column_3(String aliasedName) =>
i1.GeneratedColumn<String>('status', aliasedName, false,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<DateTime> _column_4(String aliasedName) =>
i1.GeneratedColumn<DateTime>('date', aliasedName, true,
type: i1.DriftSqlType.dateTime);
i1.GeneratedColumn<DateTime> _column_5(String aliasedName) =>
i1.GeneratedColumn<DateTime>('created_at', aliasedName, false,
type: i1.DriftSqlType.dateTime, defaultValue: Variable(DateTime.now()));
class Shape1 extends i0.VersionedTable {
Shape1({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get id =>
columnsByName['id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get title =>
columnsByName['title']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get type =>
columnsByName['type']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get description =>
columnsByName['body']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get category =>
columnsByName['category']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<String> _column_6(String aliasedName) =>
i1.GeneratedColumn<String>('type', aliasedName, false,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<String> _column_7(String aliasedName) =>
i1.GeneratedColumn<String>('category', aliasedName, false,
type: i1.DriftSqlType.string);
class Shape2 extends i0.VersionedTable {
Shape2({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get id =>
columnsByName['id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get sessionId =>
columnsByName['session_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get activityId =>
columnsByName['activity_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get results =>
columnsByName['results']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get achievements =>
columnsByName['achievements']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<int> _column_8(String aliasedName) =>
i1.GeneratedColumn<int>('session_id', aliasedName, false,
type: i1.DriftSqlType.int,
defaultConstraints:
i1.GeneratedColumn.constraintIsAlways('REFERENCES sessions (id)'));
i1.GeneratedColumn<int> _column_9(String aliasedName) =>
i1.GeneratedColumn<int>('activity_id', aliasedName, false,
type: i1.DriftSqlType.int,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES activities (id)'));
i1.GeneratedColumn<String> _column_10(String aliasedName) =>
i1.GeneratedColumn<String>('results', aliasedName, true,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<String> _column_11(String aliasedName) =>
i1.GeneratedColumn<String>('achievements', aliasedName, true,
type: i1.DriftSqlType.string);
class Shape3 extends i0.VersionedTable {
Shape3({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get id =>
columnsByName['id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get title =>
columnsByName['title']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get description =>
columnsByName['body']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get set =>
columnsByName['set']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<String> _column_12(String aliasedName) =>
i1.GeneratedColumn<String>('set', aliasedName, false,
type: i1.DriftSqlType.string);
class Shape4 extends i0.VersionedTable {
Shape4({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get id =>
columnsByName['id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get activityId =>
columnsByName['activity_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get actionId =>
columnsByName['action_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<int> _column_13(String aliasedName) =>
i1.GeneratedColumn<int>('action_id', aliasedName, false,
type: i1.DriftSqlType.int,
defaultConstraints:
i1.GeneratedColumn.constraintIsAlways('REFERENCES actions (id)'));
class Shape5 extends i0.VersionedTable {
Shape5({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get id =>
columnsByName['id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get title =>
columnsByName['title']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get description =>
columnsByName['body']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get reference =>
columnsByName['reference']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get type =>
columnsByName['type']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<String> _column_14(String aliasedName) =>
i1.GeneratedColumn<String>('reference', aliasedName, false,
additionalChecks: i1.GeneratedColumn.checkTextLength(
minTextLength: 3, maxTextLength: 256),
type: i1.DriftSqlType.string);
class Shape6 extends i0.VersionedTable {
Shape6({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get id =>
columnsByName['id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get objectId =>
columnsByName['object_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get objectType =>
columnsByName['object_type']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get mediaId =>
columnsByName['media_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<int> _column_15(String aliasedName) =>
i1.GeneratedColumn<int>('object_id', aliasedName, false,
type: i1.DriftSqlType.int);
i1.GeneratedColumn<String> _column_16(String aliasedName) =>
i1.GeneratedColumn<String>('object_type', aliasedName, false,
type: i1.DriftSqlType.string);
i1.GeneratedColumn<int> _column_17(String aliasedName) =>
i1.GeneratedColumn<int>('media_id', aliasedName, false,
type: i1.DriftSqlType.int,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'REFERENCES media_items (id)'));
final class Schema3 extends i0.VersionedSchema {
Schema3({required super.database}) : super(version: 3);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
sessions,
activities,
sessionActivities,
actions,
activityActions,
mediaItems,
objectMediaItems,
];
late final Shape0 sessions = Shape0(
source: i0.VersionedTable(
entityName: 'sessions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_2,
_column_3,
_column_4,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape1 activities = Shape1(
source: i0.VersionedTable(
entityName: 'activities',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_6,
_column_2,
_column_7,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape7 sessionActivities = Shape7(
source: i0.VersionedTable(
entityName: 'session_activities',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_8,
_column_9,
_column_18,
_column_10,
_column_11,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape3 actions = Shape3(
source: i0.VersionedTable(
entityName: 'actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_2,
_column_12,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape8 activityActions = Shape8(
source: i0.VersionedTable(
entityName: 'activity_actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_9,
_column_13,
_column_18,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape5 mediaItems = Shape5(
source: i0.VersionedTable(
entityName: 'media_items',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_2,
_column_14,
_column_6,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape6 objectMediaItems = Shape6(
source: i0.VersionedTable(
entityName: 'object_media_items',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_15,
_column_16,
_column_17,
_column_5,
],
attachedDatabase: database,
),
alias: null);
}
class Shape7 extends i0.VersionedTable {
Shape7({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get id =>
columnsByName['id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get sessionId =>
columnsByName['session_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get activityId =>
columnsByName['activity_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get postition =>
columnsByName['postition']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get results =>
columnsByName['results']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get achievements =>
columnsByName['achievements']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<int> _column_18(String aliasedName) =>
i1.GeneratedColumn<int>('postition', aliasedName, false,
type: i1.DriftSqlType.int);
class Shape8 extends i0.VersionedTable {
Shape8({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get id =>
columnsByName['id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get activityId =>
columnsByName['activity_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get actionId =>
columnsByName['action_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get postition =>
columnsByName['postition']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
final class Schema4 extends i0.VersionedSchema {
Schema4({required super.database}) : super(version: 4);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
sessions,
activities,
sessionActivities,
actions,
activityActions,
mediaItems,
objectMediaItems,
];
late final Shape0 sessions = Shape0(
source: i0.VersionedTable(
entityName: 'sessions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_2,
_column_3,
_column_4,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape1 activities = Shape1(
source: i0.VersionedTable(
entityName: 'activities',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_6,
_column_2,
_column_7,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape9 sessionActivities = Shape9(
source: i0.VersionedTable(
entityName: 'session_activities',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_8,
_column_9,
_column_19,
_column_10,
_column_11,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape3 actions = Shape3(
source: i0.VersionedTable(
entityName: 'actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_2,
_column_12,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape10 activityActions = Shape10(
source: i0.VersionedTable(
entityName: 'activity_actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_9,
_column_13,
_column_19,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape5 mediaItems = Shape5(
source: i0.VersionedTable(
entityName: 'media_items',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_2,
_column_14,
_column_6,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape6 objectMediaItems = Shape6(
source: i0.VersionedTable(
entityName: 'object_media_items',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_15,
_column_16,
_column_17,
_column_5,
],
attachedDatabase: database,
),
alias: null);
}
class Shape9 extends i0.VersionedTable {
Shape9({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get id =>
columnsByName['id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get sessionId =>
columnsByName['session_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get activityId =>
columnsByName['activity_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get position =>
columnsByName['position']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get results =>
columnsByName['results']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get achievements =>
columnsByName['achievements']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<int> _column_19(String aliasedName) =>
i1.GeneratedColumn<int>('position', aliasedName, false,
type: i1.DriftSqlType.int);
class Shape10 extends i0.VersionedTable {
Shape10({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<int> get id =>
columnsByName['id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get activityId =>
columnsByName['activity_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get actionId =>
columnsByName['action_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get position =>
columnsByName['position']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
}) {
return (currentVersion, database) async {
switch (currentVersion) {
case 1:
final schema = Schema2(database: database);
final migrator = i1.Migrator(database, schema);
await from1To2(migrator, schema);
return 2;
case 2:
final schema = Schema3(database: database);
final migrator = i1.Migrator(database, schema);
await from2To3(migrator, schema);
return 3;
case 3:
final schema = Schema4(database: database);
final migrator = i1.Migrator(database, schema);
await from3To4(migrator, schema);
return 4;
default:
throw ArgumentError.value('Unknown migration from $currentVersion');
}
};
}
i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
}) =>
i0.VersionedSchema.stepByStepHelper(
step: migrationSteps(
from1To2: from1To2,
from2To3: from2To3,
from3To4: from3To4,
));

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

167
lib/database/seed.dart Normal file
View File

@ -0,0 +1,167 @@
import 'dart:math';
import 'package:drift/drift.dart';
import 'package:sendtrain/database/database.dart';
Future<void> seedDb(AppDatabase database) async {
// seed data setup
final List<List> sessionValues = [
[
'Projecting @ Climbers Rock',
'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.'
],
[
'Moonboard @ Boardroom',
'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.'
],
[
'Off-Wall Training',
'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.'
],
[
'Climbing Outdoors',
'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.'
],
[
'Volume Session @ Gravity',
'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.'
],
];
final List<List> mediaItems = [
[
'https://www.climbing.com/wp-content/uploads/2022/06/campus-board-e1655470701154.jpeg',
MediaType.image
],
['BgheYcxhrsw', MediaType.youtube]
];
final List<String> actionTypes = [
"[[{\"actionID\": 0, \"name\": \"1, 3, 5\", \"type\": \"repititions\", \"amount\": 1, \"weight\": 0}, {\"actionID\": 1, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 300}], [{\"actionID\": 2, \"name\": \"1, 3, 5\", \"type\": \"repititions\", \"amount\": 1, \"weight\": 0}, {\"actionID\": 3, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 300}], [{\"actionID\": 4, \"name\": \"1, 3, 5\", \"type\": \"repititions\", \"amount\": 1, \"weight\": 0}, {\"actionID\": 5, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 300}]]",
"[[{\"actionID\": 0, \"name\": \"Long Pulls\", \"type\": \"seconds\", \"amount\": 5, \"weight\": 80}, {\"actionID\": 1, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 5}, {\"actionID\": 2, \"name\": \"Long Pulls\", \"type\": \"seconds\", \"amount\": 5, \"weights\": 80}, {\"actionID\": 3, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 300}], [{\"actionID\": 4, \"name\": \"Long Pulls\", \"type\": \"seconds\", \"amount\": 5, \"weight\": 80}, {\"actionID\": 5, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 5}, {\"actionID\": 6, \"name\": \"Long Pulls\", \"type\": \"seconds\", \"amount\": 5, \"weights\": 80}, {\"actionID\": 7, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 300}], [{\"actionID\": 8, \"name\": \"Long Pulls\", \"type\": \"seconds\", \"amount\": 5, \"weight\": 80}, {\"actionID\": 9, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 5}, {\"actionID\": 10, \"name\": \"Long Pulls\", \"type\": \"seconds\", \"amount\": 5, \"weights\": 80}, {\"actionID\": 11, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 300}], [{\"actionID\": 12, \"name\": \"Long Pulls\", \"type\": \"seconds\", \"amount\": 5, \"weight\": 80}, {\"actionID\": 13, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 5}, {\"actionID\": 14, \"name\": \"Long Pulls\", \"type\": \"seconds\", \"amount\": 5, \"weights\": 80}, {\"actionID\": 15, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 300}], [{\"actionID\": 16, \"name\": \"Long Pulls\", \"type\": \"seconds\", \"amount\": 5, \"weight\": 80}, {\"actionID\": 17, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 5}, {\"actionID\": 18, \"name\": \"Long Pulls\", \"type\": \"seconds\", \"amount\": 5, \"weights\": 80}, {\"actionID\": 19, \"name\": \"Rest\", \"type\": \"seconds\", \"amount\": 300}]]"
];
final int totalSessions = 15;
final int totalActivities = 6;
final int totalActions = 5;
final int totalMedia = 5;
final random = Random();
// seed loop
for (int i = 0; i < totalSessions; i++) {
// session things
var status = SessionStatus.completed;
if (i == 0) status = SessionStatus.started;
if (i == 1) status = SessionStatus.pending;
final sessionValue = sessionValues[random.nextInt(sessionValues.length)];
await database
.into(database.sessions)
.insert(SessionsCompanion.insert(
title: sessionValue[0],
content: sessionValue[1],
status: status,
date: Value(DateTime.now())))
.then((sessionId) async {
// activities things
for (int j = 0; j <= random.nextInt(totalActivities); j++) {
await database
.into(database.activities)
.insert(ActivitiesCompanion.insert(
title: "Test activity $j",
type: ActivityType
.values[random.nextInt(ActivityType.values.length)],
description:
"$j Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.",
category: ActivityCategories
.values[random.nextInt(ActivityCategories.values.length)]))
.then((activityId) async {
// session activity relationships
await database
.into(database.sessionActivities)
.insert(SessionActivitiesCompanion.insert(
sessionId: sessionId,
activityId: activityId,
position: j,
results: Value("results json, will need to test"),
achievements: Value("comma, seperated, items"),
));
// actions
for (int k = 0; k <= random.nextInt(totalActions); k++) {
await database
.into(database.actions)
.insert(ActionsCompanion.insert(
title: 'Test action $k',
description:
'$k Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
set: actionTypes[random.nextInt(actionTypes.length)]))
.then((actionId) async {
// add activity action association
await database.into(database.activityActions).insert(
ActivityActionsCompanion.insert(
activityId: activityId, actionId: actionId, position: k));
for (int l = 0; l <= random.nextInt(totalMedia); l++) {
final mediaItem = mediaItems[random.nextInt(mediaItems.length)];
await database
.into(database.mediaItems)
.insert(MediaItemsCompanion.insert(
title: 'Media title $l',
description:
'Media description $l Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
reference: mediaItem[0],
type: mediaItem[1]))
.then((mediaId) async {
await database.into(database.objectMediaItems).insert(
ObjectMediaItemsCompanion.insert(
objectId: actionId,
mediaId: mediaId,
objectType: ObjectType.actions));
});
}
});
}
for (int m = 0; m <= random.nextInt(totalMedia); m++) {
final mediaItem = mediaItems[random.nextInt(mediaItems.length)];
await database
.into(database.mediaItems)
.insert(MediaItemsCompanion.insert(
title: 'Media title $m',
description:
'Media description $m Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
reference: mediaItem[0],
type: mediaItem[1]))
.then((mediaId) async {
await database.into(database.objectMediaItems).insert(
ObjectMediaItemsCompanion.insert(
objectId: activityId,
mediaId: mediaId,
objectType: ObjectType.activities));
});
}
});
}
for (int n = 0; n <= random.nextInt(totalMedia); n++) {
final mediaItem = mediaItems[random.nextInt(mediaItems.length)];
await database
.into(database.mediaItems)
.insert(MediaItemsCompanion.insert(
title: 'Media title $n',
description:
'Media description $n Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
reference: mediaItem[0],
type: mediaItem[1]))
.then((mediaId) async {
await database.into(database.objectMediaItems).insert(
ObjectMediaItemsCompanion.insert(
objectId: sessionId,
mediaId: mediaId,
objectType: ObjectType.sessions));
});
}
});
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,84 @@
List<String> exceptions = [
'a',
'abaft',
'about',
'above',
'afore',
'after',
'along',
'amid',
'among',
'an',
'apud',
'as',
'aside',
'at',
'atop',
'below',
'but',
'by',
'circa',
'down',
'for',
'from',
'given',
'in',
'into',
'lest',
'like',
'mid',
'midst',
'minus',
'near',
'next',
'of',
'off',
'on',
'onto',
'out',
'over',
'pace',
'past',
'per',
'plus',
'pro',
'qua',
'round',
'sans',
'save',
'since',
'than',
'thru',
'till',
'times',
'to',
'under',
'until',
'unto',
'up',
'upon',
'via',
'vice',
'with',
'worth',
'the","and',
'nor',
'or',
'yet',
'so'
];
extension TitleCase on String {
String toTitleCase() {
return toLowerCase().replaceAllMapped(
RegExp(
r'[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+'),
(Match match) {
// if (exceptions.contains(match[0])) {
// return match[0]!;
// }
return "${match[0]![0].toUpperCase()}${match[0]!.substring(1).toLowerCase()}";
}).replaceAll(RegExp(r'(_|-)+'), ' ');
}
}

View File

@ -1,10 +1,11 @@
import 'package:drift/drift.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/database.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/screens/activities_screen.dart';
import 'package:sendtrain/screens/sessions_screen.dart';
// ignore: unused_import
import 'package:sendtrain/database/seed.dart';
class SendTrain extends StatelessWidget {
const SendTrain({super.key});
@ -92,86 +93,13 @@ class _AppState extends State<App> {
}
void main() {
// final database = AppDatabase();
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Projecting @ Climbers Rock',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.started,
// date: Value(DateTime.now())));
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Moonboard @ Boardroom',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.pending,
// date: Value(DateTime.now())));
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Moonboard @ Boardroom',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.completed,
// date: Value(DateTime.now())));
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Projecting @ Climbers Rock',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.completed,
// date: Value(DateTime.now())));
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Off-Wall Training',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.missed,
// date: Value(DateTime.now())));
// database.into(database.sessions).insert(SessionsCompanion.insert(
// title: 'Off-Wall Training',
// content: 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// status: SessionStatus.completed,
// date: Value(DateTime.now())));
// database.into(database.activities).insert(ActivitiesCompanion.insert(
// title: "test activity",
// type: ActivityType.technical,
// description: "test training activity",
// category: ActivityCategories.fundamentals));
// database
// .into(database.sessionActivities)
// .insert(SessionActivitiesCompanion.insert(
// sessionId: 1,
// activityId: 1,
// results: Value("results json, will need to test"),
// achievements: Value("comma, seperated, items"),
// ));
// database.into(database.actions).insert(ActionsCompanion.insert(
// title: "test action title",
// description: "teste action description",
// set: "not sure how the json will work yet",
// ));
// database
// .into(database.activityActions)
// .insert(ActivityActionsCompanion.insert(
// activityId: 1,
// actionId: 1,
// ));
// database.into(database.mediaItems).insert(MediaItemsCompanion.insert(
// title: "test youtube media item",
// description: "this is a test youtube item",
// reference: "sZVAEy9UmoY",
// type: MediaType.youtube));
// database
// .into(database.objectMediaItems)
// .insert(ObjectMediaItemsCompanion.insert(objectId: 1, mediaId: 1));
runApp(
ChangeNotifierProvider(
create: (context) => ActivityTimerModel(),
child: const SendTrain(),
),
);
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => ActivityTimerModel()),
Provider<AppDatabase>(
create: (context) => AppDatabase(),
dispose: (context, db) => db.close()),
],
child: const SendTrain(),
));
}

View File

@ -1,13 +1,15 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/database/database.dart';
class ActivityTimerModel with ChangeNotifier {
int _actionCounter = 0;
ActivityModel? _activity;
Activity? _activity;
List _sets = [];
List _actions = [];
int _currentActionNum = 0;
int _currentSetNum = 0;
Timer? _periodicTimer;
@ -20,20 +22,22 @@ class ActivityTimerModel with ChangeNotifier {
dynamic get currentAction => currentSet[_currentActionNum];
int get currentSetNum => _currentSetNum;
dynamic get currentSet => _sets[_currentSetNum];
ActivityModel? get activity => _activity;
Activity? get activity => _activity;
List get sets => _sets;
Timer? get periodicTimer => _periodicTimer;
bool get isActive => _isActive();
double get progress => _progress;
int get totalTime => _totalTime;
void setup(ActivityModel activity) {
void setup(Activity activity, List actions) {
if (_activity == null || activity.id != _activity?.id) {
_periodicTimer?.cancel();
_progress = 0;
_isc = null;
_activity = activity;
_sets = activity.actions[0].items();
// only one action for now
_sets = json.decode(actions[0].set);
_actions = actions;
_currentActionNum = 0;
_currentSetNum = 0;
setActionCount();

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:sendtrain/classes/activity_action.dart';
import 'package:sendtrain/database/database.dart' hide ActivityAction;
import 'package:sendtrain/models/activity_model.dart';
import '../widgets/activities_header.dart';
@ -13,6 +14,7 @@ class ActivitiesScreen extends StatefulWidget {
}
class _ActivitiesScreenState extends State<ActivitiesScreen> {
final Activity? activity = null;
final data = ActivityModel(
id: 1,
@ -40,23 +42,24 @@ class _ActivitiesScreenState extends State<ActivitiesScreen> {
@override
Widget build(BuildContext context) {
List<Widget> activities = List.generate(10, (i) => ActivityCard(activity: data));
return Text("N/A");
// List<Widget> activities = List.generate(10, (i) => ActivityCard(activity: data, data: activity));
return Padding(
padding: const EdgeInsets.fromLTRB(10, 15, 10, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const ActivitiesHeader(),
Expanded(
child: GridView.count(
primary: false,
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
crossAxisSpacing: 10,
mainAxisSpacing: 10,
crossAxisCount: 2,
children: activities,
))
]));
// return Padding(
// padding: const EdgeInsets.fromLTRB(10, 15, 10, 0),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: <Widget>[
// const ActivitiesHeader(),
// Expanded(
// child: GridView.count(
// primary: false,
// padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
// crossAxisSpacing: 10,
// mainAxisSpacing: 10,
// crossAxisCount: 2,
// children: activities,
// ))
// ]));
}
}

View File

@ -1,54 +1,54 @@
import 'package:drift/drift.dart' hide Column;
import 'package:flutter/material.dart';
import 'package:sendtrain/database.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/daos/sessions_dao.dart';
import 'package:sendtrain/database/database.dart';
import '../widgets/session_card.dart';
class SessionsScreen extends StatelessWidget {
final AppDatabase database = AppDatabase();
SessionsScreen({super.key});
Future<List<Session>> getSessions() async {
// database.managers.sessions.filter((session) => session.status(SessionStatus.pending));
return await database.managers.sessions.get();
}
const SessionsScreen({super.key});
@override
Widget build(BuildContext context) {
return FutureBuilder<List<Session>>(
future: getSessions(),
future: SessionsDao(Provider.of<AppDatabase>(context)).all(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
final sessions = snapshot.data!;
final pending = sessions.where((session) => session.status == SessionStatus.completed || session.status == SessionStatus.missed);
final upcoming = sessions.firstWhere((session) => session.status == SessionStatus.pending);
final current = sessions.firstWhere((session) => session.status == SessionStatus.started);
final pending = sessions.where((session) =>
session.status == SessionStatus.completed ||
session.status == SessionStatus.missed);
final upcoming = sessions.firstWhere(
(session) => session.status == SessionStatus.pending);
final current = sessions.firstWhere(
(session) => session.status == SessionStatus.started);
List<Widget> previousSessions = List.generate(pending.length, (i) => SessionCard(type: 1, session: pending.elementAt(i)));
Widget upcomingSession =
SessionCard(session: upcoming);
List<Widget> previousSessions = List.generate(pending.length,
(i) => SessionCard(type: 1, session: pending.elementAt(i)));
Widget upcomingSession = SessionCard(session: upcoming);
Widget currentSession = SessionCard(session: current);
database.close();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Padding(
padding: EdgeInsets.fromLTRB(15, 5, 0, 0),
child: Text(
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
style: TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
'Current:')),
currentSession,
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 0),
child: Text(
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
style: TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
'Upcoming:')),
upcomingSession,
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 0),
child: Text(
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
style: TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
'Previous:')),
SizedBox(
width: double.infinity,
@ -63,51 +63,14 @@ class SessionsScreen extends StatelessWidget {
],
);
} else {
return const CircularProgressIndicator();
return Container(
alignment: Alignment.center,
child: SizedBox(
height: 50.0,
width: 50.0,
child: CircularProgressIndicator(),
));
}
});
// List<Widget> previousSessions = List.generate(
// 10, (i) => SessionCard(state: 1, type: 1, session: _sessions.first));
// Widget upcomingSession = SessionCard(state: 2, session: _sessions.first);
// Widget currentSession = SessionCard(session: _sessions.first);
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: <Widget>[
// const Padding(
// padding: EdgeInsets.fromLTRB(15, 5, 0, 0),
// child: Text(
// style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
// 'Current:')),
// currentSession,
// const Padding(
// padding: EdgeInsets.fromLTRB(15, 30, 0, 0),
// child: Text(
// style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
// 'Upcoming:')),
// upcomingSession,
// const Padding(
// padding: EdgeInsets.fromLTRB(15, 30, 0, 0),
// child: Text(
// style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
// 'Previous:')),
// SizedBox(
// width: double.infinity,
// height: 160,
// child: GridView.count(
// padding: const EdgeInsets.fromLTRB(15, 10, 0, 0),
// scrollDirection: Axis.horizontal,
// crossAxisSpacing: 5,
// mainAxisSpacing: 5,
// crossAxisCount: 1,
// children: previousSessions))
// // Flexible(
// // child: ListView(
// // scrollDirection: Axis.vertical,
// // children: previousSessions,
// // )),
// ],
// );
}
}

View File

@ -1,12 +1,14 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:sendtrain/classes/activity_action.dart';
import 'package:sendtrain/extensions/string_extensions.dart';
import 'package:sendtrain/models/activity_timer_model.dart';
class ActivityActionView extends StatefulWidget {
const ActivityActionView({super.key, required this.action});
final ActivityAction action;
const ActivityActionView({super.key, required this.actions});
final List actions;
@override
State<ActivityActionView> createState() => ActivityActionViewState();
@ -25,7 +27,7 @@ class ActivityActionViewState extends State<ActivityActionView> {
Widget build(BuildContext context) {
ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: true);
List<List<Map<String, dynamic>>> sets = atm.activity!.actions[0].items();
List sets = json.decode(widget.actions[0].set);
// we need to set the scroll controller
// so we can update the selected item position
@ -34,14 +36,14 @@ class ActivityActionViewState extends State<ActivityActionView> {
return Expanded(
child: ScrollablePositionedList.builder(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 20),
itemCount: widget.action.activityActionSet.total,
itemCount: sets.length,
itemScrollController: itemScrollController,
scrollOffsetController: scrollOffsetController,
itemPositionsListener: itemPositionsListener,
scrollOffsetListener: scrollOffsetListener,
itemBuilder: (BuildContext context, int setNum) {
List<GestureDetector> content = [];
List<Map<String, dynamic>> set = sets[setNum];
List set = sets[setNum];
for (int actionNum = 0; actionNum < set.length; actionNum++) {
Map<String, dynamic> setItem = set[actionNum];
@ -74,7 +76,7 @@ class ActivityActionViewState extends State<ActivityActionView> {
: Theme.of(context).colorScheme.surfaceContainerLow,
child: Text(
textAlign: TextAlign.center,
'${setItem['name']}: ${setItem['amount']} ${setItem['type']}')))
'${setItem['name']}: ${setItem['amount']} ${setItem['type']}'.toTitleCase())))
])));
}

View File

@ -1,12 +1,13 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/daos/media_items_dao.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/extensions/string_extensions.dart';
import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/widgets/activity_view.dart';
class ActivityCard extends StatefulWidget {
final ActivityModel activity;
final Activity activity;
const ActivityCard({super.key, required this.activity});
@ -28,96 +29,118 @@ class ActivityCardState extends State<ActivityCard> {
final ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: false);
return Card(
color: atm.activity?.id == widget.activity.id
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.surfaceContainerLow,
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () => showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: const Duration(milliseconds: 220),
transitionBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
Animation<Offset> custom = Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: const Offset(0.0, 0.0))
.animate(animation);
return SlideTransition(
position: custom,
child: Dialog.fullscreen(
child: ActivityView(activity: widget.activity)));
},
barrierDismissible: true,
barrierLabel: '',
context: context,
pageBuilder: (context, animation1, animation2) {
return Container();
}),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Container(
width: 60,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: findMediaByType(
widget.activity.actions[0].media, 'image')),
// color: Colors.blue,
borderRadius:
const BorderRadius.all(Radius.elliptical(10, 10)),
),
)),
title: Consumer<ActivityTimerModel>(
builder: (context, atm, child) {
if (atm.activity?.id == widget.activity.id) {
return Text(
maxLines: 1,
"${widget.activity.title} (${formattedTime(atm.totalTime)})");
} else {
return Text(maxLines: 1, widget.activity.title);
}
},
),
subtitle: Text(maxLines: 2, widget.activity.description),
trailing: IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(Icons.close_rounded),
onPressed: () {
showAdaptiveDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Activity Removal'),
content: const Text('Would you like to permanently remove this activity from the current session?'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'Cancel'),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
},
return FutureBuilder<List<MediaItem>>(
future: MediaItemsDao(Provider.of<AppDatabase>(context))
.fromActivity(widget.activity),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<MediaItem> mediaItems = snapshot.data!;
return Card(
color: atm.activity?.id == widget.activity.id
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.surfaceContainerLow,
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () => showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: const Duration(milliseconds: 220),
transitionBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
Animation<Offset> custom = Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: const Offset(0.0, 0.0))
.animate(animation);
return SlideTransition(
position: custom,
child: Dialog.fullscreen(
child: ActivityView(activity: widget.activity)));
},
barrierDismissible: true,
barrierLabel: '',
context: context,
pageBuilder: (context, animation1, animation2) {
return Container();
}),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
// visualDensity: VisualDensity(horizontal: VisualDensity.maximumDensity),
leading: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Container(
// padding: EdgeInsets.only(top: 5, bottom: 5),
width: 60,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image:
findMediaByType(mediaItems, 'image')),
// color: Colors.blue,
borderRadius: const BorderRadius.all(
Radius.elliptical(8, 8)),
),
)),
title: Consumer<ActivityTimerModel>(
builder: (context, atm, child) {
if (atm.activity?.id == widget.activity.id) {
return Text(
maxLines: 1,
"${widget.activity.title.toTitleCase()} (${formattedTime(atm.totalTime)})");
} else {
return Text(maxLines: 1, widget.activity.title.toTitleCase());
}
},
),
subtitle: Text(maxLines: 2, widget.activity.description),
trailing: IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(Icons.close_rounded),
onPressed: () {
showAdaptiveDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Activity Removal'),
content: const Text(
'Would you like to permanently remove this activity from the current session?'),
actions: <Widget>[
TextButton(
onPressed: () =>
Navigator.pop(context, 'Cancel'),
child: const Text('Cancel'),
),
TextButton(
onPressed: () =>
Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
},
)),
],
)),
],
)),
);
);
} else {
return Container(
alignment: Alignment.center,
child: SizedBox(
height: 50.0,
width: 50.0,
child: CircularProgressIndicator(),
));
}
});
}
ImageProvider findMediaByType(List<Media>? media, String type) {
var found = media?.where((m) => m.type == 'image');
ImageProvider findMediaByType(List<MediaItem> media, String type) {
Iterable<MediaItem>? found = media.where((m) => m.type == MediaType.image);
if (found != null) {
if (found.isNotEmpty) {
return NetworkImage(found.first.reference);
} else {
// Element is not found

View File

@ -1,16 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/classes/activity_action.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/daos/actions_dao.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/extensions/string_extensions.dart';
import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/widgets/activity_action_view.dart';
import 'package:sendtrain/widgets/media_card.dart';
import 'package:sendtrain/widgets/activity_view_categories.dart';
import 'package:sendtrain/widgets/activity_view_media.dart';
import 'package:sendtrain/widgets/activity_view_types.dart';
class ActivityView extends StatefulWidget {
const ActivityView({super.key, required this.activity});
final ActivityModel activity;
const ActivityView(
{super.key, required this.activity});
final Activity activity;
@override
State<ActivityView> createState() => _ActivityViewState();
@ -19,196 +22,175 @@ class ActivityView extends StatefulWidget {
class _ActivityViewState extends State<ActivityView> {
@override
Widget build(BuildContext context) {
ActivityModel activity = widget.activity;
final Activity activity = widget.activity;
ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: false);
atm.setup(activity);
return FutureBuilder<List>(
future: ActionsDao(Provider.of<AppDatabase>(context))
.fromActivity(activity),
builder: (context, snapshot) {
if (snapshot.hasData) {
List actions = snapshot.data!;
atm.setup(activity, actions);
return Scaffold(
floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab(
distance: 70,
type: ExpandableFabType.up,
overlayStyle: ExpandableFabOverlayStyle(
color: Colors.black.withOpacity(0.5),
blur: 10,
),
children: [
FloatingActionButton.extended(
icon: const Icon(Icons.history_outlined),
label: Text('Restart'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.done_all_outlined),
label: Text('Done'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.edit_outlined),
label: Text('Edit'),
onPressed: () {},
),
]),
body: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
AppBar(
centerTitle: true,
title: const Text('Activity', style: TextStyle(fontSize: 15)),
),
Padding(
padding: const EdgeInsets.only(
left: 15, right: 20, top: 15, bottom: 10),
child: Text(
maxLines: 1,
style: const TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
activity.title)),
ActivityViewCategories(categories: activity.categories),
Padding(
padding: const EdgeInsets.only(
top: 0, bottom: 20, left: 15, right: 15),
child: Text(
textAlign: TextAlign.left,
style: const TextStyle(fontSize: 15),
activity.description)),
ActivityViewMedia(activity: activity),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
textAlign: TextAlign.left,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
'Actions')),
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Card(
clipBehavior: Clip.antiAlias,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10)),
),
color: Theme.of(context).colorScheme.onPrimary,
child: Row(children: [
Ink(
width: 70,
color: Theme.of(context).colorScheme.primaryContainer,
child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) {
return IconButton(
alignment: AlignmentDirectional.center,
icon: atm.isActive
? const Icon(Icons.pause_rounded)
: const Icon(Icons.play_arrow_rounded),
onPressed: () =>
{atm.isActive ? atm.pause() : atm.start()});
},
)),
Expanded(
flex: 1,
child: Stack(alignment: Alignment.center, children: [
Container(
alignment: Alignment.center,
child: Consumer<ActivityTimerModel>(
return Scaffold(
floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab(
distance: 70,
type: ExpandableFabType.up,
overlayStyle: ExpandableFabOverlayStyle(
color: Colors.black.withOpacity(0.5),
blur: 10,
),
children: [
FloatingActionButton.extended(
icon: const Icon(Icons.history_outlined),
label: Text('Restart'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.done_all_outlined),
label: Text('Done'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.edit_outlined),
label: Text('Edit'),
onPressed: () {},
),
]),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AppBar(
titleSpacing: 0,
centerTitle: true,
title: const Text('Activity',
style: TextStyle(fontSize: 15)),
),
Padding(
padding: const EdgeInsets.only(
left: 15, right: 20, top: 15, bottom: 10),
child: Text(
maxLines: 1,
style: const TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
activity.title.toTitleCase())),
Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 0, 10),
child: Flex(direction: Axis.horizontal, children: [
ActivityViewCategories(
categories: [activity.category]),
ActivityViewTypes(types: [activity.type])
])),
Padding(
padding: const EdgeInsets.only(
top: 0, bottom: 10, left: 15, right: 15),
child: Text(
textAlign: TextAlign.left,
style: const TextStyle(fontSize: 15),
activity.description)),
const Padding(
padding: EdgeInsets.fromLTRB(15, 20, 0, 10),
child: Text(
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
'Media:')),
ActivityViewMedia(activity: activity),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
'Actions')),
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Card(
clipBehavior: Clip.antiAlias,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10)),
),
color: Theme.of(context).colorScheme.onPrimary,
child: Row(children: [
Ink(
width: 70,
color: Theme.of(context)
.colorScheme
.primaryContainer,
child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) {
return IconButton(
alignment:
AlignmentDirectional.center,
icon: atm.isActive
? const Icon(
Icons.pause_rounded)
: const Icon(
Icons.play_arrow_rounded),
onPressed: () => {
atm.isActive
? atm.pause()
: atm.start()
});
},
)),
Expanded(
flex: 1,
child: Stack(
alignment: Alignment.center,
children: [
Container(
alignment: Alignment.center,
child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) {
return Text(
style: const TextStyle(
fontSize: 20),
textAlign: TextAlign.center,
'${atm.actionCount} ${atm.currentAction['type']}'.toTitleCase());
},
),
),
Container(
alignment: Alignment.centerRight,
padding:
EdgeInsets.only(right: 15),
child:
Consumer<ActivityTimerModel>(
builder: (context, atm,
child) {
return Text(
style: const TextStyle(
fontSize: 12),
textAlign: TextAlign.right,
'${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}');
})),
])),
]))),
Padding(
padding: EdgeInsets.only(left: 14, right: 14),
child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) {
return Text(
style: const TextStyle(fontSize: 20),
textAlign: TextAlign.center,
'${atm.actionCount} ${atm.currentAction['type']}');
},
),
),
Container(
alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 15),
child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) {
return Text(
style: const TextStyle(fontSize: 12),
textAlign: TextAlign.right,
'${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}');
})),
])),
]))),
Padding(
padding: EdgeInsets.only(left: 14, right: 14),
child:
Consumer<ActivityTimerModel>(builder: (context, atm, child) {
return LinearProgressIndicator(
value: atm.progress,
semanticsLabel: 'Activity Progress',
);
})),
ActivityActionView(action: activity.actions[0]),
]));
}
}
class ActivityViewCategories extends StatelessWidget {
const ActivityViewCategories({super.key, this.categories});
final List<String>? categories;
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: SizedBox(
height: 40,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: categories?.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(right: 5),
child: ActionChip(
visualDensity: VisualDensity.compact,
avatar: const Icon(Icons.check_circle_outline),
label: Text(maxLines: 1, '${categories?[index]}'),
onPressed: () {},
));
},
))),
],
);
}
}
class ActivityViewMedia extends StatelessWidget {
const ActivityViewMedia({super.key, this.activity});
final ActivityModel? activity;
@override
Widget build(BuildContext context) {
List<Media> media = [];
for (ActivityAction action in activity!.actions) {
if (action.media!.isNotEmpty) {
media.addAll(action.media as Iterable<Media>);
}
}
List<Widget> mediaCards =
List.generate(media.length, (i) => MediaCard(media: media[i]));
return Column(
children: [
SizedBox(
width: double.infinity,
height: 100,
child: GridView.count(
padding: const EdgeInsets.fromLTRB(15, 0, 0, 0),
scrollDirection: Axis.horizontal,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
crossAxisCount: 1,
children: mediaCards))
],
);
return LinearProgressIndicator(
value: atm.progress,
semanticsLabel: 'Activity Progress',
);
})),
ActivityActionView(actions: actions),
]));
} else {
return Container(
alignment: Alignment.center,
child: SizedBox(
height: 50.0,
width: 50.0,
child: CircularProgressIndicator(),
));
}
});
}
}

View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/extensions/string_extensions.dart';
class ActivityViewCategories extends StatelessWidget {
const ActivityViewCategories({super.key, required this.categories});
final List<ActivityCategories> categories;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 40,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.only(right: 10),
itemCount: categories.length,
itemBuilder: (BuildContext context, int index) {
return ActionChip(
visualDensity: VisualDensity.compact,
avatar: const Icon(Icons.category_rounded),
label: Text(maxLines: 1, categories[index].name.toTitleCase()),
tooltip: "Activity Category",
onPressed: () {},
);
},
));
}
}

View File

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/daos/media_items_dao.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/widgets/media_card.dart';
class ActivityViewMedia extends StatelessWidget {
const ActivityViewMedia({super.key, required this.activity});
final Activity activity;
@override
Widget build(BuildContext context) {
return FutureBuilder<List<MediaItem>>(
future: MediaItemsDao(Provider.of<AppDatabase>(context)).fromActivity(activity),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<MediaItem> mediaItems = snapshot.data!;
List<MediaCard> mediaCards = [];
for (int i = 0; i < mediaItems.length; i++) {
mediaCards.add(MediaCard(media: mediaItems[i]));
}
return Column(
children: [
SizedBox(
width: double.infinity,
height: 100,
child: GridView.count(
padding: const EdgeInsets.fromLTRB(15, 0, 0, 0),
scrollDirection: Axis.horizontal,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
crossAxisCount: 1,
children: mediaCards))
],
);
} else {
return Container(
alignment: Alignment.center,
child: SizedBox(
height: 50.0,
width: 50.0,
child: CircularProgressIndicator(),
));
}
}
);
}
}

View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/extensions/string_extensions.dart';
class ActivityViewTypes extends StatelessWidget {
const ActivityViewTypes({super.key, required this.types});
final List<ActivityType> types;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 40,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.only(right: 10),
itemCount: types.length,
itemBuilder: (BuildContext context, int index) {
return ActionChip(
visualDensity: VisualDensity.compact,
avatar: const Icon(Icons.fitness_center_rounded),
label: Text(maxLines: 1, types[index].name.toTitleCase()),
tooltip: "Activity Type",
onPressed: () {},
);
},
));
}
}

View File

@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/database/database.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
class MediaCard extends StatelessWidget {
const MediaCard({super.key, required this.media});
final Media media;
final MediaItem media;
@override
Widget build(BuildContext context) {
@ -14,22 +14,22 @@ class MediaCard extends StatelessWidget {
flags: const YoutubePlayerFlags(
autoPlay: false, mute: true, showLiveFullscreenButton: false));
DecorationImage mediaImage(Media media) {
DecorationImage mediaImage(MediaItem media) {
String image = '';
if (media.type == "image") {
if (media.type == MediaType.image) {
image = media.reference;
} else if (media.type == "youtube") {
} else if (media.type == MediaType.youtube) {
image = 'https://img.youtube.com/vi/${media.reference}/0.jpg';
}
return DecorationImage(image: NetworkImage(image), fit: BoxFit.cover);
}
Widget mediaItem(Media media) {
if (media.type == "image") {
Widget mediaItem(MediaItem media) {
if (media.type == MediaType.image) {
return Image(image: NetworkImage(media.reference));
} else if (media.type == "youtube") {
} else if (media.type == MediaType.youtube) {
return YoutubePlayer(
controller: controller,
aspectRatio: 16 / 9,
@ -62,7 +62,7 @@ class MediaCard extends StatelessWidget {
mediaItem(media),
const SizedBox(height: 15),
Text(
'${media.description}',
media.description,
style: const TextStyle(fontSize: 20),
),
const Divider(

View File

@ -1,11 +1,8 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:sendtrain/classes/activity_action.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/database.dart' hide ActivityAction;
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/session_model.dart';
import 'package:sendtrain/database/database.dart' hide ActivityAction;
import 'package:sendtrain/extensions/string_extensions.dart';
import 'package:sendtrain/widgets/session_view.dart';
class SessionCard extends StatelessWidget {
@ -22,155 +19,6 @@ class SessionCard extends StatelessWidget {
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.surfaceContainerLow;
// place holder until we can retrieve real data
final data = SessionModel(
id: 1,
title: "Projecting @ Climbers Rock",
content:
"Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.",
date: DateTime.now(),
activities: [
ActivityModel(
id: 1,
title: 'Campus Board',
type: 'fundamental',
categories: ['strength', 'power'],
description:
"Campus board session, focusing on explosiveness and contact strength.",
actions: [
ActivityAction(
id: 1,
title: '1, 3, 5',
description:
'Move between the first, third, and fifth rungs, alternating hands. Rest and alternate sides, to start',
media: [
Media(
id: 1,
reference:
'https://www.climbing.com/wp-content/uploads/2022/06/campus-board-e1655470701154.jpeg',
type: 'image',
description: 'Campus board movement'),
Media(
id: 1,
reference: '7ACyeOP-Hxo',
type: 'youtube',
description: 'How to campus board')
],
activityActionSet: Set(
type: 'drop_set',
total: 3,
rest: 300000,
reps: Reps(
type: 'repititions',
tempo: [0],
amounts: [1, 1, 1],
weights: [0],
rest: 20000))),
],
resources: ['https://www.youtube.com/watch?v=bLz0xp1PEm4']),
ActivityModel(
id: 2,
title: 'Projecting',
type: 'fundamental',
categories: ['technique', 'conditioning'],
description:
"Session focused on attempting a climb at or beyond your perceived limit.",
actions: [
ActivityAction(
id: 1,
title: 'Attempt Climb',
description:
'Attempt your selected climb, if you fall off early in the climb, attempt again. 1 repitition equals roughly doing all the moves, not necessarily in 1 attempt.',
media: [
Media(
id: 1,
reference:
'https://www.climbing.com/wp-content/uploads/2022/07/Fixed-44.jpg',
type: 'image',
description: 'Projecting a climb'),
Media(
id: 1,
reference: 'BgheYcxhrsw',
type: 'youtube',
description: 'How to project climbs')
],
activityActionSet: Set(
type: 'standard',
total: 10,
rest: 300000,
reps: Reps(
type: 'repititions',
tempo: [0],
amounts: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
weights: [0],
rest: 0))),
],
resources: ['https://www.youtube.com/watch?v=dyAvbUvY_PU']),
ActivityModel(
id: 3,
title: 'Long Block Pulls',
type: 'Hypertrophy',
categories: ['Strength', 'Power'],
description:
"Block pull on a edge of a specific size and time to induce a hypertrophic effect on the formarms.",
actions: [
ActivityAction(
id: 1,
title: 'Long Pulls',
description:
'Select your desired weight to pull, add it to the block. You should aim for an effort level of 8-9 when reaching the end of the set time, going to failure will result in significantly extended recovery time.',
media: [
Media(
id: 1,
reference:
'https://trailandcrag.com/sites/default/files/inline-images/05-min_3.jpg',
type: 'image',
description: 'Block pull example'),
Media(
id: 1,
reference: 'sZVAEy9UmoY',
type: 'youtube',
description:
'Principals of Grip gains, and related protocols')
],
activityActionSet: Set(
type: 'alternating',
total: 5,
rest: 5000,
reps: Reps(
type: 'seconds',
tempo: [0],
amounts: [5, 5, 5, 5, 5],
weights: [80, 80, 80, 80, 80],
rest: 5000))),
],
resources: ['https://www.youtube.com/watch?v=dyAvbUvY_PU']),
],
achievements: [
'got 1 3 5 first time!',
'no pain in elbow',
'life is pain',
'new PR for pullups'
],
media: [
Media(
id: 1,
reference: 'TwS8ycTY5cc',
type: 'youtube',
description: 'Attempting crux move'),
Media(
id: 1,
reference:
'https://static.wixstatic.com/media/c83481_1dd473ad49524ae5a95d993ba10e0a50~mv2.jpg/v1/fill/w_640,h_426,al_c,q_80,usm_0.66_1.00_0.01,enc_auto/c83481_1dd473ad49524ae5a95d993ba10e0a50~mv2.jpg',
type: 'image',
description: 'Struggling on deadpoints'),
Media(
id: 1,
reference: 'TwS8ycTY5cc',
type: 'youtube',
description: 'Attempting crux move')
]);
if (type == 0) {
return Card(
color: color,
@ -191,7 +39,7 @@ class SessionCard extends StatelessWidget {
.animate(animation);
return SlideTransition(
position: custom,
child: Dialog.fullscreen(child: SessionView(data: data, session: session)));
child: Dialog.fullscreen(child: SessionView(session: session)));
},
barrierDismissible: true,
barrierLabel: '',
@ -217,7 +65,7 @@ class SessionCard extends StatelessWidget {
BorderRadius.all(Radius.elliptical(10, 10)),
),
)),
title: Text(maxLines: 1, session.title),
title: Text(maxLines: 1, session.title.toTitleCase()),
subtitle: Text(maxLines: 1, dateFormat.format(session.date as DateTime)),
trailing: IconButton(
visualDensity: VisualDensity.compact,
@ -276,7 +124,7 @@ class SessionCard extends StatelessWidget {
return SlideTransition(
position: custom,
child:
Dialog.fullscreen(child: SessionView(data: data, session: session)));
Dialog.fullscreen(child: SessionView(session: session)));
},
barrierDismissible: true,
barrierLabel: '',
@ -303,7 +151,7 @@ class SessionCard extends StatelessWidget {
ListTile(
title: Text(
maxLines: 3,
session.title,
session.title.toTitleCase(),
textAlign: TextAlign.center),
subtitle: Text(
maxLines: 1,

View File

@ -2,18 +2,18 @@ import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/daos/activities_dao.dart';
import 'package:sendtrain/classes/media.dart';
import 'package:sendtrain/database.dart';
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/session_model.dart';
import 'package:sendtrain/widgets/activity_card.dart';
import 'package:sendtrain/widgets/media_card.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/extensions/string_extensions.dart';
import 'package:sendtrain/widgets/session_view_achievements.dart';
import 'package:sendtrain/widgets/session_view_activities.dart';
import 'package:sendtrain/widgets/session_view_media.dart';
class SessionView extends StatelessWidget {
const SessionView({super.key, required this.data, required this.session});
const SessionView({super.key, required this.session});
final SessionModel data;
final Session session;
@override
@ -21,145 +21,86 @@ class SessionView extends StatelessWidget {
initializeDateFormatting('en');
final DateFormat dateFormat = DateFormat('yyyy-MM-dd');
return Scaffold(
floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab(
distance: 70,
type: ExpandableFabType.up,
overlayStyle: ExpandableFabOverlayStyle(
color: Colors.black.withOpacity(0.5),
blur: 10,
),
children: [
FloatingActionButton.extended(
icon: const Icon(Icons.history_outlined),
label: Text('Restart'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.done_all_outlined),
label: Text('Done'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.edit_outlined),
label: Text('Edit'),
onPressed: () {},
),
]),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AppBar(
centerTitle: true,
title: Text('Session @ ${dateFormat.format(session.date as DateTime)}',
style: const TextStyle(fontSize: 15)),
),
Padding(
padding: const EdgeInsets.only(
left: 15, right: 20, top: 15, bottom: 10),
child: Text(
maxLines: 1,
style: const TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
session.title)),
SessionViewAchievements(achievements: data.achievements),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child:
Text(style: const TextStyle(fontSize: 15), session.content)),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
'Media:')),
SessionViewMedia(media: data.media),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
'Activites:')),
SessionViewActivities(activities: data.activities),
],
));
}
}
class SessionViewActivities extends StatelessWidget {
const SessionViewActivities({super.key, this.activities});
final List<ActivityModel>? activities;
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
// shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: activities?.length,
itemBuilder: (BuildContext context, int index) {
return ActivityCard(activity: activities![index]);
},
));
}
}
class SessionViewAchievements extends StatelessWidget {
const SessionViewAchievements({super.key, this.achievements});
final List<String>? achievements;
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: SizedBox(
height: 40,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: achievements?.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(right: 5),
child: ActionChip(
visualDensity: VisualDensity.compact,
avatar: const Icon(Icons.check_circle_outline),
label: Text(maxLines: 1, '${achievements?[index]}'),
onPressed: () {},
));
},
))),
],
);
}
}
class SessionViewMedia extends StatelessWidget {
const SessionViewMedia({super.key, this.media});
final List<Media>? media;
@override
Widget build(BuildContext context) {
List<Widget> mediaCards = List.generate((media != null) ? media!.length : 0,
(i) => MediaCard(media: media![i]));
return Column(
children: [
SizedBox(
width: double.infinity,
height: 100,
child: GridView.count(
padding: const EdgeInsets.fromLTRB(15, 0, 0, 0),
scrollDirection: Axis.horizontal,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
crossAxisCount: 1,
children: mediaCards))
],
);
return FutureBuilder<List<Activity>>(
future: ActivitiesDao(Provider.of<AppDatabase>(context)).sessionActivities(session.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
final activities = snapshot.data!;
return Scaffold(
floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab(
distance: 70,
type: ExpandableFabType.up,
overlayStyle: ExpandableFabOverlayStyle(
color: Colors.black.withOpacity(0.5),
blur: 10,
),
children: [
FloatingActionButton.extended(
icon: const Icon(Icons.history_outlined),
label: Text('Restart'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.done_all_outlined),
label: Text('Done'),
onPressed: () {},
),
FloatingActionButton.extended(
icon: const Icon(Icons.edit_outlined),
label: Text('Edit'),
onPressed: () {},
),
]),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AppBar(
centerTitle: true,
title: Text(
'Session @ ${dateFormat.format(session.date as DateTime)}',
style: const TextStyle(fontSize: 15)),
),
Padding(
padding: const EdgeInsets.only(
left: 15, right: 20, top: 15, bottom: 10),
child: Text(
maxLines: 1,
style: const TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
session.title.toTitleCase())),
SessionViewAchievements(session: session),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child: Text(
style: const TextStyle(fontSize: 15),
session.content)),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
'Media:')),
SessionViewMedia(session: session),
const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text(
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
'Activites:')),
SessionViewActivities(
activities: activities),
],
));
} else {
return Container(
alignment: Alignment.center,
child: SizedBox(
height: 50.0,
width: 50.0,
child: CircularProgressIndicator(),
));
}
});
}
}

View File

@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/daos/session_activities_dao.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/extensions/string_extensions.dart';
class SessionViewAchievements extends StatelessWidget {
const SessionViewAchievements({super.key, required this.session});
final Session session;
List<String> getAchievements(List<SessionActivity> sessionActivities) {
List<String> achievements = [];
for (int i = 0; i < sessionActivities.length; i++) {
final SessionActivity sessionActivity = sessionActivities[i];
final List? saAchievments = sessionActivity.achievements?.split(',');
if (saAchievments != null) {
saAchievments.forEach((achievement) => achievements.add(achievement));
}
}
return achievements;
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<SessionActivity>>(
future: SessionActivitiesDao(Provider.of<AppDatabase>(context))
.fromSessionId(session.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
final sessionActivities = snapshot.data!;
final achievements = getAchievements(sessionActivities);
return Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: SizedBox(
height: 40,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: achievements.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(right: 5),
child: ActionChip(
visualDensity: VisualDensity.compact,
avatar:
const Icon(Icons.check_circle_outline),
label: Text(maxLines: 1, achievements[index].toTitleCase()),
onPressed: () {},
));
},
))),
],
);
} else {
return Padding(
padding: EdgeInsets.all(15),
child: CircularProgressIndicator());
}
});
}
}

View File

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/widgets/activity_card.dart';
class SessionViewActivities extends StatelessWidget {
const SessionViewActivities({super.key, required this.activities });
final List<Activity> activities;
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
// shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
itemCount: activities.length,
itemBuilder: (BuildContext context, int index) {
return ActivityCard(
activity: activities[index]);
},
));
}
}

View File

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/daos/media_items_dao.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/widgets/media_card.dart';
class SessionViewMedia extends StatelessWidget {
const SessionViewMedia({super.key, required this.session});
final Session session;
@override
Widget build(BuildContext context) {
return FutureBuilder<List<MediaItem>>(
future: MediaItemsDao(Provider.of<AppDatabase>(context))
.fromSession(session),
builder: (context, snapshot) {
if (snapshot.hasData) {
final mediaItems = snapshot.data!;
List<Widget> mediaCards = List.generate(
mediaItems.length, (i) => MediaCard(media: mediaItems[i]));
return Column(
children: [
SizedBox(
width: double.infinity,
height: 100,
child: GridView.count(
padding: const EdgeInsets.fromLTRB(15, 0, 0, 0),
scrollDirection: Axis.horizontal,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
crossAxisCount: 1,
children: mediaCards))
],
);
} else {
return Padding(
padding: EdgeInsets.all(15),
child: CircularProgressIndicator());
}
});
}
}

View File

@ -61,6 +61,7 @@ dev_dependencies:
build_runner: ^2.4.13
json_serializable: ^6.9.0
drift_dev: ^2.22.1
test: ^1.25.7
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View File

@ -0,0 +1,29 @@
// dart format width=80
// GENERATED CODE, DO NOT EDIT BY HAND.
// ignore_for_file: type=lint
import 'package:drift/drift.dart';
import 'package:drift/internal/migrations.dart';
import 'schema_v1.dart' as v1;
import 'schema_v2.dart' as v2;
import 'schema_v3.dart' as v3;
import 'schema_v4.dart' as v4;
class GeneratedHelper implements SchemaInstantiationHelper {
@override
GeneratedDatabase databaseForVersion(QueryExecutor db, int version) {
switch (version) {
case 1:
return v1.DatabaseAtV1(db);
case 2:
return v2.DatabaseAtV2(db);
case 3:
return v3.DatabaseAtV3(db);
case 4:
return v4.DatabaseAtV4(db);
default:
throw MissingSchemaException(version, versions);
}
}
static const versions = const [1, 2, 3, 4];
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
// // dart format width=80
// // ignore_for_file: unused_local_variable, unused_import
// import 'package:drift/drift.dart';
// import 'package:drift_dev/api/migrations_native.dart';
// import 'package:sendtrain/database/database.dart';
// import 'package:test/test.dart';
// import 'generated/schema.dart';
// import 'generated/schema_v1.dart' as v1;
// import 'generated/schema_v2.dart' as v2;
// void main() {
// driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
// late SchemaVerifier verifier;
// setUpAll(() {
// verifier = SchemaVerifier(GeneratedHelper());
// });
// group('simple database migrations', () {
// // These simple tests verify all possible schema updates with a simple (no
// // data) migration. This is a quick way to ensure that written database
// // migrations properly alter the schema.
// final versions = GeneratedHelper.versions;
// for (final (i, fromVersion) in versions.indexed) {
// group('from $fromVersion', () {
// for (final toVersion in versions.skip(i + 1)) {
// test('to $toVersion', () async {
// final schema = await verifier.schemaAt(fromVersion);
// final db = AppDatabase(schema.newConnection());
// await verifier.migrateAndValidate(db, toVersion);
// await db.close();
// });
// }
// });
// }
// });
// // The following template shows how to write tests ensuring your migrations
// // preserve existing data.
// // Testing this can be useful for migrations that change existing columns
// // (e.g. by alterating their type or constraints). Migrations that only add
// // tables or columns typically don't need these advanced tests. For more
// // information, see https://drift.simonbinder.eu/migrations/tests/#verifying-data-integrity
// // TODO: This generated template shows how these tests could be written. Adopt
// // it to your own needs when testing migrations with data integrity.
// test("migration from v1 to v2 does not corrupt data", () async {
// // Add data to insert into the old database, and the expected rows after the
// // migration.
// // TODO: Fill these lists
// final oldSessionsData = <v1.SessionsData>[];
// final expectedNewSessionsData = <v2.SessionsData>[];
// final oldActivitiesData = <v1.ActivitiesData>[];
// final expectedNewActivitiesData = <v2.ActivitiesData>[];
// final oldSessionActivitiesData = <v1.SessionActivitiesData>[];
// final expectedNewSessionActivitiesData = <v2.SessionActivitiesData>[];
// final oldActionsData = <v1.ActionsData>[];
// final expectedNewActionsData = <v2.ActionsData>[];
// final oldActivityActionsData = <v1.ActivityActionsData>[];
// final expectedNewActivityActionsData = <v2.ActivityActionsData>[];
// final oldMediaItemsData = <v1.MediaItemsData>[];
// final expectedNewMediaItemsData = <v2.MediaItemsData>[];
// final oldObjectMediaItemsData = <v1.ObjectMediaItemsData>[];
// final expectedNewObjectMediaItemsData = <v2.ObjectMediaItemsData>[];
// await verifier.testWithDataIntegrity(
// oldVersion: 1,
// newVersion: 2,
// createOld: v1.DatabaseAtV1.new,
// createNew: v2.DatabaseAtV2.new,
// openTestedDatabase: AppDatabase.new,
// createItems: (batch, oldDb) {
// batch.insertAll(oldDb.sessions, oldSessionsData);
// batch.insertAll(oldDb.activities, oldActivitiesData);
// batch.insertAll(oldDb.sessionActivities, oldSessionActivitiesData);
// batch.insertAll(oldDb.actions, oldActionsData);
// batch.insertAll(oldDb.activityActions, oldActivityActionsData);
// batch.insertAll(oldDb.mediaItems, oldMediaItemsData);
// batch.insertAll(oldDb.objectMediaItems, oldObjectMediaItemsData);
// },
// validateItems: (newDb) async {
// expect(
// expectedNewSessionsData, await newDb.select(newDb.sessions).get());
// expect(expectedNewActivitiesData,
// await newDb.select(newDb.activities).get());
// expect(expectedNewSessionActivitiesData,
// await newDb.select(newDb.sessionActivities).get());
// expect(expectedNewActionsData, await newDb.select(newDb.actions).get());
// expect(expectedNewActivityActionsData,
// await newDb.select(newDb.activityActions).get());
// expect(expectedNewMediaItemsData,
// await newDb.select(newDb.mediaItems).get());
// expect(expectedNewObjectMediaItemsData,
// await newDb.select(newDb.objectMediaItems).get());
// },
// );
// });
// }