diff --git a/internal/api/media.go b/internal/api/media.go index 7994247..fb1cc33 100644 --- a/internal/api/media.go +++ b/internal/api/media.go @@ -37,7 +37,6 @@ func (api *API) mediaHandler(w http.ResponseWriter, r *http.Request) { rawUserID, _ := (*authContext.AccessToken).Get("sub") userID := rawUserID.(string) - // TODO: Derive Cache & Width Location // Derive Path fileName := path.Base(r.URL.Path) folderPath := path.Join("/" + api.Config.DataPath + "/media/" + userID) diff --git a/internal/api/middlewares.go b/internal/api/middlewares.go index e7d6651..647dc61 100644 --- a/internal/api/middlewares.go +++ b/internal/api/middlewares.go @@ -52,6 +52,20 @@ func (api *API) queryMiddleware(next http.Handler) http.Handler { **/ func (api *API) authMiddleware(next http.Handler) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + // TODO: REMOVE (SOME OF) THIS!! Only for developement due to CORS + w.Header().Set("Access-Control-Allow-Credentials", "true") + w.Header().Set("Access-Control-Allow-Origin", "*") + //w.Header().Set("Access-Control-Expose-Headers", "*") + w.Header().Set("Access-Control-Allow-Headers", "x-imagini-accesstoken,x-imagini-refreshtoken") + w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") + + // CORS Preflight + if r.Method == http.MethodOptions { + w.WriteHeader(http.StatusOK) + return + } + // Validate Tokens accessToken, err := api.validateTokens(&w, r) if err != nil { diff --git a/web_native/ios/Podfile.lock b/web_native/ios/Podfile.lock index 9e71cbf..7e946c3 100644 --- a/web_native/ios/Podfile.lock +++ b/web_native/ios/Podfile.lock @@ -39,6 +39,9 @@ PODS: - Flutter (1.0.0) - flutter_secure_storage (3.3.1): - Flutter + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) - integration_test (0.0.1): - Flutter - path_provider (0.0.1): @@ -49,6 +52,9 @@ PODS: - SDWebImage/Core (5.10.2) - shared_preferences (0.0.1): - Flutter + - sqflite (0.0.2): + - Flutter + - FMDB (>= 2.7.5) - SwiftyGif (5.3.0) - url_launcher (0.0.1): - Flutter @@ -61,12 +67,14 @@ DEPENDENCIES: - integration_test (from `.symlinks/plugins/integration_test/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`) SPEC REPOS: trunk: - DKImagePickerController - DKPhotoGallery + - FMDB - Reachability - SDWebImage - SwiftyGif @@ -86,6 +94,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/path_provider/ios" shared_preferences: :path: ".symlinks/plugins/shared_preferences/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/ios" url_launcher: :path: ".symlinks/plugins/url_launcher/ios" @@ -96,11 +106,13 @@ SPEC CHECKSUMS: file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a integration_test: 6eb66a19f7104200dcfdd62bc0077e1b09686e4f path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 SDWebImage: b969dcfc02c40a5da71eac0b03b8f1a0c794a86f shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40 url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef diff --git a/web_native/lib/api/api_provider.dart b/web_native/lib/api/api_provider.dart index 9fcfad0..fdbf4ea 100644 --- a/web_native/lib/api/api_provider.dart +++ b/web_native/lib/api/api_provider.dart @@ -6,6 +6,7 @@ import 'package:imagini/api/cookie_client/cookie_client.dart' if (dart.library.io) 'package:imagini/api/cookie_client/io_cookie_client.dart'; import 'package:imagini/core/storage_client/base_storage_client.dart'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:imagini/graphql/imagini_graphql.dart'; @@ -18,12 +19,15 @@ class APIProvider{ APIProvider(BaseStorageClient storage) { _storage = storage; - init(); } Future init() async { String _server = await _storage.get("server"); + // Initialize + if (_server == null) + _server = "http://localhost"; + httpLink = HttpLink(_server + _GRAPHQL_ENDPOINT, httpClient: getCookieClient(_storage), ); @@ -43,7 +47,7 @@ class APIProvider{ (username != null && password != null && server != null) ); - // Initialize Connection + // Initialize New Connection await _storage.set("server", server); await init(); @@ -59,12 +63,19 @@ class APIProvider{ return response; } - Image getImage(String fileName) { - // TODO: Get headers & Server - String fullURL = fileName; - return new Image.network( - fullURL, - headers: {}, + Future getImage(String fileName, int derivedContentWidth) async { + String server = await _storage.get("server"); + String accessToken = await _storage.get("accessToken"); + String refreshToken = await _storage.get("refreshToken"); + + String fullURL = "$server/media/$fileName?width=$derivedContentWidth"; + return CachedNetworkImage( + imageUrl: fullURL, + imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet, + httpHeaders: { + "X-Imagini-AccessToken": accessToken, + "X-Imagini-RefreshToken": refreshToken, + }, fit: BoxFit.contain, ); } diff --git a/web_native/lib/api/imagini_api_repository.dart b/web_native/lib/api/imagini_api_repository.dart index 1cc4459..5fc67ce 100644 --- a/web_native/lib/api/imagini_api_repository.dart +++ b/web_native/lib/api/imagini_api_repository.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:imagini/api/api_provider.dart'; import 'package:imagini/graphql/imagini_graphql.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; -import 'package:flutter/material.dart'; +import 'package:cached_network_image/cached_network_image.dart'; class ImaginiAPIRepository { APIProvider _apiProvider; @@ -41,7 +41,7 @@ class ImaginiAPIRepository { })); } - Image getImage(String fileName) { - return _apiProvider.getImage(fileName); + Future getImage(String fileName, int derivedContentWidth) { + return _apiProvider.getImage(fileName, derivedContentWidth); } } diff --git a/web_native/lib/blocs/home_bloc.dart b/web_native/lib/blocs/home_bloc.dart index 831a35a..d1a608e 100644 --- a/web_native/lib/blocs/home_bloc.dart +++ b/web_native/lib/blocs/home_bloc.dart @@ -1,9 +1,9 @@ import 'dart:async'; -import 'package:flutter/material.dart'; import 'package:imagini/core/imagini_application.dart'; import 'package:imagini/api/imagini_api_repository.dart'; import 'package:imagini/graphql/imagini_graphql.dart'; +import 'package:cached_network_image/cached_network_image.dart'; class HomeBloc{ @@ -34,8 +34,8 @@ class HomeBloc{ _mediaItemsController.close(); } - Image getImage(String fileName){ - return _imaginiAPI.getImage(fileName); + Future getImage(String fileName, int derivedContentWidth){ + return _imaginiAPI.getImage(fileName, derivedContentWidth); } getMediaItems(){ diff --git a/web_native/lib/core/imagini_application.dart b/web_native/lib/core/imagini_application.dart index e26675d..2500762 100644 --- a/web_native/lib/core/imagini_application.dart +++ b/web_native/lib/core/imagini_application.dart @@ -17,7 +17,7 @@ class ImaginiApplication { Future onCreate() async { _initRouter(); _initStorageClient(); - _initAPIRepository(); + await _initAPIRepository(); } Future onTerminate() async {} @@ -31,9 +31,10 @@ class ImaginiApplication { storageClient = getStorageClient(); } - _initAPIRepository() { + _initAPIRepository() async { // TODO: Get from config APIProvider apiProvider = new APIProvider(storageClient); + await apiProvider.init(); imaginiAPI = ImaginiAPIRepository(apiProvider); } } diff --git a/web_native/lib/core/storage_client/browser_storage_client.dart b/web_native/lib/core/storage_client/browser_storage_client.dart index 433cb76..6623a36 100644 --- a/web_native/lib/core/storage_client/browser_storage_client.dart +++ b/web_native/lib/core/storage_client/browser_storage_client.dart @@ -5,7 +5,6 @@ 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] : ""; diff --git a/web_native/lib/screens/home_screen.dart b/web_native/lib/screens/home_screen.dart index 1343c7b..35b03f2 100644 --- a/web_native/lib/screens/home_screen.dart +++ b/web_native/lib/screens/home_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:imagini/blocs/home_bloc.dart'; import 'package:imagini/core/app_provider.dart'; @@ -72,36 +73,6 @@ class _HomeScreenState extends State { } Widget _buildGridView() { - // return GridView.builder( - // itemCount: 5, - // gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount( - // childAspectRatio: (1 / 1), - // crossAxisCount: 2), - // itemBuilder: (BuildContext context, int index) { - // return _buildCard("https://i.imgur.com/CgSGqUz.jpeg"); - // } - // ); - - - // return new StaggeredGridView.countBuilder( - // crossAxisCount: 4, - // itemCount: 80, - // itemBuilder: (BuildContext context, int index) => _buildCard("https://i.imgur.com/CgSGqUz.jpeg"), - // // itemBuilder: (BuildContext context, int index) => new Container( - // // color: Colors.green, - // // child: new Center( - // // child: new CircleAvatar( - // // backgroundColor: Colors.white, - // // child: new Text('$index'), - // // ), - // // )), - // staggeredTileBuilder: (int index) => - // // new StaggeredTile.count(2, index.isEven ? 2 : 1), - // new StaggeredTile.fit(2), - // mainAxisSpacing: 4.0, - // crossAxisSpacing: 4.0, - // ); - return StreamBuilder( stream: bloc.mediaItemsResult, builder: (context, snapshot) { @@ -110,17 +81,20 @@ class _HomeScreenState extends State { List allItems = snapshot.data.mediaItems.data; + MediaQueryData queryData = MediaQuery.of(context); + + final double screenWidthSize = queryData.size.width * queryData.devicePixelRatio; + final int crossAxisCount = (screenWidthSize / 500).ceil(); + final int derivedContentWidth = (screenWidthSize / crossAxisCount).ceil(); + return new StaggeredGridView.countBuilder( - crossAxisCount: 4, + crossAxisCount: crossAxisCount, itemCount: allItems.length, itemBuilder: (BuildContext context, int index) { String fileName = allItems[index].fileName; - print(fileName); - return _buildCard("https://i.imgur.com/CgSGqUz.jpeg"); + return _buildCard(fileName, derivedContentWidth); }, - staggeredTileBuilder: (int index) => - // new StaggeredTile.count(2, index.isEven ? 2 : 1), - new StaggeredTile.fit(2), + staggeredTileBuilder: (int index) => new StaggeredTile.fit(1), mainAxisSpacing: 4.0, crossAxisSpacing: 4.0, ); @@ -153,11 +127,18 @@ class _HomeScreenState extends State { ); } - Widget _buildCard(charImageUrl) { - return new Image.network( - charImageUrl, - headers: {}, - fit: BoxFit.contain, + Widget _buildCard(charImageUrl, derivedContentWidth) { + return FutureBuilder( + future: bloc.getImage(charImageUrl, derivedContentWidth), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return PlatformCircularProgressIndicator(); + } + + return snapshot.data; + // Image newImage = snapshot.data; + // return newImage; + } ); } } diff --git a/web_native/lib/widgets/imagini_image_provider.dart.tmp b/web_native/lib/widgets/imagini_image_provider.dart.tmp deleted file mode 100644 index d4e2818..0000000 --- a/web_native/lib/widgets/imagini_image_provider.dart.tmp +++ /dev/null @@ -1,40 +0,0 @@ -import 'dart:ui'; -import 'package:flutter/material.dart'; - -class ImaginiImageProvider extends ImageProvider { - @override - final String url; - - @override - final double scale; - - @override - final Map headers; - - ImaginiImageProvider(this.url, {this.scale = 1.0, this.headers}) - : assert(url != null), - assert(scale != null); - - NetworkImage _netImg; - - @override - ImageStreamCompleter load(ImaginiImageProvider key, DecoderCallback decode) { - return MultiFrameImageStreamCompleter( - codec: _loadAsync(decode), - scale: 1.0, - debugLabel: fileId, - informationCollector: () sync* { - yield ErrorDescription('Path: $fileId'); - }, - ); - } - - Future _loadAsync(DecoderCallback decode) async { - _netImg._loadAsync(decode); - } - - @override - Future obtainKey(ImageConfiguration configuration) { - - } -} diff --git a/web_native/pubspec.lock b/web_native/pubspec.lock index 7f8dc3b..38fba12 100644 --- a/web_native/pubspec.lock +++ b/web_native/pubspec.lock @@ -113,6 +113,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "8.0.0-nullsafety.0" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0" characters: dependency: transitive description: @@ -279,6 +286,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.2" + flutter_blurhash: + dependency: transitive + description: + name: flutter_blurhash + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" flutter_driver: dependency: transitive description: flutter @@ -439,6 +460,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.4" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.19" integration_test: dependency: "direct dev" description: flutter @@ -528,6 +556,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.7" + octo_image: + dependency: transitive + description: + name: octo_image + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" package_config: dependency: transitive description: @@ -584,6 +619,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.10.0" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" platform: dependency: transitive description: @@ -722,6 +764,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0-nullsafety.4" + sqflite: + dependency: transitive + description: + name: sqflite + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2+3" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3+1" stack_trace: dependency: transitive description: @@ -757,6 +813,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0+2" term_glyph: dependency: transitive description: @@ -827,6 +890,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.1+3" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.2" uuid_enhanced: dependency: transitive description: @@ -890,6 +960,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.2" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "4.5.1" yaml: dependency: transitive description: @@ -899,4 +976,4 @@ packages: version: "2.2.1" sdks: dart: ">=2.12.0-0.0 <3.0.0" - flutter: ">=1.22.0" + flutter: ">=1.22.2" diff --git a/web_native/pubspec.yaml b/web_native/pubspec.yaml index 6a8d0a1..5a84fe6 100644 --- a/web_native/pubspec.yaml +++ b/web_native/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: json_annotation: ^3.1.0 meta: '>=1.0.0 <2.0.0' # only if you have non nullable fields gql: '>=0.12.3 <1.0.0' + cached_network_image: ^2.5.0 dev_dependencies: flutter_test: