mirror of
https://github.com/immich-app/immich.git
synced 2025-05-31 12:15:47 -04:00
Internationalization (German) of the mobile app. (#246)
* Add i18n framework to mobile app and write simple translation generator * Replace all texts in login_form with i18n keys * Localization of sharing section * Localization of asset viewer section * Use JSON as base translation format * Add check for missing/unused translation keys * Add localizely * Remove i18n directory in favour of localizely * Backup Translation * More translations * Translate home page * Translation of search page * Translate new server version announcement * Reformat code * Fix typo in german translation * Update englisch translations * Change translation keys to match dart filenames * Add /api to translated endpoint_urls * Update localizely.yml * Add languages to ios plist * Remove unused keys * Added script to check outdated key in other translations * Add download key to localizely.yml Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
f3032f74a4
commit
2b5cef156c
15
localizely.yml
Normal file
15
localizely.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
config_version: 1.0
|
||||||
|
project_id: ead34689-ec52-41d9-b675-09bc85a6cbd7
|
||||||
|
file_type: flutter_arb
|
||||||
|
upload:
|
||||||
|
files:
|
||||||
|
- file: mobile/assets/i18n/en-US.json
|
||||||
|
locale_code: en
|
||||||
|
- file: mobile/assets/i18n/de-DE.json
|
||||||
|
locale_code: de
|
||||||
|
download:
|
||||||
|
files:
|
||||||
|
- file: mobile/assets/i18n/en-US.json
|
||||||
|
locale_code: en
|
||||||
|
- file: mobile/assets/i18n/de-DE.json
|
||||||
|
locale_code: de
|
98
mobile/assets/i18n/de-DE.json
Normal file
98
mobile/assets/i18n/de-DE.json
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"date_format": "E d. LLL y \u2022 hh:mm",
|
||||||
|
"daily_title_text_date": "E, dd MMM",
|
||||||
|
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
||||||
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
|
"login_form_button_text": "Anmelden",
|
||||||
|
"login_form_save_login": "Angemeldet bleiben",
|
||||||
|
"login_form_endpoint_url": "Server URL",
|
||||||
|
"login_form_endpoint_hint": "http://deine-server-ip:port/api",
|
||||||
|
"login_form_err_trailing_whitespace": "Folgendes Leerzeichen",
|
||||||
|
"login_form_err_leading_whitespace": "Führendes Leerzichen",
|
||||||
|
"login_form_err_invalid_email": "Ungültige E-Mail",
|
||||||
|
"login_form_err_http": "Bitte gebe http:// oder https:// an",
|
||||||
|
"login_form_label_email": "E-Mail",
|
||||||
|
"login_form_email_hint": "deine@email.de",
|
||||||
|
"login_form_label_password": "Passwort",
|
||||||
|
"login_form_password_hint": "password",
|
||||||
|
"share_add_title": "Titel hinzufügen",
|
||||||
|
"album_viewer_appbar_share_err_delete": "Album konnte nicht gelöscht werden",
|
||||||
|
"album_viewer_appbar_share_err_leave": "Album konnte nicht verlassen werden",
|
||||||
|
"album_viewer_appbar_share_err_remove": "Beim Löschen von Elementen aus dem Album ist ein Problem aufgetreten",
|
||||||
|
"album_viewer_appbar_share_err_title": "Der Titel konnte nicht geändert werden",
|
||||||
|
"album_viewer_appbar_share_remove": "Entferne vom Album",
|
||||||
|
"album_viewer_appbar_share_delete": "Album löschen",
|
||||||
|
"album_viewer_appbar_share_leave": "Album verlassen",
|
||||||
|
"sharing_silver_appbar_create_shared_album": "Neues geteiltes Album",
|
||||||
|
"sharing_silver_appbar_share_partner": "Teile mit Partner",
|
||||||
|
"share_add_photos": "Fotos hinzufügen",
|
||||||
|
"album_viewer_page_share_add_users": "Nutzer hinzufügen",
|
||||||
|
"share_add": "Hinzufügen",
|
||||||
|
"create_shared_album_page_share_add_assets": "ELEMENTE HINZUFÜGEN",
|
||||||
|
"create_shared_album_page_share_select_photos": "Fotos auswählen",
|
||||||
|
"share_create_album": "Album erstellen",
|
||||||
|
"create_shared_album_page_share": "Teilen",
|
||||||
|
"select_additional_user_for_sharing_page_suggestions": "Vorschläge",
|
||||||
|
"share_invite": "Zum Album einladen",
|
||||||
|
"select_user_for_sharing_page_err_album": "Album konnte nicht erstellt werden",
|
||||||
|
"sharing_page_empty_list": "LEERE LISTE",
|
||||||
|
"sharing_page_description": "Erstelle ein geteiltes Album um Fotos und Videos mit Personen in deinem Netzwerk zu teilen.",
|
||||||
|
"sharing_page_album": "Geteilte Alben",
|
||||||
|
"exif_bottom_sheet_description": "Beschreibung hinzufügen...",
|
||||||
|
"exif_bottom_sheet_location": "STANDORT",
|
||||||
|
"exif_bottom_sheet_details": "DETAILS",
|
||||||
|
"backup_err_only_album": "Das einzige Album kann nicht entfernt werden",
|
||||||
|
"backup_controller_page_server_storage": "Server Speicher",
|
||||||
|
"backup_controller_page_status_on": "Sicherung ist aktiv",
|
||||||
|
"backup_controller_page_status_off": "Sicherung ist inaktiv",
|
||||||
|
"backup_controller_page_turn_off": "Sicherung ausschalten",
|
||||||
|
"backup_controller_page_turn_on": "Sicherung einschalten",
|
||||||
|
"backup_controller_page_desc_backup": "Aktiviere die Sicherung um Elemente automatisch auf den Server zu laden.",
|
||||||
|
"backup_controller_page_backup_selected": "Ausgewählt: ",
|
||||||
|
"backup_all": "Alle",
|
||||||
|
"backup_controller_page_none_selected": "Keine ausgewählt",
|
||||||
|
"backup_controller_page_excluded": "Ausgeschlossen: ",
|
||||||
|
"backup_controller_page_albums": "Gesicherte Alben",
|
||||||
|
"backup_controller_page_to_backup": "Zu sichernde Alben",
|
||||||
|
"backup_controller_page_select": "Auswählen",
|
||||||
|
"backup_controller_page_backup": "Sicherung",
|
||||||
|
"backup_controller_page_info": "Informationen zur Sicherung",
|
||||||
|
"backup_controller_page_total": "Gesamt",
|
||||||
|
"backup_controller_page_total_sub": "Alle Fotos und Videos",
|
||||||
|
"backup_controller_page_backup_sub": "Gesicherte Fotos und Videos",
|
||||||
|
"backup_controller_page_remainder": "Übrig",
|
||||||
|
"backup_controller_page_remainder_sub": "Noch zu sichernde Fotos und Videos",
|
||||||
|
"backup_controller_page_cancel": "Abbrechen",
|
||||||
|
"backup_controller_page_start_backup": "Sicherung starten",
|
||||||
|
"album_info_card_backup_album_included": "EINGESCHLOSSEN",
|
||||||
|
"album_info_card_backup_album_excluded": "AUSGESCHLOSSEN",
|
||||||
|
"backup_info_card_assets": "Elemente",
|
||||||
|
"backup_album_selection_page_select_albums": "Alben auswählen",
|
||||||
|
"backup_album_selection_page_selection_info": "Auswahl",
|
||||||
|
"backup_album_selection_page_total_assets": "Elemente",
|
||||||
|
"backup_album_selection_page_albums_device": "Alben auf dem Gerät ({})",
|
||||||
|
"backup_album_selection_page_albums_tap": "Tippen um einzuschließen, doppelt tippen um zu entfernen",
|
||||||
|
"backup_album_selection_page_assets_scatter": "Elemente können sich über mehrere Alben verteilen. Daher können diese vor der Sicherung eingeschlossen oder ausgeschlossen werden",
|
||||||
|
"backup_controller_page_storage_format": "{} von {} genutzt",
|
||||||
|
"tab_controller_nav_photos": "Fotos",
|
||||||
|
"tab_controller_nav_search": "Suche",
|
||||||
|
"tab_controller_nav_sharing": "Teilen",
|
||||||
|
"control_bottom_app_bar_delete": "Löschen",
|
||||||
|
"delete_dialog_title": "Für immer löschen",
|
||||||
|
"delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt",
|
||||||
|
"delete_dialog_cancel": "Abbrechen",
|
||||||
|
"delete_dialog_ok": "Löschen",
|
||||||
|
"profile_drawer_sign_out": "Abmelden",
|
||||||
|
"profile_drawer_client_server_up_to_date": "App und Server sind aktuell",
|
||||||
|
"search_bar_hint": "Durchsuche deine Fotos",
|
||||||
|
"search_page_places": "Orte",
|
||||||
|
"search_page_things": "Dinge",
|
||||||
|
"search_result_page_new_search_hint": "Neue Suche",
|
||||||
|
"search_page_no_places": "Keine Informationen über Orte verfügbar",
|
||||||
|
"version_announcement_overlay_title": "Neue Server-Version verfügbar \uD83C\uDF89",
|
||||||
|
"version_announcement_overlay_text_1": "Hallo mein Freund! Es gibt eine neue Version von",
|
||||||
|
"version_announcement_overlay_text_2": "Bitte nehm dir die Zeit und lese das ",
|
||||||
|
"version_announcement_overlay_release_notes": "Änderungsprotokoll",
|
||||||
|
"version_announcement_overlay_text_3": " und achte darauf, dass deine docker-compose und .env Dateien aktuell sind, vor allem wenn du ein System für automatische Updates benutzt (z.B. Watchtower).",
|
||||||
|
"version_announcement_overlay_ack": "Ich habe verstanden"
|
||||||
|
}
|
98
mobile/assets/i18n/en-US.json
Normal file
98
mobile/assets/i18n/en-US.json
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"date_format": "E, LLL d, y \u2022 h:mm a",
|
||||||
|
"daily_title_text_date": "E, MMM dd",
|
||||||
|
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
||||||
|
"monthly_title_text_date_format": "MMMM y",
|
||||||
|
"login_form_button_text": "Login",
|
||||||
|
"login_form_save_login": "Stay logged in",
|
||||||
|
"login_form_endpoint_url": "Server Endpoint URL",
|
||||||
|
"login_form_endpoint_hint": "http://your-server-ip:port/api",
|
||||||
|
"login_form_err_trailing_whitespace": "Trailing whitespace",
|
||||||
|
"login_form_err_leading_whitespace": "Leading whitespace",
|
||||||
|
"login_form_err_invalid_email": "Invalid Email",
|
||||||
|
"login_form_err_http": "Please specify http:// or https://",
|
||||||
|
"login_form_label_email": "Email",
|
||||||
|
"login_form_email_hint": "youremail@email.com",
|
||||||
|
"login_form_label_password": "Password",
|
||||||
|
"login_form_password_hint": "password",
|
||||||
|
"share_add_title": "Add a title",
|
||||||
|
"album_viewer_appbar_share_err_delete": "Failed to delete album",
|
||||||
|
"album_viewer_appbar_share_err_leave": "Failed to leave album",
|
||||||
|
"album_viewer_appbar_share_err_remove": "There are problems in removing assets from album",
|
||||||
|
"album_viewer_appbar_share_err_title": "Failed to change album title",
|
||||||
|
"album_viewer_appbar_share_remove": "Remove from album",
|
||||||
|
"album_viewer_appbar_share_delete": "Delete album",
|
||||||
|
"album_viewer_appbar_share_leave": "Leave album",
|
||||||
|
"sharing_silver_appbar_create_shared_album": "Create shared album",
|
||||||
|
"sharing_silver_appbar_share_partner": "Share with partner",
|
||||||
|
"share_add_photos": "Add photos",
|
||||||
|
"album_viewer_page_share_add_users": "Add users",
|
||||||
|
"share_add": "Add",
|
||||||
|
"create_shared_album_page_share_add_assets": "ADD ASSETS",
|
||||||
|
"create_shared_album_page_share_select_photos": "Select Photos",
|
||||||
|
"share_create_album": "Create album",
|
||||||
|
"create_shared_album_page_share": "Share",
|
||||||
|
"select_additional_user_for_sharing_page_suggestions": "Suggestions",
|
||||||
|
"share_invite": "Invite to album",
|
||||||
|
"select_user_for_sharing_page_err_album": "Failed to create album",
|
||||||
|
"sharing_page_empty_list": "EMPTY LIST",
|
||||||
|
"sharing_page_description": "Create shared albums to share photos and videos with people in your network.",
|
||||||
|
"sharing_page_album": "Shared albums",
|
||||||
|
"exif_bottom_sheet_description": "Add Description...",
|
||||||
|
"exif_bottom_sheet_location": "LOCATION",
|
||||||
|
"exif_bottom_sheet_details": "DETAILS",
|
||||||
|
"backup_err_only_album": "Cannot remove the only album",
|
||||||
|
"backup_controller_page_server_storage": "Server Storage",
|
||||||
|
"backup_controller_page_status_on": "Backup is on",
|
||||||
|
"backup_controller_page_status_off": "Backup is off",
|
||||||
|
"backup_controller_page_turn_off": "Turn off Backup",
|
||||||
|
"backup_controller_page_turn_on": "Turn on Backup",
|
||||||
|
"backup_controller_page_desc_backup": "Turn on backup to automatically upload new assets to the server.",
|
||||||
|
"backup_controller_page_backup_selected": "Selected: ",
|
||||||
|
"backup_all": "All",
|
||||||
|
"backup_controller_page_none_selected": "None selected",
|
||||||
|
"backup_controller_page_excluded": "Excluded: ",
|
||||||
|
"backup_controller_page_albums": "Backup Albums",
|
||||||
|
"backup_controller_page_to_backup": "Albums to be backup",
|
||||||
|
"backup_controller_page_select": "Select",
|
||||||
|
"backup_controller_page_backup": "Backup",
|
||||||
|
"backup_controller_page_info": "Backup Information",
|
||||||
|
"backup_controller_page_total": "Total",
|
||||||
|
"backup_controller_page_total_sub": "All unique photos and videos from selected albums",
|
||||||
|
"backup_controller_page_backup_sub": "Backed up photos and videos",
|
||||||
|
"backup_controller_page_remainder": "Remainder",
|
||||||
|
"backup_controller_page_remainder_sub": "Remaining photos and albums to back up from selection",
|
||||||
|
"backup_controller_page_cancel": "Cancel",
|
||||||
|
"backup_controller_page_start_backup": "Start Backup",
|
||||||
|
"album_info_card_backup_album_included": "INCLUDED",
|
||||||
|
"album_info_card_backup_album_excluded": "EXCLUDED",
|
||||||
|
"backup_info_card_assets": "assets",
|
||||||
|
"backup_album_selection_page_select_albums": "Select Albums",
|
||||||
|
"backup_album_selection_page_selection_info": "Selection Info",
|
||||||
|
"backup_album_selection_page_total_assets": "Total unique assets",
|
||||||
|
"backup_album_selection_page_albums_device": "Albums on device ({})",
|
||||||
|
"backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude",
|
||||||
|
"backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.",
|
||||||
|
"backup_controller_page_storage_format": "{} of {} used",
|
||||||
|
"tab_controller_nav_photos": "Photos",
|
||||||
|
"tab_controller_nav_search": "Search",
|
||||||
|
"tab_controller_nav_sharing": "Sharing",
|
||||||
|
"control_bottom_app_bar_delete": "Delete",
|
||||||
|
"delete_dialog_title": "Delete Permanently",
|
||||||
|
"delete_dialog_alert": "These items will be permanently deleted from Immich and from your device",
|
||||||
|
"delete_dialog_cancel": "Cancel",
|
||||||
|
"delete_dialog_ok": "Delete",
|
||||||
|
"profile_drawer_sign_out": "Sign Out",
|
||||||
|
"profile_drawer_client_server_up_to_date": "Client and Server are up-to-date",
|
||||||
|
"search_bar_hint": "Search your photos",
|
||||||
|
"search_page_places": "Places",
|
||||||
|
"search_page_things": "Things",
|
||||||
|
"search_result_page_new_search_hint": "New Search",
|
||||||
|
"search_page_no_places": "No Places Info Available",
|
||||||
|
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89",
|
||||||
|
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
||||||
|
"version_announcement_overlay_text_2": "please take your time to visit the ",
|
||||||
|
"version_announcement_overlay_release_notes": "release notes",
|
||||||
|
"version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
|
||||||
|
"version_announcement_overlay_ack": "Acknowledge"
|
||||||
|
}
|
@ -82,5 +82,11 @@
|
|||||||
<array>
|
<array>
|
||||||
<string>https</string>
|
<string>https</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
|
<key>CFBundleLocalizations</key>
|
||||||
|
<array>
|
||||||
|
<string>en</string>
|
||||||
|
<string>de</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
@ -36,7 +37,21 @@ void main() async {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
runApp(const ProviderScope(child: ImmichApp()));
|
await EasyLocalization.ensureInitialized();
|
||||||
|
|
||||||
|
var locales = const [
|
||||||
|
// Default locale
|
||||||
|
Locale('en', 'US'),
|
||||||
|
// Additional locales
|
||||||
|
Locale('de', 'DE')
|
||||||
|
];
|
||||||
|
|
||||||
|
runApp(EasyLocalization(
|
||||||
|
supportedLocales: locales,
|
||||||
|
path: 'assets/i18n',
|
||||||
|
useFallbackTranslations: true,
|
||||||
|
fallbackLocale: locales.first,
|
||||||
|
child: const ProviderScope(child: ImmichApp())));
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImmichApp extends ConsumerStatefulWidget {
|
class ImmichApp extends ConsumerStatefulWidget {
|
||||||
@ -112,6 +127,9 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
|||||||
ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo();
|
ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo();
|
||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
|
localizationsDelegates: context.localizationDelegates,
|
||||||
|
supportedLocales: context.supportedLocales,
|
||||||
|
locale: context.locale,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
home: Stack(
|
home: Stack(
|
||||||
children: [
|
children: [
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -72,7 +73,7 @@ class ExifBottomSheet extends ConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (assetDetail.exifInfo?.dateTimeOriginal != null)
|
if (assetDetail.exifInfo?.dateTimeOriginal != null)
|
||||||
Text(
|
Text(
|
||||||
DateFormat('E, LLL d, y • h:mm a').format(
|
DateFormat('date_format'.tr()).format(
|
||||||
DateTime.parse(assetDetail.exifInfo!.dateTimeOriginal!),
|
DateTime.parse(assetDetail.exifInfo!.dateTimeOriginal!),
|
||||||
),
|
),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -84,12 +85,12 @@ class ExifBottomSheet extends ConsumerWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 16.0),
|
padding: const EdgeInsets.only(top: 16.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Add Description...",
|
"exif_bottom_sheet_description",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.grey[500],
|
color: Colors.grey[500],
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
),
|
),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Location
|
// Location
|
||||||
@ -104,9 +105,9 @@ class ExifBottomSheet extends ConsumerWidget {
|
|||||||
color: Colors.grey[600],
|
color: Colors.grey[600],
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"LOCATION",
|
"exif_bottom_sheet_location",
|
||||||
style: TextStyle(fontSize: 11, color: Colors.grey[400]),
|
style: TextStyle(fontSize: 11, color: Colors.grey[400]),
|
||||||
),
|
).tr(),
|
||||||
if (assetDetail.exifInfo?.latitude != null &&
|
if (assetDetail.exifInfo?.latitude != null &&
|
||||||
assetDetail.exifInfo?.longitude != null)
|
assetDetail.exifInfo?.longitude != null)
|
||||||
_buildMap(),
|
_buildMap(),
|
||||||
@ -134,9 +135,9 @@ class ExifBottomSheet extends ConsumerWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
"DETAILS",
|
"exif_bottom_sheet_details",
|
||||||
style: TextStyle(fontSize: 11, color: Colors.grey[400]),
|
style: TextStyle(fontSize: 11, color: Colors.grey[400]),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.all(0),
|
contentPadding: const EdgeInsets.all(0),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
@ -37,10 +38,10 @@ class AlbumInfoCard extends HookConsumerWidget {
|
|||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
|
||||||
label: const Text(
|
label: const Text(
|
||||||
"INCLUDED",
|
"album_info_card_backup_album_included",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
|
fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
);
|
);
|
||||||
} else if (isExcluded) {
|
} else if (isExcluded) {
|
||||||
@ -48,10 +49,10 @@ class AlbumInfoCard extends HookConsumerWidget {
|
|||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
|
||||||
label: const Text(
|
label: const Text(
|
||||||
"EXCLUDED",
|
"album_info_card_backup_album_excluded",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
|
fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
backgroundColor: Colors.red[300],
|
backgroundColor: Colors.red[300],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -77,7 +78,7 @@ class AlbumInfoCard extends HookConsumerWidget {
|
|||||||
if (ref.watch(backupProvider).selectedBackupAlbums.length == 1) {
|
if (ref.watch(backupProvider).selectedBackupAlbums.length == 1) {
|
||||||
ImmichToast.show(
|
ImmichToast.show(
|
||||||
context: context,
|
context: context,
|
||||||
msg: "Cannot remove the only album",
|
msg: "backup_err_only_album".tr(),
|
||||||
toastType: ToastType.error,
|
toastType: ToastType.error,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
@ -104,7 +105,7 @@ class AlbumInfoCard extends HookConsumerWidget {
|
|||||||
.contains(albumInfo)) {
|
.contains(albumInfo)) {
|
||||||
ImmichToast.show(
|
ImmichToast.show(
|
||||||
context: context,
|
context: context,
|
||||||
msg: "Cannot exclude the only album",
|
msg: "backup_err_only_album".tr(),
|
||||||
toastType: ToastType.error,
|
toastType: ToastType.error,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
@ -180,7 +181,10 @@ class AlbumInfoCard extends HookConsumerWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 2.0),
|
padding: const EdgeInsets.only(top: 2.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'${albumInfo.assetCount} ${(albumInfo.isAll ? " (ALL)" : "")}',
|
albumInfo.assetCount.toString() +
|
||||||
|
(albumInfo.isAll
|
||||||
|
? " (${'backup_all'.tr()})"
|
||||||
|
: ""),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12, color: Colors.grey[600]),
|
fontSize: 12, color: Colors.grey[600]),
|
||||||
),
|
),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class BackupInfoCard extends StatelessWidget {
|
class BackupInfoCard extends StatelessWidget {
|
||||||
@ -44,7 +45,7 @@ class BackupInfoCard extends StatelessWidget {
|
|||||||
info,
|
info,
|
||||||
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const Text("assets"),
|
const Text("backup_info_card_assets").tr(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
@ -55,7 +56,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||||||
if (ref.watch(backupProvider).selectedBackupAlbums.length == 1) {
|
if (ref.watch(backupProvider).selectedBackupAlbums.length == 1) {
|
||||||
ImmichToast.show(
|
ImmichToast.show(
|
||||||
context: context,
|
context: context,
|
||||||
msg: "Cannot remove the only album",
|
msg: "backup_err_only_album".tr(),
|
||||||
toastType: ToastType.error,
|
toastType: ToastType.error,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
@ -136,20 +137,21 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||||||
icon: const Icon(Icons.arrow_back_ios_rounded),
|
icon: const Icon(Icons.arrow_back_ios_rounded),
|
||||||
),
|
),
|
||||||
title: const Text(
|
title: const Text(
|
||||||
"Select Albums",
|
"backup_album_selection_page_select_albums",
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
physics: const ClampingScrollPhysics(),
|
physics: const ClampingScrollPhysics(),
|
||||||
children: [
|
children: [
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
|
padding:
|
||||||
child: Text(
|
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
|
||||||
"Selection Info",
|
child: const Text(
|
||||||
|
"backup_album_selection_page_selection_info",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
// Selected Album Chips
|
// Selected Album Chips
|
||||||
|
|
||||||
@ -181,14 +183,18 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||||||
ListTile(
|
ListTile(
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
title: Text(
|
title: Text(
|
||||||
"Total unique assets",
|
"backup_album_selection_page_total_assets",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Colors.grey[700]),
|
color: Colors.grey[700]),
|
||||||
),
|
).tr(),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
'${ref.watch(backupProvider).allUniqueAssets.length}',
|
ref
|
||||||
|
.watch(backupProvider)
|
||||||
|
.allUniqueAssets
|
||||||
|
.length
|
||||||
|
.toString(),
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -199,19 +205,20 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
"Albums on device (${availableAlbums.length})",
|
"backup_album_selection_page_albums_device"
|
||||||
|
.tr(args: [availableAlbums.length.toString()]),
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||||
),
|
),
|
||||||
subtitle: Padding(
|
subtitle: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Tap to include, double tap to exclude",
|
"backup_album_selection_page_albums_tap",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
splashRadius: 16,
|
splashRadius: 16,
|
||||||
@ -230,21 +237,21 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||||||
borderRadius: BorderRadius.circular(12)),
|
borderRadius: BorderRadius.circular(12)),
|
||||||
elevation: 5,
|
elevation: 5,
|
||||||
title: Text(
|
title: Text(
|
||||||
'Selection Info',
|
'backup_album_selection_page_selection_info',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
),
|
).tr(),
|
||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: ListBody(
|
child: ListBody(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.',
|
'backup_album_selection_page_assets_scatter',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14, color: Colors.grey[700]),
|
fontSize: 14, color: Colors.grey[700]),
|
||||||
),
|
).tr(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -44,9 +45,9 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
title: const Text(
|
title: const Text(
|
||||||
"Server storage",
|
"backup_controller_page_server_storage",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||||
),
|
).tr(),
|
||||||
subtitle: Padding(
|
subtitle: Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -66,8 +67,11 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 12.0),
|
padding: const EdgeInsets.only(top: 12.0),
|
||||||
child: Text(
|
child: const Text('backup_controller_page_storage_format').tr(
|
||||||
'${backupState.serverInfo.diskUse} of ${backupState.serverInfo.diskSize} used'),
|
args: [
|
||||||
|
backupState.serverInfo.diskUse,
|
||||||
|
backupState.serverInfo.diskSize
|
||||||
|
]),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -76,11 +80,13 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ListTile _buildBackupController() {
|
ListTile _buildBackupController() {
|
||||||
var backUpOption =
|
var backUpOption = authenticationState.deviceInfo.isAutoBackup
|
||||||
authenticationState.deviceInfo.isAutoBackup ? "on" : "off";
|
? "backup_controller_page_status_on".tr()
|
||||||
|
: "backup_controller_page_status_off".tr();
|
||||||
var isAutoBackup = authenticationState.deviceInfo.isAutoBackup;
|
var isAutoBackup = authenticationState.deviceInfo.isAutoBackup;
|
||||||
var backupBtnText =
|
var backupBtnText = authenticationState.deviceInfo.isAutoBackup
|
||||||
authenticationState.deviceInfo.isAutoBackup ? "off" : "on";
|
? "backup_controller_page_turn_off".tr()
|
||||||
|
: "backup_controller_page_turn_on".tr();
|
||||||
return ListTile(
|
return ListTile(
|
||||||
isThreeLine: true,
|
isThreeLine: true,
|
||||||
leading: isAutoBackup
|
leading: isAutoBackup
|
||||||
@ -90,7 +96,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
)
|
)
|
||||||
: const Icon(Icons.cloud_off_rounded),
|
: const Icon(Icons.cloud_off_rounded),
|
||||||
title: Text(
|
title: Text(
|
||||||
"Back up is $backUpOption",
|
backUpOption,
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||||||
),
|
),
|
||||||
subtitle: Padding(
|
subtitle: Padding(
|
||||||
@ -100,9 +106,9 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (!isAutoBackup)
|
if (!isAutoBackup)
|
||||||
const Text(
|
const Text(
|
||||||
"Turn on backup to automatically upload new assets to the server.",
|
"backup_controller_page_desc_backup",
|
||||||
style: TextStyle(fontSize: 14),
|
style: TextStyle(fontSize: 14),
|
||||||
),
|
).tr(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
@ -123,7 +129,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
.setAutoBackup(true);
|
.setAutoBackup(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Text("Turn $backupBtnText Backup",
|
child: Text(backupBtnText,
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -134,13 +140,13 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSelectedAlbumName() {
|
Widget _buildSelectedAlbumName() {
|
||||||
var text = "Selected: ";
|
var text = "backup_controller_page_backup_selected".tr();
|
||||||
var albums = ref.watch(backupProvider).selectedBackupAlbums;
|
var albums = ref.watch(backupProvider).selectedBackupAlbums;
|
||||||
|
|
||||||
if (albums.isNotEmpty) {
|
if (albums.isNotEmpty) {
|
||||||
for (var album in albums) {
|
for (var album in albums) {
|
||||||
if (album.name == "Recent" || album.name == "Recents") {
|
if (album.name == "Recent" || album.name == "Recents") {
|
||||||
text += "${album.name} (All), ";
|
text += "${album.name} (${'backup_all'.tr()}), ";
|
||||||
} else {
|
} else {
|
||||||
text += "${album.name}, ";
|
text += "${album.name}, ";
|
||||||
}
|
}
|
||||||
@ -160,7 +166,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
"None selected",
|
"backup_controller_page_none_selected".tr(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@ -171,7 +177,7 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildExcludedAlbumName() {
|
Widget _buildExcludedAlbumName() {
|
||||||
var text = "Excluded: ";
|
var text = "backup_controller_page_excluded".tr();
|
||||||
var albums = ref.watch(backupProvider).excludedBackupAlbums;
|
var albums = ref.watch(backupProvider).excludedBackupAlbums;
|
||||||
|
|
||||||
if (albums.isNotEmpty) {
|
if (albums.isNotEmpty) {
|
||||||
@ -207,17 +213,18 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
borderOnForeground: false,
|
borderOnForeground: false,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
minVerticalPadding: 15,
|
minVerticalPadding: 15,
|
||||||
title: const Text("Backup Albums",
|
title: const Text("backup_controller_page_albums",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)),
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20))
|
||||||
|
.tr(),
|
||||||
subtitle: Padding(
|
subtitle: Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
const Text(
|
||||||
"Albums to be backed up",
|
"backup_controller_page_to_backup",
|
||||||
style: TextStyle(color: Color(0xFF808080), fontSize: 12),
|
style: TextStyle(color: Color(0xFF808080), fontSize: 12),
|
||||||
),
|
).tr(),
|
||||||
_buildSelectedAlbumName(),
|
_buildSelectedAlbumName(),
|
||||||
_buildExcludedAlbumName()
|
_buildExcludedAlbumName()
|
||||||
],
|
],
|
||||||
@ -234,14 +241,14 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
AutoRouter.of(context).push(const BackupAlbumSelectionRoute());
|
AutoRouter.of(context).push(const BackupAlbumSelectionRoute());
|
||||||
},
|
},
|
||||||
child: const Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 16.0,
|
vertical: 16.0,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: const Text(
|
||||||
"Select",
|
"backup_controller_page_select",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -387,9 +394,9 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
title: const Text(
|
title: const Text(
|
||||||
"Backup",
|
"backup_controller_page_backup",
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.watch(websocketProvider.notifier).listenUploadEvent();
|
ref.watch(websocketProvider.notifier).listenUploadEvent();
|
||||||
@ -405,27 +412,27 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
child: ListView(
|
child: ListView(
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: const Text(
|
||||||
"Backup Information",
|
"backup_controller_page_info",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
_buildFolderSelectionTile(),
|
_buildFolderSelectionTile(),
|
||||||
BackupInfoCard(
|
BackupInfoCard(
|
||||||
title: "Total",
|
title: "backup_controller_page_total".tr(),
|
||||||
subtitle: "All unique photos and videos from selected albums",
|
subtitle: "backup_controller_page_total_sub".tr(),
|
||||||
info: "${backupState.allUniqueAssets.length}",
|
info: "${backupState.allUniqueAssets.length}",
|
||||||
),
|
),
|
||||||
BackupInfoCard(
|
BackupInfoCard(
|
||||||
title: "Backup",
|
title: "backup_controller_page_backup".tr(),
|
||||||
subtitle: "Backed up photos and videos",
|
subtitle: "backup_controller_page_backup_sub".tr(),
|
||||||
info: "${backupState.selectedAlbumsBackupAssetsIds.length}",
|
info: "${backupState.selectedAlbumsBackupAssetsIds.length}",
|
||||||
),
|
),
|
||||||
BackupInfoCard(
|
BackupInfoCard(
|
||||||
title: "Remainder",
|
title: "backup_controller_page_remainder".tr(),
|
||||||
subtitle: "Remaining photos and albums to back up from selection",
|
subtitle: "backup_controller_page_remainder_sub".tr(),
|
||||||
info:
|
info:
|
||||||
"${backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length}",
|
"${backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length}",
|
||||||
),
|
),
|
||||||
@ -452,12 +459,12 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
ref.read(backupProvider.notifier).cancelBackup();
|
ref.read(backupProvider.notifier).cancelBackup();
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"CANCEL",
|
"backup_controller_page_cancel",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
).tr(),
|
||||||
)
|
)
|
||||||
: ElevatedButton(
|
: ElevatedButton(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
@ -467,12 +474,12 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
onPressed: shouldBackup ? startBackup : null,
|
onPressed: shouldBackup ? startBackup : null,
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"START BACKUP",
|
"backup_controller_page_start_backup",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/modules/home/ui/delete_diaglog.dart';
|
import 'package:immich_mobile/modules/home/ui/delete_diaglog.dart';
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ class ControlBottomAppBar extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
ControlBoxButton(
|
ControlBoxButton(
|
||||||
iconData: Icons.delete_forever_rounded,
|
iconData: Icons.delete_forever_rounded,
|
||||||
label: "Delete",
|
label: "control_bottom_app_bar_delete".tr(),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart';
|
import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart';
|
||||||
@ -19,7 +20,7 @@ class DailyTitleText extends ConsumerWidget {
|
|||||||
var currentYear = DateTime.now().year;
|
var currentYear = DateTime.now().year;
|
||||||
var groupYear = DateTime.parse(isoDate).year;
|
var groupYear = DateTime.parse(isoDate).year;
|
||||||
var formatDateTemplate =
|
var formatDateTemplate =
|
||||||
currentYear == groupYear ? 'E, MMM dd' : 'E, MMM dd, yyyy';
|
currentYear == groupYear ? "daily_title_text_date".tr() : "daily_title_text_date_year".tr();
|
||||||
var dateText =
|
var dateText =
|
||||||
DateFormat(formatDateTemplate).format(DateTime.parse(isoDate));
|
DateFormat(formatDateTemplate).format(DateTime.parse(isoDate));
|
||||||
var isMultiSelectEnable =
|
var isMultiSelectEnable =
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||||
@ -13,18 +14,17 @@ class DeleteDialog extends ConsumerWidget {
|
|||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
backgroundColor: Colors.grey[200],
|
backgroundColor: Colors.grey[200],
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
title: const Text("Delete Permanently"),
|
title: const Text("delete_dialog_title").tr(),
|
||||||
content: const Text(
|
content: const Text("delete_dialog_alert").tr(),
|
||||||
"These items will be permanently deleted from Immich and from your device"),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Cancel",
|
"delete_dialog_cancel",
|
||||||
style: TextStyle(color: Colors.blueGrey),
|
style: TextStyle(color: Colors.blueGrey),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -36,9 +36,9 @@ class DeleteDialog extends ConsumerWidget {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"Delete",
|
"delete_dialog_ok",
|
||||||
style: TextStyle(color: Colors.red[400]),
|
style: TextStyle(color: Colors.red[400]),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
@ -11,7 +12,7 @@ class MonthlyTitleText extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var monthTitleText = DateFormat('MMMM y').format(DateTime.parse(isoDate));
|
var monthTitleText = DateFormat("monthly_title_text_date_format".tr()).format(DateTime.parse(isoDate));
|
||||||
|
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
@ -183,12 +184,12 @@ class ProfileDrawer extends HookConsumerWidget {
|
|||||||
color: Colors.black54,
|
color: Colors.black54,
|
||||||
),
|
),
|
||||||
title: const Text(
|
title: const Text(
|
||||||
"Sign Out",
|
"profile_drawer_sign_out",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black54,
|
color: Colors.black54,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
bool res =
|
bool res =
|
||||||
await ref.watch(authenticationProvider.notifier).logout();
|
await ref.watch(authenticationProvider.notifier).logout();
|
||||||
@ -227,7 +228,7 @@ class ProfileDrawer extends HookConsumerWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
serverInfoState.isVersionMismatch
|
serverInfoState.isVersionMismatch
|
||||||
? serverInfoState.versionMismatchErrorMessage
|
? serverInfoState.versionMismatchErrorMessage
|
||||||
: "Client and Server are up-to-date",
|
: "profile_drawer_client_server_up_to_date".tr(),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@ -21,7 +22,7 @@ class LoginForm extends HookConsumerWidget {
|
|||||||
final passwordController =
|
final passwordController =
|
||||||
useTextEditingController.fromValue(TextEditingValue.empty);
|
useTextEditingController.fromValue(TextEditingValue.empty);
|
||||||
final serverEndpointController =
|
final serverEndpointController =
|
||||||
useTextEditingController(text: 'http://your-server-ip:2283/api');
|
useTextEditingController(text: 'login_endpoint_hint'.tr());
|
||||||
final isSaveLoginInfo = useState<bool>(false);
|
final isSaveLoginInfo = useState<bool>(false);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
@ -73,12 +74,12 @@ class LoginForm extends HookConsumerWidget {
|
|||||||
borderRadius: BorderRadius.circular(5)),
|
borderRadius: BorderRadius.circular(5)),
|
||||||
enableFeedback: true,
|
enableFeedback: true,
|
||||||
title: const Text(
|
title: const Text(
|
||||||
"Stay logged in",
|
"login_form_save_login",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.grey),
|
color: Colors.grey),
|
||||||
),
|
).tr(),
|
||||||
value: isSaveLoginInfo.value,
|
value: isSaveLoginInfo.value,
|
||||||
onChanged: (switchValue) {
|
onChanged: (switchValue) {
|
||||||
if (switchValue != null) {
|
if (switchValue != null) {
|
||||||
@ -107,11 +108,11 @@ class ServerEndpointInput extends StatelessWidget {
|
|||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
String? _validateInput(String? url) {
|
String? _validateInput(String? url) {
|
||||||
|
|
||||||
if (url?.startsWith(RegExp(r'https?://')) == true) {
|
if (url?.startsWith(RegExp(r'https?://')) == true) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return 'Please specify http:// or https://';
|
return 'login_form_err_http'.tr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,10 +120,10 @@ class ServerEndpointInput extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Server Endpoint URL',
|
labelText: 'login_form_endpoint_url'.tr(),
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
hintText: 'http://your-server-ip:port',
|
hintText: 'login_form_endpoint_hint'.tr(),
|
||||||
),
|
),
|
||||||
validator: _validateInput,
|
validator: _validateInput,
|
||||||
autovalidateMode: AutovalidateMode.always,
|
autovalidateMode: AutovalidateMode.always,
|
||||||
@ -137,9 +138,10 @@ class EmailInput extends StatelessWidget {
|
|||||||
|
|
||||||
String? _validateInput(String? email) {
|
String? _validateInput(String? email) {
|
||||||
if (email == null || email == '') return null;
|
if (email == null || email == '') return null;
|
||||||
if (email.endsWith(' ')) return 'Trailing whitespace';
|
if (email.endsWith(' ')) return 'login_form_err_trailing_whitespace'.tr();
|
||||||
if (email.startsWith(' ')) return 'Leading whitespace';
|
if (email.startsWith(' ')) return 'login_form_err_leading_whitespace'.tr();
|
||||||
if (email.contains(' ') || !email.contains('@')) return 'Invalid Email';
|
if (email.contains(' ') || !email.contains('@'))
|
||||||
|
return 'login_form_err_invalid_email'.tr();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,10 +149,10 @@ class EmailInput extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Email',
|
labelText: 'login_form_label_email'.tr(),
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
hintText: 'youremail@email.com',
|
hintText: 'login_form_email_hint'.tr(),
|
||||||
),
|
),
|
||||||
validator: _validateInput,
|
validator: _validateInput,
|
||||||
autovalidateMode: AutovalidateMode.always,
|
autovalidateMode: AutovalidateMode.always,
|
||||||
@ -168,10 +170,10 @@ class PasswordInput extends StatelessWidget {
|
|||||||
return TextFormField(
|
return TextFormField(
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Password',
|
labelText: 'login_form_label_password'.tr(),
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
hintText: 'password'),
|
hintText: 'login_form_password_hint'.tr()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,15 +224,14 @@ class LoginButton extends ConsumerWidget {
|
|||||||
} else {
|
} else {
|
||||||
ImmichToast.show(
|
ImmichToast.show(
|
||||||
context: context,
|
context: context,
|
||||||
msg:
|
msg: "login_failed".tr(),
|
||||||
"Error logging you in, check server url, email and password!",
|
|
||||||
toastType: ToastType.error,
|
toastType: ToastType.error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Login",
|
"login_form_button_text",
|
||||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
));
|
).tr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -47,8 +48,8 @@ class SearchBar extends HookConsumerWidget with PreferredSizeWidget {
|
|||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
ref.watch(searchPageStateProvider.notifier).setSearchTerm(value);
|
ref.watch(searchPageStateProvider.notifier).setSearchTerm(value);
|
||||||
},
|
},
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Search your photos',
|
hintText: 'search_bar_hint'.tr(),
|
||||||
enabledBorder: UnderlineInputBorder(
|
enabledBorder: UnderlineInputBorder(
|
||||||
borderSide: BorderSide(color: Colors.transparent),
|
borderSide: BorderSide(color: Colors.transparent),
|
||||||
),
|
),
|
||||||
|
@ -55,7 +55,7 @@ class ThumbnailWithInfo extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: MediaQuery.of(context).size.width / 3,
|
width: MediaQuery.of(context).size.width / 3,
|
||||||
child: Text(
|
child: Text(
|
||||||
textInfo.capitalizeFirstLetter(),
|
textInfo,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
@ -82,7 +83,7 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
return ThumbnailWithInfo(
|
return ThumbnailWithInfo(
|
||||||
imageUrl:
|
imageUrl:
|
||||||
'https://images.unsplash.com/photo-1612178537253-bccd437b730e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8Ymxhbmt8ZW58MHx8MHx8&auto=format&fit=crop&w=700&q=60',
|
'https://images.unsplash.com/photo-1612178537253-bccd437b730e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8Ymxhbmt8ZW58MHx8MHx8&auto=format&fit=crop&w=700&q=60',
|
||||||
textInfo: 'No Places Info Available',
|
textInfo: 'search_page_no_places'.tr(),
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -134,7 +135,7 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
return ThumbnailWithInfo(
|
return ThumbnailWithInfo(
|
||||||
imageUrl:
|
imageUrl:
|
||||||
'https://images.unsplash.com/photo-1612178537253-bccd437b730e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8Ymxhbmt8ZW58MHx8MHx8&auto=format&fit=crop&w=700&q=60',
|
'https://images.unsplash.com/photo-1612178537253-bccd437b730e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8Ymxhbmt8ZW58MHx8MHx8&auto=format&fit=crop&w=700&q=60',
|
||||||
textInfo: 'No Object Info Available',
|
textInfo: 'sarch_no_objects'.tr(),
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -158,20 +159,20 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
ListView(
|
ListView(
|
||||||
children: [
|
children: [
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(16.0),
|
padding: EdgeInsets.all(16.0),
|
||||||
child: Text(
|
child: const Text(
|
||||||
"Places",
|
"search_page_places",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
_buildPlaces(),
|
_buildPlaces(),
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(16.0),
|
padding: EdgeInsets.all(16.0),
|
||||||
child: Text(
|
child: const Text(
|
||||||
"Things",
|
"search_page_things",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
_buildThings()
|
_buildThings()
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
@ -66,8 +67,8 @@ class SearchResultPage extends HookConsumerWidget {
|
|||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
ref.watch(searchPageStateProvider.notifier).setSearchTerm(value);
|
ref.watch(searchPageStateProvider.notifier).setSearchTerm(value);
|
||||||
},
|
},
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'New Search',
|
hintText: 'search_result_page_new_search_hint'.tr(),
|
||||||
enabledBorder: UnderlineInputBorder(
|
enabledBorder: UnderlineInputBorder(
|
||||||
borderSide: BorderSide(color: Colors.transparent),
|
borderSide: BorderSide(color: Colors.transparent),
|
||||||
),
|
),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/modules/sharing/providers/album_title.provider.dart';
|
import 'package:immich_mobile/modules/sharing/providers/album_title.provider.dart';
|
||||||
@ -59,7 +60,7 @@ class AlbumTitleTextField extends ConsumerWidget {
|
|||||||
borderSide: const BorderSide(color: Colors.transparent),
|
borderSide: const BorderSide(color: Colors.transparent),
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
hintText: 'Add a title',
|
hintText: 'share_add_title'.tr(),
|
||||||
focusColor: Colors.grey[300],
|
focusColor: Colors.grey[300],
|
||||||
fillColor: Colors.grey[200],
|
fillColor: Colors.grey[200],
|
||||||
filled: isAlbumTitleTextFieldFocus.value,
|
filled: isAlbumTitleTextFieldFocus.value,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -45,7 +46,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||||||
} else {
|
} else {
|
||||||
ImmichToast.show(
|
ImmichToast.show(
|
||||||
context: context,
|
context: context,
|
||||||
msg: "Failed to delete album",
|
msg: "album_viewer_appbar_share_err_delete".tr(),
|
||||||
toastType: ToastType.error,
|
toastType: ToastType.error,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
@ -67,7 +68,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
ImmichToast.show(
|
ImmichToast.show(
|
||||||
context: context,
|
context: context,
|
||||||
msg: "Failed to leave album",
|
msg: "album_viewer_appbar_share_err_leave".tr(),
|
||||||
toastType: ToastType.error,
|
toastType: ToastType.error,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
@ -93,7 +94,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
ImmichToast.show(
|
ImmichToast.show(
|
||||||
context: context,
|
context: context,
|
||||||
msg: "There are problems in removing assets from album",
|
msg: "album_viewer_appbar_share_err_remove".tr(),
|
||||||
toastType: ToastType.error,
|
toastType: ToastType.error,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
@ -108,9 +109,9 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const Icon(Icons.delete_sweep_rounded),
|
leading: const Icon(Icons.delete_sweep_rounded),
|
||||||
title: const Text(
|
title: const Text(
|
||||||
'Remove from album',
|
'album_viewer_appbar_share_remove',
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
onTap: () => _onRemoveFromAlbumPressed(albumId),
|
onTap: () => _onRemoveFromAlbumPressed(albumId),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -121,18 +122,18 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const Icon(Icons.delete_forever_rounded),
|
leading: const Icon(Icons.delete_forever_rounded),
|
||||||
title: const Text(
|
title: const Text(
|
||||||
'Delete album',
|
'album_viewer_appbar_share_delete',
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
onTap: () => _onDeleteAlbumPressed(albumId),
|
onTap: () => _onDeleteAlbumPressed(albumId),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const Icon(Icons.person_remove_rounded),
|
leading: const Icon(Icons.person_remove_rounded),
|
||||||
title: const Text(
|
title: const Text(
|
||||||
'Leave album',
|
'album_viewer_appbar_share_leave',
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
onTap: () => _onLeaveAlbumPressed(albumId),
|
onTap: () => _onLeaveAlbumPressed(albumId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -176,7 +177,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||||||
if (!isSuccess) {
|
if (!isSuccess) {
|
||||||
ImmichToast.show(
|
ImmichToast.show(
|
||||||
context: context,
|
context: context,
|
||||||
msg: "Failed to change album title",
|
msg: "album_viewer_appbar_share_err_title".tr(),
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
toastType: ToastType.error,
|
toastType: ToastType.error,
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -74,7 +75,7 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
|
|||||||
focusColor: Colors.grey[300],
|
focusColor: Colors.grey[300],
|
||||||
fillColor: Colors.grey[200],
|
fillColor: Colors.grey[200],
|
||||||
filled: titleFocusNode.hasFocus,
|
filled: titleFocusNode.hasFocus,
|
||||||
hintText: 'Add a title',
|
hintText: 'share_add_title'.tr(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
|
||||||
@ -51,10 +52,10 @@ class SharingSliverAppBar extends StatelessWidget {
|
|||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
label: const Text(
|
label: const Text(
|
||||||
"Create shared album",
|
"sharing_silver_appbar_create_shared_album",
|
||||||
style:
|
style:
|
||||||
TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
|
TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -73,10 +74,10 @@ class SharingSliverAppBar extends StatelessWidget {
|
|||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
label: const Text(
|
label: const Text(
|
||||||
"Share with partner",
|
"sharing_silver_appbar_share_partner",
|
||||||
style:
|
style:
|
||||||
TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
|
TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -204,13 +205,13 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||||||
AlbumActionOutlinedButton(
|
AlbumActionOutlinedButton(
|
||||||
iconData: Icons.add_photo_alternate_outlined,
|
iconData: Icons.add_photo_alternate_outlined,
|
||||||
onPressed: () => _onAddPhotosPressed(albumInfo),
|
onPressed: () => _onAddPhotosPressed(albumInfo),
|
||||||
labelText: "Add photos",
|
labelText: "share_add_photos".tr(),
|
||||||
),
|
),
|
||||||
if (userId == albumInfo.ownerId)
|
if (userId == albumInfo.ownerId)
|
||||||
AlbumActionOutlinedButton(
|
AlbumActionOutlinedButton(
|
||||||
iconData: Icons.person_add_alt_rounded,
|
iconData: Icons.person_add_alt_rounded,
|
||||||
onPressed: () => _onAddUsersPressed(albumInfo),
|
onPressed: () => _onAddUsersPressed(albumInfo),
|
||||||
labelText: "Add users",
|
labelText: "album_viewer_page_share_add_users".tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -65,9 +66,9 @@ class AssetSelectionPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
title: selectedAssets.isEmpty
|
title: selectedAssets.isEmpty
|
||||||
? const Text(
|
? const Text(
|
||||||
'Add photos',
|
'share_add_photos',
|
||||||
style: TextStyle(fontSize: 18),
|
style: TextStyle(fontSize: 18),
|
||||||
)
|
).tr()
|
||||||
: Text(
|
: Text(
|
||||||
_buildAssetCountText(),
|
_buildAssetCountText(),
|
||||||
style: const TextStyle(fontSize: 18),
|
style: const TextStyle(fontSize: 18),
|
||||||
@ -86,9 +87,9 @@ class AssetSelectionPage extends HookConsumerWidget {
|
|||||||
AutoRouter.of(context).pop(payload);
|
AutoRouter.of(context).pop(payload);
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Add",
|
"share_add",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -64,13 +65,13 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
_buildTitle() {
|
_buildTitle() {
|
||||||
if (selectedAssets.isEmpty) {
|
if (selectedAssets.isEmpty) {
|
||||||
return const SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(top: 200, left: 18),
|
padding: EdgeInsets.only(top: 200, left: 18),
|
||||||
child: Text(
|
child: Text(
|
||||||
'ADD ASSETS',
|
'create_shared_album_page_share_add_assets',
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -97,12 +98,12 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
|
|||||||
label: Padding(
|
label: Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Select Photos',
|
'create_shared_album_page_share_select_photos',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: Colors.grey[700],
|
color: Colors.grey[700],
|
||||||
fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -123,7 +124,7 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
|
|||||||
AlbumActionOutlinedButton(
|
AlbumActionOutlinedButton(
|
||||||
iconData: Icons.add_photo_alternate_outlined,
|
iconData: Icons.add_photo_alternate_outlined,
|
||||||
onPressed: _onSelectPhotosButtonPressed,
|
onPressed: _onSelectPhotosButtonPressed,
|
||||||
labelText: "Add photos",
|
labelText: "share_add_photos".tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -169,16 +170,16 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
icon: const Icon(Icons.close_rounded)),
|
icon: const Icon(Icons.close_rounded)),
|
||||||
title: const Text(
|
title: const Text(
|
||||||
'Create album',
|
'share_create_album',
|
||||||
style: TextStyle(color: Colors.black),
|
style: TextStyle(color: Colors.black),
|
||||||
),
|
).tr(),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: albumTitleController.text.isNotEmpty
|
onPressed: albumTitleController.text.isNotEmpty
|
||||||
? _showSelectUserPage
|
? _showSelectUserPage
|
||||||
: null,
|
: null,
|
||||||
child: const Text(
|
child: Text(
|
||||||
'Share',
|
'create_shared_album_page_share'.tr(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -68,10 +69,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||||||
Wrap(
|
Wrap(
|
||||||
children: [...usersChip],
|
children: [...usersChip],
|
||||||
),
|
),
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(16.0),
|
padding: EdgeInsets.all(16.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Suggestions',
|
'select_additional_user_for_sharing_page_suggestions'.tr(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
@ -112,9 +113,9 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text(
|
title: const Text(
|
||||||
'Invite to album',
|
'share_invite',
|
||||||
style: TextStyle(color: Colors.black),
|
style: TextStyle(color: Colors.black),
|
||||||
),
|
).tr(),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
@ -128,9 +129,9 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||||||
onPressed:
|
onPressed:
|
||||||
sharedUsersList.value.isEmpty ? null : _addNewUsersHandler,
|
sharedUsersList.value.isEmpty ? null : _addNewUsersHandler,
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Add",
|
"share_add",
|
||||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -36,8 +37,7 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||||||
.navigate(const TabControllerRoute(children: [SharingRoute()]));
|
.navigate(const TabControllerRoute(children: [SharingRoute()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
const ScaffoldMessenger(
|
ScaffoldMessenger(child: SnackBar(content: Text('select_user_for_sharing_page_err_album').tr()));
|
||||||
child: SnackBar(content: Text('Failed to create album')));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildTileIcon(User user) {
|
_buildTileIcon(User user) {
|
||||||
@ -84,15 +84,15 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||||||
Wrap(
|
Wrap(
|
||||||
children: [...usersChip],
|
children: [...usersChip],
|
||||||
),
|
),
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(16.0),
|
padding: EdgeInsets.all(16.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Suggestions',
|
'share_suggestions',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.bold),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
@ -128,9 +128,9 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text(
|
title: const Text(
|
||||||
'Invite to album',
|
'share_invite',
|
||||||
style: TextStyle(color: Colors.black),
|
style: TextStyle(color: Colors.black),
|
||||||
),
|
).tr(),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
@ -144,9 +144,9 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||||||
onPressed:
|
onPressed:
|
||||||
sharedUsersList.value.isEmpty ? null : _createSharedAlbum,
|
sharedUsersList.value.isEmpty ? null : _createSharedAlbum,
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Create Album",
|
"share_create_album",
|
||||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
))
|
).tr())
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: suggestedShareUsers.when(
|
body: suggestedShareUsers.when(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@ -104,20 +105,20 @@ class SharingPage extends HookConsumerWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'EMPTY LIST',
|
'sharing_page_empty_list',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Create shared albums to share photos and videos with people in your network.',
|
'sharing_page_description',
|
||||||
style: TextStyle(fontSize: 12, color: Colors.grey[700]),
|
style: TextStyle(fontSize: 12, color: Colors.grey[700]),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -131,15 +132,15 @@ class SharingPage extends HookConsumerWidget {
|
|||||||
body: CustomScrollView(
|
body: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
const SharingSliverAppBar(),
|
const SharingSliverAppBar(),
|
||||||
const SliverPadding(
|
SliverPadding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||||
sliver: SliverToBoxAdapter(
|
sliver: SliverToBoxAdapter(
|
||||||
child: Text(
|
child: Text(
|
||||||
"Shared albums",
|
"sharing_page_album",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
).tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
sharedAlbums.isNotEmpty
|
sharedAlbums.isNotEmpty
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart';
|
import 'package:immich_mobile/modules/home/providers/home_page_state.provider.dart';
|
||||||
@ -41,13 +42,16 @@ class TabControllerPage extends ConsumerWidget {
|
|||||||
onTap: (index) {
|
onTap: (index) {
|
||||||
tabsRouter.setActiveIndex(index);
|
tabsRouter.setActiveIndex(index);
|
||||||
},
|
},
|
||||||
items: const [
|
items: [
|
||||||
BottomNavigationBarItem(
|
BottomNavigationBarItem(
|
||||||
label: 'Photos', icon: Icon(Icons.photo)),
|
label: 'tab_controller_nav_photos'.tr(),
|
||||||
|
icon: const Icon(Icons.photo)),
|
||||||
BottomNavigationBarItem(
|
BottomNavigationBarItem(
|
||||||
label: 'Search', icon: Icon(Icons.search)),
|
label: 'tab_controller_nav_search'.tr(),
|
||||||
|
icon: const Icon(Icons.search)),
|
||||||
BottomNavigationBarItem(
|
BottomNavigationBarItem(
|
||||||
label: 'Sharing', icon: Icon(Icons.group_outlined)),
|
label: 'tab_controller_nav_sharing'.tr(),
|
||||||
|
icon: const Icon(Icons.group_outlined)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -40,14 +41,14 @@ class VersionAnnouncementOverlay extends HookConsumerWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
const Text(
|
||||||
"New Server Version Available 🎉",
|
"version_announcement_overlay_title",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontFamily: 'WorkSans',
|
fontFamily: 'WorkSans',
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.indigo,
|
color: Colors.indigo,
|
||||||
),
|
),
|
||||||
),
|
).tr(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 16.0),
|
padding: const EdgeInsets.only(top: 16.0),
|
||||||
child: RichText(
|
child: RichText(
|
||||||
@ -58,9 +59,8 @@ class VersionAnnouncementOverlay extends HookConsumerWidget {
|
|||||||
color: Colors.black87,
|
color: Colors.black87,
|
||||||
height: 1.2),
|
height: 1.2),
|
||||||
children: <TextSpan>[
|
children: <TextSpan>[
|
||||||
const TextSpan(
|
TextSpan(
|
||||||
text:
|
text: 'version_announcement_overlay_text_1'.tr(),
|
||||||
'Hi friend, there is a new release of',
|
|
||||||
),
|
),
|
||||||
const TextSpan(
|
const TextSpan(
|
||||||
text: ' Immich ',
|
text: ' Immich ',
|
||||||
@ -70,22 +70,21 @@ class VersionAnnouncementOverlay extends HookConsumerWidget {
|
|||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const TextSpan(
|
TextSpan(
|
||||||
text:
|
text: "version_announcement_overlay_text_2".tr(),
|
||||||
"please take your time to visit the ",
|
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: "release note",
|
text: "version_announcement_overlay_release_notes"
|
||||||
|
.tr(),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = goToReleaseNote,
|
..onTap = goToReleaseNote,
|
||||||
),
|
),
|
||||||
const TextSpan(
|
TextSpan(
|
||||||
text:
|
text: "version_announcement_overlay_text_3".tr(),
|
||||||
" and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
|
)
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -93,24 +92,23 @@ class VersionAnnouncementOverlay extends HookConsumerWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 16.0),
|
padding: const EdgeInsets.only(top: 16.0),
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
shape: const StadiumBorder(),
|
shape: const StadiumBorder(),
|
||||||
visualDensity: VisualDensity.standard,
|
visualDensity: VisualDensity.standard,
|
||||||
primary: Colors.indigo,
|
primary: Colors.indigo,
|
||||||
onPrimary: Colors.grey[50],
|
onPrimary: Colors.grey[50],
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 10, horizontal: 25),
|
vertical: 10, horizontal: 25),
|
||||||
),
|
|
||||||
onPressed: onAcknowledgeTapped,
|
|
||||||
child: const Text(
|
|
||||||
"Acknowledge",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
),
|
||||||
),
|
onPressed: onAcknowledgeTapped,
|
||||||
),
|
child: const Text(
|
||||||
),
|
"version_announcement_overlay_ack",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
).tr()),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -253,6 +253,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.6"
|
version: "4.0.6"
|
||||||
|
easy_localization:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: easy_localization
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
|
easy_logger:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: easy_logger
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.2"
|
||||||
equatable:
|
equatable:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -335,6 +349,11 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
flutter_localizations:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_map:
|
flutter_map:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -842,6 +861,62 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.27.3"
|
version: "0.27.3"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.15"
|
||||||
|
shared_preferences_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.12"
|
||||||
|
shared_preferences_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_ios
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
shared_preferences_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_linux
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
shared_preferences_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_macos
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.4"
|
||||||
|
shared_preferences_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
shared_preferences_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.4"
|
||||||
|
shared_preferences_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_windows
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -40,6 +40,7 @@ dependencies:
|
|||||||
url_launcher: ^6.1.3
|
url_launcher: ^6.1.3
|
||||||
http: 0.13.4
|
http: 0.13.4
|
||||||
cancellation_token_http: ^1.1.0
|
cancellation_token_http: ^1.1.0
|
||||||
|
easy_localization: ^3.0.1
|
||||||
|
|
||||||
path: ^1.8.1
|
path: ^1.8.1
|
||||||
path_provider: ^2.0.11
|
path_provider: ^2.0.11
|
||||||
@ -59,6 +60,7 @@ flutter:
|
|||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
assets:
|
assets:
|
||||||
- assets/
|
- assets/
|
||||||
|
- assets/i18n/
|
||||||
fonts:
|
fonts:
|
||||||
- family: WorkSans
|
- family: WorkSans
|
||||||
fonts:
|
fonts:
|
||||||
|
18
mobile/scripts/check_i18n_keys.py
Normal file
18
mobile/scripts/check_i18n_keys.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
def main():
|
||||||
|
with open('assets/i18n/en-US.json', 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
for k in data.keys():
|
||||||
|
print(k)
|
||||||
|
sp = subprocess.run(['sh', '-c', f'grep -r --include="*.dart" "{k}"'])
|
||||||
|
|
||||||
|
if sp.returncode != 0:
|
||||||
|
print("Not found in source code!")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
19
mobile/scripts/check_key_uniform.py
Normal file
19
mobile/scripts/check_key_uniform.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("CHECK GERMAN TRANSLATIONS")
|
||||||
|
with open('assets/i18n/de-DE.json', 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
for k in data.keys():
|
||||||
|
print(k)
|
||||||
|
sp = subprocess.run(['sh', '-c', f'grep -r --include="./assets/i18n/en-US.json" "{k}"'])
|
||||||
|
|
||||||
|
if sp.returncode != 0:
|
||||||
|
print(f"Outdated Key! {k}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
x
Reference in New Issue
Block a user