diff --git a/.gitignore b/.gitignore index 8104aa3d..1a76ca18 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ doc/api/ .atom/ .dart_tool/ .idea -.vscode/ ios/.generated/ packages .flutter-plugins +.log diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..5743ee0d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [{ + "name": "Flutter", + "type": "dart", + "request": "launch", + "args": [ + "--flavor=development" + ], + "program": "lib/main.dart" + }] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..79ecc5ae --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.codeActionsOnSave": { + "source.organizeImports": true + } +} diff --git a/README.md b/README.md index 41350144..a590bb86 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,24 @@ + + # TailorMade -A Flutter experiment. +A Flutter experiment for managing a Fashion designer's daily routine. Logo, Design & Concept by Me. + +> Android-only support + +## Tools -## UI Shots (so far) +1. Firebase Auth +2. Firebase Cloud Firestore +3. Firebase Cloud Functions +4. Firebase Storage +5. RxDart +6. Redux +7. Redux Epics + +For a full description of OSS used, see pubspec.yaml + +## UI Shots
@@ -64,6 +80,15 @@ A Flutter experiment. + + +
+ + + + + +
diff --git a/TASKS.todo b/TASKS.todo index 0fce0b47..c6999ed5 100644 --- a/TASKS.todo +++ b/TASKS.todo @@ -1,8 +1,9 @@ PENDING: +☐ remodel w/ authID ☐ delete-able payment ☐ delete-able images ☐ implement share payment + image - +☐ filter + search jobs & contacts COMPLETED: ✔ update stats db on creation @high @done(18-07-10 16:39) diff --git a/android/.gitignore b/android/.gitignore index 65b7315a..07242eed 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -8,3 +8,4 @@ /build /captures GeneratedPluginRegistrant.java +google-services.json diff --git a/android/app/build.gradle b/android/app/build.gradle index 3d1f343e..616ad25d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -37,6 +37,19 @@ android { signingConfig signingConfigs.debug } } + + flavorDimensions "flavor-type" + + productFlavors { + development { + dimension "flavor-type" + applicationIdSuffix ".dev" + versionNameSuffix "-dev" + } + production { + dimension "flavor-type" + } + } } flutter { diff --git a/android/app/google-services.json b/android/app/google-services.json deleted file mode 100644 index 063908f6..00000000 --- a/android/app/google-services.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "project_info": { - "project_number": "411406804353", - "firebase_url": "https://tailor-made-2018.firebaseio.com", - "project_id": "tailor-made-2018", - "storage_bucket": "tailor-made-2018.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:411406804353:android:bb6c31c0382b00e8", - "android_client_info": { - "package_name": "io.github.jogboms.tailormade" - } - }, - "oauth_client": [ - { - "client_id": "411406804353-m2renekg4a6e86opvlotdr1jj4lojvc8.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyCMpRzd1u83akc6kvCza0rBjsxFmUkf7TI" - } - ], - "services": { - "analytics_service": { - "status": 1 - }, - "appinvite_service": { - "status": 1, - "other_platform_oauth_client": [] - }, - "ads_service": { - "status": 2 - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/development/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to android/app/src/development/res/mipmap-hdpi/ic_launcher.png diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/development/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to android/app/src/development/res/mipmap-mdpi/ic_launcher.png diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/development/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to android/app/src/development/res/mipmap-xhdpi/ic_launcher.png diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/development/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to android/app/src/development/res/mipmap-xxhdpi/ic_launcher.png diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/development/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to android/app/src/development/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml index 304732f8..0622e1d0 100644 --- a/android/app/src/main/res/drawable/launch_background.xml +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -1,12 +1,10 @@ - - + + + - - + + + diff --git a/android/app/src/main/res/mipmap-hdpi/logo.png b/android/app/src/main/res/mipmap-hdpi/logo.png new file mode 100755 index 00000000..eb7120e4 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/logo.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/pattern.png b/android/app/src/main/res/mipmap-hdpi/pattern.png new file mode 100755 index 00000000..7b6ce24e Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/pattern.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/logo.png b/android/app/src/main/res/mipmap-mdpi/logo.png new file mode 100755 index 00000000..e86d7df7 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/logo.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/pattern.png b/android/app/src/main/res/mipmap-mdpi/pattern.png new file mode 100755 index 00000000..25fc8c7e Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/pattern.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/logo.png b/android/app/src/main/res/mipmap-xhdpi/logo.png new file mode 100755 index 00000000..ad08d89c Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/logo.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/pattern.png b/android/app/src/main/res/mipmap-xhdpi/pattern.png new file mode 100755 index 00000000..a663a60e Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/pattern.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/logo.png b/android/app/src/main/res/mipmap-xxhdpi/logo.png new file mode 100755 index 00000000..ff71de7a Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/logo.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/pattern.png b/android/app/src/main/res/mipmap-xxhdpi/pattern.png new file mode 100755 index 00000000..77456c04 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/pattern.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/logo.png b/android/app/src/main/res/mipmap-xxxhdpi/logo.png new file mode 100755 index 00000000..0cd9c64b Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/logo.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/pattern.png b/android/app/src/main/res/mipmap-xxxhdpi/pattern.png new file mode 100755 index 00000000..f62771f3 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/pattern.png differ diff --git a/android/app/src/production/res/mipmap-hdpi/ic_launcher.png b/android/app/src/production/res/mipmap-hdpi/ic_launcher.png new file mode 100755 index 00000000..cee7958b Binary files /dev/null and b/android/app/src/production/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/production/res/mipmap-mdpi/ic_launcher.png b/android/app/src/production/res/mipmap-mdpi/ic_launcher.png new file mode 100755 index 00000000..4b8ba76e Binary files /dev/null and b/android/app/src/production/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/production/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/production/res/mipmap-xhdpi/ic_launcher.png new file mode 100755 index 00000000..583ff5e1 Binary files /dev/null and b/android/app/src/production/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/production/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/production/res/mipmap-xxhdpi/ic_launcher.png new file mode 100755 index 00000000..8960b9ff Binary files /dev/null and b/android/app/src/production/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/production/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/production/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100755 index 00000000..a2c0fca8 Binary files /dev/null and b/android/app/src/production/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/assets/images/2.0x/google_logo.png b/assets/images/2.0x/google_logo.png new file mode 100755 index 00000000..a1a565e4 Binary files /dev/null and b/assets/images/2.0x/google_logo.png differ diff --git a/assets/images/2.0x/logo.png b/assets/images/2.0x/logo.png new file mode 100755 index 00000000..ad08d89c Binary files /dev/null and b/assets/images/2.0x/logo.png differ diff --git a/assets/images/2.0x/pattern.png b/assets/images/2.0x/pattern.png new file mode 100755 index 00000000..a663a60e Binary files /dev/null and b/assets/images/2.0x/pattern.png differ diff --git a/assets/images/3.0x/google_logo.png b/assets/images/3.0x/google_logo.png new file mode 100755 index 00000000..891fdb33 Binary files /dev/null and b/assets/images/3.0x/google_logo.png differ diff --git a/assets/images/3.0x/logo.png b/assets/images/3.0x/logo.png new file mode 100755 index 00000000..ff71de7a Binary files /dev/null and b/assets/images/3.0x/logo.png differ diff --git a/assets/images/3.0x/pattern.png b/assets/images/3.0x/pattern.png new file mode 100755 index 00000000..77456c04 Binary files /dev/null and b/assets/images/3.0x/pattern.png differ diff --git a/assets/images/google_logo.png b/assets/images/google_logo.png new file mode 100755 index 00000000..0569d132 Binary files /dev/null and b/assets/images/google_logo.png differ diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100755 index 00000000..e86d7df7 Binary files /dev/null and b/assets/images/logo.png differ diff --git a/assets/images/pattern.png b/assets/images/pattern.png new file mode 100755 index 00000000..25fc8c7e Binary files /dev/null and b/assets/images/pattern.png differ diff --git a/functions/functions/src/fn/auth.ts b/functions/functions/src/fn/auth.ts new file mode 100644 index 00000000..0395d7d3 --- /dev/null +++ b/functions/functions/src/fn/auth.ts @@ -0,0 +1,52 @@ +import { firestore as Firestore } from "firebase-admin"; +import { auth, EventContext } from "firebase-functions"; +import { ChangeState } from "../utils"; + +export function _onAuth(onCreate: ChangeState) { + return async (user: auth.UserRecord, context: EventContext) => { + const db = Firestore(); + const batch = db.batch(); + + const account = db.collection("accounts").doc(user.uid); + batch.set(account, { + storeName: user.displayName, + uid: user.uid, + email: user.email, + displayName: user.displayName, + phoneNumber: user.phoneNumber, + photoURL: user.photoURL + }); + + const stats = db.collection("stats").doc(user.uid); + batch.set(stats, { + contacts: { + total: 0 + }, + gallery: { + total: 0 + }, + jobs: { + total: 0, + pending: 0, + completed: 0 + }, + payments: { + total: 0, + pending: 0, + completed: 0 + } + }); + + // Commit the batch + return batch.commit(); + }; +} + +export function onAuthCreate(onCreate: ChangeState) { + const store = auth.user(); + + // return onCreate == ChangeState.Create + // ? store.onCreate(_onAuth(onCreate)) + // : store.onDelete(_onAuth(onCreate)); + return store.onCreate(_onAuth(onCreate)); +} diff --git a/functions/functions/src/fn/stats_contacts.ts b/functions/functions/src/fn/stats_contacts.ts index b9260523..e885744c 100644 --- a/functions/functions/src/fn/stats_contacts.ts +++ b/functions/functions/src/fn/stats_contacts.ts @@ -1,15 +1,16 @@ import { firestore as Firestore } from "firebase-admin"; import { EventContext, firestore } from "firebase-functions"; import { isArray } from "util"; +import { ChangeState } from "../utils"; -export function _onStatsContacts(onCreate: boolean) { +export function _onStatsContacts(onCreate: ChangeState) { return async ( snapshot: firestore.DocumentSnapshot, context: EventContext ) => { const db = Firestore(); - const stats = db.doc("stats/current"); const contact = snapshot.data(); + const stats = db.doc(`stats/${contact.userID}`); const statsSnap = await stats.get(); @@ -25,10 +26,10 @@ export function _onStatsContacts(onCreate: boolean) { }; } -export function onStatsContacts(onCreate = true) { +export function onStatsContacts(onCreate: ChangeState) { const store = firestore.document("contacts/{contactId}"); - return onCreate + return onCreate === ChangeState.Created ? store.onCreate(_onStatsContacts(onCreate)) : store.onDelete(_onStatsContacts(onCreate)); } diff --git a/functions/functions/src/fn/stats_jobs.ts b/functions/functions/src/fn/stats_jobs.ts index cffba8bc..6c02ee36 100644 --- a/functions/functions/src/fn/stats_jobs.ts +++ b/functions/functions/src/fn/stats_jobs.ts @@ -1,14 +1,35 @@ import * as admin from "firebase-admin"; import { Change, EventContext, firestore } from "firebase-functions"; +import { isArray } from "util"; +import { ChangeState } from "../utils"; -async function _onStatsJob( +// TODO +// Maybe not the best way to go about this +async function _onStatsJobUpdate( change: Change, context: EventContext +) { + const jobs = change.before.exists ? change.before : change.after; + + return _onStatsJob(jobs, context); +} + +async function _onStatsJob( + snapshot: firestore.DocumentSnapshot, + context: EventContext ) { const db = admin.firestore(); - const stats = db.doc("stats/current"); + const _data = snapshot.data(); + + // Get user ID from Job + const userID = isArray(_data) ? _data[0].userID : _data.userID; + + const stats = db.doc(`stats/${userID}`); - const jobsSnap = await db.collection("jobs").get(); + const jobsSnap = await db + .collection("jobs") + .where("userID", "==", userID) + .get(); let completedJob = 0, pendingJob = 0, @@ -46,6 +67,14 @@ async function _onStatsJob( }); } -export const onStatsJob = firestore - .document("jobs/{jobId}") - .onWrite(_onStatsJob); +export function onStatsJob(change: ChangeState) { + const store = firestore.document("jobs/{jobId}"); + + if (change === ChangeState.Created) { + return store.onCreate(_onStatsJob); + } else if (change === ChangeState.Updated) { + return store.onUpdate(_onStatsJobUpdate); + } else { + return store.onDelete(_onStatsJob); + } +} diff --git a/functions/functions/src/index.ts b/functions/functions/src/index.ts index 3e729352..49c9f2ce 100644 --- a/functions/functions/src/index.ts +++ b/functions/functions/src/index.ts @@ -1,21 +1,29 @@ import { initializeApp } from "firebase-admin"; import { config } from "firebase-functions"; +import { onAuthCreate } from "./fn/auth"; import { onContactStats } from "./fn/contact_stats"; import { onJobPayments } from "./fn/job_payments"; import { onPaymentGallery } from "./fn/payment_gallery"; import { onStatsContacts } from "./fn/stats_contacts"; import { onStatsJob } from "./fn/stats_jobs"; +import { ChangeState } from "./utils"; initializeApp(config().firebase); +export const AuthCreate = onAuthCreate(ChangeState.Created); + export const PaymentGallery = onPaymentGallery; export const JobPayments = onJobPayments; -export const StatsJobs = onStatsJob; +export const StatsJobsCreate = onStatsJob(ChangeState.Created); + +export const StatsJobsUpdate = onStatsJob(ChangeState.Updated); + +export const StatsJobsDelete = onStatsJob(ChangeState.Deleted); -export const StatsContactsCreate = onStatsContacts(); +export const StatsContactsCreate = onStatsContacts(ChangeState.Created); -export const StatsContactsDelete = onStatsContacts(false); +export const StatsContactsDelete = onStatsContacts(ChangeState.Deleted); export const ContactStats = onContactStats; diff --git a/functions/functions/src/utils.ts b/functions/functions/src/utils.ts new file mode 100644 index 00000000..fdee0c24 --- /dev/null +++ b/functions/functions/src/utils.ts @@ -0,0 +1,5 @@ +export enum ChangeState { + Created, + Updated, + Deleted +} diff --git a/lib/main.dart b/lib/main.dart index 793360f5..31bf05b3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,8 @@ -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:redux/redux.dart'; -import 'package:tailor_made/pages/homepage/homepage.dart'; -import 'package:tailor_made/redux/actions/main.dart'; +import 'package:tailor_made/pages/splash/splash.dart'; import 'package:tailor_made/redux/main.dart'; import 'package:tailor_made/redux/states/main.dart'; import 'package:tailor_made/utils/tm_fonts.dart'; @@ -12,10 +11,7 @@ import 'package:tailor_made/utils/tm_strings.dart'; import 'package:tailor_made/utils/tm_theme.dart'; void main() { - FirebaseAuth.instance.signInAnonymously().then((r) { - print(r); - }); - + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark); return runApp(new TMApp()); } @@ -30,25 +26,16 @@ class TMApp extends StatelessWidget { debugShowCheckedModeBanner: false, title: TMStrings.appName, theme: new ThemeData( - accentColor: accentColor, - primarySwatch: primarySwatch, + accentColor: kAccentColor, + primarySwatch: kPrimarySwatch, fontFamily: TMFonts.raleway, ), onGenerateRoute: (RouteSettings settings) { return new TMNavigateRoute( - builder: (_) => TMTheme( - child: new StoreBuilder( - onInit: (store) => store.dispatch(new InitDataEvents()), - onDispose: (store) => store.dispatch(new DisposeDataEvents()), - builder: (BuildContext context, store) { - return HomePage(); - }, - ), - ), + builder: (_) => TMTheme(child: SplashPage(isColdStart: true)), settings: settings, ); }, - // home: TMTheme(child: HomePage()), ), ); } diff --git a/lib/models/account.dart b/lib/models/account.dart new file mode 100644 index 00000000..9de0a394 --- /dev/null +++ b/lib/models/account.dart @@ -0,0 +1,48 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/foundation.dart'; +import 'package:tailor_made/models/main.dart'; + +class AccountModel extends Model { + String uid; + String storeName; + String email; + String displayName; + int phoneNumber; + String photoURL; + + AccountModel({ + @required this.uid, + @required this.storeName, + @required this.email, + @required this.displayName, + @required this.phoneNumber, + @required this.photoURL, + }); + + factory AccountModel.fromJson(Map json) { + assert(json != null); + return new AccountModel( + uid: json['uid'], + storeName: json['storeName'], + email: json['email'], + displayName: json['displayName'], + phoneNumber: int.tryParse(json['phoneNumber'].toString()), + photoURL: json['photoURL'], + ); + } + + factory AccountModel.fromDoc(DocumentSnapshot doc) { + return AccountModel.fromJson(doc.data)..reference = doc.reference; + } + + toMap() { + return { + "uid": uid, + "storeName": storeName, + "email": email, + "displayName": displayName, + "phoneNumber": phoneNumber, + "photoURL": photoURL, + }; + } +} diff --git a/lib/models/contact.dart b/lib/models/contact.dart index b763e6fa..0572a6c0 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -1,10 +1,12 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:tailor_made/models/main.dart'; import 'package:tailor_made/models/measure.dart'; +import 'package:tailor_made/services/auth.dart'; import 'package:tailor_made/utils/tm_uuid.dart'; class ContactModel extends Model { String id; + String userID; String fullname; String phone; String location; @@ -16,6 +18,7 @@ class ContactModel extends Model { ContactModel({ String id, + String userID, this.fullname, this.phone, this.location, @@ -26,6 +29,7 @@ class ContactModel extends Model { this.pendingJobs = 0, }) : id = id ?? uuid(), createdAt = createdAt ?? DateTime.now(), + userID = userID ?? Auth.getUser.uid, measurements = measurements != null && measurements.isNotEmpty ? measurements : createDefaultMeasures(); factory ContactModel.fromJson(Map json) { @@ -38,6 +42,7 @@ class ContactModel extends Model { } return new ContactModel( id: json['id'], + userID: json['userID'], fullname: json['fullname'], phone: json['phone'], location: json['location'], @@ -56,6 +61,7 @@ class ContactModel extends Model { toMap() { return { "id": id, + "userID": userID, "fullname": fullname, "phone": phone, "location": location, diff --git a/lib/models/image.dart b/lib/models/image.dart index 41fc3a0c..7edc6dc5 100644 --- a/lib/models/image.dart +++ b/lib/models/image.dart @@ -1,9 +1,11 @@ import 'package:flutter/foundation.dart'; import 'package:tailor_made/models/main.dart'; +import 'package:tailor_made/services/auth.dart'; import 'package:tailor_made/utils/tm_uuid.dart'; class ImageModel extends Model { String id; + String userID; String contactID; String jobID; String path; @@ -11,19 +13,22 @@ class ImageModel extends Model { DateTime createdAt; ImageModel({ - id, + String id, + String userID, @required this.contactID, @required this.jobID, @required this.path, @required this.src, - createdAt, + DateTime createdAt, }) : id = id ?? uuid(), + userID = userID ?? Auth.getUser.uid, createdAt = createdAt ?? DateTime.now(); factory ImageModel.fromJson(Map json) { assert(json != null); return new ImageModel( id: json['id'], + userID: json['userID'], contactID: json['contactID'], jobID: json['jobID'], path: json['path'], @@ -36,6 +41,7 @@ class ImageModel extends Model { toMap() { return { "id": id, + "userID": userID, "contactID": contactID, "jobID": jobID, "path": path, diff --git a/lib/models/job.dart b/lib/models/job.dart index e963a56c..f482790c 100644 --- a/lib/models/job.dart +++ b/lib/models/job.dart @@ -4,10 +4,12 @@ import 'package:tailor_made/models/image.dart'; import 'package:tailor_made/models/main.dart'; import 'package:tailor_made/models/measure.dart'; import 'package:tailor_made/models/payment.dart'; +import 'package:tailor_made/services/auth.dart'; import 'package:tailor_made/utils/tm_uuid.dart'; class JobModel extends Model { String id; + String userID; String contactID; String name; double price; @@ -21,7 +23,8 @@ class JobModel extends Model { DateTime createdAt; JobModel({ - id, + String id, + String userID, @required this.contactID, this.name, this.price, @@ -32,8 +35,9 @@ class JobModel extends Model { this.measurements = const [], this.payments = const [], this.isComplete = false, - createdAt, + DateTime createdAt, }) : id = id ?? uuid(), + userID = userID ?? Auth.getUser.uid, createdAt = createdAt ?? DateTime.now(); factory JobModel.fromJson(Map json) { @@ -58,6 +62,7 @@ class JobModel extends Model { } return new JobModel( id: json['id'], + userID: json['userID'], contactID: json['contactID'], name: json['name'], price: double.tryParse(json['price'].toString()), @@ -80,6 +85,7 @@ class JobModel extends Model { toMap() { return { "id": id, + "userID": userID, "contactID": contactID, "name": name, "price": price, diff --git a/lib/models/payment.dart b/lib/models/payment.dart index 245c318c..884c94ba 100644 --- a/lib/models/payment.dart +++ b/lib/models/payment.dart @@ -1,9 +1,11 @@ import 'package:flutter/foundation.dart'; import 'package:tailor_made/models/main.dart'; +import 'package:tailor_made/services/auth.dart'; import 'package:tailor_made/utils/tm_uuid.dart'; class PaymentModel extends Model { String id; + String userID; String contactID; String jobID; double price; @@ -11,19 +13,22 @@ class PaymentModel extends Model { DateTime createdAt; PaymentModel({ - id, + String id, + String userID, @required this.contactID, @required this.jobID, @required this.price, @required this.notes, - createdAt, + DateTime createdAt, }) : id = id ?? uuid(), + userID = userID ?? Auth.getUser.uid, createdAt = createdAt ?? DateTime.now(); factory PaymentModel.fromJson(Map json) { assert(json != null); return new PaymentModel( id: json['id'], + userID: json['userID'], contactID: json['contactID'], jobID: json['jobID'], price: double.tryParse(json['price'].toString()), @@ -36,6 +41,7 @@ class PaymentModel extends Model { toMap() { return { "id": id, + "userID": userID, "contactID": contactID, "jobID": jobID, "price": price, diff --git a/lib/models/stats.dart b/lib/models/stats.dart index b0160f27..4d336921 100644 --- a/lib/models/stats.dart +++ b/lib/models/stats.dart @@ -24,6 +24,26 @@ class PaymentStatsModel { } } +// KEEP +// { +// "contacts": { +// "total": 0 +// }, +// "gallery": { +// "total": 0 +// }, +// "jobs": { +// "total": 0, +// "pending": 0, +// "completed": 0, +// }, +// "payments": { +// "total": 0, +// "pending": 0, +// "completed": 0, +// }, +// } + class StatsModel { DetailStatsModel jobs; DetailStatsModel contacts; diff --git a/lib/pages/contacts/contact.dart b/lib/pages/contacts/contact.dart index bddf056f..03b4951d 100644 --- a/lib/pages/contacts/contact.dart +++ b/lib/pages/contacts/contact.dart @@ -39,12 +39,13 @@ class _ContactState extends State { new SliverOverlapAbsorber( handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), child: new SliverAppBar( - backgroundColor: accentColor, + backgroundColor: kAccentColor, automaticallyImplyLeading: false, title: new ContactAppBar(contact: widget.contact), pinned: true, titleSpacing: 0.0, centerTitle: false, + brightness: Brightness.dark, expandedHeight: 0.0, forceElevated: true, bottom: tabTitles(), diff --git a/lib/pages/contacts/contacts_create.dart b/lib/pages/contacts/contacts_create.dart index dbc1bae0..30547ac4 100644 --- a/lib/pages/contacts/contacts_create.dart +++ b/lib/pages/contacts/contacts_create.dart @@ -44,7 +44,7 @@ class _ContactsCreatePageState extends State with SnackBarPr IconButton( icon: Icon( Icons.content_cut, - color: titleBaseColor, + color: kTitleBaseColor, ), onPressed: () => TMNavigate( context, diff --git a/lib/pages/contacts/ui/contact_appbar.dart b/lib/pages/contacts/ui/contact_appbar.dart index aa25f0d6..4ef25b72 100644 --- a/lib/pages/contacts/ui/contact_appbar.dart +++ b/lib/pages/contacts/ui/contact_appbar.dart @@ -157,30 +157,18 @@ class ContactAppBarState extends State { style: TextStyle( fontSize: 18.0, color: Colors.white, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, + ), + ), + Text( + widget.contact.location, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 13.0, + color: Colors.white, ), ), - isAtTop || (widget.contact.pendingJobs < 1) - ? Container() - : Text.rich( - TextSpan( - children: [ - TextSpan( - text: widget.contact.pendingJobs.toString(), - style: TextStyle(fontWeight: FontWeight.w600), - ), - TextSpan( - text: " pending", - ), - ], - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 13.0, - color: Colors.white, - ), - ), ], ); } @@ -192,7 +180,6 @@ class ContactAppBarState extends State { child: new Icon(icon, color: Colors.white), onTap: onTap, radius: 20.0, - splashColor: accentColor.withOpacity(.25), ), ); } diff --git a/lib/pages/contacts/ui/contact_form.dart b/lib/pages/contacts/ui/contact_form.dart index c15673b7..b6f7acf4 100644 --- a/lib/pages/contacts/ui/contact_form.dart +++ b/lib/pages/contacts/ui/contact_form.dart @@ -103,7 +103,7 @@ class ContactFormState extends State with SnackBarProvider { ), SizedBox(height: 32.0), RaisedButton( - color: accentColor, + color: kAccentColor, shape: StadiumBorder(), onPressed: _handleSubmit, child: Text( @@ -125,12 +125,12 @@ class ContactFormState extends State with SnackBarProvider { padding: EdgeInsets.all(4.0), decoration: BoxDecoration( shape: BoxShape.circle, - border: Border.all(color: primarySwatch.withOpacity(.5), width: 2.0), + border: Border.all(color: kPrimarySwatch.shade200, width: 2.0), ), child: DecoratedBox( decoration: BoxDecoration( shape: BoxShape.circle, - color: primarySwatch.withOpacity(.2), + color: kPrimarySwatch.shade100, ), child: Center( child: GestureDetector( @@ -143,7 +143,7 @@ class ContactFormState extends State with SnackBarProvider { contact.imageUrl != null ? CircleAvatar( backgroundImage: NetworkImage(contact.imageUrl), - backgroundColor: primarySwatch.withOpacity(.2), + backgroundColor: kPrimarySwatch.shade100, ) : SizedBox(), isLoading diff --git a/lib/pages/contacts/ui/contact_measure.dart b/lib/pages/contacts/ui/contact_measure.dart index 1aa126f6..1b23cd4b 100644 --- a/lib/pages/contacts/ui/contact_measure.dart +++ b/lib/pages/contacts/ui/contact_measure.dart @@ -35,7 +35,7 @@ class _ContactMeasureState extends State with SnackBarProvider { children.add( Padding( child: RaisedButton( - color: accentColor, + color: kAccentColor, shape: StadiumBorder(), child: Text( "FINISH", @@ -59,7 +59,7 @@ class _ContactMeasureState extends State with SnackBarProvider { IconButton( icon: Icon( Icons.remove_red_eye, - color: titleBaseColor, + color: kTitleBaseColor, ), onPressed: () => TMNavigate( context, @@ -95,8 +95,8 @@ class _ContactMeasureState extends State with SnackBarProvider { child: new Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(title.toUpperCase(), style: ralewayLight(12.0, textBaseColor.shade800)), - Text(trailing, style: ralewayLight(12.0, textBaseColor.shade800)), + Text(title.toUpperCase(), style: ralewayLight(12.0, kTextBaseColor.shade800)), + Text(trailing, style: ralewayLight(12.0, kTextBaseColor.shade800)), ], ), ); diff --git a/lib/pages/contacts/ui/contacts_list_item.dart b/lib/pages/contacts/ui/contacts_list_item.dart index 649c06d6..4d6a5684 100644 --- a/lib/pages/contacts/ui/contacts_list_item.dart +++ b/lib/pages/contacts/ui/contacts_list_item.dart @@ -34,7 +34,7 @@ class ContactsListItem extends StatelessWidget { tag: contact.id, child: new CircleAvatar( radius: 24.0, - backgroundColor: theme.accentColor.withOpacity(.5), + backgroundColor: theme.primaryColor, backgroundImage: contact.imageUrl != null ? CachedNetworkImageProvider(contact.imageUrl) : null, child: Stack( children: [ @@ -45,7 +45,7 @@ class ContactsListItem extends StatelessWidget { width: 15.5, height: 15.5, decoration: new BoxDecoration( - color: accentColor, + color: theme.accentColor, border: Border.all( color: Colors.white, style: BorderStyle.solid, @@ -93,7 +93,7 @@ class ContactsListItem extends StatelessWidget { onTap: onTapContact ?? () => TMNavigate(context, ContactPage(contact: contact)), leading: avatar(), title: title, - subtitle: Text(pending >= 1 ? "$pending pending" : "No pending wears", style: TextStyle(fontSize: 14.0, color: textBaseColor)), + subtitle: Text(pending >= 1 ? "$pending pending" : "No pending wears", style: TextStyle(fontSize: 14.0, color: kTextBaseColor)), trailing: showActions ? icons : null, ); } diff --git a/lib/pages/gallery/gallery.dart b/lib/pages/gallery/gallery.dart index 0262f926..ebd0d1bb 100644 --- a/lib/pages/gallery/gallery.dart +++ b/lib/pages/gallery/gallery.dart @@ -50,7 +50,7 @@ class GalleryPageState extends State { "${images.length} Photos", style: TextStyle( fontSize: 11.0, - color: textBaseColor, + color: kTextBaseColor, ), ) : SizedBox(), diff --git a/lib/pages/homepage/homepage.dart b/lib/pages/homepage/homepage.dart index 457b7d4c..60e3f08b 100644 --- a/lib/pages/homepage/homepage.dart +++ b/lib/pages/homepage/homepage.dart @@ -10,12 +10,15 @@ import 'package:tailor_made/pages/homepage/ui/helpers.dart'; import 'package:tailor_made/pages/homepage/ui/stats.dart'; import 'package:tailor_made/pages/homepage/ui/top_row.dart'; import 'package:tailor_made/pages/jobs/jobs_create.dart'; +import 'package:tailor_made/pages/splash/splash.dart'; import 'package:tailor_made/redux/actions/main.dart'; import 'package:tailor_made/redux/states/main.dart'; import 'package:tailor_made/redux/view_models/contacts.dart'; +import 'package:tailor_made/services/auth.dart'; import 'package:tailor_made/services/cloudstore.dart'; import 'package:tailor_made/ui/tm_loading_spinner.dart'; import 'package:tailor_made/utils/tm_colors.dart'; +import 'package:tailor_made/utils/tm_images.dart'; import 'package:tailor_made/utils/tm_navigate.dart'; import 'package:tailor_made/utils/tm_theme.dart'; @@ -56,113 +59,146 @@ class _HomePageState extends State with SingleTickerProviderStateMixin Widget build(BuildContext context) { final TMTheme theme = TMTheme.of(context); - onTapCreate(List contacts) { - return () async { - CreateOptions result = await showDialog( - context: context, - builder: (BuildContext context) { - return new SimpleDialog( - title: const Text('Select action', style: const TextStyle(fontSize: 14.0)), - children: [ - new SimpleDialogOption( - onPressed: () { - Navigator.pop(context, CreateOptions.clients); - }, - child: TMListTile( - color: Colors.orangeAccent, - icon: Icons.supervisor_account, - title: "Clients", - ), - ), - new SimpleDialogOption( - onPressed: () { - Navigator.pop(context, CreateOptions.jobs); - }, - child: TMListTile( - color: Colors.greenAccent.shade400, - icon: Icons.attach_money, - title: "Job", - ), - ), - ], - ); - }, - ); - switch (result) { - case CreateOptions.clients: - TMNavigate(context, ContactsCreatePage()); - break; - case CreateOptions.jobs: - TMNavigate(context, JobsCreatePage(contacts: contacts)); - break; - } - }; - } - return new Scaffold( backgroundColor: theme.scaffoldColor, - appBar: AppBar( - elevation: 0.0, - backgroundColor: theme.scaffoldColor, - brightness: Brightness.light, - // actions: [ - // new IconButton( - // icon: new Icon( - // Icons.person, - // color: theme.appBarColor, - // ), - // onPressed: () => TMNavigate(context, AccountsPage()), - // ) - // ], - ), - body: StreamBuilder( - stream: Cloudstore.stats.snapshots(), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return Center( - child: loadingSpinner(), - ); - } + body: Stack( + fit: StackFit.expand, + children: [ + Opacity( + opacity: .5, + child: DecoratedBox( + decoration: BoxDecoration( + image: DecorationImage( + image: TMImages.pattern, + fit: BoxFit.cover, + ), + ), + ), + ), + new StoreBuilder( + onInit: (store) => store.dispatch(new InitDataEvents()), + onDispose: (store) => store.dispatch(new DisposeDataEvents()), + builder: (BuildContext context, store) { + return StreamBuilder( + stream: Cloudstore.stats.snapshots(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Center( + child: loadingSpinner(), + ); + } - final DocumentSnapshot _data = snapshot.data; - final stats = StatsModel.fromJson(_data.data); + final DocumentSnapshot _data = snapshot.data; + final stats = StatsModel.fromJson(_data.data); - return new SafeArea( - top: false, - child: new Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - HeaderWidget(), - StatsWidget(stats: stats), - TopRowWidget(stats: stats), - BottomRowWidget(stats: stats), - new StoreConnector( - converter: (store) => ContactsViewModel(store), - onInit: (store) => store.dispatch(new InitDataEvents()), - onDispose: (store) => store.dispatch(new DisposeDataEvents()), - builder: (BuildContext context, ContactsViewModel vm) { - return new FlatButton( - shape: RoundedRectangleBorder(), - padding: EdgeInsets.only(top: 16.0, bottom: 16.0), - color: accentColor, - child: ScaleTransition( - scale: new Tween(begin: 0.95, end: 1.025).animate(controller), - alignment: FractionalOffset.center, - child: new Text( - "TAP TO CREATE", - style: ralewayBold(14.0, TMColors.white).copyWith(letterSpacing: 1.25), - ), - ), - onPressed: onTapCreate(vm.contacts), - ); - }, + return new SafeArea( + top: false, + child: new Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded(child: HeaderWidget()), + StatsWidget(stats: stats), + TopRowWidget(stats: stats), + BottomRowWidget(stats: stats), + _buildCreateBtn(), + ], + ), + ); + }, + ); + }, + ), + Align( + alignment: Alignment.topRight, + child: SafeArea( + child: IconButton( + icon: new Icon( + Icons.power_settings_new, + color: theme.appBarColor, ), - ], + // onPressed: () => TMNavigate(context, AccountsPage()), + onPressed: () async { + await Auth.signOutWithGoogle(); + Navigator.pushReplacement( + context, + TMNavigate.fadeIn( + new SplashPage(isColdStart: false), + ), + ); + }, + ), ), - ); - }, + ), + ], ), ); } + + StoreConnector _buildCreateBtn() { + return new StoreConnector( + converter: (store) => ContactsViewModel(store), + onInit: (store) => store.dispatch(new InitDataEvents()), + onDispose: (store) => store.dispatch(new DisposeDataEvents()), + builder: (BuildContext context, ContactsViewModel vm) { + return new FlatButton( + shape: RoundedRectangleBorder(), + padding: EdgeInsets.only(top: 16.0, bottom: 16.0), + color: kAccentColor, + child: ScaleTransition( + scale: new Tween(begin: 0.95, end: 1.025).animate(controller), + alignment: FractionalOffset.center, + child: new Text( + "TAP TO CREATE", + style: ralewayBold(14.0, TMColors.white).copyWith(letterSpacing: 1.25), + ), + ), + onPressed: onTapCreate(vm.contacts), + ); + }, + ); + } + + onTapCreate(List contacts) { + return () async { + CreateOptions result = await showDialog( + context: context, + builder: (BuildContext context) { + return new SimpleDialog( + title: const Text('Select action', style: const TextStyle(fontSize: 14.0)), + children: [ + new SimpleDialogOption( + onPressed: () { + Navigator.pop(context, CreateOptions.clients); + }, + child: TMListTile( + color: Colors.orangeAccent, + icon: Icons.supervisor_account, + title: "Clients", + ), + ), + new SimpleDialogOption( + onPressed: () { + Navigator.pop(context, CreateOptions.jobs); + }, + child: TMListTile( + color: Colors.greenAccent.shade400, + icon: Icons.attach_money, + title: "Job", + ), + ), + ], + ); + }, + ); + switch (result) { + case CreateOptions.clients: + TMNavigate(context, ContactsCreatePage()); + break; + case CreateOptions.jobs: + TMNavigate(context, JobsCreatePage(contacts: contacts)); + break; + } + }; + } } diff --git a/lib/pages/homepage/ui/header.dart b/lib/pages/homepage/ui/header.dart index eba592cc..6ff2918d 100644 --- a/lib/pages/homepage/ui/header.dart +++ b/lib/pages/homepage/ui/header.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:tailor_made/models/account.dart'; +import 'package:tailor_made/services/cloudstore.dart'; +import 'package:tailor_made/ui/tm_loading_spinner.dart'; import 'package:tailor_made/utils/tm_format_date.dart'; import 'package:tailor_made/utils/tm_theme.dart'; @@ -7,51 +10,62 @@ class HeaderWidget extends StatelessWidget { Widget build(BuildContext context) { final TMTheme theme = TMTheme.of(context); - return new Expanded( - child: Container( - padding: EdgeInsets.fromLTRB(20.0, 35.0, 20.0, 40.0), - width: double.infinity, - decoration: new BoxDecoration( - border: new Border( - bottom: TMBorderSide(), + return new StreamBuilder( + stream: Cloudstore.account.snapshots(), + builder: (BuildContext context, snapshot) { + if (!snapshot.hasData) { + return Center( + child: loadingSpinner(), + ); + } + + final account = AccountModel.fromDoc(snapshot.data); + + return Container( + padding: EdgeInsets.fromLTRB(20.0, 35.0, 20.0, 40.0), + width: double.infinity, + decoration: new BoxDecoration( + border: new Border( + bottom: TMBorderSide(), + ), ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - new Text( - "Hello", - style: new TextStyle( - color: theme.textColor, - fontSize: 35.0, - fontWeight: FontWeight.w200, - letterSpacing: 2.5, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + new Text( + "Hello", + style: new TextStyle( + color: theme.textColor, + fontSize: 32.0, + fontWeight: FontWeight.w200, + letterSpacing: 2.5, + ), ), - ), - new Text( - "Mikun Sews", - style: new TextStyle( - color: theme.textColor, - fontSize: 50.0, - fontWeight: FontWeight.w300, - height: 1.05, + new Text( + account.displayName.split(" ").first, + style: new TextStyle( + color: theme.textColor, + fontSize: 52.0, + fontWeight: FontWeight.w300, + height: 1.15, + ), + maxLines: 1, + overflow: TextOverflow.fade, + softWrap: false, ), - maxLines: 1, - overflow: TextOverflow.fade, - softWrap: false, - ), - new Text( - formatDate(DateTime.now(), day: "EEEE", month: "MMMM"), - style: new TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.w300, - height: 1.5, + new Text( + formatDate(DateTime.now(), day: "EEEE", month: "MMMM"), + style: new TextStyle( + fontSize: 14.0, + fontWeight: FontWeight.w300, + height: 1.75, + ), ), - ), - ], - ), - ), + ], + ), + ); + }, ); } } diff --git a/lib/pages/homepage/ui/stats.dart b/lib/pages/homepage/ui/stats.dart index e69d7887..83e8c11f 100644 --- a/lib/pages/homepage/ui/stats.dart +++ b/lib/pages/homepage/ui/stats.dart @@ -30,7 +30,7 @@ class StatsWidget extends StatelessWidget { ), ), Container( - color: borderSideColor, + color: kBorderSideColor, width: 1.0, height: 40.0, margin: EdgeInsets.only(left: 0.0, right: 0.0), @@ -43,7 +43,7 @@ class StatsWidget extends StatelessWidget { ), ), Container( - color: borderSideColor, + color: kBorderSideColor, width: 1.0, height: 40.0, margin: EdgeInsets.only(left: 0.0, right: 0.0), diff --git a/lib/pages/jobs/job.dart b/lib/pages/jobs/job.dart index 067f839f..1b599c1a 100644 --- a/lib/pages/jobs/job.dart +++ b/lib/pages/jobs/job.dart @@ -46,19 +46,19 @@ class JobPageState extends State with SnackBarProvider { Widget build(BuildContext context) { final TMTheme theme = TMTheme.of(context); - return new Scaffold( - key: scaffoldKey, - backgroundColor: theme.scaffoldColor, - body: new StreamBuilder( - stream: job.reference.snapshots(), - builder: (BuildContext context, snapshot) { - if (!snapshot.hasData) { - return Center( - child: loadingSpinner(), - ); - } - job = JobModel.fromDoc(snapshot.data); - return new NestedScrollView( + return new StreamBuilder( + stream: job.reference.snapshots(), + builder: (BuildContext context, snapshot) { + if (!snapshot.hasData) { + return Center( + child: loadingSpinner(), + ); + } + job = JobModel.fromDoc(snapshot.data); + return new Scaffold( + key: scaffoldKey, + backgroundColor: theme.scaffoldColor, + body: new NestedScrollView( headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return [ SliverAppBar( @@ -88,13 +88,24 @@ class JobPageState extends State with SnackBarProvider { GalleryGrids(job: job), const SizedBox(height: 4.0), PaymentGrids(job: job), + const SizedBox(height: 32.0), ], ), ), ), - ); - }, - ), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, + floatingActionButton: FloatingActionButton.extended( + icon: new Icon( + job.isComplete ? Icons.undo : Icons.check, + ), + backgroundColor: job.isComplete ? Colors.white : kAccentColor, + foregroundColor: job.isComplete ? kAccentColor : Colors.white, + label: Text(job.isComplete ? "Undo Completed" : "Mark Completed"), + onPressed: onTapComplete, + ), + ); + }, ); } @@ -213,9 +224,7 @@ class JobPageState extends State with SnackBarProvider { contact.fullname, maxLines: 1, overflow: TextOverflow.ellipsis, - style: ralewayRegular(16.0, textColor).copyWith( - fontWeight: FontWeight.w500, - ), + style: ralewayBold(18.0, kTitleBaseColor), ), ), iconColor: textColor, @@ -230,10 +239,10 @@ class JobPageState extends State with SnackBarProvider { actions: [ IconButton( icon: new Icon( - job.isComplete ? Icons.check_box : Icons.check_box_outline_blank, - color: textBaseColor.shade900, + Icons.check, + color: job.isComplete ? kPrimaryColor : kTextBaseColor.shade400, ), - onPressed: onTapComplete, + onPressed: null, ) ], ); @@ -244,7 +253,7 @@ class JobPageState extends State with SnackBarProvider { void onTapComplete() async { var choice = await confirmDialog( context: context, - title: Text("Marking this job as complete?"), + title: Text("Are you sure?"), ); if (choice == null || choice == false) return; diff --git a/lib/pages/jobs/job_list_item.dart b/lib/pages/jobs/job_list_item.dart index 299e585f..406f60e5 100644 --- a/lib/pages/jobs/job_list_item.dart +++ b/lib/pages/jobs/job_list_item.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:tailor_made/pages/jobs/job.dart'; import 'package:tailor_made/models/job.dart'; +import 'package:tailor_made/pages/jobs/job.dart'; import 'package:tailor_made/utils/tm_format_naira.dart'; import 'package:tailor_made/utils/tm_months.dart'; import 'package:tailor_made/utils/tm_navigate.dart'; @@ -57,13 +57,13 @@ class JobListItem extends StatelessWidget { children: [ Text(job.name, style: TextStyle(fontSize: 16.0, color: theme.textColor, fontWeight: FontWeight.w600)), new SizedBox(height: 4.0), - Text(_price, style: TextStyle(fontSize: 14.0, color: textBaseColor)), + Text(_price, style: TextStyle(fontSize: 14.0, color: kTextBaseColor)), ], ), ), new Icon( Icons.check, - color: job.isComplete ? theme.accentColor : textBaseColor.shade300, + color: job.isComplete ? theme.primaryColor : kTextBaseColor.shade300, ), ], ), diff --git a/lib/pages/jobs/jobs_create.dart b/lib/pages/jobs/jobs_create.dart index 9dc20e74..610880b3 100644 --- a/lib/pages/jobs/jobs_create.dart +++ b/lib/pages/jobs/jobs_create.dart @@ -84,8 +84,8 @@ class _JobsCreatePageState extends State with SnackBarProvider { child: new Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(title.toUpperCase(), style: ralewayLight(12.0, textBaseColor.shade800)), - Text(trailing, style: ralewayLight(12.0, textBaseColor.shade800)), + Text(title.toUpperCase(), style: ralewayLight(12.0, kTextBaseColor.shade800)), + Text(trailing, style: ralewayLight(12.0, kTextBaseColor.shade800)), ], ), ); @@ -110,7 +110,7 @@ class _JobsCreatePageState extends State with SnackBarProvider { children.add( Padding( child: RaisedButton( - color: accentColor, + color: kAccentColor, shape: StadiumBorder(), child: Text( "FINISH", @@ -191,7 +191,7 @@ class _JobsCreatePageState extends State with SnackBarProvider { contact.fullname, maxLines: 1, overflow: TextOverflow.ellipsis, - style: ralewayRegular(18.0, theme.appBarColor), + style: ralewayBold(18.0, theme.appBarColor), ), subtitle: Text("${contact.totalJobs} Jobs", style: theme.smallTextStyle), actions: widget.contacts.isNotEmpty @@ -249,7 +249,7 @@ class _JobsCreatePageState extends State with SnackBarProvider { hintStyle: TextStyle(fontSize: 14.0), border: UnderlineInputBorder( borderSide: BorderSide( - color: borderSideColor, + color: kBorderSideColor, width: 0.0, style: BorderStyle.solid, ), @@ -273,7 +273,7 @@ class _JobsCreatePageState extends State with SnackBarProvider { child: Icon( Icons.add_a_photo, size: 24.0, - color: textBaseColor.withOpacity(.35), + color: kTextBaseColor.withOpacity(.35), ), ), ), @@ -359,7 +359,7 @@ class _JobsCreatePageState extends State with SnackBarProvider { // border: InputBorder.none, border: UnderlineInputBorder( borderSide: BorderSide( - color: borderSideColor, + color: kBorderSideColor, width: 1.0, style: BorderStyle.solid, ), @@ -384,7 +384,7 @@ class _JobsCreatePageState extends State with SnackBarProvider { hintStyle: TextStyle(fontSize: 14.0), border: UnderlineInputBorder( borderSide: BorderSide( - color: borderSideColor, + color: kBorderSideColor, width: 0.0, style: BorderStyle.solid, ), diff --git a/lib/pages/jobs/ui/gallery_grids.dart b/lib/pages/jobs/ui/gallery_grids.dart index c67d509b..599f1b86 100644 --- a/lib/pages/jobs/ui/gallery_grids.dart +++ b/lib/pages/jobs/ui/gallery_grids.dart @@ -86,7 +86,7 @@ class GalleryGridsState extends State { const SizedBox(width: 16.0), Expanded(child: Text("GALLERY", style: ralewayRegular(12.0, Colors.black87))), CupertinoButton( - child: Text("SHOW ALL", style: ralewayRegular(11.0, textBaseColor)), + child: Text("SHOW ALL", style: ralewayRegular(11.0, kTextBaseColor)), onPressed: () => TMNavigate(context, GalleryPage(images: widget.job.images), fullscreenDialog: true), ), ], @@ -116,7 +116,7 @@ class GalleryGridsState extends State { child: Icon( Icons.add_a_photo, size: 24.0, - color: textBaseColor.withOpacity(.35), + color: kTextBaseColor.withOpacity(.35), ), ), ), diff --git a/lib/pages/jobs/ui/measure_create_items.dart b/lib/pages/jobs/ui/measure_create_items.dart index 133d8993..3bf43896 100644 --- a/lib/pages/jobs/ui/measure_create_items.dart +++ b/lib/pages/jobs/ui/measure_create_items.dart @@ -55,7 +55,7 @@ class JobMeasureBlock extends StatelessWidget { decoration: BoxDecoration( border: Border( bottom: BorderSide( - color: borderSideColor, + color: kBorderSideColor, width: 1.0, style: BorderStyle.solid, ), @@ -73,12 +73,12 @@ class JobMeasureBlock extends StatelessWidget { decoration: BoxDecoration( border: Border( bottom: BorderSide( - color: borderSideColor, + color: kBorderSideColor, width: 1.0, style: removeBorder ? BorderStyle.none : BorderStyle.solid, ), right: BorderSide( - color: borderSideColor, + color: kBorderSideColor, width: 1.0, style: index % 2 == 0 ? BorderStyle.solid : BorderStyle.none, ), diff --git a/lib/pages/jobs/ui/measure_list_item.dart b/lib/pages/jobs/ui/measure_list_item.dart index 87ab3fc2..cb8047e1 100644 --- a/lib/pages/jobs/ui/measure_list_item.dart +++ b/lib/pages/jobs/ui/measure_list_item.dart @@ -20,7 +20,7 @@ class MeasureListItem extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 1.0), child: Text(item.type.toLowerCase(), style: ralewayRegular(10.0, Colors.white)), decoration: BoxDecoration( - color: accentColor, + color: kAccentColor, borderRadius: BorderRadius.circular(10.0), ), ), @@ -31,12 +31,12 @@ class MeasureListItem extends StatelessWidget { if (item.value != null && item.value > 0) { children.addAll([ - Text("${item.value} ", style: ralewayRegular(16.0, accentColor)), + Text("${item.value} ", style: ralewayRegular(16.0, kAccentColor)), SizedBox(width: 2.0), - Text(item.unit, style: ralewayLight(12.0, titleBaseColor)), + Text(item.unit, style: ralewayLight(12.0, kTitleBaseColor)), ]); } else { - children.add(Text("N/A", style: ralewayLight(12.0, titleBaseColor))); + children.add(Text("N/A", style: ralewayLight(12.0, kTitleBaseColor))); } return new Container( diff --git a/lib/pages/jobs/ui/measure_lists.dart b/lib/pages/jobs/ui/measure_lists.dart index 6d2e1dc6..0c3f6356 100644 --- a/lib/pages/jobs/ui/measure_lists.dart +++ b/lib/pages/jobs/ui/measure_lists.dart @@ -26,7 +26,7 @@ class MeasureLists extends StatelessWidget { const SizedBox(width: 16.0), Expanded(child: Text("MEASUREMENTS", style: ralewayRegular(12.0, Colors.black87))), CupertinoButton( - child: Text("SHOW ALL", style: ralewayRegular(11.0, textBaseColor)), + child: Text("SHOW ALL", style: ralewayRegular(11.0, kTextBaseColor)), onPressed: () => TMNavigate(context, MeasuresPage(measurements: measurements), fullscreenDialog: true), ), ], diff --git a/lib/pages/jobs/ui/payment_grid_item.dart b/lib/pages/jobs/ui/payment_grid_item.dart index b6839b97..2fbe8c31 100644 --- a/lib/pages/jobs/ui/payment_grid_item.dart +++ b/lib/pages/jobs/ui/payment_grid_item.dart @@ -30,7 +30,7 @@ class PaymentGridItem extends StatelessWidget { child: new Material( elevation: 1.0, borderRadius: BorderRadius.circular(5.0), - color: primarySwatch.shade300, + color: kPrimaryColor, child: new InkWell( onTap: () => TMNavigate(context, PaymentPage(payment: payment), fullscreenDialog: true), child: Padding( diff --git a/lib/pages/jobs/ui/payment_grids.dart b/lib/pages/jobs/ui/payment_grids.dart index bf58f252..9ca26f91 100644 --- a/lib/pages/jobs/ui/payment_grids.dart +++ b/lib/pages/jobs/ui/payment_grids.dart @@ -69,7 +69,7 @@ class PaymentGridsState extends State { const SizedBox(width: 16.0), Expanded(child: Text("PAYMENTS", style: ralewayRegular(12.0, Colors.black87))), CupertinoButton( - child: Text("SHOW ALL", style: ralewayRegular(11.0, textBaseColor)), + child: Text("SHOW ALL", style: ralewayRegular(11.0, kTextBaseColor)), onPressed: () => TMNavigate(context, PaymentsPage(payments: widget.job.payments), fullscreenDialog: true), ), ], @@ -138,7 +138,7 @@ class PaymentGridsState extends State { child: Icon( Icons.note_add, size: 30.0, - color: textBaseColor.withOpacity(.35), + color: kTextBaseColor.withOpacity(.35), ), ), ), diff --git a/lib/pages/payments/payment.dart b/lib/pages/payments/payment.dart index 99631ecc..29017dc9 100644 --- a/lib/pages/payments/payment.dart +++ b/lib/pages/payments/payment.dart @@ -41,21 +41,21 @@ class PaymentPage extends StatelessWidget { IconButton( icon: Icon( Icons.work, - color: titleBaseColor, + color: kTitleBaseColor, ), onPressed: () => TMNavigate(context, JobPage(job: job)), ), IconButton( icon: Icon( Icons.person, - color: titleBaseColor, + color: kTitleBaseColor, ), onPressed: () => TMNavigate(context, ContactPage(contact: contact)), ), IconButton( icon: Icon( Icons.share, - color: titleBaseColor, + color: kTitleBaseColor, ), // TODO onPressed: null, diff --git a/lib/pages/payments/payment_list_item.dart b/lib/pages/payments/payment_list_item.dart index 815903e0..d242837d 100644 --- a/lib/pages/payments/payment_list_item.dart +++ b/lib/pages/payments/payment_list_item.dart @@ -35,7 +35,7 @@ class PaymentListItem extends StatelessWidget { children: [ TextSpan( text: _date.day.toString(), - style: ralewayMedium(16.0, accentColor), + style: ralewayMedium(16.0, kAccentColor), ), TextSpan(text: "\n"), TextSpan( @@ -56,7 +56,7 @@ class PaymentListItem extends StatelessWidget { width: 4.0, height: 4.0, decoration: BoxDecoration( - color: accentColor, + color: kAccentColor, shape: BoxShape.circle, ), ), diff --git a/lib/pages/payments/payments.dart b/lib/pages/payments/payments.dart index dbe42c34..3c8ce16e 100644 --- a/lib/pages/payments/payments.dart +++ b/lib/pages/payments/payments.dart @@ -50,7 +50,7 @@ class PaymentsPageState extends State { "${payments.length} Tickets", style: TextStyle( fontSize: 11.0, - color: textBaseColor, + color: kTextBaseColor, ), ) : SizedBox(), diff --git a/lib/pages/payments/payments_create.dart b/lib/pages/payments/payments_create.dart index 44a4f008..b115b697 100644 --- a/lib/pages/payments/payments_create.dart +++ b/lib/pages/payments/payments_create.dart @@ -36,7 +36,7 @@ class _PaymentsCreatePageState extends State with SnackBarPr children.add( Padding( child: RaisedButton( - color: accentColor, + color: kAccentColor, shape: StadiumBorder(), child: Text( "FINISH", @@ -82,8 +82,8 @@ class _PaymentsCreatePageState extends State with SnackBarPr child: new Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(title.toUpperCase(), style: ralewayLight(12.0, textBaseColor.shade800)), - Text(trailing, style: ralewayLight(12.0, textBaseColor.shade800)), + Text(title.toUpperCase(), style: ralewayLight(12.0, kTextBaseColor.shade800)), + Text(trailing, style: ralewayLight(12.0, kTextBaseColor.shade800)), ], ), ); @@ -102,7 +102,7 @@ class _PaymentsCreatePageState extends State with SnackBarPr hintStyle: TextStyle(fontSize: 14.0), border: UnderlineInputBorder( borderSide: BorderSide( - color: borderSideColor, + color: kBorderSideColor, width: 0.0, style: BorderStyle.solid, ), @@ -127,7 +127,7 @@ class _PaymentsCreatePageState extends State with SnackBarPr hintStyle: TextStyle(fontSize: 14.0), border: UnderlineInputBorder( borderSide: BorderSide( - color: borderSideColor, + color: kBorderSideColor, width: 0.0, style: BorderStyle.solid, ), diff --git a/lib/pages/splash/splash.dart b/lib/pages/splash/splash.dart new file mode 100644 index 00000000..32fdd49c --- /dev/null +++ b/lib/pages/splash/splash.dart @@ -0,0 +1,126 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:tailor_made/pages/homepage/homepage.dart'; +import 'package:tailor_made/services/auth.dart'; +import 'package:tailor_made/ui/tm_loading_spinner.dart'; +import 'package:tailor_made/utils/tm_images.dart'; +import 'package:tailor_made/utils/tm_navigate.dart'; +import 'package:tailor_made/utils/tm_snackbar.dart'; +import 'package:tailor_made/utils/tm_strings.dart'; +import 'package:tailor_made/utils/tm_theme.dart'; + +class SplashPage extends StatefulWidget { + final bool isColdStart; + + SplashPage({ + Key key, + @required this.isColdStart, + }) : super(key: key); + + @override + State createState() => new _SplashPageState(); +} + +class _SplashPageState extends State with SnackBarProvider { + final scaffoldKey = new GlobalKey(); + bool isLoading; + + @override + void initState() { + super.initState(); + isLoading = widget.isColdStart; + + if (widget.isColdStart == true) { + _trySilent(); + } + + Auth.onAuthStateChanged.firstWhere((user) => user != null).then( + (user) { + Auth.setUser(user); + Navigator.pushReplacement(context, TMNavigate.fadeIn(new HomePage())); + }, + ); + } + + _trySilent() async { + // Give the navigation animations, etc, some time to finish + await new Future.delayed(new Duration(seconds: 1)).then((_) => _onLogin()); + } + + _onLogin() async => await Auth.signInWithGoogle(); + + @override + Widget build(BuildContext context) { + return new Scaffold( + key: scaffoldKey, + body: Stack( + fit: StackFit.expand, + children: [ + Opacity( + opacity: .5, + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: TMImages.pattern, + fit: BoxFit.cover, + ), + ), + ), + ), + Positioned.fill( + top: null, + bottom: 32.0, + child: Text( + TMStrings.appName, + style: ralewayMedium(22.0, kTextBaseColor.withOpacity(.6)), + textAlign: TextAlign.center, + ), + ), + isLoading && widget.isColdStart + ? SizedBox() + : Center( + child: Image( + image: TMImages.logo, + width: 148.0, + color: Colors.white.withOpacity(.35), + colorBlendMode: BlendMode.saturation, + ), + ), + Positioned( + height: 96.0, + bottom: 64.0, + left: 0.0, + right: 0.0, + child: isLoading ? loadingSpinner() : Center(child: _googleBtn()), + ), + ], + ), + ); + } + + Widget _googleBtn() { + return RaisedButton( + color: Colors.white, + onPressed: () { + setState(() => isLoading = true); + try { + _onLogin(); + } catch (e) { + setState(() => isLoading = false); + showInSnackBar(e.toString()); + } + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + mainAxisSize: MainAxisSize.min, + children: [ + Image(image: TMImages.google_logo, width: 24.0), + const SizedBox(width: 8.0), + Text("Continue with Google", style: TextStyle(fontWeight: FontWeight.w700)), + ], + ), + ); + } +} diff --git a/lib/services/auth.dart b/lib/services/auth.dart new file mode 100644 index 00000000..0482f209 --- /dev/null +++ b/lib/services/auth.dart @@ -0,0 +1,55 @@ +import 'dart:async'; + +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:google_sign_in/google_sign_in.dart'; + +final GoogleSignIn _googleSignIn = new GoogleSignIn(); +final FirebaseAuth _auth = FirebaseAuth.instance; + +class Auth { + static FirebaseUser _user; + Auth._(); + + static FirebaseUser setUser(FirebaseUser user) => _user = user; + static FirebaseUser get getUser => _user; + + static Future silently() async => await _googleSignIn.signInSilently(); + + static get onAuthStateChanged => _auth.onAuthStateChanged; + + static Future signInWithGoogle() async { + // Attempt to get the currently authenticated user + GoogleSignInAccount currentUser = _googleSignIn.currentUser; + if (currentUser == null) { + // Attempt to sign in without user interaction + currentUser = await _googleSignIn.signInSilently(); + } + if (currentUser == null) { + // Force the user to interactively sign in + currentUser = await _googleSignIn.signIn(); + } + + final GoogleSignInAuthentication auth = await currentUser.authentication; + + // Authenticate with firebase + final FirebaseUser user = await _auth.signInWithGoogle( + idToken: auth.idToken, + accessToken: auth.accessToken, + ); + + assert(user != null); + assert(!user.isAnonymous); + + setUser(user); + return user; + } + + static Future signOutWithGoogle() async { + // Sign out with firebase + await _auth.signOut(); + // Sign out with google + await _googleSignIn.signOut(); + // Clear state + _user = null; + } +} diff --git a/lib/services/cloudstore.dart b/lib/services/cloudstore.dart index cbdf01c6..1a632345 100644 --- a/lib/services/cloudstore.dart +++ b/lib/services/cloudstore.dart @@ -1,13 +1,15 @@ import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:tailor_made/services/auth.dart'; class Cloudstore { static Firestore instance = Firestore.instance; Cloudstore._(); - static DocumentReference stats = instance.document("stats/current"); - static CollectionReference gallery = instance.collection("gallery"); - static CollectionReference payments = instance.collection("payments"); - static CollectionReference contacts = instance.collection("contacts"); - static CollectionReference jobs = instance.collection("jobs"); + static DocumentReference get account => instance.document("accounts/${Auth.getUser.uid}"); + static DocumentReference get stats => instance.document("stats/${Auth.getUser.uid}"); + static CollectionReference get gallery => instance.collection("gallery").where("userID", isEqualTo: Auth.getUser.uid).reference(); + static CollectionReference get payments => instance.collection("payments").where("userID", isEqualTo: Auth.getUser.uid).reference(); + static CollectionReference get contacts => instance.collection("contacts").where("userID", isEqualTo: Auth.getUser.uid).reference(); + static CollectionReference get jobs => instance.collection("jobs").where("userID", isEqualTo: Auth.getUser.uid).reference(); } diff --git a/lib/ui/circle_avatar.dart b/lib/ui/circle_avatar.dart index bc695b4e..f2ca5e9d 100644 --- a/lib/ui/circle_avatar.dart +++ b/lib/ui/circle_avatar.dart @@ -3,8 +3,8 @@ import 'package:flutter/material.dart'; import 'package:tailor_made/utils/tm_theme.dart'; CircleAvatar circleAvatar({double radius, bool useAlt = false, String imageUrl}) { - final iconColor = useAlt ? primarySwatch.shade300 : Colors.white; - final backgroundColor = useAlt ? Colors.white : primarySwatch.shade300; + final iconColor = useAlt ? kPrimaryColor : Colors.white; + final backgroundColor = useAlt ? Colors.white : kPrimaryColor; return new CircleAvatar( radius: radius ?? null, backgroundColor: imageUrl != null ? Colors.grey.shade400 : backgroundColor, diff --git a/lib/ui/tm_empty_result.dart b/lib/ui/tm_empty_result.dart index c090855f..ecdf3366 100644 --- a/lib/ui/tm_empty_result.dart +++ b/lib/ui/tm_empty_result.dart @@ -19,7 +19,7 @@ class TMEmptyResult extends StatelessWidget { children: [ new Icon( Icons.equalizer, - color: theme.accentColor, + color: theme.primaryColor, size: 36.0, ), const SizedBox(height: 8.0), diff --git a/lib/ui/tm_error_result.dart b/lib/ui/tm_error_result.dart index fc8a3855..d26eeb73 100644 --- a/lib/ui/tm_error_result.dart +++ b/lib/ui/tm_error_result.dart @@ -19,7 +19,7 @@ class TMErrorResult extends StatelessWidget { children: [ new Icon( Icons.error_outline, - color: theme.accentColor, + color: theme.primaryColor, size: 50.0, ), const SizedBox(height: 16.0), diff --git a/lib/ui/tm_loading_spinner.dart b/lib/ui/tm_loading_spinner.dart index 7e923830..191b7228 100644 --- a/lib/ui/tm_loading_spinner.dart +++ b/lib/ui/tm_loading_spinner.dart @@ -5,7 +5,7 @@ import 'package:tailor_made/utils/tm_theme.dart'; Widget loadingSpinner({Color color}) { return Center( child: SpinKitFadingFour( - color: color ?? accentColor, + color: color ?? kAccentColor, // color: color ?? primarySwatch, width: 30.0, height: 30.0, diff --git a/lib/utils/tm_colors.dart b/lib/utils/tm_colors.dart index c5766f72..58179e96 100644 --- a/lib/utils/tm_colors.dart +++ b/lib/utils/tm_colors.dart @@ -3,7 +3,8 @@ import 'package:flutter/widgets.dart'; class TMColors { TMColors._(); - + static const _baseBlue = 0xFF9168ed; + static const _basePink = 0xFFf54295; static const MaterialColor dark = const MaterialColor( 0xFF444444, const { @@ -35,12 +36,12 @@ class TMColors { }, ); static const MaterialColor biro_blue = const MaterialColor( - 0xFF0000d0, + _baseBlue, const { 50: const Color(0xFFeee5fc), 100: const Color(0xFFd1bff6), 200: const Color(0xFFb295f1), - 300: const Color(0xFF9168ed), + 300: const Color(_baseBlue), 400: const Color(0xFF7644e9), 500: const Color(0xFF5519e4), 600: const Color(0xFF4815de), @@ -49,7 +50,22 @@ class TMColors { 900: const Color(0xFF0000ca), }, ); - static const Color accent = const Color(0xFF0000d0); - static const Color primary = const Color(0xFFf54295); + static const MaterialColor slate_pink = const MaterialColor( + _basePink, + const { + 50: const Color(0xFFf54290), + 100: const Color(0xFFf54291), + 200: const Color(0xFFf54293), + 300: const Color(0xFFf54294), + 400: const Color(_basePink), + 500: const Color(0xFFf54296), + 600: const Color(0xFFf54297), + 700: const Color(0xFFf54298), + 800: const Color(0xFFf54299), + 900: const Color(0xFFf54210), + }, + ); + static const Color accent = const Color(_basePink); + static const Color primary = const Color(_baseBlue); static const Color light_grey = const Color(0xFF9B9B9B); } diff --git a/lib/utils/tm_images.dart b/lib/utils/tm_images.dart new file mode 100644 index 00000000..f446abd3 --- /dev/null +++ b/lib/utils/tm_images.dart @@ -0,0 +1,9 @@ +import 'package:flutter/widgets.dart'; + +class TMImages { + TMImages._(); + + static const ImageProvider google_logo = AssetImage('assets/images/google_logo.png'); + static const ImageProvider pattern = AssetImage('assets/images/pattern.png'); + static const ImageProvider logo = AssetImage('assets/images/logo.png'); +} diff --git a/lib/utils/tm_theme.dart b/lib/utils/tm_theme.dart index 62f5ac9c..5ef7f041 100644 --- a/lib/utils/tm_theme.dart +++ b/lib/utils/tm_theme.dart @@ -6,18 +6,21 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:tailor_made/utils/tm_colors.dart'; -const Color accentColor = TMColors.primary; -const Color accentColorAlt = Colors.blueGrey; -const MaterialColor primarySwatch = TMColors.biro_blue; -final Color borderSideColor = Colors.grey.shade300; -const MaterialColor textBaseColor = Colors.grey; -const MaterialColor titleBaseColor = TMColors.dark; -const MaterialColor backgroundBaseColor = TMColors.white; +const Color kAccentColor = TMColors.accent; +const Color kPrimaryColor = TMColors.primary; + +const MaterialColor kAccentSwatch = TMColors.slate_pink; +const MaterialColor kPrimarySwatch = TMColors.biro_blue; + +final Color kBorderSideColor = Colors.grey.shade300; +const MaterialColor kTextBaseColor = Colors.grey; +const MaterialColor kTitleBaseColor = TMColors.dark; +const MaterialColor kBackgroundBaseColor = TMColors.white; class TMBorderSide extends BorderSide { TMBorderSide({Color color}) : super( - color: color != null ? color : borderSideColor, + color: color != null ? color : kBorderSideColor, style: BorderStyle.solid, width: 1.0, ); @@ -28,11 +31,11 @@ class TMStyle extends TextStyle { : super(inherit: false, color: color, fontFamily: 'Raleway', fontSize: size, fontWeight: weight, textBaseline: TextBaseline.alphabetic); } -TextStyle ralewayThin(double fontSize, [Color color]) => new TMStyle.raleway(fontSize, FontWeight.w100, color ?? textBaseColor); -TextStyle ralewayLight(double fontSize, [Color color]) => new TMStyle.raleway(fontSize, FontWeight.w300, color ?? textBaseColor); -TextStyle ralewayRegular(double fontSize, [Color color]) => new TMStyle.raleway(fontSize, FontWeight.w400, color ?? textBaseColor); -TextStyle ralewayMedium(double fontSize, [Color color]) => new TMStyle.raleway(fontSize, FontWeight.w500, color ?? textBaseColor); -TextStyle ralewayBold(double fontSize, [Color color]) => new TMStyle.raleway(fontSize, FontWeight.w700, color ?? textBaseColor); +TextStyle ralewayThin(double fontSize, [Color color]) => new TMStyle.raleway(fontSize, FontWeight.w100, color ?? kTextBaseColor); +TextStyle ralewayLight(double fontSize, [Color color]) => new TMStyle.raleway(fontSize, FontWeight.w300, color ?? kTextBaseColor); +TextStyle ralewayRegular(double fontSize, [Color color]) => new TMStyle.raleway(fontSize, FontWeight.w400, color ?? kTextBaseColor); +TextStyle ralewayMedium(double fontSize, [Color color]) => new TMStyle.raleway(fontSize, FontWeight.w500, color ?? kTextBaseColor); +TextStyle ralewayBold(double fontSize, [Color color]) => new TMStyle.raleway(fontSize, FontWeight.w700, color ?? kTextBaseColor); /// The TextStyles and Colors used for titles, labels, and descriptions. This /// InheritedWidget is shared by all of the routes and widgets created for @@ -42,24 +45,19 @@ class TMTheme extends InheritedWidget { : assert(child != null), super(key: key, child: child); - Color get accentColor => TMColors.accent; - Color get primaryColor => TMColors.primary; - Color get borderSideColor => borderSideColor; + Color get accentColor => kAccentColor; + Color get primaryColor => kPrimaryColor; + Color get borderSideColor => kBorderSideColor; Color get appBarBackgroundColor => scaffoldColor; - final Color scaffoldColor = backgroundBaseColor; - final Color scaffoldColorAlt = textBaseColor.shade100; - final Color appBarColor = titleBaseColor; - final Color textColor = textBaseColor.shade800; - final Color textMutedColor = textBaseColor.shade500; - - // final Color scaffoldColor = Color(0xFF00251a); - // final Color scaffoldColorAlt = Colors.blueGrey.shade700; - // final Color appBarColor = Color(0xFF39796b); - // final Color textColor = Color(0xFF39796b); + final Color scaffoldColor = kBackgroundBaseColor; + final Color scaffoldColorAlt = kTextBaseColor.shade100; + final Color appBarColor = kTitleBaseColor; + final Color textColor = kTextBaseColor.shade800; + final Color textMutedColor = kTextBaseColor.shade500; TextStyle get appBarStyle => ralewayBold(20.0, appBarColor); - TextStyle get titleStyle => ralewayMedium(18.0, titleBaseColor); + TextStyle get titleStyle => ralewayMedium(18.0, kTitleBaseColor); TextStyle get smallTextStyle => ralewayRegular(12.0, textColor); TextStyle get mediumTextStyle => ralewayRegular(16.0, textColor); diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index fa6b4320..00000000 --- a/pubspec.lock +++ /dev/null @@ -1,546 +0,0 @@ -# Generated by pub -# See https://www.dartlang.org/tools/pub/glossary#lockfile -packages: - analyzer: - dependency: transitive - description: - name: analyzer - url: "https://pub.dartlang.org" - source: hosted - version: "0.31.2-alpha.2" - args: - dependency: transitive - description: - name: args - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.3" - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.7" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.3" - cached_network_image: - dependency: "direct main" - description: - name: cached_network_image - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.1+1" - carousel: - dependency: "direct main" - description: - name: carousel - url: "https://pub.dartlang.org" - source: hosted - version: "0.0.1" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.1" - cloud_firestore: - dependency: "direct main" - description: - name: cloud_firestore - url: "https://pub.dartlang.org" - source: hosted - version: "0.7.3" - cloud_functions: - dependency: "direct main" - description: - name: cloud_functions - url: "https://pub.dartlang.org" - source: hosted - version: "0.0.1" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.14.6" - convert: - dependency: transitive - description: - name: convert - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" - crypto: - dependency: transitive - description: - name: crypto - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.5" - csslib: - dependency: transitive - description: - name: csslib - url: "https://pub.dartlang.org" - source: hosted - version: "0.14.4" - firebase_analytics: - dependency: "direct main" - description: - name: firebase_analytics - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" - firebase_auth: - dependency: "direct main" - description: - name: firebase_auth - url: "https://pub.dartlang.org" - source: hosted - version: "0.5.12" - firebase_core: - dependency: transitive - description: - name: firebase_core - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.4" - firebase_storage: - dependency: "direct main" - description: - name: firebase_storage - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.7" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_cache_manager: - dependency: transitive - description: - name: flutter_cache_manager - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.1" - flutter_masked_text: - dependency: "direct main" - description: - name: flutter_masked_text - url: "https://pub.dartlang.org" - source: hosted - version: "0.5.0" - flutter_redux: - dependency: "direct main" - description: - name: flutter_redux - url: "https://pub.dartlang.org" - source: hosted - version: "0.5.2" - flutter_spinkit: - dependency: "direct main" - description: - name: flutter_spinkit - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - front_end: - dependency: transitive - description: - name: front_end - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.0-alpha.12" - glob: - dependency: transitive - description: - name: glob - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.5" - html: - dependency: transitive - description: - name: html - url: "https://pub.dartlang.org" - source: hosted - version: "0.13.3+1" - http: - dependency: transitive - description: - name: http - url: "https://pub.dartlang.org" - source: hosted - version: "0.11.3+16" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.5" - http_parser: - dependency: transitive - description: - name: http_parser - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.2" - image_picker: - dependency: "direct main" - description: - name: image_picker - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.5" - intl: - dependency: "direct main" - description: - name: intl - url: "https://pub.dartlang.org" - source: hosted - version: "0.15.6" - io: - dependency: transitive - description: - name: io - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.2+1" - js: - dependency: transitive - description: - name: js - url: "https://pub.dartlang.org" - source: hosted - version: "0.6.1" - json_rpc_2: - dependency: transitive - description: - name: json_rpc_2 - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.8" - kernel: - dependency: transitive - description: - name: kernel - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.0-alpha.12" - logging: - dependency: transitive - description: - name: logging - url: "https://pub.dartlang.org" - source: hosted - version: "0.11.3+1" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.2" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.5" - mime: - dependency: transitive - description: - name: mime - url: "https://pub.dartlang.org" - source: hosted - version: "0.9.6+1" - multi_server_socket: - dependency: transitive - description: - name: multi_server_socket - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" - node_preamble: - dependency: transitive - description: - name: node_preamble - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.2" - package_config: - dependency: transitive - description: - name: package_config - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.3" - package_resolver: - dependency: transitive - description: - name: package_resolver - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.3" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.6.1" - path_provider: - dependency: transitive - description: - name: path_provider - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.1" - photo_view: - dependency: "direct main" - description: - name: photo_view - url: "https://pub.dartlang.org" - source: hosted - version: "0.0.2" - plugin: - dependency: transitive - description: - name: plugin - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.0+2" - pool: - dependency: transitive - description: - name: pool - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.5" - pub_semver: - dependency: transitive - description: - name: pub_semver - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.1" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" - source: hosted - version: "0.29.0+1" - redux: - dependency: "direct main" - description: - name: redux - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" - redux_epics: - dependency: "direct main" - description: - name: redux_epics - url: "https://pub.dartlang.org" - source: hosted - version: "0.10.0" - redux_logging: - dependency: "direct main" - description: - name: redux_logging - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.0" - rxdart: - dependency: "direct main" - description: - name: rxdart - url: "https://pub.dartlang.org" - source: hosted - version: "0.18.1" - shared_preferences: - dependency: transitive - description: - name: shared_preferences - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.2" - shelf: - dependency: transitive - description: - name: shelf - url: "https://pub.dartlang.org" - source: hosted - version: "0.7.3+1" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.3" - shelf_static: - dependency: transitive - description: - name: shelf_static - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.7+1" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.2+2" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.4" - source_maps: - dependency: transitive - description: - name: source_maps - url: "https://pub.dartlang.org" - source: hosted - version: "0.10.5" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.9.2" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "1.6.7+1" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.2" - synchronized: - dependency: transitive - description: - name: synchronized - url: "https://pub.dartlang.org" - source: hosted - version: "1.5.0+1" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" - test: - dependency: transitive - description: - name: test - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.41" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.5" - url_launcher: - dependency: "direct main" - description: - name: url_launcher - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.2" - utf: - dependency: transitive - description: - name: utf - url: "https://pub.dartlang.org" - source: hosted - version: "0.9.0+4" - uuid: - dependency: transitive - description: - name: uuid - url: "https://pub.dartlang.org" - source: hosted - version: "0.5.3" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.6" - vm_service_client: - dependency: transitive - description: - name: vm_service_client - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.4+3" - watcher: - dependency: transitive - description: - name: watcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.9.7+8" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.8" - yaml: - dependency: transitive - description: - name: yaml - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.14" -sdks: - dart: ">=2.0.0-dev.62.0 <=2.0.0-dev.63.0.flutter-4c9689c1d2" - flutter: ">=0.2.4 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 21d36959..19887dc2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,10 +1,10 @@ name: tailor_made description: Tailor-Made with love. +version: 0.1.0 dependencies: flutter: sdk: flutter - carousel: ^0.0.1 firebase_analytics: ^1.0.1 cloud_firestore: ^0.7.3 cloud_functions: ^0.0.1 @@ -22,6 +22,7 @@ dependencies: photo_view: ^0.0.2 cached_network_image: "^0.4.1" flutter_spinkit: "^1.0.0" + google_sign_in: "^3.0.4" dev_dependencies: flutter_test: @@ -30,19 +31,19 @@ dev_dependencies: flutter: uses-material-design: true -assets: - - assets/images/ + assets: + - assets/images/ -fonts: - - family: Raleway - fonts: - - asset: assets/fonts/Raleway-Thin.ttf - weight: 100 - - asset: assets/fonts/Raleway-Light.ttf - weight: 300 - - asset: assets/fonts/Raleway-Regular.ttf - weight: 400 - - asset: assets/fonts/Raleway-Medium.ttf - weight: 500 - - asset: assets/fonts/Raleway-SemiBold.ttf - weight: 600 + fonts: + - family: Raleway + fonts: + - asset: assets/fonts/Raleway-Thin.ttf + weight: 100 + - asset: assets/fonts/Raleway-Light.ttf + weight: 300 + - asset: assets/fonts/Raleway-Regular.ttf + weight: 400 + - asset: assets/fonts/Raleway-Medium.ttf + weight: 500 + - asset: assets/fonts/Raleway-SemiBold.ttf + weight: 600 diff --git a/screenshots/ss01.png b/screenshots/ss01.png index 7c456622..cf0bd069 100644 Binary files a/screenshots/ss01.png and b/screenshots/ss01.png differ diff --git a/screenshots/ss02.png b/screenshots/ss02.png index 08950517..49ceb394 100644 Binary files a/screenshots/ss02.png and b/screenshots/ss02.png differ diff --git a/screenshots/ss03.png b/screenshots/ss03.png index a242966e..c1b4a6e9 100644 Binary files a/screenshots/ss03.png and b/screenshots/ss03.png differ diff --git a/screenshots/ss04.png b/screenshots/ss04.png index a804900c..4f6ab366 100644 Binary files a/screenshots/ss04.png and b/screenshots/ss04.png differ diff --git a/screenshots/ss05.png b/screenshots/ss05.png index 31842a7e..db401a1f 100644 Binary files a/screenshots/ss05.png and b/screenshots/ss05.png differ diff --git a/screenshots/ss06.png b/screenshots/ss06.png index 22e730ce..067fb401 100644 Binary files a/screenshots/ss06.png and b/screenshots/ss06.png differ diff --git a/screenshots/ss07.png b/screenshots/ss07.png index a57cb154..3d2b6086 100644 Binary files a/screenshots/ss07.png and b/screenshots/ss07.png differ diff --git a/screenshots/ss08.png b/screenshots/ss08.png index ae10ffba..ad810a7e 100644 Binary files a/screenshots/ss08.png and b/screenshots/ss08.png differ diff --git a/screenshots/ss09.png b/screenshots/ss09.png index 75ad8f1e..918fba20 100644 Binary files a/screenshots/ss09.png and b/screenshots/ss09.png differ diff --git a/screenshots/ss10.png b/screenshots/ss10.png index 1b6c5034..2efbe74b 100644 Binary files a/screenshots/ss10.png and b/screenshots/ss10.png differ diff --git a/screenshots/ss11.png b/screenshots/ss11.png index 9db3a5bc..c7eeb7f7 100644 Binary files a/screenshots/ss11.png and b/screenshots/ss11.png differ diff --git a/screenshots/ss12.png b/screenshots/ss12.png index abe2521f..7cee1298 100644 Binary files a/screenshots/ss12.png and b/screenshots/ss12.png differ diff --git a/screenshots/ss13.png b/screenshots/ss13.png index eb80a582..b02d8f2d 100644 Binary files a/screenshots/ss13.png and b/screenshots/ss13.png differ diff --git a/screenshots/ss14.png b/screenshots/ss14.png index 0f0ab372..7dd15123 100644 Binary files a/screenshots/ss14.png and b/screenshots/ss14.png differ diff --git a/screenshots/ss15.png b/screenshots/ss15.png index 0abc990e..fa4e0238 100644 Binary files a/screenshots/ss15.png and b/screenshots/ss15.png differ diff --git a/screenshots/ss16.png b/screenshots/ss16.png index 56213b1d..09f1b234 100644 Binary files a/screenshots/ss16.png and b/screenshots/ss16.png differ diff --git a/screenshots/ss17.png b/screenshots/ss17.png index 9eed354b..5570a834 100644 Binary files a/screenshots/ss17.png and b/screenshots/ss17.png differ diff --git a/screenshots/ss18.png b/screenshots/ss18.png new file mode 100644 index 00000000..ca8d5468 Binary files /dev/null and b/screenshots/ss18.png differ diff --git a/screenshots/ss19.png b/screenshots/ss19.png new file mode 100644 index 00000000..528839da Binary files /dev/null and b/screenshots/ss19.png differ diff --git a/screenshots/ss20.png b/screenshots/ss20.png new file mode 100644 index 00000000..1fc78312 Binary files /dev/null and b/screenshots/ss20.png differ