Skip to content

Commit

Permalink
massive refactor to deal with selected files in a much better manner.
Browse files Browse the repository at this point in the history
  • Loading branch information
David Hobley committed Apr 7, 2024
1 parent 0f48071 commit a29b62e
Show file tree
Hide file tree
Showing 46 changed files with 675 additions and 435 deletions.
6 changes: 6 additions & 0 deletions lib/interfaces/tag_handler.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import '../models/tag.dart';

abstract class TagHandler {
void removeTag(Tag tag);
void updateTags(String tags);
}
9 changes: 7 additions & 2 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:media_kit/media_kit.dart';
import 'package:shackleton/repositories/app_settings_repository.dart';
import 'package:window_manager/window_manager.dart';

import 'database/app_database.dart';
import 'misc/provider_logger.dart';
import 'providers/shackleton_theme.dart';
import 'repositories/app_settings_repository.dart';
import 'widgets/shackleton.dart';

Future<void> openDatabase() async {
Expand All @@ -16,8 +17,12 @@ Future<void> openDatabase() async {

void main() async {
WidgetsFlutterBinding.ensureInitialized();
await windowManager.ensureInitialized();
MediaKit.ensureInitialized();

// TODO: Should we care about optimising portrait images which could be made marginally smaller?
// debugInvertOversizedImages = true;

await openDatabase();

runApp(ProviderScope(
Expand All @@ -26,7 +31,7 @@ void main() async {
}

class ShackletonApp extends ConsumerWidget {
const ShackletonApp({Key? key}) : super(key: key);
const ShackletonApp({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
Expand Down
22 changes: 17 additions & 5 deletions lib/misc/keyboard_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import '../interfaces/keyboard_callback.dart';
class KeyboardHandler {
bool isIndividualMultiSelectionPressed = false;
bool isBlockMultiSelectionPressed = false;
bool processModifierKeys = true;
bool hasFocus = false;
bool isEditing = false;
KeyboardCallback keyboardCallback;
Expand Down Expand Up @@ -50,16 +51,27 @@ class KeyboardHandler {
}

return true;
} else if (HardwareKeyboard.instance.isShiftPressed) {
} else if (processModifierKeys && HardwareKeyboard.instance.isShiftPressed) {
if (event.logicalKey == LogicalKeyboardKey.tab) {
isBlockMultiSelectionPressed = false;
keyboardCallback.left();
return true;
}

isBlockMultiSelectionPressed = true;
return true;
} else {
isBlockMultiSelectionPressed = true;

if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
keyboardCallback.left();
} else if (event.logicalKey == LogicalKeyboardKey.arrowRight) {
keyboardCallback.right();
} else if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
keyboardCallback.up();
} else if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
keyboardCallback.down();
}

return true;
return true;
}
} else if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
keyboardCallback.left();

Expand Down
2 changes: 1 addition & 1 deletion lib/misc/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Future<FileOfInterest?> createZip(FileOfInterest folder, Set<FileOfInterest> fil
}

String convertLatLng(double decimal, bool isLat) {
String degree = "${decimal.toString().split(".")[0]} deg";
String degree = "${decimal.abs().toString().split(".")[0]} deg";

double minutesBeforeConversion = double.parse("0.${decimal.toString().split(".")[1]}");
String minutes = "${(minutesBeforeConversion * 60).toString().split('.')[0]}'";
Expand Down
2 changes: 2 additions & 0 deletions lib/models/folder_ui_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ String _fseToJson(FileSystemEntity entity) => entity.path;
bool _boolFromJson(int value) => value.isOdd;
int _boolToJson(bool value) => value ? 1 : 0;

const String navigationFolder = '**navigation**';

@freezed
class FolderUISettings with _$FolderUISettings {
const factory FolderUISettings({
Expand Down
2 changes: 1 addition & 1 deletion lib/models/folder_ui_settings.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ part of 'folder_ui_settings.dart';
T _$identity<T>(T value) => value;

final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');

FolderUISettings _$FolderUISettingsFromJson(Map<String, dynamic> json) {
return _FolderUISettings.fromJson(json);
Expand Down
24 changes: 16 additions & 8 deletions lib/providers/contents/folder_contents.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,41 @@ import 'dart:io';

import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../models/file_of_interest.dart';
import '../providers/file_events.dart';

import '../../models/file_of_interest.dart';
import '../../providers/contents/grid_contents.dart';
import '../../providers/contents/selected_folder_contents.dart';
import '../file_events.dart';

part 'folder_contents.g.dart';

enum EntitySortField { name, size, modified }
enum EntitySortOrder { asc, desc }

@Riverpod(keepAlive: true)
@riverpod
class FolderContents extends _$FolderContents {
EntitySortField _defaultSort = EntitySortField.name;
EntitySortOrder _defaultSortOrder = EntitySortOrder.asc;

@override
List<FileOfInterest> build(Directory path) {
getFolderContents(path);
watchFolder(path);
return state;

return getFolderContents(path);
}

void add(FileOfInterest entity) {
List<FileOfInterest> entities = [...state, entity];
state = [...sort(entities, _defaultSort)];
}

void getFolderContents(Directory path) {
List<FileOfInterest> getFolderContents(Directory path) {
List<FileOfInterest> files = [];
for (var file in path.listSync()) {
files.add(FileOfInterest(entity: file));
}
state = [...sort(files, _defaultSort)];

return sort(files, _defaultSort);
}

EntitySortField getSortField() {
Expand Down Expand Up @@ -73,7 +77,7 @@ class FolderContents extends _$FolderContents {
} else {
_defaultSort = sortField;
}
state = [...sort(state, _defaultSort)];
state = sort(List.from(state), _defaultSort);
}

void watchFolder(Directory path) async {
Expand All @@ -86,6 +90,10 @@ class FolderContents extends _$FolderContents {
if (!state.contains(foi)) {
if (!foi.isHidden) {
add(foi);
// If the selectedFolderContentsProvider contains this folder, we should update the previewGridProvider manually.
if (ref.read(selectedFolderContentsProvider).contains(FileOfInterest(entity: Directory(path.path)))) {
ref.read(gridContentsProvider.notifier).add(foi);
}
}
}
break;
Expand Down
71 changes: 31 additions & 40 deletions lib/providers/contents/grid_contents.dart
Original file line number Diff line number Diff line change
@@ -1,57 +1,56 @@
import 'dart:io';

import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shackleton/providers/selected_entities/selected_folder_contents.dart';

import '../../interfaces/file_events_callback.dart';
import '../../models/file_of_interest.dart';
import '../../providers/file_events.dart';
import '../../../interfaces/file_events_callback.dart';
import '../../../models/file_of_interest.dart';
import '../../../providers/file_events.dart';
import 'selected_folder_contents.dart';

part 'grid_contents.g.dart';

@Riverpod(keepAlive: true)
class GridContents extends _$GridContents implements FileEventsCallback {
@override
Set<FileOfInterest> build() {
List<FileOfInterest> build() {
Future(() {
register();
});

Set<FileOfInterest> entities = ref.watch(selectedFolderContentsProvider);
if (entities.isEmpty) {
Set<FileOfInterest> gridEntities = {};

for (var entity in entities) {
if (entity.canPreview) {
gridEntities.add(entity);
} else if (entity.isDirectory) {
Directory d = Directory(entity.path);
for (var e in d.listSync()) {
FileOfInterest foi = FileOfInterest(entity: e);
if (foi.canPreview) {
gridEntities.add(foi);
}
List<FileOfInterest> gridEntities = [];

for (var entity in entities) {
if (entity.canPreview) {
gridEntities.add(entity);
} else if (entity.isDirectory) {
Directory d = Directory(entity.path);
for (var e in d.listSync()) {
FileOfInterest foi = FileOfInterest(entity: e);
if (foi.canPreview) {
gridEntities.add(foi);
}
}
}
return gridEntities;
} else {
return Set.from(entities);
}
gridEntities.sort();
return gridEntities;
}

void add(FileOfInterest entity) {
if (!state.contains(entity)) {
state = { ...state, entity};
state = [ ...state, entity ];
state.sort();
}
}

void addAll(Set<FileOfInterest> entities) {
state = { ...state, ...entities };
state = { ...state, ...entities }.toList();
state.sort();
}

void clear() {
state = {};
state = [];
}

bool contains(FileOfInterest entity) {
Expand All @@ -68,33 +67,25 @@ class GridContents extends _$GridContents implements FileEventsCallback {

@override
void remove(FileOfInterest entity) {
if (entity.isDirectory) {
state = {
if (state.contains(entity)) {
state = [
for (var e in state)
if (!e.path.startsWith(entity.path))
e
};
} else {
if (state.contains(entity)) {
state = {
for (var e in state)
if (e.path != entity.path)
e
};
}
if (e.path != entity.path) e
];
}
}

void removeAll() {
state = {};
state = [];
}

void replace(FileOfInterest entity) {
state = { entity };
state = [ entity ];
}

void replaceAll(Set<FileOfInterest> entities) {
state = { ...entities };
state = List.from(entities);
state.sort();
}

int size() {
Expand Down
16 changes: 11 additions & 5 deletions lib/providers/contents/grid_tags.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shackleton/providers/selected_entities/selected_grid_entities.dart';

import '../../models/file_metadata.dart';
import '../../models/file_of_interest.dart';
import '../../providers/metadata.dart';
import '../../models/tag.dart';
import '../../providers/metadata.dart';

import 'grid_contents.dart';
import 'selected_grid_entities.dart';

part 'selected_tags.g.dart';
part 'grid_tags.g.dart';

@riverpod
class SelectedTags extends _$SelectedTags {
class GridTags extends _$GridTags {
@override
List<Tag> build() {
Set<FileOfInterest> entities = ref.watch(selectedEntitiesProvider);
List<FileOfInterest> selectedEntities = ref.watch(selectedGridEntitiesProvider);
List<FileOfInterest> gridEntities = ref.watch(gridContentsProvider);

List<FileOfInterest> entities = selectedEntities.isNotEmpty ? selectedEntities : gridEntities;

List<FileMetaData> metadata = entities.map((e) => ref.watch(metadataProvider(e))).toList();
List<Tag> tags = [...{ for (var e in metadata) ...e.tags}];
tags.sort();
Expand Down
37 changes: 15 additions & 22 deletions lib/providers/contents/pane_contents.dart
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
import 'dart:io';

import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shackleton/providers/grid_contents.dart';
import 'package:shackleton/providers/selected_entities/selected_folder_contents.dart';
import 'package:shackleton/providers/selected_entities/selected_grid_entities.dart';

import '../../interfaces/file_events_callback.dart';
import '../../models/file_of_interest.dart';
import '../../providers/file_events.dart';
import '../../../interfaces/file_events_callback.dart';
import '../../../models/file_of_interest.dart';
import 'grid_contents.dart';
import 'selected_grid_entities.dart';

part 'pane_contents.g.dart';

@riverpod
@Riverpod(keepAlive: true)
class PaneContents extends _$PaneContents implements FileEventsCallback {
@override
List<FileOfInterest> build() {
Future(() {
register();
});
List<FileOfInterest> selectedEntities = ref.watch(selectedGridEntitiesProvider);
List<FileOfInterest> gridEntities = ref.watch(gridContentsProvider);
List<FileOfInterest> sortedEntities = selectedEntities.isNotEmpty ? selectedEntities : gridEntities;

Set<FileOfInterest> entities = ref.watch(selectedGridEntitiesProvider);
if (entities.isEmpty) {
entities = ref.watch(gridContentsProvider);
}
List<FileOfInterest> sortedEntities = List.from(entities);
sortedEntities.sort();

return sortedEntities;
Expand All @@ -41,13 +32,15 @@ class PaneContents extends _$PaneContents implements FileEventsCallback {
return state.contains(entity);
}

Future<void> register() async {
ref.read(fileEventsProvider.notifier).register(this);
}

@override
void remove(FileOfInterest entity) {
state.remove(entity);
if (state.contains(entity)) {
state = [
for (var e in state)
if (e.path != entity.path)
e
];
}
}

void removeAll() {
Expand Down
Loading

0 comments on commit a29b62e

Please sign in to comment.