refactor(mobile): widgets (#9291)

* refactor(mobile): widgets

* update
This commit is contained in:
Alex 2024-05-06 23:04:21 -05:00 committed by GitHub
parent 7520ffd6c3
commit 5806a3ce25
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
203 changed files with 318 additions and 318 deletions

View file

@ -0,0 +1,277 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/models/backup/backup_state.model.dart';
import 'package:immich_mobile/providers/backup/backup.provider.dart';
import 'package:immich_mobile/providers/backup/manual_upload.provider.dart';
import 'package:immich_mobile/providers/authentication.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/providers/asset.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/providers/websocket.provider.dart';
import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_profile_info.dart';
import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_server_info.dart';
import 'package:immich_mobile/widgets/common/confirm_dialog.dart';
import 'package:immich_mobile/utils/bytes_units.dart';
import 'package:url_launcher/url_launcher.dart';
class ImmichAppBarDialog extends HookConsumerWidget {
const ImmichAppBarDialog({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
BackUpState backupState = ref.watch(backupProvider);
final theme = context.themeData;
bool isHorizontal = !context.isMobile;
final horizontalPadding = isHorizontal ? 100.0 : 20.0;
final user = ref.watch(currentUserProvider);
useEffect(
() {
ref.read(backupProvider.notifier).updateServerInfo();
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,
),
),
),
Center(
child: Image.asset(
context.isDarkTheme
? 'assets/immich-text-dark.png'
: 'assets/immich-text-light.png',
height: 16,
),
),
],
);
}
buildActionButton(IconData icon, String text, Function() onTap) {
return ListTile(
dense: true,
visualDensity: VisualDensity.standard,
contentPadding: const EdgeInsets.only(left: 30),
minLeadingWidth: 40,
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),
),
).tr(),
onTap: onTap,
);
}
buildSettingButton() {
return buildActionButton(
Icons.settings_rounded,
"profile_drawer_settings",
() => context.pushRoute(const SettingsRoute()),
);
}
buildAppLogButton() {
return buildActionButton(
Icons.assignment_outlined,
"profile_drawer_app_logs",
() => context.pushRoute(const AppLogRoute()),
);
}
buildSignOutButton() {
return buildActionButton(
Icons.logout_rounded,
"profile_drawer_sign_out",
() async {
showDialog(
context: context,
builder: (BuildContext ctx) {
return ConfirmDialog(
title: "app_bar_signout_dialog_title",
content: "app_bar_signout_dialog_content",
ok: "app_bar_signout_dialog_ok",
onOk: () async {
await ref.read(authenticationProvider.notifier).logout();
ref.read(manualUploadProvider.notifier).cancelBackup();
ref.read(backupProvider.notifier).cancelBackup();
ref.read(assetProvider.notifier).clearAllAsset();
ref.read(websocketProvider.notifier).disconnect();
context.replaceRoute(const LoginRoute());
},
);
},
);
},
);
}
Widget buildStorageInformation() {
var percentage = backupState.serverInfo.diskUsagePercentage / 100;
var usedDiskSpace = backupState.serverInfo.diskUse;
var totalDiskSpace = backupState.serverInfo.diskSize;
if (user != null && user.hasQuota) {
usedDiskSpace = formatBytes(user.quotaUsageInBytes);
totalDiskSpace = formatBytes(user.quotaSizeInBytes);
percentage = user.quotaUsageInBytes / user.quotaSizeInBytes;
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 3),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 4),
decoration: BoxDecoration(
color: context.isDarkTheme
? context.scaffoldBackgroundColor
: const Color.fromARGB(255, 225, 229, 240),
),
child: ListTile(
minLeadingWidth: 50,
leading: Icon(
Icons.storage_rounded,
color: theme.primaryColor,
),
title: Text(
"backup_controller_page_server_storage",
style: context.textTheme.labelLarge?.copyWith(
fontWeight: FontWeight.w500,
),
).tr(),
isThreeLine: true,
subtitle: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: LinearProgressIndicator(
minHeight: 5.0,
value: percentage,
backgroundColor: Colors.grey,
color: theme.primaryColor,
),
),
Padding(
padding: const EdgeInsets.only(top: 12.0),
child:
const Text('backup_controller_page_storage_format').tr(
args: [
usedDiskSpace,
totalDiskSpace,
],
),
),
],
),
),
),
),
);
}
buildFooter() {
return Padding(
padding: const EdgeInsets.only(top: 10, bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
onTap: () {
context.pop();
launchUrl(
Uri.parse('https://immich.app'),
mode: LaunchMode.externalApplication,
);
},
child: Text(
"profile_drawer_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,
);
},
child: Text(
"profile_drawer_github",
style: context.textTheme.bodySmall,
).tr(),
),
],
),
);
}
return Dialog(
clipBehavior: Clip.hardEdge,
alignment: Alignment.topCenter,
insetPadding: EdgeInsets.only(
top: isHorizontal ? 20 : 40,
left: horizontalPadding,
right: horizontalPadding,
bottom: isHorizontal ? 20 : 100,
),
backgroundColor: theme.cardColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: SizedBox(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(20),
child: buildTopRow(),
),
const AppBarProfileInfoBox(),
buildStorageInformation(),
const AppBarServerInfo(),
buildAppLogButton(),
buildSettingButton(),
buildSignOutButton(),
buildFooter(),
],
),
),
),
);
}
}

View file

@ -0,0 +1,139 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/providers/upload_profile_image.provider.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
import 'package:immich_mobile/models/authentication/authentication_state.model.dart';
import 'package:immich_mobile/providers/authentication.provider.dart';
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
class AppBarProfileInfoBox extends HookConsumerWidget {
const AppBarProfileInfoBox({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
AuthenticationState authState = ref.watch(authenticationProvider);
final uploadProfileImageStatus =
ref.watch(uploadProfileImageProvider).status;
final user = Store.tryGet(StoreKey.currentUser);
buildUserProfileImage() {
if (user == null) {
return const CircleAvatar(
radius: 20,
backgroundImage: AssetImage('assets/immich-logo.png'),
backgroundColor: Colors.transparent,
);
}
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 userImage;
}
pickUserProfileImage() async {
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(authenticationProvider.notifier).updateUserProfileImagePath(
profileImagePath,
);
if (user != null) {
user.profileImagePath = profileImagePath;
Store.put(StoreKey.currentUser, user);
ref.read(currentUserProvider.notifier).refresh();
}
}
}
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: context.isDarkTheme
? context.scaffoldBackgroundColor
: const Color.fromARGB(255, 225, 229, 240),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
),
child: ListTile(
minLeadingWidth: 50,
leading: GestureDetector(
onTap: pickUserProfileImage,
child: Stack(
clipBehavior: Clip.none,
children: [
buildUserProfileImage(),
Positioned(
bottom: -5,
right: -8,
child: Material(
color: context.isDarkTheme
? Colors.blueGrey[800]
: Colors.white,
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50.0),
),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Icon(
Icons.camera_alt_outlined,
color: context.primaryColor,
size: 14,
),
),
),
),
],
),
),
title: Text(
authState.name,
style: context.textTheme.titleMedium?.copyWith(
color: context.primaryColor,
fontWeight: FontWeight.w500,
),
),
subtitle: Text(
authState.userEmail,
style: context.textTheme.bodySmall?.copyWith(
color: context.textTheme.bodySmall?.color?.withAlpha(200),
),
),
),
),
);
}
}

View file

@ -0,0 +1,273 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/models/server_info/server_info.model.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
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,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
ServerInfo serverInfoState = ref.watch(serverInfoProvider);
final appInfo = useState({});
const titleFontSize = 12.0;
const contentFontSize = 11.0;
getPackageInfo() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
appInfo.value = {
"version": packageInfo.version,
"buildNumber": packageInfo.buildNumber,
};
}
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.isDarkTheme
? context.scaffoldBackgroundColor
: const Color.fromARGB(255, 225, 229, 240),
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
serverInfoState.isVersionMismatch
? serverInfoState.versionMismatchErrorMessage
: "profile_drawer_client_server_up_to_date".tr(),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 11,
color: context.primaryColor,
fontWeight: FontWeight.w500,
),
),
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Divider(
color: Color.fromARGB(101, 201, 201, 201),
thickness: 1,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Text(
"server_info_box_app_version".tr(),
style: TextStyle(
fontSize: titleFontSize,
color: context.textTheme.labelSmall?.color,
fontWeight: FontWeight.w500,
),
),
),
),
Expanded(
flex: 0,
child: Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Text(
"${appInfo.value["version"]} build.${appInfo.value["buildNumber"]}",
style: TextStyle(
fontSize: contentFontSize,
color: context.textTheme.labelSmall?.color
?.withOpacity(0.5),
fontWeight: FontWeight.bold,
),
),
),
),
],
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Divider(
color: Color.fromARGB(101, 201, 201, 201),
thickness: 1,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Text(
"server_info_box_server_version".tr(),
style: TextStyle(
fontSize: titleFontSize,
color: context.textTheme.labelSmall?.color,
fontWeight: FontWeight.w500,
),
),
),
),
Expanded(
flex: 0,
child: Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Text(
serverInfoState.serverVersion.major > 0
? "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch}"
: "--",
style: TextStyle(
fontSize: contentFontSize,
color: context.textTheme.labelSmall?.color
?.withOpacity(0.5),
fontWeight: FontWeight.bold,
),
),
),
),
],
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Divider(
color: Color.fromARGB(101, 201, 201, 201),
thickness: 1,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Text(
"server_info_box_server_url".tr(),
style: TextStyle(
fontSize: titleFontSize,
color: context.textTheme.labelSmall?.color,
fontWeight: FontWeight.w500,
),
),
),
),
Expanded(
flex: 0,
child: Container(
width: 200,
padding: const EdgeInsets.only(right: 10.0),
child: Tooltip(
verticalOffset: 0,
decoration: BoxDecoration(
color: context.primaryColor.withOpacity(0.9),
borderRadius: BorderRadius.circular(10),
),
textStyle: TextStyle(
color:
context.isDarkTheme ? Colors.black : Colors.white,
fontWeight: FontWeight.bold,
),
message: getServerUrl() ?? '--',
preferBelow: false,
triggerMode: TooltipTriggerMode.tap,
child: Text(
getServerUrl() ?? '--',
style: TextStyle(
fontSize: contentFontSize,
color: context.textTheme.labelSmall?.color
?.withOpacity(0.5),
fontWeight: FontWeight.bold,
overflow: TextOverflow.ellipsis,
),
textAlign: TextAlign.end,
),
),
),
),
],
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Divider(
color: Color.fromARGB(101, 201, 201, 201),
thickness: 1,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Row(
children: [
if (serverInfoState.isNewReleaseAvailable)
const Padding(
padding: EdgeInsets.only(right: 5.0),
child: Icon(
Icons.info,
color: Color.fromARGB(255, 243, 188, 106),
size: 12,
),
),
Text(
"server_info_box_latest_release".tr(),
style: TextStyle(
fontSize: titleFontSize,
color: context.textTheme.labelSmall?.color,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
Expanded(
flex: 0,
child: Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Text(
serverInfoState.latestVersion.major > 0
? "${serverInfoState.latestVersion.major}.${serverInfoState.latestVersion.minor}.${serverInfoState.latestVersion.patch}"
: "--",
style: TextStyle(
fontSize: contentFontSize,
color: context.textTheme.labelSmall?.color
?.withOpacity(0.5),
fontWeight: FontWeight.bold,
),
),
),
),
],
),
],
),
),
),
);
}
}