wip(mobile): unify-colors
This commit is contained in:
parent
780d3b174d
commit
ecb1669ab8
37 changed files with 327 additions and 246 deletions
|
@ -1,5 +1,7 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
||||
|
||||
extension ContextHelper on BuildContext {
|
||||
// Returns the current size from MediaQuery
|
||||
|
@ -32,6 +34,12 @@ extension ContextHelper on BuildContext {
|
|||
// Current ColorScheme used
|
||||
ColorScheme get colorScheme => themeData.colorScheme;
|
||||
|
||||
// Red Accent harmonized with primary color
|
||||
Color get redColor => redAccent.harmonizeWith(primaryColor);
|
||||
|
||||
// Orange Accent harmonized with primary color
|
||||
Color get orangeColor => orangeAccent.harmonizeWith(primaryColor);
|
||||
|
||||
// Pop-out from the current context with optional result
|
||||
void pop<T>([T? result]) => Navigator.of(this).pop(result);
|
||||
|
||||
|
@ -45,7 +53,7 @@ extension ContextHelper on BuildContext {
|
|||
) =>
|
||||
AutoRouter.of(this).navigate(route);
|
||||
|
||||
// Auto-Push replace route from the current context
|
||||
// Auto-Push replace route from the current context
|
||||
Future<T?> autoReplace<T extends Object?>(PageRouteInfo<dynamic> route) =>
|
||||
AutoRouter.of(this).replace(route);
|
||||
|
||||
|
|
27
mobile/lib/extensions/color_extensions.dart
Normal file
27
mobile/lib/extensions/color_extensions.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'dart:ui';
|
||||
|
||||
extension LightenDarken on Color {
|
||||
/// Darken a color by [percent] amount (100 = black)
|
||||
Color darken([int percent = 10]) {
|
||||
assert(1 <= percent && percent <= 100);
|
||||
var f = 1 - percent / 100;
|
||||
return Color.fromARGB(
|
||||
alpha,
|
||||
(red * f).round(),
|
||||
(green * f).round(),
|
||||
(blue * f).round(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Lighten a color by [percent] amount (100 = white)
|
||||
Color lighten([int percent = 10]) {
|
||||
assert(1 <= percent && percent <= 100);
|
||||
var p = percent / 100;
|
||||
return Color.fromARGB(
|
||||
alpha,
|
||||
red + ((255 - red) * p).round(),
|
||||
green + ((255 - green) * p).round(),
|
||||
blue + ((255 - blue) * p).round(),
|
||||
);
|
||||
}
|
||||
}
|
21
mobile/lib/extensions/widgetref_extensions.dart
Normal file
21
mobile/lib/extensions/widgetref_extensions.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/providers/immich_loading_overlay.provider.dart';
|
||||
|
||||
extension LoadingOverlay on WidgetRef {
|
||||
ValueNotifier<bool> useProcessingOverlay() {
|
||||
final result = useState(false);
|
||||
final immichOverlayController =
|
||||
read(immichLoadingOverlayController.notifier);
|
||||
useValueChanged(
|
||||
result.value,
|
||||
(_, __) => result.value
|
||||
? WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => immichOverlayController.show())
|
||||
: WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => immichOverlayController.hide()),
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -216,7 +216,10 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
|||
navigatorObservers: () => [TabNavigationObserver(ref: ref)],
|
||||
),
|
||||
),
|
||||
const ImmichLoadingOverlay(),
|
||||
ImmichLoadingOverlay(
|
||||
darkTheme: immichDarkTheme,
|
||||
theme: immichLightTheme,
|
||||
),
|
||||
const VersionAnnouncementOverlay(),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -50,9 +50,8 @@ class ActivitiesPage extends HookConsumerWidget {
|
|||
);
|
||||
|
||||
buildTitleWithTimestamp(Activity activity, {bool leftAlign = true}) {
|
||||
final textColor = context.isDarkTheme ? Colors.white : Colors.black;
|
||||
final textStyle = context.textTheme.bodyMedium
|
||||
?.copyWith(color: textColor.withOpacity(0.6));
|
||||
final textStyle = context.textTheme.bodyMedium?.copyWith(
|
||||
color: context.textTheme.bodyMedium?.color?.withOpacity(0.6));
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: leftAlign
|
||||
|
@ -150,14 +149,14 @@ class ActivitiesPage extends HookConsumerWidget {
|
|||
},
|
||||
),
|
||||
),
|
||||
suffixIconColor: liked ? Colors.red[700] : null,
|
||||
suffixIconColor: liked ? context.redColor : null,
|
||||
hintText: isReadOnly
|
||||
? 'shared_album_activities_input_disable'.tr()
|
||||
: 'shared_album_activities_input_hint'.tr(),
|
||||
hintStyle: TextStyle(
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 14,
|
||||
color: Colors.grey[600],
|
||||
color: context.themeData.hintColor,
|
||||
),
|
||||
),
|
||||
onEditingComplete: () async {
|
||||
|
@ -200,27 +199,31 @@ class ActivitiesPage extends HookConsumerWidget {
|
|||
onDismissed: (direction) async =>
|
||||
await ref.read(provider.notifier).removeActivity(activity.id),
|
||||
background: Container(
|
||||
color: canDelete ? Colors.red[400] : Colors.grey[600],
|
||||
color: canDelete
|
||||
? context.colorScheme.error
|
||||
: context.themeData.disabledColor,
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: canDelete
|
||||
? const Padding(
|
||||
padding: EdgeInsets.all(15),
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Icon(
|
||||
Icons.delete_sweep_rounded,
|
||||
color: Colors.black,
|
||||
color: context.colorScheme.surface,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
secondaryBackground: Container(
|
||||
color: canDelete ? Colors.red[400] : Colors.grey[600],
|
||||
color: canDelete
|
||||
? context.colorScheme.error
|
||||
: context.themeData.disabledColor,
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: canDelete
|
||||
? const Padding(
|
||||
padding: EdgeInsets.all(15),
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Icon(
|
||||
Icons.delete_sweep_rounded,
|
||||
color: Colors.black,
|
||||
color: context.colorScheme.surface,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
|
@ -288,7 +291,7 @@ class ActivitiesPage extends HookConsumerWidget {
|
|||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
Icons.favorite_rounded,
|
||||
color: Colors.red[700],
|
||||
color: context.redColor,
|
||||
),
|
||||
),
|
||||
title: buildTitleWithTimestamp(activity),
|
||||
|
|
|
@ -23,8 +23,6 @@ class AlbumThumbnailCard extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var isDarkTheme = context.isDarkTheme;
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
var cardSize = constraints.maxWidth;
|
||||
|
@ -83,7 +81,7 @@ class AlbumThumbnailCard extends StatelessWidget {
|
|||
style: TextStyle(
|
||||
fontFamily: 'WorkSans',
|
||||
fontSize: 12,
|
||||
color: isDarkTheme ? Colors.white : Colors.black,
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
if (owner != null) const TextSpan(text: ' · '),
|
||||
|
@ -124,9 +122,7 @@ class AlbumThumbnailCard extends StatelessWidget {
|
|||
album.name,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isDarkTheme
|
||||
? context.primaryColor
|
||||
: Colors.black,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -20,8 +20,6 @@ class AlbumTitleTextField extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isDarkTheme = context.isDarkTheme;
|
||||
|
||||
return TextField(
|
||||
onChanged: (v) {
|
||||
if (v.isEmpty) {
|
||||
|
@ -33,9 +31,8 @@ class AlbumTitleTextField extends ConsumerWidget {
|
|||
ref.watch(albumTitleProvider.notifier).setAlbumTitle(v);
|
||||
},
|
||||
focusNode: albumTitleTextFieldFocusNode,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 28,
|
||||
color: isDarkTheme ? Colors.grey[300] : Colors.grey[700],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
controller: albumTitleController,
|
||||
|
@ -70,15 +67,10 @@ class AlbumTitleTextField extends ConsumerWidget {
|
|||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
hintText: 'share_add_title'.tr(),
|
||||
hintStyle: TextStyle(
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 28,
|
||||
color: isDarkTheme ? Colors.grey[300] : Colors.grey[700],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
focusColor: Colors.grey[300],
|
||||
fillColor: isDarkTheme
|
||||
? const Color.fromARGB(255, 32, 33, 35)
|
||||
: Colors.grey[200],
|
||||
filled: isAlbumTitleTextFieldFocus.value,
|
||||
),
|
||||
);
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/widgetref_extensions.dart';
|
||||
import 'package:immich_mobile/modules/activities/providers/activity.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart';
|
||||
|
@ -14,7 +15,6 @@ import 'package:immich_mobile/routing/router.dart';
|
|||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
|
||||
class AlbumViewerAppbar extends HookConsumerWidget
|
||||
implements PreferredSizeWidget {
|
||||
|
@ -43,6 +43,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final newAlbumTitle = ref.watch(albumViewerProvider).editTitleText;
|
||||
final isEditAlbum = ref.watch(albumViewerProvider).isEditAlbum;
|
||||
final immichOverlayController = ref.useProcessingOverlay();
|
||||
final comments = album.shared
|
||||
? ref.watch(
|
||||
activityStatisticsStateProvider(
|
||||
|
@ -52,7 +53,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
: 0;
|
||||
|
||||
deleteAlbum() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
immichOverlayController.value = true;
|
||||
|
||||
final bool success;
|
||||
if (album.shared) {
|
||||
|
@ -74,7 +75,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
);
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
immichOverlayController.value = false;
|
||||
}
|
||||
|
||||
Future<void> showConfirmationDialog() async {
|
||||
|
@ -107,7 +108,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
'Confirm',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: !context.isDarkTheme ? Colors.red : Colors.red[300],
|
||||
color: context.colorScheme.error,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -122,7 +123,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
}
|
||||
|
||||
void onLeaveAlbumPressed() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
immichOverlayController.value = true;
|
||||
|
||||
bool isSuccess =
|
||||
await ref.watch(sharedAlbumProvider.notifier).leaveAlbum(album);
|
||||
|
@ -140,11 +141,11 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
);
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
immichOverlayController.value = false;
|
||||
}
|
||||
|
||||
void onRemoveFromAlbumPressed() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
immichOverlayController.value = true;
|
||||
|
||||
bool isSuccess =
|
||||
await ref.watch(sharedAlbumProvider.notifier).removeAssetFromAlbum(
|
||||
|
@ -167,7 +168,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
);
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
immichOverlayController.value = false;
|
||||
}
|
||||
|
||||
void handleShareAssets(
|
||||
|
@ -198,9 +199,9 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
}
|
||||
|
||||
void onShareAssetsTo() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
immichOverlayController.value = true;
|
||||
handleShareAssets(ref, context, selected);
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
immichOverlayController.value = false;
|
||||
}
|
||||
|
||||
buildBottomSheetActions() {
|
||||
|
|
|
@ -78,15 +78,10 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
|
|||
borderSide: const BorderSide(color: Colors.transparent),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
focusColor: Colors.grey[300],
|
||||
fillColor: context.isDarkTheme
|
||||
? const Color.fromARGB(255, 32, 33, 35)
|
||||
: Colors.grey[200],
|
||||
filled: titleFocusNode.hasFocus,
|
||||
hintText: 'share_add_title'.tr(),
|
||||
hintStyle: TextStyle(
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 28,
|
||||
color: context.isDarkTheme ? Colors.grey[300] : Colors.grey[700],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -4,6 +4,8 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/color_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/widgetref_extensions.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
|
@ -11,7 +13,6 @@ import 'package:immich_mobile/shared/models/album.dart';
|
|||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
|
||||
class AlbumOptionsPage extends HookConsumerWidget {
|
||||
final Album album;
|
||||
|
@ -25,6 +26,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
final userId = ref.watch(authenticationProvider).userId;
|
||||
final activityEnabled = useState(album.activityEnabled);
|
||||
final isOwner = owner?.id == userId;
|
||||
final immichOverlayController = ref.useProcessingOverlay();
|
||||
|
||||
void showErrorMessage() {
|
||||
Navigator.pop(context);
|
||||
|
@ -37,7 +39,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
void leaveAlbum() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
immichOverlayController.value = true;
|
||||
|
||||
try {
|
||||
final isSuccess =
|
||||
|
@ -54,11 +56,11 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
showErrorMessage();
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
immichOverlayController.value = false;
|
||||
}
|
||||
|
||||
void removeUserFromAlbum(User user) async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
immichOverlayController.value = true;
|
||||
|
||||
try {
|
||||
await ref
|
||||
|
@ -71,7 +73,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
immichOverlayController.value = false;
|
||||
}
|
||||
|
||||
void handleUserClick(User user) {
|
||||
|
@ -131,7 +133,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
),
|
||||
subtitle: Text(
|
||||
album.owner.value?.email ?? "",
|
||||
style: TextStyle(color: Colors.grey[500]),
|
||||
style: TextStyle(color: context.colorScheme.onSurface.darken(40)),
|
||||
),
|
||||
trailing: const Text(
|
||||
"Owner",
|
||||
|
@ -162,7 +164,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
),
|
||||
subtitle: Text(
|
||||
user.email,
|
||||
style: TextStyle(color: Colors.grey[500]),
|
||||
style: TextStyle(color: context.colorScheme.onSurface.darken(40)),
|
||||
),
|
||||
trailing: userId == user.id || isOwner
|
||||
? const Icon(Icons.more_horiz_rounded)
|
||||
|
@ -208,9 +210,6 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
album.activityEnabled = value;
|
||||
}
|
||||
},
|
||||
activeColor: activityEnabled.value
|
||||
? context.primaryColor
|
||||
: context.themeData.disabledColor,
|
||||
dense: true,
|
||||
title: Text(
|
||||
"shared_album_activity_setting_title",
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/widgetref_extensions.dart';
|
||||
import 'package:immich_mobile/modules/album/models/asset_selection_page_result.model.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
|
@ -19,7 +20,6 @@ import 'package:immich_mobile/shared/models/asset.dart';
|
|||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
|
||||
class AlbumViewerPage extends HookConsumerWidget {
|
||||
final int albumId;
|
||||
|
@ -33,6 +33,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
final userId = ref.watch(authenticationProvider).userId;
|
||||
final selection = useState<Set<Asset>>({});
|
||||
final multiSelectEnabled = useState(false);
|
||||
final immichOverlayController = ref.useProcessingOverlay();
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
|
@ -78,7 +79,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
if (returnPayload != null) {
|
||||
// Check if there is new assets add
|
||||
if (returnPayload.selectedAssets.isNotEmpty) {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
immichOverlayController.value = true;
|
||||
|
||||
var addAssetsResult =
|
||||
await ref.watch(albumServiceProvider).addAdditionalAssetToAlbum(
|
||||
|
@ -91,7 +92,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
ref.invalidate(albumDetailProvider(albumId));
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
immichOverlayController.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +103,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
);
|
||||
|
||||
if (sharedUserIds != null) {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
immichOverlayController.value = true;
|
||||
|
||||
var isSuccess = await ref
|
||||
.watch(albumServiceProvider)
|
||||
|
@ -112,7 +113,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
ref.invalidate(albumDetailProvider(album.id));
|
||||
}
|
||||
|
||||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
immichOverlayController.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -101,9 +101,8 @@ class LibraryPage extends HookConsumerWidget {
|
|||
),
|
||||
Text(
|
||||
option,
|
||||
style: TextStyle(
|
||||
style: context.textTheme.displaySmall?.copyWith(
|
||||
color: selected ? context.primaryColor : null,
|
||||
fontSize: 12.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -244,8 +243,8 @@ class LibraryPage extends HookConsumerWidget {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
buildLibraryNavButton(
|
||||
"library_page_favorites".tr(), Icons.favorite_border, () {
|
||||
buildLibraryNavButton("library_page_favorites".tr(),
|
||||
Icons.favorite_outline_rounded, () {
|
||||
context.autoNavigate(const FavoritesRoute());
|
||||
}),
|
||||
const SizedBox(width: 12.0),
|
||||
|
|
|
@ -29,9 +29,11 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
if (sharedUsersList.value.contains(user)) {
|
||||
return CircleAvatar(
|
||||
backgroundColor: context.primaryColor,
|
||||
child: const Icon(
|
||||
radius: 22,
|
||||
child: Icon(
|
||||
Icons.check_rounded,
|
||||
size: 25,
|
||||
color: context.colorScheme.surface,
|
||||
size: 24,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -49,14 +51,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Chip(
|
||||
backgroundColor: context.primaryColor.withOpacity(0.15),
|
||||
backgroundColor: context.colorScheme.primaryContainer,
|
||||
label: Text(
|
||||
user.email,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: context.textTheme.displaySmall,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -72,9 +70,9 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'select_additional_user_for_sharing_page_suggestions'.tr(),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
color: context.themeData.hintColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -51,9 +51,11 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
if (sharedUsersList.value.contains(user)) {
|
||||
return CircleAvatar(
|
||||
backgroundColor: context.primaryColor,
|
||||
child: const Icon(
|
||||
radius: 22,
|
||||
child: Icon(
|
||||
Icons.check_rounded,
|
||||
size: 25,
|
||||
color: context.colorScheme.surface,
|
||||
size: 24,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -71,14 +73,10 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Chip(
|
||||
backgroundColor: context.primaryColor.withOpacity(0.15),
|
||||
backgroundColor: context.colorScheme.primaryContainer,
|
||||
label: Text(
|
||||
user.email,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: context.textTheme.displaySmall,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -92,11 +90,11 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: const Text(
|
||||
child: Text(
|
||||
'select_user_for_sharing_page_share_suggestions',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
color: context.themeData.hintColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
).tr(),
|
||||
|
|
|
@ -81,8 +81,7 @@ class SharingPage extends HookConsumerWidget {
|
|||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color:
|
||||
context.isDarkTheme ? context.primaryColor : Colors.black,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
subtitle: isOwner
|
||||
|
@ -172,8 +171,8 @@ class SharingPage extends HookConsumerWidget {
|
|||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
side: const BorderSide(
|
||||
color: Colors.grey,
|
||||
side: BorderSide(
|
||||
color: context.themeData.hintColor,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/ui/drag_sheet.dart';
|
||||
|
||||
class AdvancedBottomSheet extends HookConsumerWidget {
|
||||
final Asset assetDetail;
|
||||
|
@ -24,57 +25,50 @@ class AdvancedBottomSheet extends HookConsumerWidget {
|
|||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
builder: (ctx, constraints) {
|
||||
// One column
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(height: 32.0),
|
||||
const Align(
|
||||
child: Text(
|
||||
"ADVANCED INFO",
|
||||
style: TextStyle(fontSize: 12.0),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 15, bottom: 10),
|
||||
child: CustomDraggingHandle(),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TextButton.icon(
|
||||
label: Text(
|
||||
"ADVANCED INFO",
|
||||
style: context.textTheme.displaySmall,
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.copy,
|
||||
size: 16.0,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: assetDetail.toString()),
|
||||
).then((_) {
|
||||
ScaffoldMessenger.of(ctx).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text("Copied to clipboard"),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32.0),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.isDarkTheme
|
||||
? Colors.grey[900]
|
||||
: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 16.0,
|
||||
left: 16,
|
||||
top: 8,
|
||||
bottom: 16,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: assetDetail.toString()),
|
||||
).then((_) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text("Copied to clipboard"),
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.copy,
|
||||
size: 16.0,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
SelectableText(
|
||||
assetDetail.toString(),
|
||||
style: const TextStyle(
|
||||
|
|
|
@ -20,7 +20,6 @@ class DescriptionInput extends HookConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final textColor = context.isDarkTheme ? Colors.white : Colors.black;
|
||||
final controller = useTextEditingController();
|
||||
final focusNode = useFocusNode();
|
||||
final isFocus = useState(false);
|
||||
|
@ -68,7 +67,7 @@ class DescriptionInput extends HookConsumerWidget {
|
|||
},
|
||||
icon: Icon(
|
||||
Icons.cancel_rounded,
|
||||
color: Colors.grey[500],
|
||||
color: context.themeData.hintColor,
|
||||
),
|
||||
splashRadius: 10,
|
||||
);
|
||||
|
@ -102,7 +101,7 @@ class DescriptionInput extends HookConsumerWidget {
|
|||
hintStyle: TextStyle(
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 12,
|
||||
color: textColor.withOpacity(0.5),
|
||||
color: context.colorScheme.onSurface.withOpacity(0.5),
|
||||
),
|
||||
suffixIcon: suffixIcon,
|
||||
),
|
||||
|
|
|
@ -112,7 +112,7 @@ class ExifBottomSheet extends HookConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final assetWithExif = ref.watch(assetDetailProvider(asset));
|
||||
final exifInfo = (assetWithExif.value ?? asset).exifInfo;
|
||||
var textColor = context.isDarkTheme ? Colors.white : Colors.black;
|
||||
var textColor = context.colorScheme.onSurface;
|
||||
|
||||
buildMap() {
|
||||
return Padding(
|
||||
|
|
|
@ -61,7 +61,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
|
|||
Widget buildAssetInfoTable() {
|
||||
return Table(
|
||||
border: TableBorder.all(
|
||||
color: context.themeData.primaryColorLight,
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
width: 1,
|
||||
),
|
||||
children: [
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/color_extensions.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/add_to_album_sliverlist.dart';
|
||||
import 'package:immich_mobile/modules/home/models/selection_state.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
|
||||
|
@ -128,7 +129,8 @@ class ControlBottomAppBar extends ConsumerWidget {
|
|||
ScrollController scrollController,
|
||||
) {
|
||||
return Card(
|
||||
elevation: 18.0,
|
||||
elevation: 2,
|
||||
color: context.colorScheme.surface.lighten(15),
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(12),
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/widgetref_extensions.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
|
@ -49,7 +50,7 @@ class HomePage extends HookConsumerWidget {
|
|||
|
||||
final tipOneOpacity = useState(0.0);
|
||||
final refreshCount = useState(0);
|
||||
final processing = useState(false);
|
||||
final processing = ref.useProcessingOverlay();
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
|
@ -363,7 +364,6 @@ class HomePage extends HookConsumerWidget {
|
|||
selectionAssetState: selectionAssetState.value,
|
||||
onStack: onStack,
|
||||
),
|
||||
if (processing.value) const Center(child: ImmichLoadingIndicator()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -125,6 +125,8 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
final refetchMarkers = useState(true);
|
||||
final isLoading =
|
||||
ref.watch(mapStateNotifier.select((state) => state.isLoading));
|
||||
final mapStyle =
|
||||
ref.watch(mapStateNotifier.select((state) => state.mapStyle));
|
||||
final maxZoom = ref.read(mapStateNotifier.notifier).maxZoom;
|
||||
final zoomLevel = math.min(maxZoom, 14.0);
|
||||
final themeData = isDarkTheme ? immichDarkTheme : immichLightTheme;
|
||||
|
@ -440,7 +442,7 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
extendBodyBehindAppBar: true,
|
||||
body: Stack(
|
||||
children: [
|
||||
if (!isLoading)
|
||||
if (!isLoading || mapStyle != null)
|
||||
FlutterMap(
|
||||
mapController: mapController,
|
||||
options: MapOptions(
|
||||
|
@ -465,7 +467,7 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
markerLayer,
|
||||
],
|
||||
),
|
||||
if (!isLoading)
|
||||
if (!isLoading || mapStyle != null)
|
||||
MapPageBottomSheet(
|
||||
mapPageEventStream: mapPageEventSC.stream,
|
||||
bottomSheetEventSC: bottomSheetEventSC,
|
||||
|
@ -474,10 +476,13 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
isDarkTheme: isDarkTheme,
|
||||
),
|
||||
if (showLoadingIndicator.value || isLoading)
|
||||
Positioned(
|
||||
top: context.height * 0.35,
|
||||
left: context.width * 0.425,
|
||||
child: const ImmichLoadingIndicator(),
|
||||
IgnorePointer(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
color: context.colorScheme.surface.withAlpha(70),
|
||||
child: const Center(child: ImmichLoadingIndicator()),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -35,17 +35,21 @@ class SettingsSwitchListTile extends StatelessWidget {
|
|||
onChanged!(value);
|
||||
}
|
||||
},
|
||||
activeColor:
|
||||
enabled ? context.primaryColor : context.themeData.disabledColor,
|
||||
activeColor: enabled ? null : context.themeData.disabledColor,
|
||||
dense: true,
|
||||
title: Text(
|
||||
title,
|
||||
style:
|
||||
context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: enabled ? null : context.themeData.disabledColor,
|
||||
),
|
||||
),
|
||||
subtitle: subtitle != null
|
||||
? Text(
|
||||
subtitle!,
|
||||
style: TextStyle(
|
||||
color: enabled ? null : context.themeData.disabledColor,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/widgetref_extensions.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/asset_grid/immich_asset_grid.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
|
||||
import 'package:immich_mobile/modules/trash/providers/trashed_asset.provider.dart';
|
||||
|
@ -11,7 +12,6 @@ import 'package:immich_mobile/shared/models/asset.dart';
|
|||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/confirm_dialog.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
|
||||
class TrashPage extends HookConsumerWidget {
|
||||
|
@ -24,7 +24,7 @@ class TrashPage extends HookConsumerWidget {
|
|||
ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays));
|
||||
final selectionEnabledHook = useState(false);
|
||||
final selection = useState(<Asset>{});
|
||||
final processing = useState(false);
|
||||
final processing = ref.useProcessingOverlay();
|
||||
|
||||
void selectionListener(
|
||||
bool multiselect,
|
||||
|
@ -267,8 +267,6 @@ class TrashPage extends HookConsumerWidget {
|
|||
),
|
||||
),
|
||||
if (selectionEnabledHook.value) buildBottomBar(),
|
||||
if (processing.value)
|
||||
const Center(child: ImmichLoadingIndicator()),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -21,14 +21,6 @@ class TabNavigationObserver extends AutoRouterObserver {
|
|||
required this.ref,
|
||||
});
|
||||
|
||||
@override
|
||||
void didInitTabRoute(TabPageRoute route, TabPageRoute? previousRoute) {
|
||||
// Perform tasks on first navigation to SearchRoute
|
||||
if (route.name == 'SearchRoute') {
|
||||
// ref.refresh(getCuratedLocationProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> didChangeTabRoute(
|
||||
TabPageRoute route,
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
class ImmichLoadingOverlayNotifier extends StateNotifier<bool> {
|
||||
ImmichLoadingOverlayNotifier() : super(false);
|
||||
|
||||
void show() => state = true;
|
||||
|
||||
void hide() => state = false;
|
||||
}
|
||||
|
||||
final immichLoadingOverlayController =
|
||||
StateNotifierProvider.autoDispose<ImmichLoadingOverlayNotifier, bool>(
|
||||
(_) => ImmichLoadingOverlayNotifier(),
|
||||
);
|
|
@ -44,7 +44,7 @@ class ConfirmDialog extends ConsumerWidget {
|
|||
child: Text(
|
||||
ok,
|
||||
style: TextStyle(
|
||||
color: Colors.red[400],
|
||||
color: context.colorScheme.error,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
).tr(),
|
||||
|
|
|
@ -4,8 +4,6 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|||
import 'package:immich_mobile/shared/models/store.dart';
|
||||
import 'package:immich_mobile/shared/ui/app_bar_dialog/app_bar_dialog.dart';
|
||||
import 'package:immich_mobile/shared/ui/user_circle_avatar.dart';
|
||||
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
|
||||
|
@ -26,7 +24,6 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
final bool isEnableAutoBackup =
|
||||
backupState.backgroundBackup || backupState.autoBackup;
|
||||
final ServerInfo serverInfoState = ref.watch(serverInfoProvider);
|
||||
AuthenticationState authState = ref.watch(authenticationProvider);
|
||||
final user = Store.tryGet(StoreKey.currentUser);
|
||||
const widgetSize = 30.0;
|
||||
|
||||
|
@ -44,9 +41,9 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
color: Colors.black,
|
||||
borderRadius: BorderRadius.circular(widgetSize / 2),
|
||||
),
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
Icons.info,
|
||||
color: Color.fromARGB(255, 243, 188, 106),
|
||||
color: context.orangeColor,
|
||||
size: widgetSize / 2,
|
||||
),
|
||||
),
|
||||
|
@ -54,7 +51,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
alignment: Alignment.bottomRight,
|
||||
isLabelVisible: serverInfoState.isVersionMismatch,
|
||||
offset: const Offset(2, 2),
|
||||
child: authState.profileImagePath.isEmpty || user == null
|
||||
child: user == null
|
||||
? const Icon(
|
||||
Icons.face_outlined,
|
||||
size: widgetSize,
|
||||
|
|
|
@ -15,13 +15,13 @@ class ImmichLoadingIndicator extends StatelessWidget {
|
|||
height: 60,
|
||||
width: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: context.primaryColor.withAlpha(200),
|
||||
color: context.primaryColor,
|
||||
borderRadius: BorderRadius.circular(borderRadius ?? 10),
|
||||
),
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: const CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
strokeWidth: 2,
|
||||
child: CircularProgressIndicator(
|
||||
color: context.colorScheme.onPrimary,
|
||||
strokeWidth: 3,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/color_extensions.dart';
|
||||
|
||||
enum ToastType { info, success, error }
|
||||
|
||||
|
@ -9,7 +10,7 @@ class ImmichToast {
|
|||
required BuildContext context,
|
||||
required String msg,
|
||||
ToastType toastType = ToastType.info,
|
||||
ToastGravity gravity = ToastGravity.TOP,
|
||||
ToastGravity gravity = ToastGravity.BOTTOM,
|
||||
int durationInSecond = 3,
|
||||
}) {
|
||||
final fToast = FToast();
|
||||
|
@ -18,7 +19,7 @@ class ImmichToast {
|
|||
Color getColor(ToastType type, BuildContext context) {
|
||||
switch (type) {
|
||||
case ToastType.info:
|
||||
return context.primaryColor;
|
||||
return const Color.fromARGB(255, 48, 111, 220);
|
||||
case ToastType.success:
|
||||
return const Color.fromARGB(255, 78, 140, 124);
|
||||
case ToastType.error:
|
||||
|
@ -26,41 +27,39 @@ class ImmichToast {
|
|||
}
|
||||
}
|
||||
|
||||
Icon getIcon(ToastType type) {
|
||||
IconData getIcon(ToastType type) {
|
||||
switch (type) {
|
||||
case ToastType.info:
|
||||
return Icon(
|
||||
Icons.info_outline_rounded,
|
||||
color: context.primaryColor,
|
||||
);
|
||||
case ToastType.success:
|
||||
return const Icon(
|
||||
Icons.check_circle_rounded,
|
||||
color: Color.fromARGB(255, 78, 140, 124),
|
||||
);
|
||||
return Icons.info_outline_rounded;
|
||||
case ToastType.error:
|
||||
return const Icon(
|
||||
Icons.error_outline_rounded,
|
||||
color: Color.fromARGB(255, 240, 162, 156),
|
||||
);
|
||||
return Icons.check_circle_outline_rounded;
|
||||
case ToastType.success:
|
||||
return Icons.report_problem_outlined;
|
||||
}
|
||||
}
|
||||
|
||||
fToast.showToast(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[50],
|
||||
color: context.colorScheme.inverseSurface,
|
||||
border: Border.all(
|
||||
color: Colors.black12,
|
||||
color: context.colorScheme.inverseSurface,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
getIcon(toastType),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 15, right: 5),
|
||||
child: Icon(
|
||||
getIcon(toastType),
|
||||
color: getColor(toastType, context),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12.0,
|
||||
),
|
||||
|
@ -68,8 +67,7 @@ class ImmichToast {
|
|||
child: Text(
|
||||
msg,
|
||||
style: TextStyle(
|
||||
color: getColor(toastType, context),
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.colorScheme.onInverseSurface,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -46,7 +46,7 @@ class UserCircleAvatar extends ConsumerWidget {
|
|||
user.firstName[0].toUpperCase(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.isDarkTheme ? Colors.black : Colors.white,
|
||||
color: context.colorScheme.onInverseSurface,
|
||||
),
|
||||
);
|
||||
return CircleAvatar(
|
||||
|
|
|
@ -11,8 +11,6 @@ class AppLogDetailPage extends HookConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
var isDarkTheme = context.isDarkTheme;
|
||||
|
||||
buildStackMessage(String stackTrace) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
|
@ -53,7 +51,7 @@ class AppLogDetailPage extends HookConsumerWidget {
|
|||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: Padding(
|
||||
|
@ -112,7 +110,7 @@ class AppLogDetailPage extends HookConsumerWidget {
|
|||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: Padding(
|
||||
|
@ -151,7 +149,7 @@ class AppLogDetailPage extends HookConsumerWidget {
|
|||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: Padding(
|
||||
|
|
|
@ -16,7 +16,6 @@ class AppLogPage extends HookConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final immichLogger = ImmichLogger();
|
||||
final logMessages = useState(immichLogger.messages);
|
||||
final isDarkTheme = context.isDarkTheme;
|
||||
|
||||
Widget colorStatusIndicator(Color color) {
|
||||
return Column(
|
||||
|
@ -39,12 +38,13 @@ class AppLogPage extends HookConsumerWidget {
|
|||
case LogLevel.INFO:
|
||||
return colorStatusIndicator(context.primaryColor);
|
||||
case LogLevel.SEVERE:
|
||||
return colorStatusIndicator(Colors.redAccent);
|
||||
|
||||
return colorStatusIndicator(context.redColor);
|
||||
case LogLevel.WARNING:
|
||||
return colorStatusIndicator(Colors.orangeAccent);
|
||||
return colorStatusIndicator(context.orangeColor);
|
||||
default:
|
||||
return colorStatusIndicator(Colors.grey);
|
||||
return colorStatusIndicator(
|
||||
context.colorScheme.onSurfaceVariant.withAlpha(150),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,15 +53,11 @@ class AppLogPage extends HookConsumerWidget {
|
|||
case LogLevel.INFO:
|
||||
return Colors.transparent;
|
||||
case LogLevel.SEVERE:
|
||||
return isDarkTheme
|
||||
? Colors.redAccent.withOpacity(0.25)
|
||||
: Colors.redAccent.withOpacity(0.075);
|
||||
return context.redColor.withAlpha(50);
|
||||
case LogLevel.WARNING:
|
||||
return isDarkTheme
|
||||
? Colors.orangeAccent.withOpacity(0.25)
|
||||
: Colors.orangeAccent.withOpacity(0.075);
|
||||
return context.orangeColor.withAlpha(50);
|
||||
default:
|
||||
return context.primaryColor.withOpacity(0.1);
|
||||
return context.primaryColor.withAlpha(50);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,7 +112,7 @@ class AppLogPage extends HookConsumerWidget {
|
|||
separatorBuilder: (context, index) {
|
||||
return Divider(
|
||||
height: 0,
|
||||
color: isDarkTheme ? Colors.white70 : Colors.grey[600],
|
||||
color: context.themeData.dividerColor,
|
||||
);
|
||||
},
|
||||
itemCount: logMessages.value.length,
|
||||
|
@ -139,7 +135,7 @@ class AppLogPage extends HookConsumerWidget {
|
|||
TextSpan(
|
||||
text: "#$index ",
|
||||
style: TextStyle(
|
||||
color: isDarkTheme ? Colors.white70 : Colors.grey[600],
|
||||
color: context.themeData.dividerColor,
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
|
@ -158,7 +154,7 @@ class AppLogPage extends HookConsumerWidget {
|
|||
"[${logMessage.context1}] Logged on ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}",
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: Colors.grey[600],
|
||||
color: context.themeData.hintColor,
|
||||
),
|
||||
),
|
||||
leading: buildLeadingIcon(logMessage.level),
|
||||
|
|
|
@ -1,41 +1,73 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/shared/providers/immich_loading_overlay.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
||||
|
||||
class ImmichLoadingOverlay extends StatelessWidget {
|
||||
const ImmichLoadingOverlay({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
class ImmichLoadingOverlay extends StatefulHookConsumerWidget {
|
||||
final ThemeData theme;
|
||||
final ThemeData darkTheme;
|
||||
|
||||
const ImmichLoadingOverlay(
|
||||
{super.key, required this.theme, required this.darkTheme});
|
||||
|
||||
@override
|
||||
ImmichLoadingOverlayState createState() => ImmichLoadingOverlayState();
|
||||
}
|
||||
|
||||
class ImmichLoadingOverlayState extends ConsumerState<ImmichLoadingOverlay>
|
||||
with WidgetsBindingObserver {
|
||||
Brightness currentPlatformBrightness =
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangePlatformBrightness() {
|
||||
super.didChangePlatformBrightness();
|
||||
|
||||
setState(() {
|
||||
currentPlatformBrightness =
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable:
|
||||
ImmichLoadingOverlayController.appLoader.loaderShowingNotifier,
|
||||
builder: (context, shouldShow, child) {
|
||||
return shouldShow
|
||||
? const Scaffold(
|
||||
backgroundColor: Colors.black54,
|
||||
body: Center(
|
||||
child: ImmichLoadingIndicator(),
|
||||
),
|
||||
)
|
||||
: const SizedBox();
|
||||
},
|
||||
final shouldShow = ref.watch(immichLoadingOverlayController);
|
||||
|
||||
final themeMode = ref.watch(immichThemeProvider);
|
||||
final currentBrightness = themeMode == ThemeMode.system
|
||||
? currentPlatformBrightness
|
||||
: themeMode == ThemeMode.dark
|
||||
? Brightness.dark
|
||||
: Brightness.light;
|
||||
|
||||
return IgnorePointer(
|
||||
child: shouldShow
|
||||
? Theme(
|
||||
data: currentBrightness == Brightness.dark
|
||||
? widget.darkTheme
|
||||
: widget.theme,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
color: context.colorScheme.surface.withAlpha(70),
|
||||
child: const Center(child: ImmichLoadingIndicator()),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ImmichLoadingOverlayController {
|
||||
static final ImmichLoadingOverlayController appLoader =
|
||||
ImmichLoadingOverlayController();
|
||||
ValueNotifier<bool> loaderShowingNotifier = ValueNotifier(false);
|
||||
ValueNotifier<String> loaderTextNotifier = ValueNotifier('error message');
|
||||
|
||||
void show() {
|
||||
loaderShowingNotifier.value = true;
|
||||
}
|
||||
|
||||
void hide() {
|
||||
loaderShowingNotifier.value = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -173,3 +173,6 @@ ThemeData getThemeForScheme(ColorScheme scheme) {
|
|||
|
||||
ThemeData immichLightTheme = getThemeForScheme(_lightColorScheme);
|
||||
ThemeData immichDarkTheme = getThemeForScheme(_darkColorScheme);
|
||||
|
||||
const redAccent = Colors.redAccent;
|
||||
const orangeAccent = Colors.orangeAccent;
|
||||
|
|
|
@ -321,6 +321,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
dynamic_color:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dynamic_color
|
||||
sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.8"
|
||||
easy_image_viewer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -60,6 +60,7 @@ dependencies:
|
|||
wakelock_plus: ^1.1.1
|
||||
flutter_local_notifications: ^15.1.0+1
|
||||
timezone: ^0.9.2
|
||||
dynamic_color: ^1.6.8
|
||||
|
||||
openapi:
|
||||
path: openapi
|
||||
|
|
Loading…
Reference in a new issue