security update

This commit is contained in:
Mguy13 2022-12-27 12:44:21 +01:00
parent 6946b2e802
commit ccc5eccda8
11 changed files with 58 additions and 31 deletions

2
.gitignore vendored
View File

@ -1,5 +1,7 @@
# Custom # Custom
*.pdf *.pdf
*.env
env.g.dart
# Miscellaneous # Miscellaneous
*.class *.class

12
lib/env/env.dart vendored Normal file
View File

@ -0,0 +1,12 @@
import 'package:envied/envied.dart';
part 'env.g.dart';
@Envied()
abstract class Env {
@EnviedField(
varName: 'UNSPLASH_API_KEY',
defaultValue: '',
)
static const unsplashApiKey = _Env.unsplashApiKey;
}

View File

@ -17,7 +17,7 @@ abstract class BaseViewModel<T extends Object?> extends ChangeNotifier {
final ValueNotifier<ViewModelState> _state = ValueNotifier(ViewModelState.isInitialising); final ValueNotifier<ViewModelState> _state = ValueNotifier(ViewModelState.isInitialising);
ValueListenable<ViewModelState> get state => _state; ValueListenable<ViewModelState> get state => _state;
final LoggingService _loggingService = LoggingService.locate; final LoggingService log = LoggingService.locate;
String? _errorMessage; String? _errorMessage;
String get errorMessage => _errorMessage ?? strings.somethingWentWrong; String get errorMessage => _errorMessage ?? strings.somethingWentWrong;
@ -27,7 +27,7 @@ abstract class BaseViewModel<T extends Object?> extends ChangeNotifier {
_mounted = mounted; _mounted = mounted;
_isInitialised.value = true; _isInitialised.value = true;
_state.value = ViewModelState.isInitialised; _state.value = ViewModelState.isInitialised;
_loggingService.successfulInit(location: runtimeType.toString()); log.successfulInit(location: runtimeType.toString());
} }
void setBusy(bool isBusy) { void setBusy(bool isBusy) {
@ -55,7 +55,7 @@ abstract class BaseViewModel<T extends Object?> extends ChangeNotifier {
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
_loggingService.successfulDispose(location: runtimeType.toString()); log.successfulDispose(location: runtimeType.toString());
} }
late final bool Function() _mounted; late final bool Function() _mounted;

View File

@ -7,11 +7,15 @@ import '../data/dtos/image_model_dto.dart';
/// Since I used a site that was more obscure than the ones in the examples, this (otherwise pointless /// Since I used a site that was more obscure than the ones in the examples, this (otherwise pointless
/// and convoluting) interface is for adding a bit of flexibility to change strategy to some other site. /// and convoluting) interface is for adding a bit of flexibility to change strategy to some other site.
abstract class ImagesApi { abstract class ImagesApi {
ImagesApi({required String token}) : _token = token;
/// Access token provided to be used with API calls
final String _token;
/// Returns images fetched through an API as [ImageModelDTO]s. /// Returns images fetched through an API as [ImageModelDTO]s.
FutureOr<Iterable<ImageModelDTO>> fetchImageUri({required String token}); FutureOr<Iterable<ImageModelDTO>> fetchImageUri();
FutureOr<Iterable<ImageModelDTO>> searchImages({ FutureOr<Iterable<ImageModelDTO>> searchImages({
required String searchStr, required String searchStr,
required String token,
}); });
} }

View File

@ -9,12 +9,14 @@ import '/locator.dart';
import '../abstracts/images_api.dart'; import '../abstracts/images_api.dart';
import '../data/dtos/image_model_dto.dart'; import '../data/dtos/image_model_dto.dart';
class UnsplashImagesApi implements ImagesApi { class UnsplashImagesApi extends ImagesApi {
final LoggingService _loggingService = LoggingService.locate; final LoggingService _loggingService = LoggingService.locate;
final random = Random(); final random = Random();
UnsplashImagesApi({required super.token});
@override @override
FutureOr<Iterable<ImageModelDTO>> fetchImageUri({required String token}) async { FutureOr<Iterable<ImageModelDTO>> fetchImageUri() async {
// Dummy fetching delay emulation // Dummy fetching delay emulation
await Future.delayed(const Duration( await Future.delayed(const Duration(
milliseconds: ConstValues.defaultEmulatedLatencyMillis * ConstValues.numberOfImages)); milliseconds: ConstValues.defaultEmulatedLatencyMillis * ConstValues.numberOfImages));
@ -54,7 +56,6 @@ class UnsplashImagesApi implements ImagesApi {
@override @override
FutureOr<Iterable<ImageModelDTO>> searchImages({ FutureOr<Iterable<ImageModelDTO>> searchImages({
required String searchStr, required String searchStr,
required String token,
}) async { }) async {
final numberOfResults = random.nextIntInRange(min: 0, max: ConstValues.numberOfImages); final numberOfResults = random.nextIntInRange(min: 0, max: ConstValues.numberOfImages);

View File

@ -49,7 +49,7 @@ class ImagesService {
Future<void> _init() async { Future<void> _init() async {
_loggingService.info('Fetching and creating image models...'); _loggingService.info('Fetching and creating image models...');
final fetchedImageModelDtos = await _imagesApi.fetchImageUri(token: ''); final fetchedImageModelDtos = await _imagesApi.fetchImageUri();
final favouritesStatuses = _localStorageService.storedFavouritesStates; final favouritesStatuses = _localStorageService.storedFavouritesStates;
// Prefill from stored values // Prefill from stored values
@ -128,7 +128,6 @@ class ImagesService {
case SearchOption.web: case SearchOption.web:
return (await _imagesApi.searchImages( return (await _imagesApi.searchImages(
searchStr: imageNamePart, searchStr: imageNamePart,
token: '',
)) ))
.map( .map(
(final imageModelDto) => ImageModel.fromDto( (final imageModelDto) => ImageModel.fromDto(

View File

@ -6,7 +6,6 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import '/features/core/abstracts/base_view_model.dart'; import '/features/core/abstracts/base_view_model.dart';
import '/features/core/data/extensions/value_notifier_extensions.dart'; import '/features/core/data/extensions/value_notifier_extensions.dart';
import '/features/core/services/logging_service.dart';
import '/features/core/services/navigation_service.dart'; import '/features/core/services/navigation_service.dart';
import '/locator.dart'; import '/locator.dart';
import '../../data/enums/search_option.dart'; import '../../data/enums/search_option.dart';
@ -20,17 +19,14 @@ class GalleryViewModel extends BaseViewModel {
required ImagesService imagesService, required ImagesService imagesService,
required NavigationService navigationService, required NavigationService navigationService,
required ImageCacheManagerService imageCacheManagerService, required ImageCacheManagerService imageCacheManagerService,
required LoggingService loggingService,
}) : _imagesService = imagesService, }) : _imagesService = imagesService,
_navigationService = navigationService, _navigationService = navigationService,
_imageCacheManagerService = imageCacheManagerService, _imageCacheManagerService = imageCacheManagerService;
_loggingService = loggingService;
final ImagesService _imagesService; final ImagesService _imagesService;
final NavigationService _navigationService; final NavigationService _navigationService;
//todo(mehul): Use to implement pull-to-refresh or an extra widget //todo(mehul): Use to implement pull-to-refresh or an extra widget
final ImageCacheManagerService _imageCacheManagerService; final ImageCacheManagerService _imageCacheManagerService;
final LoggingService _loggingService;
final ValueNotifier<bool> _isDisplayingPressingPrompt = ValueNotifier(true); final ValueNotifier<bool> _isDisplayingPressingPrompt = ValueNotifier(true);
ValueListenable<bool> get isDisplayingPressingPrompt => _isDisplayingPressingPrompt; ValueListenable<bool> get isDisplayingPressingPrompt => _isDisplayingPressingPrompt;
@ -59,7 +55,7 @@ class GalleryViewModel extends BaseViewModel {
// If empty-string (from backspacing) -> reset state. // If empty-string (from backspacing) -> reset state.
if (searchTerm.isEmpty) { if (searchTerm.isEmpty) {
_imageSearchResultsNotifier.value = []; _imageSearchResultsNotifier.value = [];
_loggingService.info('Clearing results on search string removal'); log.info('Clearing results on search string removal');
return; return;
} }
@ -82,7 +78,7 @@ class GalleryViewModel extends BaseViewModel {
// If transitioning from 'Searching', clear previous results immediately // If transitioning from 'Searching', clear previous results immediately
if (_isSearchingNotifier.value) { if (_isSearchingNotifier.value) {
_imageSearchResultsNotifier.value = []; _imageSearchResultsNotifier.value = [];
_loggingService.info('Clearing of results on view mode change'); log.info('Clearing of results on view mode change');
} }
_isSearchingNotifier.flipValue(); _isSearchingNotifier.flipValue();
@ -92,10 +88,10 @@ class GalleryViewModel extends BaseViewModel {
void onSearchOptionChanged(SearchOption? option) { void onSearchOptionChanged(SearchOption? option) {
_searchOptionNotifier.value = option!; _searchOptionNotifier.value = option!;
_loggingService.info('Switched over to $option search'); log.info('Switched over to $option search');
_imageSearchResultsNotifier.value = []; _imageSearchResultsNotifier.value = [];
_loggingService.info('Cleared resultsw from view'); log.info('Cleared resultsw from view');
//todo(mehul): Either redo search or force user to type in new (trigger) by clearing field //todo(mehul): Either redo search or force user to type in new (trigger) by clearing field
} }

View File

@ -1,22 +1,18 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:mc_gallery/features/home/views/image_carousel/image_carousel_view.dart';
import '/features/core/abstracts/base_view_model.dart'; import '/features/core/abstracts/base_view_model.dart';
import '/features/core/services/logging_service.dart';
import '/features/home/services/images_service.dart'; import '/features/home/services/images_service.dart';
import '/features/home/views/image_carousel/image_carousel_view.dart';
import '/locator.dart'; import '/locator.dart';
import '../../data/models/image_model.dart'; import '../../data/models/image_model.dart';
class ImageCarouselViewModel extends BaseViewModel { class ImageCarouselViewModel extends BaseViewModel {
ImageCarouselViewModel({ ImageCarouselViewModel({
required ImagesService imagesService, required ImagesService imagesService,
required LoggingService loggingService, }) : _imagesService = imagesService;
}) : _imagesService = imagesService,
_loggingService = loggingService;
final ImagesService _imagesService; final ImagesService _imagesService;
final LoggingService _loggingService;
late final ValueNotifier<ImageModel> _currentImageModelNotifier; late final ValueNotifier<ImageModel> _currentImageModelNotifier;
ValueListenable<ImageModel> get currentImageModelListenable => _currentImageModelNotifier; ValueListenable<ImageModel> get currentImageModelListenable => _currentImageModelNotifier;
@ -24,8 +20,8 @@ class ImageCarouselViewModel extends BaseViewModel {
@override @override
Future<void> initialise(bool Function() mounted, [arguments]) async { Future<void> initialise(bool Function() mounted, [arguments]) async {
_currentImageModelNotifier = ValueNotifier(_imagesService.imageModels _currentImageModelNotifier = ValueNotifier(_imagesService.imageModels
.elementAt((arguments! as ImageCarouselViewArguments).imageIndexKey)); .elementAt((arguments as ImageCarouselViewArguments).imageIndexKey));
_loggingService.info('Initialized with image: ${_currentImageModelNotifier.value.imageIndex}'); log.info('Initialized with image: ${_currentImageModelNotifier.value.imageIndex}');
super.initialise(mounted, arguments); super.initialise(mounted, arguments);
} }
@ -37,7 +33,7 @@ class ImageCarouselViewModel extends BaseViewModel {
void swipedTo({required int newIndex}) { void swipedTo({required int newIndex}) {
_currentImageModelNotifier.value = _imagesService.imageModelAt(index: newIndex); _currentImageModelNotifier.value = _imagesService.imageModelAt(index: newIndex);
_loggingService.info('Swiped to image: ${_currentImageModelNotifier.value.imageIndex}'); log.info('Swiped to image: ${_currentImageModelNotifier.value.imageIndex}');
} }
String get currentImageUrl => currentImageModelListenable.value.uri.toString(); String get currentImageUrl => currentImageModelListenable.value.uri.toString();

View File

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart'; import 'package:internet_connection_checker/internet_connection_checker.dart';
import 'package:mc_gallery/env/env.dart';
import 'features/core/abstracts/router/app_router.dart'; import 'features/core/abstracts/router/app_router.dart';
import 'features/core/services/app_lifecycle_service.dart'; import 'features/core/services/app_lifecycle_service.dart';
@ -37,7 +38,7 @@ class Locator {
static void _registerAPIs() { static void _registerAPIs() {
instance().registerFactory( instance().registerFactory(
() => UnsplashImagesApi(), () => UnsplashImagesApi(token: Env.unsplashApiKey),
); );
} }
@ -47,13 +48,11 @@ class Locator {
imagesService: ImagesService.locate, imagesService: ImagesService.locate,
navigationService: NavigationService.locate, navigationService: NavigationService.locate,
imageCacheManagerService: ImageCacheManagerService.locate, imageCacheManagerService: ImageCacheManagerService.locate,
loggingService: LoggingService.locate,
), ),
); );
instance().registerFactory( instance().registerFactory(
() => ImageCarouselViewModel( () => ImageCarouselViewModel(
imagesService: ImagesService.locate, imagesService: ImagesService.locate,
loggingService: LoggingService.locate,
), ),
); );
} }

View File

@ -232,6 +232,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.6" version: "4.0.6"
envied:
dependency: "direct main"
description:
name: envied
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
envied_generator:
dependency: "direct dev"
description:
name: envied_generator
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:

View File

@ -29,6 +29,9 @@ dependencies:
hive: ^2.2.3 hive: ^2.2.3
hive_flutter: ^1.1.0 hive_flutter: ^1.1.0
# Environment
envied: ^0.3.0
# Util backend # Util backend
intl_utils: ^2.8.1 intl_utils: ^2.8.1
connectivity_plus: ^3.0.2 connectivity_plus: ^3.0.2
@ -58,6 +61,7 @@ dev_dependencies:
build_runner: ^2.3.3 build_runner: ^2.3.3
json_serializable: ^6.5.4 json_serializable: ^6.5.4
hive_generator: ^2.0.0 hive_generator: ^2.0.0
envied_generator: ^0.3.0
# Annotations # Annotations
json_annotation: ^4.7.0 json_annotation: ^4.7.0