mirror of
https://github.com/immich-app/immich.git
synced 2025-12-12 15:50:43 -08:00
feat(mobile): immich-ui icon button (#24502)
* feat(mobile): immich-ui icon button * fix lint --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
@@ -1,173 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
|
||||||
import 'package:drift/drift.dart' hide Column;
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
|
|
||||||
final _features = [
|
|
||||||
_Feature(
|
|
||||||
name: 'Main Timeline',
|
|
||||||
icon: Icons.timeline_rounded,
|
|
||||||
onTap: (ctx, _) => ctx.pushRoute(const TabShellRoute()),
|
|
||||||
),
|
|
||||||
_Feature(
|
|
||||||
name: 'Selection Mode Timeline',
|
|
||||||
icon: Icons.developer_mode_rounded,
|
|
||||||
onTap: (ctx, ref) async {
|
|
||||||
final user = ref.watch(currentUserProvider);
|
|
||||||
if (user == null) {
|
|
||||||
return Future.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
final assets = await ref.read(remoteAssetRepositoryProvider).getSome(user.id);
|
|
||||||
|
|
||||||
final selectedAssets = await ctx.pushRoute<Set<BaseAsset>>(
|
|
||||||
DriftAssetSelectionTimelineRoute(lockedSelectionAssets: assets.toSet()),
|
|
||||||
);
|
|
||||||
|
|
||||||
Logger("FeaturesInDevelopment").fine("Selected ${selectedAssets?.length ?? 0} assets");
|
|
||||||
|
|
||||||
return Future.value();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()),
|
|
||||||
_Feature(
|
|
||||||
name: 'Sync Local',
|
|
||||||
icon: Icons.photo_album_rounded,
|
|
||||||
onTap: (_, ref) => ref.read(backgroundSyncProvider).syncLocal(),
|
|
||||||
),
|
|
||||||
_Feature(
|
|
||||||
name: 'Sync Local Full (1)',
|
|
||||||
icon: Icons.photo_library_rounded,
|
|
||||||
onTap: (_, ref) => ref.read(backgroundSyncProvider).syncLocal(full: true),
|
|
||||||
),
|
|
||||||
_Feature(
|
|
||||||
name: 'Hash Local Assets (2)',
|
|
||||||
icon: Icons.numbers_outlined,
|
|
||||||
onTap: (_, ref) => ref.read(backgroundSyncProvider).hashAssets(),
|
|
||||||
),
|
|
||||||
_Feature(
|
|
||||||
name: 'Sync Remote (3)',
|
|
||||||
icon: Icons.refresh_rounded,
|
|
||||||
onTap: (_, ref) => ref.read(backgroundSyncProvider).syncRemote(),
|
|
||||||
),
|
|
||||||
_Feature(
|
|
||||||
name: 'WAL Checkpoint',
|
|
||||||
icon: Icons.save_rounded,
|
|
||||||
onTap: (_, ref) => ref.read(driftProvider).customStatement("pragma wal_checkpoint(truncate)"),
|
|
||||||
),
|
|
||||||
_Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()),
|
|
||||||
_Feature(
|
|
||||||
name: 'Clear Delta Checkpoint',
|
|
||||||
icon: Icons.delete_rounded,
|
|
||||||
onTap: (_, ref) => ref.read(nativeSyncApiProvider).clearSyncCheckpoint(),
|
|
||||||
),
|
|
||||||
_Feature(
|
|
||||||
name: 'Clear Local Data',
|
|
||||||
style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold),
|
|
||||||
icon: Icons.delete_forever_rounded,
|
|
||||||
onTap: (_, ref) async {
|
|
||||||
final db = ref.read(driftProvider);
|
|
||||||
await db.localAssetEntity.deleteAll();
|
|
||||||
await db.localAlbumEntity.deleteAll();
|
|
||||||
await db.localAlbumAssetEntity.deleteAll();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_Feature(
|
|
||||||
name: 'Clear Remote Data',
|
|
||||||
style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold),
|
|
||||||
icon: Icons.delete_sweep_rounded,
|
|
||||||
onTap: (_, ref) async {
|
|
||||||
final db = ref.read(driftProvider);
|
|
||||||
await db.remoteAssetEntity.deleteAll();
|
|
||||||
await db.remoteExifEntity.deleteAll();
|
|
||||||
await db.remoteAlbumEntity.deleteAll();
|
|
||||||
await db.remoteAlbumUserEntity.deleteAll();
|
|
||||||
await db.remoteAlbumAssetEntity.deleteAll();
|
|
||||||
await db.memoryEntity.deleteAll();
|
|
||||||
await db.memoryAssetEntity.deleteAll();
|
|
||||||
await db.stackEntity.deleteAll();
|
|
||||||
await db.personEntity.deleteAll();
|
|
||||||
await db.assetFaceEntity.deleteAll();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_Feature(
|
|
||||||
name: 'Local Media Summary',
|
|
||||||
style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold),
|
|
||||||
icon: Icons.table_chart_rounded,
|
|
||||||
onTap: (ctx, _) => ctx.pushRoute(const LocalMediaSummaryRoute()),
|
|
||||||
),
|
|
||||||
_Feature(
|
|
||||||
name: 'Remote Media Summary',
|
|
||||||
style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold),
|
|
||||||
icon: Icons.summarize_rounded,
|
|
||||||
onTap: (ctx, _) => ctx.pushRoute(const RemoteMediaSummaryRoute()),
|
|
||||||
),
|
|
||||||
_Feature(
|
|
||||||
name: 'Reset Sqlite',
|
|
||||||
icon: Icons.table_view_rounded,
|
|
||||||
style: const TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
|
|
||||||
onTap: (_, ref) async {
|
|
||||||
final drift = ref.read(driftProvider);
|
|
||||||
// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
|
|
||||||
final migrator = drift.createMigrator();
|
|
||||||
for (final entity in drift.allSchemaEntities) {
|
|
||||||
await migrator.drop(entity);
|
|
||||||
await migrator.create(entity);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
@RoutePage()
|
|
||||||
class FeatInDevPage extends StatelessWidget {
|
|
||||||
const FeatInDevPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(title: Text('features_in_development'.tr()), centerTitle: true),
|
|
||||||
body: Column(
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
flex: 1,
|
|
||||||
child: ListView.builder(
|
|
||||||
itemBuilder: (_, index) {
|
|
||||||
final feat = _features[index];
|
|
||||||
return Consumer(
|
|
||||||
builder: (ctx, ref, _) => ListTile(
|
|
||||||
title: Text(feat.name, style: feat.style),
|
|
||||||
trailing: Icon(feat.icon),
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
onTap: () => unawaited(feat.onTap(ctx, ref)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
itemCount: _features.length,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Divider(height: 0),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Feature {
|
|
||||||
const _Feature({required this.name, required this.icon, required this.onTap, this.style});
|
|
||||||
|
|
||||||
final String name;
|
|
||||||
final IconData icon;
|
|
||||||
final TextStyle? style;
|
|
||||||
final Future<void> Function(BuildContext, WidgetRef _) onTap;
|
|
||||||
}
|
|
||||||
51
mobile/lib/presentation/pages/dev/ui_showcase.page.dart
Normal file
51
mobile/lib/presentation/pages/dev/ui_showcase.page.dart
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_ui/immich_ui.dart';
|
||||||
|
|
||||||
|
List<Widget> _showcaseBuilder(Function(ImmichVariant variant, ImmichColor color) builder) {
|
||||||
|
final children = <Widget>[];
|
||||||
|
|
||||||
|
final items = [
|
||||||
|
(variant: ImmichVariant.filled, title: "Filled Variant"),
|
||||||
|
(variant: ImmichVariant.ghost, title: "Ghost Variant"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (final (:variant, :title) in items) {
|
||||||
|
children.add(Text(title));
|
||||||
|
children.add(Row(spacing: 10, children: [for (var color in ImmichColor.values) builder(variant, color)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class ImmichUIShowcasePage extends StatelessWidget {
|
||||||
|
const ImmichUIShowcasePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('Immich UI Showcase')),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
spacing: 10,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text("IconButton", style: context.textTheme.titleLarge),
|
||||||
|
..._showcaseBuilder(
|
||||||
|
(variant, color) =>
|
||||||
|
ImmichIconButton(icon: Icons.favorite, color: color, variant: variant, onTap: () {}),
|
||||||
|
),
|
||||||
|
Text("CloseButton", style: context.textTheme.titleLarge),
|
||||||
|
..._showcaseBuilder((variant, color) => ImmichCloseButton(color: color, variant: variant, onTap: () {})),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart';
|
import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart';
|
||||||
|
import 'package:immich_ui/immich_ui.dart';
|
||||||
|
|
||||||
/// A widget for cropping an image.
|
/// A widget for cropping an image.
|
||||||
/// This widget uses [HookWidget] to manage its lifecycle and state. It allows
|
/// This widget uses [HookWidget] to manage its lifecycle and state. It allows
|
||||||
@@ -30,11 +31,13 @@ class DriftCropImagePage extends HookWidget {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: context.scaffoldBackgroundColor,
|
backgroundColor: context.scaffoldBackgroundColor,
|
||||||
title: Text("crop".tr()),
|
title: Text("crop".tr()),
|
||||||
leading: CloseButton(color: context.primaryColor),
|
leading: const ImmichCloseButton(),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
ImmichIconButton(
|
||||||
icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24),
|
icon: Icons.done_rounded,
|
||||||
onPressed: () async {
|
color: ImmichColor.primary,
|
||||||
|
variant: ImmichVariant.ghost,
|
||||||
|
onTap: () async {
|
||||||
final croppedImage = await cropController.croppedImage();
|
final croppedImage = await cropController.croppedImage();
|
||||||
unawaited(context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true)));
|
unawaited(context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true)));
|
||||||
},
|
},
|
||||||
@@ -72,17 +75,17 @@ class DriftCropImagePage extends HookWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
ImmichIconButton(
|
||||||
icon: Icon(Icons.rotate_left, color: context.themeData.iconTheme.color),
|
icon: Icons.rotate_left,
|
||||||
onPressed: () {
|
variant: ImmichVariant.ghost,
|
||||||
cropController.rotateLeft();
|
color: ImmichColor.secondary,
|
||||||
},
|
onTap: () => cropController.rotateLeft(),
|
||||||
),
|
),
|
||||||
IconButton(
|
ImmichIconButton(
|
||||||
icon: Icon(Icons.rotate_right, color: context.themeData.iconTheme.color),
|
icon: Icons.rotate_right,
|
||||||
onPressed: () {
|
variant: ImmichVariant.ghost,
|
||||||
cropController.rotateRight();
|
color: ImmichColor.secondary,
|
||||||
},
|
onTap: () => cropController.rotateRight(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -78,9 +78,9 @@ import 'package:immich_mobile/pages/search/recently_taken.page.dart';
|
|||||||
import 'package:immich_mobile/pages/search/search.page.dart';
|
import 'package:immich_mobile/pages/search/search.page.dart';
|
||||||
import 'package:immich_mobile/pages/settings/sync_status.page.dart';
|
import 'package:immich_mobile/pages/settings/sync_status.page.dart';
|
||||||
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/ui_showcase.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/download_info.page.dart';
|
import 'package:immich_mobile/presentation/pages/download_info.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_activities.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_activities.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_album.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_album.page.dart';
|
||||||
@@ -286,7 +286,6 @@ class AppRouter extends RootStackRouter {
|
|||||||
AutoRoute(page: ShareIntentRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: ShareIntentRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: LockedRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]),
|
AutoRoute(page: LockedRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: PinAuthRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: PinAuthRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: FeatInDevRoute.page, guards: [_authGuard, _duplicateGuard]),
|
|
||||||
AutoRoute(page: LocalMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: LocalMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: RemoteMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: RemoteMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: DriftBackupRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: DriftBackupRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
@@ -338,6 +337,7 @@ class AppRouter extends RootStackRouter {
|
|||||||
AutoRoute(page: DriftBackupAssetDetailRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: DriftBackupAssetDetailRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: AssetTroubleshootRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: AssetTroubleshootRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: DownloadInfoRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: DownloadInfoRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
|
AutoRoute(page: ImmichUIShowcaseRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
// required to handle all deeplinks in deep_link.service.dart
|
// required to handle all deeplinks in deep_link.service.dart
|
||||||
// auto_route_library#1722
|
// auto_route_library#1722
|
||||||
RedirectRoute(path: '*', redirectTo: '/'),
|
RedirectRoute(path: '*', redirectTo: '/'),
|
||||||
|
|||||||
@@ -1648,22 +1648,6 @@ class FavoritesRoute extends PageRouteInfo<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
|
||||||
/// [FeatInDevPage]
|
|
||||||
class FeatInDevRoute extends PageRouteInfo<void> {
|
|
||||||
const FeatInDevRoute({List<PageRouteInfo>? children})
|
|
||||||
: super(FeatInDevRoute.name, initialChildren: children);
|
|
||||||
|
|
||||||
static const String name = 'FeatInDevRoute';
|
|
||||||
|
|
||||||
static PageInfo page = PageInfo(
|
|
||||||
name,
|
|
||||||
builder: (data) {
|
|
||||||
return const FeatInDevPage();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [FilterImagePage]
|
/// [FilterImagePage]
|
||||||
class FilterImageRoute extends PageRouteInfo<FilterImageRouteArgs> {
|
class FilterImageRoute extends PageRouteInfo<FilterImageRouteArgs> {
|
||||||
@@ -1831,6 +1815,22 @@ class HeaderSettingsRoute extends PageRouteInfo<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [ImmichUIShowcasePage]
|
||||||
|
class ImmichUIShowcaseRoute extends PageRouteInfo<void> {
|
||||||
|
const ImmichUIShowcaseRoute({List<PageRouteInfo>? children})
|
||||||
|
: super(ImmichUIShowcaseRoute.name, initialChildren: children);
|
||||||
|
|
||||||
|
static const String name = 'ImmichUIShowcaseRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const ImmichUIShowcasePage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [LibraryPage]
|
/// [LibraryPage]
|
||||||
class LibraryRoute extends PageRouteInfo<void> {
|
class LibraryRoute extends PageRouteInfo<void> {
|
||||||
|
|||||||
@@ -155,8 +155,8 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||||||
...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)),
|
...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)),
|
||||||
if (kDebugMode || kProfileMode)
|
if (kDebugMode || kProfileMode)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.science_rounded),
|
icon: const Icon(Icons.palette_rounded),
|
||||||
onPressed: () => context.pushRoute(const FeatInDevRoute()),
|
onPressed: () => context.pushRoute(const ImmichUIShowcaseRoute()),
|
||||||
),
|
),
|
||||||
if (isCasting)
|
if (isCasting)
|
||||||
Padding(
|
Padding(
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ class ImmichSliverAppBar extends ConsumerWidget {
|
|||||||
...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)),
|
...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)),
|
||||||
if ((kDebugMode || kProfileMode) && !isReadonlyModeEnabled)
|
if ((kDebugMode || kProfileMode) && !isReadonlyModeEnabled)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.science_rounded),
|
icon: const Icon(Icons.palette_rounded),
|
||||||
onPressed: () => context.pushRoute(const FeatInDevRoute()),
|
onPressed: () => context.pushRoute(const ImmichUIShowcaseRoute()),
|
||||||
),
|
),
|
||||||
if (showUploadButton && !isReadonlyModeEnabled)
|
if (showUploadButton && !isReadonlyModeEnabled)
|
||||||
const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()),
|
const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()),
|
||||||
|
|||||||
3
mobile/packages/ui/lib/immich_ui.dart
Normal file
3
mobile/packages/ui/lib/immich_ui.dart
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export 'src/buttons/close_button.dart';
|
||||||
|
export 'src/buttons/icon_button.dart';
|
||||||
|
export 'src/types.dart';
|
||||||
25
mobile/packages/ui/lib/src/buttons/close_button.dart
Normal file
25
mobile/packages/ui/lib/src/buttons/close_button.dart
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:immich_ui/src/buttons/icon_button.dart';
|
||||||
|
import 'package:immich_ui/src/types.dart';
|
||||||
|
|
||||||
|
class ImmichCloseButton extends StatelessWidget {
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
final ImmichVariant variant;
|
||||||
|
final ImmichColor color;
|
||||||
|
|
||||||
|
const ImmichCloseButton({
|
||||||
|
super.key,
|
||||||
|
this.onTap,
|
||||||
|
this.color = ImmichColor.primary,
|
||||||
|
this.variant = ImmichVariant.ghost,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => ImmichIconButton(
|
||||||
|
key: key,
|
||||||
|
icon: Icons.close,
|
||||||
|
color: color,
|
||||||
|
variant: variant,
|
||||||
|
onTap: onTap ?? () => Navigator.of(context).pop(),
|
||||||
|
);
|
||||||
|
}
|
||||||
48
mobile/packages/ui/lib/src/buttons/icon_button.dart
Normal file
48
mobile/packages/ui/lib/src/buttons/icon_button.dart
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:immich_ui/src/types.dart';
|
||||||
|
|
||||||
|
class ImmichIconButton extends StatelessWidget {
|
||||||
|
final IconData icon;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
final ImmichVariant variant;
|
||||||
|
final ImmichColor color;
|
||||||
|
|
||||||
|
const ImmichIconButton({
|
||||||
|
super.key,
|
||||||
|
required this.icon,
|
||||||
|
required this.onTap,
|
||||||
|
this.color = ImmichColor.primary,
|
||||||
|
this.variant = ImmichVariant.filled,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final background = switch (variant) {
|
||||||
|
ImmichVariant.filled => switch (color) {
|
||||||
|
ImmichColor.primary => Theme.of(context).colorScheme.primary,
|
||||||
|
ImmichColor.secondary => Theme.of(context).colorScheme.secondary,
|
||||||
|
},
|
||||||
|
ImmichVariant.ghost => Colors.transparent,
|
||||||
|
};
|
||||||
|
|
||||||
|
final foreground = switch (variant) {
|
||||||
|
ImmichVariant.filled => switch (color) {
|
||||||
|
ImmichColor.primary => Theme.of(context).colorScheme.onPrimary,
|
||||||
|
ImmichColor.secondary => Theme.of(context).colorScheme.onSecondary,
|
||||||
|
},
|
||||||
|
ImmichVariant.ghost => switch (color) {
|
||||||
|
ImmichColor.primary => Theme.of(context).colorScheme.primary,
|
||||||
|
ImmichColor.secondary => Theme.of(context).colorScheme.secondary,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return IconButton(
|
||||||
|
icon: Icon(icon),
|
||||||
|
onPressed: onTap,
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
backgroundColor: background,
|
||||||
|
foregroundColor: foreground,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
mobile/packages/ui/lib/src/types.dart
Normal file
9
mobile/packages/ui/lib/src/types.dart
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
enum ImmichVariant {
|
||||||
|
filled,
|
||||||
|
ghost,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ImmichColor {
|
||||||
|
primary,
|
||||||
|
secondary,
|
||||||
|
}
|
||||||
55
mobile/packages/ui/pubspec.lock
Normal file
55
mobile/packages/ui/pubspec.lock
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.19.1"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.11.1"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.16.0"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
sdks:
|
||||||
|
dart: ">=3.8.0-0 <4.0.0"
|
||||||
12
mobile/packages/ui/pubspec.yaml
Normal file
12
mobile/packages/ui/pubspec.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
name: immich_ui
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
@@ -1015,6 +1015,13 @@ packages:
|
|||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
immich_ui:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "packages/ui"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.0.0"
|
||||||
integration_test:
|
integration_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ dependencies:
|
|||||||
hooks_riverpod: ^2.6.1
|
hooks_riverpod: ^2.6.1
|
||||||
http: ^1.5.0
|
http: ^1.5.0
|
||||||
image_picker: ^1.2.0
|
image_picker: ^1.2.0
|
||||||
|
immich_ui:
|
||||||
|
path: './packages/ui'
|
||||||
intl: ^0.20.2
|
intl: ^0.20.2
|
||||||
isar:
|
isar:
|
||||||
git:
|
git:
|
||||||
|
|||||||
Reference in New Issue
Block a user