wip(mobile): unify-colors
This commit is contained in:
parent
f6180fccdc
commit
d20801c65c
36 changed files with 420 additions and 516 deletions
|
@ -1,5 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
Color immichBackgroundColor = const Color(0xFFf6f8fe);
|
||||
Color immichDarkBackgroundColor = const Color.fromARGB(255, 0, 0, 0);
|
||||
Color immichDarkThemePrimaryColor = const Color.fromARGB(255, 173, 203, 250);
|
|
@ -28,25 +28,33 @@ class AlbumThumbnailCard extends StatelessWidget {
|
|||
var cardSize = constraints.maxWidth;
|
||||
|
||||
buildEmptyThumbnail() {
|
||||
return Container(
|
||||
return SizedBox(
|
||||
height: cardSize,
|
||||
width: cardSize,
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkMode ? Colors.grey[800] : Colors.grey[200],
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.no_photography,
|
||||
size: cardSize * .15,
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.no_photography,
|
||||
size: cardSize * .15,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
buildAlbumThumbnail() => ImmichImage(
|
||||
album.thumbnail.value,
|
||||
width: cardSize,
|
||||
height: cardSize,
|
||||
buildAlbumThumbnail() => Card(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: ImmichImage(
|
||||
album.thumbnail.value,
|
||||
width: cardSize,
|
||||
height: cardSize,
|
||||
),
|
||||
);
|
||||
|
||||
buildAlbumTextRow() {
|
||||
|
|
|
@ -21,39 +21,48 @@ class AlbumThumbnailListTile extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var cardSize = 68.0;
|
||||
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
buildEmptyThumbnail() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkMode ? Colors.grey[800] : Colors.grey[200],
|
||||
),
|
||||
child: SizedBox(
|
||||
height: cardSize,
|
||||
width: cardSize,
|
||||
child: const Center(
|
||||
child: Icon(Icons.no_photography),
|
||||
return SizedBox(
|
||||
height: cardSize,
|
||||
width: cardSize,
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.no_photography,
|
||||
size: cardSize * .15,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
buildAlbumThumbnail() {
|
||||
return CachedNetworkImage(
|
||||
width: cardSize,
|
||||
height: cardSize,
|
||||
fit: BoxFit.cover,
|
||||
fadeInDuration: const Duration(milliseconds: 200),
|
||||
imageUrl: getAlbumThumbnailUrl(
|
||||
album,
|
||||
type: ThumbnailFormat.JPEG,
|
||||
return Card(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: CachedNetworkImage(
|
||||
width: cardSize,
|
||||
height: cardSize,
|
||||
fit: BoxFit.cover,
|
||||
fadeInDuration: const Duration(milliseconds: 200),
|
||||
imageUrl: getAlbumThumbnailUrl(
|
||||
album,
|
||||
type: ThumbnailFormat.JPEG,
|
||||
),
|
||||
httpHeaders: {
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
},
|
||||
cacheKey:
|
||||
getAlbumThumbNailCacheKey(album, type: ThumbnailFormat.JPEG),
|
||||
errorWidget: (context, url, error) =>
|
||||
const Icon(Icons.image_not_supported_outlined),
|
||||
),
|
||||
httpHeaders: {
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
},
|
||||
cacheKey: getAlbumThumbNailCacheKey(album, type: ThumbnailFormat.JPEG),
|
||||
errorWidget: (context, url, error) =>
|
||||
const Icon(Icons.image_not_supported_outlined),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
titleFocusNode: titleFocusNode,
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
padding: const EdgeInsets.only(left: 8.0, bottom: 20.0),
|
||||
child: Text(
|
||||
album.name,
|
||||
style: const TextStyle(
|
||||
|
|
|
@ -21,7 +21,6 @@ class LibraryPage extends HookConsumerWidget {
|
|||
final trashEnabled =
|
||||
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
|
||||
final albums = ref.watch(albumProvider);
|
||||
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
var settings = ref.watch(appSettingsServiceProvider);
|
||||
|
||||
useEffect(
|
||||
|
@ -138,51 +137,55 @@ class LibraryPage extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
Widget buildCreateAlbumButton() {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
AutoRouter.of(context).push(CreateAlbumRoute(isSharedAlbum: false));
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
var cardSize = constraints.maxWidth;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
AutoRouter.of(context)
|
||||
.push(CreateAlbumRoute(isSharedAlbum: false));
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 32),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: cardSize,
|
||||
width: cardSize,
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.add_rounded,
|
||||
size: 28,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8.0,
|
||||
bottom: 16,
|
||||
left: 8.0,
|
||||
),
|
||||
child: const Text(
|
||||
'library_page_new_album',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
).tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 32),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: isDarkMode
|
||||
? const Color.fromARGB(255, 53, 53, 53)
|
||||
: const Color.fromARGB(255, 203, 203, 203),
|
||||
),
|
||||
color: isDarkMode ? Colors.grey[900] : Colors.grey[50],
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.add_rounded,
|
||||
size: 28,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8.0,
|
||||
bottom: 16,
|
||||
),
|
||||
child: const Text(
|
||||
'library_page_new_album',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
).tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -192,30 +195,19 @@ class LibraryPage extends HookConsumerWidget {
|
|||
Function() onClick,
|
||||
) {
|
||||
return Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: onClick,
|
||||
label: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 13.0,
|
||||
color: isDarkMode ? Colors.white : Colors.grey[800],
|
||||
),
|
||||
),
|
||||
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
),
|
||||
),
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
backgroundColor: isDarkMode ? Colors.grey[900] : Colors.grey[50],
|
||||
side: BorderSide(
|
||||
color: isDarkMode ? Colors.grey[800]! : Colors.grey[300]!,
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
icon: Icon(
|
||||
icon,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -293,7 +285,6 @@ class LibraryPage extends HookConsumerWidget {
|
|||
sliver: SliverGrid(
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 250,
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 12,
|
||||
childAspectRatio: .7,
|
||||
),
|
||||
|
|
|
@ -134,13 +134,12 @@ class SharingPage extends HookConsumerWidget {
|
|||
Icons.photo_album_outlined,
|
||||
size: 20,
|
||||
),
|
||||
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
label: const Text(
|
||||
"sharing_silver_appbar_create_shared_album",
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 11,
|
||||
),
|
||||
).tr(),
|
||||
),
|
||||
),
|
||||
|
@ -153,12 +152,11 @@ class SharingPage extends HookConsumerWidget {
|
|||
Icons.link,
|
||||
size: 20,
|
||||
),
|
||||
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
label: const Text(
|
||||
"sharing_silver_appbar_shared_links",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 11,
|
||||
),
|
||||
maxLines: 1,
|
||||
).tr(),
|
||||
),
|
||||
|
@ -198,7 +196,10 @@ class SharingPage extends HookConsumerWidget {
|
|||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'sharing_page_empty_list',
|
||||
style: Theme.of(context).textTheme.displaySmall,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.displaySmall
|
||||
?.copyWith(color: Theme.of(context).primaryColor),
|
||||
).tr(),
|
||||
),
|
||||
Padding(
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
||||
import 'package:immich_mobile/modules/backup/ui/album_info_card.dart';
|
||||
import 'package:immich_mobile/modules/backup/ui/album_info_list_tile.dart';
|
||||
|
@ -148,13 +147,12 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
album.name,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: isDarkTheme ? Colors.black : immichBackgroundColor,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.red[300],
|
||||
deleteIconColor:
|
||||
isDarkTheme ? Colors.black : immichBackgroundColor,
|
||||
deleteIconColor: Theme.of(context).colorScheme.surface,
|
||||
deleteIcon: const Icon(
|
||||
Icons.cancel_rounded,
|
||||
size: 15,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
class DisableMultiSelectButton extends ConsumerWidget {
|
||||
class DisableMultiSelectButton extends StatelessWidget {
|
||||
const DisableMultiSelectButton({
|
||||
Key? key,
|
||||
required this.onPressed,
|
||||
|
@ -12,25 +11,29 @@ class DisableMultiSelectButton extends ConsumerWidget {
|
|||
final int selectedItemCount;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 16.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
onPressed();
|
||||
},
|
||||
icon: const Icon(Icons.close_rounded),
|
||||
label: Text(
|
||||
'$selectedItemCount',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 16.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.surface,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
onPressed: () {
|
||||
onPressed();
|
||||
},
|
||||
icon: const Icon(Icons.close_rounded),
|
||||
label: Text(
|
||||
'$selectedItemCount',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
|||
/// Build the Scroll Thumb and label using the current configuration
|
||||
typedef ScrollThumbBuilder = Widget Function(
|
||||
Color backgroundColor,
|
||||
Color foregroundColor,
|
||||
Animation<double> thumbAnimation,
|
||||
Animation<double> labelAnimation,
|
||||
double height, {
|
||||
|
@ -33,6 +34,9 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
/// The background color of the label and thumb
|
||||
final Color backgroundColor;
|
||||
|
||||
/// The foreground color of the arrows in the thumb
|
||||
final Color foregroundColor;
|
||||
|
||||
/// The amount of padding that should surround the thumb
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
|
@ -66,6 +70,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
required this.scrollStateListener,
|
||||
this.heightScrollThumb = 48.0,
|
||||
this.backgroundColor = Colors.white,
|
||||
this.foregroundColor = Colors.white,
|
||||
this.padding,
|
||||
this.scrollbarAnimationDuration = const Duration(milliseconds: 300),
|
||||
this.scrollbarTimeToFade = const Duration(milliseconds: 600),
|
||||
|
@ -85,6 +90,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
static buildScrollThumbAndLabel({
|
||||
required Widget scrollThumb,
|
||||
required Color backgroundColor,
|
||||
required Color foregroundColor,
|
||||
required Animation<double>? thumbAnimation,
|
||||
required Animation<double>? labelAnimation,
|
||||
required Text? labelText,
|
||||
|
@ -123,6 +129,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
) {
|
||||
return (
|
||||
Color backgroundColor,
|
||||
Color foregroundColor,
|
||||
Animation<double> thumbAnimation,
|
||||
Animation<double> labelAnimation,
|
||||
double height, {
|
||||
|
@ -131,7 +138,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
}) {
|
||||
final scrollThumb = CustomPaint(
|
||||
key: scrollThumbKey,
|
||||
foregroundPainter: ArrowCustomPainter(Colors.white),
|
||||
foregroundPainter: ArrowCustomPainter(foregroundColor),
|
||||
child: Material(
|
||||
elevation: 4.0,
|
||||
color: backgroundColor,
|
||||
|
@ -150,6 +157,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
return buildScrollThumbAndLabel(
|
||||
scrollThumb: scrollThumb,
|
||||
backgroundColor: backgroundColor,
|
||||
foregroundColor: foregroundColor,
|
||||
thumbAnimation: thumbAnimation,
|
||||
labelAnimation: labelAnimation,
|
||||
labelText: labelText,
|
||||
|
@ -286,6 +294,7 @@ class DraggableScrollbarState extends State<DraggableScrollbar>
|
|||
padding: widget.padding,
|
||||
child: widget.scrollThumbBuilder(
|
||||
widget.backgroundColor,
|
||||
widget.foregroundColor,
|
||||
_thumbAnimation,
|
||||
_labelAnimation,
|
||||
widget.heightScrollThumb,
|
||||
|
|
|
@ -53,9 +53,12 @@ class GroupDividerTitle extends ConsumerWidget {
|
|||
Icons.check_circle_rounded,
|
||||
color: Theme.of(context).primaryColor,
|
||||
)
|
||||
: const Icon(
|
||||
: Icon(
|
||||
Icons.check_circle_outline_rounded,
|
||||
color: Colors.grey,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onBackground
|
||||
.withAlpha(100),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -221,11 +221,7 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
|||
padding: const EdgeInsets.only(left: 12.0, top: 24.0),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).textTheme.displayLarge?.color,
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -243,7 +239,7 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
|||
bottom: widget.margin,
|
||||
right: i + 1 == num ? 0.0 : widget.margin,
|
||||
),
|
||||
color: Colors.grey,
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -328,8 +324,8 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
|||
|
||||
return Text(
|
||||
DateFormat.yMMMM().format(date),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
);
|
||||
|
@ -372,7 +368,8 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
|||
scrollStateListener: dragScrolling,
|
||||
itemPositionsListener: _itemPositionsListener,
|
||||
controller: _itemScrollController,
|
||||
backgroundColor: Theme.of(context).hintColor,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||
labelTextBuilder: _labelBuilder,
|
||||
labelConstraints: const BoxConstraints(maxHeight: 28),
|
||||
scrollbarAnimationDuration: const Duration(milliseconds: 300),
|
||||
|
|
|
@ -43,9 +43,6 @@ class ThumbnailImage extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
|
||||
final assetContainerColor =
|
||||
isDarkTheme ? Colors.blueGrey : Theme.of(context).primaryColorLight;
|
||||
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
|
||||
final isFromDto = asset.id == Isar.autoIncrement;
|
||||
|
||||
|
@ -54,11 +51,13 @@ class ThumbnailImage extends StatelessWidget {
|
|||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: assetContainerColor,
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.check_circle_rounded,
|
||||
color: Theme.of(context).primaryColor,
|
||||
color: onDeselect == null
|
||||
? Theme.of(context).colorScheme.primary.withAlpha(120)
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -132,9 +131,10 @@ class ThumbnailImage extends StatelessWidget {
|
|||
}
|
||||
|
||||
Widget buildImage() {
|
||||
final image = SizedBox(
|
||||
final image = Container(
|
||||
width: 300,
|
||||
height: 300,
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
child: Hero(
|
||||
tag: isFromDto
|
||||
? '${asset.remoteId}-$heroOffset'
|
||||
|
@ -153,9 +153,9 @@ class ThumbnailImage extends StatelessWidget {
|
|||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 0,
|
||||
color: onDeselect == null ? Colors.grey : assetContainerColor,
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
color: onDeselect == null ? Colors.grey : assetContainerColor,
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
|
@ -197,14 +197,14 @@ class ThumbnailImage extends StatelessWidget {
|
|||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.decelerate,
|
||||
decoration: BoxDecoration(
|
||||
border: multiselectEnabled && isSelected
|
||||
? Border.all(
|
||||
color: onDeselect == null
|
||||
? Colors.grey
|
||||
: assetContainerColor,
|
||||
width: 8,
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
width: 12,
|
||||
)
|
||||
: const Border(),
|
||||
),
|
||||
|
|
|
@ -42,7 +42,6 @@ class ControlBottomAppBar extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
var hasRemote =
|
||||
selectionAssetState.hasRemote || selectionAssetState.hasMerged;
|
||||
var hasLocal = selectionAssetState.hasLocal;
|
||||
|
@ -128,8 +127,6 @@ class ControlBottomAppBar extends ConsumerWidget {
|
|||
ScrollController scrollController,
|
||||
) {
|
||||
return Card(
|
||||
color: isDarkMode ? Colors.grey[900] : Colors.grey[100],
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 18.0,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
|
|
|
@ -127,9 +127,9 @@ class LoginForm extends HookConsumerWidget {
|
|||
);
|
||||
|
||||
populateTestLoginInfo() {
|
||||
usernameController.text = 'testuser@email.com';
|
||||
passwordController.text = 'password';
|
||||
serverEndpointController.text = 'http://10.1.15.216:2283/api';
|
||||
usernameController.text = 'demo@immich.app';
|
||||
passwordController.text = 'demo';
|
||||
serverEndpointController.text = 'https://demo.immich.app';
|
||||
}
|
||||
|
||||
login() async {
|
||||
|
@ -303,7 +303,10 @@ class LoginForm extends HookConsumerWidget {
|
|||
children: [
|
||||
Text(
|
||||
serverEndpointController.text,
|
||||
style: Theme.of(context).textTheme.displaySmall,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.displaySmall
|
||||
?.copyWith(color: Theme.of(context).primaryColor),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (isPasswordLoginEnable.value) ...[
|
||||
|
|
|
@ -120,6 +120,7 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
final selectedAssets = useState(<Asset>{});
|
||||
final showLoadingIndicator = useState(false);
|
||||
final refetchMarkers = useState(true);
|
||||
final themeData = isDarkTheme ? immichDarkTheme : immichLightTheme;
|
||||
|
||||
if (refetchMarkers.value) {
|
||||
mapMarkerData.value = ref.watch(mapMarkersProvider).when(
|
||||
|
@ -189,7 +190,7 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => Theme(
|
||||
data: isDarkTheme ? immichDarkTheme : immichLightTheme,
|
||||
data: themeData,
|
||||
child: LocationServiceDisabledDialog(),
|
||||
),
|
||||
);
|
||||
|
@ -203,7 +204,7 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
shouldRequestPermission = await showDialog(
|
||||
context: context,
|
||||
builder: (context) => Theme(
|
||||
data: isDarkTheme ? immichDarkTheme : immichLightTheme,
|
||||
data: themeData,
|
||||
child: LocationPermissionDisabledDialog(),
|
||||
),
|
||||
);
|
||||
|
@ -438,7 +439,7 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
),
|
||||
child: Theme(
|
||||
// Override app theme based on map theme
|
||||
data: isDarkTheme ? immichDarkTheme : immichLightTheme,
|
||||
data: themeData,
|
||||
child: Scaffold(
|
||||
appBar: MapAppBar(
|
||||
isDarkTheme: isDarkTheme,
|
||||
|
|
|
@ -17,7 +17,7 @@ class MemoryLane extends HookConsumerWidget {
|
|||
.whenData(
|
||||
(memories) => memories != null
|
||||
? Container(
|
||||
margin: const EdgeInsets.only(top: 10),
|
||||
margin: const EdgeInsets.only(top: 10, left: 10),
|
||||
height: 200,
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
|
|
|
@ -118,6 +118,7 @@ class PartnerPage extends HookConsumerWidget {
|
|||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
|
@ -126,12 +127,15 @@ class PartnerPage extends HookConsumerWidget {
|
|||
style: TextStyle(fontSize: 14),
|
||||
).tr(),
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
onPressed: availableUsers.whenOrNull(
|
||||
data: (data) => addNewUsersHandler,
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: availableUsers.whenOrNull(
|
||||
data: (data) => addNewUsersHandler,
|
||||
),
|
||||
icon: const Icon(Icons.person_add),
|
||||
label: const Text("partner_page_add_partner").tr(),
|
||||
),
|
||||
icon: const Icon(Icons.person_add),
|
||||
label: const Text("partner_page_add_partner").tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -30,8 +30,8 @@ class CuratedPeopleRow extends StatelessWidget {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: SizedBox(
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
width: 120,
|
||||
height: 120,
|
||||
child: ThumbnailWithInfo(
|
||||
textInfo: '',
|
||||
onTap: () {},
|
||||
|
|
|
@ -22,8 +22,6 @@ class ThumbnailWithInfo extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
var textAndIconColor = isDarkMode ? Colors.grey[100] : Colors.grey[700];
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onTap();
|
||||
|
@ -34,7 +32,7 @@ class ThumbnailWithInfo extends StatelessWidget {
|
|||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
color: isDarkMode ? Colors.grey[900] : Colors.grey[100],
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
child: imageUrl != null
|
||||
? ClipRRect(
|
||||
|
@ -43,6 +41,17 @@ class ThumbnailWithInfo extends StatelessWidget {
|
|||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (context, url) {
|
||||
return SizedBox.square(
|
||||
dimension: 250,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
imageUrl: imageUrl!,
|
||||
httpHeaders: {
|
||||
"Authorization":
|
||||
|
@ -55,7 +64,7 @@ class ThumbnailWithInfo extends StatelessWidget {
|
|||
: Center(
|
||||
child: Icon(
|
||||
noImageIcon ?? Icons.not_listed_location,
|
||||
color: textAndIconColor,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -67,10 +76,10 @@ class ThumbnailWithInfo extends StatelessWidget {
|
|||
begin: FractionalOffset.topCenter,
|
||||
end: FractionalOffset.bottomCenter,
|
||||
colors: [
|
||||
Colors.grey.withOpacity(0.0),
|
||||
Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.0),
|
||||
textInfo == ''
|
||||
? Colors.black.withOpacity(0.1)
|
||||
: Colors.black.withOpacity(0.5),
|
||||
? Theme.of(context).colorScheme.surface.withOpacity(0.1)
|
||||
: Theme.of(context).colorScheme.surface.withOpacity(0.6),
|
||||
],
|
||||
stops: const [0.0, 1.0],
|
||||
),
|
||||
|
|
|
@ -86,7 +86,6 @@ class AdvancedSettings extends HookConsumerWidget {
|
|||
min: 1.0,
|
||||
divisions: 7,
|
||||
label: logLevel,
|
||||
activeColor: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
|
|
|
@ -50,7 +50,6 @@ class LayoutSettings extends HookConsumerWidget {
|
|||
return Column(
|
||||
children: [
|
||||
SwitchListTile.adaptive(
|
||||
activeColor: Theme.of(context).primaryColor,
|
||||
title: Text(
|
||||
"asset_list_layout_settings_dynamic_layout_title",
|
||||
style: Theme.of(context)
|
||||
|
@ -75,7 +74,6 @@ class LayoutSettings extends HookConsumerWidget {
|
|||
).tr(),
|
||||
),
|
||||
RadioListTile(
|
||||
activeColor: Theme.of(context).primaryColor,
|
||||
title: Text(
|
||||
"asset_list_layout_settings_group_by_month_day",
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
|
@ -86,7 +84,6 @@ class LayoutSettings extends HookConsumerWidget {
|
|||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
),
|
||||
RadioListTile(
|
||||
activeColor: Theme.of(context).primaryColor,
|
||||
title: Text(
|
||||
"asset_list_layout_settings_group_by_month",
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
|
@ -97,7 +94,6 @@ class LayoutSettings extends HookConsumerWidget {
|
|||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
),
|
||||
RadioListTile(
|
||||
activeColor: Theme.of(context).primaryColor,
|
||||
title: Text(
|
||||
"asset_list_layout_settings_group_automatically",
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
|
|
|
@ -33,7 +33,6 @@ class StorageIndicator extends HookConsumerWidget {
|
|||
);
|
||||
|
||||
return SwitchListTile.adaptive(
|
||||
activeColor: Theme.of(context).primaryColor,
|
||||
title: Text(
|
||||
"theme_setting_asset_list_storage_indicator_title",
|
||||
style: Theme.of(context)
|
||||
|
|
|
@ -51,7 +51,6 @@ class TilesPerRow extends HookConsumerWidget {
|
|||
max: 6,
|
||||
divisions: 4,
|
||||
label: "${itemsValue.value.toInt()}",
|
||||
activeColor: Theme.of(context).primaryColor,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -149,7 +149,6 @@ class NotificationSetting extends HookConsumerWidget {
|
|||
max: 5.0,
|
||||
divisions: 5,
|
||||
label: formattedValue,
|
||||
activeColor: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -23,29 +23,31 @@ class SettingsSwitchListTile extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SwitchListTile.adaptive(
|
||||
selectedTileColor: enabled ? null : Theme.of(context).disabledColor,
|
||||
value: valueNotifier.value,
|
||||
onChanged: (bool value) {
|
||||
if (enabled) {
|
||||
valueNotifier.value = value;
|
||||
appSettingService.setSetting(settingsEnum, value);
|
||||
}
|
||||
if (onChanged != null) {
|
||||
onChanged!(value);
|
||||
}
|
||||
},
|
||||
activeColor: enabled
|
||||
? Theme.of(context).primaryColor
|
||||
: Theme.of(context).disabledColor,
|
||||
onChanged: enabled
|
||||
? (bool value) {
|
||||
valueNotifier.value = value;
|
||||
appSettingService.setSetting(settingsEnum, value);
|
||||
if (onChanged != null) {
|
||||
onChanged!(value);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
dense: true,
|
||||
title: Text(
|
||||
title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: enabled
|
||||
? null
|
||||
: Theme.of(context).colorScheme.onSurface.withAlpha(100),
|
||||
),
|
||||
),
|
||||
subtitle: subtitle != null ? Text(subtitle!) : null,
|
||||
subtitle: subtitle != null
|
||||
? Text(
|
||||
subtitle!,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ class ThemeSetting extends HookConsumerWidget {
|
|||
);
|
||||
|
||||
return ExpansionTile(
|
||||
textColor: Theme.of(context).primaryColor,
|
||||
title: const Text(
|
||||
'theme_setting_theme_title',
|
||||
style: TextStyle(
|
||||
|
@ -39,7 +38,6 @@ class ThemeSetting extends HookConsumerWidget {
|
|||
).tr(),
|
||||
children: [
|
||||
SwitchListTile.adaptive(
|
||||
activeColor: Theme.of(context).primaryColor,
|
||||
title: Text(
|
||||
'theme_setting_system_theme_switch',
|
||||
style: Theme.of(context)
|
||||
|
@ -77,7 +75,6 @@ class ThemeSetting extends HookConsumerWidget {
|
|||
),
|
||||
if (currentTheme.value != ThemeMode.system)
|
||||
SwitchListTile.adaptive(
|
||||
activeColor: Theme.of(context).primaryColor,
|
||||
title: Text(
|
||||
'theme_setting_dark_mode_switch',
|
||||
style: Theme.of(context)
|
||||
|
|
|
@ -23,7 +23,6 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
BackUpState backupState = ref.watch(backupProvider);
|
||||
final theme = Theme.of(context);
|
||||
bool isDarkTheme = theme.brightness == Brightness.dark;
|
||||
bool isHorizontal = MediaQuery.of(context).size.width > 600;
|
||||
final horizontalPadding = isHorizontal ? 100.0 : 20.0;
|
||||
final user = ref.watch(currentUserProvider);
|
||||
|
@ -135,11 +134,8 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 3),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkTheme
|
||||
? Theme.of(context).scaffoldBackgroundColor
|
||||
: const Color.fromARGB(255, 225, 229, 240),
|
||||
),
|
||||
decoration:
|
||||
BoxDecoration(color: Theme.of(context).colorScheme.surface),
|
||||
child: ListTile(
|
||||
minLeadingWidth: 50,
|
||||
leading: Icon(
|
||||
|
@ -161,7 +157,8 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
child: LinearProgressIndicator(
|
||||
minHeight: 5.0,
|
||||
value: backupState.serverInfo.diskUsagePercentage / 100.0,
|
||||
backgroundColor: Colors.grey,
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.onSurface.withAlpha(50),
|
||||
color: theme.primaryColor,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -18,7 +18,6 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
|||
AuthenticationState authState = ref.watch(authenticationProvider);
|
||||
final uploadProfileImageStatus =
|
||||
ref.watch(uploadProfileImageProvider).status;
|
||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
final user = Store.tryGet(StoreKey.currentUser);
|
||||
|
||||
buildUserProfileImage() {
|
||||
|
@ -91,9 +90,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
|||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Theme.of(context).scaffoldBackgroundColor
|
||||
: const Color.fromARGB(255, 225, 229, 240),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
topRight: Radius.circular(10),
|
||||
|
@ -111,7 +108,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
|||
bottom: -5,
|
||||
right: -8,
|
||||
child: Material(
|
||||
color: isDarkMode ? Colors.blueGrey[800] : Colors.white,
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
elevation: 3,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
|
|
|
@ -39,9 +39,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
|||
padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Theme.of(context).scaffoldBackgroundColor
|
||||
: const Color.fromARGB(255, 225, 229, 240),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(10),
|
||||
bottomRight: Radius.circular(10),
|
||||
|
@ -139,7 +137,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
|||
child: Text(
|
||||
serverInfoState.serverVersion.major > 0
|
||||
? "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch}"
|
||||
: "?",
|
||||
: "--",
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Theme.of(context)
|
||||
|
@ -190,9 +188,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
|||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
textStyle: TextStyle(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.black
|
||||
: Colors.white,
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
message: getServerUrl() ?? '--',
|
||||
|
|
|
@ -9,7 +9,7 @@ class CustomDraggingHandle extends StatelessWidget {
|
|||
height: 5,
|
||||
width: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[500],
|
||||
color: Theme.of(context).dividerColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -28,7 +28,6 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
final ServerInfo serverInfoState = ref.watch(serverInfoProvider);
|
||||
AuthenticationState authState = ref.watch(authenticationProvider);
|
||||
final user = Store.tryGet(StoreKey.currentUser);
|
||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
const widgetSize = 30.0;
|
||||
|
||||
buildProfileIndicator() {
|
||||
|
@ -69,9 +68,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
);
|
||||
}
|
||||
|
||||
getBackupBadgeIcon() {
|
||||
final iconColor = isDarkMode ? Colors.white : Colors.black;
|
||||
|
||||
Widget? getBackupBadgeIcon() {
|
||||
if (isEnableAutoBackup) {
|
||||
if (backupState.backupProgress == BackUpProgressEnum.inProgress) {
|
||||
return Container(
|
||||
|
@ -79,7 +76,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
strokeCap: StrokeCap.round,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(iconColor),
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
);
|
||||
} else if (backupState.backupProgress !=
|
||||
|
@ -88,7 +85,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
return Icon(
|
||||
Icons.check_outlined,
|
||||
size: 9,
|
||||
color: iconColor,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -97,14 +94,14 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
return Icon(
|
||||
Icons.cloud_off_rounded,
|
||||
size: 9,
|
||||
color: iconColor,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
buildBackupIndicator() {
|
||||
final indicatorIcon = getBackupBadgeIcon();
|
||||
final badgeBackground = isDarkMode ? Colors.blueGrey[800] : Colors.white;
|
||||
|
||||
return InkWell(
|
||||
onTap: () => AutoRouter.of(context).push(const BackupControllerRoute()),
|
||||
|
@ -114,11 +111,11 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
width: widgetSize / 2,
|
||||
height: widgetSize / 2,
|
||||
decoration: BoxDecoration(
|
||||
color: badgeBackground,
|
||||
border: Border.all(
|
||||
color: isDarkMode ? Colors.black : Colors.grey,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(widgetSize / 2),
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
child: indicatorIcon,
|
||||
),
|
||||
|
@ -126,10 +123,9 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
alignment: Alignment.bottomRight,
|
||||
isLabelVisible: indicatorIcon != null,
|
||||
offset: const Offset(2, 2),
|
||||
child: Icon(
|
||||
child: const Icon(
|
||||
Icons.backup_rounded,
|
||||
size: widgetSize,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -158,12 +154,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 10),
|
||||
child: const Text(
|
||||
child: Text(
|
||||
'IMMICH',
|
||||
style: TextStyle(
|
||||
fontFamily: 'SnowburstOne',
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 24,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -32,8 +32,8 @@ class ImmichImage extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
if (this.asset == null) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.grey,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
|
@ -60,10 +60,12 @@ class ImmichImage extends StatelessWidget {
|
|||
return Stack(
|
||||
children: [
|
||||
if (useGrayBoxPlaceholder)
|
||||
const SizedBox.square(
|
||||
SizedBox.square(
|
||||
dimension: 250,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(color: Colors.grey),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (useProgressIndicator)
|
||||
|
@ -109,10 +111,12 @@ class ImmichImage extends StatelessWidget {
|
|||
return Stack(
|
||||
children: [
|
||||
if (useGrayBoxPlaceholder)
|
||||
const SizedBox.square(
|
||||
SizedBox.square(
|
||||
dimension: 250,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(color: Colors.grey),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (useProgressIndicator)
|
||||
|
|
|
@ -13,7 +13,7 @@ class PhotoViewDefaultError extends StatelessWidget {
|
|||
child: Center(
|
||||
child: Icon(
|
||||
Icons.broken_image,
|
||||
color: Colors.grey[400],
|
||||
color: Theme.of(context).primaryColor,
|
||||
size: 40.0,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -79,8 +79,9 @@ class SplashScreenPage extends HookConsumerWidget {
|
|||
[],
|
||||
);
|
||||
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: const Center(
|
||||
child: Image(
|
||||
image: AssetImage('assets/immich-logo-no-outline.png'),
|
||||
width: 80,
|
||||
|
|
|
@ -24,7 +24,7 @@ class TabControllerPage extends HookConsumerWidget {
|
|||
children: [
|
||||
icon,
|
||||
Positioned(
|
||||
right: -14,
|
||||
right: -16,
|
||||
child: SizedBox(
|
||||
height: 12,
|
||||
width: 12,
|
||||
|
@ -114,9 +114,8 @@ class TabControllerPage extends HookConsumerWidget {
|
|||
Icons.photo_library_outlined,
|
||||
),
|
||||
selectedIcon: buildIcon(
|
||||
Icon(
|
||||
const Icon(
|
||||
Icons.photo_library,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -125,9 +124,8 @@ class TabControllerPage extends HookConsumerWidget {
|
|||
icon: const Icon(
|
||||
Icons.search_rounded,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
selectedIcon: const Icon(
|
||||
Icons.search,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
NavigationDestination(
|
||||
|
@ -135,9 +133,8 @@ class TabControllerPage extends HookConsumerWidget {
|
|||
icon: const Icon(
|
||||
Icons.group_outlined,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
selectedIcon: const Icon(
|
||||
Icons.group,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
NavigationDestination(
|
||||
|
@ -146,9 +143,8 @@ class TabControllerPage extends HookConsumerWidget {
|
|||
Icons.photo_album_outlined,
|
||||
),
|
||||
selectedIcon: buildIcon(
|
||||
Icon(
|
||||
const Icon(
|
||||
Icons.photo_album_rounded,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||
|
||||
|
@ -20,259 +19,157 @@ final immichThemeProvider = StateProvider<ThemeMode>((ref) {
|
|||
}
|
||||
});
|
||||
|
||||
ThemeData base = ThemeData(
|
||||
chipTheme: const ChipThemeData(
|
||||
side: BorderSide.none,
|
||||
),
|
||||
sliderTheme: const SliderThemeData(
|
||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7),
|
||||
trackHeight: 2.0,
|
||||
),
|
||||
);
|
||||
|
||||
ThemeData immichLightTheme = ThemeData(
|
||||
useMaterial3: true,
|
||||
ColorScheme _lightColorScheme = const ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primarySwatch: Colors.indigo,
|
||||
primaryColor: Colors.indigo,
|
||||
hintColor: Colors.indigo,
|
||||
focusColor: Colors.indigo,
|
||||
splashColor: Colors.indigo.withOpacity(0.15),
|
||||
fontFamily: 'WorkSans',
|
||||
scaffoldBackgroundColor: immichBackgroundColor,
|
||||
snackBarTheme: const SnackBarThemeData(
|
||||
contentTextStyle: TextStyle(
|
||||
fontFamily: 'WorkSans',
|
||||
color: Colors.indigo,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
appBarTheme: AppBarTheme(
|
||||
titleTextStyle: const TextStyle(
|
||||
fontFamily: 'WorkSans',
|
||||
color: Colors.indigo,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
backgroundColor: immichBackgroundColor,
|
||||
foregroundColor: Colors.indigo,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: true,
|
||||
),
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||
type: BottomNavigationBarType.fixed,
|
||||
backgroundColor: immichBackgroundColor,
|
||||
selectedItemColor: Colors.indigo,
|
||||
),
|
||||
cardTheme: const CardTheme(
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
drawerTheme: DrawerThemeData(
|
||||
backgroundColor: immichBackgroundColor,
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.indigo,
|
||||
),
|
||||
displayMedium: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
displaySmall: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.indigo,
|
||||
),
|
||||
titleSmall: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
fontSize: 26.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.indigo,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
chipTheme: base.chipTheme,
|
||||
sliderTheme: base.sliderTheme,
|
||||
popupMenuTheme: const PopupMenuThemeData(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
surfaceTintColor: Colors.transparent,
|
||||
color: Colors.white,
|
||||
),
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
indicatorColor: Colors.indigo.withOpacity(0.15),
|
||||
iconTheme: MaterialStatePropertyAll(
|
||||
IconThemeData(color: Colors.grey[700]),
|
||||
),
|
||||
backgroundColor: immichBackgroundColor,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
labelTextStyle: MaterialStatePropertyAll(
|
||||
TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
),
|
||||
dialogTheme: const DialogTheme(
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.indigo,
|
||||
),
|
||||
),
|
||||
labelStyle: TextStyle(
|
||||
color: Colors.indigo,
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
textSelectionTheme: const TextSelectionThemeData(
|
||||
cursorColor: Colors.indigo,
|
||||
),
|
||||
primary: Color(0xff4755b5),
|
||||
onPrimary: Color(0xffffffff),
|
||||
primaryContainer: Color(0xffdfe0ff),
|
||||
onPrimaryContainer: Color(0xff000d60),
|
||||
secondary: Color(0xff5b5d72),
|
||||
onSecondary: Color(0xffffffff),
|
||||
secondaryContainer: Color(0xFFD6D8FF),
|
||||
onSecondaryContainer: Color(0xff181a2c),
|
||||
tertiary: Color(0xff77536c),
|
||||
onTertiary: Color(0xffffffff),
|
||||
tertiaryContainer: Color(0xffffd7f0),
|
||||
onTertiaryContainer: Color(0xff2d1127),
|
||||
error: Color(0xffba1a1a),
|
||||
onError: Color(0xffffffff),
|
||||
errorContainer: Color(0xffffdad6),
|
||||
onErrorContainer: Color(0xff410002),
|
||||
background: Color(0xfff9f6fc),
|
||||
onBackground: Color(0xff1b1b1f),
|
||||
surface: Color(0xfff9f6fc),
|
||||
onSurface: Color(0xff1b1b1f),
|
||||
surfaceVariant: Color(0xffdeddea),
|
||||
onSurfaceVariant: Color(0xff46464f),
|
||||
outline: Color(0xff777680),
|
||||
outlineVariant: Color(0xffc7c5d0),
|
||||
shadow: Color(0xff000000),
|
||||
scrim: Color(0xff000000),
|
||||
inverseSurface: Color(0xff303137),
|
||||
onInverseSurface: Color(0xfff3f0f4),
|
||||
inversePrimary: Color(0xffbcc3ff),
|
||||
surfaceTint: Color(0xff4755b5),
|
||||
);
|
||||
|
||||
ThemeData immichDarkTheme = ThemeData(
|
||||
useMaterial3: true,
|
||||
ColorScheme _darkColorScheme = const ColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
primarySwatch: Colors.indigo,
|
||||
primaryColor: immichDarkThemePrimaryColor,
|
||||
scaffoldBackgroundColor: immichDarkBackgroundColor,
|
||||
hintColor: Colors.grey[600],
|
||||
fontFamily: 'WorkSans',
|
||||
snackBarTheme: SnackBarThemeData(
|
||||
contentTextStyle: TextStyle(
|
||||
fontFamily: 'WorkSans',
|
||||
color: immichDarkThemePrimaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
backgroundColor: Colors.grey[900],
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: immichDarkThemePrimaryColor,
|
||||
),
|
||||
),
|
||||
appBarTheme: AppBarTheme(
|
||||
titleTextStyle: TextStyle(
|
||||
fontFamily: 'WorkSans',
|
||||
color: immichDarkThemePrimaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
backgroundColor: const Color.fromARGB(255, 32, 33, 35),
|
||||
foregroundColor: immichDarkThemePrimaryColor,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: true,
|
||||
),
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||
type: BottomNavigationBarType.fixed,
|
||||
backgroundColor: const Color.fromARGB(255, 35, 36, 37),
|
||||
selectedItemColor: immichDarkThemePrimaryColor,
|
||||
),
|
||||
drawerTheme: DrawerThemeData(
|
||||
backgroundColor: immichDarkBackgroundColor,
|
||||
scrimColor: Colors.white.withOpacity(0.1),
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
displayLarge: const TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color.fromARGB(255, 255, 255, 255),
|
||||
),
|
||||
displayMedium: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color.fromARGB(255, 255, 255, 255),
|
||||
),
|
||||
displaySmall: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: immichDarkThemePrimaryColor,
|
||||
),
|
||||
titleSmall: const TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleMedium: const TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleLarge: const TextStyle(
|
||||
fontSize: 26.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
cardColor: Colors.grey[900],
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.black87,
|
||||
backgroundColor: immichDarkThemePrimaryColor,
|
||||
),
|
||||
),
|
||||
chipTheme: base.chipTheme,
|
||||
sliderTheme: base.sliderTheme,
|
||||
popupMenuTheme: const PopupMenuThemeData(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
indicatorColor: immichDarkThemePrimaryColor.withOpacity(0.4),
|
||||
iconTheme: MaterialStatePropertyAll(
|
||||
IconThemeData(color: Colors.grey[500]),
|
||||
),
|
||||
backgroundColor: Colors.grey[900],
|
||||
surfaceTintColor: Colors.transparent,
|
||||
labelTextStyle: MaterialStatePropertyAll(
|
||||
TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey[500],
|
||||
),
|
||||
),
|
||||
),
|
||||
dialogTheme: const DialogTheme(
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: immichDarkThemePrimaryColor,
|
||||
),
|
||||
),
|
||||
labelStyle: TextStyle(
|
||||
color: immichDarkThemePrimaryColor,
|
||||
),
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
cursorColor: immichDarkThemePrimaryColor,
|
||||
),
|
||||
primary: Color(0xffa4c8ff),
|
||||
onPrimary: Color(0xff00315e),
|
||||
primaryContainer: Color(0xFF182C40),
|
||||
onPrimaryContainer: Color(0xffd4e3ff),
|
||||
secondary: Color(0xffbcc7dc),
|
||||
onSecondary: Color(0xff37474f),
|
||||
secondaryContainer: Color(0xff3d4758),
|
||||
onSecondaryContainer: Color(0xffd8e3f8),
|
||||
tertiary: Color(0xffdabde2),
|
||||
onTertiary: Color(0xff3d2946),
|
||||
tertiaryContainer: Color(0xff543f5e),
|
||||
onTertiaryContainer: Color(0xfff6d9ff),
|
||||
error: Color(0xffffb4ab),
|
||||
onError: Color(0xff690005),
|
||||
errorContainer: Color(0xff93000a),
|
||||
onErrorContainer: Color(0xffffb4ab),
|
||||
background: Color(0xff101214),
|
||||
onBackground: Color(0xffe2e2e5),
|
||||
surface: Color(0xff101214),
|
||||
onSurface: Color(0xffe2e2e5),
|
||||
surfaceVariant: Color(0xff363c42),
|
||||
onSurfaceVariant: Color(0xffc1c7ce),
|
||||
outline: Color(0xff8b9198),
|
||||
outlineVariant: Color(0xff41474d),
|
||||
shadow: Color(0xff000000),
|
||||
scrim: Color(0xff000000),
|
||||
inverseSurface: Color(0xffeeeef1),
|
||||
onInverseSurface: Color(0xff2e3133),
|
||||
inversePrimary: Color(0xff1c5fa5),
|
||||
surfaceTint: Color(0xff90caf9),
|
||||
);
|
||||
|
||||
ThemeData getThemeForScheme(ColorScheme scheme) {
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: scheme.brightness,
|
||||
colorScheme: scheme,
|
||||
primaryColor: scheme.primary,
|
||||
scaffoldBackgroundColor: scheme.background,
|
||||
fontFamily: 'WorkSans',
|
||||
appBarTheme: AppBarTheme(
|
||||
iconTheme: IconThemeData(color: scheme.primary),
|
||||
titleTextStyle: TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: scheme.primary,
|
||||
),
|
||||
),
|
||||
cardTheme: const CardTheme(elevation: 2.0),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
visualDensity: VisualDensity.standard,
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 11,
|
||||
),
|
||||
shadowColor: scheme.shadow,
|
||||
foregroundColor: scheme.onPrimary,
|
||||
backgroundColor: scheme.primary,
|
||||
),
|
||||
),
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
iconTheme: MaterialStateProperty.resolveWith<IconThemeData?>((states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return IconThemeData(color: scheme.primary);
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: scheme.primary,
|
||||
),
|
||||
displayMedium: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
displaySmall: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleSmall: const TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleMedium: const TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
fontSize: 26.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: scheme.primary,
|
||||
),
|
||||
),
|
||||
chipTheme: const ChipThemeData(
|
||||
side: BorderSide.none,
|
||||
),
|
||||
sliderTheme: const SliderThemeData(
|
||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7),
|
||||
trackHeight: 2.0,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
labelStyle: TextStyle(
|
||||
color: scheme.primary,
|
||||
),
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData immichLightTheme = getThemeForScheme(_lightColorScheme);
|
||||
ThemeData immichDarkTheme = getThemeForScheme(_darkColorScheme);
|
||||
|
|
Loading…
Reference in a new issue