diff --git a/web_native/lib/api/APIProvider.dart b/web_native/lib/api/APIProvider.dart new file mode 100644 index 0000000..4cd07f0 --- /dev/null +++ b/web_native/lib/api/APIProvider.dart @@ -0,0 +1,133 @@ +import 'dart:io'; +import 'dart:async'; +import 'dart:convert'; + +import 'package:meta/meta.dart'; +import 'package:http/http.dart' as http; + +import 'package:imagini/models/api/response/LoginResponse.dart'; + +class APIProvider{ + String _server, _accessToken, _refreshToken; + + static const String _LOGIN_API = "/api/v1/Login"; + static const String _MEDIA_ITEMS_API = "/api/v1/MediaItems"; + static const String _ALBUMS_API = "/api/v1/Albums"; + static const String _TAGS_API = "/api/v1/Tags"; + + APIProvider({ + @required String server, + String accessToken, + String refreshToken + }) { + _server = server; + _accessToken = accessToken; + _refreshToken = refreshToken; + } + + // Endpoint: /api/v1/Login + Future login([ + String username, + String password, + ]) async { + assert( + (username != null && password != null) || + (_accessToken != null && _refreshToken != null) + ); + + http.Response response = await http.post( + Uri.encodeFull(_server + _LOGIN_API), + body: jsonEncode({ + 'user': username, + 'password': password, + }), + ).timeout(Duration(seconds: 10)); + + if (response.statusCode != 200) { + // Fuck + return LoginResponse.fromJson(jsonDecode(response.body)); + } + + // Success + String setCookieVal = response.headers["set-cookie"]; + List allCookies = setCookieVal.split(',') + .map((cookie) => Cookie.fromSetCookieValue(cookie)).toList(); + + Cookie accessToken = allCookies.firstWhere((cookie) => cookie.name == "AccessToken"); + Cookie refreshToken = allCookies.firstWhere((cookie) => cookie.name == "RefreshToken"); + + _accessToken = accessToken.toString(); + _refreshToken = refreshToken.toString(); + + print("Status: ${response.statusCode.toString()}"); + print("Body: ${response.body.toString()}"); + print("AccessToken: ${accessToken.toString()}"); + print("RefreshToken: ${refreshToken.toString()}"); + + return LoginResponse.fromJson(jsonDecode(response.body)); + } + + void logout() { + } + + Future mediaItems([ + String startDate, + String endDate, + String albumID, + List tagID, + String type, // TODO: Make enum + int page, + ]) async { + // Query: + // /api/v1/MediaItems + // Derive Params: + // startDate: + // &createdAt=>2020-10-10T10:10:10 + // endDate: + // &createdAt=<2020-10-10T10:10:10 + // albumID: + // &albumID=9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d + // tagID: + // &tagID=9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d,9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d + // type: + // &type=Photos + // &type=Videos + // page: + // &page=4 + + // Returns: + // { + // + // } + + return null; + } + + Future tags([ + int page + ]) async { + // Query: + // /api/v1/Tags + // Derive Params: + // page: + // &page=4 + return null; + } + + Future albums([ + int page + ]) async { + // Query: + // /api/v1/Albums + // Derive Params: + // page: + // &page=4 + return null; + } + + Future me() async { + return null; + } + + void dispose() {} +} diff --git a/web_native/lib/api/ImaginiAPIRepository.dart b/web_native/lib/api/ImaginiAPIRepository.dart new file mode 100644 index 0000000..51ac20f --- /dev/null +++ b/web_native/lib/api/ImaginiAPIRepository.dart @@ -0,0 +1,12 @@ +import 'package:imagini/api/APIProvider.dart'; +import 'package:imagini/models/api/response/LoginResponse.dart'; + +class ImaginiAPIRepository { + APIProvider _apiProvider; + + ImaginiAPIRepository(this._apiProvider); + + Stream login(String user, password) { + return Stream.fromFuture(_apiProvider.login(user, password)); + } +} diff --git a/web_native/lib/blocs/SplashBloc.dart b/web_native/lib/blocs/SplashBloc.dart new file mode 100644 index 0000000..0c39bf9 --- /dev/null +++ b/web_native/lib/blocs/SplashBloc.dart @@ -0,0 +1,35 @@ +import 'dart:async'; + +import 'package:imagini/core/ImaginiApplication.dart'; +import 'package:imagini/api/ImaginiAPIRepository.dart'; + +import 'package:imagini/models/api/response/LoginResponse.dart'; + +class SplashBloc{ + + final ImaginiApplication _application; + + final _loginController = StreamController(); + Stream get loginResult => _loginController.stream; + + SplashBloc(this._application){ + _init(); + } + + void _init(){ + // Do Initial Load + initializeLogin(); + } + + void dispose(){ + _loginController.close(); + } + + initializeLogin(){ + ImaginiAPIRepository imaginiAPI = _application.imaginiAPI; + _loginController.addStream(imaginiAPI.login("admin", "admin2")); + + // imaginiAPI.login("admin", "admin1").listen((LoginResponse lr) { + // }); + } +} diff --git a/web_native/lib/blocs/login/bloc.dart b/web_native/lib/blocs/login/bloc.dart deleted file mode 100644 index 9bb4975..0000000 --- a/web_native/lib/blocs/login/bloc.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'dart:io'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:imagini/blocs/login/events.dart'; -import 'package:imagini/blocs/login/states.dart'; -import 'package:imagini/services/api/api.dart'; -import 'package:imagini/services/api/exceptions.dart'; - -class LoginBloc extends Bloc { - final ImaginiAPI imaginiAPI; - Map loginResult; - String exampleResult; - - LoginBloc({ this.imaginiAPI }) : super(LoginInitState()); - - @override - Stream mapEventToState(LoginEvents event) async* { - switch (event) { - case LoginEvents.loginResult: - yield LoginLoading(); - try { - // exampleResult = await imaginiAPI.exampleApi(); - loginResult = await imaginiAPI.loginAPI("admin", "admin"); - yield LoginSuccess(); - } on SocketException { - yield LoginFailed( - error: ConnectionRefusedException('No Internet'), - ); - } on FormatException { - yield LoginFailed( - error: InvalidFormatException('Invalid Response Format'), - ); - } catch (e) { - print(e); - yield LoginFailed( - error: UnknownException('Unknown Error'), - ); - } - break; - } - } -} diff --git a/web_native/lib/blocs/login/events.dart b/web_native/lib/blocs/login/events.dart deleted file mode 100644 index ab69f01..0000000 --- a/web_native/lib/blocs/login/events.dart +++ /dev/null @@ -1,3 +0,0 @@ -enum LoginEvents { - loginResult, -} diff --git a/web_native/lib/blocs/login/states.dart b/web_native/lib/blocs/login/states.dart deleted file mode 100644 index 088dac7..0000000 --- a/web_native/lib/blocs/login/states.dart +++ /dev/null @@ -1,28 +0,0 @@ -// import 'package:equatable/equatable.dart'; -// -// abstract class LoginState extends Equatable { -// @override -// List get props => []; -// } - -abstract class LoginState {} - -class LoginInitState extends LoginState {} - -class LoginLoading extends LoginState {} - -class LoginSuccess extends LoginState {} - -class LoginNeeded extends LoginState {} - -class LoginFailed extends LoginState { - final error; - LoginFailed({this.error}); -} - -class LoginLoaded extends LoginState {} - -class LoginListError extends LoginState { - final error; - LoginListError({this.error}); -} diff --git a/web_native/lib/blocs/theme/bloc.dart b/web_native/lib/blocs/theme/bloc.dart deleted file mode 100644 index 78cb850..0000000 --- a/web_native/lib/blocs/theme/bloc.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:imagini/blocs/theme/events.dart'; -import 'package:imagini/blocs/theme/state.dart'; -import 'package:imagini/settings/app_themes.dart'; - -class ThemeBloc extends Bloc { - // - ThemeBloc() - : super( - ThemeState( - themeData: AppThemes.appThemeData[AppTheme.lightTheme], - ), - ); - - @override - Stream mapEventToState(ThemeEvent event) async* { - if (event is ThemeEvent) { - yield ThemeState( - themeData: AppThemes.appThemeData[event.appTheme], - ); - } - } -} diff --git a/web_native/lib/blocs/theme/events.dart b/web_native/lib/blocs/theme/events.dart deleted file mode 100644 index f81b18d..0000000 --- a/web_native/lib/blocs/theme/events.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:imagini/settings/app_themes.dart'; - -class ThemeEvent { - final AppTheme appTheme; - ThemeEvent({this.appTheme}); -} diff --git a/web_native/lib/blocs/theme/state.dart b/web_native/lib/blocs/theme/state.dart deleted file mode 100644 index dadfe28..0000000 --- a/web_native/lib/blocs/theme/state.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:flutter/material.dart'; - -class ThemeState { - final ThemeData themeData; - ThemeState({this.themeData}); -} diff --git a/web_native/lib/components/roundedalertdialog.dart b/web_native/lib/components/roundedalertdialog.dart deleted file mode 100644 index c881f0d..0000000 --- a/web_native/lib/components/roundedalertdialog.dart +++ /dev/null @@ -1,636 +0,0 @@ -import 'dart:async'; -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; - -import 'package:flutter/material.dart'; - -// Examples can assume: -// enum Department { treasury, state } - -/// A material design dialog. -/// -/// This dialog widget does not have any opinion about the contents of the -/// dialog. Rather than using this widget directly, consider using [AlertDialog] -/// or [SimpleDialog], which implement specific kinds of material design -/// dialogs. -/// -/// See also: -/// -/// * [AlertDialog], for dialogs that have a message and some buttons. -/// * [SimpleDialog], for dialogs that offer a variety of options. -/// * [showDialog], which actually displays the dialog and returns its result. -/// * -class Dialog extends StatelessWidget { - /// Creates a dialog. - /// - /// Typically used in conjunction with [showDialog]. - const Dialog({ - Key key, - this.child, - this.insetAnimationDuration: const Duration(milliseconds: 100), - this.insetAnimationCurve: Curves.decelerate, - }) : super(key: key); - - /// The widget below this widget in the tree. - /// - /// {@macro flutter.widgets.child} - final Widget child; - - /// The duration of the animation to show when the system keyboard intrudes - /// into the space that the dialog is placed in. - /// - /// Defaults to 100 milliseconds. - final Duration insetAnimationDuration; - - /// The curve to use for the animation shown when the system keyboard intrudes - /// into the space that the dialog is placed in. - /// - /// Defaults to [Curves.fastOutSlowIn]. - final Curve insetAnimationCurve; - - Color _getColor(BuildContext context) { - return Theme.of(context).dialogBackgroundColor; - } - - @override - Widget build(BuildContext context) { - return new AnimatedPadding( - padding: MediaQuery.of(context).viewInsets + - const EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0), - duration: insetAnimationDuration, - curve: insetAnimationCurve, - child: new MediaQuery.removeViewInsets( - removeLeft: true, - removeTop: true, - removeRight: true, - removeBottom: true, - context: context, - child: new Center( - child: new ConstrainedBox( - constraints: const BoxConstraints(minWidth: 280.0), - child: new Material( - shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10.0)), side: BorderSide(color: Colors.white, width: 1.0, style: BorderStyle.solid)), - elevation: 30.0, - color: _getColor(context), - type: MaterialType.card, - child: child, - ), - ), - ), - ), - ); - } -} - -/// A material design alert dialog. -/// -/// An alert dialog informs the user about situations that require -/// acknowledgement. An alert dialog has an optional title and an optional list -/// of actions. The title is displayed above the content and the actions are -/// displayed below the content. -/// -/// If the content is too large to fit on the screen vertically, the dialog will -/// display the title and the actions and let the content overflow. Consider -/// using a scrolling widget, such as [ListView], for [content] to avoid -/// overflow. -/// -/// For dialogs that offer the user a choice between several options, consider -/// using a [SimpleDialog]. -/// -/// Typically passed as the child widget to [showDialog], which displays the -/// dialog. -/// -/// ## Sample code -/// -/// This snippet shows a method in a [State] which, when called, displays a dialog box -/// and returns a [Future] that completes when the dialog is dismissed. -/// -/// ```dart -/// Future _neverSatisfied() async { -/// return showDialog( -/// context: context, -/// barrierDismissible: false, // user must tap button! -/// builder: (BuildContext context) { -/// return new AlertDialog( -/// title: new Text('Rewind and remember'), -/// content: new SingleChildScrollView( -/// child: new ListBody( -/// children: [ -/// new Text('You will never be satisfied.'), -/// new Text('You\’re like me. I’m never satisfied.'), -/// ], -/// ), -/// ), -/// actions: [ -/// new FlatButton( -/// child: new Text('Regret'), -/// onPressed: () { -/// Navigator.of(context).pop(); -/// }, -/// ), -/// ], -/// ); -/// }, -/// ); -/// } -/// ``` -/// -/// See also: -/// -/// * [SimpleDialog], which handles the scrolling of the contents but has no [actions]. -/// * [Dialog], on which [AlertDialog] and [SimpleDialog] are based. -/// * [showDialog], which actually displays the dialog and returns its result. -/// * -class CustomAlertDialog extends StatelessWidget { - /// Creates an alert dialog. - /// - /// Typically used in conjunction with [showDialog]. - /// - /// The [contentPadding] must not be null. The [titlePadding] defaults to - /// null, which implies a default that depends on the values of the other - /// properties. See the documentation of [titlePadding] for details. - const CustomAlertDialog({ - Key key, - this.title, - this.titlePadding, - this.content, - this.contentPadding: const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0), - this.actions, - this.semanticLabel, - }) : assert(contentPadding != null), - super(key: key); - - /// The (optional) title of the dialog is displayed in a large font at the top - /// of the dialog. - /// - /// Typically a [Text] widget. - final Widget title; - - /// Padding around the title. - /// - /// If there is no title, no padding will be provided. Otherwise, this padding - /// is used. - /// - /// This property defaults to providing 24 pixels on the top, left, and right - /// of the title. If the [content] is not null, then no bottom padding is - /// provided (but see [contentPadding]). If it _is_ null, then an extra 20 - /// pixels of bottom padding is added to separate the [title] from the - /// [actions]. - final EdgeInsetsGeometry titlePadding; - - /// The (optional) content of the dialog is displayed in the center of the - /// dialog in a lighter font. - /// - /// Typically, this is a [ListView] containing the contents of the dialog. - /// Using a [ListView] ensures that the contents can scroll if they are too - /// big to fit on the display. - final Widget content; - - /// Padding around the content. - /// - /// If there is no content, no padding will be provided. Otherwise, padding of - /// 20 pixels is provided above the content to separate the content from the - /// title, and padding of 24 pixels is provided on the left, right, and bottom - /// to separate the content from the other edges of the dialog. - final EdgeInsetsGeometry contentPadding; - - /// The (optional) set of actions that are displayed at the bottom of the - /// dialog. - /// - /// Typically this is a list of [FlatButton] widgets. - /// - /// These widgets will be wrapped in a [ButtonBar], which introduces 8 pixels - /// of padding on each side. - /// - /// If the [title] is not null but the [content] _is_ null, then an extra 20 - /// pixels of padding is added above the [ButtonBar] to separate the [title] - /// from the [actions]. - final List actions; - - /// The semantic label of the dialog used by accessibility frameworks to - /// announce screen transitions when the dialog is opened and closed. - /// - /// If this label is not provided, a semantic label will be infered from the - /// [title] if it is not null. If there is no title, the label will be taken - /// from [MaterialLocalizations.alertDialogLabel]. - /// - /// See also: - /// - /// * [SemanticsConfiguration.isRouteName], for a description of how this - /// value is used. - final String semanticLabel; - - @override - Widget build(BuildContext context) { - final List children = []; - String label = semanticLabel; - - if (title != null) { - children.add(new Padding( - padding: titlePadding ?? - new EdgeInsets.fromLTRB( - 24.0, 24.0, 24.0, content == null ? 20.0 : 0.0), - child: new DefaultTextStyle( - style: Theme.of(context).textTheme.title, - child: new Semantics(child: title, namesRoute: true), - ), - )); - } else { - switch (defaultTargetPlatform) { - case TargetPlatform.iOS: - label = semanticLabel; - break; - case TargetPlatform.android: - case TargetPlatform.fuchsia: - label = semanticLabel ?? - MaterialLocalizations.of(context)?.alertDialogLabel; - } - } - - if (content != null) { - children.add(new Flexible( - child: new Padding( - padding: contentPadding, - child: new DefaultTextStyle( - style: Theme.of(context).textTheme.subhead, - child: content, - ), - ), - )); - } - - if (actions != null) { - children.add(new ButtonTheme.bar( - child: new ButtonBar( - children: actions, - ), - )); - } - - Widget dialogChild = new IntrinsicWidth( - child: new Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: children, - ), - ); - - if (label != null) - dialogChild = - new Semantics(namesRoute: true, label: label, child: dialogChild); - - return new Dialog(child: dialogChild); - } -} - -/// An option used in a [SimpleDialog]. -/// -/// A simple dialog offers the user a choice between several options. This -/// widget is commonly used to represent each of the options. If the user -/// selects this option, the widget will call the [onPressed] callback, which -/// typically uses [Navigator.pop] to close the dialog. -/// -/// The padding on a [SimpleDialogOption] is configured to combine with the -/// default [SimpleDialog.contentPadding] so that each option ends up 8 pixels -/// from the other vertically, with 20 pixels of spacing between the dialog's -/// title and the first option, and 24 pixels of spacing between the last option -/// and the bottom of the dialog. -/// -/// ## Sample code -/// -/// ```dart -/// new SimpleDialogOption( -/// onPressed: () { Navigator.pop(context, Department.treasury); }, -/// child: const Text('Treasury department'), -/// ) -/// ``` -/// -/// See also: -/// -/// * [SimpleDialog], for a dialog in which to use this widget. -/// * [showDialog], which actually displays the dialog and returns its result. -/// * [FlatButton], which are commonly used as actions in other kinds of -/// dialogs, such as [AlertDialog]s. -/// * -class SimpleDialogOption extends StatelessWidget { - /// Creates an option for a [SimpleDialog]. - const SimpleDialogOption({ - Key key, - this.onPressed, - this.child, - }) : super(key: key); - - /// The callback that is called when this option is selected. - /// - /// If this is set to null, the option cannot be selected. - /// - /// When used in a [SimpleDialog], this will typically call [Navigator.pop] - /// with a value for [showDialog] to complete its future with. - final VoidCallback onPressed; - - /// The widget below this widget in the tree. - /// - /// Typically a [Text] widget. - final Widget child; - - @override - Widget build(BuildContext context) { - return new InkWell( - onTap: onPressed, - child: new Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 24.0), - child: child), - ); - } -} - -/// A simple material design dialog. -/// -/// A simple dialog offers the user a choice between several options. A simple -/// dialog has an optional title that is displayed above the choices. -/// -/// Choices are normally represented using [SimpleDialogOption] widgets. If -/// other widgets are used, see [contentPadding] for notes regarding the -/// conventions for obtaining the spacing expected by Material Design. -/// -/// For dialogs that inform the user about a situation, consider using an -/// [AlertDialog]. -/// -/// Typically passed as the child widget to [showDialog], which displays the -/// dialog. -/// -/// ## Sample code -/// -/// In this example, the user is asked to select between two options. These -/// options are represented as an enum. The [showDialog] method here returns -/// a [Future] that completes to a value of that enum. If the user cancels -/// the dialog (e.g. by hitting the back button on Android, or tapping on the -/// mask behind the dialog) then the future completes with the null value. -/// -/// The return value in this example is used as the index for a switch statement. -/// One advantage of using an enum as the return value and then using that to -/// drive a switch statement is that the analyzer will flag any switch statement -/// that doesn't mention every value in the enum. -/// -/// ```dart -/// Future _askedToLead() async { -/// switch (await showDialog( -/// context: context, -/// builder: (BuildContext context) { -/// return new SimpleDialog( -/// title: const Text('Select assignment'), -/// children: [ -/// new SimpleDialogOption( -/// onPressed: () { Navigator.pop(context, Department.treasury); }, -/// child: const Text('Treasury department'), -/// ), -/// new SimpleDialogOption( -/// onPressed: () { Navigator.pop(context, Department.state); }, -/// child: const Text('State department'), -/// ), -/// ], -/// ); -/// } -/// )) { -/// case Department.treasury: -/// // Let's go. -/// // ... -/// break; -/// case Department.state: -/// // ... -/// break; -/// } -/// } -/// ``` -/// -/// See also: -/// -/// * [SimpleDialogOption], which are options used in this type of dialog. -/// * [AlertDialog], for dialogs that have a row of buttons below the body. -/// * [Dialog], on which [SimpleDialog] and [AlertDialog] are based. -/// * [showDialog], which actually displays the dialog and returns its result. -/// * -class SimpleDialog extends StatelessWidget { - /// Creates a simple dialog. - /// - /// Typically used in conjunction with [showDialog]. - /// - /// The [titlePadding] and [contentPadding] arguments must not be null. - const SimpleDialog({ - Key key, - this.title, - this.titlePadding: const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0.0), - this.children, - this.contentPadding: const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0), - this.semanticLabel, - }) : assert(titlePadding != null), - assert(contentPadding != null), - super(key: key); - - /// The (optional) title of the dialog is displayed in a large font at the top - /// of the dialog. - /// - /// Typically a [Text] widget. - final Widget title; - - /// Padding around the title. - /// - /// If there is no title, no padding will be provided. - /// - /// By default, this provides the recommend Material Design padding of 24 - /// pixels around the left, top, and right edges of the title. - /// - /// See [contentPadding] for the conventions regarding padding between the - /// [title] and the [children]. - final EdgeInsetsGeometry titlePadding; - - /// The (optional) content of the dialog is displayed in a - /// [SingleChildScrollView] underneath the title. - /// - /// Typically a list of [SimpleDialogOption]s. - final List children; - - /// Padding around the content. - /// - /// By default, this is 12 pixels on the top and 16 pixels on the bottom. This - /// is intended to be combined with children that have 24 pixels of padding on - /// the left and right, and 8 pixels of padding on the top and bottom, so that - /// the content ends up being indented 20 pixels from the title, 24 pixels - /// from the bottom, and 24 pixels from the sides. - /// - /// The [SimpleDialogOption] widget uses such padding. - /// - /// If there is no [title], the [contentPadding] should be adjusted so that - /// the top padding ends up being 24 pixels. - final EdgeInsetsGeometry contentPadding; - - /// The semantic label of the dialog used by accessibility frameworks to - /// announce screen transitions when the dialog is opened and closed. - /// - /// If this label is not provided, a semantic label will be infered from the - /// [title] if it is not null. If there is no title, the label will be taken - /// from [MaterialLocalizations.dialogLabel]. - /// - /// See also: - /// - /// * [SemanticsConfiguration.isRouteName], for a description of how this - /// value is used. - final String semanticLabel; - - @override - Widget build(BuildContext context) { - final List body = []; - String label = semanticLabel; - - if (title != null) { - body.add(new Padding( - padding: titlePadding, - child: new DefaultTextStyle( - style: Theme.of(context).textTheme.title, - child: new Semantics(namesRoute: true, child: title), - ))); - } else { - switch (defaultTargetPlatform) { - case TargetPlatform.iOS: - label = semanticLabel; - break; - case TargetPlatform.android: - case TargetPlatform.fuchsia: - label = - semanticLabel ?? MaterialLocalizations.of(context)?.dialogLabel; - } - } - - if (children != null) { - body.add(new Flexible( - child: new SingleChildScrollView( - padding: contentPadding, - child: new ListBody(children: children), - ))); - } - - Widget dialogChild = new IntrinsicWidth( - stepWidth: 56.0, - child: new ConstrainedBox( - constraints: const BoxConstraints(minWidth: 280.0), - child: new Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: body, - ), - ), - ); - - if (label != null) - dialogChild = new Semantics( - namesRoute: true, - label: label, - child: dialogChild, - ); - return new Dialog(child: dialogChild); - } -} - -class _DialogRoute extends PopupRoute { - _DialogRoute({ - @required this.theme, - bool barrierDismissible: true, - this.barrierLabel, - @required this.child, - RouteSettings settings, - }) : assert(barrierDismissible != null), - _barrierDismissible = barrierDismissible, - super(settings: settings); - - final Widget child; - final ThemeData theme; - - @override - Duration get transitionDuration => const Duration(milliseconds: 150); - - @override - bool get barrierDismissible => _barrierDismissible; - final bool _barrierDismissible; - - @override - Color get barrierColor => Colors.black54; - - @override - final String barrierLabel; - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return new SafeArea( - child: new Builder(builder: (BuildContext context) { - final Widget annotatedChild = new Semantics( - child: child, - scopesRoute: true, - explicitChildNodes: true, - ); - return theme != null - ? new Theme(data: theme, child: annotatedChild) - : annotatedChild; - }), - ); - } - - @override - Widget buildTransitions(BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) { - return new FadeTransition( - opacity: new CurvedAnimation(parent: animation, curve: Curves.easeOut), - child: child); - } -} - -/// Displays a dialog above the current contents of the app. -/// -/// This function takes a `builder` which typically builds a [Dialog] widget. -/// Content below the dialog is dimmed with a [ModalBarrier]. This widget does -/// not share a context with the location that `showDialog` is originally -/// called from. Use a [StatefulBuilder] or a custom [StatefulWidget] if the -/// dialog needs to update dynamically. -/// -/// The `context` argument is used to look up the [Navigator] and [Theme] for -/// the dialog. It is only used when the method is called. Its corresponding -/// widget can be safely removed from the tree before the dialog is closed. -/// -/// The `child` argument is deprecated, and should be replaced with `builder`. -/// -/// Returns a [Future] that resolves to the value (if any) that was passed to -/// [Navigator.pop] when the dialog was closed. -/// -/// The dialog route created by this method is pushed to the root navigator. -/// If the application has multiple [Navigator] objects, it may be necessary to -/// call `Navigator.of(context, rootNavigator: true).pop(result)` to close the -/// dialog rather just 'Navigator.pop(context, result)`. -/// -/// See also: -/// * [AlertDialog], for dialogs that have a row of buttons below a body. -/// * [SimpleDialog], which handles the scrolling of the contents and does -/// not show buttons below its body. -/// * [Dialog], on which [SimpleDialog] and [AlertDialog] are based. -/// * -Future customShowDialog({ - @required - BuildContext context, - bool barrierDismissible: true, - @Deprecated( - 'Instead of using the "child" argument, return the child from a closure ' - 'provided to the "builder" argument. This will ensure that the BuildContext ' - 'is appropriate for widgets built in the dialog.') - Widget child, - WidgetBuilder builder, -}) { - assert(child == null || builder == null); - return Navigator.of(context, rootNavigator: true).push(new _DialogRoute( - child: child ?? new Builder(builder: builder), - theme: Theme.of(context, shadowThemeOnly: true), - barrierDismissible: barrierDismissible, - barrierLabel: - MaterialLocalizations.of(context).modalBarrierDismissLabel, - )); -} \ No newline at end of file diff --git a/web_native/lib/core/AppComponent.dart b/web_native/lib/core/AppComponent.dart new file mode 100644 index 0000000..d7ff1e2 --- /dev/null +++ b/web_native/lib/core/AppComponent.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +import 'package:imagini/core/Env.dart'; +import 'package:imagini/core/AppProvider.dart'; +import 'package:imagini/core/ImaginiApplication.dart'; + +class AppComponent extends StatefulWidget { + + final ImaginiApplication _application; + + AppComponent(this._application); + + @override + State createState() { + return new AppComponentState(_application); + } +} + +class AppComponentState extends State { + + final ImaginiApplication _application; + + AppComponentState(this._application); + + @override + void dispose() async { + // Log.info('dispose'); + super.dispose(); + await _application.onTerminate(); + } + + @override + Widget build(BuildContext context) { + + final app = new MaterialApp( + title: Env.value.appName, + debugShowCheckedModeBanner: false, + theme: new ThemeData( + primarySwatch: Colors.blue, + ), + onGenerateRoute: _application.router.generator, + ); + print('initial core.route = ${app.initialRoute}'); + + final appProvider = AppProvider(child: app, application: _application); + return appProvider; + } +} diff --git a/web_native/lib/core/AppProvider.dart b/web_native/lib/core/AppProvider.dart new file mode 100644 index 0000000..6efc539 --- /dev/null +++ b/web_native/lib/core/AppProvider.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:fluro/fluro.dart'; + +import 'package:imagini/core/ImaginiApplication.dart'; + +class AppProvider extends InheritedWidget { + + final ImaginiApplication application; + + AppProvider({Key key, Widget child, this.application}) + : super(key: key, child: child); + + bool updateShouldNotify(_) => true; + + static AppProvider of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + static FluroRouter getRouter(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType().application.router; + } + + static ImaginiApplication getApplication(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType().application; + } + +} diff --git a/web_native/lib/core/AppRoutes.dart b/web_native/lib/core/AppRoutes.dart new file mode 100644 index 0000000..da3dbb0 --- /dev/null +++ b/web_native/lib/core/AppRoutes.dart @@ -0,0 +1,50 @@ +import 'package:fluro/fluro.dart'; +import 'package:flutter/material.dart'; + +import 'package:imagini/screens/HomeScreen.dart'; +import 'package:imagini/screens/LoginScreen.dart'; +import 'package:imagini/screens/SplashScreen.dart'; + +var splashHandler = new Handler( + handlerFunc: (BuildContext context, Map> params) { + return SplashScreen(); + } +); + +var loginHandler = new Handler( + handlerFunc: (BuildContext context, Map> params) { + return LoginScreen(); + } +); + +var homeHandler = new Handler( + handlerFunc: (BuildContext context, Map> params) { + return HomeScreen(); + } +); + +// var appDetailRouteHandler = new Handler( +// handlerFunc: (BuildContext context, Map> params) { +// String appId = params['appId']?.first; +// String heroTag = params['heroTag']?.first; +// String title = params['title']?.first; +// String url = params['url']?.first; +// String titleTag = params['titleTag']?.first; +// +// return new AppDetailPage(appId: num.parse(appId), heroTag:heroTag,title: title, url: url, titleTag: titleTag); +// }); + +class AppRoutes { + static void configureRoutes(FluroRouter router) { + router.notFoundHandler = new Handler( + handlerFunc: (BuildContext context, Map> params) { + print('ROUTE WAS NOT FOUND !!!'); + return; + } + ); + router.define(SplashScreen.PATH, handler: splashHandler); + router.define(LoginScreen.PATH, handler: loginHandler); + router.define(HomeScreen.PATH, handler: homeHandler); + // router.define(AppDetailPage.PATH, handler: appDetailRouteHandler); + } +} diff --git a/web_native/lib/core/Env.dart b/web_native/lib/core/Env.dart new file mode 100644 index 0000000..5b87b63 --- /dev/null +++ b/web_native/lib/core/Env.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +import 'package:imagini/core/AppComponent.dart'; +import 'package:imagini/core/ImaginiApplication.dart'; + +class Env { + static Env value; + String appName; + + Env() { + value = this; + _init(); + } + + _init() async { + WidgetsFlutterBinding.ensureInitialized(); + var application = ImaginiApplication(); + await application.onCreate(); + runApp(AppComponent(application)); + } +} diff --git a/web_native/lib/core/ImaginiApplication.dart b/web_native/lib/core/ImaginiApplication.dart new file mode 100644 index 0000000..fff3c2c --- /dev/null +++ b/web_native/lib/core/ImaginiApplication.dart @@ -0,0 +1,27 @@ +import 'package:fluro/fluro.dart'; + +import 'package:imagini/core/AppRoutes.dart'; +import 'package:imagini/api/APIProvider.dart'; +import 'package:imagini/api/ImaginiAPIRepository.dart'; + +class ImaginiApplication { + FluroRouter router; + ImaginiAPIRepository imaginiAPI; + + Future onCreate() async { + _initRouter(); + _initAPIRepository(); + } + + Future onTerminate() async {} + + _initRouter() { + router = new FluroRouter(); + AppRoutes.configureRoutes(router); + } + + _initAPIRepository() { + APIProvider apiProvider = new APIProvider(server: "http://10.0.20.170:8484"); + imaginiAPI = ImaginiAPIRepository(apiProvider); + } +} diff --git a/web_native/lib/main.dart b/web_native/lib/main.dart index d1cb97f..aa45dc9 100644 --- a/web_native/lib/main.dart +++ b/web_native/lib/main.dart @@ -1,38 +1,7 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:imagini/core/Env.dart'; -import 'package:imagini/services/api/api.dart'; -import 'package:imagini/screens/login.dart'; -import 'package:imagini/blocs/login/bloc.dart'; +void main() => ProductionImagini(); -import 'package:imagini/settings/preferences.dart'; -import 'package:imagini/blocs/theme/bloc.dart'; -import 'package:imagini/blocs/theme/state.dart'; - -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - await Preferences.init(); - runApp(ImaginiApp()); -} - -class ImaginiApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (context) => ThemeBloc(), - child: BlocBuilder(builder: (BuildContext context, ThemeState themeState) { - return MaterialApp( - title: 'Imagini', - debugShowCheckedModeBanner: false, - theme: themeState.themeData, - home: BlocProvider( - // create: (context) => LoginBloc(albumsRepo: AlbumServices()), - create: (context) => LoginBloc(imaginiAPI: ImaginiAPI()), - child: LoginScreen(), - ), - ); - }, - ), - ); - } +class ProductionImagini extends Env { + final String appName = "Imagini"; } diff --git a/web_native/lib/models/api/Album.dart b/web_native/lib/models/api/Album.dart new file mode 100644 index 0000000..e69de29 diff --git a/web_native/lib/models/api/MediaItem.dart b/web_native/lib/models/api/MediaItem.dart new file mode 100644 index 0000000..e69de29 diff --git a/web_native/lib/models/api/Tag.dart b/web_native/lib/models/api/Tag.dart new file mode 100644 index 0000000..e69de29 diff --git a/web_native/lib/models/api/User.dart b/web_native/lib/models/api/User.dart new file mode 100644 index 0000000..e69de29 diff --git a/web_native/lib/models/api/response/AlbumsResponse.dart b/web_native/lib/models/api/response/AlbumsResponse.dart new file mode 100644 index 0000000..e69de29 diff --git a/web_native/lib/models/api/response/LoginResponse.dart b/web_native/lib/models/api/response/LoginResponse.dart new file mode 100644 index 0000000..b69c5f6 --- /dev/null +++ b/web_native/lib/models/api/response/LoginResponse.dart @@ -0,0 +1,19 @@ +class LoginResponse { + final String success; + final String error; + final String deviceUUID; + + LoginResponse(this.success, this.error, this.deviceUUID); + + LoginResponse.fromJson(Map json) + : success = json['success'], + error = json['error'], + deviceUUID = json['deviceUUID']; + + Map toJson() => + { + 'success': success, + 'error': error, + 'deviceUUID': deviceUUID, + }; +} diff --git a/web_native/lib/models/api/response/MediaItemsResponse.dart b/web_native/lib/models/api/response/MediaItemsResponse.dart new file mode 100644 index 0000000..e69de29 diff --git a/web_native/lib/models/api/response/TagsResponse.dart b/web_native/lib/models/api/response/TagsResponse.dart new file mode 100644 index 0000000..e69de29 diff --git a/web_native/lib/models/contact.dart b/web_native/lib/models/contact.dart deleted file mode 100644 index 505b45c..0000000 --- a/web_native/lib/models/contact.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:meta/meta.dart'; - -class Contact { - final String avatarUrl; - final String name; - - Contact({@required this.avatarUrl, @required this.name}); -} \ No newline at end of file diff --git a/web_native/lib/routes.dart b/web_native/lib/routes.dart deleted file mode 100644 index 3a81cd7..0000000 --- a/web_native/lib/routes.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:imagini/screens/splash/splashscreen.dart'; -import 'package:imagini/screens/me/mescreen.dart'; -import 'package:imagini/screens/login/loginscreen.dart'; -import 'package:imagini/screens/upload/uploadscreen.dart'; -import 'package:imagini/screens/gallery/galleryscreen.dart'; -import 'package:imagini/screens/settings/settingsscreen.dart'; - -final Map routes = { - "/": (BuildContext context) => SplashScreen(), - "/Me": (BuildContext context) => MeScreen(), - "/Login": (BuildContext context) => LoginScreen(), - "/Upload": (BuildContext context) => UploadScreen(), - "/Gallery": (BuildContext context) => GalleryScreen(), - "/Settings": (BuildContext context) => SettingsScreen(), -}; diff --git a/web_native/lib/screens/HomeScreen.dart b/web_native/lib/screens/HomeScreen.dart new file mode 100644 index 0000000..8d0c0bb --- /dev/null +++ b/web_native/lib/screens/HomeScreen.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; + +class HomeScreen extends StatefulWidget { + static const String PATH = '/Home'; + + HomeScreen({Key key}) : super(key: key); + + @override + _HomeScreenState createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + + // HomeBloc bloc; + + void _init(){ + // if(null == bloc){ + // bloc = HomeBloc(AppProvider.getApplication(context)); + // } + } + + @override + void dispose() { + super.dispose(); + // bloc.dispose(); + } + + @override + Widget build(BuildContext context) { + _init(); + + return PlatformScaffold( + body: _buildGridView(), + bottomNavBar: _buildNavBar() + ); + } + + Widget _buildNavBar() { + return PlatformNavBar( + currentIndex: 0, + itemChanged: (index) => setState( + () { + // _selectedTabIndex = index; + print(index); + }, + ), + items: [ + BottomNavigationBarItem( + label: "Gallery", + icon: Icon(PlatformIcons(context).collections), + ), + BottomNavigationBarItem( + label: "Me", + icon: Icon(PlatformIcons(context).person), + ), + BottomNavigationBarItem( + label: "Settings", + icon: Icon(PlatformIcons(context).settings), + ), + ], + ); + } + + Widget _buildGridView() { + // return GridView.builder( + // itemCount: 5, + // gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount( + // childAspectRatio: (1 / 1), + // crossAxisCount: 2), + // itemBuilder: (BuildContext context, int index) { + // return _buildCard("https://i.imgur.com/CgSGqUz.jpeg"); + // } + // ); + + + return new StaggeredGridView.countBuilder( + crossAxisCount: 4, + itemCount: 80, + itemBuilder: (BuildContext context, int index) => _buildCard("https://i.imgur.com/CgSGqUz.jpeg"), + // itemBuilder: (BuildContext context, int index) => new Container( + // color: Colors.green, + // child: new Center( + // child: new CircleAvatar( + // backgroundColor: Colors.white, + // child: new Text('$index'), + // ), + // )), + staggeredTileBuilder: (int index) => + // new StaggeredTile.count(2, index.isEven ? 2 : 1), + new StaggeredTile.fit(2), + mainAxisSpacing: 4.0, + crossAxisSpacing: 4.0, + ); + } + + Widget _buildCard(charImageUrl) { + return new Image.network( + charImageUrl, + fit: BoxFit.contain, + ); + } +} diff --git a/web_native/lib/screens/LoginScreen.dart b/web_native/lib/screens/LoginScreen.dart new file mode 100644 index 0000000..eb344c3 --- /dev/null +++ b/web_native/lib/screens/LoginScreen.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; + +import 'package:fluro/fluro.dart'; +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; + +import 'package:imagini/core/AppProvider.dart'; + +class LoginScreen extends StatefulWidget { + static const String PATH = '/Login'; + + LoginScreen({Key key}) : super(key: key); + + @override + _LoginScreenState createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State { + + // LoginBloc bloc; + + @override + void dispose() { + super.dispose(); + // bloc.dispose(); + } + + @override + Widget build(BuildContext context) { + _init(); + + return Scaffold( + body: Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 500), + child: Container( + margin: EdgeInsets.fromLTRB(50, 0, 50, 0), + height: 500, + child: Column( + children: [ + Container( + child: FittedBox( + fit: BoxFit.contain, + child: const FlutterLogo(), + ), + width: 175, + margin: EdgeInsets.fromLTRB(0, 0, 0, 50), + ), + Expanded( + child: TextField( + decoration: InputDecoration( + labelText: 'Server Address' + ), + ), + ), + Expanded( + child: TextField( + decoration: InputDecoration( + labelText: 'Username / Email' + ), + ), + ), + Expanded( + child: TextField( + obscureText: true, + enableSuggestions: false, + autocorrect: false, + decoration: InputDecoration( + labelText: 'Password' + ), + ), + ), + SizedBox( + width: double.infinity, + child: PlatformButton( + onPressed: () { + AppProvider.getRouter(context).navigateTo(context, "/Home", transition: TransitionType.fadeIn); + }, + child: Text('Login') + ), + ), + ], + ), + ), + ), + ), + ); + } + + void _init(){ + // if(null == bloc){ + // bloc = LoginBloc(AppProvider.getApplication(context)); + // } + } +} diff --git a/web_native/lib/screens/SplashScreen.dart b/web_native/lib/screens/SplashScreen.dart new file mode 100644 index 0000000..1a3a36a --- /dev/null +++ b/web_native/lib/screens/SplashScreen.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:fluro/fluro.dart'; + +import 'package:imagini/models/api/response/LoginResponse.dart'; +import 'package:imagini/core/AppProvider.dart'; +import 'package:imagini/blocs/SplashBloc.dart'; + +class SplashScreen extends StatefulWidget { + static const String PATH = '/'; + + SplashScreen({Key key}) : super(key: key); + + @override + _SplashScreenState createState() => _SplashScreenState(); +} + +class _SplashScreenState extends State { + + SplashBloc bloc; + + @override + void dispose() { + super.dispose(); + bloc.dispose(); + } + + @override + Widget build(BuildContext context) { + _init(); + + return Scaffold( + body: Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 500), + child: Container( + margin: EdgeInsets.fromLTRB(50, 0, 50, 0), + height: 270, + child: Column( + children: [ + Container( + child: FittedBox( + fit: BoxFit.contain, + child: const FlutterLogo(), + ), + width: 175, + margin: EdgeInsets.fromLTRB(0, 0, 0, 50), + ), + PlatformCircularProgressIndicator() + ], + ), + ), + ), + ), + ); + } + + void _init(){ + if(null == bloc){ + bloc = SplashBloc(AppProvider.getApplication(context)); + bloc.loginResult.listen((LoginResponse lr) { + if (lr.error != null) { + AppProvider.getRouter(context).navigateTo(context, "/Login", transition: TransitionType.fadeIn); + } else { + AppProvider.getRouter(context).navigateTo(context, "/Home", transition: TransitionType.fadeIn); + } + }); + } + } +} diff --git a/web_native/lib/screens/TemplateScreen.dart.template b/web_native/lib/screens/TemplateScreen.dart.template new file mode 100644 index 0000000..791c94c --- /dev/null +++ b/web_native/lib/screens/TemplateScreen.dart.template @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +class TemplatesScreen extends StatefulWidget { + static const String PATH = '/Templates'; + + TemplatesScreen({Key key}) : super(key: key); + + @override + _TemplatesScreenState createState() => _TemplatesScreenState(); +} + +class _TemplatesScreenState extends State { + + // TemplatesBloc bloc; + + @override + void dispose() { + super.dispose(); + // bloc.dispose(); + } + + @override + Widget build(BuildContext context) { + _init(); + + return Scaffold( + body: Center( + child: Text("Templates") + ), + ); + } + + void _init(){ + // if(null == bloc){ + // bloc = TemplatesBloc(AppProvider.getApplication(context)); + // } + } +} diff --git a/web_native/lib/screens/login.dart b/web_native/lib/screens/login.dart deleted file mode 100644 index 3fe6791..0000000 --- a/web_native/lib/screens/login.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:imagini/blocs/login/bloc.dart'; -import 'package:imagini/blocs/login/states.dart'; -import 'package:imagini/blocs/login/events.dart'; -import 'package:imagini/blocs/theme/bloc.dart'; -import 'package:imagini/blocs/theme/events.dart'; -import 'package:imagini/settings/preferences.dart'; -import 'package:imagini/settings/app_themes.dart'; - -class LoginScreen extends StatefulWidget { - @override - _LoginScreenState createState() => _LoginScreenState(); -} - -class _LoginScreenState extends State { - @override - void initState() { - super.initState(); - _loadTheme(); - _loadLogin(); - } - - _loadTheme() async { - context.read().add(ThemeEvent(appTheme: Preferences.getTheme())); - } - - _loadLogin() async { - context.read().add(LoginEvents.loginResult); - } - - _setTheme(bool darkTheme) async { - AppTheme selectedTheme = - darkTheme ? AppTheme.lightTheme : AppTheme.darkTheme; - context.read().add(ThemeEvent(appTheme: selectedTheme)); - Preferences.saveTheme(selectedTheme); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: _body(), - ); - } - - _body() { - return BlocBuilder(builder: (BuildContext context, LoginState state) { - - // Set Theme - _setTheme(true); - - if (state is LoginNeeded) { - // TODO: Load Login Form - return Center( child: Text("Login Needed") ); - } - if (state is LoginFailed) { - // TODO: Update Form Failed - return Center( child: Text("Login Failed: ${state.error.message.toString()}") ); - } - if (state is LoginSuccess) { - // TODO: Navigate to /Gallery - return Center( child: Text("Login Success") ); - } - - // TODO: Login Screen - return Center( child: Text("Login Loading") ); - }); - } -} diff --git a/web_native/lib/screens/old/gallery/components/body.dart b/web_native/lib/screens/old/gallery/components/body.dart deleted file mode 100644 index 51d2aed..0000000 --- a/web_native/lib/screens/old/gallery/components/body.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -class Body extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Center( - child: RaisedButton( - onPressed: () { - Navigator.pushNamed(context, '/Upload'); - }, - child: Text('Page Two!'), - ), - ); - } -} diff --git a/web_native/lib/screens/old/gallery/gallery-bloc.dart b/web_native/lib/screens/old/gallery/gallery-bloc.dart deleted file mode 100644 index 76a8ee0..0000000 --- a/web_native/lib/screens/old/gallery/gallery-bloc.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:async'; -import 'dart:ui'; -import 'package:flutter/services.dart'; -import 'package:imagini/models/contact.dart'; -import 'package:flutter/material.dart'; -import 'package:imagini/bloc/bloc.dart'; - -class GalleryBloc extends Bloc { - StreamSubscription _audioPlayerStateSubscription; - - Stream get example => _exampleSubject.stream; - Sink get exampleSink => _exampleSubject.sink; - final StreamController _exampleSubject = StreamController(); - - GalleryBloc(); - - void dispose() { - _exampleSubject.close(); - } -} diff --git a/web_native/lib/screens/old/gallery/galleryscreen.dart b/web_native/lib/screens/old/gallery/galleryscreen.dart deleted file mode 100644 index d2df30a..0000000 --- a/web_native/lib/screens/old/gallery/galleryscreen.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:imagini/screens/gallery/components/body.dart'; -import 'package:imagini/screens/gallery/gallery-bloc.dart'; -import 'package:imagini/bloc/bloc-prov.dart'; -import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; - - -class GalleryScreen extends StatefulWidget { - @override - _GalleryScreenState createState() => _GalleryScreenState(); -} - -class _GalleryScreenState extends State { - GalleryBloc exampleBloc; - - @override - void initState() { - super.initState(); - exampleBloc = GalleryBloc(); - } - - @override - void dispose() { - exampleBloc.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return BlocProvider( - bloc: exampleBloc, - child: PlatformScaffold( - appBar: PlatformAppBar( - title: Text('Gallery'), - cupertino: (_, __) => CupertinoNavigationBarData( - // Issue with cupertino where a bar with no transparency - // will push the list down. Adding some alpha value fixes it (in a hacky way) - backgroundColor: Colors.lightGreen.withAlpha(254), - ), - ), - body: Body(), - ), - ); - } -} diff --git a/web_native/lib/screens/old/login/components/body.dart b/web_native/lib/screens/old/login/components/body.dart deleted file mode 100644 index 7e0cbbf..0000000 --- a/web_native/lib/screens/old/login/components/body.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; - -class Body extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Center( - child: Container( - margin: EdgeInsets.fromLTRB(50, 0, 50, 0), - height: 500, - child: Column( - children: [ - Container( - child: FittedBox( - fit: BoxFit.contain, - child: const FlutterLogo(), - ), - width: 175, - margin: EdgeInsets.fromLTRB(0, 0, 0, 50), - ), - Expanded( - child: TextField( - decoration: InputDecoration( - labelText: 'Server Address' - ), - ), - ), - Expanded( - child: TextField( - decoration: InputDecoration( - labelText: 'Username / Email' - ), - ), - ), - Expanded( - child: TextField( - obscureText: true, - enableSuggestions: false, - autocorrect: false, - decoration: InputDecoration( - labelText: 'Password' - ), - ), - ), - SizedBox( - width: double.infinity, - height: 50, - child: RaisedButton( - onPressed: () { - Navigator.pushNamed(context, '/Gallery'); - }, - child: Text('Login') - ), - ), - ], - ), - ), - ); - } -} diff --git a/web_native/lib/screens/old/login/login-bloc.dart b/web_native/lib/screens/old/login/login-bloc.dart deleted file mode 100644 index 0abd766..0000000 --- a/web_native/lib/screens/old/login/login-bloc.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:async'; -import 'dart:ui'; -import 'package:flutter/services.dart'; -import 'package:imagini/models/contact.dart'; -import 'package:flutter/material.dart'; -import 'package:imagini/bloc/bloc.dart'; - -class LoginBloc extends Bloc { - StreamSubscription _audioPlayerStateSubscription; - - Stream get example => _exampleSubject.stream; - Sink get exampleSink => _exampleSubject.sink; - final StreamController _exampleSubject = StreamController(); - - LoginBloc(); - - void dispose() { - _exampleSubject.close(); - } -} diff --git a/web_native/lib/screens/old/login/loginscreen.dart b/web_native/lib/screens/old/login/loginscreen.dart deleted file mode 100644 index 91b3bca..0000000 --- a/web_native/lib/screens/old/login/loginscreen.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:imagini/screens/login/components/body.dart'; -import 'package:imagini/screens/login/login-bloc.dart'; -import 'package:imagini/bloc/bloc-prov.dart'; - -class LoginScreen extends StatefulWidget { - @override - _LoginScreenState createState() => _LoginScreenState(); -} - -class _LoginScreenState extends State { - LoginBloc loginBloc; - - @override - void initState() { - super.initState(); - loginBloc = LoginBloc(); - } - - @override - void dispose() { - loginBloc.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return BlocProvider( - bloc: LoginBloc(), - child: Scaffold( - // appBar: AppBar( - // title: Text("Login"), - // ), - body: Body(), - ), - ); - } -} diff --git a/web_native/lib/screens/old/me/components/body.dart b/web_native/lib/screens/old/me/components/body.dart deleted file mode 100644 index 51d2aed..0000000 --- a/web_native/lib/screens/old/me/components/body.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -class Body extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Center( - child: RaisedButton( - onPressed: () { - Navigator.pushNamed(context, '/Upload'); - }, - child: Text('Page Two!'), - ), - ); - } -} diff --git a/web_native/lib/screens/old/me/me-bloc.dart b/web_native/lib/screens/old/me/me-bloc.dart deleted file mode 100644 index 20e3afa..0000000 --- a/web_native/lib/screens/old/me/me-bloc.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:async'; -import 'dart:ui'; -import 'package:flutter/services.dart'; -import 'package:imagini/models/contact.dart'; -import 'package:flutter/material.dart'; -import 'package:imagini/bloc/bloc.dart'; - -class MeBloc extends Bloc { - StreamSubscription _audioPlayerStateSubscription; - - Stream get example => _exampleSubject.stream; - Sink get exampleSink => _exampleSubject.sink; - final StreamController _exampleSubject = StreamController(); - - MeBloc(); - - void dispose() { - _exampleSubject.close(); - } -} diff --git a/web_native/lib/screens/old/me/mescreen.dart b/web_native/lib/screens/old/me/mescreen.dart deleted file mode 100644 index 63dd476..0000000 --- a/web_native/lib/screens/old/me/mescreen.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:imagini/screens/me/components/body.dart'; -import 'package:imagini/screens/me/me-bloc.dart'; -import 'package:imagini/bloc/bloc-prov.dart'; -import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; - - -class MeScreen extends StatefulWidget { - @override - _MeScreenState createState() => _MeScreenState(); -} - -class _MeScreenState extends State { - MeBloc exampleBloc; - - @override - void initState() { - super.initState(); - exampleBloc = MeBloc(); - } - - @override - void dispose() { - exampleBloc.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return BlocProvider( - bloc: exampleBloc, - child: PlatformScaffold( - appBar: PlatformAppBar( - title: Text('Mes'), - cupertino: (_, __) => CupertinoNavigationBarData( - // Issue with cupertino where a bar with no transparency - // will push the list down. Adding some alpha value fixes it (in a hacky way) - backgroundColor: Colors.lightGreen.withAlpha(254), - ), - ), - body: Body(), - ), - ); - } -} diff --git a/web_native/lib/screens/old/settings/components/body.dart b/web_native/lib/screens/old/settings/components/body.dart deleted file mode 100644 index 51d2aed..0000000 --- a/web_native/lib/screens/old/settings/components/body.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -class Body extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Center( - child: RaisedButton( - onPressed: () { - Navigator.pushNamed(context, '/Upload'); - }, - child: Text('Page Two!'), - ), - ); - } -} diff --git a/web_native/lib/screens/old/settings/settings-bloc.dart b/web_native/lib/screens/old/settings/settings-bloc.dart deleted file mode 100644 index f3a9bc2..0000000 --- a/web_native/lib/screens/old/settings/settings-bloc.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:async'; -import 'dart:ui'; -import 'package:flutter/services.dart'; -import 'package:imagini/models/contact.dart'; -import 'package:flutter/material.dart'; -import 'package:imagini/bloc/bloc.dart'; - -class SettingsBloc extends Bloc { - StreamSubscription _audioPlayerStateSubscription; - - Stream get example => _exampleSubject.stream; - Sink get exampleSink => _exampleSubject.sink; - final StreamController _exampleSubject = StreamController(); - - SettingsBloc(); - - void dispose() { - _exampleSubject.close(); - } -} diff --git a/web_native/lib/screens/old/settings/settingsscreen.dart b/web_native/lib/screens/old/settings/settingsscreen.dart deleted file mode 100644 index ba6c4b7..0000000 --- a/web_native/lib/screens/old/settings/settingsscreen.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:imagini/screens/settings/components/body.dart'; -import 'package:imagini/screens/settings/settings-bloc.dart'; -import 'package:imagini/bloc/bloc-prov.dart'; -import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; - - -class SettingsScreen extends StatefulWidget { - @override - _SettingsScreenState createState() => _SettingsScreenState(); -} - -class _SettingsScreenState extends State { - SettingsBloc exampleBloc; - - @override - void initState() { - super.initState(); - exampleBloc = SettingsBloc(); - } - - @override - void dispose() { - exampleBloc.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return BlocProvider( - bloc: exampleBloc, - child: PlatformScaffold( - appBar: PlatformAppBar( - title: Text('Settings'), - cupertino: (_, __) => CupertinoNavigationBarData( - // Issue with cupertino where a bar with no transparency - // will push the list down. Adding some alpha value fixes it (in a hacky way) - backgroundColor: Colors.lightGreen.withAlpha(254), - ), - ), - body: Body(), - ), - ); - } -} diff --git a/web_native/lib/screens/old/splash/components/body.dart b/web_native/lib/screens/old/splash/components/body.dart deleted file mode 100644 index 8f45789..0000000 --- a/web_native/lib/screens/old/splash/components/body.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; - -class Body extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Center( - child: RaisedButton( - onPressed: () { - // Back (popping) - // Navigator.pop(context); - - // Go to Login Screen - Navigator.pushNamed(context, '/Login'); - }, - child: Text('Login'), - ), - ); - } -} diff --git a/web_native/lib/screens/old/splash/splash-bloc.dart b/web_native/lib/screens/old/splash/splash-bloc.dart deleted file mode 100644 index 2d06753..0000000 --- a/web_native/lib/screens/old/splash/splash-bloc.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:async'; -import 'dart:ui'; -import 'package:flutter/services.dart'; -import 'package:imagini/models/contact.dart'; -import 'package:flutter/material.dart'; -import 'package:imagini/bloc/bloc.dart'; - -class SplashBloc extends Bloc { - StreamSubscription _audioPlayerStateSubscription; - - Stream get example => _exampleSubject.stream; - Sink get exampleSink => _exampleSubject.sink; - final StreamController _exampleSubject = StreamController(); - - SplashBloc(); - - void dispose() { - _exampleSubject.close(); - } -} diff --git a/web_native/lib/screens/old/upload/upload-bloc.dart b/web_native/lib/screens/old/upload/upload-bloc.dart deleted file mode 100644 index fcc97e2..0000000 --- a/web_native/lib/screens/old/upload/upload-bloc.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:async'; -import 'dart:ui'; -import 'package:flutter/services.dart'; -import 'package:imagini/models/contact.dart'; -import 'package:flutter/material.dart'; -import 'package:imagini/bloc/bloc.dart'; - -class UploadBloc extends Bloc { - StreamSubscription _audioPlayerStateSubscription; - - Stream get example => _exampleSubject.stream; - Sink get exampleSink => _exampleSubject.sink; - final StreamController _exampleSubject = StreamController(); - - UploadBloc(); - - void dispose() { - _exampleSubject.close(); - } -} diff --git a/web_native/lib/screens/old/upload/uploadscreen.dart b/web_native/lib/screens/old/upload/uploadscreen.dart deleted file mode 100644 index dca1c1b..0000000 --- a/web_native/lib/screens/old/upload/uploadscreen.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'package:cross_file/cross_file.dart' show XFile; -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; -import 'package:imagini/bloc/bloc-prov.dart'; -import 'package:imagini/screens/upload/upload-bloc.dart'; -import 'package:tus_client/tus_client.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class UploadScreen extends StatefulWidget { - @override - _UploadScreenState createState() => _UploadScreenState(); -} - -class _UploadScreenState extends State { - UploadBloc exampleBloc; - - double _progress = 0; - XFile _file; - TusClient _client; - Uri _fileUrl; - - @override - void initState() { - super.initState(); - exampleBloc = UploadBloc(); - } - - @override - void dispose() { - exampleBloc.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return BlocProvider( - bloc: exampleBloc, - child: PlatformScaffold( - appBar: PlatformAppBar( - title: Text('Uploads'), - cupertino: (_, __) => CupertinoNavigationBarData( - // Issue with cupertino where a bar with no transparency - // will push the list down. Adding some alpha value fixes it (in a hacky way) - backgroundColor: Colors.lightGreen.withAlpha(254), - ), - ), - body: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - SizedBox(height: 12), - Padding( - padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2), - child: Text( - "This demo uses TUS client to upload a file", - style: TextStyle(fontSize: 18), - ), - ), - Padding( - padding: const EdgeInsets.all(6), - child: Card( - color: Colors.teal, - child: InkWell( - onTap: () async { - _file = - await _getXFile(await FilePicker.platform.pickFiles()); - setState(() { - _progress = 0; - _fileUrl = null; - }); - }, - child: Container( - padding: EdgeInsets.all(20), - child: Column( - children: [ - Icon(Icons.cloud_upload, color: Colors.white, size: 60), - Text( - "Upload a file", - style: TextStyle(fontSize: 25, color: Colors.white), - ), - ], - ), - ), - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8), - child: Row( - children: [ - Expanded( - child: RaisedButton( - onPressed: _file == null - ? null - : () async { - // Create a client - print("Create a client"); - _client = TusClient( - Uri.parse("https://master.tus.io/files/"), - _file, - store: TusMemoryStore(), - ); - - print("Starting upload"); - await _client.upload( - onComplete: () async { - print("Completed!"); - setState(() => _fileUrl = _client.uploadUrl); - }, - onProgress: (progress) { - print("Progress: $progress"); - setState(() => _progress = progress); - }, - ); - }, - child: Text("Upload"), - ), - ), - SizedBox(width: 8), - Expanded( - child: RaisedButton( - onPressed: _progress == 0 - ? null - : () async { - _client.pause(); - }, - child: Text("Pause"), - ), - ), - ], - ), - ), - Stack( - children: [ - Container( - margin: const EdgeInsets.all(8), - padding: const EdgeInsets.all(1), - color: Colors.grey, - width: double.infinity, - child: Text(" "), - ), - FractionallySizedBox( - widthFactor: _progress / 100, - child: Container( - margin: const EdgeInsets.all(8), - padding: const EdgeInsets.all(1), - color: Colors.green, - child: Text(" "), - ), - ), - Container( - margin: const EdgeInsets.all(8), - padding: const EdgeInsets.all(1), - width: double.infinity, - child: Text("Progress: ${_progress.toStringAsFixed(1)}%"), - ), - ], - ), - GestureDetector( - onTap: _progress != 100 - ? null - : () async { - await launch(_fileUrl.toString()); - }, - child: Container( - color: _progress == 100 ? Colors.green : Colors.grey, - padding: const EdgeInsets.all(8.0), - margin: const EdgeInsets.all(8.0), - child: - Text(_progress == 100 ? "Link to view:\n $_fileUrl" : "-"), - ), - ), - ], - ), - ), - ), - ); - } - - Future _getXFile(FilePickerResult result) async { - if (result != null) { - final chosenFile = result.files.first; - if (chosenFile.path != null) { - // Android, iOS, Desktop - return XFile(chosenFile.path); - } else { - // Web - return XFile.fromData( - chosenFile.bytes, - name: chosenFile.name, - ); - } - } - return null; - } -} diff --git a/web_native/lib/services/api/api.dart b/web_native/lib/services/api/api.dart deleted file mode 100644 index 84964c1..0000000 --- a/web_native/lib/services/api/api.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'dart:io'; -import 'dart:async'; -import 'dart:convert'; -import 'package:http/http.dart' as http; -// import 'package:flutter/foundation.dart' show kIsWeb; -// import 'package:flutter_secure_storage/flutter_secure_storage.dart'; - -// if(kIsWeb) { -// // Use web storage -// } else { -// // Use flutter_secure_storage -// } - -// final _storage = FlutterSecureStorage(); - -class ImaginiAPI { - static const _baseUrl = "http://10.0.20.170:8484"; - // static const String _GET_ALBUMS = '/albums'; - - Future exampleApi() async { - http.Response response = await http.get( - Uri.encodeFull("https://www.example.com/api"), - ); - print("Status: ${response.statusCode.toString()}"); - print("Respone: ${response.body.toString()}"); - return response.body; - } - - Future> loginAPI(String user, String password) async { - http.Response response = await http.post( - Uri.encodeFull(_baseUrl + "/api/v1/Login"), - body: jsonEncode({ - 'user': user, - 'password': password, - }), - ).timeout(Duration(seconds: 10)); - - - - // TODO: - // - StatusCode: - // - 405 (StatusMethodNotAllowed) - // - 400 (StatusBadRequest) - // - 401 (StatusUnauthorized) - // - - // - - // - - - String setCookieVal = response.headers["set-cookie"]; - List allCookies = setCookieVal.split(',') - .map((cookie) => Cookie.fromSetCookieValue(cookie)).toList(); - - Cookie accessToken = allCookies.firstWhere((cookie) => cookie.name == "AccessToken"); - Cookie refreshToken = allCookies.firstWhere((cookie) => cookie.name == "RefreshToken"); - - print("Status: ${response.statusCode.toString()}"); - print("Body: ${response.body.toString()}"); - print("AccessToken: ${accessToken.toString()}"); - print("RefreshToken: ${refreshToken.toString()}"); - return response.headers; - } - - Future mediaItemsAPI(String albumID, String tagID) async { - return null; - } - - Future tagsAPI() async { - return null; - } - - Future albumsAPI() async { - return null; - } - - Future meAPI() async { - return null; - } - - // API Calls: - // - Login - // - MediaItems - // - Tags - // - Albums - // - Me -} diff --git a/web_native/lib/services/api/exceptions.dart b/web_native/lib/services/api/exceptions.dart deleted file mode 100644 index c455899..0000000 --- a/web_native/lib/services/api/exceptions.dart +++ /dev/null @@ -1,19 +0,0 @@ -class ConnectionRefusedException { - var message; - ConnectionRefusedException(this.message); -} - -class NoServiceFoundException { - var message; - NoServiceFoundException(this.message); -} - -class InvalidFormatException { - var message; - InvalidFormatException(this.message); -} - -class UnknownException { - var message; - UnknownException(this.message); -} diff --git a/web_native/lib/settings/app_themes.dart b/web_native/lib/settings/app_themes.dart deleted file mode 100644 index 7d6a481..0000000 --- a/web_native/lib/settings/app_themes.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -class AppThemes { - static final appThemeData = { - AppTheme.lightTheme: ThemeData( - scaffoldBackgroundColor: Colors.white, - primarySwatch: Colors.blue, - backgroundColor: Colors.white, - textTheme: TextTheme( - bodyText1: TextStyle( - color: Colors.black, - ), - ), - ), - AppTheme.darkTheme: ThemeData( - scaffoldBackgroundColor: Colors.black, - primarySwatch: Colors.teal, - backgroundColor: Colors.black, - textTheme: TextTheme( - bodyText1: TextStyle( - color: Colors.white, - ), - ), - ) - }; -} - -enum AppTheme { - lightTheme, - darkTheme, -} diff --git a/web_native/lib/settings/preferences.dart b/web_native/lib/settings/preferences.dart deleted file mode 100644 index ea7018c..0000000 --- a/web_native/lib/settings/preferences.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:convert'; - -import 'package:imagini/settings/app_themes.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -class Preferences { - // - static SharedPreferences preferences; - static const String KEY_SELECTED_THEME = 'key_selected_theme'; - - static init() async { - preferences = await SharedPreferences.getInstance(); - } - - static void saveTheme(AppTheme selectedTheme) async { - if (null == selectedTheme) { - selectedTheme = AppTheme.lightTheme; - } - String theme = jsonEncode(selectedTheme.toString()); - preferences.setString(KEY_SELECTED_THEME, theme); - } - - static AppTheme getTheme() { - String theme = preferences.getString(KEY_SELECTED_THEME); - if (null == theme) { - return AppTheme.lightTheme; - } - return getThemeFromString(jsonDecode(theme)); - } - - static AppTheme getThemeFromString(String themeString) { - for (AppTheme theme in AppTheme.values) { - if (theme.toString() == themeString) { - return theme; - } - } - return null; - } -} diff --git a/web_native/lib/theme-old/style.dart b/web_native/lib/theme-old/style.dart deleted file mode 100644 index 1923240..0000000 --- a/web_native/lib/theme-old/style.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; - -ThemeData appTheme() { - return ThemeData( - primaryColor: Colors.white, - accentColor: Colors.orange, - hintColor: Colors.white, - dividerColor: Colors.white, - buttonColor: Colors.white, - scaffoldBackgroundColor: Colors.blue, - canvasColor: Colors.blue, - ); -} diff --git a/web_native/packages/imagini_api/lib/imagini_api.dart b/web_native/packages/imagini_api/lib/imagini_api.dart new file mode 100644 index 0000000..17eecc4 --- /dev/null +++ b/web_native/packages/imagini_api/lib/imagini_api.dart @@ -0,0 +1,3 @@ +library imagini_api; + +export 'src/imagini_api.dart'; diff --git a/web_native/packages/imagini_api/lib/src/imagini_api.dart b/web_native/packages/imagini_api/lib/src/imagini_api.dart new file mode 100644 index 0000000..eb959d1 --- /dev/null +++ b/web_native/packages/imagini_api/lib/src/imagini_api.dart @@ -0,0 +1,136 @@ +import 'dart:io'; +import 'dart:async'; +import 'dart:convert'; +import 'package:http/http.dart' as http; + +import 'package:meta/meta.dart'; + +enum ImaginiState { unknown, authenticated, unauthenticated } + +class ImaginiAPI{ + String _server, _accessToken, _refreshToken; + final _controller = StreamController(); + + ImaginiAPI({ + @required String server, + String accessToken, + String refreshToken + }) { + _server = server; + _accessToken = accessToken; + _refreshToken = refreshToken; + } + + Stream get status async* { + await Future.delayed(const Duration(seconds: 1)); + yield ImaginiState.unauthenticated; + yield* _controller.stream; + } + + // Endpoint: /api/v1/Login + Future login([ + String username, + String password, + ]) async { + assert( + (username != null && password != null) || + (_accessToken != null && _refreshToken != null) + ); + + http.Response response = await http.post( + Uri.encodeFull(_server + "/api/v1/Login"), + body: jsonEncode({ + 'user': username, + 'password': password, + }), + ).timeout(Duration(seconds: 10)); + + if (response.statusCode != 200) { + // Fuck + return; + } + + // Success + String setCookieVal = response.headers["set-cookie"]; + List allCookies = setCookieVal.split(',') + .map((cookie) => Cookie.fromSetCookieValue(cookie)).toList(); + + Cookie accessToken = allCookies.firstWhere((cookie) => cookie.name == "AccessToken"); + Cookie refreshToken = allCookies.firstWhere((cookie) => cookie.name == "RefreshToken"); + + _accessToken = accessToken.toString(); + _refreshToken = refreshToken.toString(); + + _controller.add(ImaginiState.authenticated); + + print("Status: ${response.statusCode.toString()}"); + print("Body: ${response.body.toString()}"); + print("AccessToken: ${accessToken.toString()}"); + print("RefreshToken: ${refreshToken.toString()}"); + } + + void logout() { + _controller.add(ImaginiState.unauthenticated); + } + + Future mediaItems([ + String startDate, + String endDate, + String albumID, + List tagID, + String type, // TODO: Make enum + int page, + ]) async { + // Query: + // /api/v1/MediaItems + // Derive Params: + // startDate: + // &createdAt=>2020-10-10T10:10:10 + // endDate: + // &createdAt=<2020-10-10T10:10:10 + // albumID: + // &albumID=9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d + // tagID: + // &tagID=9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d,9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d + // type: + // &type=Photos + // &type=Videos + // page: + // &page=4 + + // Returns: + // { + // + // } + + return null; + } + + Future tags([ + int page + ]) async { + // Query: + // /api/v1/Tags + // Derive Params: + // page: + // &page=4 + return null; + } + + Future albums([ + int page + ]) async { + // Query: + // /api/v1/Albums + // Derive Params: + // page: + // &page=4 + return null; + } + + Future me() async { + return null; + } + + void dispose() => _controller.close(); +} diff --git a/web_native/packages/imagini_api/pubspec.yaml b/web_native/packages/imagini_api/pubspec.yaml new file mode 100644 index 0000000..3e00d8d --- /dev/null +++ b/web_native/packages/imagini_api/pubspec.yaml @@ -0,0 +1,9 @@ +name: imagini_api +description: Dart package that implements the Imagini API +version: 1.0.0 + +environment: + sdk: ">=2.1.0 <3.0.0" + +dependencies: + meta: ^1.1.8 diff --git a/web_native/pubspec.lock b/web_native/pubspec.lock index 4af80d7..ed21a07 100644 --- a/web_native/pubspec.lock +++ b/web_native/pubspec.lock @@ -148,6 +148,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.5" + fluro: + dependency: "direct main" + description: + name: fluro + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.8" flutter: dependency: "direct main" description: flutter @@ -186,6 +193,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.3.5" + flutter_staggered_grid_view: + dependency: "direct main" + description: + name: flutter_staggered_grid_view + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.3" flutter_test: dependency: "direct dev" description: flutter diff --git a/web_native/pubspec.yaml b/web_native/pubspec.yaml index 87bcb08..0daa3f8 100644 --- a/web_native/pubspec.yaml +++ b/web_native/pubspec.yaml @@ -23,6 +23,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_staggered_grid_view: "^0.3.2" + fluro: ^1.7.8 equatable: ^1.2.5 http: ^0.12.2 shared_preferences: ^0.5.12+4