diff --git a/.gitignore b/.gitignore
index 1575361..a970ee2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+imagini.db
+media/
notes
web/node_modules/
web/dist/
diff --git a/README.md b/README.md
index fcd8f09..9ca208b 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,32 @@
-#uImagini
+# Imagini
+A self hosted photo library with user management & authentication. Cross platform Client supporting Android, iOS, and Web.
-## Running Server
- # cd ./cmd/
- CONFIG_PATH=$(pwd) DATA_PATH=$(pwd) go run main.go serve
+## Server
+### Running
-## Generate GraphQL Models
- # cd ./cmd/
+ CONFIG_PATH=$(pwd) DATA_PATH=$(pwd) go run cmd/main.go serve
+
+## Building
+
+ # Generate GraphQL Models
go run github.com/99designs/gqlgen generate
- go run main.go generate
+ go run cmd/main.go generate
-## Generate GraphQL Documentation
- # From app root
+ # Generate GraphQL Documentation
graphdoc -e http://localhost:8484/query -o ./docs/schema
-## Server Build
+## Client
+See `web_native` subfolder.
+### Running
-## Flutter Build
+ # Chrome
+ flutter run -d chrome
+
+ # Simulator
+ open -a Simulator
flutter run
-## Generate GraphQL Flutter Models
+### Building
+
+ # Generate GraphQL Flutter Models
flutter pub run build_runner build
diff --git a/internal/api/auth.go b/internal/api/auth.go
index 297d6c1..e357195 100644
--- a/internal/api/auth.go
+++ b/internal/api/auth.go
@@ -41,35 +41,46 @@ func (api *API) refreshTokens(refreshToken jwt.Token) (string, string, error) {
}
// Update Access Token
- accessTokenCookie, err := api.Auth.CreateJWTAccessToken(user, device)
+ accessToken, err := api.Auth.CreateJWTAccessToken(user, device)
if err != nil {
return "", "", err
}
- return accessTokenCookie, "", err
+ return accessToken, "", err
}
func (api *API) validateTokens(w *http.ResponseWriter, r *http.Request) (jwt.Token, error) {
- // TODO: Check from X-Imagini-AccessToken
- // TODO: Check from X-Imagini-RefreshToken
-
- // Validate Access Token
- accessCookie, _ := r.Cookie("AccessToken")
- if accessCookie != nil {
- accessToken, err := api.Auth.ValidateJWTAccessToken(accessCookie.Value)
+ accessTokenHeader := r.Header.Get("X-Imagini-AccessToken")
+ if accessTokenHeader != "" {
+ accessToken, err := api.Auth.ValidateJWTAccessToken(accessTokenHeader)
if err == nil {
return accessToken, nil
}
}
- // Validate Refresh Cookie Exists
- refreshCookie, _ := r.Cookie("RefreshToken")
- if refreshCookie == nil {
+ refreshTokenHeader := r.Header.Get("X-Imagini-RefreshToken")
+ if refreshTokenHeader == "" {
return nil, errors.New("Tokens Invalid")
}
+ // Validate Access Token
+ // accessCookie, _ := r.Cookie("AccessToken")
+ // if accessCookie != nil {
+ // accessToken, err := api.Auth.ValidateJWTAccessToken(accessCookie.Value)
+ // if err == nil {
+ // return accessToken, nil
+ // }
+ // }
+
+ // Validate Refresh Cookie Exists
+ // refreshCookie, _ := r.Cookie("RefreshToken")
+ // if refreshCookie == nil {
+ // return nil, errors.New("Tokens Invalid")
+ // }
+
// Validate Refresh Token
- refreshToken, err := api.Auth.ValidateJWTRefreshToken(refreshCookie.Value)
+ // refreshToken, err := api.Auth.ValidateJWTRefreshToken(refreshCookie.Value)
+ refreshToken, err := api.Auth.ValidateJWTRefreshToken(refreshTokenHeader)
if err != nil {
return nil, errors.New("Tokens Invalid")
}
@@ -81,21 +92,21 @@ func (api *API) validateTokens(w *http.ResponseWriter, r *http.Request) (jwt.Tok
}
// TODO: Actually Refresh Refresh Token
- newRefreshToken = refreshCookie.Value
+ // newRefreshToken = refreshCookie.Value
+ newRefreshToken = refreshTokenHeader
// Set appropriate cookies (TODO: Only for web!)
// Update Access & Refresh Cookies
- http.SetCookie(*w, &http.Cookie{
- Name: "AccessToken",
- Value: newAccessToken,
- })
- http.SetCookie(*w, &http.Cookie{
- Name: "RefreshToken",
- Value: newRefreshToken,
- })
+ // http.SetCookie(*w, &http.Cookie{
+ // Name: "AccessToken",
+ // Value: newAccessToken,
+ // })
+ // http.SetCookie(*w, &http.Cookie{
+ // Name: "RefreshToken",
+ // Value: newRefreshToken,
+ // })
- // Only for iOS & Android (TODO: Remove for web! Only cause affected by CORS during development)
(*w).Header().Set("X-Imagini-AccessToken", newAccessToken)
(*w).Header().Set("X-Imagini-RefreshToken", newRefreshToken)
diff --git a/web_native/README.md b/web_native/README.md
index 0b980d1..6d867ab 100644
--- a/web_native/README.md
+++ b/web_native/README.md
@@ -1,16 +1,16 @@
-# imagini
+# Imagini Client
+A cross platform (iOS, Android, & Web) client used with the Imagini server.
-A new Flutter project.
+## Running
-## Getting Started
+ # Chrome
+ flutter run -d chrome
-This project is a starting point for a Flutter application.
+ # Simulator
+ open -a Simulator
+ flutter run
-A few resources to get you started if this is your first Flutter project:
+## Building
-- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
-- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
-
-For help getting started with Flutter, view our
-[online documentation](https://flutter.dev/docs), which offers tutorials,
-samples, guidance on mobile development, and a full API reference.
+ # Generate GraphQL Flutter Models
+ flutter pub run build_runner build
diff --git a/web_native/graphql/mediaItems.graphql b/web_native/graphql/mediaItems.graphql
new file mode 100644
index 0000000..582746a
--- /dev/null
+++ b/web_native/graphql/mediaItems.graphql
@@ -0,0 +1,18 @@
+query mediaItems($order: Order, $page: Page, $filter: MediaItemFilter) {
+ mediaItems(filter: $filter, page: $page, order: $order) {
+ data {
+ id
+ fileName
+ latitude
+ longitude
+ isVideo
+ origName
+ createdAt
+ }
+ page {
+ size
+ page
+ total
+ }
+ }
+}
diff --git a/web_native/ios/Podfile.lock b/web_native/ios/Podfile.lock
index 51d516e..9e71cbf 100644
--- a/web_native/ios/Podfile.lock
+++ b/web_native/ios/Podfile.lock
@@ -1,4 +1,7 @@
PODS:
+ - connectivity (0.0.1):
+ - Flutter
+ - Reachability
- DKImagePickerController/Core (4.3.2):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
@@ -38,6 +41,9 @@ PODS:
- Flutter
- integration_test (0.0.1):
- Flutter
+ - path_provider (0.0.1):
+ - Flutter
+ - Reachability (3.2)
- SDWebImage (5.10.2):
- SDWebImage/Core (= 5.10.2)
- SDWebImage/Core (5.10.2)
@@ -48,10 +54,12 @@ PODS:
- Flutter
DEPENDENCIES:
+ - connectivity (from `.symlinks/plugins/connectivity/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)
+ - path_provider (from `.symlinks/plugins/path_provider/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
@@ -59,10 +67,13 @@ SPEC REPOS:
trunk:
- DKImagePickerController
- DKPhotoGallery
+ - Reachability
- SDWebImage
- SwiftyGif
EXTERNAL SOURCES:
+ connectivity:
+ :path: ".symlinks/plugins/connectivity/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
@@ -71,18 +82,23 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
integration_test:
:path: ".symlinks/plugins/integration_test/ios"
+ path_provider:
+ :path: ".symlinks/plugins/path_provider/ios"
shared_preferences:
:path: ".symlinks/plugins/shared_preferences/ios"
url_launcher:
:path: ".symlinks/plugins/url_launcher/ios"
SPEC CHECKSUMS:
+ connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467
DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
- integration_test: 5ed24a436eb7ec17b6a13046e9bf7ca4a404e59e
+ integration_test: 6eb66a19f7104200dcfdd62bc0077e1b09686e4f
+ path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
+ Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
SDWebImage: b969dcfc02c40a5da71eac0b03b8f1a0c794a86f
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40
diff --git a/web_native/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/web_native/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 1d526a1..919434a 100644
--- a/web_native/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/web_native/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -2,6 +2,6 @@
+ location = "self:">
diff --git a/web_native/lib/api/api_provider.dart b/web_native/lib/api/api_provider.dart
index 7b48f0d..b1647cf 100644
--- a/web_native/lib/api/api_provider.dart
+++ b/web_native/lib/api/api_provider.dart
@@ -1,61 +1,51 @@
import 'dart:async';
-// ignore: uri_does_not_exist
-import 'cookie_client_stub.dart'
- // ignore: uri_does_not_exist
- if (dart.library.html) 'browser_cookie_client.dart'
- // ignore: uri_does_not_exist
- if (dart.library.io) 'io_cookie_client.dart';
+import 'package:imagini/api/cookie_client/cookie_client.dart'
+ if (dart.library.html) 'package:imagini/api/cookie_client/browser_cookie_client.dart'
+ if (dart.library.io) 'package:imagini/api/cookie_client/io_cookie_client.dart';
-import 'package:meta/meta.dart';
+import 'package:imagini/core/storage_client/base_storage_client.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:imagini/graphql/imagini_graphql.dart';
class APIProvider{
- String _server, _accessToken, _refreshToken;
-
- GraphQLClient _client;
- HttpLink httpLink;
- // CookieLink cookieLink;
static const String _GRAPHQL_ENDPOINT = "/query";
- APIProvider({
- @required String server,
- String accessToken,
- String refreshToken
- }) {
- _server = server;
- _accessToken = accessToken;
- _refreshToken = refreshToken;
+ BaseStorageClient _storage;
+ GraphQLClient _client;
+ HttpLink httpLink;
+
+ APIProvider(BaseStorageClient storage) {
+ _storage = storage;
+ init();
+ }
+
+ Future init() async {
+ String _server = await _storage.get("server");
+
httpLink = HttpLink(_server + _GRAPHQL_ENDPOINT,
- httpClient: getCookieClient(),
+ httpClient: getCookieClient(_storage),
);
- // cookieLink = CookieLink(_updateAccessToken, _updateRefreshToken);
_client = GraphQLClient(
cache: GraphQLCache(),
link: httpLink,
);
}
- // void _updateAccessToken(_accessToken) {
- // print("Updating Access Token: $_accessToken");
- // this._accessToken = _accessToken;
- // }
-
- // void _updateRefreshToken(_refreshToken) {
- // print("Updating Refresh Token: $_accessToken");
- // this._refreshToken = _refreshToken;
- // }
-
- Future login([
+ Future login(
String username,
String password,
- ]) async {
+ String server,
+ ) async {
assert(
- (username != null && password != null)
+ (username != null && password != null && server != null)
);
+ // Initialize Connection
+ await _storage.set("server", server);
+ await init();
+
QueryResult response = await _client.query(
QueryOptions(
document: LoginQuery().document,
@@ -65,79 +55,27 @@ class APIProvider{
},
)
);
-
- final loginResponse = Login$Query.fromJson(response.data);
- return loginResponse.login;
+ return response;
}
- Future me() async {
+ Future me() async {
QueryResult response = await _client.query(
QueryOptions(
document: MeQuery().document,
)
);
-
- final meResponse = Me$Query.fromJson(response.data);
- return meResponse.me;
+ return response;
}
- 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
+ Future mediaItems() async {
+ QueryResult response = await _client.query(
+ QueryOptions(
+ document: MediaItemsQuery().document,
+ )
+ );
- // 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;
+ print(response);
+ return response;
}
void dispose() {}
diff --git a/web_native/lib/api/browser_cookie_client.dart b/web_native/lib/api/browser_cookie_client.dart
deleted file mode 100644
index 8e8de01..0000000
--- a/web_native/lib/api/browser_cookie_client.dart
+++ /dev/null
@@ -1,30 +0,0 @@
-import 'package:http/browser_client.dart';
-import "package:http/http.dart";
-
-BaseClient getCookieClient() => ClientWithCookies();
-
-class ClientWithCookies extends BrowserClient {
- String _accessToken = "asdasdasd";
- String _refreshToken;
-
- @override
- Future send(BaseRequest request) async {
- request.headers.addAll({
- 'X-Imagini-AccessToken': _accessToken,
- 'X-Imagini-RefreshToken': _refreshToken,
- });
-
- return super.send(request).then((response) {
- if (response.headers.containsKey("x-imagini-accesstoken")) {
- this._accessToken = response.headers["x-imagini-accesstoken"];
- }
- if (response.headers.containsKey("x-imagini-refreshtoken")) {
- this._refreshToken = response.headers["x-imagini-refreshtoken"];
- }
-
- print("Access Token: $_accessToken");
- print("Refresh Token: $_refreshToken");
- return response;
- });
- }
-}
diff --git a/web_native/lib/api/cookie_client/browser_cookie_client.dart b/web_native/lib/api/cookie_client/browser_cookie_client.dart
new file mode 100644
index 0000000..53fba47
--- /dev/null
+++ b/web_native/lib/api/cookie_client/browser_cookie_client.dart
@@ -0,0 +1,36 @@
+import 'package:http/browser_client.dart';
+import "package:http/http.dart";
+import "package:imagini/core/storage_client/base_storage_client.dart";
+
+BaseClient getCookieClient(storage) => ClientWithCookies(storage);
+
+class ClientWithCookies extends BrowserClient {
+ BaseStorageClient _storage;
+
+ ClientWithCookies(BaseStorageClient storage) {
+ _storage = storage;
+ }
+
+ @override
+ Future send(BaseRequest request) async {
+ String _accessToken = await _storage.get("accessToken");
+ String _refreshToken = await _storage.get("refreshToken");
+
+ request.headers.addAll({
+ 'X-Imagini-AccessToken': _accessToken,
+ 'X-Imagini-RefreshToken': _refreshToken,
+ });
+
+ return super.send(request).then((response) async {
+ // We've been told to update our access token
+ if (response.headers.containsKey("x-imagini-accesstoken")) {
+ await _storage.set("accessToken", response.headers["x-imagini-accesstoken"]);
+ }
+ // We've been told to update our refresh token
+ if (response.headers.containsKey("x-imagini-refreshtoken")) {
+ await _storage.set("refreshToken", response.headers["x-imagini-refreshtoken"]);
+ }
+ return response;
+ });
+ }
+}
diff --git a/web_native/lib/api/cookie_client_stub.dart b/web_native/lib/api/cookie_client/cookie_client.dart
similarity index 60%
rename from web_native/lib/api/cookie_client_stub.dart
rename to web_native/lib/api/cookie_client/cookie_client.dart
index 216be4a..9fd9007 100644
--- a/web_native/lib/api/cookie_client_stub.dart
+++ b/web_native/lib/api/cookie_client/cookie_client.dart
@@ -1,4 +1,4 @@
import 'package:http/http.dart';
-BaseClient getCookieClient() => throw UnsupportedError(
+BaseClient getCookieClient(storage) => throw UnsupportedError(
'Cannot create a client without dart:html or dart:io.');
diff --git a/web_native/lib/api/cookie_client/io_cookie_client.dart b/web_native/lib/api/cookie_client/io_cookie_client.dart
new file mode 100644
index 0000000..26a4508
--- /dev/null
+++ b/web_native/lib/api/cookie_client/io_cookie_client.dart
@@ -0,0 +1,43 @@
+import 'package:http/io_client.dart';
+import "package:http/http.dart";
+import "package:imagini/core/storage_client/base_storage_client.dart";
+
+BaseClient getCookieClient(storage) => IOClientWithCookies(storage);
+
+class IOClientWithCookies extends IOClient {
+ BaseStorageClient _storage;
+
+ IOClientWithCookies(BaseStorageClient storage) {
+ _storage = storage;
+ }
+
+ @override
+ Future send(BaseRequest request) async {
+ String _accessToken = await _storage.get("accessToken");
+ String _refreshToken = await _storage.get("refreshToken");
+
+ request.headers.addAll({
+ 'X-Imagini-AccessToken': _accessToken,
+ 'X-Imagini-RefreshToken': _refreshToken,
+ });
+
+ return super.send(request).then((response) async {
+ // We've been told to update our access token
+ if (response.headers.containsKey("x-imagini-accesstoken")) {
+ await _storage.set("accessToken", response.headers["x-imagini-accesstoken"]);
+ }
+ // We've been told to update our refresh token
+ if (response.headers.containsKey("x-imagini-refreshtoken")) {
+ await _storage.set("refreshToken", response.headers["x-imagini-refreshtoken"]);
+ }
+
+ _accessToken = await _storage.get("accessToken");
+ _refreshToken = await _storage.get("refreshToken");
+
+ print("Access Token: $_accessToken");
+ print("Refresh Token: $_refreshToken");
+
+ return response;
+ });
+ }
+}
diff --git a/web_native/lib/api/imagini_api_repository.dart b/web_native/lib/api/imagini_api_repository.dart
index 32bb3e8..657bd53 100644
--- a/web_native/lib/api/imagini_api_repository.dart
+++ b/web_native/lib/api/imagini_api_repository.dart
@@ -1,12 +1,35 @@
+import 'dart:async';
import 'package:imagini/api/api_provider.dart';
import 'package:imagini/graphql/imagini_graphql.dart';
+import 'package:graphql_flutter/graphql_flutter.dart';
class ImaginiAPIRepository {
APIProvider _apiProvider;
ImaginiAPIRepository(this._apiProvider);
- Stream login(String user, password) {
- return Stream.fromFuture(_apiProvider.login(user, password));
+ Stream login(String user, password, server) {
+ return Stream.fromFuture(_apiProvider.login(user, password, server).then((QueryResult resp) {
+ if (resp.exception != null)
+ return false;
+
+ final loginResponse = Login$Query.fromJson(resp.data);
+ if (loginResponse.login.result == AuthResult.failure)
+ return false;
+
+ return true;
+ }));
+ }
+
+ Stream me() {
+ return Stream.fromFuture(_apiProvider.me());
+ }
+
+ Stream isAuthenticated() {
+ return Stream.fromFuture(_apiProvider.me().then((QueryResult resp) {
+ if (resp.exception != null)
+ return false;
+ return true;
+ }));
}
}
diff --git a/web_native/lib/api/io_cookie_client.dart b/web_native/lib/api/io_cookie_client.dart
deleted file mode 100644
index f9e954f..0000000
--- a/web_native/lib/api/io_cookie_client.dart
+++ /dev/null
@@ -1,28 +0,0 @@
-import 'package:http/io_client.dart';
-import "package:http/http.dart";
-
-BaseClient getCookieClient() => IOClientWithCookies();
-
-class IOClientWithCookies extends IOClient {
- String _accessToken;
- String _refreshToken;
-
- @override
- Future send(BaseRequest request) async {
- // String cookie = await getCookie();
- // String getCookieString(String _) => cookie;
- // request.headers.update('cookie', getCookieString);
- return super.send(request).then((response) {
- if (response.headers.containsKey("x-imagini-accesstoken")) {
- this._accessToken = response.headers["x-imagini-accesstoken"];
- }
- if (response.headers.containsKey("x-imagini-refreshtoken")) {
- this._refreshToken = response.headers["x-imagini-refreshtoken"];
- }
-
- print("Access Token: $_accessToken");
- print("Refresh Token: $_refreshToken");
- return response;
- });
- }
-}
diff --git a/web_native/lib/blocs/login_bloc.dart b/web_native/lib/blocs/login_bloc.dart
new file mode 100644
index 0000000..2b7f029
--- /dev/null
+++ b/web_native/lib/blocs/login_bloc.dart
@@ -0,0 +1,40 @@
+import 'dart:async';
+
+import 'package:imagini/core/imagini_application.dart';
+import 'package:imagini/api/imagini_api_repository.dart';
+
+class LoginBloc{
+
+ final ImaginiApplication _application;
+ ImaginiAPIRepository _imaginiAPI;
+
+ final _loginController = StreamController.broadcast();
+ Stream get loginResult => _loginController.stream;
+
+ final _authenticatedController = StreamController.broadcast();
+ Stream get authenticatedResult => _authenticatedController.stream;
+
+ LoginBloc(this._application){
+ _init();
+ }
+
+ void _init(){
+ _imaginiAPI = _application.imaginiAPI;
+ checkAuthentication();
+
+ // attemptLogin("admin", "admin", "http://localhost:8484");
+ }
+
+ void dispose(){
+ _loginController.close();
+ _authenticatedController.close();
+ }
+
+ checkAuthentication(){
+ _authenticatedController.addStream(_imaginiAPI.isAuthenticated());
+ }
+
+ attemptLogin(String username, password, server){
+ _loginController.addStream(_imaginiAPI.login(username, password, server));
+ }
+}
diff --git a/web_native/lib/blocs/splash_bloc.dart b/web_native/lib/blocs/splash_bloc.dart
deleted file mode 100644
index 782866b..0000000
--- a/web_native/lib/blocs/splash_bloc.dart
+++ /dev/null
@@ -1,36 +0,0 @@
-import 'dart:async';
-
-import 'package:imagini/core/imagini_application.dart';
-import 'package:imagini/api/imagini_api_repository.dart';
-import 'package:imagini/graphql/imagini_graphql.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;
-
- // TODO: This should actually attempt to load the existing Tokens, not login
- _loginController.addStream(imaginiAPI.login("admin", "admin"));
-
- // imaginiAPI.login("admin", "admin1").listen((LoginResponse lr) {
- // });
- }
-}
diff --git a/web_native/lib/core/app_component.dart b/web_native/lib/core/app_component.dart
index 7f785f5..c049ad0 100644
--- a/web_native/lib/core/app_component.dart
+++ b/web_native/lib/core/app_component.dart
@@ -24,7 +24,6 @@ class AppComponentState extends State {
@override
void dispose() async {
- // Log.info('dispose');
super.dispose();
await _application.onTerminate();
}
diff --git a/web_native/lib/core/app_routes.dart b/web_native/lib/core/app_routes.dart
index 3127066..a40be2c 100644
--- a/web_native/lib/core/app_routes.dart
+++ b/web_native/lib/core/app_routes.dart
@@ -3,13 +3,13 @@ import 'package:flutter/material.dart';
import 'package:imagini/screens/home_screen.dart';
import 'package:imagini/screens/login_screen.dart';
-import 'package:imagini/screens/splash_screen.dart';
+// import 'package:imagini/screens/splash_screen.dart';
-var splashHandler = new Handler(
- handlerFunc: (BuildContext context, Map> params) {
- return SplashScreen();
- }
-);
+// var splashHandler = new Handler(
+// handlerFunc: (BuildContext context, Map> params) {
+// return SplashScreen();
+// }
+// );
var loginHandler = new Handler(
handlerFunc: (BuildContext context, Map> params) {
@@ -42,7 +42,7 @@ class AppRoutes {
return;
}
);
- router.define(SplashScreen.PATH, handler: splashHandler);
+ // 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/imagini_application.dart b/web_native/lib/core/imagini_application.dart
index 1834ba1..e26675d 100644
--- a/web_native/lib/core/imagini_application.dart
+++ b/web_native/lib/core/imagini_application.dart
@@ -3,13 +3,20 @@ import 'package:fluro/fluro.dart';
import 'package:imagini/core/app_routes.dart';
import 'package:imagini/api/api_provider.dart';
import 'package:imagini/api/imagini_api_repository.dart';
+import 'package:imagini/core/storage_client/base_storage_client.dart';
+
+import 'package:imagini/core/storage_client/storage_client.dart'
+ if (dart.library.html) 'package:imagini/core/storage_client/browser_storage_client.dart'
+ if (dart.library.io) 'package:imagini/core/storage_client/mobile_storage_client.dart';
class ImaginiApplication {
FluroRouter router;
ImaginiAPIRepository imaginiAPI;
+ BaseStorageClient storageClient;
Future onCreate() async {
_initRouter();
+ _initStorageClient();
_initAPIRepository();
}
@@ -20,8 +27,13 @@ class ImaginiApplication {
AppRoutes.configureRoutes(router);
}
+ _initStorageClient() {
+ storageClient = getStorageClient();
+ }
+
_initAPIRepository() {
- APIProvider apiProvider = new APIProvider(server: "http://localhost:8484");
+ // TODO: Get from config
+ APIProvider apiProvider = new APIProvider(storageClient);
imaginiAPI = ImaginiAPIRepository(apiProvider);
}
}
diff --git a/web_native/lib/core/storage_client/base_storage_client.dart b/web_native/lib/core/storage_client/base_storage_client.dart
new file mode 100644
index 0000000..e99fc55
--- /dev/null
+++ b/web_native/lib/core/storage_client/base_storage_client.dart
@@ -0,0 +1,4 @@
+abstract class BaseStorageClient {
+ Future get(String key);
+ Future set(String key, String value);
+}
diff --git a/web_native/lib/core/storage_client/browser_storage_client.dart b/web_native/lib/core/storage_client/browser_storage_client.dart
new file mode 100644
index 0000000..433cb76
--- /dev/null
+++ b/web_native/lib/core/storage_client/browser_storage_client.dart
@@ -0,0 +1,19 @@
+import 'dart:html';
+
+import './base_storage_client.dart';
+
+BaseStorageClient getStorageClient() => BrowserStorageClient();
+
+class BrowserStorageClient extends BaseStorageClient {
+
+ @override
+ Future get(String key) async {
+ var requestedValue = window.localStorage.containsKey(key) ? window.localStorage[key] : "";
+ return requestedValue;
+ }
+
+ @override
+ Future set(String key, String value) async {
+ window.localStorage[key] = value;
+ }
+}
diff --git a/web_native/lib/core/storage_client/mobile_storage_client.dart b/web_native/lib/core/storage_client/mobile_storage_client.dart
new file mode 100644
index 0000000..7f54a13
--- /dev/null
+++ b/web_native/lib/core/storage_client/mobile_storage_client.dart
@@ -0,0 +1,19 @@
+import 'package:flutter_secure_storage/flutter_secure_storage.dart';
+
+import './base_storage_client.dart';
+
+BaseStorageClient getStorageClient() => MobileStorageClient();
+
+class MobileStorageClient extends BaseStorageClient {
+ final storage = new FlutterSecureStorage();
+
+ @override
+ Future get(String key) async {
+ return storage.read(key: key);
+ }
+
+ @override
+ Future set(String key, String value) async {
+ return storage.write(key: key, value: value);
+ }
+}
diff --git a/web_native/lib/core/storage_client/storage_client.dart b/web_native/lib/core/storage_client/storage_client.dart
new file mode 100644
index 0000000..b11c7e1
--- /dev/null
+++ b/web_native/lib/core/storage_client/storage_client.dart
@@ -0,0 +1,4 @@
+import './base_storage_client.dart';
+
+BaseStorageClient getStorageClient() => throw UnsupportedError(
+ 'Cannot create a storage client.');
diff --git a/web_native/lib/graphql/imagini_graphql.graphql.dart b/web_native/lib/graphql/imagini_graphql.graphql.dart
index 9d3f397..130f48f 100644
--- a/web_native/lib/graphql/imagini_graphql.graphql.dart
+++ b/web_native/lib/graphql/imagini_graphql.graphql.dart
@@ -170,6 +170,362 @@ class Me$Query with EquatableMixin {
Map toJson() => _$Me$QueryToJson(this);
}
+@JsonSerializable(explicitToJson: true)
+class MediaItems$Query$MediaItemResponse$MediaItem with EquatableMixin {
+ MediaItems$Query$MediaItemResponse$MediaItem();
+
+ factory MediaItems$Query$MediaItemResponse$MediaItem.fromJson(
+ Map json) =>
+ _$MediaItems$Query$MediaItemResponse$MediaItemFromJson(json);
+
+ String id;
+
+ String fileName;
+
+ double latitude;
+
+ double longitude;
+
+ bool isVideo;
+
+ String origName;
+
+ DateTime createdAt;
+
+ @override
+ List