added sound during countdown, and upgraded minsdkversion

This commit is contained in:
Joshua Burman 2025-02-10 17:08:13 -05:00
parent 60bc571987
commit 23663f484b
26 changed files with 6661 additions and 145 deletions

Binary file not shown.

BIN
assets/audio/count_tone.wav Normal file

Binary file not shown.

View File

@ -15,7 +15,7 @@ class ActionsDao extends DatabaseAccessor<AppDatabase> with _$ActionsDaoMixin {
return await (select(actions)..where((action) => action.id.equals(id) )).getSingle();
}
Future<List<Action>> fromActivity(Activity activity) async {
Future<List<Action>> fromActivity(Activity activity, Session session) async {
final result = select(db.activityActions).join(
[
innerJoin(
@ -24,7 +24,8 @@ class ActionsDao extends DatabaseAccessor<AppDatabase> with _$ActionsDaoMixin {
),
],
)
..where(db.activityActions.activityId.equals(activity.id));
..where(db.activityActions.activityId.equals(activity.id))
..where(db.activityActions.sessionId.equals(session.id));
final actions = (await result.get())
.map((e) => e.readTable(db.actions))
@ -33,5 +34,31 @@ class ActionsDao extends DatabaseAccessor<AppDatabase> with _$ActionsDaoMixin {
return actions;
}
Stream<List<Action>> watchActivityActions(Activity activity, Session session) {
final result = select(db.activityActions).join(
[
innerJoin(
db.actions,
db.actions.id.equalsExp(db.activityActions.actionId),
),
],
)
..where(db.activityActions.activityId.equals(activity.id))
..where(db.activityActions.sessionId.equals(session.id));
// final actions = result.watch().map((rows) {
// return rows.map((row) {
// row.readTable(db.actions);
// }).toList();
// });
final actions = (result.watch()).map((rows) {
return rows.map((row) => row.readTable(db.actions)).toList();
});
return actions;
}
Future createOrUpdate(ActionsCompanion action) => into(actions).insertOnConflictUpdate(action);
Future replace(Action action) => update(actions).replace(action);
}

View File

@ -12,6 +12,7 @@ class ActivityActionsDao extends DatabaseAccessor<AppDatabase> with _$ActivityAc
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 createOrUpdate(ActivityActionsCompanion activityAction) => into(activityActions).insertOnConflictUpdate(activityAction);
// Future<List<ActivityAction>> all() async {
// return await select(activityActions).get();

View File

@ -6,5 +6,6 @@ part of 'activity_actions_dao.dart';
mixin _$ActivityActionsDaoMixin on DatabaseAccessor<AppDatabase> {
$ActivitiesTable get activities => attachedDatabase.activities;
$ActionsTable get actions => attachedDatabase.actions;
$SessionsTable get sessions => attachedDatabase.sessions;
$ActivityActionsTable get activityActions => attachedDatabase.activityActions;
}

View File

@ -35,7 +35,7 @@ class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 33;
int get schemaVersion => 35;
@override
MigrationStrategy get migration {
@ -155,10 +155,10 @@ class Activities extends Table {
class ActivityActions extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get activityId =>
integer().references(Activities, #id, onDelete: KeyAction.cascade)();
IntColumn get activityId => integer().references(Activities, #id, onDelete: KeyAction.cascade)();
IntColumn get actionId =>
integer().references(Actions, #id, onDelete: KeyAction.cascade)();
IntColumn get sessionId => integer().references(Sessions, #id, onDelete: KeyAction.cascade)();
IntColumn get position => integer()();
DateTimeColumn get createdAt =>
dateTime().withDefault(Variable(DateTime.now()))();

View File

@ -2365,6 +2365,15 @@ class $ActivityActionsTable extends ActivityActions
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES actions (id) ON DELETE CASCADE'));
static const VerificationMeta _sessionIdMeta =
const VerificationMeta('sessionId');
@override
late final GeneratedColumn<int> sessionId = GeneratedColumn<int>(
'session_id', aliasedName, false,
type: DriftSqlType.int,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES sessions (id) ON DELETE CASCADE'));
static const VerificationMeta _positionMeta =
const VerificationMeta('position');
@override
@ -2381,7 +2390,7 @@ class $ActivityActionsTable extends ActivityActions
defaultValue: Variable(DateTime.now()));
@override
List<GeneratedColumn> get $columns =>
[id, activityId, actionId, position, createdAt];
[id, activityId, actionId, sessionId, position, createdAt];
@override
String get aliasedName => _alias ?? actualTableName;
@override
@ -2409,6 +2418,12 @@ class $ActivityActionsTable extends ActivityActions
} else if (isInserting) {
context.missing(_actionIdMeta);
}
if (data.containsKey('session_id')) {
context.handle(_sessionIdMeta,
sessionId.isAcceptableOrUnknown(data['session_id']!, _sessionIdMeta));
} else if (isInserting) {
context.missing(_sessionIdMeta);
}
if (data.containsKey('position')) {
context.handle(_positionMeta,
position.isAcceptableOrUnknown(data['position']!, _positionMeta));
@ -2434,6 +2449,8 @@ class $ActivityActionsTable extends ActivityActions
.read(DriftSqlType.int, data['${effectivePrefix}activity_id'])!,
actionId: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}action_id'])!,
sessionId: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}session_id'])!,
position: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}position'])!,
createdAt: attachedDatabase.typeMapping
@ -2451,12 +2468,14 @@ class ActivityAction extends DataClass implements Insertable<ActivityAction> {
final int id;
final int activityId;
final int actionId;
final int sessionId;
final int position;
final DateTime createdAt;
const ActivityAction(
{required this.id,
required this.activityId,
required this.actionId,
required this.sessionId,
required this.position,
required this.createdAt});
@override
@ -2465,6 +2484,7 @@ class ActivityAction extends DataClass implements Insertable<ActivityAction> {
map['id'] = Variable<int>(id);
map['activity_id'] = Variable<int>(activityId);
map['action_id'] = Variable<int>(actionId);
map['session_id'] = Variable<int>(sessionId);
map['position'] = Variable<int>(position);
map['created_at'] = Variable<DateTime>(createdAt);
return map;
@ -2475,6 +2495,7 @@ class ActivityAction extends DataClass implements Insertable<ActivityAction> {
id: Value(id),
activityId: Value(activityId),
actionId: Value(actionId),
sessionId: Value(sessionId),
position: Value(position),
createdAt: Value(createdAt),
);
@ -2487,6 +2508,7 @@ class ActivityAction extends DataClass implements Insertable<ActivityAction> {
id: serializer.fromJson<int>(json['id']),
activityId: serializer.fromJson<int>(json['activityId']),
actionId: serializer.fromJson<int>(json['actionId']),
sessionId: serializer.fromJson<int>(json['sessionId']),
position: serializer.fromJson<int>(json['position']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
);
@ -2498,6 +2520,7 @@ class ActivityAction extends DataClass implements Insertable<ActivityAction> {
'id': serializer.toJson<int>(id),
'activityId': serializer.toJson<int>(activityId),
'actionId': serializer.toJson<int>(actionId),
'sessionId': serializer.toJson<int>(sessionId),
'position': serializer.toJson<int>(position),
'createdAt': serializer.toJson<DateTime>(createdAt),
};
@ -2507,12 +2530,14 @@ class ActivityAction extends DataClass implements Insertable<ActivityAction> {
{int? id,
int? activityId,
int? actionId,
int? sessionId,
int? position,
DateTime? createdAt}) =>
ActivityAction(
id: id ?? this.id,
activityId: activityId ?? this.activityId,
actionId: actionId ?? this.actionId,
sessionId: sessionId ?? this.sessionId,
position: position ?? this.position,
createdAt: createdAt ?? this.createdAt,
);
@ -2522,6 +2547,7 @@ class ActivityAction extends DataClass implements Insertable<ActivityAction> {
activityId:
data.activityId.present ? data.activityId.value : this.activityId,
actionId: data.actionId.present ? data.actionId.value : this.actionId,
sessionId: data.sessionId.present ? data.sessionId.value : this.sessionId,
position: data.position.present ? data.position.value : this.position,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
);
@ -2533,6 +2559,7 @@ class ActivityAction extends DataClass implements Insertable<ActivityAction> {
..write('id: $id, ')
..write('activityId: $activityId, ')
..write('actionId: $actionId, ')
..write('sessionId: $sessionId, ')
..write('position: $position, ')
..write('createdAt: $createdAt')
..write(')'))
@ -2541,7 +2568,7 @@ class ActivityAction extends DataClass implements Insertable<ActivityAction> {
@override
int get hashCode =>
Object.hash(id, activityId, actionId, position, createdAt);
Object.hash(id, activityId, actionId, sessionId, position, createdAt);
@override
bool operator ==(Object other) =>
identical(this, other) ||
@ -2549,6 +2576,7 @@ class ActivityAction extends DataClass implements Insertable<ActivityAction> {
other.id == this.id &&
other.activityId == this.activityId &&
other.actionId == this.actionId &&
other.sessionId == this.sessionId &&
other.position == this.position &&
other.createdAt == this.createdAt);
}
@ -2557,12 +2585,14 @@ class ActivityActionsCompanion extends UpdateCompanion<ActivityAction> {
final Value<int> id;
final Value<int> activityId;
final Value<int> actionId;
final Value<int> sessionId;
final Value<int> position;
final Value<DateTime> createdAt;
const ActivityActionsCompanion({
this.id = const Value.absent(),
this.activityId = const Value.absent(),
this.actionId = const Value.absent(),
this.sessionId = const Value.absent(),
this.position = const Value.absent(),
this.createdAt = const Value.absent(),
});
@ -2570,15 +2600,18 @@ class ActivityActionsCompanion extends UpdateCompanion<ActivityAction> {
this.id = const Value.absent(),
required int activityId,
required int actionId,
required int sessionId,
required int position,
this.createdAt = const Value.absent(),
}) : activityId = Value(activityId),
actionId = Value(actionId),
sessionId = Value(sessionId),
position = Value(position);
static Insertable<ActivityAction> custom({
Expression<int>? id,
Expression<int>? activityId,
Expression<int>? actionId,
Expression<int>? sessionId,
Expression<int>? position,
Expression<DateTime>? createdAt,
}) {
@ -2586,6 +2619,7 @@ class ActivityActionsCompanion extends UpdateCompanion<ActivityAction> {
if (id != null) 'id': id,
if (activityId != null) 'activity_id': activityId,
if (actionId != null) 'action_id': actionId,
if (sessionId != null) 'session_id': sessionId,
if (position != null) 'position': position,
if (createdAt != null) 'created_at': createdAt,
});
@ -2595,12 +2629,14 @@ class ActivityActionsCompanion extends UpdateCompanion<ActivityAction> {
{Value<int>? id,
Value<int>? activityId,
Value<int>? actionId,
Value<int>? sessionId,
Value<int>? position,
Value<DateTime>? createdAt}) {
return ActivityActionsCompanion(
id: id ?? this.id,
activityId: activityId ?? this.activityId,
actionId: actionId ?? this.actionId,
sessionId: sessionId ?? this.sessionId,
position: position ?? this.position,
createdAt: createdAt ?? this.createdAt,
);
@ -2618,6 +2654,9 @@ class ActivityActionsCompanion extends UpdateCompanion<ActivityAction> {
if (actionId.present) {
map['action_id'] = Variable<int>(actionId.value);
}
if (sessionId.present) {
map['session_id'] = Variable<int>(sessionId.value);
}
if (position.present) {
map['position'] = Variable<int>(position.value);
}
@ -2633,6 +2672,7 @@ class ActivityActionsCompanion extends UpdateCompanion<ActivityAction> {
..write('id: $id, ')
..write('activityId: $activityId, ')
..write('actionId: $actionId, ')
..write('sessionId: $sessionId, ')
..write('position: $position, ')
..write('createdAt: $createdAt')
..write(')'))
@ -3359,6 +3399,13 @@ abstract class _$AppDatabase extends GeneratedDatabase {
TableUpdate('activity_actions', kind: UpdateKind.delete),
],
),
WritePropagation(
on: TableUpdateQuery.onTableName('sessions',
limitUpdateKind: UpdateKind.delete),
result: [
TableUpdate('activity_actions', kind: UpdateKind.delete),
],
),
WritePropagation(
on: TableUpdateQuery.onTableName('media_items',
limitUpdateKind: UpdateKind.delete),
@ -3411,6 +3458,23 @@ final class $$SessionsTableReferences
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: cache));
}
static MultiTypedResultKey<$ActivityActionsTable, List<ActivityAction>>
_activityActionsRefsTable(_$AppDatabase db) =>
MultiTypedResultKey.fromTable(db.activityActions,
aliasName: $_aliasNameGenerator(
db.sessions.id, db.activityActions.sessionId));
$$ActivityActionsTableProcessedTableManager get activityActionsRefs {
final manager =
$$ActivityActionsTableTableManager($_db, $_db.activityActions)
.filter((f) => f.sessionId.id($_item.id));
final cache =
$_typedResult.readTableOrNull(_activityActionsRefsTable($_db));
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: cache));
}
}
class $$SessionsTableFilterComposer
@ -3468,6 +3532,27 @@ class $$SessionsTableFilterComposer
));
return f(composer);
}
Expression<bool> activityActionsRefs(
Expression<bool> Function($$ActivityActionsTableFilterComposer f) f) {
final $$ActivityActionsTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.id,
referencedTable: $db.activityActions,
getReferencedColumn: (t) => t.sessionId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$ActivityActionsTableFilterComposer(
$db: $db,
$table: $db.activityActions,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return f(composer);
}
}
class $$SessionsTableOrderingComposer
@ -3559,6 +3644,27 @@ class $$SessionsTableAnnotationComposer
));
return f(composer);
}
Expression<T> activityActionsRefs<T extends Object>(
Expression<T> Function($$ActivityActionsTableAnnotationComposer a) f) {
final $$ActivityActionsTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.id,
referencedTable: $db.activityActions,
getReferencedColumn: (t) => t.sessionId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$ActivityActionsTableAnnotationComposer(
$db: $db,
$table: $db.activityActions,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return f(composer);
}
}
class $$SessionsTableTableManager extends RootTableManager<
@ -3572,7 +3678,8 @@ class $$SessionsTableTableManager extends RootTableManager<
$$SessionsTableUpdateCompanionBuilder,
(Session, $$SessionsTableReferences),
Session,
PrefetchHooks Function({bool sessionActivitiesRefs})> {
PrefetchHooks Function(
{bool sessionActivitiesRefs, bool activityActionsRefs})> {
$$SessionsTableTableManager(_$AppDatabase db, $SessionsTable table)
: super(TableManagerState(
db: db,
@ -3627,11 +3734,13 @@ class $$SessionsTableTableManager extends RootTableManager<
.map((e) =>
(e.readTable(table), $$SessionsTableReferences(db, table, e)))
.toList(),
prefetchHooksCallback: ({sessionActivitiesRefs = false}) {
prefetchHooksCallback: (
{sessionActivitiesRefs = false, activityActionsRefs = false}) {
return PrefetchHooks(
db: db,
explicitlyWatchedTables: [
if (sessionActivitiesRefs) db.sessionActivities
if (sessionActivitiesRefs) db.sessionActivities,
if (activityActionsRefs) db.activityActions
],
addJoins: null,
getPrefetchedDataCallback: (items) async {
@ -3647,6 +3756,18 @@ class $$SessionsTableTableManager extends RootTableManager<
referencedItemsForCurrentItem:
(item, referencedItems) => referencedItems
.where((e) => e.sessionId == item.id),
typedResults: items),
if (activityActionsRefs)
await $_getPrefetchedData(
currentTable: table,
referencedTable: $$SessionsTableReferences
._activityActionsRefsTable(db),
managerFromTypedResult: (p0) =>
$$SessionsTableReferences(db, table, p0)
.activityActionsRefs,
referencedItemsForCurrentItem:
(item, referencedItems) => referencedItems
.where((e) => e.sessionId == item.id),
typedResults: items)
];
},
@ -3666,7 +3787,8 @@ typedef $$SessionsTableProcessedTableManager = ProcessedTableManager<
$$SessionsTableUpdateCompanionBuilder,
(Session, $$SessionsTableReferences),
Session,
PrefetchHooks Function({bool sessionActivitiesRefs})>;
PrefetchHooks Function(
{bool sessionActivitiesRefs, bool activityActionsRefs})>;
typedef $$ActivitiesTableCreateCompanionBuilder = ActivitiesCompanion Function({
Value<int> id,
required String title,
@ -4953,6 +5075,7 @@ typedef $$ActivityActionsTableCreateCompanionBuilder = ActivityActionsCompanion
Value<int> id,
required int activityId,
required int actionId,
required int sessionId,
required int position,
Value<DateTime> createdAt,
});
@ -4961,6 +5084,7 @@ typedef $$ActivityActionsTableUpdateCompanionBuilder = ActivityActionsCompanion
Value<int> id,
Value<int> activityId,
Value<int> actionId,
Value<int> sessionId,
Value<int> position,
Value<DateTime> createdAt,
});
@ -4995,6 +5119,19 @@ final class $$ActivityActionsTableReferences extends BaseReferences<
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]));
}
static $SessionsTable _sessionIdTable(_$AppDatabase db) =>
db.sessions.createAlias(
$_aliasNameGenerator(db.activityActions.sessionId, db.sessions.id));
$$SessionsTableProcessedTableManager get sessionId {
final manager = $$SessionsTableTableManager($_db, $_db.sessions)
.filter((f) => f.id($_item.sessionId!));
final item = $_typedResult.readTableOrNull(_sessionIdTable($_db));
if (item == null) return manager;
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]));
}
}
class $$ActivityActionsTableFilterComposer
@ -5054,6 +5191,26 @@ class $$ActivityActionsTableFilterComposer
));
return composer;
}
$$SessionsTableFilterComposer get sessionId {
final $$SessionsTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.sessionId,
referencedTable: $db.sessions,
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$SessionsTableFilterComposer(
$db: $db,
$table: $db.sessions,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$ActivityActionsTableOrderingComposer
@ -5113,6 +5270,26 @@ class $$ActivityActionsTableOrderingComposer
));
return composer;
}
$$SessionsTableOrderingComposer get sessionId {
final $$SessionsTableOrderingComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.sessionId,
referencedTable: $db.sessions,
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$SessionsTableOrderingComposer(
$db: $db,
$table: $db.sessions,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$ActivityActionsTableAnnotationComposer
@ -5172,6 +5349,26 @@ class $$ActivityActionsTableAnnotationComposer
));
return composer;
}
$$SessionsTableAnnotationComposer get sessionId {
final $$SessionsTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.sessionId,
referencedTable: $db.sessions,
getReferencedColumn: (t) => t.id,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$SessionsTableAnnotationComposer(
$db: $db,
$table: $db.sessions,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
}
class $$ActivityActionsTableTableManager extends RootTableManager<
@ -5185,7 +5382,7 @@ class $$ActivityActionsTableTableManager extends RootTableManager<
$$ActivityActionsTableUpdateCompanionBuilder,
(ActivityAction, $$ActivityActionsTableReferences),
ActivityAction,
PrefetchHooks Function({bool activityId, bool actionId})> {
PrefetchHooks Function({bool activityId, bool actionId, bool sessionId})> {
$$ActivityActionsTableTableManager(
_$AppDatabase db, $ActivityActionsTable table)
: super(TableManagerState(
@ -5201,6 +5398,7 @@ class $$ActivityActionsTableTableManager extends RootTableManager<
Value<int> id = const Value.absent(),
Value<int> activityId = const Value.absent(),
Value<int> actionId = const Value.absent(),
Value<int> sessionId = const Value.absent(),
Value<int> position = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
}) =>
@ -5208,6 +5406,7 @@ class $$ActivityActionsTableTableManager extends RootTableManager<
id: id,
activityId: activityId,
actionId: actionId,
sessionId: sessionId,
position: position,
createdAt: createdAt,
),
@ -5215,6 +5414,7 @@ class $$ActivityActionsTableTableManager extends RootTableManager<
Value<int> id = const Value.absent(),
required int activityId,
required int actionId,
required int sessionId,
required int position,
Value<DateTime> createdAt = const Value.absent(),
}) =>
@ -5222,6 +5422,7 @@ class $$ActivityActionsTableTableManager extends RootTableManager<
id: id,
activityId: activityId,
actionId: actionId,
sessionId: sessionId,
position: position,
createdAt: createdAt,
),
@ -5231,7 +5432,8 @@ class $$ActivityActionsTableTableManager extends RootTableManager<
$$ActivityActionsTableReferences(db, table, e)
))
.toList(),
prefetchHooksCallback: ({activityId = false, actionId = false}) {
prefetchHooksCallback: (
{activityId = false, actionId = false, sessionId = false}) {
return PrefetchHooks(
db: db,
explicitlyWatchedTables: [],
@ -5269,6 +5471,16 @@ class $$ActivityActionsTableTableManager extends RootTableManager<
$$ActivityActionsTableReferences._actionIdTable(db).id,
) as T;
}
if (sessionId) {
state = state.withJoin(
currentTable: table,
currentColumn: table.sessionId,
referencedTable:
$$ActivityActionsTableReferences._sessionIdTable(db),
referencedColumn:
$$ActivityActionsTableReferences._sessionIdTable(db).id,
) as T;
}
return state;
},
@ -5291,7 +5503,7 @@ typedef $$ActivityActionsTableProcessedTableManager = ProcessedTableManager<
$$ActivityActionsTableUpdateCompanionBuilder,
(ActivityAction, $$ActivityActionsTableReferences),
ActivityAction,
PrefetchHooks Function({bool activityId, bool actionId})>;
PrefetchHooks Function({bool activityId, bool actionId, bool sessionId})>;
typedef $$MediaItemsTableCreateCompanionBuilder = MediaItemsCompanion Function({
Value<int> id,
required String title,

View File

@ -5287,6 +5287,329 @@ i1.GeneratedColumn<String> _column_62(String aliasedName) => i1.GeneratedColumn<
type: i1.DriftSqlType.string,
defaultValue: Variable(
"{\"currentSet\": 0, \"currentRep\": 0, \"currentActionType\": 0, \"currentTime\": 0, \"currentAction\": 0}"));
final class Schema34 extends i0.VersionedSchema {
Schema34({required super.database}) : super(version: 34);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
sessions,
activities,
sessionActivities,
actions,
activityActions,
mediaItems,
objectMediaItems,
];
late final Shape12 sessions = Shape12(
source: i0.VersionedTable(
entityName: 'sessions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_2,
_column_3,
_column_11,
_column_20,
_column_4,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape17 activities = Shape17(
source: i0.VersionedTable(
entityName: 'activities',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_40,
_column_36,
_column_37,
_column_38,
_column_27,
_column_28,
_column_29,
_column_35,
_column_31,
_column_32,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape13 sessionActivities = Shape13(
source: i0.VersionedTable(
entityName: 'session_activities',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_21,
_column_22,
_column_19,
_column_10,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape21 actions = Shape21(
source: i0.VersionedTable(
entityName: 'actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_41,
_column_2,
_column_42,
_column_43,
_column_44,
_column_45,
_column_46,
_column_47,
_column_48,
_column_49,
_column_53,
_column_54,
_column_51,
_column_52,
_column_58,
_column_62,
_column_12,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape22 activityActions = Shape22(
source: i0.VersionedTable(
entityName: 'activity_actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_9,
_column_23,
_column_8,
_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_41,
_column_2,
_column_25,
_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_24,
_column_5,
],
attachedDatabase: database,
),
alias: null);
}
class Shape22 extends i0.VersionedTable {
Shape22({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 sessionId =>
columnsByName['session_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>;
}
final class Schema35 extends i0.VersionedSchema {
Schema35({required super.database}) : super(version: 35);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
sessions,
activities,
sessionActivities,
actions,
activityActions,
mediaItems,
objectMediaItems,
];
late final Shape12 sessions = Shape12(
source: i0.VersionedTable(
entityName: 'sessions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_1,
_column_2,
_column_3,
_column_11,
_column_20,
_column_4,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape17 activities = Shape17(
source: i0.VersionedTable(
entityName: 'activities',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_40,
_column_36,
_column_37,
_column_38,
_column_27,
_column_28,
_column_29,
_column_35,
_column_31,
_column_32,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape13 sessionActivities = Shape13(
source: i0.VersionedTable(
entityName: 'session_activities',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_21,
_column_22,
_column_19,
_column_10,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape21 actions = Shape21(
source: i0.VersionedTable(
entityName: 'actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_41,
_column_2,
_column_42,
_column_43,
_column_44,
_column_45,
_column_46,
_column_47,
_column_48,
_column_49,
_column_53,
_column_54,
_column_51,
_column_52,
_column_58,
_column_62,
_column_12,
_column_5,
],
attachedDatabase: database,
),
alias: null);
late final Shape22 activityActions = Shape22(
source: i0.VersionedTable(
entityName: 'activity_actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_0,
_column_22,
_column_23,
_column_21,
_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_41,
_column_2,
_column_25,
_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_24,
_column_5,
],
attachedDatabase: database,
),
alias: null);
}
i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
@ -5320,6 +5643,8 @@ i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema31 schema) from30To31,
required Future<void> Function(i1.Migrator m, Schema32 schema) from31To32,
required Future<void> Function(i1.Migrator m, Schema33 schema) from32To33,
required Future<void> Function(i1.Migrator m, Schema34 schema) from33To34,
required Future<void> Function(i1.Migrator m, Schema35 schema) from34To35,
}) {
return (currentVersion, database) async {
switch (currentVersion) {
@ -5483,6 +5808,16 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema);
await from32To33(migrator, schema);
return 33;
case 33:
final schema = Schema34(database: database);
final migrator = i1.Migrator(database, schema);
await from33To34(migrator, schema);
return 34;
case 34:
final schema = Schema35(database: database);
final migrator = i1.Migrator(database, schema);
await from34To35(migrator, schema);
return 35;
default:
throw ArgumentError.value('Unknown migration from $currentVersion');
}
@ -5522,6 +5857,8 @@ i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema31 schema) from30To31,
required Future<void> Function(i1.Migrator m, Schema32 schema) from31To32,
required Future<void> Function(i1.Migrator m, Schema33 schema) from32To33,
required Future<void> Function(i1.Migrator m, Schema34 schema) from33To34,
required Future<void> Function(i1.Migrator m, Schema35 schema) from34To35,
}) =>
i0.VersionedSchema.stepByStepHelper(
step: migrationSteps(
@ -5557,4 +5894,6 @@ i1.OnUpgrade stepByStep({
from30To31: from30To31,
from31To32: from31To32,
from32To33: from32To33,
from33To34: from33To34,
from34To35: from34To35,
));

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -43,14 +43,14 @@ Future<void> seedDb(AppDatabase database) async {
['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 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 totalActions = 5;
final int totalMedia = 5;
final random = Random();
@ -73,7 +73,8 @@ Future<void> seedDb(AppDatabase database) async {
Map<Symbol, Value> payload = {
Symbol('title'): Value<String>(exercise['name']),
Symbol('description'): Value<String>(json.encode(exercise['instructions'])),
Symbol('description'):
Value<String>(json.encode(exercise['instructions'])),
Symbol('force'): Value<String>(exercise['force'] ?? "")
};
@ -125,8 +126,7 @@ Future<void> seedDb(AppDatabase database) async {
.into(database.mediaItems)
.insert(MediaItemsCompanion.insert(
title: exercise['name'],
description:
exercise['name'],
description: exercise['name'],
reference: mediaItem,
type: MediaType.image))
.then((mediaId) async {
@ -175,32 +175,56 @@ Future<void> seedDb(AppDatabase database) async {
));
// 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.',
totalSets: 5,
totalReps: "[1]",
restBeforeSets: Value(30000),
restBetweenSets: Value(300000),
restBetweenReps: Value(15000),
restAfterSets: Value(300000),
repType: RepType.time,
repLength: Value(10000),
repWeights: Value("[110]"),
setWeights: Value("[1]"),
isAlternating: Value(true),
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));
});
}
// await database
// .into(database.actions)
// .insert(ActionsCompanion.insert(
// title: 'Test action',
// description:
// 'Beta pully beta beta pinch one arm crimpy. Futuristic pinch, dyno dynamic drop knee climb. Climbing ondra slopey onsight beta ondra power endurance.',
// totalSets: 5,
// totalReps: "[1]",
// restBeforeSets: Value(30000),
// restBetweenSets: Value(300000),
// restBetweenReps: Value(15000),
// restAfterSets: Value(300000),
// repType: RepType.time,
// repLength: Value(10000),
// repWeights: Value("[110]"),
// setWeights: Value("[1]"),
// isAlternating: Value(true),
// 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, sessionId: sessionId, position: 0));
// });
// 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.',
// totalSets: 5,
// totalReps: "[1]",
// restBeforeSets: Value(30000),
// restBetweenSets: Value(300000),
// restBetweenReps: Value(15000),
// restAfterSets: Value(300000),
// repType: RepType.time,
// repLength: Value(10000),
// repWeights: Value("[110]"),
// setWeights: Value("[1]"),
// isAlternating: Value(true),
// 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 n = 0; n <= random.nextInt(totalMedia); n++) {

View File

@ -42,3 +42,21 @@ String jsonToDescription(List text) {
return content;
}
Widget formItemWrapper(Widget content,
[EdgeInsets padding = const EdgeInsets.fromLTRB(0, 0, 0, 0)]) {
return Expanded(child: Padding(padding: padding, child: content));
}
List<DropdownMenuEntry> numericDropDownItems(String type, int itemLimit) {
final List<DropdownMenuEntry> items = [];
// String entryName = type;
for (int i = 0; i < itemLimit; i++) {
// if (i != 0) entryName = "${type}s";
items.add(DropdownMenuEntry(value: i + 1, label: "${i + 1}"));
}
return items;
}

View File

@ -79,6 +79,10 @@ class _AppState extends State<App> {
alignment: Alignment.center,
child: const Text('In Progress...'),
),
Container(
alignment: Alignment.center,
child: const Text('Profile in Progress...'),
),
][currentPageIndex]),
bottomNavigationBar: NavigationBar(
onDestinationSelected: (int index) {
@ -98,7 +102,9 @@ class _AppState extends State<App> {
NavigationDestination(
icon: Icon(Icons.group), label: "Team Send"),
NavigationDestination(
icon: Icon(Icons.analytics), label: "Progress")
icon: Icon(Icons.analytics), label: "Progress"),
NavigationDestination(
icon: Icon(Icons.account_circle_rounded), label: "Profile"),
]),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
@ -116,8 +122,7 @@ void main() {
runApp(MultiProvider(
providers: [
Provider<AppDatabase>(
create: (context) => db,
dispose: (context, db) => db.close()),
create: (context) => db, dispose: (context, db) => db.close()),
ChangeNotifierProvider(create: (context) => ActivityTimerModel()),
ChangeNotifierProvider(create: (context) => ActionTimer()),
],

View File

@ -96,7 +96,7 @@ class ActionModel {
position: totalItems,
action: action,
time: action.restAfterSets!,
name: 'rest'));
name: 'cooldown'));
}
return items;
@ -164,8 +164,20 @@ class Set extends Item {
if (action.repType == RepType.time) {
for (int i = 0; i < totalReps; i++) {
position = position > 0 ? position + 1 : position;
// don't show a rest before first rep
if (i > 0) {
items.add(Rest(
id: position,
position: position,
parentId: id,
action: action,
time: action.restBetweenReps,
name: 'rest'));
}
items.add(Reps(
id: position, position: position, parentId: id, action: action));
id: ++position, position: position, parentId: id, action: action));
if (action.isAlternating) {
items.add(Rest(
@ -180,17 +192,6 @@ class Set extends Item {
position: position,
parentId: id,
action: action));
// don't show a rest after the last rep
if (i < totalReps - 1) {
items.add(Rest(
id: ++position,
position: position,
parentId: id,
action: action,
time: action.restBetweenReps,
name: 'prepare'));
}
}
}
} else {

View File

@ -2,6 +2,9 @@ import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_sound/public/flutter_sound_player.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/models/action_model.dart';
@ -11,6 +14,7 @@ class ActionTimer with ChangeNotifier {
double _progress = 0;
int _currentTime = 0;
final List<ItemScrollController> _scrollControllers = [];
final FlutterSoundPlayer _mPlayer = FlutterSoundPlayer();
ActionTimer();
@ -51,27 +55,41 @@ class ActionTimer with ChangeNotifier {
}
void setup(ActionModel actionModel, ItemScrollController scrollController,
[bool resetOnLoad = true]) {
[bool resetOnLoad = true]) async {
_scrollControllers.add(scrollController);
if (resetOnLoad) {
if (this.actionModel == actionModel) {
reset();
_scrollControllers.add(scrollController);
}
this.actionModel = actionModel;
setAction(currentAction.id);
}
_scrollControllers.add(scrollController);
}
Future pause() async =>
await actionModel?.updateStatus(ActionStatus.paused).whenComplete(() {
_periodicTimer?.cancel();
notifyListeners();
// _mPlayer.stopPlayer();
// Be careful : you must `close` the audio session when you have finished with it.
});
Future start() async {
await actionModel!.updateStatus(ActionStatus.started);
await _mPlayer.openPlayer();
Uint8List? countTone;
Uint8List? finishTone;
await rootBundle
.load('assets/audio/count_tone.wav')
.then((data) => countTone = data.buffer.asUint8List());
await rootBundle
.load('assets/audio/count_finish.mp3')
.then((data) => finishTone = data.buffer.asUint8List());
// start timer
if (_periodicTimer == null || _periodicTimer!.isActive == false) {
@ -83,8 +101,15 @@ class ActionTimer with ChangeNotifier {
case RepType.time:
_currentTime--;
if (_currentTime <= 3 && _currentTime != 0) {
await _mPlayer.startPlayer(
fromDataBuffer: countTone, codec: Codec.pcm16WAV);
}
if (_currentTime == 0) {
// move to next action
await _mPlayer.startPlayer(
fromDataBuffer: finishTone, codec: Codec.mp3);
await setAction(state['currentAction'] + 1);
}
@ -97,18 +122,21 @@ class ActionTimer with ChangeNotifier {
notifyListeners();
}
Future close() async =>
await actionModel!.updateStatus(ActionStatus.complete).whenComplete(() {
Future close() async => await actionModel!
.updateStatus(ActionStatus.complete)
.whenComplete(() async {
_periodicTimer!.cancel();
_mPlayer.closePlayer();
notifyListeners();
});
Future reset() async {
await actionModel!.updateStatus(ActionStatus.pending);
await actionModel!.updateState(json.encode(_stateConstructor()));
await actionModel?.updateStatus(ActionStatus.pending);
await actionModel?.updateState(json.encode(_stateConstructor()));
_periodicTimer?.cancel();
_progress = 0;
_scrollControllers.clear();
_mPlayer.closePlayer();
notifyListeners();
}
@ -134,39 +162,49 @@ class ActionTimer with ChangeNotifier {
}
setAction(int actionNum, [bool isManual = false]) async {
Item item = allActions[actionNum];
Map newState = state;
if (actionNum < allActions.length) {
Item item = allActions[actionNum];
Map newState = state;
newState['currentAction'] = actionNum;
newState['currentSet'] = item.parentId;
newState['currentRep'] = item.id;
newState['currentTime'] = _currentTime = item.value!;
newState['currentAction'] = actionNum;
newState['currentSet'] = item.parentId;
newState['currentRep'] = item.id;
newState['currentTime'] = _currentTime = item.value!;
await actionModel!
.updateState(json.encode(newState))
.whenComplete(() async {
if (isManual) {
await pause();
await updateProgress();
}
await actionModel!
.updateState(json.encode(newState))
.whenComplete(() async {
// if manual select, pause next action
if (isManual) {
await pause();
await updateProgress();
}
int index = currentAction.parentId != null
? currentAction.parentId!
: currentAction.id;
int index = currentAction.parentId != null
? currentAction.parentId!
: currentAction.id;
for (int i = 0; i < _scrollControllers.length; i++) {
ItemScrollController sc = _scrollControllers[i];
if (_scrollControllers.isNotEmpty) {
for (int i = 0; i < _scrollControllers.length; i++) {
ItemScrollController sc = _scrollControllers[i];
sc.scrollTo(
index: index,
duration: Duration(milliseconds: 500),
curve: Curves.easeInOutCubic);
}
// _scrollController?.scrollTo(
// index: index,
// duration: Duration(milliseconds: 500),
// curve: Curves.easeInOutCubic);
});
sc.scrollTo(
index: index,
duration: Duration(milliseconds: 500),
curve: Curves.easeInOutCubic);
}
}
// _scrollController?.scrollTo(
// index: index,
// duration: Duration(milliseconds: 500),
// curve: Curves.easeInOutCubic);
});
} else {
await actionModel?.updateStatus(ActionStatus.complete).whenComplete(() {
_periodicTimer?.cancel();
notifyListeners();
});
}
notifyListeners();
}

View File

@ -1,39 +1,305 @@
import 'dart:convert';
import 'package:drift/drift.dart' hide Column;
import 'package:flutter/material.dart' hide Action;
import 'package:provider/provider.dart';
import 'package:sendtrain/daos/actions_dao.dart';
import 'package:sendtrain/daos/activity_actions_dao.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/helpers/widget_helpers.dart';
import 'package:sendtrain/widgets/generic/elements/form_drop_down.dart';
import 'package:sendtrain/widgets/generic/elements/form_text_input.dart';
class ActivityActionEditor extends StatelessWidget {
ActivityActionEditor({super.key, required this.action, this.callback});
class ActivityActionEditor extends StatefulWidget {
const ActivityActionEditor(
{super.key,
required this.session,
required this.activity,
this.action,
this.callback});
final Action action;
final Session session;
final Activity activity;
final Action? action;
final Function? callback;
@override
State<ActivityActionEditor> createState() => _ActivityActionEditorState();
}
class _ActivityActionEditorState extends State<ActivityActionEditor> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final Map<String, TextEditingController> actionEditController = {
'sets': TextEditingController(),
'reps': TextEditingController(),
'weight': TextEditingController(),
'repLength': TextEditingController(),
'preparation': TextEditingController(),
'setRest': TextEditingController(),
'repRest': TextEditingController(),
'cooldown': TextEditingController(),
'type': TextEditingController(),
'alternating': TextEditingController(),
};
late final AppDatabase db;
bool isAlternating = false;
bool isTimed = false;
String editorType = 'Create';
@override
void initState() {
super.initState();
db = Provider.of<AppDatabase>(context, listen: false);
// if we're editing a session, we'll want to populate it with the appropriate values
if (widget.action != null) {
final Action action = widget.action!;
editorType = 'Edit';
isAlternating = action.isAlternating;
isTimed = action.repType == RepType.time ? true : false;
actionEditController['sets']?.text = action.totalSets.toString();
actionEditController['reps']?.text =
json.decode(action.totalReps)[0].toString();
actionEditController['weight']?.text =
json.decode(action.repWeights ?? "")[0].toString();
actionEditController['repLength']?.text =
((action.repLength ?? 0) ~/ 1000).toString();
actionEditController['preparation']?.text =
((action.restBeforeSets ?? 0) ~/ 1000).toString();
actionEditController['setRest']?.text =
((action.restBetweenSets ?? 0) ~/ 1000).toString();
actionEditController['repRest']?.text =
((action.restBetweenReps ?? 0) ~/ 1000).toString();
actionEditController['cooldown']?.text =
((action.restAfterSets ?? 0) ~/ 1000).toString();
actionEditController['isTimed']?.text = isTimed.toString();
actionEditController['alternating']?.text = isAlternating.toString();
}
}
@override
Widget build(BuildContext context) {
String editorType = 'Create';
if (widget.action != null) {
editorType = 'Edit';
}
return Padding(
padding: EdgeInsets.fromLTRB(15, 0, 15, 15),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10, bottom: 10),
child: Text('$editorType Action',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge)),
FormTextInput(
controller: actionEditController['sets']!,
title: 'Total Sets'),])));
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10, bottom: 10),
child: Text('$editorType Action',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge)),
Row(children: [
formItemWrapper(
CheckboxListTile(
title: Text("Reps alternate? (eg. Left/Right Hand)"),
value: isAlternating,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(10.0)),
),
onChanged: (bool? value) {
setState(() {
isAlternating = value!;
});
},
),
EdgeInsets.fromLTRB(10, 10, 10, 10)),
]),
Row(children: [
formItemWrapper(
CheckboxListTile(
title: Text("Are reps timed?"),
value: isTimed,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(10.0)),
),
onChanged: (bool? value) {
setState(() {
isTimed = value!;
});
},
),
EdgeInsets.fromLTRB(10, 10, 10, 15))
]),
Row(children: [
FormDropDown(
title: 'Sets',
entries: numericDropDownItems('Set', 50),
controller: actionEditController['sets']!),
FormDropDown(
title: 'Reps',
entries: numericDropDownItems('Rep', 100),
controller: actionEditController['reps']!,
)
]),
Row(children: [
formItemWrapper(
FormTextInput(
type: InputTypes.number,
controller: actionEditController['preparation']!,
title: 'Preparation (sec)',
hint: 'time before start',
requiresValidation: false),
EdgeInsets.fromLTRB(10, 5, 10, 0)),
formItemWrapper(
FormTextInput(
type: InputTypes.number,
controller: actionEditController['cooldown']!,
title: 'Cooldown (sec)',
hint: 'rest after completion',
requiresValidation: false),
EdgeInsets.fromLTRB(10, 5, 10, 0)),
]),
Row(children: [
formItemWrapper(
FormTextInput(
type: InputTypes.number,
controller: actionEditController['setRest']!,
title: 'Set Rest (sec)',
hint: 'Rest between sets',
requiresValidation: false),
EdgeInsets.only(left: 10, right: 10)),
formItemWrapper(
FormTextInput(
type: InputTypes.number,
controller: actionEditController['repRest']!,
title: 'Rep Rest (sec)',
hint: 'Rest between reps',
requiresValidation: false),
EdgeInsets.only(left: 10, right: 10)),
]),
Row(children: [
formItemWrapper(
FormTextInput(
type: InputTypes.number,
controller: actionEditController['repLength']!,
title: 'Rep Length (sec)',
hint: 'Total rep time (not required)',
requiresValidation: false),
EdgeInsets.only(left: 10, right: 10)),
formItemWrapper(
FormTextInput(
type: InputTypes.number,
controller: actionEditController['weight']!,
title: 'Weight',
hint: 'Weight for reps',
requiresValidation: false),
EdgeInsets.only(left: 10, right: 10)),
]),
Row(mainAxisAlignment: MainAxisAlignment.end, children: [
Padding(
padding: EdgeInsets.only(top: 10, right: 10),
child: FilledButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
if (widget.action != null) {
Action newAction = widget.action!.copyWith(
totalSets: int.parse(
actionEditController['sets']!.text),
totalReps: json.encode([
int.parse(
actionEditController['reps']!.text)
]),
repLength: Value<int>(int.parse(
actionEditController['repLength']!
.text) *
1000),
restBeforeSets: Value<int>(int.parse(
actionEditController['preparation']!
.text) *
1000),
restBetweenSets: Value<int>(int.parse(
actionEditController['setRest']!
.text) *
1000),
restBetweenReps: Value<int>(int.parse(
actionEditController['repRest']!
.text) *
1000),
restAfterSets: Value<int>(int.parse(
actionEditController['cooldown']!
.text) *
1000),
repType: int.parse(actionEditController[
'repLength']!
.text) >
0
? RepType.time
: RepType.count,
repWeights: Value<String>(json.encode([
int.parse(
actionEditController['weight']!.text)
])),
// setWeights: Value<String>(json.encode([actionEditController['setWeights']!.text])),
isAlternating: isAlternating,
);
// var result = await ActionsDao(db).createOrUpdate(
// newAction.toCompanion(true));
await ActionsDao(db).replace(newAction);
} else {
// create action
await ActionsDao(db)
.createOrUpdate(ActionsCompanion(
title: Value('title'),
description: Value('description'),
totalSets: Value(int.parse(
actionEditController['sets']!
.text)),
totalReps: Value(json.encode(
[int.parse(actionEditController['reps']!.text)])),
repLength: Value<int>(
int.parse(actionEditController['repLength']!.text) *
1000),
restBeforeSets: Value<int>(
int.parse(actionEditController['preparation']!.text) *
1000),
restBetweenSets: Value<int>(
int.parse(actionEditController['setRest']!.text) *
1000),
restBetweenReps:
Value<int>(int.parse(actionEditController['repRest']!.text) * 1000),
restAfterSets: Value<int>(int.parse(actionEditController['cooldown']!.text) * 1000),
repType: Value(int.parse(actionEditController['repLength']!.text) > 0 ? RepType.time : RepType.count),
repWeights: Value<String>(json.encode([int.parse(actionEditController['weight']!.text)])),
// setWeights: Value<String>(json.encode([actionEditController['setWeights']!.text])),
isAlternating: Value<bool>(isAlternating),
// repType: RepType.values.firstWhere((e) => e.toString() == "RepType.${actionEditController['repType']!.text}"),
set: Value("")))
.then((actionId) {
ActivityActionsDao(db).createOrUpdate(
ActivityActionsCompanion(
activityId:
Value(widget.activity.id),
sessionId: Value(widget.session.id),
actionId: Value(actionId),
position: Value(0)));
});
}
Navigator.pop(
_formKey.currentContext!, 'Submit');
if (widget.callback != null) {
await widget.callback!();
}
}
},
child: Text('Submit')))
])
])));
}
}
}

View File

@ -3,20 +3,32 @@ import 'package:provider/provider.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/extensions/string_extensions.dart';
import 'package:sendtrain/helpers/widget_helpers.dart';
import 'package:sendtrain/models/action_model.dart';
import 'package:sendtrain/providers/action_timer.dart';
import 'package:sendtrain/widgets/activities/activity_action_editor.dart';
import 'package:sendtrain/widgets/generic/elements/add_card_generic.dart';
class ActivityActionView extends StatefulWidget {
const ActivityActionView({super.key, required this.actions, this.resetOnLoad = true});
// class ActivityActionView extends StatefulWidget {
class ActivityActionView extends StatelessWidget {
ActivityActionView(
{super.key,
required this.session,
required this.activity,
required this.actions,
this.callback,
this.resetOnLoad = true});
final Session session;
final Activity activity;
final List actions;
final Function? callback;
final bool resetOnLoad;
@override
State<ActivityActionView> createState() => ActivityActionViewState();
}
// @override
// State<ActivityActionView> createState() => ActivityActionViewState();
// }
class ActivityActionViewState extends State<ActivityActionView> {
// class ActivityActionViewState extends State<ActivityActionView> {
// class ActivityActionView extends StatelessWidget {
// ActivityActionView({super.key, required this.actions});
@ -30,7 +42,7 @@ class ActivityActionViewState extends State<ActivityActionView> {
ScrollOffsetListener.create();
late final ActionTimer at;
int actionCount = 0;
// int actionCount = 0;
GestureDetector gtBuild(
ActionTimer at, Item item, int actionNum, int selectedIndex,
@ -68,17 +80,23 @@ class ActivityActionViewState extends State<ActivityActionView> {
}));
}
@override
void initState() {
super.initState();
at = Provider.of<ActionTimer>(context, listen: false);
}
// @override
// void initState() {
// super.initState();
// at = Provider.of<ActionTimer>(context, listen: false);
// }
@override
Widget build(BuildContext context) {
if (widget.actions.isNotEmpty) {
at.setup(ActionModel(
action: widget.actions.first, db: Provider.of<AppDatabase>(context)), itemScrollController, widget.resetOnLoad);
at = Provider.of<ActionTimer>(context, listen: false);
int actionCount = 0;
if (actions.isNotEmpty) {
at.setup(
ActionModel(
action: actions.first,
db: Provider.of<AppDatabase>(context)),
itemScrollController,
resetOnLoad);
// WidgetsBinding.instance.addPostFrameCallback((_) {
// if (itemScrollController.isAttached) {
@ -117,7 +135,7 @@ class ActivityActionViewState extends State<ActivityActionView> {
onPressed: () => {
if (at.started)
{at.pause()}
else if (at.available)
else if (at.available || at.complete)
{at.start()}
});
},
@ -220,7 +238,12 @@ class ActivityActionViewState extends State<ActivityActionView> {
description:
'Click here to create an exercise template (sets and reps, etc) for your activity!',
action: () {
print('teset');
showEditorSheet(
context,
ActivityActionEditor(
session: session,
activity: activity,
callback: callback));
});
}
}

View File

@ -49,7 +49,7 @@ class ActivityCardState extends State<ActivityCard> {
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () => showGenericDialog(
ActivityView(activity: widget.activity), context),
ActivityView(session: widget.session, activity: widget.activity), context),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[

View File

@ -7,6 +7,7 @@ import 'package:sendtrain/daos/actions_dao.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/extensions/string_extensions.dart';
import 'package:sendtrain/helpers/widget_helpers.dart';
import 'package:sendtrain/providers/action_timer.dart';
import 'package:sendtrain/widgets/activities/activity_action_editor.dart';
import 'package:sendtrain/widgets/activities/activity_action_view.dart';
import 'package:sendtrain/widgets/activities/activity_view_categories.dart';
@ -14,7 +15,9 @@ import 'package:sendtrain/widgets/activities/activity_view_media.dart';
import 'package:sendtrain/widgets/builders/dialogs.dart';
class ActivityView extends StatefulWidget {
const ActivityView({super.key, required this.activity});
const ActivityView(
{super.key, required this.session, required this.activity});
final Session session;
final Activity activity;
@override
@ -22,6 +25,17 @@ class ActivityView extends StatefulWidget {
}
class _ActivityViewState extends State<ActivityView> {
final _fabKey = GlobalKey<ExpandableFabState>();
void resetState() async {
final state = _fabKey.currentState;
if (state != null && state.isOpen) {
state.toggle();
}
setState(() {});
}
List<ActivityMuscle> activityMuscle(Activity activity) {
List<ActivityMuscle> muscles = [];
@ -39,10 +53,11 @@ class _ActivityViewState extends State<ActivityView> {
@override
Widget build(BuildContext context) {
final Activity activity = widget.activity;
final Session session = widget.session;
return FutureBuilder<List>(
future: ActionsDao(Provider.of<AppDatabase>(context))
.fromActivity(activity),
.fromActivity(activity, session),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Action> actions = snapshot.data! as List<Action>;
@ -61,12 +76,19 @@ class _ActivityViewState extends State<ActivityView> {
child: Scaffold(
floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab(
key: _fabKey,
distance: 70,
type: ExpandableFabType.up,
overlayStyle: ExpandableFabOverlayStyle(
color: Colors.black.withOpacity(0.5),
blur: 10,
),
onOpen: () {
// pause the activity on open
ActionTimer at =
Provider.of<ActionTimer>(context, listen: false);
if (at.started) at.pause();
},
children: [
// FloatingActionButton.extended(
// icon: const Icon(Icons.upload_outlined),
@ -80,7 +102,10 @@ class _ActivityViewState extends State<ActivityView> {
showEditorSheet(
context,
ActivityActionEditor(
action: actions.first, callback: () {}));
session: session,
activity: activity,
action: actions.first,
callback: resetState));
},
),
FloatingActionButton.extended(
@ -228,7 +253,10 @@ class _ActivityViewState extends State<ActivityView> {
context,
Column(children: [
ActivityActionView(
session: session,
activity: activity,
actions: actions,
callback: resetState,
resetOnLoad: false)
]),
Theme.of(context).colorScheme.surface);
@ -237,7 +265,11 @@ class _ActivityViewState extends State<ActivityView> {
alignment: Alignment.bottomCenter,
)
])),
ActivityActionView(actions: actions)
ActivityActionView(
session: session,
activity: activity,
actions: actions,
callback: resetState)
])));
// ] +
// action(actions, context)));

View File

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:sendtrain/helpers/widget_helpers.dart';
class FormDropDown extends StatelessWidget {
const FormDropDown(
{super.key,
required this.title,
required this.entries,
required this.controller});
final List<DropdownMenuEntry> entries;
final String title;
final TextEditingController controller;
@override
Widget build(BuildContext context) {
return formItemWrapper(
DropdownMenu(
leadingIcon: Icon(Icons.select_all_rounded),
initialSelection: controller.text,
controller: controller,
expandedInsets: EdgeInsets.zero,
inputDecorationTheme: InputDecorationTheme(
filled: true,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(12))),
label: Text(title),
dropdownMenuEntries: entries),
EdgeInsets.fromLTRB(10, 5, 10, 5));
}
}

View File

@ -1,4 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
enum InputTypes { text, number }
class FormTextInput extends StatelessWidget {
const FormTextInput(
@ -9,7 +12,10 @@ class FormTextInput extends StatelessWidget {
this.maxLines,
this.minLines,
this.onTap,
this.requiresValidation=true});
this.requiresValidation = true,
this.type = InputTypes.text,
this.hint,
this.validations});
final TextEditingController controller;
final String title;
@ -18,12 +24,25 @@ class FormTextInput extends StatelessWidget {
final Icon? icon;
final dynamic onTap;
final bool requiresValidation;
final InputTypes type;
final String? hint;
final Function? validations;
@override
Widget build(BuildContext context) {
final Map params = {};
if (type == InputTypes.number) {
params['keyboardType'] = TextInputType.number;
params['inputFormatters'] = <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
];
}
return Padding(
padding: EdgeInsets.only(top: 10, bottom: 10),
child: TextFormField(
keyboardType: params['keyboardType'] ?? TextInputType.text,
inputFormatters: params['inputFormatters'] ?? [],
minLines: minLines ?? 1,
maxLines: maxLines ?? 1,
controller: controller,
@ -34,6 +53,7 @@ class FormTextInput extends StatelessWidget {
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(12)),
labelText: title,
hintText: hint ?? '',
),
validator: (String? value) {
if (requiresValidation == true) {
@ -41,9 +61,11 @@ class FormTextInput extends StatelessWidget {
return 'Please enter some text';
}
if (value.length < 3) {
return 'Please enter a minimum of 3 characters';
}
if (validations != null) validations!(value);
// if (value.length < 3) {
// return 'Please enter a minimum of 3 characters';
// }
}
return null;
},

View File

@ -1,6 +1,4 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@ -8,7 +6,6 @@ import 'package:sendtrain/daos/media_items_dao.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/helpers/widget_helpers.dart';
import 'package:sendtrain/widgets/builders/dialogs.dart';
import 'package:video_player/video_player.dart';
class MediaCard extends StatelessWidget {
const MediaCard(

View File

@ -52,6 +52,7 @@ dependencies:
video_player: ^2.9.2
dart_casing: ^3.0.1
collection: ^1.18.0
flutter_sound: ^9.23.1
flutter_launcher_name:
name: "SendTrain"
@ -87,6 +88,7 @@ flutter:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
assets:
- assets/audio/
- assets/images/
- assets/exercises.json

View File

@ -36,6 +36,8 @@ import 'schema_v30.dart' as v30;
import 'schema_v31.dart' as v31;
import 'schema_v32.dart' as v32;
import 'schema_v33.dart' as v33;
import 'schema_v34.dart' as v34;
import 'schema_v35.dart' as v35;
class GeneratedHelper implements SchemaInstantiationHelper {
@override
@ -107,6 +109,10 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v32.DatabaseAtV32(db);
case 33:
return v33.DatabaseAtV33(db);
case 34:
return v34.DatabaseAtV34(db);
case 35:
return v35.DatabaseAtV35(db);
default:
throw MissingSchemaException(version, versions);
}
@ -145,6 +151,8 @@ class GeneratedHelper implements SchemaInstantiationHelper {
30,
31,
32,
33
33,
34,
35
];
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff