Documentation, Basic Login Workflow
This commit is contained in:
		
							parent
							
								
									fec590b16e
								
							
						
					
					
						commit
						5212d7bf70
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,3 +1,5 @@ | ||||
| imagini.db | ||||
| media/ | ||||
| notes | ||||
| web/node_modules/ | ||||
| web/dist/ | ||||
|  | ||||
							
								
								
									
										34
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								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 | ||||
|  | ||||
| @ -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) | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
							
								
								
									
										18
									
								
								web_native/graphql/mediaItems.graphql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								web_native/graphql/mediaItems.graphql
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -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 | ||||
|  | ||||
| @ -2,6 +2,6 @@ | ||||
| <Workspace | ||||
|    version = "1.0"> | ||||
|    <FileRef | ||||
|       location = "group:Runner.xcodeproj"> | ||||
|       location = "self:"> | ||||
|    </FileRef> | ||||
| </Workspace> | ||||
|  | ||||
| @ -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<void> 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$Query$AuthResponse> login([ | ||||
|   Future<QueryResult> 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$Query$User> me() async { | ||||
|   Future<QueryResult> me() async { | ||||
|     QueryResult response = await _client.query( | ||||
|       QueryOptions( | ||||
|         document: MeQuery().document, | ||||
|       ) | ||||
|     ); | ||||
| 
 | ||||
|     final meResponse = Me$Query.fromJson(response.data); | ||||
|     return meResponse.me; | ||||
|     return response; | ||||
|   } | ||||
| 
 | ||||
|   Future<String> mediaItems([ | ||||
|     String startDate, | ||||
|     String endDate, | ||||
|     String albumID, | ||||
|     List<String> 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<QueryResult> mediaItems() async { | ||||
|     QueryResult response = await _client.query( | ||||
|       QueryOptions( | ||||
|         document: MediaItemsQuery().document, | ||||
|       ) | ||||
|     ); | ||||
| 
 | ||||
|     // Returns: | ||||
|     //    { | ||||
|     //     | ||||
|     //    } | ||||
| 
 | ||||
|     return null; | ||||
|   } | ||||
|    | ||||
|   Future<String> tags([ | ||||
|     int page | ||||
|   ]) async { | ||||
|     // Query: | ||||
|     //    /api/v1/Tags | ||||
|     // Derive Params: | ||||
|     //    page: | ||||
|     //       &page=4 | ||||
|     return null; | ||||
|   } | ||||
|    | ||||
|   Future<String> albums([ | ||||
|     int page | ||||
|   ]) async { | ||||
|     // Query: | ||||
|     //    /api/v1/Albums | ||||
|     // Derive Params: | ||||
|     //    page: | ||||
|     //       &page=4 | ||||
|     return null; | ||||
|   } | ||||
|    | ||||
|   Future<String> me() async { | ||||
|     return null; | ||||
|     print(response); | ||||
|     return response; | ||||
|   } | ||||
| 
 | ||||
|   void dispose() {} | ||||
|  | ||||
| @ -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<StreamedResponse> 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; | ||||
|     }); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										36
									
								
								web_native/lib/api/cookie_client/browser_cookie_client.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								web_native/lib/api/cookie_client/browser_cookie_client.dart
									
									
									
									
									
										Normal file
									
								
							| @ -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<StreamedResponse> 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; | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| @ -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.'); | ||||
							
								
								
									
										43
									
								
								web_native/lib/api/cookie_client/io_cookie_client.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								web_native/lib/api/cookie_client/io_cookie_client.dart
									
									
									
									
									
										Normal file
									
								
							| @ -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<IOStreamedResponse> 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; | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| @ -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$Query$AuthResponse> login(String user, password) { | ||||
|     return Stream.fromFuture(_apiProvider.login(user, password)); | ||||
|   Stream<bool> 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<QueryResult> me() { | ||||
|     return Stream.fromFuture(_apiProvider.me()); | ||||
|   } | ||||
| 
 | ||||
|   Stream<bool> isAuthenticated() { | ||||
|     return Stream.fromFuture(_apiProvider.me().then((QueryResult resp) { | ||||
|       if (resp.exception != null) | ||||
|         return false; | ||||
|       return true; | ||||
|     })); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -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<IOStreamedResponse> 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; | ||||
|     }); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										40
									
								
								web_native/lib/blocs/login_bloc.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								web_native/lib/blocs/login_bloc.dart
									
									
									
									
									
										Normal file
									
								
							| @ -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<bool>.broadcast(); | ||||
|   Stream<bool> get loginResult => _loginController.stream; | ||||
| 
 | ||||
|   final _authenticatedController = StreamController<bool>.broadcast(); | ||||
|   Stream<bool> 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)); | ||||
|   } | ||||
| } | ||||
| @ -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<Login$Query$AuthResponse>(); | ||||
|   Stream<Login$Query$AuthResponse> 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) { | ||||
|     // }); | ||||
|   } | ||||
| } | ||||
| @ -24,7 +24,6 @@ class AppComponentState extends State<AppComponent> { | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() async { | ||||
|     // Log.info('dispose'); | ||||
|     super.dispose(); | ||||
|     await _application.onTerminate(); | ||||
|   } | ||||
|  | ||||
| @ -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<String, List<String>> params) { | ||||
|       return SplashScreen(); | ||||
|   } | ||||
| ); | ||||
| // var splashHandler = new Handler( | ||||
| //   handlerFunc: (BuildContext context, Map<String, List<String>> params) { | ||||
| //       return SplashScreen(); | ||||
| //   } | ||||
| // ); | ||||
| 
 | ||||
| var loginHandler = new Handler( | ||||
|   handlerFunc: (BuildContext context, Map<String, List<String>> 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); | ||||
|  | ||||
| @ -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<void> 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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,4 @@ | ||||
| abstract class BaseStorageClient { | ||||
|   Future<String> get(String key); | ||||
|   Future<void> set(String key, String value); | ||||
| } | ||||
| @ -0,0 +1,19 @@ | ||||
| import 'dart:html'; | ||||
| 
 | ||||
| import './base_storage_client.dart'; | ||||
| 
 | ||||
| BaseStorageClient getStorageClient() => BrowserStorageClient(); | ||||
| 
 | ||||
| class BrowserStorageClient extends BaseStorageClient { | ||||
| 
 | ||||
|   @override | ||||
|   Future<String> get(String key) async { | ||||
|       var requestedValue = window.localStorage.containsKey(key) ? window.localStorage[key] : ""; | ||||
|       return requestedValue; | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Future<void> set(String key, String value) async { | ||||
|       window.localStorage[key] = value; | ||||
|   } | ||||
| } | ||||
| @ -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<String> get(String key) async { | ||||
|     return storage.read(key: key); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Future<void> set(String key, String value) async { | ||||
|     return storage.write(key: key, value: value); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										4
									
								
								web_native/lib/core/storage_client/storage_client.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								web_native/lib/core/storage_client/storage_client.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| import './base_storage_client.dart'; | ||||
| 
 | ||||
| BaseStorageClient getStorageClient() => throw UnsupportedError( | ||||
|     'Cannot create a storage client.'); | ||||
| @ -170,6 +170,362 @@ class Me$Query with EquatableMixin { | ||||
|   Map<String, dynamic> 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<String, dynamic> json) => | ||||
|       _$MediaItems$Query$MediaItemResponse$MediaItemFromJson(json); | ||||
| 
 | ||||
|   String id; | ||||
| 
 | ||||
|   String fileName; | ||||
| 
 | ||||
|   double latitude; | ||||
| 
 | ||||
|   double longitude; | ||||
| 
 | ||||
|   bool isVideo; | ||||
| 
 | ||||
|   String origName; | ||||
| 
 | ||||
|   DateTime createdAt; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => | ||||
|       [id, fileName, latitude, longitude, isVideo, origName, createdAt]; | ||||
|   Map<String, dynamic> toJson() => | ||||
|       _$MediaItems$Query$MediaItemResponse$MediaItemToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class MediaItems$Query$MediaItemResponse$PageResponse with EquatableMixin { | ||||
|   MediaItems$Query$MediaItemResponse$PageResponse(); | ||||
| 
 | ||||
|   factory MediaItems$Query$MediaItemResponse$PageResponse.fromJson( | ||||
|           Map<String, dynamic> json) => | ||||
|       _$MediaItems$Query$MediaItemResponse$PageResponseFromJson(json); | ||||
| 
 | ||||
|   int size; | ||||
| 
 | ||||
|   int page; | ||||
| 
 | ||||
|   int total; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [size, page, total]; | ||||
|   Map<String, dynamic> toJson() => | ||||
|       _$MediaItems$Query$MediaItemResponse$PageResponseToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class MediaItems$Query$MediaItemResponse with EquatableMixin { | ||||
|   MediaItems$Query$MediaItemResponse(); | ||||
| 
 | ||||
|   factory MediaItems$Query$MediaItemResponse.fromJson( | ||||
|           Map<String, dynamic> json) => | ||||
|       _$MediaItems$Query$MediaItemResponseFromJson(json); | ||||
| 
 | ||||
|   List<MediaItems$Query$MediaItemResponse$MediaItem> data; | ||||
| 
 | ||||
|   MediaItems$Query$MediaItemResponse$PageResponse page; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [data, page]; | ||||
|   Map<String, dynamic> toJson() => | ||||
|       _$MediaItems$Query$MediaItemResponseToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class MediaItems$Query with EquatableMixin { | ||||
|   MediaItems$Query(); | ||||
| 
 | ||||
|   factory MediaItems$Query.fromJson(Map<String, dynamic> json) => | ||||
|       _$MediaItems$QueryFromJson(json); | ||||
| 
 | ||||
|   MediaItems$Query$MediaItemResponse mediaItems; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [mediaItems]; | ||||
|   Map<String, dynamic> toJson() => _$MediaItems$QueryToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class TimeFilter with EquatableMixin { | ||||
|   TimeFilter( | ||||
|       {this.equalTo, | ||||
|       this.notEqualTo, | ||||
|       this.lessThan, | ||||
|       this.lessThanOrEqualTo, | ||||
|       this.greaterThan, | ||||
|       this.greaterThanOrEqualTo}); | ||||
| 
 | ||||
|   factory TimeFilter.fromJson(Map<String, dynamic> json) => | ||||
|       _$TimeFilterFromJson(json); | ||||
| 
 | ||||
|   DateTime equalTo; | ||||
| 
 | ||||
|   DateTime notEqualTo; | ||||
| 
 | ||||
|   DateTime lessThan; | ||||
| 
 | ||||
|   DateTime lessThanOrEqualTo; | ||||
| 
 | ||||
|   DateTime greaterThan; | ||||
| 
 | ||||
|   DateTime greaterThanOrEqualTo; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [ | ||||
|         equalTo, | ||||
|         notEqualTo, | ||||
|         lessThan, | ||||
|         lessThanOrEqualTo, | ||||
|         greaterThan, | ||||
|         greaterThanOrEqualTo | ||||
|       ]; | ||||
|   Map<String, dynamic> toJson() => _$TimeFilterToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class FloatFilter with EquatableMixin { | ||||
|   FloatFilter( | ||||
|       {this.equalTo, | ||||
|       this.notEqualTo, | ||||
|       this.lessThan, | ||||
|       this.lessThanOrEqualTo, | ||||
|       this.greaterThan, | ||||
|       this.greaterThanOrEqualTo}); | ||||
| 
 | ||||
|   factory FloatFilter.fromJson(Map<String, dynamic> json) => | ||||
|       _$FloatFilterFromJson(json); | ||||
| 
 | ||||
|   double equalTo; | ||||
| 
 | ||||
|   double notEqualTo; | ||||
| 
 | ||||
|   double lessThan; | ||||
| 
 | ||||
|   double lessThanOrEqualTo; | ||||
| 
 | ||||
|   double greaterThan; | ||||
| 
 | ||||
|   double greaterThanOrEqualTo; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [ | ||||
|         equalTo, | ||||
|         notEqualTo, | ||||
|         lessThan, | ||||
|         lessThanOrEqualTo, | ||||
|         greaterThan, | ||||
|         greaterThanOrEqualTo | ||||
|       ]; | ||||
|   Map<String, dynamic> toJson() => _$FloatFilterToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class BooleanFilter with EquatableMixin { | ||||
|   BooleanFilter({this.equalTo, this.notEqualTo}); | ||||
| 
 | ||||
|   factory BooleanFilter.fromJson(Map<String, dynamic> json) => | ||||
|       _$BooleanFilterFromJson(json); | ||||
| 
 | ||||
|   bool equalTo; | ||||
| 
 | ||||
|   bool notEqualTo; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [equalTo, notEqualTo]; | ||||
|   Map<String, dynamic> toJson() => _$BooleanFilterToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class IDFilter with EquatableMixin { | ||||
|   IDFilter({this.equalTo, this.notEqualTo}); | ||||
| 
 | ||||
|   factory IDFilter.fromJson(Map<String, dynamic> json) => | ||||
|       _$IDFilterFromJson(json); | ||||
| 
 | ||||
|   String equalTo; | ||||
| 
 | ||||
|   String notEqualTo; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [equalTo, notEqualTo]; | ||||
|   Map<String, dynamic> toJson() => _$IDFilterToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class StringFilter with EquatableMixin { | ||||
|   StringFilter( | ||||
|       {this.equalTo, | ||||
|       this.notEqualTo, | ||||
|       this.startsWith, | ||||
|       this.notStartsWith, | ||||
|       this.endsWith, | ||||
|       this.notEndsWith, | ||||
|       this.contains, | ||||
|       this.notContains}); | ||||
| 
 | ||||
|   factory StringFilter.fromJson(Map<String, dynamic> json) => | ||||
|       _$StringFilterFromJson(json); | ||||
| 
 | ||||
|   String equalTo; | ||||
| 
 | ||||
|   String notEqualTo; | ||||
| 
 | ||||
|   String startsWith; | ||||
| 
 | ||||
|   String notStartsWith; | ||||
| 
 | ||||
|   String endsWith; | ||||
| 
 | ||||
|   String notEndsWith; | ||||
| 
 | ||||
|   String contains; | ||||
| 
 | ||||
|   String notContains; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [ | ||||
|         equalTo, | ||||
|         notEqualTo, | ||||
|         startsWith, | ||||
|         notStartsWith, | ||||
|         endsWith, | ||||
|         notEndsWith, | ||||
|         contains, | ||||
|         notContains | ||||
|       ]; | ||||
|   Map<String, dynamic> toJson() => _$StringFilterToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class MediaItemFilter with EquatableMixin { | ||||
|   MediaItemFilter( | ||||
|       {this.id, | ||||
|       this.createdAt, | ||||
|       this.updatedAt, | ||||
|       this.exifDate, | ||||
|       this.latitude, | ||||
|       this.longitude, | ||||
|       this.isVideo, | ||||
|       this.origName, | ||||
|       this.tags, | ||||
|       this.albums}); | ||||
| 
 | ||||
|   factory MediaItemFilter.fromJson(Map<String, dynamic> json) => | ||||
|       _$MediaItemFilterFromJson(json); | ||||
| 
 | ||||
|   IDFilter id; | ||||
| 
 | ||||
|   TimeFilter createdAt; | ||||
| 
 | ||||
|   TimeFilter updatedAt; | ||||
| 
 | ||||
|   TimeFilter exifDate; | ||||
| 
 | ||||
|   FloatFilter latitude; | ||||
| 
 | ||||
|   FloatFilter longitude; | ||||
| 
 | ||||
|   BooleanFilter isVideo; | ||||
| 
 | ||||
|   StringFilter origName; | ||||
| 
 | ||||
|   TagFilter tags; | ||||
| 
 | ||||
|   AlbumFilter albums; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [ | ||||
|         id, | ||||
|         createdAt, | ||||
|         updatedAt, | ||||
|         exifDate, | ||||
|         latitude, | ||||
|         longitude, | ||||
|         isVideo, | ||||
|         origName, | ||||
|         tags, | ||||
|         albums | ||||
|       ]; | ||||
|   Map<String, dynamic> toJson() => _$MediaItemFilterToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class TagFilter with EquatableMixin { | ||||
|   TagFilter({this.id, this.createdAt, this.updatedAt, this.name}); | ||||
| 
 | ||||
|   factory TagFilter.fromJson(Map<String, dynamic> json) => | ||||
|       _$TagFilterFromJson(json); | ||||
| 
 | ||||
|   IDFilter id; | ||||
| 
 | ||||
|   TimeFilter createdAt; | ||||
| 
 | ||||
|   TimeFilter updatedAt; | ||||
| 
 | ||||
|   StringFilter name; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [id, createdAt, updatedAt, name]; | ||||
|   Map<String, dynamic> toJson() => _$TagFilterToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class AlbumFilter with EquatableMixin { | ||||
|   AlbumFilter({this.id, this.createdAt, this.updatedAt, this.name}); | ||||
| 
 | ||||
|   factory AlbumFilter.fromJson(Map<String, dynamic> json) => | ||||
|       _$AlbumFilterFromJson(json); | ||||
| 
 | ||||
|   IDFilter id; | ||||
| 
 | ||||
|   TimeFilter createdAt; | ||||
| 
 | ||||
|   TimeFilter updatedAt; | ||||
| 
 | ||||
|   StringFilter name; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [id, createdAt, updatedAt, name]; | ||||
|   Map<String, dynamic> toJson() => _$AlbumFilterToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class Page with EquatableMixin { | ||||
|   Page({this.size, this.page}); | ||||
| 
 | ||||
|   factory Page.fromJson(Map<String, dynamic> json) => _$PageFromJson(json); | ||||
| 
 | ||||
|   int size; | ||||
| 
 | ||||
|   int page; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [size, page]; | ||||
|   Map<String, dynamic> toJson() => _$PageToJson(this); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class Order with EquatableMixin { | ||||
|   Order({this.by, this.direction}); | ||||
| 
 | ||||
|   factory Order.fromJson(Map<String, dynamic> json) => _$OrderFromJson(json); | ||||
| 
 | ||||
|   String by; | ||||
| 
 | ||||
|   @JsonKey(unknownEnumValue: OrderDirection.artemisUnknown) | ||||
|   OrderDirection direction; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [by, direction]; | ||||
|   Map<String, dynamic> toJson() => _$OrderToJson(this); | ||||
| } | ||||
| 
 | ||||
| enum AuthResult { | ||||
|   @JsonValue('Success') | ||||
|   success, | ||||
| @ -194,6 +550,14 @@ enum AuthType { | ||||
|   @JsonValue('ARTEMIS_UNKNOWN') | ||||
|   artemisUnknown, | ||||
| } | ||||
| enum OrderDirection { | ||||
|   @JsonValue('ASC') | ||||
|   asc, | ||||
|   @JsonValue('DESC') | ||||
|   desc, | ||||
|   @JsonValue('ARTEMIS_UNKNOWN') | ||||
|   artemisUnknown, | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class LoginArguments extends JsonSerializable with EquatableMixin { | ||||
| @ -508,3 +872,161 @@ class MeQuery extends GraphQLQuery<Me$Query, JsonSerializable> { | ||||
|   @override | ||||
|   Me$Query parse(Map<String, dynamic> json) => Me$Query.fromJson(json); | ||||
| } | ||||
| 
 | ||||
| @JsonSerializable(explicitToJson: true) | ||||
| class MediaItemsArguments extends JsonSerializable with EquatableMixin { | ||||
|   MediaItemsArguments({this.order, this.page, this.filter}); | ||||
| 
 | ||||
|   @override | ||||
|   factory MediaItemsArguments.fromJson(Map<String, dynamic> json) => | ||||
|       _$MediaItemsArgumentsFromJson(json); | ||||
| 
 | ||||
|   final Order order; | ||||
| 
 | ||||
|   final Page page; | ||||
| 
 | ||||
|   final MediaItemFilter filter; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [order, page, filter]; | ||||
|   @override | ||||
|   Map<String, dynamic> toJson() => _$MediaItemsArgumentsToJson(this); | ||||
| } | ||||
| 
 | ||||
| class MediaItemsQuery | ||||
|     extends GraphQLQuery<MediaItems$Query, MediaItemsArguments> { | ||||
|   MediaItemsQuery({this.variables}); | ||||
| 
 | ||||
|   @override | ||||
|   final DocumentNode document = DocumentNode(definitions: [ | ||||
|     OperationDefinitionNode( | ||||
|         type: OperationType.query, | ||||
|         name: NameNode(value: 'mediaItems'), | ||||
|         variableDefinitions: [ | ||||
|           VariableDefinitionNode( | ||||
|               variable: VariableNode(name: NameNode(value: 'order')), | ||||
|               type: NamedTypeNode( | ||||
|                   name: NameNode(value: 'Order'), isNonNull: false), | ||||
|               defaultValue: DefaultValueNode(value: null), | ||||
|               directives: []), | ||||
|           VariableDefinitionNode( | ||||
|               variable: VariableNode(name: NameNode(value: 'page')), | ||||
|               type: NamedTypeNode( | ||||
|                   name: NameNode(value: 'Page'), isNonNull: false), | ||||
|               defaultValue: DefaultValueNode(value: null), | ||||
|               directives: []), | ||||
|           VariableDefinitionNode( | ||||
|               variable: VariableNode(name: NameNode(value: 'filter')), | ||||
|               type: NamedTypeNode( | ||||
|                   name: NameNode(value: 'MediaItemFilter'), isNonNull: false), | ||||
|               defaultValue: DefaultValueNode(value: null), | ||||
|               directives: []) | ||||
|         ], | ||||
|         directives: [], | ||||
|         selectionSet: SelectionSetNode(selections: [ | ||||
|           FieldNode( | ||||
|               name: NameNode(value: 'mediaItems'), | ||||
|               alias: null, | ||||
|               arguments: [ | ||||
|                 ArgumentNode( | ||||
|                     name: NameNode(value: 'filter'), | ||||
|                     value: VariableNode(name: NameNode(value: 'filter'))), | ||||
|                 ArgumentNode( | ||||
|                     name: NameNode(value: 'page'), | ||||
|                     value: VariableNode(name: NameNode(value: 'page'))), | ||||
|                 ArgumentNode( | ||||
|                     name: NameNode(value: 'order'), | ||||
|                     value: VariableNode(name: NameNode(value: 'order'))) | ||||
|               ], | ||||
|               directives: [], | ||||
|               selectionSet: SelectionSetNode(selections: [ | ||||
|                 FieldNode( | ||||
|                     name: NameNode(value: 'data'), | ||||
|                     alias: null, | ||||
|                     arguments: [], | ||||
|                     directives: [], | ||||
|                     selectionSet: SelectionSetNode(selections: [ | ||||
|                       FieldNode( | ||||
|                           name: NameNode(value: 'id'), | ||||
|                           alias: null, | ||||
|                           arguments: [], | ||||
|                           directives: [], | ||||
|                           selectionSet: null), | ||||
|                       FieldNode( | ||||
|                           name: NameNode(value: 'fileName'), | ||||
|                           alias: null, | ||||
|                           arguments: [], | ||||
|                           directives: [], | ||||
|                           selectionSet: null), | ||||
|                       FieldNode( | ||||
|                           name: NameNode(value: 'latitude'), | ||||
|                           alias: null, | ||||
|                           arguments: [], | ||||
|                           directives: [], | ||||
|                           selectionSet: null), | ||||
|                       FieldNode( | ||||
|                           name: NameNode(value: 'longitude'), | ||||
|                           alias: null, | ||||
|                           arguments: [], | ||||
|                           directives: [], | ||||
|                           selectionSet: null), | ||||
|                       FieldNode( | ||||
|                           name: NameNode(value: 'isVideo'), | ||||
|                           alias: null, | ||||
|                           arguments: [], | ||||
|                           directives: [], | ||||
|                           selectionSet: null), | ||||
|                       FieldNode( | ||||
|                           name: NameNode(value: 'origName'), | ||||
|                           alias: null, | ||||
|                           arguments: [], | ||||
|                           directives: [], | ||||
|                           selectionSet: null), | ||||
|                       FieldNode( | ||||
|                           name: NameNode(value: 'createdAt'), | ||||
|                           alias: null, | ||||
|                           arguments: [], | ||||
|                           directives: [], | ||||
|                           selectionSet: null) | ||||
|                     ])), | ||||
|                 FieldNode( | ||||
|                     name: NameNode(value: 'page'), | ||||
|                     alias: null, | ||||
|                     arguments: [], | ||||
|                     directives: [], | ||||
|                     selectionSet: SelectionSetNode(selections: [ | ||||
|                       FieldNode( | ||||
|                           name: NameNode(value: 'size'), | ||||
|                           alias: null, | ||||
|                           arguments: [], | ||||
|                           directives: [], | ||||
|                           selectionSet: null), | ||||
|                       FieldNode( | ||||
|                           name: NameNode(value: 'page'), | ||||
|                           alias: null, | ||||
|                           arguments: [], | ||||
|                           directives: [], | ||||
|                           selectionSet: null), | ||||
|                       FieldNode( | ||||
|                           name: NameNode(value: 'total'), | ||||
|                           alias: null, | ||||
|                           arguments: [], | ||||
|                           directives: [], | ||||
|                           selectionSet: null) | ||||
|                     ])) | ||||
|               ])) | ||||
|         ])) | ||||
|   ]); | ||||
| 
 | ||||
|   @override | ||||
|   final String operationName = 'mediaItems'; | ||||
| 
 | ||||
|   @override | ||||
|   final MediaItemsArguments variables; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object> get props => [document, operationName, variables]; | ||||
|   @override | ||||
|   MediaItems$Query parse(Map<String, dynamic> json) => | ||||
|       MediaItems$Query.fromJson(json); | ||||
| } | ||||
|  | ||||
| @ -192,6 +192,318 @@ Map<String, dynamic> _$Me$QueryToJson(Me$Query instance) => <String, dynamic>{ | ||||
|       'me': instance.me?.toJson(), | ||||
|     }; | ||||
| 
 | ||||
| MediaItems$Query$MediaItemResponse$MediaItem | ||||
|     _$MediaItems$Query$MediaItemResponse$MediaItemFromJson( | ||||
|         Map<String, dynamic> json) { | ||||
|   return MediaItems$Query$MediaItemResponse$MediaItem() | ||||
|     ..id = json['id'] as String | ||||
|     ..fileName = json['fileName'] as String | ||||
|     ..latitude = (json['latitude'] as num)?.toDouble() | ||||
|     ..longitude = (json['longitude'] as num)?.toDouble() | ||||
|     ..isVideo = json['isVideo'] as bool | ||||
|     ..origName = json['origName'] as String | ||||
|     ..createdAt = json['createdAt'] == null | ||||
|         ? null | ||||
|         : DateTime.parse(json['createdAt'] as String); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$MediaItems$Query$MediaItemResponse$MediaItemToJson( | ||||
|         MediaItems$Query$MediaItemResponse$MediaItem instance) => | ||||
|     <String, dynamic>{ | ||||
|       'id': instance.id, | ||||
|       'fileName': instance.fileName, | ||||
|       'latitude': instance.latitude, | ||||
|       'longitude': instance.longitude, | ||||
|       'isVideo': instance.isVideo, | ||||
|       'origName': instance.origName, | ||||
|       'createdAt': instance.createdAt?.toIso8601String(), | ||||
|     }; | ||||
| 
 | ||||
| MediaItems$Query$MediaItemResponse$PageResponse | ||||
|     _$MediaItems$Query$MediaItemResponse$PageResponseFromJson( | ||||
|         Map<String, dynamic> json) { | ||||
|   return MediaItems$Query$MediaItemResponse$PageResponse() | ||||
|     ..size = json['size'] as int | ||||
|     ..page = json['page'] as int | ||||
|     ..total = json['total'] as int; | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$MediaItems$Query$MediaItemResponse$PageResponseToJson( | ||||
|         MediaItems$Query$MediaItemResponse$PageResponse instance) => | ||||
|     <String, dynamic>{ | ||||
|       'size': instance.size, | ||||
|       'page': instance.page, | ||||
|       'total': instance.total, | ||||
|     }; | ||||
| 
 | ||||
| MediaItems$Query$MediaItemResponse _$MediaItems$Query$MediaItemResponseFromJson( | ||||
|     Map<String, dynamic> json) { | ||||
|   return MediaItems$Query$MediaItemResponse() | ||||
|     ..data = (json['data'] as List) | ||||
|         ?.map((e) => e == null | ||||
|             ? null | ||||
|             : MediaItems$Query$MediaItemResponse$MediaItem.fromJson( | ||||
|                 e as Map<String, dynamic>)) | ||||
|         ?.toList() | ||||
|     ..page = json['page'] == null | ||||
|         ? null | ||||
|         : MediaItems$Query$MediaItemResponse$PageResponse.fromJson( | ||||
|             json['page'] as Map<String, dynamic>); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$MediaItems$Query$MediaItemResponseToJson( | ||||
|         MediaItems$Query$MediaItemResponse instance) => | ||||
|     <String, dynamic>{ | ||||
|       'data': instance.data?.map((e) => e?.toJson())?.toList(), | ||||
|       'page': instance.page?.toJson(), | ||||
|     }; | ||||
| 
 | ||||
| MediaItems$Query _$MediaItems$QueryFromJson(Map<String, dynamic> json) { | ||||
|   return MediaItems$Query() | ||||
|     ..mediaItems = json['mediaItems'] == null | ||||
|         ? null | ||||
|         : MediaItems$Query$MediaItemResponse.fromJson( | ||||
|             json['mediaItems'] as Map<String, dynamic>); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$MediaItems$QueryToJson(MediaItems$Query instance) => | ||||
|     <String, dynamic>{ | ||||
|       'mediaItems': instance.mediaItems?.toJson(), | ||||
|     }; | ||||
| 
 | ||||
| TimeFilter _$TimeFilterFromJson(Map<String, dynamic> json) { | ||||
|   return TimeFilter( | ||||
|     equalTo: json['equalTo'] == null | ||||
|         ? null | ||||
|         : DateTime.parse(json['equalTo'] as String), | ||||
|     notEqualTo: json['notEqualTo'] == null | ||||
|         ? null | ||||
|         : DateTime.parse(json['notEqualTo'] as String), | ||||
|     lessThan: json['lessThan'] == null | ||||
|         ? null | ||||
|         : DateTime.parse(json['lessThan'] as String), | ||||
|     lessThanOrEqualTo: json['lessThanOrEqualTo'] == null | ||||
|         ? null | ||||
|         : DateTime.parse(json['lessThanOrEqualTo'] as String), | ||||
|     greaterThan: json['greaterThan'] == null | ||||
|         ? null | ||||
|         : DateTime.parse(json['greaterThan'] as String), | ||||
|     greaterThanOrEqualTo: json['greaterThanOrEqualTo'] == null | ||||
|         ? null | ||||
|         : DateTime.parse(json['greaterThanOrEqualTo'] as String), | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$TimeFilterToJson(TimeFilter instance) => | ||||
|     <String, dynamic>{ | ||||
|       'equalTo': instance.equalTo?.toIso8601String(), | ||||
|       'notEqualTo': instance.notEqualTo?.toIso8601String(), | ||||
|       'lessThan': instance.lessThan?.toIso8601String(), | ||||
|       'lessThanOrEqualTo': instance.lessThanOrEqualTo?.toIso8601String(), | ||||
|       'greaterThan': instance.greaterThan?.toIso8601String(), | ||||
|       'greaterThanOrEqualTo': instance.greaterThanOrEqualTo?.toIso8601String(), | ||||
|     }; | ||||
| 
 | ||||
| FloatFilter _$FloatFilterFromJson(Map<String, dynamic> json) { | ||||
|   return FloatFilter( | ||||
|     equalTo: (json['equalTo'] as num)?.toDouble(), | ||||
|     notEqualTo: (json['notEqualTo'] as num)?.toDouble(), | ||||
|     lessThan: (json['lessThan'] as num)?.toDouble(), | ||||
|     lessThanOrEqualTo: (json['lessThanOrEqualTo'] as num)?.toDouble(), | ||||
|     greaterThan: (json['greaterThan'] as num)?.toDouble(), | ||||
|     greaterThanOrEqualTo: (json['greaterThanOrEqualTo'] as num)?.toDouble(), | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$FloatFilterToJson(FloatFilter instance) => | ||||
|     <String, dynamic>{ | ||||
|       'equalTo': instance.equalTo, | ||||
|       'notEqualTo': instance.notEqualTo, | ||||
|       'lessThan': instance.lessThan, | ||||
|       'lessThanOrEqualTo': instance.lessThanOrEqualTo, | ||||
|       'greaterThan': instance.greaterThan, | ||||
|       'greaterThanOrEqualTo': instance.greaterThanOrEqualTo, | ||||
|     }; | ||||
| 
 | ||||
| BooleanFilter _$BooleanFilterFromJson(Map<String, dynamic> json) { | ||||
|   return BooleanFilter( | ||||
|     equalTo: json['equalTo'] as bool, | ||||
|     notEqualTo: json['notEqualTo'] as bool, | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$BooleanFilterToJson(BooleanFilter instance) => | ||||
|     <String, dynamic>{ | ||||
|       'equalTo': instance.equalTo, | ||||
|       'notEqualTo': instance.notEqualTo, | ||||
|     }; | ||||
| 
 | ||||
| IDFilter _$IDFilterFromJson(Map<String, dynamic> json) { | ||||
|   return IDFilter( | ||||
|     equalTo: json['equalTo'] as String, | ||||
|     notEqualTo: json['notEqualTo'] as String, | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$IDFilterToJson(IDFilter instance) => <String, dynamic>{ | ||||
|       'equalTo': instance.equalTo, | ||||
|       'notEqualTo': instance.notEqualTo, | ||||
|     }; | ||||
| 
 | ||||
| StringFilter _$StringFilterFromJson(Map<String, dynamic> json) { | ||||
|   return StringFilter( | ||||
|     equalTo: json['equalTo'] as String, | ||||
|     notEqualTo: json['notEqualTo'] as String, | ||||
|     startsWith: json['startsWith'] as String, | ||||
|     notStartsWith: json['notStartsWith'] as String, | ||||
|     endsWith: json['endsWith'] as String, | ||||
|     notEndsWith: json['notEndsWith'] as String, | ||||
|     contains: json['contains'] as String, | ||||
|     notContains: json['notContains'] as String, | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$StringFilterToJson(StringFilter instance) => | ||||
|     <String, dynamic>{ | ||||
|       'equalTo': instance.equalTo, | ||||
|       'notEqualTo': instance.notEqualTo, | ||||
|       'startsWith': instance.startsWith, | ||||
|       'notStartsWith': instance.notStartsWith, | ||||
|       'endsWith': instance.endsWith, | ||||
|       'notEndsWith': instance.notEndsWith, | ||||
|       'contains': instance.contains, | ||||
|       'notContains': instance.notContains, | ||||
|     }; | ||||
| 
 | ||||
| MediaItemFilter _$MediaItemFilterFromJson(Map<String, dynamic> json) { | ||||
|   return MediaItemFilter( | ||||
|     id: json['id'] == null | ||||
|         ? null | ||||
|         : IDFilter.fromJson(json['id'] as Map<String, dynamic>), | ||||
|     createdAt: json['createdAt'] == null | ||||
|         ? null | ||||
|         : TimeFilter.fromJson(json['createdAt'] as Map<String, dynamic>), | ||||
|     updatedAt: json['updatedAt'] == null | ||||
|         ? null | ||||
|         : TimeFilter.fromJson(json['updatedAt'] as Map<String, dynamic>), | ||||
|     exifDate: json['exifDate'] == null | ||||
|         ? null | ||||
|         : TimeFilter.fromJson(json['exifDate'] as Map<String, dynamic>), | ||||
|     latitude: json['latitude'] == null | ||||
|         ? null | ||||
|         : FloatFilter.fromJson(json['latitude'] as Map<String, dynamic>), | ||||
|     longitude: json['longitude'] == null | ||||
|         ? null | ||||
|         : FloatFilter.fromJson(json['longitude'] as Map<String, dynamic>), | ||||
|     isVideo: json['isVideo'] == null | ||||
|         ? null | ||||
|         : BooleanFilter.fromJson(json['isVideo'] as Map<String, dynamic>), | ||||
|     origName: json['origName'] == null | ||||
|         ? null | ||||
|         : StringFilter.fromJson(json['origName'] as Map<String, dynamic>), | ||||
|     tags: json['tags'] == null | ||||
|         ? null | ||||
|         : TagFilter.fromJson(json['tags'] as Map<String, dynamic>), | ||||
|     albums: json['albums'] == null | ||||
|         ? null | ||||
|         : AlbumFilter.fromJson(json['albums'] as Map<String, dynamic>), | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$MediaItemFilterToJson(MediaItemFilter instance) => | ||||
|     <String, dynamic>{ | ||||
|       'id': instance.id?.toJson(), | ||||
|       'createdAt': instance.createdAt?.toJson(), | ||||
|       'updatedAt': instance.updatedAt?.toJson(), | ||||
|       'exifDate': instance.exifDate?.toJson(), | ||||
|       'latitude': instance.latitude?.toJson(), | ||||
|       'longitude': instance.longitude?.toJson(), | ||||
|       'isVideo': instance.isVideo?.toJson(), | ||||
|       'origName': instance.origName?.toJson(), | ||||
|       'tags': instance.tags?.toJson(), | ||||
|       'albums': instance.albums?.toJson(), | ||||
|     }; | ||||
| 
 | ||||
| TagFilter _$TagFilterFromJson(Map<String, dynamic> json) { | ||||
|   return TagFilter( | ||||
|     id: json['id'] == null | ||||
|         ? null | ||||
|         : IDFilter.fromJson(json['id'] as Map<String, dynamic>), | ||||
|     createdAt: json['createdAt'] == null | ||||
|         ? null | ||||
|         : TimeFilter.fromJson(json['createdAt'] as Map<String, dynamic>), | ||||
|     updatedAt: json['updatedAt'] == null | ||||
|         ? null | ||||
|         : TimeFilter.fromJson(json['updatedAt'] as Map<String, dynamic>), | ||||
|     name: json['name'] == null | ||||
|         ? null | ||||
|         : StringFilter.fromJson(json['name'] as Map<String, dynamic>), | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$TagFilterToJson(TagFilter instance) => <String, dynamic>{ | ||||
|       'id': instance.id?.toJson(), | ||||
|       'createdAt': instance.createdAt?.toJson(), | ||||
|       'updatedAt': instance.updatedAt?.toJson(), | ||||
|       'name': instance.name?.toJson(), | ||||
|     }; | ||||
| 
 | ||||
| AlbumFilter _$AlbumFilterFromJson(Map<String, dynamic> json) { | ||||
|   return AlbumFilter( | ||||
|     id: json['id'] == null | ||||
|         ? null | ||||
|         : IDFilter.fromJson(json['id'] as Map<String, dynamic>), | ||||
|     createdAt: json['createdAt'] == null | ||||
|         ? null | ||||
|         : TimeFilter.fromJson(json['createdAt'] as Map<String, dynamic>), | ||||
|     updatedAt: json['updatedAt'] == null | ||||
|         ? null | ||||
|         : TimeFilter.fromJson(json['updatedAt'] as Map<String, dynamic>), | ||||
|     name: json['name'] == null | ||||
|         ? null | ||||
|         : StringFilter.fromJson(json['name'] as Map<String, dynamic>), | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$AlbumFilterToJson(AlbumFilter instance) => | ||||
|     <String, dynamic>{ | ||||
|       'id': instance.id?.toJson(), | ||||
|       'createdAt': instance.createdAt?.toJson(), | ||||
|       'updatedAt': instance.updatedAt?.toJson(), | ||||
|       'name': instance.name?.toJson(), | ||||
|     }; | ||||
| 
 | ||||
| Page _$PageFromJson(Map<String, dynamic> json) { | ||||
|   return Page( | ||||
|     size: json['size'] as int, | ||||
|     page: json['page'] as int, | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$PageToJson(Page instance) => <String, dynamic>{ | ||||
|       'size': instance.size, | ||||
|       'page': instance.page, | ||||
|     }; | ||||
| 
 | ||||
| Order _$OrderFromJson(Map<String, dynamic> json) { | ||||
|   return Order( | ||||
|     by: json['by'] as String, | ||||
|     direction: _$enumDecodeNullable(_$OrderDirectionEnumMap, json['direction'], | ||||
|         unknownValue: OrderDirection.artemisUnknown), | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$OrderToJson(Order instance) => <String, dynamic>{ | ||||
|       'by': instance.by, | ||||
|       'direction': _$OrderDirectionEnumMap[instance.direction], | ||||
|     }; | ||||
| 
 | ||||
| const _$OrderDirectionEnumMap = { | ||||
|   OrderDirection.asc: 'ASC', | ||||
|   OrderDirection.desc: 'DESC', | ||||
|   OrderDirection.artemisUnknown: 'ARTEMIS_UNKNOWN', | ||||
| }; | ||||
| 
 | ||||
| LoginArguments _$LoginArgumentsFromJson(Map<String, dynamic> json) { | ||||
|   return LoginArguments( | ||||
|     user: json['user'] as String, | ||||
| @ -219,3 +531,25 @@ Map<String, dynamic> _$CreateMediaItemArgumentsToJson( | ||||
|     <String, dynamic>{ | ||||
|       'file': fromDartMultipartFileToGraphQLUpload(instance.file), | ||||
|     }; | ||||
| 
 | ||||
| MediaItemsArguments _$MediaItemsArgumentsFromJson(Map<String, dynamic> json) { | ||||
|   return MediaItemsArguments( | ||||
|     order: json['order'] == null | ||||
|         ? null | ||||
|         : Order.fromJson(json['order'] as Map<String, dynamic>), | ||||
|     page: json['page'] == null | ||||
|         ? null | ||||
|         : Page.fromJson(json['page'] as Map<String, dynamic>), | ||||
|     filter: json['filter'] == null | ||||
|         ? null | ||||
|         : MediaItemFilter.fromJson(json['filter'] as Map<String, dynamic>), | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| Map<String, dynamic> _$MediaItemsArgumentsToJson( | ||||
|         MediaItemsArguments instance) => | ||||
|     <String, dynamic>{ | ||||
|       'order': instance.order?.toJson(), | ||||
|       'page': instance.page?.toJson(), | ||||
|       'filter': instance.filter?.toJson(), | ||||
|     }; | ||||
|  | ||||
| @ -1,12 +1,13 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| 
 | ||||
| import 'package:fluro/fluro.dart'; | ||||
| 
 | ||||
| import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; | ||||
| 
 | ||||
| import 'package:imagini/blocs/login_bloc.dart'; | ||||
| import 'package:imagini/core/app_provider.dart'; | ||||
| 
 | ||||
| class LoginScreen extends StatefulWidget { | ||||
|   static const String PATH = '/Login'; | ||||
|   static const String PATH = '/'; | ||||
| 
 | ||||
|   LoginScreen({Key key}) : super(key: key); | ||||
| 
 | ||||
| @ -16,7 +17,7 @@ class LoginScreen extends StatefulWidget { | ||||
| 
 | ||||
| class _LoginScreenState extends State<LoginScreen> { | ||||
| 
 | ||||
|   // LoginBloc bloc; | ||||
|   LoginBloc bloc; | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
| @ -29,12 +30,76 @@ class _LoginScreenState extends State<LoginScreen> { | ||||
|     _init(); | ||||
| 
 | ||||
|     return Scaffold( | ||||
|       body: Center( | ||||
|       body: StreamBuilder<bool>( | ||||
|         stream: bloc.authenticatedResult, | ||||
|         builder: (context, snapshot) { | ||||
|           if (snapshot.data == null || snapshot.data == true) | ||||
|             return _appLoading(); | ||||
|           return _appLogin(); | ||||
|         } | ||||
|       ) | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   void _init(){ | ||||
|     if(bloc != null){ | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     bloc = LoginBloc(AppProvider.getApplication(context)); | ||||
|     bloc.authenticatedResult.listen((bool status) { | ||||
|       if (status) | ||||
|         AppProvider.getRouter(context).navigateTo(context, "/Home", transition: TransitionType.fadeIn); | ||||
|     }); | ||||
| 
 | ||||
|     bloc.loginResult.listen((bool status) { | ||||
|       if (status == null || status == false) | ||||
|         return; | ||||
| 
 | ||||
|       AppProvider.getRouter(context).navigateTo(context, "/Home", transition: TransitionType.fadeIn); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   Widget _appLoading(){ | ||||
|     return Center( | ||||
|       child: ConstrainedBox( | ||||
|         constraints: BoxConstraints(maxWidth: 500), | ||||
|         child: Container( | ||||
|           margin: EdgeInsets.fromLTRB(50, 0, 50, 0), | ||||
|           height: 270, | ||||
|           child: Column( | ||||
|             children: <Widget>[ | ||||
|               Container( | ||||
|                 child: FittedBox( | ||||
|                   fit: BoxFit.contain, | ||||
|                   child: const FlutterLogo(), | ||||
|                 ), | ||||
|                 width: 175, | ||||
|                 margin: EdgeInsets.fromLTRB(0, 0, 0, 50), | ||||
|               ), | ||||
|               PlatformCircularProgressIndicator() | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Widget _appLogin(){ | ||||
|     TextEditingController serverController = new TextEditingController(); | ||||
|     TextEditingController userController = new TextEditingController(); | ||||
|     TextEditingController passwordController = new TextEditingController(); | ||||
| 
 | ||||
|     final _formKey = GlobalKey<FormState>(); | ||||
| 
 | ||||
|     return Center( | ||||
|       child: ConstrainedBox( | ||||
|         constraints: BoxConstraints(maxWidth: 500), | ||||
|         child: Container( | ||||
|           margin: EdgeInsets.fromLTRB(50, 0, 50, 0), | ||||
|           height: 500, | ||||
|           child: Form( | ||||
|             key: _formKey, | ||||
|             child: Column( | ||||
|               children: <Widget>[ | ||||
|                 Container( | ||||
| @ -46,24 +111,45 @@ class _LoginScreenState extends State<LoginScreen> { | ||||
|                   margin: EdgeInsets.fromLTRB(0, 0, 0, 50), | ||||
|                 ), | ||||
|                 Expanded( | ||||
|                   child: TextField( | ||||
|                   child: TextFormField( | ||||
|                     controller: serverController, | ||||
|                     validator: (value) { | ||||
|                       if (value.isEmpty) { | ||||
|                         return 'Please enter server address'; | ||||
|                       } | ||||
|                       return null; | ||||
|                     }, | ||||
|                     decoration: InputDecoration( | ||||
|                       labelText: 'Server Address' | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|                 Expanded( | ||||
|                   child: TextField( | ||||
|                   child: TextFormField( | ||||
|                     controller: userController, | ||||
|                     validator: (value) { | ||||
|                       if (value.isEmpty) { | ||||
|                         return 'Please enter username or email'; | ||||
|                       } | ||||
|                       return null; | ||||
|                     }, | ||||
|                     decoration: InputDecoration( | ||||
|                       labelText: 'Username / Email' | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|                 Expanded( | ||||
|                   child: TextField( | ||||
|                   child: TextFormField( | ||||
|                     obscureText: true, | ||||
|                     enableSuggestions: false, | ||||
|                     autocorrect: false, | ||||
|                     controller: passwordController, | ||||
|                     validator: (value) { | ||||
|                       if (value.isEmpty) { | ||||
|                         return 'Please enter password'; | ||||
|                       } | ||||
|                       return null; | ||||
|                     }, | ||||
|                     decoration: InputDecoration( | ||||
|                       labelText: 'Password' | ||||
|                     ), | ||||
| @ -73,11 +159,31 @@ class _LoginScreenState extends State<LoginScreen> { | ||||
|                   width: double.infinity, | ||||
|                   child: PlatformButton( | ||||
|                     onPressed: () { | ||||
|                       AppProvider.getRouter(context).navigateTo(context, "/Home", transition: TransitionType.fadeIn); | ||||
|                       if (!_formKey.currentState.validate())  | ||||
|                         return; | ||||
|                       bloc.attemptLogin(userController.text, passwordController.text, serverController.text); | ||||
|                     }, | ||||
|                     child: Text('Login') | ||||
|                   ), | ||||
|                 ), | ||||
| 
 | ||||
|                 StreamBuilder<bool>( | ||||
|                   stream: bloc.loginResult, | ||||
|                   builder: (context, snapshot) { | ||||
|                     if (snapshot.data == null || snapshot.data == true) | ||||
|                       return Container(); | ||||
| 
 | ||||
|                     return Expanded( | ||||
|                       child: Padding( | ||||
|                         padding: EdgeInsets.fromLTRB(20, 20, 20, 20), | ||||
|                         child: Text( | ||||
|                           "Login Failed", | ||||
|                           style: TextStyle(color: Colors.red), | ||||
|                         ) | ||||
|                       ), | ||||
|                     ); | ||||
|                   } | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
| @ -85,10 +191,4 @@ class _LoginScreenState extends State<LoginScreen> { | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   void _init(){ | ||||
|     // if(null == bloc){ | ||||
|     //   bloc = LoginBloc(AppProvider.getApplication(context)); | ||||
|     // } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,70 +0,0 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; | ||||
| import 'package:fluro/fluro.dart'; | ||||
| 
 | ||||
| import 'package:imagini/core/app_provider.dart'; | ||||
| import 'package:imagini/blocs/splash_bloc.dart'; | ||||
| import 'package:imagini/graphql/imagini_graphql.dart'; | ||||
| 
 | ||||
| class SplashScreen extends StatefulWidget { | ||||
|   static const String PATH = '/'; | ||||
| 
 | ||||
|   SplashScreen({Key key}) : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   _SplashScreenState createState() => _SplashScreenState(); | ||||
| } | ||||
| 
 | ||||
| class _SplashScreenState extends State<SplashScreen> { | ||||
| 
 | ||||
|   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: <Widget>[ | ||||
|                 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((Login$Query$AuthResponse lr) { | ||||
|         if (lr.result == AuthResult.success) { | ||||
|           AppProvider.getRouter(context).navigateTo(context, "/Home", transition: TransitionType.fadeIn); | ||||
|         } else { | ||||
|           AppProvider.getRouter(context).navigateTo(context, "/Login", transition: TransitionType.fadeIn); | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user