mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
chore: bump dart sdk to 3.8 (#20355)
* chore: bump dart sdk to 3.8 * chore: make build * make pigeon * chore: format files --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
parent
9b3718120b
commit
e52b9d15b5
643 changed files with 32561 additions and 35292 deletions
|
|
@ -34,27 +34,18 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
final user = ref.watch(currentUserProvider);
|
||||
final isLoggingOut = useState(false);
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
ref.read(backupProvider.notifier).updateDiskInfo();
|
||||
ref.read(currentUserProvider.notifier).refresh();
|
||||
return null;
|
||||
},
|
||||
[],
|
||||
);
|
||||
useEffect(() {
|
||||
ref.read(backupProvider.notifier).updateDiskInfo();
|
||||
ref.read(currentUserProvider.notifier).refresh();
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
buildTopRow() {
|
||||
return Stack(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: InkWell(
|
||||
onTap: () => context.pop(),
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
child: InkWell(onTap: () => context.pop(), child: const Icon(Icons.close, size: 20)),
|
||||
),
|
||||
Center(
|
||||
child: Image.asset(
|
||||
|
|
@ -66,29 +57,16 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
buildActionButton(
|
||||
IconData icon,
|
||||
String text,
|
||||
Function() onTap, {
|
||||
Widget? trailing,
|
||||
}) {
|
||||
buildActionButton(IconData icon, String text, Function() onTap, {Widget? trailing}) {
|
||||
return ListTile(
|
||||
dense: true,
|
||||
visualDensity: VisualDensity.standard,
|
||||
contentPadding: const EdgeInsets.only(left: 30, right: 30),
|
||||
minLeadingWidth: 40,
|
||||
leading: SizedBox(
|
||||
child: Icon(
|
||||
icon,
|
||||
color: theme.textTheme.labelLarge?.color?.withAlpha(250),
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
leading: SizedBox(child: Icon(icon, color: theme.textTheme.labelLarge?.color?.withAlpha(250), size: 20)),
|
||||
title: Text(
|
||||
text,
|
||||
style: theme.textTheme.labelLarge?.copyWith(
|
||||
color: theme.textTheme.labelLarge?.color?.withAlpha(250),
|
||||
),
|
||||
style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)),
|
||||
).tr(),
|
||||
onTap: onTap,
|
||||
trailing: trailing,
|
||||
|
|
@ -96,11 +74,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
buildSettingButton() {
|
||||
return buildActionButton(
|
||||
Icons.settings_outlined,
|
||||
"settings",
|
||||
() => context.pushRoute(const SettingsRoute()),
|
||||
);
|
||||
return buildActionButton(Icons.settings_outlined, "settings", () => context.pushRoute(const SettingsRoute()));
|
||||
}
|
||||
|
||||
buildAppLogButton() {
|
||||
|
|
@ -142,10 +116,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
);
|
||||
},
|
||||
trailing: isLoggingOut.value
|
||||
? const SizedBox.square(
|
||||
dimension: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
? const SizedBox.square(dimension: 20, child: CircularProgressIndicator(strokeWidth: 2))
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
|
@ -165,20 +136,13 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 3),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.surface,
|
||||
),
|
||||
decoration: BoxDecoration(color: context.colorScheme.surface),
|
||||
child: ListTile(
|
||||
minLeadingWidth: 50,
|
||||
leading: Icon(
|
||||
Icons.storage_rounded,
|
||||
color: theme.primaryColor,
|
||||
),
|
||||
leading: Icon(Icons.storage_rounded, color: theme.primaryColor),
|
||||
title: Text(
|
||||
"backup_controller_page_server_storage",
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w500),
|
||||
).tr(),
|
||||
isThreeLine: true,
|
||||
subtitle: Padding(
|
||||
|
|
@ -196,12 +160,9 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: const Text('backup_controller_page_storage_format').tr(
|
||||
namedArgs: {
|
||||
'used': usedDiskSpace,
|
||||
'total': totalDiskSpace,
|
||||
},
|
||||
),
|
||||
child: const Text(
|
||||
'backup_controller_page_storage_format',
|
||||
).tr(namedArgs: {'used': usedDiskSpace, 'total': totalDiskSpace}),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -220,43 +181,19 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
InkWell(
|
||||
onTap: () {
|
||||
context.pop();
|
||||
launchUrl(
|
||||
Uri.parse('https://immich.app'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
launchUrl(Uri.parse('https://immich.app'), mode: LaunchMode.externalApplication);
|
||||
},
|
||||
child: Text(
|
||||
"documentation",
|
||||
style: context.textTheme.bodySmall,
|
||||
).tr(),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 20,
|
||||
child: Text(
|
||||
"•",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
child: Text("documentation", style: context.textTheme.bodySmall).tr(),
|
||||
),
|
||||
const SizedBox(width: 20, child: Text("•", textAlign: TextAlign.center)),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
context.pop();
|
||||
launchUrl(
|
||||
Uri.parse('https://github.com/immich-app/immich'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
launchUrl(Uri.parse('https://github.com/immich-app/immich'), mode: LaunchMode.externalApplication);
|
||||
},
|
||||
child: Text(
|
||||
"profile_drawer_github",
|
||||
style: context.textTheme.bodySmall,
|
||||
).tr(),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 20,
|
||||
child: Text(
|
||||
"•",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
child: Text("profile_drawer_github", style: context.textTheme.bodySmall).tr(),
|
||||
),
|
||||
const SizedBox(width: 20, child: Text("•", textAlign: TextAlign.center)),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
context.pop();
|
||||
|
|
@ -291,20 +228,13 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
right: horizontalPadding,
|
||||
bottom: isHorizontal ? 20 : 100,
|
||||
),
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(20),
|
||||
),
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
child: SizedBox(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: buildTopRow(),
|
||||
),
|
||||
Container(padding: const EdgeInsets.all(20), child: buildTopRow()),
|
||||
const AppBarProfileInfoBox(),
|
||||
buildStorageInformation(),
|
||||
const AppBarServerInfo(),
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
|
|||
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
|
||||
|
||||
class AppBarProfileInfoBox extends HookConsumerWidget {
|
||||
const AppBarProfileInfoBox({
|
||||
super.key,
|
||||
});
|
||||
const AppBarProfileInfoBox({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
|
@ -29,38 +27,24 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
final userImage = UserCircleAvatar(
|
||||
radius: 22,
|
||||
size: 44,
|
||||
user: user,
|
||||
);
|
||||
final userImage = UserCircleAvatar(radius: 22, size: 44, user: user);
|
||||
|
||||
if (uploadProfileImageStatus == UploadProfileStatus.loading) {
|
||||
return const SizedBox(
|
||||
height: 40,
|
||||
width: 40,
|
||||
child: ImmichLoadingIndicator(borderRadius: 20),
|
||||
);
|
||||
return const SizedBox(height: 40, width: 40, child: ImmichLoadingIndicator(borderRadius: 20));
|
||||
}
|
||||
|
||||
return userImage;
|
||||
}
|
||||
|
||||
pickUserProfileImage() async {
|
||||
final XFile? image = await ImagePicker().pickImage(
|
||||
source: ImageSource.gallery,
|
||||
maxHeight: 1024,
|
||||
maxWidth: 1024,
|
||||
);
|
||||
final XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024);
|
||||
|
||||
if (image != null) {
|
||||
var success = await ref.watch(uploadProfileImageProvider.notifier).upload(image);
|
||||
|
||||
if (success) {
|
||||
final profileImagePath = ref.read(uploadProfileImageProvider).profileImagePath;
|
||||
ref.watch(authProvider.notifier).updateUserProfileImagePath(
|
||||
profileImagePath,
|
||||
);
|
||||
ref.watch(authProvider.notifier).updateUserProfileImagePath(profileImagePath);
|
||||
if (user != null) {
|
||||
ref.read(currentUserProvider.notifier).refresh();
|
||||
}
|
||||
|
|
@ -74,10 +58,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
|||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.surface,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
topRight: Radius.circular(10),
|
||||
),
|
||||
borderRadius: const BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)),
|
||||
),
|
||||
child: ListTile(
|
||||
minLeadingWidth: 50,
|
||||
|
|
@ -93,16 +74,10 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
|||
child: Material(
|
||||
color: context.colorScheme.surfaceContainerHighest,
|
||||
elevation: 3,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(50.0)),
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(50.0))),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Icon(
|
||||
Icons.camera_alt_outlined,
|
||||
color: context.primaryColor,
|
||||
size: 14,
|
||||
),
|
||||
child: Icon(Icons.camera_alt_outlined, color: context.primaryColor, size: 14),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -111,16 +86,11 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
|||
),
|
||||
title: Text(
|
||||
authState.name,
|
||||
style: context.textTheme.titleMedium?.copyWith(
|
||||
color: context.primaryColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
authState.userEmail,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: context.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
style: context.textTheme.bodySmall?.copyWith(color: context.colorScheme.onSurfaceSecondary),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@ import 'package:immich_mobile/utils/url_helper.dart';
|
|||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
class AppBarServerInfo extends HookConsumerWidget {
|
||||
const AppBarServerInfo({
|
||||
super.key,
|
||||
});
|
||||
const AppBarServerInfo({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
|
@ -27,29 +25,20 @@ class AppBarServerInfo extends HookConsumerWidget {
|
|||
getPackageInfo() async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
|
||||
appInfo.value = {
|
||||
"version": packageInfo.version,
|
||||
"buildNumber": packageInfo.buildNumber,
|
||||
};
|
||||
appInfo.value = {"version": packageInfo.version, "buildNumber": packageInfo.buildNumber};
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
getPackageInfo();
|
||||
return null;
|
||||
},
|
||||
[],
|
||||
);
|
||||
useEffect(() {
|
||||
getPackageInfo();
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.surface,
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(10),
|
||||
bottomRight: Radius.circular(10),
|
||||
),
|
||||
borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(10), bottomRight: Radius.circular(10)),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8),
|
||||
|
|
@ -63,17 +52,10 @@ class AppBarServerInfo extends HookConsumerWidget {
|
|||
? serverInfoState.versionMismatchErrorMessage
|
||||
: "profile_drawer_client_server_up_to_date".tr(),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: context.primaryColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
style: TextStyle(fontSize: 11, color: context.primaryColor, fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Divider(thickness: 1),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
|
@ -106,10 +88,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Divider(thickness: 1),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
|
@ -144,10 +123,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Divider(thickness: 1),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
|
@ -197,10 +173,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Divider(thickness: 1),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
|
@ -212,11 +185,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
|||
if (serverInfoState.isNewReleaseAvailable)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 5.0),
|
||||
child: Icon(
|
||||
Icons.info,
|
||||
color: Color.fromARGB(255, 243, 188, 106),
|
||||
size: 12,
|
||||
),
|
||||
child: Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: 12),
|
||||
),
|
||||
Text(
|
||||
"latest_version".tr(),
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ class ConfirmDialog extends StatelessWidget {
|
|||
}
|
||||
|
||||
return AlertDialog(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
title: Text(title).tr(),
|
||||
content: Text(content).tr(),
|
||||
actions: [
|
||||
|
|
@ -36,20 +34,14 @@ class ConfirmDialog extends StatelessWidget {
|
|||
onPressed: () => context.pop(false),
|
||||
child: Text(
|
||||
cancel,
|
||||
style: TextStyle(
|
||||
color: context.primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: onOkPressed,
|
||||
child: Text(
|
||||
ok,
|
||||
style: TextStyle(
|
||||
color: context.colorScheme.error,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -16,11 +16,8 @@ Future<String?> showDateTimePicker({
|
|||
}) {
|
||||
return showDialog<String?>(
|
||||
context: context,
|
||||
builder: (context) => _DateTimePicker(
|
||||
initialDateTime: initialDateTime,
|
||||
initialTZ: initialTZ,
|
||||
initialTZOffset: initialTZOffset,
|
||||
),
|
||||
builder: (context) =>
|
||||
_DateTimePicker(initialDateTime: initialDateTime, initialTZ: initialTZ, initialTZOffset: initialTZOffset),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -33,18 +30,12 @@ class _DateTimePicker extends HookWidget {
|
|||
final String? initialTZ;
|
||||
final Duration? initialTZOffset;
|
||||
|
||||
const _DateTimePicker({
|
||||
this.initialDateTime,
|
||||
this.initialTZ,
|
||||
this.initialTZOffset,
|
||||
});
|
||||
const _DateTimePicker({this.initialDateTime, this.initialTZ, this.initialTZOffset});
|
||||
|
||||
_TimeZoneOffset _getInitiationLocation() {
|
||||
if (initialTZ != null) {
|
||||
try {
|
||||
return _TimeZoneOffset.fromLocation(
|
||||
tz.timeZoneDatabase.get(initialTZ!),
|
||||
);
|
||||
return _TimeZoneOffset.fromLocation(tz.timeZoneDatabase.get(initialTZ!));
|
||||
} on LocationNotFoundException {
|
||||
// no-op
|
||||
}
|
||||
|
|
@ -59,10 +50,8 @@ class _DateTimePicker extends HookWidget {
|
|||
(location) => location.currentTimeZone.offset == offsetInMilli,
|
||||
);
|
||||
// Prefer locations with abbreviation first
|
||||
final location = locations.firstWhereOrNull(
|
||||
(e) => !e.currentTimeZone.abbreviation.contains("0"),
|
||||
) ??
|
||||
locations.firstOrNull;
|
||||
final location =
|
||||
locations.firstWhereOrNull((e) => !e.currentTimeZone.abbreviation.contains("0")) ?? locations.firstOrNull;
|
||||
if (location != null) {
|
||||
return _TimeZoneOffset.fromLocation(location);
|
||||
}
|
||||
|
|
@ -86,11 +75,7 @@ class _DateTimePicker extends HookWidget {
|
|||
(timezone) => DropdownMenuEntry<_TimeZoneOffset>(
|
||||
value: timezone,
|
||||
label: timezone.display,
|
||||
style: ButtonStyle(
|
||||
textStyle: WidgetStatePropertyAll(
|
||||
context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
style: ButtonStyle(textStyle: WidgetStatePropertyAll(context.textTheme.bodyMedium)),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
|
@ -109,10 +94,7 @@ class _DateTimePicker extends HookWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
final newTime = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: TimeOfDay.fromDateTime(date.value),
|
||||
);
|
||||
final newTime = await showTimePicker(context: context, initialTime: TimeOfDay.fromDateTime(date.value));
|
||||
|
||||
if (newTime == null) {
|
||||
return;
|
||||
|
|
@ -145,10 +127,7 @@ class _DateTimePicker extends HookWidget {
|
|||
onPressed: popWithDateTime,
|
||||
child: Text(
|
||||
"action_common_update",
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor),
|
||||
).tr(),
|
||||
),
|
||||
],
|
||||
|
|
@ -156,46 +135,22 @@ class _DateTimePicker extends HookWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
"date_and_time",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
).tr(),
|
||||
const Text("date_and_time", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)).tr(),
|
||||
const SizedBox(height: 32),
|
||||
ListTile(
|
||||
tileColor: context.colorScheme.surfaceContainerHighest,
|
||||
shape: ShapeBorder.lerp(
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10),
|
||||
),
|
||||
),
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10),
|
||||
),
|
||||
),
|
||||
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
1,
|
||||
),
|
||||
trailing: Icon(
|
||||
Icons.edit_outlined,
|
||||
size: 18,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
title: Text(
|
||||
DateFormat("dd-MM-yyyy hh:mm a").format(date.value),
|
||||
style: context.textTheme.bodyMedium,
|
||||
).tr(),
|
||||
trailing: Icon(Icons.edit_outlined, size: 18, color: context.primaryColor),
|
||||
title: Text(DateFormat("dd-MM-yyyy hh:mm a").format(date.value), style: context.textTheme.bodyMedium).tr(),
|
||||
onTap: pickDate,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
DropdownSearchMenu(
|
||||
trailingIcon: Icon(
|
||||
Icons.arrow_drop_down,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
trailingIcon: Icon(Icons.arrow_drop_down, color: context.primaryColor),
|
||||
hintText: "timezone".tr(),
|
||||
label: const Text('timezone').tr(),
|
||||
textStyle: context.textTheme.bodyMedium,
|
||||
|
|
@ -213,26 +168,17 @@ class _TimeZoneOffset implements Comparable<_TimeZoneOffset> {
|
|||
final String display;
|
||||
final Location location;
|
||||
|
||||
const _TimeZoneOffset({
|
||||
required this.display,
|
||||
required this.location,
|
||||
});
|
||||
const _TimeZoneOffset({required this.display, required this.location});
|
||||
|
||||
_TimeZoneOffset copyWith({
|
||||
String? display,
|
||||
Location? location,
|
||||
}) {
|
||||
return _TimeZoneOffset(
|
||||
display: display ?? this.display,
|
||||
location: location ?? this.location,
|
||||
);
|
||||
_TimeZoneOffset copyWith({String? display, Location? location}) {
|
||||
return _TimeZoneOffset(display: display ?? this.display, location: location ?? this.location);
|
||||
}
|
||||
|
||||
int get offsetInMilliseconds => location.currentTimeZone.offset;
|
||||
|
||||
_TimeZoneOffset.fromLocation(tz.Location l)
|
||||
: display = _getFormattedOffset(l.currentTimeZone.offset, l),
|
||||
location = l;
|
||||
: display = _getFormattedOffset(l.currentTimeZone.offset, l),
|
||||
location = l;
|
||||
|
||||
@override
|
||||
int compareTo(_TimeZoneOffset other) {
|
||||
|
|
|
|||
|
|
@ -11,12 +11,7 @@ class DelayedLoadingIndicator extends StatelessWidget {
|
|||
/// An optional fade in duration to animate the loading
|
||||
final Duration? fadeInDuration;
|
||||
|
||||
const DelayedLoadingIndicator({
|
||||
super.key,
|
||||
this.delay = const Duration(seconds: 3),
|
||||
this.child,
|
||||
this.fadeInDuration,
|
||||
});
|
||||
const DelayedLoadingIndicator({super.key, this.delay = const Duration(seconds: 3), this.child, this.fadeInDuration});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -25,18 +20,12 @@ class DelayedLoadingIndicator extends StatelessWidget {
|
|||
builder: (context, snapshot) {
|
||||
late Widget c;
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
c = child ??
|
||||
const ImmichLoadingIndicator(
|
||||
key: ValueKey('loading'),
|
||||
);
|
||||
c = child ?? const ImmichLoadingIndicator(key: ValueKey('loading'));
|
||||
} else {
|
||||
c = Container(key: const ValueKey('hiding'));
|
||||
}
|
||||
|
||||
return AnimatedSwitcher(
|
||||
duration: fadeInDuration ?? Duration.zero,
|
||||
child: c,
|
||||
);
|
||||
return AnimatedSwitcher(duration: fadeInDuration ?? Duration.zero, child: c);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,13 +18,7 @@ class CustomDraggingHandle extends StatelessWidget {
|
|||
}
|
||||
|
||||
class ControlBoxButton extends StatelessWidget {
|
||||
const ControlBoxButton({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.iconData,
|
||||
this.onPressed,
|
||||
this.onLongPressed,
|
||||
});
|
||||
const ControlBoxButton({super.key, required this.label, required this.iconData, this.onPressed, this.onLongPressed});
|
||||
|
||||
final String label;
|
||||
final IconData iconData;
|
||||
|
|
@ -37,9 +31,7 @@ class ControlBoxButton extends StatelessWidget {
|
|||
|
||||
return MaterialButton(
|
||||
padding: const EdgeInsets.all(10),
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
onPressed: onPressed,
|
||||
onLongPress: onLongPressed,
|
||||
minWidth: minWidth,
|
||||
|
|
|
|||
|
|
@ -34,13 +34,8 @@ class DropdownSearchMenu<T> extends HookWidget {
|
|||
);
|
||||
final showTimeZoneDropdown = useState<bool>(false);
|
||||
|
||||
final effectiveConstraints = menuConstraints ??
|
||||
const BoxConstraints(
|
||||
minWidth: 280,
|
||||
maxWidth: 280,
|
||||
minHeight: 0,
|
||||
maxHeight: 280,
|
||||
);
|
||||
final effectiveConstraints =
|
||||
menuConstraints ?? const BoxConstraints(minWidth: 280, maxWidth: 280, minHeight: 0, maxHeight: 280);
|
||||
|
||||
final inputDecoration = InputDecoration(
|
||||
contentPadding: const EdgeInsets.fromLTRB(12, 4, 12, 4),
|
||||
|
|
@ -58,12 +53,7 @@ class DropdownSearchMenu<T> extends HookWidget {
|
|||
child: InputDecorator(
|
||||
decoration: inputDecoration,
|
||||
child: selectedItem.value != null
|
||||
? Text(
|
||||
selectedItem.value!.label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: textStyle,
|
||||
)
|
||||
? Text(selectedItem.value!.label, maxLines: 1, overflow: TextOverflow.ellipsis, style: textStyle)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
|
|
@ -89,9 +79,7 @@ class DropdownSearchMenu<T> extends HookWidget {
|
|||
autofocus: true,
|
||||
focusNode: focusNode,
|
||||
controller: textEditingController,
|
||||
decoration: inputDecoration.copyWith(
|
||||
hintText: "search_timezone".tr(),
|
||||
),
|
||||
decoration: inputDecoration.copyWith(hintText: "search_timezone".tr()),
|
||||
maxLines: 1,
|
||||
style: context.textTheme.bodyMedium,
|
||||
expands: false,
|
||||
|
|
@ -125,23 +113,14 @@ class DropdownSearchMenu<T> extends HookWidget {
|
|||
builder: (BuildContext context) {
|
||||
final bool highlight = AutocompleteHighlightedOption.of(context) == index;
|
||||
if (highlight) {
|
||||
SchedulerBinding.instance.addPostFrameCallback(
|
||||
(Duration timeStamp) {
|
||||
Scrollable.ensureVisible(
|
||||
context,
|
||||
alignment: 0.5,
|
||||
);
|
||||
},
|
||||
debugLabel: 'AutocompleteOptions.ensureVisible',
|
||||
);
|
||||
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
|
||||
Scrollable.ensureVisible(context, alignment: 0.5);
|
||||
}, debugLabel: 'AutocompleteOptions.ensureVisible');
|
||||
}
|
||||
return Container(
|
||||
color: highlight ? Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.12) : null,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
option.label,
|
||||
style: textStyle,
|
||||
),
|
||||
child: Text(option.label, style: textStyle),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -22,12 +22,7 @@ class FadeInPlaceholderImage extends StatelessWidget {
|
|||
fit: StackFit.expand,
|
||||
children: [
|
||||
placeholder,
|
||||
FadeInImage(
|
||||
fadeInDuration: duration,
|
||||
image: image,
|
||||
fit: fit,
|
||||
placeholder: MemoryImage(kTransparentImage),
|
||||
),
|
||||
FadeInImage(fadeInDuration: duration, image: image, fit: fit, placeholder: MemoryImage(kTransparentImage)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -36,23 +36,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
|
||||
buildProfileIndicator() {
|
||||
return InkWell(
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (ctx) => const ImmichAppBarDialog(),
|
||||
),
|
||||
onTap: () =>
|
||||
showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
child: Badge(
|
||||
label: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black,
|
||||
borderRadius: BorderRadius.circular(widgetSize / 2),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.info,
|
||||
color: Color.fromARGB(255, 243, 188, 106),
|
||||
size: widgetSize / 2,
|
||||
),
|
||||
decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)),
|
||||
child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2),
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
alignment: Alignment.bottomRight,
|
||||
|
|
@ -60,17 +50,10 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable),
|
||||
offset: const Offset(-2, -12),
|
||||
child: user == null
|
||||
? const Icon(
|
||||
Icons.face_outlined,
|
||||
size: widgetSize,
|
||||
)
|
||||
? const Icon(Icons.face_outlined, size: widgetSize)
|
||||
: Semantics(
|
||||
label: "logged_in_as".tr(namedArgs: {"user": user.name}),
|
||||
child: UserCircleAvatar(
|
||||
radius: 17,
|
||||
size: 31,
|
||||
user: user,
|
||||
),
|
||||
child: UserCircleAvatar(radius: 17, size: 31, user: user),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -124,9 +107,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
height: widgetSize / 2,
|
||||
decoration: BoxDecoration(
|
||||
color: badgeBackground,
|
||||
border: Border.all(
|
||||
color: context.colorScheme.outline.withValues(alpha: .3),
|
||||
),
|
||||
border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)),
|
||||
borderRadius: BorderRadius.circular(widgetSize / 2),
|
||||
),
|
||||
child: indicatorIcon,
|
||||
|
|
@ -135,22 +116,14 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
alignment: Alignment.bottomRight,
|
||||
isLabelVisible: indicatorIcon != null,
|
||||
offset: const Offset(-2, -12),
|
||||
child: Icon(
|
||||
Icons.backup_rounded,
|
||||
size: widgetSize,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return AppBar(
|
||||
backgroundColor: context.themeData.appBarTheme.backgroundColor,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(5),
|
||||
),
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))),
|
||||
automaticallyImplyLeading: false,
|
||||
centerTitle: false,
|
||||
title: Builder(
|
||||
|
|
@ -176,12 +149,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
),
|
||||
actions: [
|
||||
if (actions != null)
|
||||
...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)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.science_rounded),
|
||||
|
|
@ -192,25 +160,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
padding: const EdgeInsets.only(right: 12),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => const CastDialog(),
|
||||
);
|
||||
showDialog(context: context, builder: (context) => const CastDialog());
|
||||
},
|
||||
icon: Icon(
|
||||
isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded,
|
||||
),
|
||||
icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded),
|
||||
),
|
||||
),
|
||||
if (showUploadButton)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 20),
|
||||
child: buildBackupIndicator(),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 20),
|
||||
child: buildProfileIndicator(),
|
||||
),
|
||||
if (showUploadButton) Padding(padding: const EdgeInsets.only(right: 20), child: buildBackupIndicator()),
|
||||
Padding(padding: const EdgeInsets.only(right: 20), child: buildProfileIndicator()),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,32 +28,19 @@ class ImmichImage extends StatelessWidget {
|
|||
// either by using the asset ID or the asset itself
|
||||
/// [asset] is the Asset to request, or else use [assetId] to get a remote
|
||||
/// image provider
|
||||
static ImageProvider imageProvider({
|
||||
Asset? asset,
|
||||
String? assetId,
|
||||
double width = 1080,
|
||||
double height = 1920,
|
||||
}) {
|
||||
static ImageProvider imageProvider({Asset? asset, String? assetId, double width = 1080, double height = 1920}) {
|
||||
if (asset == null && assetId == null) {
|
||||
throw Exception('Must supply either asset or assetId');
|
||||
}
|
||||
|
||||
if (asset == null) {
|
||||
return ImmichRemoteImageProvider(
|
||||
assetId: assetId!,
|
||||
);
|
||||
return ImmichRemoteImageProvider(assetId: assetId!);
|
||||
}
|
||||
|
||||
if (useLocal(asset)) {
|
||||
return ImmichLocalImageProvider(
|
||||
asset: asset,
|
||||
width: width,
|
||||
height: height,
|
||||
);
|
||||
return ImmichLocalImageProvider(asset: asset, width: width, height: height);
|
||||
} else {
|
||||
return ImmichRemoteImageProvider(
|
||||
assetId: asset.remoteId!,
|
||||
);
|
||||
return ImmichRemoteImageProvider(assetId: asset.remoteId!);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -68,17 +55,11 @@ class ImmichImage extends StatelessWidget {
|
|||
color: Colors.grey,
|
||||
width: width,
|
||||
height: height,
|
||||
child: const Center(
|
||||
child: Icon(Icons.no_photography),
|
||||
),
|
||||
child: const Center(child: Icon(Icons.no_photography)),
|
||||
);
|
||||
}
|
||||
|
||||
final imageProviderInstance = ImmichImage.imageProvider(
|
||||
asset: asset,
|
||||
width: context.width,
|
||||
height: context.height,
|
||||
);
|
||||
final imageProviderInstance = ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height);
|
||||
|
||||
return OctoImage(
|
||||
fadeInDuration: const Duration(milliseconds: 0),
|
||||
|
|
@ -96,11 +77,7 @@ class ImmichImage extends StatelessWidget {
|
|||
errorBuilder: (context, error, stackTrace) {
|
||||
imageProviderInstance.evict();
|
||||
|
||||
return Icon(
|
||||
Icons.image_not_supported_outlined,
|
||||
size: 32,
|
||||
color: Colors.red[200],
|
||||
);
|
||||
return Icon(Icons.image_not_supported_outlined, size: 32, color: Colors.red[200]);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,22 +5,15 @@ import 'package:immich_mobile/widgets/common/immich_logo.dart';
|
|||
class ImmichLoadingIndicator extends HookWidget {
|
||||
final double? borderRadius;
|
||||
|
||||
const ImmichLoadingIndicator({
|
||||
super.key,
|
||||
this.borderRadius,
|
||||
});
|
||||
const ImmichLoadingIndicator({super.key, this.borderRadius});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final logoAnimationController = useAnimationController(
|
||||
duration: const Duration(seconds: 6),
|
||||
)
|
||||
final logoAnimationController = useAnimationController(duration: const Duration(seconds: 6))
|
||||
..reverse()
|
||||
..repeat();
|
||||
|
||||
final borderAnimationController = useAnimationController(
|
||||
duration: const Duration(seconds: 6),
|
||||
)..repeat();
|
||||
final borderAnimationController = useAnimationController(duration: const Duration(seconds: 6))..repeat();
|
||||
|
||||
return Container(
|
||||
height: 80,
|
||||
|
|
@ -34,10 +27,7 @@ class ImmichLoadingIndicator extends HookWidget {
|
|||
animation: borderAnimationController,
|
||||
builder: (context, child) {
|
||||
return CustomPaint(
|
||||
painter: GradientBorderPainter(
|
||||
animation: borderAnimationController.value,
|
||||
strokeWidth: 3,
|
||||
),
|
||||
painter: GradientBorderPainter(animation: borderAnimationController.value, strokeWidth: 3),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
|
|
@ -45,9 +35,7 @@ class ImmichLoadingIndicator extends HookWidget {
|
|||
padding: const EdgeInsets.all(15),
|
||||
child: RotationTransition(
|
||||
turns: logoAnimationController,
|
||||
child: const ImmichLogo(
|
||||
heroTag: 'logo',
|
||||
),
|
||||
child: const ImmichLogo(heroTag: 'logo'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -67,10 +55,7 @@ class GradientBorderPainter extends CustomPainter {
|
|||
const Color(0xFF18C249),
|
||||
];
|
||||
|
||||
GradientBorderPainter({
|
||||
required this.animation,
|
||||
required this.strokeWidth,
|
||||
});
|
||||
GradientBorderPainter({required this.animation, required this.strokeWidth});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
|
|
@ -96,10 +81,7 @@ class GradientBorderPainter extends CustomPainter {
|
|||
colors.first.withValues(alpha: opacity),
|
||||
],
|
||||
// Add evenly distributed stops
|
||||
stops: List.generate(
|
||||
colors.length + 1,
|
||||
(index) => index / colors.length,
|
||||
),
|
||||
stops: List.generate(colors.length + 1, (index) => index / colors.length),
|
||||
tileMode: TileMode.clamp,
|
||||
// Use transformations to rotate the gradient
|
||||
transform: GradientRotation(-animation * 2 * 3.14159),
|
||||
|
|
|
|||
|
|
@ -4,11 +4,7 @@ class ImmichLogo extends StatelessWidget {
|
|||
final double size;
|
||||
final dynamic heroTag;
|
||||
|
||||
const ImmichLogo({
|
||||
super.key,
|
||||
this.size = 100,
|
||||
this.heroTag,
|
||||
});
|
||||
const ImmichLogo({super.key, this.size = 100, this.heroTag});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
|||
|
|
@ -52,11 +52,7 @@ class ImmichSliverAppBar extends ConsumerWidget {
|
|||
pinned: pinned,
|
||||
snap: snap,
|
||||
expandedHeight: expandedHeight,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(5),
|
||||
),
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))),
|
||||
automaticallyImplyLeading: false,
|
||||
centerTitle: false,
|
||||
title: title ?? const _ImmichLogoWithText(),
|
||||
|
|
@ -66,38 +62,21 @@ class ImmichSliverAppBar extends ConsumerWidget {
|
|||
padding: const EdgeInsets.only(right: 12),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => const CastDialog(),
|
||||
);
|
||||
showDialog(context: context, builder: (context) => const CastDialog());
|
||||
},
|
||||
icon: Icon(
|
||||
isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded,
|
||||
),
|
||||
icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded),
|
||||
),
|
||||
),
|
||||
const _SyncStatusIndicator(),
|
||||
if (actions != null)
|
||||
...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)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.science_rounded),
|
||||
onPressed: () => context.pushRoute(const FeatInDevRoute()),
|
||||
),
|
||||
if (showUploadButton)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 20),
|
||||
child: _BackupIndicator(),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 20),
|
||||
child: _ProfileIndicator(),
|
||||
),
|
||||
if (showUploadButton) const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()),
|
||||
const Padding(padding: EdgeInsets.only(right: 20), child: _ProfileIndicator()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
@ -159,23 +138,12 @@ class _ProfileIndicator extends ConsumerWidget {
|
|||
const widgetSize = 30.0;
|
||||
|
||||
return InkWell(
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (ctx) => const ImmichAppBarDialog(),
|
||||
),
|
||||
onTap: () => showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
child: Badge(
|
||||
label: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black,
|
||||
borderRadius: BorderRadius.circular(widgetSize / 2),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.info,
|
||||
color: Color.fromARGB(255, 243, 188, 106),
|
||||
size: widgetSize / 2,
|
||||
),
|
||||
decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)),
|
||||
child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2),
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
alignment: Alignment.bottomRight,
|
||||
|
|
@ -183,17 +151,10 @@ class _ProfileIndicator extends ConsumerWidget {
|
|||
serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable),
|
||||
offset: const Offset(-2, -12),
|
||||
child: user == null
|
||||
? const Icon(
|
||||
Icons.face_outlined,
|
||||
size: widgetSize,
|
||||
)
|
||||
? const Icon(Icons.face_outlined, size: widgetSize)
|
||||
: Semantics(
|
||||
label: "logged_in_as".tr(namedArgs: {"user": user.name}),
|
||||
child: UserCircleAvatar(
|
||||
radius: 17,
|
||||
size: 31,
|
||||
user: user,
|
||||
),
|
||||
child: UserCircleAvatar(radius: 17, size: 31, user: user),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -218,9 +179,7 @@ class _BackupIndicator extends ConsumerWidget {
|
|||
height: widgetSize / 2,
|
||||
decoration: BoxDecoration(
|
||||
color: badgeBackground,
|
||||
border: Border.all(
|
||||
color: context.colorScheme.outline.withValues(alpha: .3),
|
||||
),
|
||||
border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)),
|
||||
borderRadius: BorderRadius.circular(widgetSize / 2),
|
||||
),
|
||||
child: indicatorIcon,
|
||||
|
|
@ -229,11 +188,7 @@ class _BackupIndicator extends ConsumerWidget {
|
|||
alignment: Alignment.bottomRight,
|
||||
isLabelVisible: indicatorIcon != null,
|
||||
offset: const Offset(-2, -12),
|
||||
child: Icon(
|
||||
Icons.backup_rounded,
|
||||
size: widgetSize,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -263,8 +218,9 @@ class _BackupIndicator extends ConsumerWidget {
|
|||
return Container(
|
||||
padding: const EdgeInsets.all(3.5),
|
||||
child: Theme(
|
||||
data: context.themeData
|
||||
.copyWith(progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true)),
|
||||
data: context.themeData.copyWith(
|
||||
progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true),
|
||||
),
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
strokeCap: StrokeCap.round,
|
||||
|
|
@ -302,27 +258,13 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_rotationController = AnimationController(
|
||||
duration: const Duration(seconds: 2),
|
||||
vsync: this,
|
||||
);
|
||||
_dismissalController = AnimationController(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
vsync: this,
|
||||
);
|
||||
_rotationAnimation = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
).animate(_rotationController);
|
||||
_rotationController = AnimationController(duration: const Duration(seconds: 2), vsync: this);
|
||||
_dismissalController = AnimationController(duration: const Duration(milliseconds: 300), vsync: this);
|
||||
_rotationAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(_rotationController);
|
||||
_dismissalAnimation = Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 0.0,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _dismissalController,
|
||||
curve: Curves.easeOutQuart,
|
||||
),
|
||||
);
|
||||
).animate(CurvedAnimation(parent: _dismissalController, curve: Curves.easeOutQuart));
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -366,11 +308,7 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with
|
|||
opacity: isSyncing ? 1.0 : _dismissalAnimation.value,
|
||||
child: Transform.rotate(
|
||||
angle: _rotationAnimation.value * 2 * 3.14159 * -1, // Rotate counter-clockwise
|
||||
child: Icon(
|
||||
Icons.sync,
|
||||
size: 24,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
child: Icon(Icons.sync, size: 24, color: context.primaryColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -13,13 +13,7 @@ import 'package:octo_image/octo_image.dart';
|
|||
import 'package:immich_mobile/providers/user.provider.dart';
|
||||
|
||||
class ImmichThumbnail extends HookConsumerWidget {
|
||||
const ImmichThumbnail({
|
||||
this.asset,
|
||||
this.width = 250,
|
||||
this.height = 250,
|
||||
this.fit = BoxFit.cover,
|
||||
super.key,
|
||||
});
|
||||
const ImmichThumbnail({this.asset, this.width = 250, this.height = 250, this.fit = BoxFit.cover, super.key});
|
||||
|
||||
final Asset? asset;
|
||||
final double width;
|
||||
|
|
@ -30,35 +24,19 @@ class ImmichThumbnail extends HookConsumerWidget {
|
|||
/// either by using the asset ID or the asset itself
|
||||
/// [asset] is the Asset to request, or else use [assetId] to get a remote
|
||||
/// image provider
|
||||
static ImageProvider imageProvider({
|
||||
Asset? asset,
|
||||
String? assetId,
|
||||
String? userId,
|
||||
int thumbnailSize = 256,
|
||||
}) {
|
||||
static ImageProvider imageProvider({Asset? asset, String? assetId, String? userId, int thumbnailSize = 256}) {
|
||||
if (asset == null && assetId == null) {
|
||||
throw Exception('Must supply either asset or assetId');
|
||||
}
|
||||
|
||||
if (asset == null) {
|
||||
return ImmichRemoteThumbnailProvider(
|
||||
assetId: assetId!,
|
||||
);
|
||||
return ImmichRemoteThumbnailProvider(assetId: assetId!);
|
||||
}
|
||||
|
||||
if (ImmichImage.useLocal(asset)) {
|
||||
return ImmichLocalThumbnailProvider(
|
||||
asset: asset,
|
||||
height: thumbnailSize,
|
||||
width: thumbnailSize,
|
||||
userId: userId,
|
||||
);
|
||||
return ImmichLocalThumbnailProvider(asset: asset, height: thumbnailSize, width: thumbnailSize, userId: userId);
|
||||
} else {
|
||||
return ImmichRemoteThumbnailProvider(
|
||||
assetId: asset.remoteId!,
|
||||
height: thumbnailSize,
|
||||
width: thumbnailSize,
|
||||
);
|
||||
return ImmichRemoteThumbnailProvider(assetId: asset.remoteId!, height: thumbnailSize, width: thumbnailSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,23 +50,13 @@ class ImmichThumbnail extends HookConsumerWidget {
|
|||
color: Colors.grey,
|
||||
width: width,
|
||||
height: height,
|
||||
child: const Center(
|
||||
child: Icon(Icons.no_photography),
|
||||
),
|
||||
child: const Center(child: Icon(Icons.no_photography)),
|
||||
);
|
||||
}
|
||||
|
||||
final assetAltText = getAltText(
|
||||
asset!.exifInfo,
|
||||
asset!.fileCreatedAt,
|
||||
asset!.type,
|
||||
[],
|
||||
);
|
||||
final assetAltText = getAltText(asset!.exifInfo, asset!.fileCreatedAt, asset!.type, []);
|
||||
|
||||
final thumbnailProviderInstance = ImmichThumbnail.imageProvider(
|
||||
asset: asset,
|
||||
userId: userId,
|
||||
);
|
||||
final thumbnailProviderInstance = ImmichThumbnail.imageProvider(asset: asset, userId: userId);
|
||||
|
||||
customErrorBuilder(BuildContext ctx, Object error, StackTrace? stackTrace) {
|
||||
thumbnailProviderInstance.evict();
|
||||
|
|
|
|||
|
|
@ -5,18 +5,12 @@ class ImmichTitleText extends StatelessWidget {
|
|||
final double fontSize;
|
||||
final Color? color;
|
||||
|
||||
const ImmichTitleText({
|
||||
super.key,
|
||||
this.fontSize = 48,
|
||||
this.color,
|
||||
});
|
||||
const ImmichTitleText({super.key, this.fontSize = 48, this.color});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Image(
|
||||
image: AssetImage(
|
||||
context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png',
|
||||
),
|
||||
image: AssetImage(context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png'),
|
||||
width: fontSize * 4,
|
||||
filterQuality: FilterQuality.high,
|
||||
color: context.primaryColor,
|
||||
|
|
|
|||
|
|
@ -16,25 +16,16 @@ class ImmichToast {
|
|||
fToast.init(context);
|
||||
|
||||
Color getColor(ToastType type, BuildContext context) => switch (type) {
|
||||
ToastType.info => context.primaryColor,
|
||||
ToastType.success => const Color.fromARGB(255, 78, 140, 124),
|
||||
ToastType.error => const Color.fromARGB(255, 220, 48, 85),
|
||||
};
|
||||
ToastType.info => context.primaryColor,
|
||||
ToastType.success => const Color.fromARGB(255, 78, 140, 124),
|
||||
ToastType.error => const Color.fromARGB(255, 220, 48, 85),
|
||||
};
|
||||
|
||||
Icon getIcon(ToastType type) => switch (type) {
|
||||
ToastType.info => Icon(
|
||||
Icons.info_outline_rounded,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
ToastType.success => const Icon(
|
||||
Icons.check_circle_rounded,
|
||||
color: Color.fromARGB(255, 78, 140, 124),
|
||||
),
|
||||
ToastType.error => const Icon(
|
||||
Icons.error_outline_rounded,
|
||||
color: Color.fromARGB(255, 240, 162, 156),
|
||||
),
|
||||
};
|
||||
ToastType.info => Icon(Icons.info_outline_rounded, color: context.primaryColor),
|
||||
ToastType.success => const Icon(Icons.check_circle_rounded, color: Color.fromARGB(255, 78, 140, 124)),
|
||||
ToastType.error => const Icon(Icons.error_outline_rounded, color: Color.fromARGB(255, 240, 162, 156)),
|
||||
};
|
||||
|
||||
fToast.showToast(
|
||||
child: Container(
|
||||
|
|
@ -42,26 +33,17 @@ class ImmichToast {
|
|||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(16.0)),
|
||||
color: context.colorScheme.surfaceContainer,
|
||||
border: Border.all(
|
||||
color: context.colorScheme.outline.withValues(alpha: .5),
|
||||
width: 1,
|
||||
),
|
||||
border: Border.all(color: context.colorScheme.outline.withValues(alpha: .5), width: 1),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
getIcon(toastType),
|
||||
const SizedBox(
|
||||
width: 12.0,
|
||||
),
|
||||
const SizedBox(width: 12.0),
|
||||
Flexible(
|
||||
child: Text(
|
||||
msg,
|
||||
style: TextStyle(
|
||||
color: getColor(toastType, context),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
),
|
||||
style: TextStyle(color: getColor(toastType, context), fontWeight: FontWeight.w600, fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -12,14 +12,10 @@ class LocalAlbumsSliverAppBar extends StatelessWidget {
|
|||
pinned: true,
|
||||
snap: false,
|
||||
backgroundColor: context.colorScheme.surfaceContainer,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))),
|
||||
automaticallyImplyLeading: true,
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
"on_this_device".t(context: context),
|
||||
),
|
||||
title: Text("on_this_device".t(context: context)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,16 +9,11 @@ import 'package:immich_mobile/widgets/map/map_thumbnail.dart';
|
|||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||
|
||||
Future<LatLng?> showLocationPicker({
|
||||
required BuildContext context,
|
||||
LatLng? initialLatLng,
|
||||
}) {
|
||||
Future<LatLng?> showLocationPicker({required BuildContext context, LatLng? initialLatLng}) {
|
||||
return showDialog<LatLng?>(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (ctx) => _LocationPicker(
|
||||
initialLatLng: initialLatLng,
|
||||
),
|
||||
builder: (ctx) => _LocationPicker(initialLatLng: initialLatLng),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -27,9 +22,7 @@ enum _LocationPickerMode { map, manual }
|
|||
class _LocationPicker extends HookWidget {
|
||||
final LatLng? initialLatLng;
|
||||
|
||||
const _LocationPicker({
|
||||
this.initialLatLng,
|
||||
});
|
||||
const _LocationPicker({this.initialLatLng});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -39,9 +32,7 @@ class _LocationPicker extends HookWidget {
|
|||
final pickerMode = useState(_LocationPickerMode.map);
|
||||
|
||||
Future<void> onMapTap() async {
|
||||
final newLatLng = await context.pushRoute<LatLng?>(
|
||||
MapLocationPickerRoute(initialLatLng: latlng),
|
||||
);
|
||||
final newLatLng = await context.pushRoute<LatLng?>(MapLocationPickerRoute(initialLatLng: latlng));
|
||||
if (newLatLng != null) {
|
||||
latitude.value = newLatLng.latitude;
|
||||
longitude.value = newLatLng.longitude;
|
||||
|
|
@ -81,10 +72,7 @@ class _LocationPicker extends HookWidget {
|
|||
onPressed: () => context.maybePop(latlng),
|
||||
child: Text(
|
||||
"action_common_update",
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor),
|
||||
).tr(),
|
||||
),
|
||||
],
|
||||
|
|
@ -129,10 +117,7 @@ class _ManualPickerInput extends HookWidget {
|
|||
autofocus: false,
|
||||
decoration: InputDecoration(
|
||||
labelText: decorationText.tr(),
|
||||
labelStyle: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
labelStyle: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.auto,
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: hintText.tr(),
|
||||
|
|
@ -188,10 +173,7 @@ class _ManualPicker extends HookWidget {
|
|||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
"edit_location_dialog_title",
|
||||
textAlign: TextAlign.center,
|
||||
).tr(),
|
||||
const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(),
|
||||
const SizedBox(height: 12),
|
||||
TextButton.icon(
|
||||
icon: const Text("location_picker_choose_on_map").tr(),
|
||||
|
|
@ -228,27 +210,17 @@ class _MapPicker extends StatelessWidget {
|
|||
final Function() onModeSwitch;
|
||||
final Function() onMapTap;
|
||||
|
||||
const _MapPicker({
|
||||
required this.latlng,
|
||||
required this.onModeSwitch,
|
||||
required this.onMapTap,
|
||||
super.key,
|
||||
});
|
||||
const _MapPicker({required this.latlng, required this.onModeSwitch, required this.onMapTap, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
"edit_location_dialog_title",
|
||||
textAlign: TextAlign.center,
|
||||
).tr(),
|
||||
const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(),
|
||||
const SizedBox(height: 12),
|
||||
TextButton.icon(
|
||||
icon: Text(
|
||||
"${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}",
|
||||
),
|
||||
icon: Text("${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}"),
|
||||
label: const Icon(Icons.edit_outlined, size: 16),
|
||||
onPressed: onModeSwitch,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -14,11 +14,7 @@ import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
|||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||
|
||||
class MesmerizingSliverAppBar extends ConsumerStatefulWidget {
|
||||
const MesmerizingSliverAppBar({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.icon = Icons.camera,
|
||||
});
|
||||
const MesmerizingSliverAppBar({super.key, required this.title, this.icon = Icons.camera});
|
||||
|
||||
final String title;
|
||||
final IconData icon;
|
||||
|
|
@ -62,23 +58,11 @@ class _MesmerizingSliverAppBarState extends ConsumerState<MesmerizingSliverAppBa
|
|||
leading: IconButton(
|
||||
icon: Icon(
|
||||
Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back,
|
||||
color: Color.lerp(
|
||||
Colors.white,
|
||||
context.primaryColor,
|
||||
_scrollProgress,
|
||||
),
|
||||
color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress),
|
||||
shadows: [
|
||||
_scrollProgress < 0.95
|
||||
? Shadow(
|
||||
offset: const Offset(0, 2),
|
||||
blurRadius: 5,
|
||||
color: Colors.black.withValues(alpha: 0.5),
|
||||
)
|
||||
: const Shadow(
|
||||
offset: Offset(0, 2),
|
||||
blurRadius: 0,
|
||||
color: Colors.transparent,
|
||||
),
|
||||
? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5))
|
||||
: const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
|
|
@ -106,11 +90,7 @@ class _MesmerizingSliverAppBarState extends ConsumerState<MesmerizingSliverAppBa
|
|||
child: scrollProgress > 0.95
|
||||
? Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: context.primaryColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
),
|
||||
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
|
|
@ -131,11 +111,7 @@ class _ExpandedBackground extends ConsumerStatefulWidget {
|
|||
final String title;
|
||||
final IconData icon;
|
||||
|
||||
const _ExpandedBackground({
|
||||
required this.scrollProgress,
|
||||
required this.title,
|
||||
required this.icon,
|
||||
});
|
||||
const _ExpandedBackground({required this.scrollProgress, required this.title, required this.icon});
|
||||
|
||||
@override
|
||||
ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState();
|
||||
|
|
@ -149,20 +125,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_slideController = AnimationController(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
vsync: this,
|
||||
);
|
||||
_slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this);
|
||||
|
||||
_slideAnimation = Tween<Offset>(
|
||||
begin: const Offset(0, 1.5),
|
||||
end: Offset.zero,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _slideController,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
);
|
||||
).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic));
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
if (mounted) {
|
||||
|
|
@ -188,10 +156,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
|
|||
offset: Offset(0, widget.scrollProgress * 50),
|
||||
child: Transform.scale(
|
||||
scale: 1.4 - (widget.scrollProgress * 0.2),
|
||||
child: _RandomAssetBackground(
|
||||
timelineService: timelineService,
|
||||
icon: widget.icon,
|
||||
),
|
||||
child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
|
|
@ -202,9 +167,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
|
|||
colors: [
|
||||
Colors.transparent,
|
||||
Colors.transparent,
|
||||
Colors.black.withValues(
|
||||
alpha: 0.6 + (widget.scrollProgress * 0.2),
|
||||
),
|
||||
Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.2)),
|
||||
],
|
||||
stops: const [0.0, 0.65, 1.0],
|
||||
),
|
||||
|
|
@ -232,21 +195,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
|
|||
fontSize: 36,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 0.5,
|
||||
shadows: [
|
||||
Shadow(
|
||||
offset: Offset(0, 2),
|
||||
blurRadius: 12,
|
||||
color: Colors.black45,
|
||||
),
|
||||
],
|
||||
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black45)],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: const _ItemCountText(),
|
||||
),
|
||||
AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -280,26 +234,15 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final assetCount = ref.watch(
|
||||
timelineServiceProvider.select((s) => s.totalAssets),
|
||||
);
|
||||
final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets));
|
||||
|
||||
return Text(
|
||||
'items_count'.t(
|
||||
context: context,
|
||||
args: {"count": assetCount},
|
||||
),
|
||||
'items_count'.t(context: context, args: {"count": assetCount}),
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
// letterSpacing: 0.2,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
shadows: [
|
||||
const Shadow(
|
||||
offset: Offset(0, 1),
|
||||
blurRadius: 6,
|
||||
color: Colors.black45,
|
||||
),
|
||||
],
|
||||
shadows: [const Shadow(offset: Offset(0, 1), blurRadius: 6, color: Colors.black45)],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -309,10 +252,7 @@ class _RandomAssetBackground extends StatefulWidget {
|
|||
final TimelineService timelineService;
|
||||
final IconData icon;
|
||||
|
||||
const _RandomAssetBackground({
|
||||
required this.timelineService,
|
||||
required this.icon,
|
||||
});
|
||||
const _RandomAssetBackground({required this.timelineService, required this.icon});
|
||||
|
||||
@override
|
||||
State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState();
|
||||
|
|
@ -332,50 +272,26 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_zoomController = AnimationController(
|
||||
duration: const Duration(seconds: 12),
|
||||
vsync: this,
|
||||
);
|
||||
_zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this);
|
||||
|
||||
_crossFadeController = AnimationController(
|
||||
duration: const Duration(milliseconds: 1200),
|
||||
vsync: this,
|
||||
);
|
||||
_crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this);
|
||||
|
||||
_zoomAnimation = Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 1.2,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _zoomController,
|
||||
curve: Curves.easeInOut,
|
||||
),
|
||||
);
|
||||
).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut));
|
||||
|
||||
_panAnimation = Tween<Offset>(
|
||||
begin: Offset.zero,
|
||||
end: const Offset(0.5, -0.5),
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _zoomController,
|
||||
curve: Curves.easeInOut,
|
||||
),
|
||||
);
|
||||
).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut));
|
||||
|
||||
_crossFadeAnimation = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _crossFadeController,
|
||||
curve: Curves.easeInOutCubic,
|
||||
),
|
||||
);
|
||||
).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic));
|
||||
|
||||
Future.delayed(
|
||||
Durations.medium1,
|
||||
() => _loadFirstAsset(),
|
||||
);
|
||||
Future.delayed(Durations.medium1, () => _loadFirstAsset());
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -465,9 +381,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
|
|||
}
|
||||
|
||||
return AnimatedBuilder(
|
||||
animation: Listenable.merge(
|
||||
[_zoomAnimation, _panAnimation, _crossFadeAnimation],
|
||||
),
|
||||
animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]),
|
||||
builder: (context, child) {
|
||||
return Transform.scale(
|
||||
scale: _zoomAnimation.value,
|
||||
|
|
@ -499,11 +413,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
|
|||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: Icon(
|
||||
Icons.error_outline_rounded,
|
||||
size: 24,
|
||||
color: Colors.red[300],
|
||||
),
|
||||
child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -530,11 +440,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
|
|||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: Icon(
|
||||
Icons.error_outline_rounded,
|
||||
size: 24,
|
||||
color: Colors.red[300],
|
||||
),
|
||||
child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -63,25 +63,13 @@ class _MesmerizingSliverAppBarState extends ConsumerState<RemoteAlbumSliverAppBa
|
|||
return const SliverToBoxAdapter(child: SizedBox.shrink());
|
||||
}
|
||||
|
||||
Color? actionIconColor = Color.lerp(
|
||||
Colors.white,
|
||||
context.primaryColor,
|
||||
_scrollProgress,
|
||||
);
|
||||
Color? actionIconColor = Color.lerp(Colors.white, context.primaryColor, _scrollProgress);
|
||||
|
||||
List<Shadow> actionIconShadows = [
|
||||
if (_scrollProgress < 0.95)
|
||||
Shadow(
|
||||
offset: const Offset(0, 2),
|
||||
blurRadius: 5,
|
||||
color: Colors.black.withValues(alpha: 0.5),
|
||||
)
|
||||
Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5))
|
||||
else
|
||||
const Shadow(
|
||||
offset: Offset(0, 2),
|
||||
blurRadius: 0,
|
||||
color: Colors.transparent,
|
||||
),
|
||||
const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent),
|
||||
];
|
||||
|
||||
return isMultiSelectEnabled
|
||||
|
|
@ -111,20 +99,12 @@ class _MesmerizingSliverAppBarState extends ConsumerState<RemoteAlbumSliverAppBa
|
|||
actions: [
|
||||
if (widget.onToggleAlbumOrder != null)
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.swap_vert_rounded,
|
||||
color: actionIconColor,
|
||||
shadows: actionIconShadows,
|
||||
),
|
||||
icon: Icon(Icons.swap_vert_rounded, color: actionIconColor, shadows: actionIconShadows),
|
||||
onPressed: widget.onToggleAlbumOrder,
|
||||
),
|
||||
if (widget.onShowOptions != null)
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.more_vert,
|
||||
color: actionIconColor,
|
||||
shadows: actionIconShadows,
|
||||
),
|
||||
icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows),
|
||||
onPressed: widget.onShowOptions,
|
||||
),
|
||||
],
|
||||
|
|
@ -149,11 +129,7 @@ class _MesmerizingSliverAppBarState extends ConsumerState<RemoteAlbumSliverAppBa
|
|||
child: scrollProgress > 0.95
|
||||
? Text(
|
||||
currentAlbum.name,
|
||||
style: TextStyle(
|
||||
color: context.primaryColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
),
|
||||
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
|
|
@ -174,11 +150,7 @@ class _ExpandedBackground extends ConsumerStatefulWidget {
|
|||
final IconData icon;
|
||||
final void Function()? onEditTitle;
|
||||
|
||||
const _ExpandedBackground({
|
||||
required this.scrollProgress,
|
||||
required this.icon,
|
||||
this.onEditTitle,
|
||||
});
|
||||
const _ExpandedBackground({required this.scrollProgress, required this.icon, this.onEditTitle});
|
||||
|
||||
@override
|
||||
ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState();
|
||||
|
|
@ -192,20 +164,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_slideController = AnimationController(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
vsync: this,
|
||||
);
|
||||
_slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this);
|
||||
|
||||
_slideAnimation = Tween<Offset>(
|
||||
begin: const Offset(0, 1.5),
|
||||
end: Offset.zero,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _slideController,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
);
|
||||
).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic));
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
if (mounted) {
|
||||
|
|
@ -229,9 +193,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
|
|||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final dateRange = ref.watch(
|
||||
remoteAlbumDateRangeProvider(currentAlbum.id),
|
||||
);
|
||||
final dateRange = ref.watch(remoteAlbumDateRangeProvider(currentAlbum.id));
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
|
|
@ -239,18 +201,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
|
|||
offset: Offset(0, widget.scrollProgress * 50),
|
||||
child: Transform.scale(
|
||||
scale: 1.4 - (widget.scrollProgress * 0.2),
|
||||
child: _RandomAssetBackground(
|
||||
timelineService: timelineService,
|
||||
icon: widget.icon,
|
||||
),
|
||||
child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon),
|
||||
),
|
||||
),
|
||||
ClipRect(
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: widget.scrollProgress * 2.0,
|
||||
sigmaY: widget.scrollProgress * 2.0,
|
||||
),
|
||||
filter: ImageFilter.blur(sigmaX: widget.scrollProgress * 2.0, sigmaY: widget.scrollProgress * 2.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
|
|
@ -260,9 +216,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
|
|||
Colors.black.withValues(alpha: 0.05),
|
||||
Colors.transparent,
|
||||
Colors.black.withValues(alpha: 0.3),
|
||||
Colors.black.withValues(
|
||||
alpha: 0.6 + (widget.scrollProgress * 0.25),
|
||||
),
|
||||
Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.25)),
|
||||
],
|
||||
stops: const [0.0, 0.15, 0.55, 1.0],
|
||||
),
|
||||
|
|
@ -291,32 +245,17 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
|
|||
),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
shadows: [
|
||||
Shadow(
|
||||
offset: Offset(0, 2),
|
||||
blurRadius: 12,
|
||||
color: Colors.black87,
|
||||
),
|
||||
],
|
||||
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)],
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
" • ",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
shadows: [
|
||||
Shadow(
|
||||
offset: Offset(0, 2),
|
||||
blurRadius: 12,
|
||||
color: Colors.black87,
|
||||
),
|
||||
],
|
||||
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)],
|
||||
),
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: const _ItemCountText(),
|
||||
),
|
||||
AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()),
|
||||
],
|
||||
),
|
||||
GestureDetector(
|
||||
|
|
@ -333,13 +272,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
|
|||
fontSize: 36,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 0.5,
|
||||
shadows: [
|
||||
Shadow(
|
||||
offset: Offset(0, 2),
|
||||
blurRadius: 12,
|
||||
color: Colors.black54,
|
||||
),
|
||||
],
|
||||
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black54)],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -349,31 +282,20 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
|
|||
GestureDetector(
|
||||
onTap: widget.onEditTitle,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: 80,
|
||||
),
|
||||
constraints: const BoxConstraints(maxHeight: 80),
|
||||
child: SingleChildScrollView(
|
||||
child: Text(
|
||||
currentAlbum.description,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
shadows: [
|
||||
Shadow(
|
||||
offset: Offset(0, 2),
|
||||
blurRadius: 8,
|
||||
color: Colors.black54,
|
||||
),
|
||||
],
|
||||
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 8, color: Colors.black54)],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child: RemoteAlbumSharedUserIcons(),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(top: 8.0), child: RemoteAlbumSharedUserIcons()),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -407,24 +329,13 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final assetCount = ref.watch(
|
||||
timelineServiceProvider.select((s) => s.totalAssets),
|
||||
);
|
||||
final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets));
|
||||
|
||||
return Text(
|
||||
'items_count'.t(
|
||||
context: context,
|
||||
args: {"count": assetCount},
|
||||
),
|
||||
'items_count'.t(context: context, args: {"count": assetCount}),
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
color: Colors.white,
|
||||
shadows: [
|
||||
const Shadow(
|
||||
offset: Offset(0, 2),
|
||||
blurRadius: 12,
|
||||
color: Colors.black87,
|
||||
),
|
||||
],
|
||||
shadows: [const Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -434,10 +345,7 @@ class _RandomAssetBackground extends StatefulWidget {
|
|||
final TimelineService timelineService;
|
||||
final IconData icon;
|
||||
|
||||
const _RandomAssetBackground({
|
||||
required this.timelineService,
|
||||
required this.icon,
|
||||
});
|
||||
const _RandomAssetBackground({required this.timelineService, required this.icon});
|
||||
|
||||
@override
|
||||
State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState();
|
||||
|
|
@ -457,50 +365,26 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_zoomController = AnimationController(
|
||||
duration: const Duration(seconds: 12),
|
||||
vsync: this,
|
||||
);
|
||||
_zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this);
|
||||
|
||||
_crossFadeController = AnimationController(
|
||||
duration: const Duration(milliseconds: 1200),
|
||||
vsync: this,
|
||||
);
|
||||
_crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this);
|
||||
|
||||
_zoomAnimation = Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 1.2,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _zoomController,
|
||||
curve: Curves.easeInOut,
|
||||
),
|
||||
);
|
||||
).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut));
|
||||
|
||||
_panAnimation = Tween<Offset>(
|
||||
begin: Offset.zero,
|
||||
end: const Offset(0.5, -0.5),
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _zoomController,
|
||||
curve: Curves.easeInOut,
|
||||
),
|
||||
);
|
||||
).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut));
|
||||
|
||||
_crossFadeAnimation = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _crossFadeController,
|
||||
curve: Curves.easeInOutCubic,
|
||||
),
|
||||
);
|
||||
).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic));
|
||||
|
||||
Future.delayed(
|
||||
Durations.medium1,
|
||||
() => _loadFirstAsset(),
|
||||
);
|
||||
Future.delayed(Durations.medium1, () => _loadFirstAsset());
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -590,9 +474,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
|
|||
}
|
||||
|
||||
return AnimatedBuilder(
|
||||
animation: Listenable.merge(
|
||||
[_zoomAnimation, _panAnimation, _crossFadeAnimation],
|
||||
),
|
||||
animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]),
|
||||
builder: (context, child) {
|
||||
return Transform.scale(
|
||||
scale: _zoomAnimation.value,
|
||||
|
|
@ -624,11 +506,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
|
|||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: Icon(
|
||||
Icons.error_outline_rounded,
|
||||
size: 24,
|
||||
color: Colors.red[300],
|
||||
),
|
||||
child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -655,11 +533,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
|
|||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: Icon(
|
||||
Icons.error_outline_rounded,
|
||||
size: 24,
|
||||
color: Colors.red[300],
|
||||
),
|
||||
child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -15,11 +15,7 @@ class ScaffoldErrorBody extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"scaffold_body_error_occurred",
|
||||
style: context.textTheme.displayMedium,
|
||||
textAlign: TextAlign.center,
|
||||
).tr(),
|
||||
Text("scaffold_body_error_occurred", style: context.textTheme.displayMedium, textAlign: TextAlign.center).tr(),
|
||||
if (withIcon)
|
||||
Center(
|
||||
child: Padding(
|
||||
|
|
@ -34,11 +30,7 @@ class ScaffoldErrorBody extends StatelessWidget {
|
|||
if (withIcon && errorMsg != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Text(
|
||||
errorMsg!,
|
||||
style: context.textTheme.displaySmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
child: Text(errorMsg!, style: context.textTheme.displaySmall, textAlign: TextAlign.center),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -43,40 +43,22 @@ class SearchField extends StatelessWidget {
|
|||
contentPadding: contentPadding,
|
||||
filled: filled,
|
||||
fillColor: context.primaryColor.withValues(alpha: 0.1),
|
||||
hintStyle: context.textTheme.bodyLarge?.copyWith(
|
||||
color: context.themeData.colorScheme.onSurfaceSecondary,
|
||||
),
|
||||
hintStyle: context.textTheme.bodyLarge?.copyWith(color: context.themeData.colorScheme.onSurfaceSecondary),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(25),
|
||||
),
|
||||
borderSide: BorderSide(
|
||||
color: context.colorScheme.surfaceDim,
|
||||
),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(color: context.colorScheme.surfaceDim),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(25),
|
||||
),
|
||||
borderSide: BorderSide(
|
||||
color: context.colorScheme.surfaceContainer,
|
||||
),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(color: context.colorScheme.surfaceContainer),
|
||||
),
|
||||
disabledBorder: OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(25),
|
||||
),
|
||||
borderSide: BorderSide(
|
||||
color: context.colorScheme.surfaceDim,
|
||||
),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(color: context.colorScheme.surfaceDim),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(25),
|
||||
),
|
||||
borderSide: BorderSide(
|
||||
color: context.colorScheme.primary.withAlpha(100),
|
||||
),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(color: context.colorScheme.primary.withAlpha(100)),
|
||||
),
|
||||
prefixIcon: prefixIcon,
|
||||
suffixIcon: suffixIcon,
|
||||
|
|
|
|||
|
|
@ -7,9 +7,7 @@ import 'package:immich_mobile/extensions/translate_extensions.dart';
|
|||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||
|
||||
class SelectionSliverAppBar extends ConsumerStatefulWidget {
|
||||
const SelectionSliverAppBar({
|
||||
super.key,
|
||||
});
|
||||
const SelectionSliverAppBar({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<SelectionSliverAppBar> createState() => _SelectionSliverAppBarState();
|
||||
|
|
@ -18,13 +16,9 @@ class SelectionSliverAppBar extends ConsumerStatefulWidget {
|
|||
class _SelectionSliverAppBarState extends ConsumerState<SelectionSliverAppBar> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selection = ref.watch(
|
||||
multiSelectProvider.select((s) => s.selectedAssets),
|
||||
);
|
||||
final selection = ref.watch(multiSelectProvider.select((s) => s.selectedAssets));
|
||||
|
||||
final toExclude = ref.watch(
|
||||
multiSelectProvider.select((s) => s.lockedSelectionAssets),
|
||||
);
|
||||
final toExclude = ref.watch(multiSelectProvider.select((s) => s.lockedSelectionAssets));
|
||||
|
||||
final filteredAssets = selection.where((asset) {
|
||||
return !toExclude.contains(asset);
|
||||
|
|
@ -40,9 +34,7 @@ class _SelectionSliverAppBarState extends ConsumerState<SelectionSliverAppBar> {
|
|||
pinned: true,
|
||||
snap: false,
|
||||
backgroundColor: context.colorScheme.surfaceContainer,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||
),
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))),
|
||||
automaticallyImplyLeading: false,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.close_rounded),
|
||||
|
|
@ -52,22 +44,13 @@ class _SelectionSliverAppBarState extends ConsumerState<SelectionSliverAppBar> {
|
|||
},
|
||||
),
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
"Select {count}".t(
|
||||
context: context,
|
||||
args: {
|
||||
'count': filteredAssets.length.toString(),
|
||||
},
|
||||
),
|
||||
),
|
||||
title: Text("Select {count}".t(context: context, args: {'count': filteredAssets.length.toString()})),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => onDone(filteredAssets),
|
||||
child: Text(
|
||||
'done'.t(context: context),
|
||||
style: context.textTheme.titleSmall?.copyWith(
|
||||
color: context.colorScheme.primary,
|
||||
),
|
||||
style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -11,10 +11,7 @@ class ShareDialog extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const CircularProgressIndicator(),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 12),
|
||||
child: const Text('share_dialog_preparing').tr(),
|
||||
),
|
||||
Container(margin: const EdgeInsets.only(top: 12), child: const Text('share_dialog_preparing').tr()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,21 +6,14 @@ import 'package:octo_image/octo_image.dart';
|
|||
|
||||
/// Simple set to show [OctoPlaceholder.circularProgressIndicator] as
|
||||
/// placeholder and [OctoError.icon] as error.
|
||||
OctoSet blurHashOrPlaceholder(
|
||||
Uint8List? blurhash, {
|
||||
BoxFit? fit,
|
||||
Text? errorMessage,
|
||||
}) {
|
||||
OctoSet blurHashOrPlaceholder(Uint8List? blurhash, {BoxFit? fit, Text? errorMessage}) {
|
||||
return OctoSet(
|
||||
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit),
|
||||
errorBuilder: blurHashErrorBuilder(blurhash, fit: fit, message: errorMessage),
|
||||
);
|
||||
}
|
||||
|
||||
OctoPlaceholderBuilder blurHashPlaceholderBuilder(
|
||||
Uint8List? blurhash, {
|
||||
BoxFit? fit,
|
||||
}) {
|
||||
OctoPlaceholderBuilder blurHashPlaceholderBuilder(Uint8List? blurhash, {BoxFit? fit}) {
|
||||
return (context) => blurhash == null
|
||||
? const ThumbnailPlaceholder()
|
||||
: FadeInPlaceholderImage(
|
||||
|
|
|
|||
|
|
@ -16,13 +16,7 @@ class UserCircleAvatar extends ConsumerWidget {
|
|||
double size;
|
||||
bool hasBorder;
|
||||
|
||||
UserCircleAvatar({
|
||||
super.key,
|
||||
this.radius = 22,
|
||||
this.size = 44,
|
||||
this.hasBorder = false,
|
||||
required this.user,
|
||||
});
|
||||
UserCircleAvatar({super.key, this.radius = 22, this.size = 44, this.hasBorder = false, required this.user});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
|
@ -43,12 +37,7 @@ class UserCircleAvatar extends ConsumerWidget {
|
|||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: hasBorder
|
||||
? Border.all(
|
||||
color: Colors.grey[500]!,
|
||||
width: 1,
|
||||
)
|
||||
: null,
|
||||
border: hasBorder ? Border.all(color: Colors.grey[500]!, width: 1) : null,
|
||||
),
|
||||
child: CircleAvatar(
|
||||
backgroundColor: userAvatarColor,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue