Compare commits
11 Commits
email-veri
...
main
Author | SHA1 | Date | |
---|---|---|---|
62645b6b4b | |||
9030efd6ed | |||
f5e0be7486 | |||
3b6b765bcd | |||
6079da7fb9 | |||
9eebc28517 | |||
57fa2edef6 | |||
9739ff4901 | |||
0593f5b96b | |||
d4a7c7be66 | |||
d78a7ac298 |
.gitignoreTaskfile.yml
mobile
android
ios
lib
account.dartapi.dartexplore.dartgroup.dartgroup_members.dartgroup_noticeboard.dartgroups.darthome.dartlib.dartlogin.dartmain.dartmodel.dartobject.dartonboarding.dart
pubspec.lockpubspec.yamlpatterns
project.dartprojects.dartregister.dartsettings.dartuser.dartutil.dartverify_email.dartwelcome.dart
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
*.swp
|
*.swp
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
mobile/android/app/.cxx/
|
||||||
|
@ -67,6 +67,7 @@ tasks:
|
|||||||
deps:
|
deps:
|
||||||
- lint-web
|
- lint-web
|
||||||
- lint-api
|
- lint-api
|
||||||
|
- lint-mobile
|
||||||
|
|
||||||
lint-web:
|
lint-web:
|
||||||
desc: Lint web frontend
|
desc: Lint web frontend
|
||||||
@ -83,6 +84,14 @@ tasks:
|
|||||||
- bash -c "source {{.VENV}} && ruff format ."
|
- bash -c "source {{.VENV}} && ruff format ."
|
||||||
- bash -c "source {{.VENV}} && ruff check --fix ."
|
- bash -c "source {{.VENV}} && ruff check --fix ."
|
||||||
|
|
||||||
|
lint-mobile:
|
||||||
|
desc: Lint mobile app
|
||||||
|
dir: 'mobile'
|
||||||
|
cmds:
|
||||||
|
- echo "[MOBILE] Linting Flutter app..."
|
||||||
|
- dart fix --apply
|
||||||
|
- dart analyze
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
desc: Remove all dependencies
|
desc: Remove all dependencies
|
||||||
cmds:
|
cmds:
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
plugins {
|
||||||
|
id "com.android.application"
|
||||||
|
id "kotlin-android"
|
||||||
|
id "dev.flutter.flutter-gradle-plugin"
|
||||||
|
id "com.google.gms.google-services"
|
||||||
|
}
|
||||||
|
|
||||||
def localProperties = new Properties()
|
def localProperties = new Properties()
|
||||||
def localPropertiesFile = rootProject.file('local.properties')
|
def localPropertiesFile = rootProject.file('local.properties')
|
||||||
if (localPropertiesFile.exists()) {
|
if (localPropertiesFile.exists()) {
|
||||||
@ -6,11 +13,6 @@ if (localPropertiesFile.exists()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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')
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
if (flutterVersionCode == null) {
|
if (flutterVersionCode == null) {
|
||||||
flutterVersionCode = '1'
|
flutterVersionCode = '1'
|
||||||
@ -21,10 +23,6 @@ if (flutterVersionName == null) {
|
|||||||
flutterVersionName = '1.0'
|
flutterVersionName = '1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
|
||||||
apply plugin: 'kotlin-android'
|
|
||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
|
||||||
|
|
||||||
def keystoreProperties = new Properties()
|
def keystoreProperties = new Properties()
|
||||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||||
if (keystorePropertiesFile.exists()) {
|
if (keystorePropertiesFile.exists()) {
|
||||||
@ -32,20 +30,18 @@ if (keystorePropertiesFile.exists()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 33
|
compileSdkVersion flutter.compileSdkVersion
|
||||||
|
namespace 'com.treadl'
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
}
|
}
|
||||||
|
|
||||||
lintOptions {
|
|
||||||
disable 'InvalidPackage'
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.treadl"
|
applicationId "com.treadl"
|
||||||
minSdkVersion 29
|
minSdk = flutter.minSdkVersion
|
||||||
targetSdkVersion 34
|
targetSdk = flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
@ -64,14 +60,19 @@ android {
|
|||||||
signingConfig signingConfigs.release
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lint {
|
||||||
|
disable 'InvalidPackage'
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '17'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flutter {
|
flutter {
|
||||||
source '../..'
|
source '../..'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
|
||||||
}
|
|
||||||
|
|
||||||
apply plugin: 'com.google.gms.google-services'
|
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
// Generated file.
|
|
||||||
//
|
|
||||||
// If you wish to remove Flutter's multidex support, delete this entire file.
|
|
||||||
//
|
|
||||||
// Modifications to this file should be done in a copy under a different name
|
|
||||||
// as this file may be regenerated.
|
|
||||||
|
|
||||||
package io.flutter.app;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.annotation.CallSuper;
|
|
||||||
import androidx.multidex.MultiDex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extension of {@link android.app.Application}, adding multidex support.
|
|
||||||
*/
|
|
||||||
public class FlutterMultiDexApplication extends Application {
|
|
||||||
@Override
|
|
||||||
@CallSuper
|
|
||||||
protected void attachBaseContext(Context base) {
|
|
||||||
super.attachBaseContext(base);
|
|
||||||
MultiDex.install(this);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +1,3 @@
|
|||||||
buildscript {
|
|
||||||
ext.kotlin_version = '1.8.20'
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
jcenter()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath 'com.android.tools.build:gradle:7.4.1'
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
||||||
classpath 'com.google.gms:google-services:4.3.3'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
org.gradle.jvmargs=-Xmx1536M
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
android.enableR8=true
|
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
android.bundle.enableUncompressedNativeLibs=false
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
#Sun Apr 06 21:07:46 BST 2025
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@ -1,15 +1,26 @@
|
|||||||
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 flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||||
|
|
||||||
def plugins = new Properties()
|
repositories {
|
||||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
google()
|
||||||
if (pluginsFile.exists()) {
|
mavenCentral()
|
||||||
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.each { name, path ->
|
plugins {
|
||||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||||
include ":$name"
|
id "com.android.application" version '8.9.1' apply false
|
||||||
project(":$name").projectDir = pluginDirectory
|
id "org.jetbrains.kotlin.android" version "2.1.10" apply false
|
||||||
|
id "com.google.gms.google-services" version "4.3.3" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
include ":app"
|
||||||
|
@ -33,31 +33,31 @@ PODS:
|
|||||||
- file_picker (0.0.1):
|
- file_picker (0.0.1):
|
||||||
- DKImagePickerController/PhotoGallery
|
- DKImagePickerController/PhotoGallery
|
||||||
- Flutter
|
- Flutter
|
||||||
- Firebase/CoreOnly (11.8.0):
|
- Firebase/CoreOnly (11.10.0):
|
||||||
- FirebaseCore (~> 11.8.0)
|
- FirebaseCore (~> 11.10.0)
|
||||||
- Firebase/Messaging (11.8.0):
|
- Firebase/Messaging (11.10.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseMessaging (~> 11.8.0)
|
- FirebaseMessaging (~> 11.10.0)
|
||||||
- firebase_core (3.12.1):
|
- firebase_core (3.13.0):
|
||||||
- Firebase/CoreOnly (= 11.8.0)
|
- Firebase/CoreOnly (= 11.10.0)
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_messaging (15.2.4):
|
- firebase_messaging (15.2.5):
|
||||||
- Firebase/Messaging (= 11.8.0)
|
- Firebase/Messaging (= 11.10.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- FirebaseCore (11.8.1):
|
- FirebaseCore (11.10.0):
|
||||||
- FirebaseCoreInternal (~> 11.8.0)
|
- FirebaseCoreInternal (~> 11.10.0)
|
||||||
- GoogleUtilities/Environment (~> 8.0)
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
- GoogleUtilities/Logger (~> 8.0)
|
- GoogleUtilities/Logger (~> 8.0)
|
||||||
- FirebaseCoreInternal (11.8.0):
|
- FirebaseCoreInternal (11.10.0):
|
||||||
- "GoogleUtilities/NSData+zlib (~> 8.0)"
|
- "GoogleUtilities/NSData+zlib (~> 8.0)"
|
||||||
- FirebaseInstallations (11.8.0):
|
- FirebaseInstallations (11.10.0):
|
||||||
- FirebaseCore (~> 11.8.0)
|
- FirebaseCore (~> 11.10.0)
|
||||||
- GoogleUtilities/Environment (~> 8.0)
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||||
- PromisesObjC (~> 2.4)
|
- PromisesObjC (~> 2.4)
|
||||||
- FirebaseMessaging (11.8.0):
|
- FirebaseMessaging (11.10.0):
|
||||||
- FirebaseCore (~> 11.8.0)
|
- FirebaseCore (~> 11.10.0)
|
||||||
- FirebaseInstallations (~> 11.0)
|
- FirebaseInstallations (~> 11.0)
|
||||||
- GoogleDataTransport (~> 10.0)
|
- GoogleDataTransport (~> 10.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 8.0)
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.0)
|
||||||
@ -172,18 +172,18 @@ SPEC CHECKSUMS:
|
|||||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||||
Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf
|
Firebase: 1fe1c0a7d9aaea32efe01fbea5f0ebd8d70e53a2
|
||||||
firebase_core: 8d552814f6c01ccde5d88939fced4ec26f2f5510
|
firebase_core: 2d4534e7b489907dcede540c835b48981d890943
|
||||||
firebase_messaging: 8b96a4f09841c15a16b96973ef5c3dcfc1a064e4
|
firebase_messaging: 75bc93a4df25faccad67f6662ae872ac9ae69b64
|
||||||
FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d
|
FirebaseCore: 8344daef5e2661eb004b177488d6f9f0f24251b7
|
||||||
FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629
|
FirebaseCoreInternal: ef4505d2afb1d0ebbc33162cb3795382904b5679
|
||||||
FirebaseInstallations: 6c963bd2a86aca0481eef4f48f5a4df783ae5917
|
FirebaseInstallations: 9980995bdd06ec8081dfb6ab364162bdd64245c3
|
||||||
FirebaseMessaging: 487b634ccdf6f7b7ff180fdcb2a9935490f764e8
|
FirebaseMessaging: 2b9f56aa4ed286e1f0ce2ee1d413aabb8f9f5cb9
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
|
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
||||||
image_picker_ios: afb77645f1e1060a27edb6793996ff9b42256909
|
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
|
||||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
import 'model.dart';
|
import 'model.dart';
|
||||||
import 'lib.dart';
|
|
||||||
|
|
||||||
class _AccountScreenState extends State<AccountScreen> {
|
class _AccountScreenState extends State<AccountScreen> {
|
||||||
final TextEditingController _emailController = TextEditingController();
|
final TextEditingController _emailController = TextEditingController();
|
||||||
@ -19,8 +16,8 @@ class _AccountScreenState extends State<AccountScreen> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
AppModel model = Provider.of<AppModel>(context, listen: false);
|
AppModel model = Provider.of<AppModel>(context, listen: false);
|
||||||
if (model.user != null) {
|
if (model.user != null) {
|
||||||
_emailController.text = model.user!.email! ?? '';
|
_emailController.text = model.user!.email ?? '';
|
||||||
_usernameController.text = model.user!.username! ?? '';
|
_usernameController.text = model.user!.username;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +93,6 @@ class _AccountScreenState extends State<AccountScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
AppModel model = Provider.of<AppModel>(context);
|
AppModel model = Provider.of<AppModel>(context);
|
||||||
User? user = model.user;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Edit Account'),
|
title: Text('Edit Account'),
|
||||||
@ -149,7 +145,7 @@ class _AccountScreenState extends State<AccountScreen> {
|
|||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
child: Text('Open Treadl website'),
|
child: Text('Open Treadl website'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
launch('https://treadl.com/${model.user?.username}');
|
launchUrl(Uri.parse('https://treadl.com/${model.user?.username}'));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@ -160,6 +156,7 @@ class _AccountScreenState extends State<AccountScreen> {
|
|||||||
|
|
||||||
class AccountScreen extends StatefulWidget {
|
class AccountScreen extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
AccountScreen();
|
const AccountScreen({super.key});
|
||||||
_AccountScreenState createState() => _AccountScreenState();
|
@override
|
||||||
|
State<AccountScreen> createState() => _AccountScreenState();
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'util.dart';
|
import 'util.dart';
|
||||||
import 'model.dart';
|
|
||||||
|
|
||||||
class Api {
|
class Api {
|
||||||
|
|
||||||
@ -13,7 +10,7 @@ class Api {
|
|||||||
final String apiBase = 'https://api.treadl.com';
|
final String apiBase = 'https://api.treadl.com';
|
||||||
//final String apiBase = 'http://localhost:2001';
|
//final String apiBase = 'http://localhost:2001';
|
||||||
|
|
||||||
Api({token: null}) {
|
Api({token}) {
|
||||||
if (token != null) _token = token;
|
if (token != null) _token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +26,7 @@ class Api {
|
|||||||
Map<String,String> headers = {};
|
Map<String,String> headers = {};
|
||||||
String? token = await loadToken();
|
String? token = await loadToken();
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
headers['Authorization'] = 'Bearer ' + token!;
|
headers['Authorization'] = 'Bearer $token';
|
||||||
}
|
}
|
||||||
if (method == 'POST' || method == 'DELETE') {
|
if (method == 'POST' || method == 'DELETE') {
|
||||||
headers['Content-Type'] = 'application/json';
|
headers['Content-Type'] = 'application/json';
|
||||||
@ -42,17 +39,17 @@ class Api {
|
|||||||
return await client.get(url, headers: await getHeaders('GET'));
|
return await client.get(url, headers: await getHeaders('GET'));
|
||||||
}
|
}
|
||||||
Future<http.Response> _post(Uri url, Map<String, dynamic>? data) async {
|
Future<http.Response> _post(Uri url, Map<String, dynamic>? data) async {
|
||||||
String? json = null;
|
String? json;
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
json = jsonEncode(data!);
|
json = jsonEncode(data);
|
||||||
}
|
}
|
||||||
http.Client client = http.Client();
|
http.Client client = http.Client();
|
||||||
return await client.post(url, headers: await getHeaders('POST'), body: json);
|
return await client.post(url, headers: await getHeaders('POST'), body: json);
|
||||||
}
|
}
|
||||||
Future<http.Response> _put(Uri url, Map<String, dynamic>? data) async {
|
Future<http.Response> _put(Uri url, Map<String, dynamic>? data) async {
|
||||||
String? json = null;
|
String? json;
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
json = jsonEncode(data!);
|
json = jsonEncode(data);
|
||||||
}
|
}
|
||||||
http.Client client = http.Client();
|
http.Client client = http.Client();
|
||||||
return await client.put(url, headers: await getHeaders('POST'), body: json);
|
return await client.put(url, headers: await getHeaders('POST'), body: json);
|
||||||
@ -86,15 +83,13 @@ class Api {
|
|||||||
if (response == null) {
|
if (response == null) {
|
||||||
return {'success': false, 'message': 'No response for your request'};
|
return {'success': false, 'message': 'No response for your request'};
|
||||||
}
|
}
|
||||||
int status = response!.statusCode;
|
int status = response.statusCode;
|
||||||
if (status == 200) {
|
if (status == 200) {
|
||||||
print('SUCCESS');
|
Map<String, dynamic> respData = jsonDecode(response.body);
|
||||||
Map<String, dynamic> respData = jsonDecode(response!.body);
|
|
||||||
return {'success': true, 'payload': respData};
|
return {'success': true, 'payload': respData};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
print('ERROR');
|
Map<String, dynamic> respData = jsonDecode(response.body);
|
||||||
Map<String, dynamic> respData = jsonDecode(response!.body);
|
|
||||||
return {'success': false, 'code': status, 'message': respData['message']};
|
return {'success': false, 'code': status, 'message': respData['message']};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
import 'util.dart';
|
import 'util.dart';
|
||||||
import 'lib.dart';
|
import 'lib.dart';
|
||||||
@ -23,7 +20,7 @@ class _ExploreTabState extends State<ExploreTab> {
|
|||||||
|
|
||||||
void getExploreData() async {
|
void getExploreData() async {
|
||||||
if (explorePage == -1) return;
|
if (explorePage == -1) return;
|
||||||
var data = await api.request('GET', '/search/explore?page=${explorePage}');
|
var data = await api.request('GET', '/search/explore?page=$explorePage');
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
setState(() {
|
setState(() {
|
||||||
loading = false;
|
loading = false;
|
||||||
@ -76,42 +73,42 @@ class _ExploreTabState extends State<ExploreTab> {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: CircularProgressIndicator()
|
child: CircularProgressIndicator()
|
||||||
)
|
)
|
||||||
: Container(
|
: Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
children: [
|
||||||
children: [
|
SizedBox(height: 10),
|
||||||
SizedBox(height: 10),
|
CustomText('Discover projects', 'h1', margin: 5),
|
||||||
CustomText('Discover projects', 'h1', margin: 5),
|
SizedBox(height: 5),
|
||||||
SizedBox(height: 5),
|
SizedBox(
|
||||||
Container(
|
height: 130,
|
||||||
height: 130,
|
child: ListView(
|
||||||
child: ListView(
|
scrollDirection: Axis.horizontal,
|
||||||
scrollDirection: Axis.horizontal,
|
children: projects.map((p) => ProjectCard(p)).toList()
|
||||||
children: projects.map((p) => ProjectCard(p)).toList()
|
)
|
||||||
)
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
CustomText('Recent patterns', 'h1', margin: 5),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
Expanded(child: Container(
|
||||||
|
margin: EdgeInsets.only(left: 15, right: 15),
|
||||||
|
child: GridView.count(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
mainAxisSpacing: 5,
|
||||||
|
crossAxisSpacing: 5,
|
||||||
|
childAspectRatio: 0.9,
|
||||||
|
children: patternCards,
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
)),
|
||||||
CustomText('Recent patterns', 'h1', margin: 5),
|
]
|
||||||
SizedBox(height: 5),
|
|
||||||
Expanded(child: Container(
|
|
||||||
margin: EdgeInsets.only(left: 15, right: 15),
|
|
||||||
child: GridView.count(
|
|
||||||
crossAxisCount: 2,
|
|
||||||
mainAxisSpacing: 5,
|
|
||||||
crossAxisSpacing: 5,
|
|
||||||
childAspectRatio: 0.9,
|
|
||||||
children: patternCards,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExploreTab extends StatefulWidget {
|
class ExploreTab extends StatefulWidget {
|
||||||
|
const ExploreTab({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ExploreTabState createState() => _ExploreTabState();
|
State<ExploreTab> createState() => _ExploreTabState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
import 'group_noticeboard.dart';
|
import 'group_noticeboard.dart';
|
||||||
import 'group_members.dart';
|
import 'group_members.dart';
|
||||||
|
|
||||||
class _GroupScreenState extends State<GroupScreen> {
|
class _GroupScreenState extends State<GroupScreen> {
|
||||||
final String id;
|
|
||||||
Map<String, dynamic>? _group;
|
Map<String, dynamic>? _group;
|
||||||
int _selectedIndex = 0;
|
int _selectedIndex = 0;
|
||||||
|
|
||||||
_GroupScreenState(this.id) { }
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
fetchGroup();
|
fetchGroup();
|
||||||
@ -20,7 +15,7 @@ class _GroupScreenState extends State<GroupScreen> {
|
|||||||
|
|
||||||
void fetchGroup() async {
|
void fetchGroup() async {
|
||||||
Api api = Api();
|
Api api = Api();
|
||||||
var data = await api.request('GET', '/groups/' + id);
|
var data = await api.request('GET', '/groups/${widget.id}');
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_group = data['payload'];
|
_group = data['payload'];
|
||||||
@ -65,7 +60,7 @@ class _GroupScreenState extends State<GroupScreen> {
|
|||||||
|
|
||||||
class GroupScreen extends StatefulWidget {
|
class GroupScreen extends StatefulWidget {
|
||||||
final String id;
|
final String id;
|
||||||
GroupScreen(this.id) { }
|
const GroupScreen(this.id, {super.key});
|
||||||
@override
|
@override
|
||||||
_GroupScreenState createState() => _GroupScreenState(id);
|
State<GroupScreen> createState() => _GroupScreenState();
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,22 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
import 'util.dart';
|
import 'util.dart';
|
||||||
import 'user.dart';
|
|
||||||
|
|
||||||
class _GroupMembersTabState extends State<GroupMembersTab> {
|
class _GroupMembersTabState extends State<GroupMembersTab> {
|
||||||
final Map<String,dynamic> _group;
|
|
||||||
final Api api = Api();
|
final Api api = Api();
|
||||||
List<dynamic> _members = [];
|
List<dynamic> _members = [];
|
||||||
bool _loading = false;
|
bool _loading = false;
|
||||||
|
|
||||||
_GroupMembersTabState(this._group) { }
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
getMembers(_group['_id']);
|
getMembers(widget.group['_id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getMembers(String id) async {
|
void getMembers(String id) async {
|
||||||
setState(() => _loading = true);
|
setState(() => _loading = true);
|
||||||
var data = await api.request('GET', '/groups/' + id + '/members');
|
var data = await api.request('GET', '/groups/$id/members');
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_members = data['payload']['members'];
|
_members = data['payload']['members'];
|
||||||
@ -31,8 +26,8 @@ class _GroupMembersTabState extends State<GroupMembersTab> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget getMemberCard(member) {
|
Widget getMemberCard(member) {
|
||||||
return new ListTile(
|
return ListTile(
|
||||||
onTap: () => context.push('/' + member['username']),
|
onTap: () => context.push('/${member["username"]}'),
|
||||||
leading: Util.avatarImage(Util.avatarUrl(member), size: 40),
|
leading: Util.avatarImage(Util.avatarUrl(member), size: 40),
|
||||||
trailing: Icon(Icons.keyboard_arrow_right),
|
trailing: Icon(Icons.keyboard_arrow_right),
|
||||||
title: Text(member['username'])
|
title: Text(member['username'])
|
||||||
@ -49,17 +44,15 @@ class _GroupMembersTabState extends State<GroupMembersTab> {
|
|||||||
)
|
)
|
||||||
:Column(
|
:Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Expanded(
|
||||||
child: Expanded(
|
child: ListView.builder(
|
||||||
child: ListView.builder(
|
padding: const EdgeInsets.all(8),
|
||||||
padding: const EdgeInsets.all(8),
|
itemCount: _members.length,
|
||||||
itemCount: _members.length,
|
itemBuilder: (BuildContext context, int index) {
|
||||||
itemBuilder: (BuildContext context, int index) {
|
return getMemberCard(_members[index]);
|
||||||
return getMemberCard(_members[index]);
|
},
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -67,7 +60,7 @@ class _GroupMembersTabState extends State<GroupMembersTab> {
|
|||||||
|
|
||||||
class GroupMembersTab extends StatefulWidget {
|
class GroupMembersTab extends StatefulWidget {
|
||||||
final Map<String,dynamic> group;
|
final Map<String,dynamic> group;
|
||||||
GroupMembersTab(this.group) { }
|
const GroupMembersTab(this.group, {super.key});
|
||||||
@override
|
@override
|
||||||
_GroupMembersTabState createState() => _GroupMembersTabState(group);
|
State<GroupMembersTab> createState() => _GroupMembersTabState();
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,29 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
import 'lib.dart';
|
import 'lib.dart';
|
||||||
|
|
||||||
class _GroupNoticeBoardTabState extends State<GroupNoticeBoardTab> {
|
class _GroupNoticeBoardTabState extends State<GroupNoticeBoardTab> {
|
||||||
final TextEditingController _newEntryController = TextEditingController();
|
final TextEditingController _newEntryController = TextEditingController();
|
||||||
final Api api = Api();
|
final Api api = Api();
|
||||||
Map<String,dynamic> _group;
|
|
||||||
List<dynamic> _entries = [];
|
List<dynamic> _entries = [];
|
||||||
bool showPostButton = false;
|
bool showPostButton = false;
|
||||||
bool _loading = false;
|
bool _loading = false;
|
||||||
bool _posting = false;
|
bool _posting = false;
|
||||||
|
|
||||||
_GroupNoticeBoardTabState(this._group) { }
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
getEntries(_group['_id']);
|
getEntries(widget.group['_id']);
|
||||||
_newEntryController.addListener(() {
|
_newEntryController.addListener(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
showPostButton = _newEntryController.text.length > 0 ? true : false;
|
showPostButton = _newEntryController.text.isNotEmpty ? true : false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void getEntries(String id) async {
|
void getEntries(String id) async {
|
||||||
setState(() => _loading = true);
|
setState(() => _loading = true);
|
||||||
var data = await api.request('GET', '/groups/' + id + '/entries');
|
var data = await api.request('GET', '/groups/$id/entries');
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_entries = data['payload']['entries'];
|
_entries = data['payload']['entries'];
|
||||||
@ -39,9 +34,9 @@ class _GroupNoticeBoardTabState extends State<GroupNoticeBoardTab> {
|
|||||||
|
|
||||||
void _sendPost(context) async {
|
void _sendPost(context) async {
|
||||||
String text = _newEntryController.text;
|
String text = _newEntryController.text;
|
||||||
if (text.length == 0) return;
|
if (text.isEmpty) return;
|
||||||
setState(() => _posting = true);
|
setState(() => _posting = true);
|
||||||
var data = await api.request('POST', '/groups/' + _group['_id'] + '/entries', {'content': text});
|
var data = await api.request('POST', '/groups/${widget.group["_id"]}/entries', {'content': text});
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
_newEntryController.value = TextEditingValue(text: '');
|
_newEntryController.value = TextEditingValue(text: '');
|
||||||
FocusScope.of(context).requestFocus(FocusNode());
|
FocusScope.of(context).requestFocus(FocusNode());
|
||||||
@ -83,28 +78,26 @@ class _GroupNoticeBoardTabState extends State<GroupNoticeBoardTab> {
|
|||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
NoticeboardInput(_newEntryController, () => _sendPost(context), _posting, label: 'Write a new post to the group'),
|
NoticeboardInput(_newEntryController, () => _sendPost(context), _posting, label: 'Write a new post to the group'),
|
||||||
Container(
|
Expanded(
|
||||||
child: Expanded(
|
child: _loading ?
|
||||||
child: _loading ?
|
Container(
|
||||||
Container(
|
margin: const EdgeInsets.all(10.0),
|
||||||
margin: const EdgeInsets.all(10.0),
|
alignment: Alignment.center,
|
||||||
alignment: Alignment.center,
|
child: CircularProgressIndicator()
|
||||||
child: CircularProgressIndicator()
|
)
|
||||||
)
|
:
|
||||||
:
|
ListView.builder(
|
||||||
ListView.builder(
|
padding: const EdgeInsets.all(0),
|
||||||
padding: const EdgeInsets.all(0),
|
itemCount: entries.length,
|
||||||
itemCount: entries.length,
|
itemBuilder: (BuildContext context, int index) {
|
||||||
itemBuilder: (BuildContext context, int index) {
|
return Container(
|
||||||
return Container(
|
key: Key(entries[index]['_id']),
|
||||||
key: Key(entries[index]['_id']),
|
child: NoticeboardPost(entries[index],
|
||||||
child: NoticeboardPost(entries[index],
|
onDelete: _onDelete,
|
||||||
onDelete: _onDelete,
|
onReply: _onReply,
|
||||||
onReply: _onReply,
|
));
|
||||||
));
|
},
|
||||||
},
|
),
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@ -113,7 +106,7 @@ class _GroupNoticeBoardTabState extends State<GroupNoticeBoardTab> {
|
|||||||
|
|
||||||
class GroupNoticeBoardTab extends StatefulWidget {
|
class GroupNoticeBoardTab extends StatefulWidget {
|
||||||
final Map<String,dynamic> group;
|
final Map<String,dynamic> group;
|
||||||
GroupNoticeBoardTab(this.group) { }
|
const GroupNoticeBoardTab(this.group, {super.key});
|
||||||
@override
|
@override
|
||||||
_GroupNoticeBoardTabState createState() => _GroupNoticeBoardTabState(group);
|
State<GroupNoticeBoardTab> createState() => _GroupNoticeBoardTabState();
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,13 @@ class _GroupsTabState extends State<GroupsTab> {
|
|||||||
Widget buildGroupCard(Map<String,dynamic> group) {
|
Widget buildGroupCard(Map<String,dynamic> group) {
|
||||||
String? description = group['description'];
|
String? description = group['description'];
|
||||||
if (description != null && description.length > 80) {
|
if (description != null && description.length > 80) {
|
||||||
description = description.substring(0, 77) + '...';
|
description = '${description.substring(0, 77)}...';
|
||||||
} else if (description == null) {
|
} else {
|
||||||
description = 'This group doesn\'t have a description.';
|
description ??= 'This group doesn\'t have a description.';
|
||||||
}
|
}
|
||||||
return Card(
|
return Card(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => context.push('/groups/' + group['_id']),
|
onTap: () => context.push('/groups/${group["_id"]}'),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Icon(Icons.people, size: 40, color: Colors.pink[300]),
|
leading: Icon(Icons.people, size: 40, color: Colors.pink[300]),
|
||||||
trailing: Icon(Icons.keyboard_arrow_right),
|
trailing: Icon(Icons.keyboard_arrow_right),
|
||||||
@ -51,18 +51,18 @@ class _GroupsTabState extends State<GroupsTab> {
|
|||||||
|
|
||||||
Widget getBody() {
|
Widget getBody() {
|
||||||
AppModel model = Provider.of<AppModel>(context);
|
AppModel model = Provider.of<AppModel>(context);
|
||||||
if (model.user == null)
|
if (model.user == null) {
|
||||||
return LoginNeeded(text: 'Once logged in, you\'ll find your groups here.');
|
return LoginNeeded(text: 'Once logged in, you\'ll find your groups here.');
|
||||||
else if (_loading)
|
} else if (_loading) {
|
||||||
return CircularProgressIndicator();
|
return CircularProgressIndicator();
|
||||||
else if (_groups != null && _groups.length > 0)
|
} else if (_groups.isNotEmpty) {
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: _groups.length,
|
itemCount: _groups.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return buildGroupCard(_groups[index]);
|
return buildGroupCard(_groups[index]);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -72,6 +72,7 @@ class _GroupsTabState extends State<GroupsTab> {
|
|||||||
Text('Groups let you meet and keep in touch with others in the weaving community.', textAlign: TextAlign.center),
|
Text('Groups let you meet and keep in touch with others in the weaving community.', textAlign: TextAlign.center),
|
||||||
Text('Please use our website to join and leave groups.', textAlign: TextAlign.center),
|
Text('Please use our website to join and leave groups.', textAlign: TextAlign.center),
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -90,6 +91,8 @@ class _GroupsTabState extends State<GroupsTab> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GroupsTab extends StatefulWidget {
|
class GroupsTab extends StatefulWidget {
|
||||||
|
const GroupsTab({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_GroupsTabState createState() => _GroupsTabState();
|
State<GroupsTab> createState() => _GroupsTabState();
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:convert';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'explore.dart';
|
import 'explore.dart';
|
||||||
import 'projects.dart';
|
import 'projects.dart';
|
||||||
import 'groups.dart';
|
import 'groups.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatefulWidget {
|
class HomeScreen extends StatefulWidget {
|
||||||
|
const HomeScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<HomeScreen> createState() => _MyStatefulWidgetState();
|
State<HomeScreen> createState() => _MyStatefulWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyStatefulWidgetState extends State<HomeScreen> {
|
class _MyStatefulWidgetState extends State<HomeScreen> {
|
||||||
int _selectedIndex = 0;
|
int _selectedIndex = 0;
|
||||||
List<Widget> _widgetOptions = <Widget> [
|
final List<Widget> _widgetOptions = <Widget> [
|
||||||
ExploreTab(),
|
ExploreTab(),
|
||||||
ProjectsTab(),
|
ProjectsTab(),
|
||||||
GroupsTab()
|
GroupsTab()
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
@ -7,9 +6,6 @@ import 'package:url_launcher/url_launcher.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
import 'util.dart';
|
import 'util.dart';
|
||||||
import 'user.dart';
|
|
||||||
import 'object.dart';
|
|
||||||
import 'project.dart';
|
|
||||||
|
|
||||||
class Alert extends StatelessWidget {
|
class Alert extends StatelessWidget {
|
||||||
final String type;
|
final String type;
|
||||||
@ -18,7 +14,7 @@ class Alert extends StatelessWidget {
|
|||||||
final String actionText;
|
final String actionText;
|
||||||
final Widget? descriptionWidget;
|
final Widget? descriptionWidget;
|
||||||
final Function? action;
|
final Function? action;
|
||||||
Alert({this.type = 'info', this.title = '', this.description = '', this.descriptionWidget = null, this.actionText = 'Click here', this.action}) {}
|
const Alert({super.key, this.type = 'info', this.title = '', this.description = '', this.descriptionWidget, this.actionText = 'Click here', this.action});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -38,9 +34,9 @@ class Alert extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.all(15),
|
padding: EdgeInsets.all(15),
|
||||||
margin: EdgeInsets.all(15),
|
margin: EdgeInsets.all(15),
|
||||||
decoration: new BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: accentColor,
|
color: accentColor,
|
||||||
borderRadius: new BorderRadius.all(Radius.circular(10.0)),
|
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(color: Colors.grey[50]!, spreadRadius: 5),
|
BoxShadow(color: Colors.grey[50]!, spreadRadius: 5),
|
||||||
],
|
],
|
||||||
@ -52,7 +48,7 @@ class Alert extends StatelessWidget {
|
|||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
Text(description, textAlign: TextAlign.center),
|
Text(description, textAlign: TextAlign.center),
|
||||||
if (descriptionWidget != null) descriptionWidget!,
|
if (descriptionWidget != null) descriptionWidget!,
|
||||||
if (actionText != null && action != null)
|
if (action != null)
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
child: Text(actionText),
|
child: Text(actionText),
|
||||||
onPressed: () => action!(),
|
onPressed: () => action!(),
|
||||||
@ -67,28 +63,25 @@ class NoticeboardPost extends StatefulWidget {
|
|||||||
final Map<String,dynamic> _entry;
|
final Map<String,dynamic> _entry;
|
||||||
final Function? onDelete;
|
final Function? onDelete;
|
||||||
final Function? onReply;
|
final Function? onReply;
|
||||||
NoticeboardPost(this._entry, {this.onDelete = null, this.onReply = null});
|
const NoticeboardPost(this._entry, {super.key, this.onDelete, this.onReply});
|
||||||
_NoticeboardPostState createState() => _NoticeboardPostState(_entry, onDelete: onDelete, onReply: onReply);
|
@override
|
||||||
|
State<NoticeboardPost> createState() => _NoticeboardPostState();
|
||||||
}
|
}
|
||||||
class _NoticeboardPostState extends State<NoticeboardPost> {
|
class _NoticeboardPostState extends State<NoticeboardPost> {
|
||||||
final Map<String,dynamic> _entry;
|
final Api api = Api();
|
||||||
final Api api = new Api();
|
|
||||||
final Function? onDelete;
|
|
||||||
final Function? onReply;
|
|
||||||
final TextEditingController _replyController = TextEditingController();
|
final TextEditingController _replyController = TextEditingController();
|
||||||
bool _isReplying = false;
|
bool _isReplying = false;
|
||||||
bool _replying = false;
|
bool _replying = false;
|
||||||
|
|
||||||
_NoticeboardPostState(this._entry, {this.onDelete = null, this.onReply = null}) { }
|
|
||||||
|
|
||||||
void _sendReply() async {
|
void _sendReply() async {
|
||||||
setState(() => _replying = true);
|
setState(() => _replying = true);
|
||||||
var data = await api.request('POST', '/groups/' + _entry['group'] + '/entries/' + _entry['_id'] + '/replies', {'content': _replyController.text});
|
var data = await api.request('POST', '/groups/${widget._entry["group"]}/entries/${widget._entry["_id"]}/replies', {'content': _replyController.text});
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
_replyController.value = TextEditingValue(text: '');
|
_replyController.value = TextEditingValue(text: '');
|
||||||
|
if (!mounted) return;
|
||||||
FocusScope.of(context).requestFocus(FocusNode());
|
FocusScope.of(context).requestFocus(FocusNode());
|
||||||
if (onReply != null) {
|
if (widget.onReply != null) {
|
||||||
onReply!(data['payload']);
|
widget.onReply!(data['payload']);
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_replying = false;
|
_replying = false;
|
||||||
@ -98,38 +91,40 @@ class _NoticeboardPostState extends State<NoticeboardPost> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _deletePost() async {
|
void _deletePost() async {
|
||||||
var data = await api.request('DELETE', '/groups/' + _entry['group'] + '/entries/' + _entry['_id']);
|
var data = await api.request('DELETE', '/groups/${widget._entry["group"]}/entries/${widget._entry["_id"]}');
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
if (onDelete != null) {
|
if (widget.onDelete != null) {
|
||||||
onDelete!(_entry);
|
widget.onDelete!(widget._entry);
|
||||||
|
}
|
||||||
|
if (mounted) {
|
||||||
|
context.pop();
|
||||||
}
|
}
|
||||||
context.pop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var createdAt = DateTime.parse(_entry['createdAt']);
|
var createdAt = DateTime.parse(widget._entry['createdAt']);
|
||||||
bool isReply = _entry['inReplyTo'] != null;
|
bool isReply = widget._entry['inReplyTo'] != null;
|
||||||
int replyCount = _entry['replies'] == null ? 0 : _entry['replies']!.length;
|
int replyCount = widget._entry['replies'] == null ? 0 : widget._entry['replies']!.length;
|
||||||
String replyText = 'Write a reply...';
|
String replyText = 'Write a reply...';
|
||||||
if (replyCount == 1) replyText = '1 Reply';
|
if (replyCount == 1) replyText = '1 Reply';
|
||||||
if (replyCount > 1) replyText = replyCount.toString() + ' replies';
|
if (replyCount > 1) replyText = '$replyCount replies';
|
||||||
if (_isReplying) replyText = 'Cancel reply';
|
if (_isReplying) replyText = 'Cancel reply';
|
||||||
List<Widget> replyWidgets = [];
|
List<Widget> replyWidgets = [];
|
||||||
if (_entry['replies'] != null) {
|
if (widget._entry['replies'] != null) {
|
||||||
for (int i = 0; i < _entry['replies']!.length; i++) {
|
for (int i = 0; i < widget._entry['replies']!.length; i++) {
|
||||||
replyWidgets.add(new Container(
|
replyWidgets.add(Container(
|
||||||
key: Key(_entry['replies']![i]['_id']),
|
key: Key(widget._entry['replies']![i]['_id']),
|
||||||
child: NoticeboardPost(_entry['replies']![i], onDelete: onDelete)
|
child: NoticeboardPost(widget._entry['replies']![i], onDelete: widget.onDelete)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new GestureDetector(
|
return GestureDetector(
|
||||||
key: Key(_entry['_id']),
|
key: Key(widget._entry['_id']),
|
||||||
onLongPress: () async {
|
onLongPress: () async {
|
||||||
Dialog simpleDialog = Dialog(
|
Dialog simpleDialog = Dialog(
|
||||||
child: Container(
|
child: SizedBox(
|
||||||
height: 160.0,
|
height: 160.0,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -137,7 +132,7 @@ class _NoticeboardPostState extends State<NoticeboardPost> {
|
|||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
//color: Colors.orange,
|
//color: Colors.orange,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
launch('https://www.treadl.com');
|
launchUrl(Uri.parse('https://www.treadl.com/report'));
|
||||||
},
|
},
|
||||||
child: Text('Report this post'),
|
child: Text('Report this post'),
|
||||||
),
|
),
|
||||||
@ -168,11 +163,11 @@ class _NoticeboardPostState extends State<NoticeboardPost> {
|
|||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => context.push('/' + _entry['authorUser']['username']),
|
onTap: () => context.push('/${widget._entry["authorUser"]["username"]}'),
|
||||||
child: Util.avatarImage(Util.avatarUrl(_entry['authorUser']), size: isReply ? 30 : 40)
|
child: Util.avatarImage(Util.avatarUrl(widget._entry['authorUser']), size: isReply ? 30 : 40)
|
||||||
),
|
),
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
Text(_entry['authorUser']['username'], style: TextStyle(color: Colors.pink)),
|
Text(widget._entry['authorUser']['username'], style: TextStyle(color: Colors.pink)),
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
Text(DateFormat('kk:mm on MMMM d y').format(createdAt), style: TextStyle(color: Colors.grey, fontSize: 10)),
|
Text(DateFormat('kk:mm on MMMM d y').format(createdAt), style: TextStyle(color: Colors.grey, fontSize: 10)),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
@ -184,7 +179,7 @@ class _NoticeboardPostState extends State<NoticeboardPost> {
|
|||||||
),
|
),
|
||||||
Row(children: [
|
Row(children: [
|
||||||
SizedBox(width: 45),
|
SizedBox(width: 45),
|
||||||
Expanded(child: Text(_entry['content'], textAlign: TextAlign.left))
|
Expanded(child: Text(widget._entry['content'], textAlign: TextAlign.left))
|
||||||
]),
|
]),
|
||||||
_isReplying ? NoticeboardInput(_replyController, _sendReply, _replying, label: 'Reply to this post') : SizedBox(width: 0),
|
_isReplying ? NoticeboardInput(_replyController, _sendReply, _replying, label: 'Reply to this post') : SizedBox(width: 0),
|
||||||
Column(
|
Column(
|
||||||
@ -201,7 +196,7 @@ class NoticeboardInput extends StatelessWidget {
|
|||||||
final Function _onPost;
|
final Function _onPost;
|
||||||
final bool _posting;
|
final bool _posting;
|
||||||
final String label;
|
final String label;
|
||||||
NoticeboardInput(this._controller, this._onPost, this._posting, {this.label = 'Write a new post'}) {}
|
const NoticeboardInput(this._controller, this._onPost, this._posting, {super.key, this.label = 'Write a new post'});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -218,7 +213,7 @@ class NoticeboardInput extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => _onPost!(),
|
onPressed: () => _onPost(),
|
||||||
color: Colors.pink,
|
color: Colors.pink,
|
||||||
icon: _posting ? CircularProgressIndicator() : Icon(Icons.send),
|
icon: _posting ? CircularProgressIndicator() : Icon(Icons.send),
|
||||||
)
|
)
|
||||||
@ -230,13 +225,13 @@ class NoticeboardInput extends StatelessWidget {
|
|||||||
|
|
||||||
class UserChip extends StatelessWidget {
|
class UserChip extends StatelessWidget {
|
||||||
final Map<String,dynamic> user;
|
final Map<String,dynamic> user;
|
||||||
UserChip(this.user) {}
|
const UserChip(this.user, {super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
ImageProvider? avatar = Util.avatarUrl(user);
|
ImageProvider? avatar = Util.avatarUrl(user);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => context.push('/' + user['username']),
|
onTap: () => context.push('/${user["username"]}'),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -251,7 +246,7 @@ class UserChip extends StatelessWidget {
|
|||||||
|
|
||||||
class PatternCard extends StatelessWidget {
|
class PatternCard extends StatelessWidget {
|
||||||
final Map<String,dynamic> object;
|
final Map<String,dynamic> object;
|
||||||
PatternCard(this.object) {}
|
const PatternCard(this.object, {super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -263,7 +258,7 @@ class PatternCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.push('/' + object['projectObject']['owner']['username'] + '/' + object['projectObject']['path'] + '/' + object['_id']);
|
context.push('/${object["projectObject"]["owner"]["username"]}/${object["projectObject"]["path"]}/${object["_id"]}');
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@ -296,7 +291,7 @@ class PatternCard extends StatelessWidget {
|
|||||||
|
|
||||||
class ProjectCard extends StatelessWidget {
|
class ProjectCard extends StatelessWidget {
|
||||||
final Map<String,dynamic> project;
|
final Map<String,dynamic> project;
|
||||||
ProjectCard(this.project) {}
|
const ProjectCard(this.project, {super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -308,7 +303,7 @@ class ProjectCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.push('/' + this.project['owner']['username'] + '/' + this.project['path']);
|
context.push('/${project["owner"]["username"]}/${project["path"]}');
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@ -337,19 +332,20 @@ class CustomText extends StatelessWidget {
|
|||||||
final String text;
|
final String text;
|
||||||
final String type;
|
final String type;
|
||||||
final double margin;
|
final double margin;
|
||||||
TextStyle? style;
|
late final TextStyle style;
|
||||||
CustomText(this.text, this.type, {this.margin = 0}) { }
|
CustomText(this.text, this.type, {super.key, this.margin = 0}) {
|
||||||
|
if (type == 'h1') {
|
||||||
@override
|
style = TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (this.type == 'h1') {
|
|
||||||
style = Theme.of(context).textTheme.titleLarge;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
style = TextStyle();
|
style = TextStyle();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.all(this.margin),
|
margin: EdgeInsets.all(margin),
|
||||||
child: Text(text, style: style)
|
child: Text(text, style: style)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -357,7 +353,7 @@ class CustomText extends StatelessWidget {
|
|||||||
|
|
||||||
class LoginNeeded extends StatelessWidget {
|
class LoginNeeded extends StatelessWidget {
|
||||||
final String? text;
|
final String? text;
|
||||||
LoginNeeded({this.text}) {}
|
const LoginNeeded({super.key, this.text});
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
@ -372,7 +368,7 @@ class LoginNeeded extends StatelessWidget {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.push('/welcome');
|
context.push('/welcome');
|
||||||
},
|
},
|
||||||
child: new Text("Login or register",
|
child: Text("Login or register",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -385,7 +381,7 @@ class EmptyBox extends StatelessWidget {
|
|||||||
final String title;
|
final String title;
|
||||||
final String? description;
|
final String? description;
|
||||||
|
|
||||||
EmptyBox(this.title, {this.description}) {}
|
const EmptyBox(this.title, {super.key, this.description});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@ -18,16 +17,19 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
var data = await api.request('POST', '/accounts/login', {'email': _emailController.text, 'password': _passwordController.text});
|
var data = await api.request('POST', '/accounts/login', {'email': _emailController.text, 'password': _passwordController.text});
|
||||||
setState(() => _loggingIn = false);
|
setState(() => _loggingIn = false);
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
|
if (!context.mounted) return;
|
||||||
AppModel model = Provider.of<AppModel>(context, listen: false);
|
AppModel model = Provider.of<AppModel>(context, listen: false);
|
||||||
await model.setToken(data['payload']['token']);
|
await model.setToken(data['payload']['token']);
|
||||||
|
if (!context.mounted) return;
|
||||||
context.go('/onboarding');
|
context.go('/onboarding');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (!context.mounted) return;
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => CupertinoAlertDialog(
|
builder: (BuildContext context) => CupertinoAlertDialog(
|
||||||
title: new Text("There was a problem logging you in"),
|
title: Text("There was a problem logging you in"),
|
||||||
content: new Text(data['message']),
|
content: Text(data['message']),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
CupertinoDialogAction(
|
CupertinoDialogAction(
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
@ -74,7 +76,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [GestureDetector(
|
children: [GestureDetector(
|
||||||
onTap: () => launch('https://treadl.com/password/forgotten'),
|
onTap: () => launchUrl(Uri.parse('https://treadl.com/password/forgotten')),
|
||||||
child: Text('Forgotten your password?'),
|
child: Text('Forgotten your password?'),
|
||||||
)]
|
)]
|
||||||
),
|
),
|
||||||
@ -93,6 +95,8 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LoginScreen extends StatefulWidget {
|
class LoginScreen extends StatefulWidget {
|
||||||
|
const LoginScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_LoginScreenState createState() => _LoginScreenState();
|
State<LoginScreen> createState() => _LoginScreenState();
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
import 'model.dart';
|
import 'model.dart';
|
||||||
import 'welcome.dart';
|
import 'welcome.dart';
|
||||||
@ -15,7 +14,7 @@ import 'home.dart';
|
|||||||
import 'project.dart';
|
import 'project.dart';
|
||||||
import 'object.dart';
|
import 'object.dart';
|
||||||
import 'settings.dart';
|
import 'settings.dart';
|
||||||
import 'verifyEmail.dart';
|
import 'verify_email.dart';
|
||||||
import 'group.dart';
|
import 'group.dart';
|
||||||
import 'user.dart';
|
import 'user.dart';
|
||||||
import 'account.dart';
|
import 'account.dart';
|
||||||
@ -61,9 +60,11 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatefulWidget {
|
class MyApp extends StatefulWidget {
|
||||||
|
const MyApp({super.key});
|
||||||
|
|
||||||
// Create the initialization Future outside of `build`:
|
// Create the initialization Future outside of `build`:
|
||||||
@override
|
@override
|
||||||
_AppState createState() => _AppState();
|
State<MyApp> createState() => _AppState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppState extends State<MyApp> {
|
class _AppState extends State<MyApp> {
|
||||||
@ -90,17 +91,16 @@ class _AppState extends State<MyApp> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Startup extends StatelessWidget {
|
class Startup extends StatelessWidget {
|
||||||
bool _handled = false;
|
|
||||||
|
|
||||||
Startup() {
|
Startup({super.key}) {
|
||||||
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
/*FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
||||||
if (message.notification != null) {
|
if (message.notification != null) {
|
||||||
print(message.notification!);
|
print(message.notification!);
|
||||||
String text = '';
|
String text = '';
|
||||||
if (message.notification != null && message.notification!.body != null) {
|
if (message.notification != null && message.notification!.body != null) {
|
||||||
text = message.notification!.body!;
|
text = message.notification!.body!;
|
||||||
}
|
}
|
||||||
/*Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: text,
|
msg: text,
|
||||||
toastLength: Toast.LENGTH_LONG,
|
toastLength: Toast.LENGTH_LONG,
|
||||||
gravity: ToastGravity.TOP,
|
gravity: ToastGravity.TOP,
|
||||||
@ -108,21 +108,20 @@ class Startup extends StatelessWidget {
|
|||||||
backgroundColor: Colors.grey[100],
|
backgroundColor: Colors.grey[100],
|
||||||
textColor: Colors.black,
|
textColor: Colors.black,
|
||||||
fontSize: 16.0
|
fontSize: 16.0
|
||||||
);*/
|
);
|
||||||
}
|
}
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkToken(BuildContext context) async {
|
void checkToken(BuildContext context) async {
|
||||||
if (_handled) return;
|
|
||||||
_handled = true;
|
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
String? token = prefs.getString('apiToken');
|
String? token = prefs.getString('apiToken');
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
|
if (!context.mounted) return;
|
||||||
AppModel model = Provider.of<AppModel>(context, listen: false);
|
AppModel model = Provider.of<AppModel>(context, listen: false);
|
||||||
await model.setToken(token!);
|
await model.setToken(token);
|
||||||
FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
|
FirebaseMessaging firebaseMessaging = FirebaseMessaging.instance;
|
||||||
await _firebaseMessaging.requestPermission(
|
await firebaseMessaging.requestPermission(
|
||||||
alert: true,
|
alert: true,
|
||||||
announcement: false,
|
announcement: false,
|
||||||
badge: true,
|
badge: true,
|
||||||
@ -131,13 +130,13 @@ class Startup extends StatelessWidget {
|
|||||||
provisional: false,
|
provisional: false,
|
||||||
sound: true,
|
sound: true,
|
||||||
);
|
);
|
||||||
String? _pushToken = await _firebaseMessaging.getToken();
|
String? pushToken = await firebaseMessaging.getToken();
|
||||||
if (_pushToken != null) {
|
if (pushToken != null) {
|
||||||
print("sending push");
|
|
||||||
Api api = Api();
|
Api api = Api();
|
||||||
api.request('PUT', '/accounts/pushToken', {'pushToken': _pushToken!});
|
api.request('PUT', '/accounts/pushToken', {'pushToken': pushToken});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!context.mounted) return;
|
||||||
context.go('/home');
|
context.go('/home');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ class User {
|
|||||||
String? avatarUrl;
|
String? avatarUrl;
|
||||||
bool? emailVerified;
|
bool? emailVerified;
|
||||||
|
|
||||||
User(this.id, this.username, {this.avatar, this.avatarUrl}) {}
|
User(this.id, this.username, {this.avatar, this.avatarUrl});
|
||||||
|
|
||||||
static User loadJSON(Map<String,dynamic> input) {
|
static User loadJSON(Map<String,dynamic> input) {
|
||||||
User newUser = User(input['_id'], input['username'], avatar: input['avatar'], avatarUrl: input['avatarUrl']);
|
User newUser = User(input['_id'], input['username'], avatar: input['avatar'], avatarUrl: input['avatarUrl']);
|
||||||
@ -58,7 +58,6 @@ class AppModel extends ChangeNotifier {
|
|||||||
var data = await api.request('GET', '/users/me');
|
var data = await api.request('GET', '/users/me');
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
setUser(User.loadJSON(data['payload']));
|
setUser(User.loadJSON(data['payload']));
|
||||||
print(data);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
prefs.remove('apiToken');
|
prefs.remove('apiToken');
|
||||||
|
@ -2,26 +2,19 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:flutter_html/flutter_html.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
import 'util.dart';
|
import 'util.dart';
|
||||||
import 'model.dart';
|
import 'model.dart';
|
||||||
import 'patterns/pattern.dart';
|
|
||||||
import 'patterns/viewer.dart';
|
import 'patterns/viewer.dart';
|
||||||
|
|
||||||
class _ObjectScreenState extends State<ObjectScreen> {
|
class _ObjectScreenState extends State<ObjectScreen> {
|
||||||
final String username;
|
|
||||||
final String projectPath;
|
|
||||||
final String id;
|
|
||||||
Map<String,dynamic>? object;
|
Map<String,dynamic>? object;
|
||||||
Map<String,dynamic>? pattern;
|
Map<String,dynamic>? pattern;
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
final Api api = Api();
|
final Api api = Api();
|
||||||
|
|
||||||
_ObjectScreenState(this.username, this.projectPath, this.id) { }
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -29,7 +22,7 @@ class _ObjectScreenState extends State<ObjectScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fetchObject() async {
|
void fetchObject() async {
|
||||||
var data = await api.request('GET', '/objects/' + id);
|
var data = await api.request('GET', '/objects/${widget.id}');
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
setState(() {
|
setState(() {
|
||||||
object = data['payload'];
|
object = data['payload'];
|
||||||
@ -42,7 +35,7 @@ class _ObjectScreenState extends State<ObjectScreen> {
|
|||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
File? file;
|
File? file;
|
||||||
if (object!['type'] == 'pattern') {
|
if (object!['type'] == 'pattern') {
|
||||||
var data = await api.request('GET', '/objects/' + id + '/wif');
|
var data = await api.request('GET', '/objects/${widget.id}/wif');
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
file = await Util.writeFile(object!['name'] + '.wif', data['payload']['wif']);
|
file = await Util.writeFile(object!['name'] + '.wif', data['payload']['wif']);
|
||||||
}
|
}
|
||||||
@ -53,14 +46,15 @@ class _ObjectScreenState extends State<ObjectScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
Util.shareFile(file!, withDelete: true);
|
Util.shareFile(file, withDelete: true);
|
||||||
}
|
}
|
||||||
setState(() => _isLoading = false);
|
setState(() => _isLoading = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deleteObject(BuildContext context, BuildContext modalContext) async {
|
void _deleteObject(BuildContext context, BuildContext modalContext) async {
|
||||||
var data = await api.request('DELETE', '/objects/' + id);
|
var data = await api.request('DELETE', '/objects/${widget.id}');
|
||||||
if (data['success']) {
|
if (data['success']) {
|
||||||
|
if (!context.mounted) return;
|
||||||
context.go('/home');
|
context.go('/home');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,8 +63,8 @@ class _ObjectScreenState extends State<ObjectScreen> {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: modalContext,
|
context: modalContext,
|
||||||
builder: (BuildContext context) => CupertinoAlertDialog(
|
builder: (BuildContext context) => CupertinoAlertDialog(
|
||||||
title: new Text('Really delete this item?'),
|
title: Text('Really delete this item?'),
|
||||||
content: new Text('This action cannot be undone.'),
|
content: Text('This action cannot be undone.'),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
CupertinoDialogAction(
|
CupertinoDialogAction(
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
@ -109,7 +103,8 @@ class _ObjectScreenState extends State<ObjectScreen> {
|
|||||||
TextButton(
|
TextButton(
|
||||||
child: Text('OK'),
|
child: Text('OK'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
var data = await api.request('PUT', '/objects/' + id, {'name': renameController.text});
|
var data = await api.request('PUT', '/objects/${widget.id}', {'name': renameController.text});
|
||||||
|
if (!context.mounted) return;
|
||||||
if (data['success']) {
|
if (data['success']) {
|
||||||
context.pop();
|
context.pop();
|
||||||
object!['name'] = data['payload']['name'];
|
object!['name'] = data['payload']['name'];
|
||||||
@ -143,8 +138,8 @@ class _ObjectScreenState extends State<ObjectScreen> {
|
|||||||
),
|
),
|
||||||
CupertinoActionSheetAction(
|
CupertinoActionSheetAction(
|
||||||
onPressed: () => _confirmDeleteObject(modalContext),
|
onPressed: () => _confirmDeleteObject(modalContext),
|
||||||
child: Text('Delete item'),
|
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
|
child: Text('Delete item'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@ -160,7 +155,6 @@ class _ObjectScreenState extends State<ObjectScreen> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
else if (object!['isImage'] == true && object!['url'] != null) {
|
else if (object!['isImage'] == true && object!['url'] != null) {
|
||||||
print(object!['url']);
|
|
||||||
return Image.network(object!['url']);
|
return Image.network(object!['url']);
|
||||||
}
|
}
|
||||||
else if (object!['type'] == 'pattern') {
|
else if (object!['type'] == 'pattern') {
|
||||||
@ -168,7 +162,7 @@ class _ObjectScreenState extends State<ObjectScreen> {
|
|||||||
return PatternViewer(pattern!, withEditor: true);
|
return PatternViewer(pattern!, withEditor: true);
|
||||||
}
|
}
|
||||||
else if (object!['previewUrl'] != null) {
|
else if (object!['previewUrl'] != null) {
|
||||||
return Image.network(object!['previewUrl']!);;
|
return Image.network(object!['previewUrl']!);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return Column(
|
return Column(
|
||||||
@ -188,7 +182,7 @@ class _ObjectScreenState extends State<ObjectScreen> {
|
|||||||
Text('Treadl cannot display this type of item.'),
|
Text('Treadl cannot display this type of item.'),
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
ElevatedButton(child: Text('View file'), onPressed: () {
|
ElevatedButton(child: Text('View file'), onPressed: () {
|
||||||
launch(object!['url']);
|
launchUrl(object!['url']);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
@ -199,9 +193,6 @@ class _ObjectScreenState extends State<ObjectScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
AppModel model = Provider.of<AppModel>(context);
|
AppModel model = Provider.of<AppModel>(context);
|
||||||
User? user = model.user;
|
User? user = model.user;
|
||||||
String description = '';
|
|
||||||
if (object?['description'] != null)
|
|
||||||
description = object!['description']!;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(object?['name'] ?? 'Object'),
|
title: Text(object?['name'] ?? 'Object'),
|
||||||
@ -237,8 +228,8 @@ class ObjectScreen extends StatefulWidget {
|
|||||||
final String username;
|
final String username;
|
||||||
final String projectPath;
|
final String projectPath;
|
||||||
final String id;
|
final String id;
|
||||||
ObjectScreen(this.username, this.projectPath, this.id, ) { }
|
const ObjectScreen(this.username, this.projectPath, this.id, {super.key});
|
||||||
@override
|
@override
|
||||||
_ObjectScreenState createState() => _ObjectScreenState(username, projectPath, id);
|
State<ObjectScreen> createState() => _ObjectScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
@ -22,8 +20,8 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
|
|||||||
void _requestPushPermissions() async {
|
void _requestPushPermissions() async {
|
||||||
try {
|
try {
|
||||||
setState(() => _loading = true);
|
setState(() => _loading = true);
|
||||||
FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
|
FirebaseMessaging firebaseMessaging = FirebaseMessaging.instance;
|
||||||
await _firebaseMessaging.requestPermission(
|
await firebaseMessaging.requestPermission(
|
||||||
alert: true,
|
alert: true,
|
||||||
announcement: false,
|
announcement: false,
|
||||||
badge: true,
|
badge: true,
|
||||||
@ -32,12 +30,11 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
|
|||||||
provisional: false,
|
provisional: false,
|
||||||
sound: true,
|
sound: true,
|
||||||
);
|
);
|
||||||
_pushToken = await _firebaseMessaging.getToken();
|
_pushToken = await firebaseMessaging.getToken();
|
||||||
if (_pushToken != null) {
|
if (_pushToken != null) {
|
||||||
api.request('PUT', '/accounts/pushToken', {'pushToken': _pushToken!});
|
api.request('PUT', '/accounts/pushToken', {'pushToken': _pushToken!});
|
||||||
}
|
}
|
||||||
}
|
} catch (_) { }
|
||||||
on Exception { }
|
|
||||||
setState(() => _loading = false);
|
setState(() => _loading = false);
|
||||||
_controller.animateToPage(2, duration: Duration(milliseconds: 500), curve: Curves.easeInOut);
|
_controller.animateToPage(2, duration: Duration(milliseconds: 500), curve: Curves.easeInOut);
|
||||||
}
|
}
|
||||||
@ -85,12 +82,12 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
|
|||||||
Text('We recommend enabling push notifications so you can keep up-to-date with your groups and projects.', style: TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
|
Text('We recommend enabling push notifications so you can keep up-to-date with your groups and projects.', style: TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
|
onPressed: _requestPushPermissions,
|
||||||
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||||
_loading ? CircularProgressIndicator() : SizedBox(width: 0),
|
_loading ? CircularProgressIndicator() : SizedBox(width: 0),
|
||||||
_loading ? SizedBox(width: 10) : SizedBox(width: 0),
|
_loading ? SizedBox(width: 10) : SizedBox(width: 0),
|
||||||
Text('Continue', style: TextStyle(color: Colors.pink)),
|
Text('Continue', style: TextStyle(color: Colors.pink)),
|
||||||
]),
|
]),
|
||||||
onPressed: _requestPushPermissions,
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -121,6 +118,8 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class OnboardingScreen extends StatefulWidget {
|
class OnboardingScreen extends StatefulWidget {
|
||||||
|
const OnboardingScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_OnboardingScreenState createState() => _OnboardingScreenState();
|
State<OnboardingScreen> createState() => _OnboardingScreenState();
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,10 @@ import '../util.dart';
|
|||||||
|
|
||||||
class DrawdownPainter extends CustomPainter {
|
class DrawdownPainter extends CustomPainter {
|
||||||
final Map<String,dynamic> pattern;
|
final Map<String,dynamic> pattern;
|
||||||
final double BASE_SIZE;
|
final double baseSize;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DrawdownPainter(this.BASE_SIZE, this.pattern) {}
|
DrawdownPainter(this.baseSize, this.pattern);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
@ -20,10 +20,10 @@ class DrawdownPainter extends CustomPainter {
|
|||||||
..strokeWidth = 1;
|
..strokeWidth = 1;
|
||||||
|
|
||||||
// Draw grid
|
// Draw grid
|
||||||
for (double i = 0; i <= size.width; i += BASE_SIZE) {
|
for (double i = 0; i <= size.width; i += baseSize) {
|
||||||
canvas.drawLine(Offset(i.toDouble(), size.height), Offset(i.toDouble(), 0), paint);
|
canvas.drawLine(Offset(i.toDouble(), size.height), Offset(i.toDouble(), 0), paint);
|
||||||
}
|
}
|
||||||
for (double y = 0; y <= size.height; y += BASE_SIZE) {
|
for (double y = 0; y <= size.height; y += baseSize) {
|
||||||
canvas.drawLine(Offset(0, y.toDouble()), Offset(size.width, y.toDouble()), paint);
|
canvas.drawLine(Offset(0, y.toDouble()), Offset(size.width, y.toDouble()), paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,9 +42,9 @@ class DrawdownPainter extends CustomPainter {
|
|||||||
String threadType = filteredTieup.contains(shaft) ? 'warp' : 'weft';
|
String threadType = filteredTieup.contains(shaft) ? 'warp' : 'weft';
|
||||||
|
|
||||||
Rect rect = Offset(
|
Rect rect = Offset(
|
||||||
size.width - BASE_SIZE * (thread + 1),
|
size.width - baseSize * (thread + 1),
|
||||||
tread * BASE_SIZE
|
tread * baseSize
|
||||||
) & Size(BASE_SIZE, BASE_SIZE);
|
) & Size(baseSize, baseSize);
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
rect,
|
rect,
|
||||||
Paint()
|
Paint()
|
||||||
|
@ -7,40 +7,40 @@ import 'drawdown.dart';
|
|||||||
class Pattern extends StatelessWidget {
|
class Pattern extends StatelessWidget {
|
||||||
final Map<String,dynamic> pattern;
|
final Map<String,dynamic> pattern;
|
||||||
final Function? onUpdate;
|
final Function? onUpdate;
|
||||||
final double BASE_SIZE = 5;
|
final double baseSize = 5;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Pattern(this.pattern, {this.onUpdate}) {}
|
const Pattern(this.pattern, {super.key, this.onUpdate});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var warp = pattern['warp'];
|
var warp = pattern['warp'];
|
||||||
var weft = pattern['weft'];
|
var weft = pattern['weft'];
|
||||||
|
|
||||||
double draftWidth = warp['threading']?.length * BASE_SIZE + weft['treadles'] * BASE_SIZE + BASE_SIZE;
|
double draftWidth = warp['threading']?.length * baseSize + weft['treadles'] * baseSize + baseSize;
|
||||||
double draftHeight = warp['shafts'] * BASE_SIZE + weft['treadling']?.length * BASE_SIZE + BASE_SIZE;
|
double draftHeight = warp['shafts'] * baseSize + weft['treadling']?.length * baseSize + baseSize;
|
||||||
|
|
||||||
double tieupTop = BASE_SIZE;
|
double tieupTop = baseSize;
|
||||||
double tieupRight = BASE_SIZE;
|
double tieupRight = baseSize;
|
||||||
double tieupWidth = weft['treadles'] * BASE_SIZE;
|
double tieupWidth = weft['treadles'] * baseSize;
|
||||||
double tieupHeight = warp['shafts'] * BASE_SIZE;
|
double tieupHeight = warp['shafts'] * baseSize;
|
||||||
|
|
||||||
double warpTop = 0;
|
double warpTop = 0;
|
||||||
double warpRight = weft['treadles'] * BASE_SIZE + BASE_SIZE * 2;
|
double warpRight = weft['treadles'] * baseSize + baseSize * 2;
|
||||||
double warpWidth = warp['threading']?.length * BASE_SIZE;
|
double warpWidth = warp['threading']?.length * baseSize;
|
||||||
double warpHeight = warp['shafts'] * BASE_SIZE + BASE_SIZE;
|
double warpHeight = warp['shafts'] * baseSize + baseSize;
|
||||||
|
|
||||||
double weftRight = 0;
|
double weftRight = 0;
|
||||||
double weftTop = warp['shafts'] * BASE_SIZE + BASE_SIZE * 2;
|
double weftTop = warp['shafts'] * baseSize + baseSize * 2;
|
||||||
double weftWidth = weft['treadles'] * BASE_SIZE + BASE_SIZE;
|
double weftWidth = weft['treadles'] * baseSize + baseSize;
|
||||||
double weftHeight = weft['treadling'].length * BASE_SIZE;
|
double weftHeight = weft['treadling'].length * baseSize;
|
||||||
|
|
||||||
double drawdownTop = warpHeight + BASE_SIZE;
|
double drawdownTop = warpHeight + baseSize;
|
||||||
double drawdownRight = weftWidth + BASE_SIZE;
|
double drawdownRight = weftWidth + baseSize;
|
||||||
double drawdownWidth = warpWidth;
|
double drawdownWidth = warpWidth;
|
||||||
double drawdownHeight = weftHeight;
|
double drawdownHeight = weftHeight;
|
||||||
|
|
||||||
return Container(
|
return SizedBox(
|
||||||
width: draftWidth,
|
width: draftWidth,
|
||||||
height: draftHeight,
|
height: draftHeight,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@ -53,8 +53,8 @@ class Pattern extends StatelessWidget {
|
|||||||
var tieups = pattern['tieups'];
|
var tieups = pattern['tieups'];
|
||||||
double dx = details.localPosition.dx;
|
double dx = details.localPosition.dx;
|
||||||
double dy = details.localPosition.dy;
|
double dy = details.localPosition.dy;
|
||||||
int tie = (dx / BASE_SIZE).toInt();
|
int tie = (dx / baseSize).toInt();
|
||||||
int shaft = ((tieupHeight - dy) / BASE_SIZE).toInt() + 1;
|
int shaft = ((tieupHeight - dy) / baseSize).toInt() + 1;
|
||||||
if (tieups[tie].contains(shaft)) {
|
if (tieups[tie].contains(shaft)) {
|
||||||
tieups[tie].remove(shaft);
|
tieups[tie].remove(shaft);
|
||||||
} else {
|
} else {
|
||||||
@ -67,7 +67,7 @@ class Pattern extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
size: Size(tieupWidth, tieupHeight),
|
size: Size(tieupWidth, tieupHeight),
|
||||||
painter: TieupPainter(BASE_SIZE, this.pattern),
|
painter: TieupPainter(baseSize, pattern),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@ -75,7 +75,7 @@ class Pattern extends StatelessWidget {
|
|||||||
top: warpTop,
|
top: warpTop,
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
size: Size(warpWidth, warpHeight),
|
size: Size(warpWidth, warpHeight),
|
||||||
painter: WarpPainter(BASE_SIZE, this.pattern),
|
painter: WarpPainter(baseSize, pattern),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@ -83,7 +83,7 @@ class Pattern extends StatelessWidget {
|
|||||||
top: weftTop,
|
top: weftTop,
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
size: Size(weftWidth, weftHeight),
|
size: Size(weftWidth, weftHeight),
|
||||||
painter: WeftPainter(BASE_SIZE, this.pattern),
|
painter: WeftPainter(baseSize, pattern),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@ -91,7 +91,7 @@ class Pattern extends StatelessWidget {
|
|||||||
top: drawdownTop,
|
top: drawdownTop,
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
size: Size(drawdownWidth, drawdownHeight),
|
size: Size(drawdownWidth, drawdownHeight),
|
||||||
painter: DrawdownPainter(BASE_SIZE, this.pattern),
|
painter: DrawdownPainter(baseSize, pattern),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
class TieupPainter extends CustomPainter {
|
class TieupPainter extends CustomPainter {
|
||||||
final Map<String,dynamic> pattern;
|
final Map<String,dynamic> pattern;
|
||||||
final double BASE_SIZE;
|
final double baseSize;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TieupPainter(this.BASE_SIZE, this.pattern) {}
|
TieupPainter(this.baseSize, this.pattern);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
@ -15,20 +15,20 @@ class TieupPainter extends CustomPainter {
|
|||||||
..color = Colors.black..strokeWidth = 0.5;
|
..color = Colors.black..strokeWidth = 0.5;
|
||||||
|
|
||||||
// Draw grid
|
// Draw grid
|
||||||
for (double i = 0; i <= size.width; i += BASE_SIZE) {
|
for (double i = 0; i <= size.width; i += baseSize) {
|
||||||
canvas.drawLine(Offset(i.toDouble(), size.height), Offset(i.toDouble(), 0), paint);
|
canvas.drawLine(Offset(i.toDouble(), size.height), Offset(i.toDouble(), 0), paint);
|
||||||
}
|
}
|
||||||
for (double y = 0; y <= size.height; y += BASE_SIZE) {
|
for (double y = 0; y <= size.height; y += baseSize) {
|
||||||
canvas.drawLine(Offset(0, y.toDouble()), Offset(size.width, y.toDouble()), paint);
|
canvas.drawLine(Offset(0, y.toDouble()), Offset(size.width, y.toDouble()), paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < tieup.length; i++) {
|
for (var i = 0; i < tieup.length; i++) {
|
||||||
List<dynamic>? tie = tieup[i];
|
List<dynamic>? tie = tieup[i];
|
||||||
if (tie != null) {
|
if (tie != null) {
|
||||||
for (var j = 0; j < tie!.length; j++) {
|
for (var j = 0; j < tie.length; j++) {
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
Offset(i.toDouble()*BASE_SIZE, size.height - (tie[j]*BASE_SIZE)) &
|
Offset(i.toDouble()*baseSize, size.height - (tie[j]*baseSize)) &
|
||||||
Size(BASE_SIZE.toDouble(), BASE_SIZE.toDouble()),
|
Size(baseSize.toDouble(), baseSize.toDouble()),
|
||||||
paint);
|
paint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,33 +4,29 @@ import 'pattern.dart';
|
|||||||
class PatternViewer extends StatefulWidget {
|
class PatternViewer extends StatefulWidget {
|
||||||
final Map<String,dynamic> pattern;
|
final Map<String,dynamic> pattern;
|
||||||
final bool withEditor;
|
final bool withEditor;
|
||||||
PatternViewer(this.pattern, {this.withEditor = false}) {}
|
const PatternViewer(this.pattern, {super.key, this.withEditor = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PatternViewer> createState() => _PatternViewerState(this.pattern, this.withEditor);
|
State<PatternViewer> createState() => _PatternViewerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PatternViewerState extends State<PatternViewer> {
|
class _PatternViewerState extends State<PatternViewer> {
|
||||||
Map<String,dynamic> pattern;
|
|
||||||
final bool withEditor;
|
|
||||||
bool controllerInitialised = false;
|
bool controllerInitialised = false;
|
||||||
final controller = TransformationController();
|
final controller = TransformationController();
|
||||||
final double BASE_SIZE = 5;
|
final double baseSize = 5;
|
||||||
|
|
||||||
_PatternViewerState(this.pattern, this.withEditor) {}
|
|
||||||
|
|
||||||
void updatePattern(update) {
|
void updatePattern(update) {
|
||||||
setState(() {
|
setState(() {
|
||||||
pattern!.addAll(update);
|
widget.pattern.addAll(update);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (!controllerInitialised) {
|
if (!controllerInitialised) {
|
||||||
var warp = pattern['warp'];
|
var warp = widget.pattern['warp'];
|
||||||
var weft = pattern['weft'];
|
var weft = widget.pattern['weft'];
|
||||||
double draftWidth = warp['threading']?.length * BASE_SIZE + weft['treadles'] * BASE_SIZE + BASE_SIZE;
|
double draftWidth = warp['threading']?.length * baseSize + weft['treadles'] * baseSize + baseSize;
|
||||||
final zoomFactor = 1.0;
|
final zoomFactor = 1.0;
|
||||||
final xTranslate = draftWidth - MediaQuery.of(context).size.width - 0;
|
final xTranslate = draftWidth - MediaQuery.of(context).size.width - 0;
|
||||||
final yTranslate = 0.0;
|
final yTranslate = 0.0;
|
||||||
@ -47,7 +43,7 @@ class _PatternViewerState extends State<PatternViewer> {
|
|||||||
maxScale: 5,
|
maxScale: 5,
|
||||||
constrained: false,
|
constrained: false,
|
||||||
transformationController: controller,
|
transformationController: controller,
|
||||||
child: RepaintBoundary(child: Pattern(pattern))
|
child: RepaintBoundary(child: Pattern(widget.pattern))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@ import '../util.dart';
|
|||||||
|
|
||||||
class WarpPainter extends CustomPainter {
|
class WarpPainter extends CustomPainter {
|
||||||
final Map<String,dynamic> pattern;
|
final Map<String,dynamic> pattern;
|
||||||
final double BASE_SIZE;
|
final double baseSize;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
WarpPainter(this.BASE_SIZE, this.pattern) {}
|
WarpPainter(this.baseSize, this.pattern);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
@ -15,17 +15,12 @@ class WarpPainter extends CustomPainter {
|
|||||||
var paint = Paint()
|
var paint = Paint()
|
||||||
..color = Colors.black
|
..color = Colors.black
|
||||||
..strokeWidth = 0.5;
|
..strokeWidth = 0.5;
|
||||||
var thickPaint = Paint()
|
|
||||||
..color = Colors.black
|
|
||||||
..strokeWidth = 1.5;
|
|
||||||
|
|
||||||
// Draw grid
|
// Draw grid
|
||||||
int columnsPainted = 0;
|
for (double i = size.width; i >= 0; i -= baseSize) {
|
||||||
for (double i = size.width; i >= 0; i -= BASE_SIZE) {
|
|
||||||
canvas.drawLine(Offset(i.toDouble(), size.height), Offset(i.toDouble(), 0), paint);
|
canvas.drawLine(Offset(i.toDouble(), size.height), Offset(i.toDouble(), 0), paint);
|
||||||
columnsPainted += 1;
|
|
||||||
}
|
}
|
||||||
for (double y = 0; y <= size.height; y += BASE_SIZE) {
|
for (double y = 0; y <= size.height; y += baseSize) {
|
||||||
canvas.drawLine(Offset(0, y.toDouble()), Offset(size.width, y.toDouble()), paint);
|
canvas.drawLine(Offset(0, y.toDouble()), Offset(size.width, y.toDouble()), paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,12 +29,12 @@ class WarpPainter extends CustomPainter {
|
|||||||
var thread = warp['threading'][i];
|
var thread = warp['threading'][i];
|
||||||
int? shaft = thread?['shaft'];
|
int? shaft = thread?['shaft'];
|
||||||
String? colour = warp['defaultColour'];
|
String? colour = warp['defaultColour'];
|
||||||
double x = size.width - (i+1)*BASE_SIZE;
|
double x = size.width - (i+1)*baseSize;
|
||||||
if (shaft != null) {
|
if (shaft != null) {
|
||||||
if (shaft! > 0) {
|
if (shaft > 0) {
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
Offset(x, size.height - shaft!*BASE_SIZE) &
|
Offset(x, size.height - shaft*baseSize) &
|
||||||
Size(BASE_SIZE.toDouble(), BASE_SIZE.toDouble()),
|
Size(baseSize.toDouble(), baseSize.toDouble()),
|
||||||
paint
|
paint
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -51,9 +46,9 @@ class WarpPainter extends CustomPainter {
|
|||||||
if (colour != null) {
|
if (colour != null) {
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
Offset(x, 0) &
|
Offset(x, 0) &
|
||||||
Size(BASE_SIZE.toDouble(), BASE_SIZE.toDouble()),
|
Size(baseSize.toDouble(), baseSize.toDouble()),
|
||||||
Paint()
|
Paint()
|
||||||
..color = Util.rgb(colour!)
|
..color = Util.rgb(colour)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@ import '../util.dart';
|
|||||||
|
|
||||||
class WeftPainter extends CustomPainter {
|
class WeftPainter extends CustomPainter {
|
||||||
final Map<String,dynamic> pattern;
|
final Map<String,dynamic> pattern;
|
||||||
final double BASE_SIZE;
|
final double baseSize;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
WeftPainter(this.BASE_SIZE, this.pattern) {}
|
WeftPainter(this.baseSize, this.pattern);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
@ -15,29 +15,24 @@ class WeftPainter extends CustomPainter {
|
|||||||
var paint = Paint()
|
var paint = Paint()
|
||||||
..color = Colors.black
|
..color = Colors.black
|
||||||
..strokeWidth = 0.5;
|
..strokeWidth = 0.5;
|
||||||
var thickPaint = Paint()
|
|
||||||
..color = Colors.black
|
|
||||||
..strokeWidth = 1.5;
|
|
||||||
|
|
||||||
// Draw grid
|
// Draw grid
|
||||||
int rowsPainted = 0;
|
for (double i = 0; i <= size.width; i += baseSize) {
|
||||||
for (double i = 0; i <= size.width; i += BASE_SIZE) {
|
|
||||||
canvas.drawLine(Offset(i.toDouble(), size.height), Offset(i.toDouble(), 0), paint);
|
canvas.drawLine(Offset(i.toDouble(), size.height), Offset(i.toDouble(), 0), paint);
|
||||||
}
|
}
|
||||||
for (double y = 0; y <= size.height; y += BASE_SIZE) {
|
for (double y = 0; y <= size.height; y += baseSize) {
|
||||||
canvas.drawLine(Offset(0, y.toDouble()), Offset(size.width, y.toDouble()), paint);
|
canvas.drawLine(Offset(0, y.toDouble()), Offset(size.width, y.toDouble()), paint);
|
||||||
rowsPainted += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < weft['treadling'].length; i++) {
|
for (var i = 0; i < weft['treadling'].length; i++) {
|
||||||
var thread = weft['treadling'][i];
|
var thread = weft['treadling'][i];
|
||||||
int? treadle = thread?['treadle'];
|
int? treadle = thread?['treadle'];
|
||||||
String? colour = weft['defaultColour'];
|
String? colour = weft['defaultColour'];
|
||||||
double y = i.toDouble()*BASE_SIZE;
|
double y = i.toDouble()*baseSize;
|
||||||
if (treadle != null && treadle! > 0) {
|
if (treadle != null && treadle > 0) {
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
Offset((treadle!.toDouble()-1)*BASE_SIZE, y) &
|
Offset((treadle.toDouble()-1)*baseSize, y) &
|
||||||
Size(BASE_SIZE.toDouble(), BASE_SIZE.toDouble()),
|
Size(baseSize.toDouble(), baseSize.toDouble()),
|
||||||
paint
|
paint
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -46,10 +41,10 @@ class WeftPainter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
if (colour != null) {
|
if (colour != null) {
|
||||||
canvas.drawRect(
|
canvas.drawRect(
|
||||||
Offset(size.width - BASE_SIZE, y) &
|
Offset(size.width - baseSize, y) &
|
||||||
Size(BASE_SIZE.toDouble(), BASE_SIZE.toDouble()),
|
Size(baseSize.toDouble(), baseSize.toDouble()),
|
||||||
Paint()
|
Paint()
|
||||||
..color = Util.rgb(colour!)
|
..color = Util.rgb(colour)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,7 @@ import 'model.dart';
|
|||||||
import 'lib.dart';
|
import 'lib.dart';
|
||||||
|
|
||||||
class _ProjectScreenState extends State<ProjectScreen> {
|
class _ProjectScreenState extends State<ProjectScreen> {
|
||||||
final String username;
|
late final String fullPath;
|
||||||
final String projectPath;
|
|
||||||
final String fullPath;
|
|
||||||
final picker = ImagePicker();
|
final picker = ImagePicker();
|
||||||
final Api api = Api();
|
final Api api = Api();
|
||||||
Map<String,dynamic>? project;
|
Map<String,dynamic>? project;
|
||||||
@ -24,19 +22,17 @@ class _ProjectScreenState extends State<ProjectScreen> {
|
|||||||
bool _loading = false;
|
bool _loading = false;
|
||||||
Map<String,dynamic>? _creatingObject;
|
Map<String,dynamic>? _creatingObject;
|
||||||
|
|
||||||
_ProjectScreenState(this.username, this.projectPath, {this.project}) :
|
|
||||||
fullPath = username + '/' + projectPath;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
fullPath = '${widget.username}/${widget.projectPath}';
|
||||||
getProject(fullPath);
|
getProject(fullPath);
|
||||||
getObjects(fullPath);
|
getObjects(fullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getProject(String fullName) async {
|
void getProject(String fullName) async {
|
||||||
setState(() => _loading = true);
|
setState(() => _loading = true);
|
||||||
var data = await api.request('GET', '/projects/' + fullName);
|
var data = await api.request('GET', '/projects/$fullName');
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
setState(() {
|
setState(() {
|
||||||
project = data['payload'];
|
project = data['payload'];
|
||||||
@ -47,7 +43,7 @@ class _ProjectScreenState extends State<ProjectScreen> {
|
|||||||
|
|
||||||
void getObjects(String fullName) async {
|
void getObjects(String fullName) async {
|
||||||
setState(() => _loading = true);
|
setState(() => _loading = true);
|
||||||
var data = await api.request('GET', '/projects/' + fullName + '/objects');
|
var data = await api.request('GET', '/projects/$fullName/objects');
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_objects = data['payload']['objects'];
|
_objects = data['payload']['objects'];
|
||||||
@ -124,7 +120,7 @@ class _ProjectScreenState extends State<ProjectScreen> {
|
|||||||
PlatformFile file = result.files.single;
|
PlatformFile file = result.files.single;
|
||||||
XFile xFile = XFile(file.path!);
|
XFile xFile = XFile(file.path!);
|
||||||
String? ext = file.extension;
|
String? ext = file.extension;
|
||||||
if (ext != null && ext!.toLowerCase() == 'wif' || xFile.name.toLowerCase().contains('.wif')) {
|
if (ext != null && ext.toLowerCase() == 'wif' || xFile.name.toLowerCase().contains('.wif')) {
|
||||||
final String contents = await xFile.readAsString();
|
final String contents = await xFile.readAsString();
|
||||||
_createObjectFromWif(file.name, contents);
|
_createObjectFromWif(file.name, contents);
|
||||||
} else {
|
} else {
|
||||||
@ -134,16 +130,16 @@ class _ProjectScreenState extends State<ProjectScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _chooseImage() async {
|
void _chooseImage() async {
|
||||||
File file;
|
|
||||||
try {
|
try {
|
||||||
final XFile? imageFile = await picker.pickImage(source: ImageSource.gallery);
|
final XFile? imageFile = await picker.pickImage(source: ImageSource.gallery);
|
||||||
if (imageFile == null) return;
|
if (imageFile == null) return;
|
||||||
final f = new DateFormat('yyyy-MM-dd_hh-mm-ss');
|
final f = DateFormat('yyyy-MM-dd_hh-mm-ss');
|
||||||
String time = f.format(new DateTime.now());
|
String time = f.format(DateTime.now());
|
||||||
String name = project!['name'] + ' ' + time + '.' + imageFile.name.split('.').last;
|
String name = '${project!["name"]} $time.${imageFile.name.split(".").last}';
|
||||||
_createObjectFromFile(name, imageFile);
|
_createObjectFromFile(name, imageFile);
|
||||||
}
|
}
|
||||||
on Exception {
|
on Exception {
|
||||||
|
if (!mounted) return;
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => CupertinoAlertDialog(
|
builder: (BuildContext context) => CupertinoAlertDialog(
|
||||||
@ -162,30 +158,30 @@ class _ProjectScreenState extends State<ProjectScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void showSettingsModal() {
|
void showSettingsModal() {
|
||||||
Widget settingsDialog = new _ProjectSettingsDialog(project!, _onDeleteProject, _onUpdateProject);
|
Widget settingsDialog = _ProjectSettingsDialog(project!, _onDeleteProject, _onUpdateProject);
|
||||||
showCupertinoModalPopup(context: context, builder: (BuildContext context) => settingsDialog);
|
showCupertinoModalPopup(context: context, builder: (BuildContext context) => settingsDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getNetworkImageBox(String url) {
|
Widget getNetworkImageBox(String url) {
|
||||||
return new AspectRatio(
|
return AspectRatio(
|
||||||
aspectRatio: 1 / 1,
|
aspectRatio: 1 / 1,
|
||||||
child: new Container(
|
child: Container(
|
||||||
decoration: new BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
image: new DecorationImage(
|
image: DecorationImage(
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
alignment: FractionalOffset.topCenter,
|
alignment: FractionalOffset.topCenter,
|
||||||
image: new NetworkImage(url),
|
image: NetworkImage(url),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Widget getIconBox(Icon icon) {
|
Widget getIconBox(Icon icon) {
|
||||||
return new AspectRatio(
|
return AspectRatio(
|
||||||
aspectRatio: 1 / 1,
|
aspectRatio: 1 / 1,
|
||||||
child: new Container(
|
child: Container(
|
||||||
decoration: new BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.grey[100],
|
color: Colors.grey[100],
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
),
|
),
|
||||||
@ -236,10 +232,10 @@ class _ProjectScreenState extends State<ProjectScreen> {
|
|||||||
leader = CircularProgressIndicator();
|
leader = CircularProgressIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Card(
|
return Card(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.push('/' + username + '/' + projectPath + '/' + object['_id']);
|
context.push('/${widget.username}/${widget.projectPath}/${object["_id"]}');
|
||||||
},
|
},
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: leader,
|
leading: leader,
|
||||||
@ -252,17 +248,18 @@ class _ProjectScreenState extends State<ProjectScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget getBody() {
|
Widget getBody() {
|
||||||
if (_loading || project == null)
|
if (_loading || project == null) {
|
||||||
return CircularProgressIndicator();
|
return CircularProgressIndicator();
|
||||||
else if ((_objects != null && _objects.length > 0) || _creatingObject != null)
|
} else if ((_objects.isNotEmpty) || _creatingObject != null) {
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: _objects.length + (_creatingObject != null ? 1 : 0),
|
itemCount: _objects.length + (_creatingObject != null ? 1 : 0),
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return getObjectCard(index);
|
return getObjectCard(index);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
else
|
} else {
|
||||||
return EmptyBox('This project is currently empty', description: 'If this is your project, you can add a pattern file, an image, or something else to this project using the + button below.');
|
return EmptyBox('This project is currently empty', description: 'If this is your project, you can add a pattern file, an image, or something else to this project using the + button below.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -329,8 +326,8 @@ class _ProjectScreenState extends State<ProjectScreen> {
|
|||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
heroTag: null,
|
heroTag: null,
|
||||||
child: const Icon(Icons.insert_drive_file_outlined),
|
|
||||||
onPressed: _chooseFile,
|
onPressed: _chooseFile,
|
||||||
|
child: const Icon(Icons.insert_drive_file_outlined),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
@ -342,10 +339,9 @@ class _ProjectScreenState extends State<ProjectScreen> {
|
|||||||
class ProjectScreen extends StatefulWidget {
|
class ProjectScreen extends StatefulWidget {
|
||||||
final String username;
|
final String username;
|
||||||
final String projectPath;
|
final String projectPath;
|
||||||
final Map<String,dynamic>? project;
|
const ProjectScreen(this.username, this.projectPath, {super.key});
|
||||||
ProjectScreen(this.username, this.projectPath, {this.project, }) { }
|
|
||||||
@override
|
@override
|
||||||
_ProjectScreenState createState() => _ProjectScreenState(username, projectPath, project: project);
|
State<ProjectScreen> createState() => _ProjectScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ProjectSettingsDialog extends StatelessWidget {
|
class _ProjectSettingsDialog extends StatelessWidget {
|
||||||
@ -355,7 +351,7 @@ class _ProjectSettingsDialog extends StatelessWidget {
|
|||||||
final Function _onUpdateProject;
|
final Function _onUpdateProject;
|
||||||
final Api api = Api();
|
final Api api = Api();
|
||||||
_ProjectSettingsDialog(this.project, this._onDelete, this._onUpdateProject) :
|
_ProjectSettingsDialog(this.project, this._onDelete, this._onUpdateProject) :
|
||||||
fullPath = project['owner']['username'] + '/' + project['path'];
|
fullPath = '${project["owner"]["username"]}/${project["path"]}';
|
||||||
|
|
||||||
void _renameProject(BuildContext context) async {
|
void _renameProject(BuildContext context) async {
|
||||||
TextEditingController renameController = TextEditingController();
|
TextEditingController renameController = TextEditingController();
|
||||||
@ -379,8 +375,9 @@ class _ProjectSettingsDialog extends StatelessWidget {
|
|||||||
TextButton(
|
TextButton(
|
||||||
child: Text('OK'),
|
child: Text('OK'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
var data = await api.request('PUT', '/projects/' + fullPath, {'name': renameController.text});
|
var data = await api.request('PUT', '/projects/$fullPath', {'name': renameController.text});
|
||||||
if (data['success']) {
|
if (data['success']) {
|
||||||
|
if (!context.mounted) return;
|
||||||
context.pop();
|
context.pop();
|
||||||
_onUpdateProject(data['payload']);
|
_onUpdateProject(data['payload']);
|
||||||
} else {
|
} else {
|
||||||
@ -393,6 +390,7 @@ class _ProjectSettingsDialog extends StatelessWidget {
|
|||||||
textColor: Colors.white,
|
textColor: Colors.white,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (!context.mounted) return;
|
||||||
context.pop();
|
context.pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -403,15 +401,16 @@ class _ProjectSettingsDialog extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _toggleVisibility(BuildContext context, bool checked) async {
|
void _toggleVisibility(BuildContext context, bool checked) async {
|
||||||
var data = await api.request('PUT', '/projects/' + fullPath, {'visibility': checked ? 'private': 'public'});
|
var data = await api.request('PUT', '/projects/$fullPath', {'visibility': checked ? 'private': 'public'});
|
||||||
if (data['success']) {
|
if (data['success']) {
|
||||||
|
if (!context.mounted) return;
|
||||||
context.pop();
|
context.pop();
|
||||||
_onUpdateProject(data['payload']);
|
_onUpdateProject(data['payload']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deleteProject(BuildContext context, BuildContext modalContext) async {
|
void _deleteProject(BuildContext context, BuildContext modalContext) async {
|
||||||
var data = await api.request('DELETE', '/projects/' + fullPath);
|
var data = await api.request('DELETE', '/projects/$fullPath');
|
||||||
if (data['success']) {
|
if (data['success']) {
|
||||||
_onDelete();
|
_onDelete();
|
||||||
}
|
}
|
||||||
@ -421,8 +420,8 @@ class _ProjectSettingsDialog extends StatelessWidget {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: modalContext,
|
context: modalContext,
|
||||||
builder: (BuildContext context) => CupertinoAlertDialog(
|
builder: (BuildContext context) => CupertinoAlertDialog(
|
||||||
title: new Text('Really delete this project?'),
|
title: Text('Really delete this project?'),
|
||||||
content: new Text('This will remove any files and objects inside the project. This action cannot be undone.'),
|
content: Text('This will remove any files and objects inside the project. This action cannot be undone.'),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
CupertinoDialogAction(
|
CupertinoDialogAction(
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
@ -454,7 +453,7 @@ class _ProjectSettingsDialog extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
CupertinoSwitch(
|
CupertinoSwitch(
|
||||||
value: project?['visibility'] == 'private',
|
value: project['visibility'] == 'private',
|
||||||
onChanged: (c) => _toggleVisibility(context, c),
|
onChanged: (c) => _toggleVisibility(context, c),
|
||||||
),
|
),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
@ -468,8 +467,8 @@ class _ProjectSettingsDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
CupertinoActionSheetAction(
|
CupertinoActionSheetAction(
|
||||||
onPressed: () { _confirmDeleteProject(context); },
|
onPressed: () { _confirmDeleteProject(context); },
|
||||||
child: Text('Delete project'),
|
|
||||||
isDestructiveAction: true,
|
isDestructiveAction: true,
|
||||||
|
child: Text('Delete project'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
@ -39,47 +38,42 @@ class _ProjectsTabState extends State<ProjectsTab> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
void _onCreateProject(newProject) {
|
void _onCreateProject(newProject) {
|
||||||
List<dynamic> _newProjects = _projects;
|
List<dynamic> newProjects = _projects;
|
||||||
_newProjects.insert(0, newProject);
|
newProjects.insert(0, newProject);
|
||||||
setState(() {
|
setState(() {
|
||||||
_projects = _newProjects;
|
_projects = newProjects;
|
||||||
_creatingProject = false;
|
_creatingProject = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDeleteProject(String id) {
|
|
||||||
List<dynamic> _newProjects = _projects.where((p) => p['_id'] != id).toList();
|
|
||||||
setState(() {
|
|
||||||
_projects = _newProjects;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void showNewProjectDialog() async {
|
void showNewProjectDialog() async {
|
||||||
Widget simpleDialog = new _NewProjectDialog(_onCreatingProject, _onCreateProject);
|
Widget simpleDialog = _NewProjectDialog(_onCreatingProject, _onCreateProject);
|
||||||
showDialog(context: context, builder: (BuildContext context) => simpleDialog);
|
showDialog(context: context, builder: (BuildContext context) => simpleDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildProjectCard(Map<String,dynamic> project) {
|
Widget buildProjectCard(Map<String,dynamic> project) {
|
||||||
String description = project['description'] != null ? project['description'].replaceAll("\n", " ") : '';
|
String description = project['description'] != null ? project['description'].replaceAll("\n", " ") : '';
|
||||||
if (description != null && description.length > 80) {
|
if (description.length > 80) {
|
||||||
description = description.substring(0, 77) + '...';
|
description = '${description.substring(0, 77)}...';
|
||||||
}
|
}
|
||||||
if (project['visibility'] == 'public') {
|
if (project['visibility'] == 'public') {
|
||||||
description = "PUBLIC PROJECT\n" + description;
|
description = "PUBLIC PROJECT\n$description";
|
||||||
}
|
}
|
||||||
else description = "PRIVATE PROJECT\n" + description;
|
else {
|
||||||
return new Card(
|
description = "PRIVATE PROJECT\n$description";
|
||||||
|
}
|
||||||
|
return Card(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.push('/' + project['owner']['username'] + '/' + project['path']);
|
context.push('/${project["owner"]["username"]}/${project["path"]}');
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.all(5),
|
padding: EdgeInsets.all(5),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: new AspectRatio(
|
leading: AspectRatio(
|
||||||
aspectRatio: 1 / 1,
|
aspectRatio: 1 / 1,
|
||||||
child: new Container(
|
child: Container(
|
||||||
decoration: new BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.grey[100],
|
color: Colors.grey[100],
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
),
|
),
|
||||||
@ -87,7 +81,7 @@ class _ProjectsTabState extends State<ProjectsTab> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: Icon(Icons.keyboard_arrow_right),
|
trailing: Icon(Icons.keyboard_arrow_right),
|
||||||
title: Text(project['name'] != null ? project['name'] : 'Untitled project'),
|
title: Text(project['name'] ?? 'Untitled project'),
|
||||||
subtitle: Text(description),
|
subtitle: Text(description),
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
@ -97,26 +91,29 @@ class _ProjectsTabState extends State<ProjectsTab> {
|
|||||||
|
|
||||||
Widget getBody() {
|
Widget getBody() {
|
||||||
AppModel model = Provider.of<AppModel>(context);
|
AppModel model = Provider.of<AppModel>(context);
|
||||||
if (model.user == null)
|
if (model.user == null) {
|
||||||
return LoginNeeded(text: 'Once logged in, you\'ll find your own projects shown here.');
|
return LoginNeeded(text: 'Once logged in, you\'ll find your own projects shown here.');
|
||||||
if (_loading)
|
}
|
||||||
|
if (_loading) {
|
||||||
return CircularProgressIndicator();
|
return CircularProgressIndicator();
|
||||||
else if (_projects != null && _projects.length > 0)
|
} else if (_projects.isNotEmpty) {
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: _projects.length,
|
itemCount: _projects.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return buildProjectCard(_projects[index]);
|
return buildProjectCard(_projects[index]);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
else return Column(
|
} else {
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
Text('Create your first project', style: TextStyle(fontSize: 20), textAlign: TextAlign.center),
|
children: [
|
||||||
Image(image: AssetImage('assets/reading.png'), width: 300),
|
Text('Create your first project', style: TextStyle(fontSize: 20), textAlign: TextAlign.center),
|
||||||
Text('Projects contain all the files and patterns that make up a piece of work. Create one using the + button below.', textAlign: TextAlign.center),
|
Image(image: AssetImage('assets/reading.png'), width: 300),
|
||||||
]
|
Text('Projects contain all the files and patterns that make up a piece of work. Create one using the + button below.', textAlign: TextAlign.center),
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -156,20 +153,17 @@ class _ProjectsTabState extends State<ProjectsTab> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ProjectsTab extends StatefulWidget {
|
class ProjectsTab extends StatefulWidget {
|
||||||
|
const ProjectsTab({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ProjectsTabState createState() => _ProjectsTabState();
|
State<ProjectsTab> createState() => _ProjectsTabState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NewProjectDialogState extends State<_NewProjectDialog> {
|
class _NewProjectDialogState extends State<_NewProjectDialog> {
|
||||||
final TextEditingController _newProjectNameController = TextEditingController();
|
final TextEditingController _newProjectNameController = TextEditingController();
|
||||||
final Function _onStart;
|
|
||||||
final Function _onComplete;
|
|
||||||
String _newProjectName = '';
|
|
||||||
bool _newProjectPrivate = false;
|
bool _newProjectPrivate = false;
|
||||||
final Api api = Api();
|
final Api api = Api();
|
||||||
|
|
||||||
_NewProjectDialogState(this._onStart, this._onComplete) {}
|
|
||||||
|
|
||||||
void _onToggleProjectVisibility(checked) {
|
void _onToggleProjectVisibility(checked) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_newProjectPrivate = checked;
|
_newProjectPrivate = checked;
|
||||||
@ -177,13 +171,15 @@ class _NewProjectDialogState extends State<_NewProjectDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _createProject() async {
|
void _createProject() async {
|
||||||
_onStart();
|
widget._onStart();
|
||||||
String name = _newProjectNameController.text;
|
String name = _newProjectNameController.text;
|
||||||
bool priv = _newProjectPrivate;
|
bool priv = _newProjectPrivate;
|
||||||
var data = await api.request('POST', '/projects', {'name': name, 'visibility': priv ? 'private' : 'public'});
|
var data = await api.request('POST', '/projects', {'name': name, 'visibility': priv ? 'private' : 'public'});
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
_onComplete(data['payload']);
|
widget._onComplete(data['payload']);
|
||||||
context.pop();
|
if (mounted) {
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +227,7 @@ class _NewProjectDialogState extends State<_NewProjectDialog> {
|
|||||||
class _NewProjectDialog extends StatefulWidget {
|
class _NewProjectDialog extends StatefulWidget {
|
||||||
final Function _onComplete;
|
final Function _onComplete;
|
||||||
final Function _onStart;
|
final Function _onStart;
|
||||||
_NewProjectDialog(this._onStart, this._onComplete) {}
|
const _NewProjectDialog(this._onStart, this._onComplete);
|
||||||
@override
|
@override
|
||||||
_NewProjectDialogState createState() => _NewProjectDialogState(_onStart, _onComplete);
|
State<_NewProjectDialog> createState() => _NewProjectDialogState();
|
||||||
}
|
}
|
||||||
|
@ -19,16 +19,19 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||||||
var data = await api.request('POST', '/accounts/register', {'username': _usernameController.text, 'email': _emailController.text, 'password': _passwordController.text});
|
var data = await api.request('POST', '/accounts/register', {'username': _usernameController.text, 'email': _emailController.text, 'password': _passwordController.text});
|
||||||
setState(() => _registering = false);
|
setState(() => _registering = false);
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
|
if (!context.mounted) return;
|
||||||
AppModel model = Provider.of<AppModel>(context, listen: false);
|
AppModel model = Provider.of<AppModel>(context, listen: false);
|
||||||
await model.setToken(data['payload']['token']);
|
await model.setToken(data['payload']['token']);
|
||||||
|
if (!context.mounted) return;
|
||||||
context.go('/onboarding');
|
context.go('/onboarding');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (!context.mounted) return;
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => CupertinoAlertDialog(
|
builder: (BuildContext context) => CupertinoAlertDialog(
|
||||||
title: new Text("There was a problem registering your account"),
|
title: Text("There was a problem registering your account"),
|
||||||
content: new Text(data['message']),
|
content: Text(data['message']),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
CupertinoDialogAction(
|
CupertinoDialogAction(
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
@ -84,9 +87,9 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||||||
text: 'By registering you agree to Treadl\'s ',
|
text: 'By registering you agree to Treadl\'s ',
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
children: <TextSpan>[
|
children: <TextSpan>[
|
||||||
TextSpan(text: 'Terms of Use', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.pink), recognizer: new TapGestureRecognizer()..onTap = () => launch('https://treadl.com/terms-of-use')),
|
TextSpan(text: 'Terms of Use', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.pink), recognizer: TapGestureRecognizer()..onTap = () => launchUrl(Uri.parse('https://treadl.com/terms-of-use'))),
|
||||||
TextSpan(text: ' and '),
|
TextSpan(text: ' and '),
|
||||||
TextSpan(text: 'Privacy Policy', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.pink), recognizer: new TapGestureRecognizer()..onTap = () => launch('https://treadl.com/privacy')),
|
TextSpan(text: 'Privacy Policy', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.pink), recognizer: TapGestureRecognizer()..onTap = () => launchUrl(Uri.parse('https://treadl.com/privacy'))),
|
||||||
TextSpan(text: '.'),
|
TextSpan(text: '.'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -106,6 +109,8 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RegisterScreen extends StatefulWidget {
|
class RegisterScreen extends StatefulWidget {
|
||||||
|
const RegisterScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_RegisterScreenState createState() => _RegisterScreenState();
|
State<RegisterScreen> createState() => _RegisterScreenState();
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import 'model.dart';
|
|||||||
|
|
||||||
class SettingsScreen extends StatelessWidget {
|
class SettingsScreen extends StatelessWidget {
|
||||||
final TextEditingController _passwordController = TextEditingController();
|
final TextEditingController _passwordController = TextEditingController();
|
||||||
|
|
||||||
|
SettingsScreen({super.key});
|
||||||
|
|
||||||
void _logout(BuildContext context) async {
|
void _logout(BuildContext context) async {
|
||||||
AppModel model = Provider.of<AppModel>(context, listen: false);
|
AppModel model = Provider.of<AppModel>(context, listen: false);
|
||||||
@ -44,16 +46,18 @@ class SettingsScreen extends StatelessWidget {
|
|||||||
Api api = Api();
|
Api api = Api();
|
||||||
var data = await api.request('DELETE', '/accounts', {'password': _passwordController.text});
|
var data = await api.request('DELETE', '/accounts', {'password': _passwordController.text});
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
|
if (!context.mounted) return;
|
||||||
AppModel model = Provider.of<AppModel>(context, listen: false);
|
AppModel model = Provider.of<AppModel>(context, listen: false);
|
||||||
model.setToken(null);
|
model.setToken(null);
|
||||||
model.setUser(null);
|
model.setUser(null);
|
||||||
context.go('/home');
|
context.go('/home');
|
||||||
} else {
|
} else {
|
||||||
|
if (!context.mounted) return;
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => CupertinoAlertDialog(
|
builder: (BuildContext context) => CupertinoAlertDialog(
|
||||||
title: new Text('There was a problem with deleting your account'),
|
title: Text('There was a problem with deleting your account'),
|
||||||
content: new Text(data['message']),
|
content: Text(data['message']),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
CupertinoDialogAction(
|
CupertinoDialogAction(
|
||||||
isDefaultAction: true,
|
isDefaultAction: true,
|
||||||
@ -93,9 +97,7 @@ class SettingsScreen extends StatelessWidget {
|
|||||||
child:
|
child:
|
||||||
Text('Thanks for using Treadl', style: Theme.of(context).textTheme.titleLarge),
|
Text('Thanks for using Treadl', style: Theme.of(context).textTheme.titleLarge),
|
||||||
),
|
),
|
||||||
Container(
|
Text("Treadl is an app for managing your projects and for keeping in touch with your weaving communities.\n\nWe're always trying to make Treadl better, so if you have any feedback please let us know!", style: Theme.of(context).textTheme.bodyMedium),
|
||||||
child: Text("Treadl is an app for managing your projects and for keeping in touch with your weaving communities.\n\nWe're always trying to make Treadl better, so if you have any feedback please let us know!", style: Theme.of(context).textTheme.bodyMedium)
|
|
||||||
),
|
|
||||||
|
|
||||||
SizedBox(height: 30),
|
SizedBox(height: 30),
|
||||||
|
|
||||||
@ -136,26 +138,26 @@ class SettingsScreen extends StatelessWidget {
|
|||||||
'body': ''
|
'body': ''
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
launch(emailLaunchUri.toString());
|
launchUrl(Uri.parse(emailLaunchUri.toString()));
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.link),
|
leading: Icon(Icons.link),
|
||||||
trailing: Icon(Icons.explore),
|
trailing: Icon(Icons.explore),
|
||||||
title: Text('Visit Our Website'),
|
title: Text('Visit Our Website'),
|
||||||
onTap: () => launch('https://treadl.com'),
|
onTap: () => launchUrl(Uri.parse('https://treadl.com')),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.insert_drive_file),
|
leading: Icon(Icons.insert_drive_file),
|
||||||
trailing: Icon(Icons.explore),
|
trailing: Icon(Icons.explore),
|
||||||
title: Text('Terms of Use'),
|
title: Text('Terms of Use'),
|
||||||
onTap: () => launch('https://treadl.com/terms-of-use'),
|
onTap: () => launchUrl(Uri.parse('https://treadl.com/terms-of-use')),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.insert_drive_file),
|
leading: Icon(Icons.insert_drive_file),
|
||||||
trailing: Icon(Icons.explore),
|
trailing: Icon(Icons.explore),
|
||||||
title: Text('Privacy Policy'),
|
title: Text('Privacy Policy'),
|
||||||
onTap: () => launch('https://treadl.com/privacy'),
|
onTap: () => launchUrl(Uri.parse('https://treadl.com/privacy')),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'util.dart';
|
import 'util.dart';
|
||||||
@ -8,24 +7,21 @@ import 'api.dart';
|
|||||||
import 'lib.dart';
|
import 'lib.dart';
|
||||||
|
|
||||||
class _UserScreenState extends State<UserScreen> with SingleTickerProviderStateMixin {
|
class _UserScreenState extends State<UserScreen> with SingleTickerProviderStateMixin {
|
||||||
final String username;
|
|
||||||
final Api api = Api();
|
final Api api = Api();
|
||||||
TabController? _tabController;
|
TabController? _tabController;
|
||||||
Map<String,dynamic>? _user;
|
Map<String,dynamic>? _user;
|
||||||
bool _loading = false;
|
bool _loading = false;
|
||||||
_UserScreenState(this.username) { }
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_tabController = new TabController(length: 2, vsync: this);
|
_tabController = TabController(length: 2, vsync: this);
|
||||||
getUser(username);
|
getUser(widget.username);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getUser(String username) async {
|
void getUser(String username) async {
|
||||||
if (username == null) return;
|
|
||||||
setState(() => _loading = true);
|
setState(() => _loading = true);
|
||||||
var data = await api.request('GET', '/users/' + username);
|
var data = await api.request('GET', '/users/$username');
|
||||||
if (data['success'] == true) {
|
if (data['success'] == true) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_user = data['payload'];
|
_user = data['payload'];
|
||||||
@ -35,9 +31,9 @@ class _UserScreenState extends State<UserScreen> with SingleTickerProviderStateM
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget getBody() {
|
Widget getBody() {
|
||||||
if (_loading)
|
if (_loading) {
|
||||||
return CircularProgressIndicator();
|
return CircularProgressIndicator();
|
||||||
else if (_user != null && _tabController != null) {
|
} else if (_user != null && _tabController != null) {
|
||||||
var u = _user!;
|
var u = _user!;
|
||||||
String? created;
|
String? created;
|
||||||
if (u['createdAt'] != null) {
|
if (u['createdAt'] != null) {
|
||||||
@ -63,7 +59,7 @@ class _UserScreenState extends State<UserScreen> with SingleTickerProviderStateM
|
|||||||
Text(u['location'])
|
Text(u['location'])
|
||||||
]) : SizedBox(height: 1),
|
]) : SizedBox(height: 1),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
Text('Member' + (created != null ? (' since ' + created!) : ''),
|
Text('Member${created != null ? (' since $created') : ''}',
|
||||||
style: TextStyle(color: Colors.grey[500])
|
style: TextStyle(color: Colors.grey[500])
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
@ -72,9 +68,9 @@ class _UserScreenState extends State<UserScreen> with SingleTickerProviderStateM
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
String url = u['website'];
|
String url = u['website'];
|
||||||
if (!url.startsWith('http')) {
|
if (!url.startsWith('http')) {
|
||||||
url = 'http://' + url;
|
url = 'http://$url';
|
||||||
}
|
}
|
||||||
launch(url);
|
launchUrl(Uri.parse(url));
|
||||||
},
|
},
|
||||||
child: Text(u['website'],
|
child: Text(u['website'],
|
||||||
style: TextStyle(color: Colors.pink))
|
style: TextStyle(color: Colors.pink))
|
||||||
@ -134,8 +130,9 @@ class _UserScreenState extends State<UserScreen> with SingleTickerProviderStateM
|
|||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
return Text('User not found');
|
return Text('User not found');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -143,12 +140,12 @@ class _UserScreenState extends State<UserScreen> with SingleTickerProviderStateM
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(username),
|
title: Text(widget.username),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.person),
|
icon: Icon(Icons.person),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
launch('https://www.treadl.com/' + username);
|
launchUrl(Uri.parse('https://www.treadl.com/${widget.username}'));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@ -164,7 +161,7 @@ class _UserScreenState extends State<UserScreen> with SingleTickerProviderStateM
|
|||||||
|
|
||||||
class UserScreen extends StatefulWidget {
|
class UserScreen extends StatefulWidget {
|
||||||
final String username;
|
final String username;
|
||||||
UserScreen(this.username) { }
|
const UserScreen(this.username, {super.key});
|
||||||
@override
|
@override
|
||||||
_UserScreenState createState() => _UserScreenState(username);
|
State<UserScreen> createState() => _UserScreenState();
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,14 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:convert';
|
|
||||||
import 'model.dart';
|
import 'model.dart';
|
||||||
|
|
||||||
String APP_URL = 'https://www.treadl.com';
|
String appBaseUrl = 'https://www.treadl.com';
|
||||||
|
|
||||||
class Util {
|
class Util {
|
||||||
|
|
||||||
static ImageProvider? avatarUrl(Map<String,dynamic> user) {
|
static ImageProvider? avatarUrl(Map<String,dynamic> user) {
|
||||||
if (user != null && user['avatar'] != null) {
|
if (user['avatar'] != null) {
|
||||||
if (user['avatar'].length < 3) {
|
if (user['avatar'].length < 3) {
|
||||||
return AssetImage('assets/avatars/${user['avatar']}.png');
|
return AssetImage('assets/avatars/${user['avatar']}.png');
|
||||||
}
|
}
|
||||||
@ -23,19 +22,19 @@ class Util {
|
|||||||
|
|
||||||
static Widget avatarImage(ImageProvider? image, {double size=30}) {
|
static Widget avatarImage(ImageProvider? image, {double size=30}) {
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
return new Container(
|
return Container(
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
decoration: new BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
image: new DecorationImage(
|
image: DecorationImage(
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
image: image
|
image: image
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return new Container(
|
return Container(
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -54,7 +53,7 @@ class Util {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static String appUrl(String path) {
|
static String appUrl(String path) {
|
||||||
return APP_URL + '/' + path;
|
return '$appBaseUrl/$path';
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<String> storagePath() async {
|
static Future<String> storagePath() async {
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
@ -9,17 +7,18 @@ import 'model.dart';
|
|||||||
import 'lib.dart';
|
import 'lib.dart';
|
||||||
|
|
||||||
class _VerifyEmailScreenState extends State<VerifyEmailScreen> {
|
class _VerifyEmailScreenState extends State<VerifyEmailScreen> {
|
||||||
final String? token;
|
String? token;
|
||||||
bool loading = false;
|
bool loading = false;
|
||||||
String? error;
|
String? error;
|
||||||
String? success;
|
String? success;
|
||||||
Api api = Api();
|
Api api = Api();
|
||||||
_VerifyEmailScreenState({required this.token});
|
_VerifyEmailScreenState();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_verify(context);
|
_verify(context);
|
||||||
|
token = widget.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _verify(BuildContext context) async {
|
void _verify(BuildContext context) async {
|
||||||
@ -48,6 +47,7 @@ class _VerifyEmailScreenState extends State<VerifyEmailScreen> {
|
|||||||
textColor: Colors.white,
|
textColor: Colors.white,
|
||||||
fontSize: 20.0
|
fontSize: 20.0
|
||||||
);
|
);
|
||||||
|
if (!context.mounted) return;
|
||||||
context.go('/');
|
context.go('/');
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -147,6 +147,7 @@ class _VerifyEmailScreenState extends State<VerifyEmailScreen> {
|
|||||||
class VerifyEmailScreen extends StatefulWidget {
|
class VerifyEmailScreen extends StatefulWidget {
|
||||||
final String? token;
|
final String? token;
|
||||||
@override
|
@override
|
||||||
VerifyEmailScreen({required this.token});
|
const VerifyEmailScreen({super.key, required this.token});
|
||||||
_VerifyEmailScreenState createState() => _VerifyEmailScreenState(token: token);
|
@override
|
||||||
|
State<VerifyEmailScreen> createState() => _VerifyEmailScreenState();
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'login.dart';
|
|
||||||
|
|
||||||
class WelcomeScreen extends StatelessWidget {
|
class WelcomeScreen extends StatelessWidget {
|
||||||
|
const WelcomeScreen({super.key});
|
||||||
|
|
||||||
void _login(BuildContext context) {
|
void _login(BuildContext context) {
|
||||||
context.push('/login');
|
context.push('/login');
|
||||||
}
|
}
|
||||||
@ -27,7 +27,7 @@ class WelcomeScreen extends StatelessWidget {
|
|||||||
SizedBox(height: 30),
|
SizedBox(height: 30),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => _login(context),
|
onPressed: () => _login(context),
|
||||||
child: new Text("Login",
|
child: Text("Login",
|
||||||
style: TextStyle(color: Colors.pink),
|
style: TextStyle(color: Colors.pink),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
)
|
)
|
||||||
@ -35,14 +35,14 @@ class WelcomeScreen extends StatelessWidget {
|
|||||||
SizedBox(height: 15),
|
SizedBox(height: 15),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => _register(context),
|
onPressed: () => _register(context),
|
||||||
child: new Text("Register",
|
child: Text("Register",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
SizedBox(height: 35),
|
SizedBox(height: 35),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => context.pop(),
|
onPressed: () => context.pop(),
|
||||||
child: new Text("Cancel",
|
child: Text("Cancel",
|
||||||
style: TextStyle(color: Colors.white),
|
style: TextStyle(color: Colors.white),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
)
|
)
|
||||||
|
@ -5,10 +5,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _flutterfire_internals
|
name: _flutterfire_internals
|
||||||
sha256: "7fd72d77a7487c26faab1d274af23fb008763ddc10800261abbfb2c067f183d5"
|
sha256: de9ecbb3ddafd446095f7e833c853aff2fa1682b017921fe63a833f9d6f0e422
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.53"
|
version: "1.3.54"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -93,10 +93,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.6"
|
||||||
csslib:
|
csslib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -133,58 +133,58 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file
|
name: file
|
||||||
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.4"
|
version: "7.0.1"
|
||||||
file_picker:
|
file_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
sha256: "8d938fd5c11dc81bf1acd4f7f0486c683fe9e79a0b13419e27730f9ce4d8a25b"
|
sha256: "09b474c0c8117484b80cbebc043801ff91e05cfbd2874d512825c899e1754694"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.2.1"
|
version: "9.2.3"
|
||||||
file_selector_linux:
|
file_selector_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file_selector_linux
|
name: file_selector_linux
|
||||||
sha256: d17c5e450192cdc40b718804dfb4eaf79a71bed60ee9530703900879ba50baa3
|
sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.1+3"
|
version: "0.9.3+2"
|
||||||
file_selector_macos:
|
file_selector_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file_selector_macos
|
name: file_selector_macos
|
||||||
sha256: "6290eec24fc4cc62535fe609e0c6714d3c1306191dc8c3b0319eaecc09423a3a"
|
sha256: "271ab9986df0c135d45c3cdb6bd0faa5db6f4976d3e4b437cf7d0f258d941bfc"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.2"
|
version: "0.9.4+2"
|
||||||
file_selector_platform_interface:
|
file_selector_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file_selector_platform_interface
|
name: file_selector_platform_interface
|
||||||
sha256: "2a7f4bbf7bd2f022ecea85bfb1754e87f7dd403a9abc17a84a4fa2ddfe2abc0a"
|
sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.1"
|
version: "2.6.2"
|
||||||
file_selector_windows:
|
file_selector_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file_selector_windows
|
name: file_selector_windows
|
||||||
sha256: ef246380b66d1fb9089fc65622c387bf3780bca79f533424c31d07f12c2c7fd8
|
sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.2"
|
version: "0.9.3+4"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_core
|
name: firebase_core
|
||||||
sha256: f4d8f49574a4e396f34567f3eec4d38ab9c3910818dec22ca42b2a467c685d8b
|
sha256: "017d17d9915670e6117497e640b2859e0b868026ea36bf3a57feb28c3b97debe"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.12.1"
|
version: "3.13.0"
|
||||||
firebase_core_platform_interface:
|
firebase_core_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -197,34 +197,42 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core_web
|
name: firebase_core_web
|
||||||
sha256: faa5a76f6380a9b90b53bc3bdcb85bc7926a382e0709b9b5edac9f7746651493
|
sha256: "129a34d1e0fb62e2b488d988a1fc26cc15636357e50944ffee2862efe8929b23"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.21.1"
|
version: "2.22.0"
|
||||||
firebase_messaging:
|
firebase_messaging:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging
|
name: firebase_messaging
|
||||||
sha256: "5fc345c6341f9dc69fd0ffcbf508c784fd6d1b9e9f249587f30434dd8b6aa281"
|
sha256: "5f8918848ee0c8eb172fc7698619b2bcd7dda9ade8b93522c6297dd8f9178356"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "15.2.4"
|
version: "15.2.5"
|
||||||
firebase_messaging_platform_interface:
|
firebase_messaging_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging_platform_interface
|
name: firebase_messaging_platform_interface
|
||||||
sha256: a935924cf40925985c8049df4968b1dde5c704f570f3ce380b31d3de6990dd94
|
sha256: "0bbea00680249595fc896e7313a2bd90bd55be6e0abbe8b9a39d81b6b306acb6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.6.4"
|
version: "4.6.5"
|
||||||
firebase_messaging_web:
|
firebase_messaging_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_messaging_web
|
name: firebase_messaging_web
|
||||||
sha256: fafebf6a1921931334f3f10edb5037a5712288efdd022881e2d093e5654a2fd4
|
sha256: ffb392ce2a7e8439cd0a9a80e3c702194e73c927e5c7b4f0adf6faa00b245b17
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.10.4"
|
version: "3.10.5"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -234,10 +242,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_expandable_fab
|
name: flutter_expandable_fab
|
||||||
sha256: b14caf78720a48f650e6e1a38d724e33b1f5348d646fa1c266570c31a7f87ef3
|
sha256: "4d03f54e5384897e32606e9959cef5e7857e5a203e24684f95dfbb5f7fb9b88e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.4.1"
|
||||||
flutter_html:
|
flutter_html:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -254,6 +262,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.14.3"
|
version: "0.14.3"
|
||||||
|
flutter_lints:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_lints
|
||||||
|
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -308,10 +324,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.1.2"
|
||||||
image:
|
image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -332,42 +348,42 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_android
|
name: image_picker_android
|
||||||
sha256: "1a27bf4cc0330389cebe465bab08fe6dec97e44015b4899637344bb7297759ec"
|
sha256: "8bd392ba8b0c8957a157ae0dc9fcf48c58e6c20908d5880aea1d79734df090e9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.9+2"
|
version: "0.8.12+22"
|
||||||
image_picker_for_web:
|
image_picker_for_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_for_web
|
name: image_picker_for_web
|
||||||
sha256: "869fe8a64771b7afbc99fc433a5f7be2fea4d1cb3d7c11a48b6b579eb9c797f0"
|
sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "3.0.6"
|
||||||
image_picker_ios:
|
image_picker_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_ios
|
name: image_picker_ios
|
||||||
sha256: eac0a62104fa12feed213596df0321f57ce5a572562f72a68c4ff81e9e4caacf
|
sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.9"
|
version: "0.8.12+2"
|
||||||
image_picker_linux:
|
image_picker_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_linux
|
name: image_picker_linux
|
||||||
sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa"
|
sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.1+1"
|
version: "0.2.1+2"
|
||||||
image_picker_macos:
|
image_picker_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_macos
|
name: image_picker_macos
|
||||||
sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62"
|
sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.1+1"
|
version: "0.2.1+2"
|
||||||
image_picker_platform_interface:
|
image_picker_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -424,6 +440,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
|
lints:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lints
|
||||||
|
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.1"
|
||||||
list_counter:
|
list_counter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -436,10 +460,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: logging
|
name: logging
|
||||||
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.3.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -468,10 +492,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: mime
|
name: mime
|
||||||
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "2.0.0"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -524,34 +548,34 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_windows
|
name: path_provider_windows
|
||||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.3.0"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4"
|
sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.1.0"
|
version: "6.1.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.6"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -568,22 +592,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.1"
|
version: "6.0.1"
|
||||||
process:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: process
|
|
||||||
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.2.4"
|
|
||||||
provider:
|
provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: provider
|
name: provider
|
||||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
sha256: "489024f942069c2920c844ee18bb3d467c69e48955a4f32d1677f71be103e310"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.2"
|
version: "6.1.4"
|
||||||
share_plus:
|
share_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -604,10 +620,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a"
|
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.2"
|
version: "2.5.3"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -721,10 +737,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: typed_data
|
name: typed_data
|
||||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.4.0"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -745,10 +761,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_ios
|
name: url_launcher_ios
|
||||||
sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626"
|
sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.2"
|
version: "6.3.3"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -793,10 +809,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f"
|
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.2"
|
version: "4.5.1"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -833,26 +849,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
|
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.1.0"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xml
|
name: xml
|
||||||
sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5"
|
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.2"
|
version: "6.5.0"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: yaml
|
name: yaml
|
||||||
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.7.0 <4.0.0"
|
dart: ">=3.7.0 <4.0.0"
|
||||||
flutter: ">=3.27.0"
|
flutter: ">=3.27.0"
|
||||||
|
@ -40,9 +40,11 @@ dependencies:
|
|||||||
go_router: ^14.8.1
|
go_router: ^14.8.1
|
||||||
fluttertoast: ^8.2.12
|
fluttertoast: ^8.2.12
|
||||||
|
|
||||||
|
firebase_core: any
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_lints: ^5.0.0
|
||||||
|
|
||||||
flutter_icons:
|
flutter_icons:
|
||||||
android: "launcher_icon"
|
android: "launcher_icon"
|
||||||
|
Loading…
Reference in New Issue
Block a user