16 Commits

Author SHA1 Message Date
604b099010 converted to activity from activity model finally 2024-12-24 16:58:50 -05:00
c6030f8ac5 seed update, action prep, titleization 2024-12-24 14:41:39 -05:00
ffd696053a Activity category and type widgets 2024-12-23 23:46:34 -05:00
fb0b73ecaf dry-ing up activity view things 2024-12-21 17:51:56 -05:00
1234a300e1 small refactor, better dao thigns 2024-12-21 17:51:24 -05:00
3153bf13f9 db seed fix, cleanup 2024-12-20 21:07:05 -05:00
68443b3427 moved to migration strategy, moved daos to top lib 2024-12-20 14:19:17 -05:00
5d27744ead convert to dao calls, prep for migration strategy and first start db seed 2024-12-20 13:56:26 -05:00
67d7a374d4 down with daos, mild refactoring, moving to pulling from real data 2024-12-15 22:21:23 -05:00
54d47245ae Merge pull request 'drift-impl' (#3) from drift-impl into main
Reviewed-on: #3
2024-12-10 00:19:53 -08:00
7f2cf0b49f more seed data and passing data along 2024-12-10 02:26:34 -05:00
8fe60e7ae3 officially getting data from the dbgit add . -p! 2024-12-10 02:10:02 -05:00
b2e2eb67b0 seeded db, updated some db fields 2024-12-09 00:06:44 -05:00
0dc7c3ced0 upgraded kotlin, gradle, added drift 2024-12-08 22:25:23 -05:00
13fe7e2ef4 more prep work for local data storage 2024-12-08 14:40:12 -05:00
d6e62024d7 added dash for local db, ui prep work 2024-12-08 14:14:10 -05:00
71 changed files with 14879 additions and 920 deletions

143
.gitignore vendored
View File

@ -9,6 +9,7 @@
.history .history
.svn/ .svn/
migrate_working_dir/ migrate_working_dir/
bkp
# IntelliJ related # IntelliJ related
*.iml *.iml
@ -44,3 +45,145 @@ app.*.map.json
/android/app/debug /android/app/debug
/android/app/profile /android/app/profile
/android/app/release /android/app/release
# Do not remove or rename entries in this file, only add new ones
# See https://github.com/flutter/flutter/issues/128635 for more context.
# Miscellaneous
*.class
*.lock
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# Visual Studio Code related
.classpath
.project
.settings/
.vscode/*
# Flutter repo-specific
/bin/cache/
/bin/internal/bootstrap.bat
/bin/internal/bootstrap.sh
/bin/mingit/
/dev/benchmarks/mega_gallery/
/dev/bots/.recipe_deps
/dev/bots/android_tools/
/dev/devicelab/ABresults*.json
/dev/docs/doc/
/dev/docs/api_docs.zip
/dev/docs/flutter.docs.zip
/dev/docs/lib/
/dev/docs/pubspec.yaml
/dev/integration_tests/**/xcuserdata
/dev/integration_tests/**/Pods
/packages/flutter/coverage/
version
analysis_benchmark.json
# packages file containing multi-root paths
.packages.generated
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
**/generated_plugin_registrant.dart
.packages
.pub-preload-cache/
.pub-cache/
.pub/
build/
flutter_*.png
linked_*.ds
unlinked.ds
unlinked_spec.ds
# Android related
**/android/**/gradle-wrapper.jar
.gradle/
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
**/android/key.properties
*.jks
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/.last_build_id
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/ephemeral
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# macOS
**/Flutter/ephemeral/
**/Pods/
**/macos/Flutter/GeneratedPluginRegistrant.swift
**/macos/Flutter/ephemeral
**/xcuserdata/
# Windows
**/windows/flutter/ephemeral/
**/windows/flutter/generated_plugin_registrant.cc
**/windows/flutter/generated_plugin_registrant.h
**/windows/flutter/generated_plugins.cmake
# Linux
**/linux/flutter/ephemeral/
**/linux/flutter/generated_plugin_registrant.cc
**/linux/flutter/generated_plugin_registrant.h
**/linux/flutter/generated_plugins.cmake
# Coverage
coverage/
# Symbols
app.*.symbols
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock
!.vscode/settings.json

View File

@ -1,11 +1,11 @@
# This file tracks properties of this Flutter project. # This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc. # Used by Flutter tool to assess capabilities and perform upgrades etc.
# #
# This file should be version controlled. # This file should be version controlled and should not be manually edited.
version: version:
revision: 9944297138845a94256f1cf37beb88ff9a8e811a revision: "dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668"
channel: stable channel: "stable"
project_type: app project_type: app
@ -13,26 +13,26 @@ project_type: app
migration: migration:
platforms: platforms:
- platform: root - platform: root
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: android - platform: android
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: ios - platform: ios
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: linux - platform: linux
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: macos - platform: macos
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: web - platform: web
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
- platform: windows - platform: windows
create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
# User provided section # User provided section

View File

@ -1,74 +0,0 @@
{
// Place your snippets for dart here. Each snippet is defined under a snippet name and has a prefix, body and
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
// same ids are connected.
// Example:
// "Print to console": {
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
"Stateless Widget": {
"prefix": "stateless",
"body": [
"import 'package:flutter/material.dart';",
"\n",
"class ${1:MyClass} extends StatelessWidget {",
"@override",
"Widget build(BuildContext context) {",
"return Container($0);}}"
]
},
"Stateful Widget": {
"prefix": "stateful",
"body": [
"import 'package:flutter/material.dart';",
"\n",
"class ${1:MyClass} extends StatefulWidget {",
"const ${1:MyClass}({Key key, $2}) : super(key: key);",
"\n",
"@override",
"_${1:MyClass}State createState() => _${1:MyClass}State();",
"}",
"\n",
"class _${1:MyClass}State extends State<${1:MyClass}> {",
"\n",
"@override",
"void initState() {",
"super.initState();",
"}",
"\n",
"@override",
"void dispose() {",
"super.dispose();",
"}",
"\n",
"@override",
"Widget build(BuildContext context) {",
"return Container($0);}}"
]
},
"Widget Test": {
"prefix": "widgettest",
"body": [
"import 'package:flutter_test/flutter_test.dart';",
"import 'package:flutter/material.dart';",
"//import 'package:piota/${1:widgetfile}.dart';",
"\n",
"import 'ui_test_util.dart';",
"\n",
"void main() {",
"group('${2:groupname}', () {",
"final testableWidget = testWidget(${3:Container()},Size(375, 667));",
"testWidgets('${3:Container()} test', (WidgetTester tester) async {",
"final finder = find.byKey(Key('${5:keyname}'));",
"await tester.pumpWidget(testableWidget);",
"expect(finder, findsOneWidget);",
"});});}"
]
}
}

View File

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

2
android/.gitignore vendored
View File

@ -7,7 +7,7 @@ gradle-wrapper.jar
GeneratedPluginRegistrant.java GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore. # Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app # See https://flutter.dev/to/reference-keystore
key.properties key.properties
**/*.keystore **/*.keystore
**/*.jks **/*.jks

View File

@ -1,71 +1,44 @@
def localProperties = new Properties() plugins {
def localPropertiesFile = rootProject.file('local.properties') id "com.android.application"
if (localPropertiesFile.exists()) { id "kotlin-android"
localPropertiesFile.withReader('UTF-8') { reader -> // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
localProperties.load(reader) id "dev.flutter.flutter-gradle-plugin"
}
} }
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion flutter.compileSdkVersion namespace = "com.example.sendtrain"
ndkVersion flutter.ndkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = JavaVersion.VERSION_1_8
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
} }
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.sendtrain.sendtrain" applicationId = "com.example.sendtrain"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdkVersion flutter.minSdkVersion minSdk = flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion targetSdk = flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode = flutter.versionCode
versionName flutterVersionName versionName = flutter.versionName
} }
buildTypes { buildTypes {
release { release {
// TODO: Add your own signing config for the release build. // TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug signingConfig = signingConfigs.debug
} }
} }
} }
flutter { flutter {
source '../..' source = "../.."
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
} }

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
package="com.sendtrain.sendtrain">
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.

View File

@ -1,13 +1,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
package="com.sendtrain.sendtrain">
<application <application
android:label="SendTrain" android:label="sendtrain"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
@ -31,4 +31,15 @@
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
</application> </application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest> </manifest>

View File

@ -0,0 +1,5 @@
package com.example.sendtrain
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity()

View File

@ -1,6 +0,0 @@
package com.sendtrain.sendtrain
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
package="com.sendtrain.sendtrain">
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.

View File

@ -1,16 +1,3 @@
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects { allprojects {
repositories { repositories {
google() google()
@ -18,12 +5,12 @@ allprojects {
} }
} }
rootProject.buildDir = '../build' rootProject.buildDir = "../build"
subprojects { subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}" project.buildDir = "${rootProject.buildDir}/${project.name}"
} }
subprojects { subprojects {
project.evaluationDependsOn(':app') project.evaluationDependsOn(":app")
} }
tasks.register("clean", Delete) { tasks.register("clean", Delete) {

View File

@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true

View File

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip

View File

@ -1,11 +1,25 @@
include ':app' pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
def localPropertiesFile = new File(rootProject.projectDir, "local.properties") includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
def properties = new Properties()
assert localPropertiesFile.exists() repositories {
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } google()
mavenCentral()
gradlePluginPortal()
}
}
def flutterSdkPath = properties.getProperty("flutter.sdk") plugins {
assert flutterSdkPath != null, "flutter.sdk not set in local.properties" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" id "com.android.application" version "8.1.0" apply false
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
}
include ":app"

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

View File

@ -1,33 +0,0 @@
PODS:
- Flutter (1.0.0)
- flutter_inappwebview (0.0.1):
- Flutter
- flutter_inappwebview/Core (= 0.0.1)
- OrderedSet (~> 5.0)
- flutter_inappwebview/Core (0.0.1):
- Flutter
- OrderedSet (~> 5.0)
- OrderedSet (5.0.0)
DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
SPEC REPOS:
trunk:
- OrderedSet
EXTERNAL SOURCES:
Flutter:
:path: Flutter
flutter_inappwebview:
:path: ".symlinks/plugins/flutter_inappwebview/ios"
SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
COCOAPODS: 1.12.0

View File

@ -0,0 +1,12 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

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;
}

151
lib/database/database.dart Normal file
View File

@ -0,0 +1,151 @@
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());
@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()))();
}
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()))();
}
enum ActivityCategories { fundamentals, conditioning, advanced, custom, pro }
enum ActivityType {
strength,
power,
conditioning,
hypertrophy,
endurance,
stability,
mobility,
flexibility,
rehabilitation,
technical
}
class Activities extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 3, max: 32)();
TextColumn get type => textEnum<ActivityType>()();
TextColumn get description => text().named('body')();
TextColumn get category => textEnum<ActivityCategories>()();
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)();
IntColumn get position => integer()();
DateTimeColumn get createdAt =>
dateTime().withDefault(Variable(DateTime.now()))();
}
class Actions extends Table {
IntColumn get id => integer().autoIncrement()();
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()))();
}
enum ObjectType {
actions,
activities,
sessions,
}
class ObjectMediaItems extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get objectId => integer()();
TextColumn get objectType => textEnum<ObjectType>()();
IntColumn get mediaId => integer().references(MediaItems, #id)();
DateTimeColumn get createdAt =>
dateTime().withDefault(Variable(DateTime.now()))();
}
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: 256)();
TextColumn get type => textEnum<MediaType>()();
DateTimeColumn get createdAt =>
dateTime().withDefault(Variable(DateTime.now()))();
}

4519
lib/database/database.g.dart Normal file

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,8 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/models/activity_timer_model.dart'; import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/screens/activities_screen.dart'; import 'package:sendtrain/screens/activities_screen.dart';
import 'package:sendtrain/screens/sessions_screen.dart'; import 'package:sendtrain/screens/sessions_screen.dart';
// ignore: unused_import
import 'package:sendtrain/database/seed.dart';
class SendTrain extends StatelessWidget { class SendTrain extends StatelessWidget {
const SendTrain({super.key}); const SendTrain({super.key});
@ -44,7 +47,7 @@ class _AppState extends State<App> {
body: Padding( body: Padding(
padding: const EdgeInsets.fromLTRB(0, 50, 0, 0), padding: const EdgeInsets.fromLTRB(0, 50, 0, 0),
child: <Widget>[ child: <Widget>[
const SessionsScreen(), SessionsScreen(),
const ActivitiesScreen(), const ActivitiesScreen(),
Container( Container(
alignment: Alignment.center, alignment: Alignment.center,
@ -90,10 +93,13 @@ class _AppState extends State<App> {
} }
void main() { void main() {
runApp( runApp(MultiProvider(
ChangeNotifierProvider( providers: [
create: (context) => ActivityTimerModel(), ChangeNotifierProvider(create: (context) => ActivityTimerModel()),
Provider<AppDatabase>(
create: (context) => AppDatabase(),
dispose: (context, db) => db.close()),
],
child: const SendTrain(), child: const SendTrain(),
), ));
);
} }

View File

@ -1,45 +1,66 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.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 { class ActivityTimerModel with ChangeNotifier {
int _actionCounter = 0; int _actionCounter = 0;
ActivityModel? _activity; Activity? _activity;
List _sets = []; List _sets = [];
List _actions = [];
int _currentActionNum = 0; int _currentActionNum = 0;
int _currentSetNum = 0; int _currentSetNum = 0;
Timer? _periodicTimer; Timer? _periodicTimer;
double _progress = 0; double _progress = 0;
ItemScrollController? _isc; ItemScrollController? _isc;
int _totalTime = 0;
int get actionCount => _actionCounter; int get actionCount => _actionCounter;
int get currentActionNum => _currentActionNum; int get currentActionNum => _currentActionNum;
dynamic get currentAction => currentSet[_currentActionNum]; dynamic get currentAction => currentSet[_currentActionNum];
int get currentSetNum => _currentSetNum; int get currentSetNum => _currentSetNum;
dynamic get currentSet => _sets[_currentSetNum]; dynamic get currentSet => _sets[_currentSetNum];
ActivityModel? get activity => _activity; Activity? get activity => _activity;
List get sets => _sets; List get sets => _sets;
Timer? get periodicTimer => _periodicTimer; Timer? get periodicTimer => _periodicTimer;
bool get isActive => _isActive(); bool get isActive => _isActive();
double get progress => _progress; 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) { if (_activity == null || activity.id != _activity?.id) {
_periodicTimer?.cancel(); _periodicTimer?.cancel();
_progress = 0; _progress = 0;
_isc = null; _isc = null;
_activity = activity; _activity = activity;
_sets = activity.actions[0].items(); // only one action for now
_sets = json.decode(actions[0].set);
_actions = actions;
_currentActionNum = 0; _currentActionNum = 0;
_currentSetNum = 0; _currentSetNum = 0;
setActionCount(); setActionCount();
getTotalTime();
} }
moveToIndex(_currentSetNum); moveToIndex(_currentSetNum);
} }
void getTotalTime() {
int time = 0;
for(int setIndex = 0; _sets.length > setIndex; setIndex++) {
for (int actionIndex = 0; _sets[setIndex].length > actionIndex; actionIndex++) {
var action = _sets[setIndex][actionIndex];
if (action['type'] == 'seconds') {
time = time + action['amount'] as int;
}
}
}
_totalTime = time;
}
void reset() { void reset() {
_progress = 0; _progress = 0;
_currentActionNum = 0; _currentActionNum = 0;
@ -89,6 +110,7 @@ class ActivityTimerModel with ChangeNotifier {
case 'seconds': case 'seconds':
if (_actionCounter > 0) { if (_actionCounter > 0) {
_actionCounter--; _actionCounter--;
_totalTime--;
} else { } else {
nextAction(_currentActionNum + 1); nextAction(_currentActionNum + 1);
setActionCount(); setActionCount();
@ -110,8 +132,8 @@ class ActivityTimerModel with ChangeNotifier {
void setAction(int setNum, int actionNum, String type) { void setAction(int setNum, int actionNum, String type) {
_currentActionNum = actionNum; _currentActionNum = actionNum;
_currentSetNum = setNum; _currentSetNum = setNum;
moveToIndex(_currentSetNum);
notifyListeners(); notifyListeners();
moveToIndex(_currentSetNum);
} }
void nextAction(int nextActionIndex) { void nextAction(int nextActionIndex) {

View File

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

View File

@ -1,4 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sendtrain/daos/sessions_dao.dart';
import 'package:sendtrain/database/database.dart';
import '../widgets/session_card.dart'; import '../widgets/session_card.dart';
class SessionsScreen extends StatelessWidget { class SessionsScreen extends StatelessWidget {
@ -6,10 +9,23 @@ class SessionsScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
List<Widget> previousSessions = return FutureBuilder<List<Session>>(
List.generate(10, (i) => const SessionCard(state: 1, type: 1)); future: SessionsDao(Provider.of<AppDatabase>(context)).all(),
Widget upcomingSession = const SessionCard(state: 2); builder: (context, snapshot) {
Widget currentSession = const SessionCard(); 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);
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);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -17,19 +33,22 @@ class SessionsScreen extends StatelessWidget {
const Padding( const Padding(
padding: EdgeInsets.fromLTRB(15, 5, 0, 0), padding: EdgeInsets.fromLTRB(15, 5, 0, 0),
child: Text( child: Text(
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold), style: TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
'Current:')), 'Current:')),
currentSession, currentSession,
const Padding( const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 0), padding: EdgeInsets.fromLTRB(15, 30, 0, 0),
child: Text( child: Text(
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold), style: TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
'Upcoming:')), 'Upcoming:')),
upcomingSession, upcomingSession,
const Padding( const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 0), padding: EdgeInsets.fromLTRB(15, 30, 0, 0),
child: Text( child: Text(
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold), style: TextStyle(
fontSize: 25, fontWeight: FontWeight.bold),
'Previous:')), 'Previous:')),
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
@ -41,12 +60,17 @@ class SessionsScreen extends StatelessWidget {
mainAxisSpacing: 5, mainAxisSpacing: 5,
crossAxisCount: 1, crossAxisCount: 1,
children: previousSessions)) children: previousSessions))
// Flexible(
// child: ListView(
// scrollDirection: Axis.vertical,
// children: previousSessions,
// )),
], ],
); );
} else {
return Container(
alignment: Alignment.center,
child: SizedBox(
height: 50.0,
width: 50.0,
child: CircularProgressIndicator(),
));
}
});
} }
} }

View File

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

View File

@ -1,20 +1,47 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sendtrain/classes/media.dart'; import 'package:provider/provider.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'; import 'package:sendtrain/widgets/activity_view.dart';
class ActivityCard extends StatelessWidget { class ActivityCard extends StatefulWidget {
final Activity activity;
const ActivityCard({super.key, required this.activity}); const ActivityCard({super.key, required this.activity});
final ActivityModel activity; @override
State<ActivityCard> createState() => ActivityCardState();
}
class ActivityCardState extends State<ActivityCard> {
String formattedTime(int timeInSecond) {
int sec = timeInSecond % 60;
int min = (timeInSecond / 60).floor();
String minute = min.toString().length <= 1 ? "0$min" : "$min";
String second = sec.toString().length <= 1 ? "0$sec" : "$sec";
return "$minute:$second";
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: false);
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( return Card(
color: const Color(0xff3A5FB6), color: atm.activity?.id == widget.activity.id
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.surfaceContainerLow,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
child: InkWell( child: InkWell(
splashColor: Colors.deepPurple,
onTap: () => showGeneralDialog( onTap: () => showGeneralDialog(
barrierColor: Colors.black.withOpacity(0.5), barrierColor: Colors.black.withOpacity(0.5),
transitionDuration: const Duration(milliseconds: 220), transitionDuration: const Duration(milliseconds: 220),
@ -29,7 +56,7 @@ class ActivityCard extends StatelessWidget {
return SlideTransition( return SlideTransition(
position: custom, position: custom,
child: Dialog.fullscreen( child: Dialog.fullscreen(
child: ActivityView(activity: activity))); child: ActivityView(activity: widget.activity)));
}, },
barrierDismissible: true, barrierDismissible: true,
barrierLabel: '', barrierLabel: '',
@ -41,32 +68,79 @@ class ActivityCard extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
ListTile( ListTile(
// visualDensity: VisualDensity(horizontal: VisualDensity.maximumDensity),
leading: Padding( leading: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Container( child: Container(
// padding: EdgeInsets.only(top: 5, bottom: 5),
width: 60, width: 60,
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
fit: BoxFit.cover, fit: BoxFit.fill,
image: findMediaByType( image:
activity.actions[0].media, 'image')), findMediaByType(mediaItems, 'image')),
// color: Colors.blue, // color: Colors.blue,
borderRadius: borderRadius: const BorderRadius.all(
const BorderRadius.all(Radius.elliptical(10, 10)), Radius.elliptical(8, 8)),
), ),
)), )),
title: Text(maxLines: 1, activity.title), title: Consumer<ActivityTimerModel>(
subtitle: Text(maxLines: 2, activity.description), 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) { ImageProvider findMediaByType(List<MediaItem> media, String type) {
var found = media?.where((m) => m.type == 'image'); Iterable<MediaItem>? found = media.where((m) => m.type == MediaType.image);
if (found != null) { if (found.isNotEmpty) {
return NetworkImage(found.first.reference); return NetworkImage(found.first.reference);
} else { } else {
// Element is not found // Element is not found

View File

@ -1,15 +1,19 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:sendtrain/classes/activity_action.dart'; import 'package:sendtrain/daos/actions_dao.dart';
import 'package:sendtrain/classes/media.dart'; import 'package:sendtrain/database/database.dart';
import 'package:sendtrain/models/activity_model.dart'; import 'package:sendtrain/extensions/string_extensions.dart';
import 'package:sendtrain/models/activity_timer_model.dart'; import 'package:sendtrain/models/activity_timer_model.dart';
import 'package:sendtrain/widgets/activity_action_view.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 { class ActivityView extends StatefulWidget {
const ActivityView({super.key, required this.activity}); const ActivityView(
final ActivityModel activity; {super.key, required this.activity});
final Activity activity;
@override @override
State<ActivityView> createState() => _ActivityViewState(); State<ActivityView> createState() => _ActivityViewState();
@ -18,38 +22,88 @@ class ActivityView extends StatefulWidget {
class _ActivityViewState extends State<ActivityView> { class _ActivityViewState extends State<ActivityView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ActivityModel activity = widget.activity; final Activity activity = widget.activity;
ActivityTimerModel atm = ActivityTimerModel atm =
Provider.of<ActivityTimerModel>(context, listen: false); 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 Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ 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( AppBar(
titleSpacing: 0,
centerTitle: true, centerTitle: true,
title: const Text('Activity', style: TextStyle(fontSize: 15)), title: const Text('Activity',
style: TextStyle(fontSize: 15)),
), ),
Padding( Padding(
padding: padding: const EdgeInsets.only(
const EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 10), left: 15, right: 20, top: 15, bottom: 10),
child: Text( child: Text(
maxLines: 1, maxLines: 1,
style: const TextStyle(fontSize: 25, fontWeight: FontWeight.bold), style: const TextStyle(
activity.title)), fontSize: 25, fontWeight: FontWeight.bold),
ActivityViewCategories(categories: activity.categories), activity.title.toTitleCase())),
Padding( Padding(
padding: padding: const EdgeInsets.fromLTRB(10, 0, 0, 10),
const EdgeInsets.only(top: 0, bottom: 20, left: 15, right: 15), 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( child: Text(
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: const TextStyle(fontSize: 15), style: const TextStyle(fontSize: 15),
activity.description)), activity.description)),
const Padding(
padding: EdgeInsets.fromLTRB(15, 20, 0, 10),
child: Text(
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
'Media:')),
ActivityViewMedia(activity: activity), ActivityViewMedia(activity: activity),
const Padding( const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10), padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text( child: Text(
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
'Actions')), 'Actions')),
Padding( Padding(
padding: const EdgeInsets.only(left: 10, right: 10), padding: const EdgeInsets.only(left: 10, right: 10),
@ -64,39 +118,54 @@ class _ActivityViewState extends State<ActivityView> {
child: Row(children: [ child: Row(children: [
Ink( Ink(
width: 70, width: 70,
color: Theme.of(context).colorScheme.primaryContainer, color: Theme.of(context)
.colorScheme
.primaryContainer,
child: Consumer<ActivityTimerModel>( child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) { builder: (context, atm, child) {
return IconButton( return IconButton(
alignment: AlignmentDirectional.center, alignment:
AlignmentDirectional.center,
icon: atm.isActive icon: atm.isActive
? const Icon(Icons.pause_rounded) ? const Icon(
: const Icon(Icons.play_arrow_rounded), Icons.pause_rounded)
onPressed: () => : const Icon(
{atm.isActive ? atm.pause() : atm.start()}); Icons.play_arrow_rounded),
onPressed: () => {
atm.isActive
? atm.pause()
: atm.start()
});
}, },
)), )),
Expanded( Expanded(
flex: 1, flex: 1,
child: Stack(alignment: Alignment.center, children: [ child: Stack(
alignment: Alignment.center,
children: [
Container( Container(
alignment: Alignment.center, alignment: Alignment.center,
child: Consumer<ActivityTimerModel>( child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) { builder: (context, atm, child) {
return Text( return Text(
style: const TextStyle(fontSize: 20), style: const TextStyle(
fontSize: 20),
textAlign: TextAlign.center, textAlign: TextAlign.center,
'${atm.actionCount} ${atm.currentAction['type']}'); '${atm.actionCount} ${atm.currentAction['type']}'.toTitleCase());
}, },
), ),
), ),
Container( Container(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 10), padding:
child: Consumer<ActivityTimerModel>( EdgeInsets.only(right: 15),
builder: (context, atm, child) { child:
Consumer<ActivityTimerModel>(
builder: (context, atm,
child) {
return Text( return Text(
style: const TextStyle(fontSize: 12), style: const TextStyle(
fontSize: 12),
textAlign: TextAlign.right, textAlign: TextAlign.right,
'${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}'); '${atm.currentAction['actionID'] + 1} of ${atm.totalActions()}');
})), })),
@ -104,82 +173,24 @@ class _ActivityViewState extends State<ActivityView> {
]))), ]))),
Padding( Padding(
padding: EdgeInsets.only(left: 14, right: 14), padding: EdgeInsets.only(left: 14, right: 14),
child: Consumer<ActivityTimerModel>(builder: (context, atm, child) { child: Consumer<ActivityTimerModel>(
builder: (context, atm, child) {
return LinearProgressIndicator( return LinearProgressIndicator(
value: atm.progress, value: atm.progress,
semanticsLabel: 'Activity Progress', semanticsLabel: 'Activity Progress',
); );
})), })),
ActivityActionView(action: activity.actions[0]), ActivityActionView(actions: actions),
]); ]));
} } else {
} return Container(
alignment: Alignment.center,
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( child: SizedBox(
height: 40, height: 50.0,
child: ListView.builder( width: 50.0,
shrinkWrap: true, child: CircularProgressIndicator(),
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))
],
);
} }
} }

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

View File

@ -1,170 +1,23 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/date_symbol_data_local.dart';
import 'package:sendtrain/classes/activity_action.dart'; import 'package:sendtrain/database/database.dart' hide ActivityAction;
import 'package:sendtrain/classes/media.dart'; import 'package:sendtrain/extensions/string_extensions.dart';
import 'package:sendtrain/models/activity_model.dart';
import 'package:sendtrain/models/session_model.dart';
import 'package:sendtrain/widgets/session_view.dart'; import 'package:sendtrain/widgets/session_view.dart';
class SessionCard extends StatelessWidget { class SessionCard extends StatelessWidget {
final int state;
final int type; final int type;
const SessionCard({super.key, this.state = 0, this.type = 0}); final Session session;
const SessionCard({super.key, this.type = 0, required this.session});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
initializeDateFormatting('en'); initializeDateFormatting('en');
final DateFormat dateFormat = DateFormat('yyyy-MM-dd'); final DateFormat dateFormat = DateFormat('yyyy-MM-dd');
Color color = (state == 0) Color color = (session.status == SessionStatus.started)
? const Color(0xff3A5FB6) ? Theme.of(context).colorScheme.primaryContainer
: ThemeData.dark(useMaterial3: true).colorScheme.surfaceBright; : 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) { if (type == 0) {
return Card( return Card(
@ -186,7 +39,7 @@ class SessionCard extends StatelessWidget {
.animate(animation); .animate(animation);
return SlideTransition( return SlideTransition(
position: custom, position: custom,
child: Dialog.fullscreen(child: SessionView(data: data))); child: Dialog.fullscreen(child: SessionView(session: session)));
}, },
barrierDismissible: true, barrierDismissible: true,
barrierLabel: '', barrierLabel: '',
@ -212,8 +65,32 @@ class SessionCard extends StatelessWidget {
BorderRadius.all(Radius.elliptical(10, 10)), BorderRadius.all(Radius.elliptical(10, 10)),
), ),
)), )),
title: Text(maxLines: 1, data.title), title: Text(maxLines: 1, session.title.toTitleCase()),
subtitle: Text(maxLines: 1, dateFormat.format(data.date)), subtitle: Text(maxLines: 1, dateFormat.format(session.date as DateTime)),
trailing: IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(Icons.close_rounded),
onPressed: () {
showAdaptiveDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Session Removal'),
content: const Text(
'Would you like to permanently remove this session?'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'Cancel'),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
},
),
), ),
ListTile( ListTile(
contentPadding: const EdgeInsets.fromLTRB(15, 0, 15, 15), contentPadding: const EdgeInsets.fromLTRB(15, 0, 15, 15),
@ -221,14 +98,14 @@ class SessionCard extends StatelessWidget {
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.w300), style: const TextStyle(fontWeight: FontWeight.w300),
data.content), session.content),
), ),
], ],
)), )),
); );
} else { } else {
return Card( return Card(
color: const Color.fromARGB(125, 0, 0, 0), color: color,
child: InkWell( child: InkWell(
// overlayColor: MaterialStateColor(Colors.deepPurple as int), // overlayColor: MaterialStateColor(Colors.deepPurple as int),
splashColor: Colors.deepPurple, splashColor: Colors.deepPurple,
@ -247,7 +124,7 @@ class SessionCard extends StatelessWidget {
return SlideTransition( return SlideTransition(
position: custom, position: custom,
child: child:
Dialog.fullscreen(child: SessionView(data: data))); Dialog.fullscreen(child: SessionView(session: session)));
}, },
barrierDismissible: true, barrierDismissible: true,
barrierLabel: '', barrierLabel: '',
@ -274,11 +151,11 @@ class SessionCard extends StatelessWidget {
ListTile( ListTile(
title: Text( title: Text(
maxLines: 3, maxLines: 3,
data.title, session.title.toTitleCase(),
textAlign: TextAlign.center), textAlign: TextAlign.center),
subtitle: Text( subtitle: Text(
maxLines: 1, maxLines: 1,
dateFormat.format(data.date), dateFormat.format(session.date as DateTime),
textAlign: TextAlign.center), textAlign: TextAlign.center),
), ),
]))))); ])))));

View File

@ -1,143 +1,106 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.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/database.dart';
import 'package:sendtrain/models/activity_model.dart'; import 'package:sendtrain/extensions/string_extensions.dart';
import 'package:sendtrain/models/session_model.dart'; import 'package:sendtrain/widgets/session_view_achievements.dart';
import 'package:sendtrain/widgets/activity_card.dart'; import 'package:sendtrain/widgets/session_view_activities.dart';
import 'package:sendtrain/widgets/media_card.dart'; import 'package:sendtrain/widgets/session_view_media.dart';
class SessionView extends StatelessWidget { class SessionView extends StatelessWidget {
const SessionView({super.key, required this.data}); const SessionView({super.key, required this.session});
final SessionModel data; final Session session;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
initializeDateFormatting('en'); initializeDateFormatting('en');
final DateFormat dateFormat = DateFormat('yyyy-MM-dd'); final DateFormat dateFormat = DateFormat('yyyy-MM-dd');
return Column( 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, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
AppBar( AppBar(
centerTitle: true, centerTitle: true,
title: Text('Session @ ${dateFormat.format(data.date)}', title: Text(
'Session @ ${dateFormat.format(session.date as DateTime)}',
style: const TextStyle(fontSize: 15)), style: const TextStyle(fontSize: 15)),
), ),
Padding( Padding(
padding: padding: const EdgeInsets.only(
const EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 10), left: 15, right: 20, top: 15, bottom: 10),
child: Text( child: Text(
maxLines: 1, maxLines: 1,
style: style: const TextStyle(
const TextStyle(fontSize: 25, fontWeight: FontWeight.bold), fontSize: 25, fontWeight: FontWeight.bold),
data.title)), session.title.toTitleCase())),
SessionViewAchievements(achievements: data.achievements), SessionViewAchievements(session: session),
Padding( Padding(
padding: const EdgeInsets.only(left: 15, right: 15), padding: const EdgeInsets.only(left: 15, right: 15),
child: Text( child: Text(
style: const TextStyle(fontSize: 15), style: const TextStyle(fontSize: 15),
data.content)), session.content)),
const Padding( const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10), padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text( child: Text(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
'Media:')), 'Media:')),
SessionViewMedia(media: data.media), SessionViewMedia(session: session),
const Padding( const Padding(
padding: EdgeInsets.fromLTRB(15, 30, 0, 10), padding: EdgeInsets.fromLTRB(15, 30, 0, 10),
child: Text( child: Text(
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
'Activites:')), 'Activites:')),
SessionViewActivities(activities: data.activities), SessionViewActivities(
// TextButton( activities: activities),
// onPressed: () {
// Navigator.pop(context);
// },
// child: const Text('Close'),
// ),
], ],
);
}
}
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]);
},
)); ));
} } else {
} return Container(
alignment: Alignment.center,
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( child: SizedBox(
height: 40, height: 50.0,
child: ListView.builder( width: 50.0,
scrollDirection: Axis.horizontal, child: CircularProgressIndicator(),
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))
],
);
} }
} }

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

@ -1,11 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
void fl_register_plugins(FlPluginRegistry* registry) {
}

View File

@ -1,15 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@ -1,23 +0,0 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View File

@ -1,12 +0,0 @@
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
import flutter_inappwebview_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
}

View File

@ -0,0 +1,12 @@
import Cocoa
import FlutterMacOS
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

View File

@ -41,6 +41,9 @@ dependencies:
json_annotation: ^4.9.0 json_annotation: ^4.9.0
provider: ^6.1.2 provider: ^6.1.2
scrollable_positioned_list: ^0.3.8 scrollable_positioned_list: ^0.3.8
drift: ^2.22.1
flutter_expandable_fab: ^2.3.0
drift_flutter: ^0.2.2
flutter_launcher_name: flutter_launcher_name:
name: "SendTrain" name: "SendTrain"
@ -57,6 +60,8 @@ dev_dependencies:
flutter_lints: ^5.0.0 flutter_lints: ^5.0.0
build_runner: ^2.4.13 build_runner: ^2.4.13
json_serializable: ^6.9.0 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 # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # 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());
// },
// );
// });
// }

View File

@ -1,14 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
}

View File

@ -1,15 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter/plugin_registry.h>
// Registers Flutter plugins.
void RegisterPlugins(flutter::PluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@ -1,24 +0,0 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
flutter_inappwebview_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)