Merge branch 'main' into clip

This commit is contained in:
vishnukvmd 2023-11-14 09:35:41 +05:30
commit 3d0fefb715
38 changed files with 611 additions and 118 deletions

View file

@ -278,6 +278,7 @@
"${BUILT_PRODUCTS_DIR}/background_fetch/background_fetch.framework",
"${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework",
"${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework",
"${BUILT_PRODUCTS_DIR}/file_saver/file_saver.framework",
"${BUILT_PRODUCTS_DIR}/fk_user_agent/fk_user_agent.framework",
"${BUILT_PRODUCTS_DIR}/flutter_email_sender/flutter_email_sender.framework",
"${BUILT_PRODUCTS_DIR}/flutter_image_compress/flutter_image_compress.framework",
@ -356,6 +357,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/background_fetch.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_saver.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fk_user_agent.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_email_sender.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_image_compress.framework",

View file

@ -24,6 +24,8 @@ class MessageLookup extends MessageLookupByLibrary {
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"addToHiddenAlbum":
MessageLookupByLibrary.simpleMessage("Add to hidden album"),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
"moveToHiddenAlbum":
MessageLookupByLibrary.simpleMessage("Move to hidden album")
};

View file

@ -503,6 +503,8 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteAlbumsDialogBody": MessageLookupByLibrary.simpleMessage(
"Damit werden alle leeren Alben gelöscht. Dies ist nützlich, wenn du das Durcheinander in deiner Albenliste verringern möchtest."),
"deleteAll": MessageLookupByLibrary.simpleMessage("Alle löschen"),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
"deleteEmailRequest": MessageLookupByLibrary.simpleMessage(
"Bitte sende eine E-Mail an <warning>account-deletion@ente.io</warning> von Deiner bei uns hinterlegten E-Mail-Adresse."),
"deleteEmptyAlbums":

View file

@ -187,6 +187,8 @@ class MessageLookup extends MessageLookupByLibrary {
static String m59(count) =>
"${Intl.plural(count, zero: '', one: '1 day', other: '${count} days')}";
static String m64(endDate) => "Valid till ${endDate}";
static String m60(email) => "Verify ${email}";
static String m61(email) => "We have sent a mail to <green>${email}</green>";
@ -218,6 +220,9 @@ class MessageLookup extends MessageLookupByLibrary {
"addLocation": MessageLookupByLibrary.simpleMessage("Add location"),
"addLocationButton": MessageLookupByLibrary.simpleMessage("Add"),
"addMore": MessageLookupByLibrary.simpleMessage("Add more"),
"addOnPageSubtitle":
MessageLookupByLibrary.simpleMessage("Details of add-ons"),
"addOns": MessageLookupByLibrary.simpleMessage("Add-ons"),
"addPhotos": MessageLookupByLibrary.simpleMessage("Add photos"),
"addSelected": MessageLookupByLibrary.simpleMessage("Add selected"),
"addToAlbum": MessageLookupByLibrary.simpleMessage("Add to album"),
@ -486,6 +491,8 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteAlbumsDialogBody": MessageLookupByLibrary.simpleMessage(
"This will delete all empty albums. This is useful when you want to reduce the clutter in your album list."),
"deleteAll": MessageLookupByLibrary.simpleMessage("Delete All"),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
"deleteEmailRequest": MessageLookupByLibrary.simpleMessage(
"Please send an email to <warning>account-deletion@ente.io</warning> from your registered email address."),
"deleteEmptyAlbums":
@ -690,6 +697,10 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Grant permission"),
"groupNearbyPhotos":
MessageLookupByLibrary.simpleMessage("Group nearby photos"),
"hearUsExplanation": MessageLookupByLibrary.simpleMessage(
"We don\'t track app installs. It\'d help if you told us where you found us!"),
"hearUsWhereTitle": MessageLookupByLibrary.simpleMessage(
"How did you hear about Ente? (optional)"),
"hidden": MessageLookupByLibrary.simpleMessage("Hidden"),
"hide": MessageLookupByLibrary.simpleMessage("Hide"),
"hiding": MessageLookupByLibrary.simpleMessage("Hiding..."),
@ -1325,6 +1336,7 @@ class MessageLookup extends MessageLookupByLibrary {
"useSelectedPhoto":
MessageLookupByLibrary.simpleMessage("Use selected photo"),
"usedSpace": MessageLookupByLibrary.simpleMessage("Used space"),
"validTill": m64,
"verificationFailedPleaseTryAgain":
MessageLookupByLibrary.simpleMessage(
"Verification failed, please try again"),
@ -1342,6 +1354,7 @@ class MessageLookup extends MessageLookupByLibrary {
"videoSmallCase": MessageLookupByLibrary.simpleMessage("video"),
"viewActiveSessions":
MessageLookupByLibrary.simpleMessage("View active sessions"),
"viewAddOnButton": MessageLookupByLibrary.simpleMessage("View add-ons"),
"viewAll": MessageLookupByLibrary.simpleMessage("View all"),
"viewAllExifData":
MessageLookupByLibrary.simpleMessage("View all EXIF data"),

View file

@ -451,6 +451,8 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteAlbumsDialogBody": MessageLookupByLibrary.simpleMessage(
"Esto eliminará todos los álbunes vacíos. Esto es útil cuando quieres reducir el desorden en tu lista de álbumes."),
"deleteAll": MessageLookupByLibrary.simpleMessage("Borrar Todo"),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
"deleteEmailRequest": MessageLookupByLibrary.simpleMessage(
"Por favor, envíe un correo electrónico a <warning>account-deletion@ente.io</warning> desde su dirección de correo electrónico registrada."),
"deleteEmptyAlbums":

View file

@ -509,6 +509,8 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteAlbumsDialogBody": MessageLookupByLibrary.simpleMessage(
"Ceci supprimera tous les albums vides. Ceci est utile lorsque vous voulez réduire l\'encombrement dans votre liste d\'albums."),
"deleteAll": MessageLookupByLibrary.simpleMessage("Tout Supprimer"),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
"deleteEmailRequest": MessageLookupByLibrary.simpleMessage(
"Veuillez envoyer un e-mail à <warning>account-deletion@ente.io</warning> à partir de votre adresse e-mail enregistrée."),
"deleteEmptyAlbums":

View file

@ -187,6 +187,8 @@ class MessageLookup extends MessageLookupByLibrary {
static String m59(count) =>
"${Intl.plural(count, zero: '', one: '1 giorno', other: '${count} giorni')}";
static String m64(endDate) => "Valido fino al ${endDate}";
static String m60(email) => "Verifica ${email}";
static String m61(email) =>
@ -220,6 +222,9 @@ class MessageLookup extends MessageLookupByLibrary {
"addLocation": MessageLookupByLibrary.simpleMessage("Aggiungi luogo"),
"addLocationButton": MessageLookupByLibrary.simpleMessage("Aggiungi"),
"addMore": MessageLookupByLibrary.simpleMessage("Aggiungi altri"),
"addOnPageSubtitle": MessageLookupByLibrary.simpleMessage(
"Dettagli dei componenti aggiuntivi"),
"addOns": MessageLookupByLibrary.simpleMessage("Componenti aggiuntivi"),
"addPhotos": MessageLookupByLibrary.simpleMessage("Aggiungi foto"),
"addSelected":
MessageLookupByLibrary.simpleMessage("Aggiungi selezionate"),
@ -501,6 +506,8 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteAlbumsDialogBody": MessageLookupByLibrary.simpleMessage(
"Questo eliminerà tutti gli album vuoti. È utile quando si desidera ridurre l\'ingombro nella lista degli album."),
"deleteAll": MessageLookupByLibrary.simpleMessage("Elimina tutto"),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"Questo account è collegato ad altre app di ente, se ne utilizzi.\\n\\nI tuoi dati caricati, su tutte le app di ente, saranno pianificati per la cancellazione e il tuo account verrà eliminato definitivamente."),
"deleteEmailRequest": MessageLookupByLibrary.simpleMessage(
"Invia un\'email a <warning>account-deletion@ente.io</warning> dal tuo indirizzo email registrato."),
"deleteEmptyAlbums":
@ -710,6 +717,10 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Concedi il permesso"),
"groupNearbyPhotos": MessageLookupByLibrary.simpleMessage(
"Raggruppa foto nelle vicinanze"),
"hearUsExplanation": MessageLookupByLibrary.simpleMessage(
"Non teniamo traccia del numero di installazioni dell\'app. Sarebbe utile se ci dicesse dove ci ha trovato!"),
"hearUsWhereTitle": MessageLookupByLibrary.simpleMessage(
"Come hai sentito parlare di Ente? (opzionale)"),
"hidden": MessageLookupByLibrary.simpleMessage("Nascosti"),
"hide": MessageLookupByLibrary.simpleMessage("Nascondi"),
"hiding": MessageLookupByLibrary.simpleMessage("Nascondendo..."),
@ -1376,6 +1387,7 @@ class MessageLookup extends MessageLookupByLibrary {
"useSelectedPhoto":
MessageLookupByLibrary.simpleMessage("Usa la foto selezionata"),
"usedSpace": MessageLookupByLibrary.simpleMessage("Spazio utilizzato"),
"validTill": m64,
"verificationFailedPleaseTryAgain":
MessageLookupByLibrary.simpleMessage(
"Verifica fallita, per favore prova di nuovo"),
@ -1394,6 +1406,8 @@ class MessageLookup extends MessageLookupByLibrary {
"videoSmallCase": MessageLookupByLibrary.simpleMessage("video"),
"viewActiveSessions":
MessageLookupByLibrary.simpleMessage("Visualizza sessioni attive"),
"viewAddOnButton": MessageLookupByLibrary.simpleMessage(
"Visualizza componenti aggiuntivi"),
"viewAll": MessageLookupByLibrary.simpleMessage("Visualizza tutte"),
"viewAllExifData":
MessageLookupByLibrary.simpleMessage("Mostra tutti i dati EXIF"),

View file

@ -24,6 +24,8 @@ class MessageLookup extends MessageLookupByLibrary {
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"addToHiddenAlbum":
MessageLookupByLibrary.simpleMessage("Add to hidden album"),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
"moveToHiddenAlbum":
MessageLookupByLibrary.simpleMessage("Move to hidden album")
};

View file

@ -501,6 +501,8 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteAlbumsDialogBody": MessageLookupByLibrary.simpleMessage(
"Hiermee worden alle lege albums verwijderd. Dit is handig wanneer je rommel in je albumlijst wilt verminderen."),
"deleteAll": MessageLookupByLibrary.simpleMessage("Alles Verwijderen"),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"Dit account is gekoppeld aan andere ente apps, als je er gebruik van maakt.\\n\\nJe geüploade gegevens worden in alle ente apps gepland voor verwijdering, en je account wordt permanent verwijderd voor alle ente diensten."),
"deleteEmailRequest": MessageLookupByLibrary.simpleMessage(
"Stuur een e-mail naar <warning>account-deletion@ente.io</warning> vanaf het door jou geregistreerde e-mailadres."),
"deleteEmptyAlbums":

View file

@ -36,6 +36,8 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteAccount": MessageLookupByLibrary.simpleMessage("Slett konto"),
"deleteAccountFeedbackPrompt": MessageLookupByLibrary.simpleMessage(
"Vi er lei oss for at du forlater oss. Gi oss gjerne en tilbakemelding så vi kan forbedre oss."),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
"email": MessageLookupByLibrary.simpleMessage("E-post"),
"enterValidEmail": MessageLookupByLibrary.simpleMessage(
"Vennligst skriv inn en gyldig e-postadresse."),

View file

@ -61,6 +61,8 @@ class MessageLookup extends MessageLookupByLibrary {
"Przykro nam, że odchodzisz. Wyjaśnij nam, dlaczego nas opuszczasz, aby pomóc ulepszać nasze usługi."),
"deleteAccountPermanentlyButton":
MessageLookupByLibrary.simpleMessage("Usuń konto na stałe"),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
"deleteEmailRequest": MessageLookupByLibrary.simpleMessage(
"Wyślij wiadomość e-mail na <warning>account-deletion@ente.io</warning> z zarejestrowanego adresu e-mail."),
"deleteReason1": MessageLookupByLibrary.simpleMessage(

View file

@ -157,6 +157,8 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteAlbum": MessageLookupByLibrary.simpleMessage("Excluir álbum"),
"deleteAlbumDialog": MessageLookupByLibrary.simpleMessage(
"Também excluir as fotos (e vídeos) presentes neste álbum de <bold>todos os</bold> outros álbuns dos quais eles fazem parte?"),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."),
"deleteEmailRequest": MessageLookupByLibrary.simpleMessage(
"Por favor, envie um e-mail para <warning>account-deletion@ente.io</warning> a partir do seu endereço de e-mail registrado."),
"deletePhotos": MessageLookupByLibrary.simpleMessage("Excluir fotos"),

View file

@ -170,6 +170,8 @@ class MessageLookup extends MessageLookupByLibrary {
static String m59(count) =>
"${Intl.plural(count, zero: '', one: '1天', other: '${count} 天')}";
static String m64(endDate) => "有效期至 ${endDate}";
static String m60(email) => "验证 ${email}";
static String m61(email) => "我们已经发送邮件到 <green>${email}</green>";
@ -196,6 +198,8 @@ class MessageLookup extends MessageLookupByLibrary {
"addLocation": MessageLookupByLibrary.simpleMessage("添加地点"),
"addLocationButton": MessageLookupByLibrary.simpleMessage("添加"),
"addMore": MessageLookupByLibrary.simpleMessage("添加更多"),
"addOnPageSubtitle": MessageLookupByLibrary.simpleMessage("附加组件详情"),
"addOns": MessageLookupByLibrary.simpleMessage("附加组件"),
"addPhotos": MessageLookupByLibrary.simpleMessage("添加照片"),
"addSelected": MessageLookupByLibrary.simpleMessage("添加所选项"),
"addToAlbum": MessageLookupByLibrary.simpleMessage("添加到相册"),
@ -415,6 +419,8 @@ class MessageLookup extends MessageLookupByLibrary {
"deleteAlbumsDialogBody": MessageLookupByLibrary.simpleMessage(
"这将删除所有空相册。 当您想减少相册列表中的混乱时,这很有用。"),
"deleteAll": MessageLookupByLibrary.simpleMessage("全部删除"),
"deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage(
"此账户已链接到其他 ente 旗下的应用程序(如果您使用任何 ente 旗下的应用程序)。\\n\\n您在所有 ente 旗下的应用程序中上传的数据将被安排删除,并且您的账户将被永久删除。"),
"deleteEmailRequest": MessageLookupByLibrary.simpleMessage(
"请从您注册的电子邮件地址发送电子邮件到 <warning>account-delettion@ente.io</warning>。"),
"deleteEmptyAlbums": MessageLookupByLibrary.simpleMessage("删除空相册"),
@ -580,6 +586,10 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("请在“设置”应用中将权限更改为允许访问所有所有照片"),
"grantPermission": MessageLookupByLibrary.simpleMessage("授予权限"),
"groupNearbyPhotos": MessageLookupByLibrary.simpleMessage("将附近的照片分组"),
"hearUsExplanation": MessageLookupByLibrary.simpleMessage(
"我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!"),
"hearUsWhereTitle":
MessageLookupByLibrary.simpleMessage("您是如何知道Ente的 (可选的)"),
"hidden": MessageLookupByLibrary.simpleMessage("已隐藏"),
"hide": MessageLookupByLibrary.simpleMessage("隐藏"),
"hiding": MessageLookupByLibrary.simpleMessage("正在隐藏..."),
@ -1107,6 +1117,7 @@ class MessageLookup extends MessageLookupByLibrary {
"useRecoveryKey": MessageLookupByLibrary.simpleMessage("使用恢复密钥"),
"useSelectedPhoto": MessageLookupByLibrary.simpleMessage("使用所选照片"),
"usedSpace": MessageLookupByLibrary.simpleMessage("已用空间"),
"validTill": m64,
"verificationFailedPleaseTryAgain":
MessageLookupByLibrary.simpleMessage("验证失败,请重试"),
"verificationId": MessageLookupByLibrary.simpleMessage("验证 ID"),
@ -1120,6 +1131,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("正在验证恢复密钥..."),
"videoSmallCase": MessageLookupByLibrary.simpleMessage("视频"),
"viewActiveSessions": MessageLookupByLibrary.simpleMessage("查看活动会话"),
"viewAddOnButton": MessageLookupByLibrary.simpleMessage("查看附加组件"),
"viewAll": MessageLookupByLibrary.simpleMessage("查看全部"),
"viewAllExifData": MessageLookupByLibrary.simpleMessage("查看所有 EXIF 数据"),
"viewLogs": MessageLookupByLibrary.simpleMessage("查看日志"),

View file

@ -190,17 +190,6 @@ class S {
);
}
/// `This account is linked to other ente apps, if you use any.`
/// `Your uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted.`
String get deleteConfirmDialogBody {
return Intl.message(
'This account is linked to other ente apps, if you use any.\n\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted.',
name: 'deleteConfirmDialogBody',
desc: '',
args: [],
);
}
/// `Delete Account Permanently`
String get deleteAccountPermanentlyButton {
return Intl.message(
@ -3985,6 +3974,16 @@ class S {
);
}
/// `Valid till {endDate}`
String validTill(Object endDate) {
return Intl.message(
'Valid till $endDate',
name: 'validTill',
desc: '',
args: [endDate],
);
}
/// `Free trial valid till {endDate}.\nYou can choose a paid plan afterwards.`
String playStoreFreeTrialValidTill(Object endDate) {
return Intl.message(
@ -7775,6 +7774,66 @@ class S {
args: [],
);
}
/// `This account is linked to other ente apps, if you use any.\n\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted.`
String get deleteConfirmDialogBody {
return Intl.message(
'This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted.',
name: 'deleteConfirmDialogBody',
desc: '',
args: [],
);
}
/// `How did you hear about Ente? (optional)`
String get hearUsWhereTitle {
return Intl.message(
'How did you hear about Ente? (optional)',
name: 'hearUsWhereTitle',
desc: '',
args: [],
);
}
/// `We don't track app installs. It'd help if you told us where you found us!`
String get hearUsExplanation {
return Intl.message(
'We don\'t track app installs. It\'d help if you told us where you found us!',
name: 'hearUsExplanation',
desc: '',
args: [],
);
}
/// `View add-ons`
String get viewAddOnButton {
return Intl.message(
'View add-ons',
name: 'viewAddOnButton',
desc: '',
args: [],
);
}
/// `Add-ons`
String get addOns {
return Intl.message(
'Add-ons',
name: 'addOns',
desc: '',
args: [],
);
}
/// `Details of add-ons`
String get addOnPageSubtitle {
return Intl.message(
'Details of add-ons',
name: 'addOnPageSubtitle',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<S> {

View file

@ -563,6 +563,7 @@
"faqs": "FAQs",
"renewsOn": "Renews on {endDate}",
"freeTrialValidTill": "Free trial valid till {endDate}",
"validTill": "Valid till {endDate}",
"playStoreFreeTrialValidTill": "Free trial valid till {endDate}.\nYou can choose a paid plan afterwards.",
"subWillBeCancelledOn": "Your subscription will be cancelled on {endDate}",
"subscription": "Subscription",
@ -1108,5 +1109,10 @@
"crashReporting": "Crash reporting",
"addToHiddenAlbum": "Add to hidden album",
"moveToHiddenAlbum": "Move to hidden album",
"deleteConfirmDialogBody": "This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."
}
"deleteConfirmDialogBody": "This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted.",
"hearUsWhereTitle": "How did you hear about Ente? (optional)",
"hearUsExplanation": "We don't track app installs. It'd help if you told us where you found us!",
"viewAddOnButton": "View add-ons",
"addOns": "Add-ons",
"addOnPageSubtitle": "Details of add-ons"
}

View file

@ -557,6 +557,7 @@
"faqs": "FAQ",
"renewsOn": "Si rinnova il {endDate}",
"freeTrialValidTill": "La prova gratuita termina il {endDate}",
"validTill": "Valido fino al {endDate}",
"playStoreFreeTrialValidTill": "Prova gratuita valida fino al {endDate}.\nPuoi scegliere un piano a pagamento in seguito.",
"subWillBeCancelledOn": "L'abbonamento verrà cancellato il {endDate}",
"subscription": "Abbonamento",
@ -1102,5 +1103,10 @@
"crashReporting": "Segnalazione di crash",
"addToHiddenAlbum": "Aggiungi ad album nascosto",
"moveToHiddenAlbum": "Sposta in album nascosto",
"deleteConfirmDialogBody": "Questo account è collegato ad altre app di ente, se ne utilizzi.\\n\\nI tuoi dati caricati, su tutte le app di ente, saranno pianificati per la cancellazione e il tuo account verrà eliminato definitivamente."
"deleteConfirmDialogBody": "Questo account è collegato ad altre app di ente, se ne utilizzi.\\n\\nI tuoi dati caricati, su tutte le app di ente, saranno pianificati per la cancellazione e il tuo account verrà eliminato definitivamente.",
"hearUsWhereTitle": "Come hai sentito parlare di Ente? (opzionale)",
"hearUsExplanation": "Non teniamo traccia del numero di installazioni dell'app. Sarebbe utile se ci dicesse dove ci ha trovato!",
"viewAddOnButton": "Visualizza componenti aggiuntivi",
"addOns": "Componenti aggiuntivi",
"addOnPageSubtitle": "Dettagli dei componenti aggiuntivi"
}

View file

@ -557,6 +557,7 @@
"faqs": "常见问题",
"renewsOn": "在 {endDate} 前续费",
"freeTrialValidTill": "免费试用有效期至 {endDate}",
"validTill": "有效期至 {endDate}",
"playStoreFreeTrialValidTill": "免费试用有效期至 {endDate}。\n之后您可以选择付费计划。",
"subWillBeCancelledOn": "您的订阅将于 {endDate} 取消",
"subscription": "订阅",
@ -1102,5 +1103,10 @@
"crashReporting": "崩溃报告",
"addToHiddenAlbum": "添加到隐藏相册",
"moveToHiddenAlbum": "移至隐藏相册",
"deleteConfirmDialogBody": "此账户已链接到其他 ente 旗下的应用程序(如果您使用任何 ente 旗下的应用程序)。\\n\\n您在所有 ente 旗下的应用程序中上传的数据将被安排删除,并且您的账户将被永久删除。"
"deleteConfirmDialogBody": "此账户已链接到其他 ente 旗下的应用程序(如果您使用任何 ente 旗下的应用程序)。\\n\\n您在所有 ente 旗下的应用程序中上传的数据将被安排删除,并且您的账户将被永久删除。",
"hearUsWhereTitle": "您是如何知道Ente的 (可选的)",
"hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!",
"viewAddOnButton": "查看附加组件",
"addOns": "附加组件",
"addOnPageSubtitle": "附加组件详情"
}

View file

@ -0,0 +1,60 @@
class Bonus {
int storage;
String type;
int validTill;
bool isRevoked;
Bonus(this.storage, this.type, this.validTill, this.isRevoked);
factory Bonus.fromJson(Map<String, dynamic> json) {
return Bonus(
json['storage'],
json['type'],
json['validTill'],
json['isRevoked'],
);
}
Map<String, dynamic> toJson() {
return {
'storage': storage,
'type': type,
'validTill': validTill,
'isRevoked': isRevoked,
};
}
}
class BonusData {
static Set<String> signUpBonusTypes = {'SIGN_UP', 'REFERRAL'};
final List<Bonus> storageBonuses;
BonusData(this.storageBonuses);
List<Bonus> getAddOnBonuses() {
return storageBonuses
.where((b) => !signUpBonusTypes.contains(b.type))
.toList();
}
int totalAddOnBonus() {
return getAddOnBonuses().fold(0, (sum, bonus) => sum + bonus.storage);
}
factory BonusData.fromJson(Map<String, dynamic>? json) {
if (json == null || json['storageBonuses'] == null) {
return BonusData([]);
}
return BonusData(
(json['storageBonuses'] as List)
.map((bonus) => Bonus.fromJson(bonus))
.toList(),
);
}
Map<String, dynamic> toJson() {
return {
'storageBonuses': storageBonuses.map((bonus) => bonus.toJson()).toList(),
};
}
}

View file

@ -1,3 +1,5 @@
import "package:photos/models/api/storage_bonus/bonus.dart";
class ReferralView {
PlanInfo planInfo;
String code;
@ -86,34 +88,6 @@ class ReferralStat {
}
}
class Bonus {
int storage;
String type;
int validTill;
bool isRevoked;
Bonus(this.storage, this.type, this.validTill, this.isRevoked);
// fromJson
factory Bonus.fromJson(Map<String, dynamic> json) {
return Bonus(
json['storage'],
json['type'],
json['validTill'],
json['isRevoked'],
);
}
Map<String, dynamic> toJson() {
return {
'storage': storage,
'type': type,
'validTill': validTill,
'isRevoked': isRevoked,
};
}
}
class BonusDetails {
List<ReferralStat> referralStats;
List<Bonus> bonuses;

View file

@ -6,7 +6,7 @@ import "package:photos/models/file/trash_file.dart";
extension FilePropsExtn on EnteFile {
bool get isLivePhoto => fileType == FileType.livePhoto;
bool get isMotionPhoto => pubMagicMetadata?.mvi != null;
bool get isMotionPhoto => (pubMagicMetadata?.mvi ?? 0) > 0;
bool get isLiveOrMotionPhoto => isLivePhoto || isMotionPhoto;

View file

@ -43,7 +43,7 @@ class PubMagicMetadata {
double? lat;
double? long;
// Motion Video Index. Positive value indicates that the file is a motion
// Motion Video Index. Positive value (>0) indicates that the file is a motion
// photo
int? mvi;

View file

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:math';
import 'package:collection/collection.dart';
import "package:photos/models/api/storage_bonus/bonus.dart";
import 'package:photos/models/file/file_type.dart';
import 'package:photos/models/subscription.dart';
@ -14,6 +15,7 @@ class UserDetails {
final Subscription subscription;
final FamilyData? familyData;
final ProfileData? profileData;
final BonusData? bonusData;
const UserDetails(
this.email,
@ -24,6 +26,7 @@ class UserDetails {
this.subscription,
this.familyData,
this.profileData,
this.bonusData,
);
bool isPartOfFamily() {
@ -55,6 +58,12 @@ class UserDetails {
storageBonus;
}
// This is the total storage for which user has paid for.
int getPlanPlusAddonStorage() {
return (isPartOfFamily() ? familyData!.storage : subscription.storage) +
bonusData!.totalAddOnBonus();
}
factory UserDetails.fromMap(Map<String, dynamic> map) {
return UserDetails(
map['email'] as String,
@ -65,6 +74,7 @@ class UserDetails {
Subscription.fromMap(map['subscription']),
FamilyData.fromMap(map['familyData']),
ProfileData.fromJson(map['profileData']),
BonusData.fromJson(map['bonusData']),
);
}
@ -78,6 +88,7 @@ class UserDetails {
'subscription': subscription.toMap(),
'familyData': familyData?.toMap(),
'profileData': profileData?.toJson(),
'bonusData': bonusData?.toJson(),
};
}
@ -123,6 +134,7 @@ class FamilyMember {
factory FamilyMember.fromJson(String source) =>
FamilyMember.fromMap(json.decode(source));
}
class ProfileData {
bool canDisableEmailMFA;
bool isEmailMFAEnabled;
@ -135,7 +147,6 @@ class ProfileData {
this.isTwoFactorEnabled = false,
});
// Factory method to create ProfileData instance from JSON
factory ProfileData.fromJson(Map<String, dynamic>? json) {
return ProfileData(
@ -153,6 +164,7 @@ class ProfileData {
'isTwoFactorEnabled': isTwoFactorEnabled,
};
}
String toJsonString() => json.encode(toJson());
}

View file

@ -52,6 +52,7 @@ import "package:uuid/uuid.dart";
class UserService {
static const keyHasEnabledTwoFactor = "has_enabled_two_factor";
static const keyUserDetails = "user_details";
static const kReferralSource = "referral_source";
final SRP6GroupParameters kDefaultSrpGroup = SRP6StandardGroups.rfc5054_4096;
final _dio = NetworkClient.instance.getDio();
@ -318,13 +319,17 @@ class UserService {
}) async {
final dialog = createProgressDialog(context, S.of(context).pleaseWait);
await dialog.show();
final verifyData = {
"email": _config.getEmail(),
"ott": ott,
};
if (!_config.isLoggedIn()) {
verifyData["source"] = _getRefSource();
}
try {
final response = await _dio.post(
_config.getHttpEndpoint() + "/users/verify-email",
data: {
"email": _config.getEmail(),
"ott": ott,
},
data: verifyData,
);
await dialog.hide();
if (response.statusCode == 200) {
@ -392,6 +397,14 @@ class UserService {
emailValueNotifier.value = email;
}
Future<void> setRefSource(String refSource) async {
await _preferences.setString(kReferralSource, refSource);
}
String _getRefSource() {
return _preferences.getString(kReferralSource) ?? "";
}
Future<void> changeEmail(
BuildContext context,
String email,
@ -666,33 +679,23 @@ class UserService {
} on DioError catch (e, s) {
await dialog.hide();
if (e.response != null && e.response!.statusCode == 401) {
final dialogChoice = await showChoiceDialog(
await _showContactSupportDialog(
context,
title: S.of(context).incorrectPasswordTitle,
body: S.of(context).pleaseTryAgain,
firstButtonLabel: S.of(context).contactSupport,
secondButtonLabel: S.of(context).ok,
S.of(context).incorrectPasswordTitle,
S.of(context).pleaseTryAgain,
);
if (dialogChoice!.action == ButtonAction.first) {
await sendLogs(
context,
S.of(context).contactSupport,
"support@ente.io",
postShare: () {},
);
}
} else {
_logger.fine('failed to verify password', e, s);
await showErrorDialog(
_logger.severe('failed to verify password', e, s);
await _showContactSupportDialog(
context,
S.of(context).oops,
S.of(context).verificationFailedPleaseTryAgain,
);
}
} catch (e, s) {
_logger.fine('failed to verify password', e, s);
_logger.severe('failed to verify password', e, s);
await dialog.hide();
await showErrorDialog(
await _showContactSupportDialog(
context,
S.of(context).oops,
S.of(context).verificationFailedPleaseTryAgain,
@ -1161,4 +1164,26 @@ class UserService {
rethrow;
}
}
Future<void> _showContactSupportDialog(
BuildContext context,
String title,
String message,
) async {
final dialogChoice = await showChoiceDialog(
context,
title: title,
body: message,
firstButtonLabel: S.of(context).contactSupport,
secondButtonLabel: S.of(context).ok,
);
if (dialogChoice!.action == ButtonAction.first) {
await sendLogs(
context,
S.of(context).contactSupport,
"support@ente.io",
postShare: () {},
);
}
}
}

View file

@ -6,8 +6,10 @@ import 'package:photos/core/configuration.dart';
import 'package:photos/ente_theme_data.dart';
import "package:photos/generated/l10n.dart";
import 'package:photos/services/user_service.dart';
import "package:photos/theme/ente_theme.dart";
import 'package:photos/ui/common/dynamic_fab.dart';
import 'package:photos/ui/common/web_page.dart';
import "package:photos/utils/toast_util.dart";
import 'package:step_progress_indicator/step_progress_indicator.dart';
import "package:styled_text/styled_text.dart";
@ -30,6 +32,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
String? _email;
String? _password;
String _cnfPassword = '';
String _referralSource = '';
double _passwordStrength = 0.0;
bool _emailIsValid = false;
bool _hasAgreedToTOS = true;
@ -104,6 +107,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
onPressedFunction: () {
_config.setVolatilePassword(_passwordController1.text);
UserService.instance.setEmail(_email!);
UserService.instance.setRefSource(_referralSource);
UserService.instance
.sendOtt(context, _email!, isCreateAccountScreen: true);
FocusScope.of(context).unfocus();
@ -325,6 +329,52 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
),
),
const SizedBox(height: 4),
Padding(
padding:
const EdgeInsets.symmetric(vertical: 0, horizontal: 20),
child: Text(
S.of(context).hearUsWhereTitle,
style: getEnteTextTheme(context).smallFaint,
),
),
const SizedBox(height: 4),
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
child: TextFormField(
style: Theme.of(context).textTheme.titleMedium,
decoration: InputDecoration(
fillColor: null,
filled: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 14,
),
border: UnderlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(6),
),
suffixIcon: InkWell(
onTap: () {
showToast(
context,
S.of(context).hearUsExplanation,
iosLongToastLengthInSec: 4,
);
},
child: Icon(
Icons.info_outline_rounded,
color: getEnteColorScheme(context).fillStrong,
),
),
),
onChanged: (value) {
_referralSource = value.trim();
},
autocorrect: false,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.next,
),
),
const Divider(thickness: 1),
const SizedBox(height: 12),
_getAgreement(),
@ -377,31 +427,33 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
.copyWith(fontSize: 12),
tags: {
'u-terms': StyledTextActionTag(
(String? text, Map<String?, String?> attrs) => Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return WebPage(
S.of(context).termsOfServicesTitle,
"https://ente.io/terms",
);
},
),
(String? text, Map<String?, String?> attrs) =>
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return WebPage(
S.of(context).termsOfServicesTitle,
"https://ente.io/terms",
);
},
),
),
style: const TextStyle(
decoration: TextDecoration.underline,
),
),
'u-policy': StyledTextActionTag(
(String? text, Map<String?, String?> attrs) => Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return WebPage(
S.of(context).privacyPolicyTitle,
"https://ente.io/privacy",
);
},
),
(String? text, Map<String?, String?> attrs) =>
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return WebPage(
S.of(context).privacyPolicyTitle,
"https://ente.io/privacy",
);
},
),
),
style: const TextStyle(
decoration: TextDecoration.underline,
),
@ -442,16 +494,17 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
.copyWith(fontSize: 12),
tags: {
'underline': StyledTextActionTag(
(String? text, Map<String?, String?> attrs) => Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return WebPage(
S.of(context).encryption,
"https://ente.io/architecture",
);
},
),
(String? text, Map<String?, String?> attrs) =>
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return WebPage(
S.of(context).encryption,
"https://ente.io/architecture",
);
},
),
),
style: const TextStyle(
decoration: TextDecoration.underline,
),

View file

@ -122,12 +122,14 @@ class _StorageDetailsScreenState extends State<StorageDetailsScreen> {
leftValue: convertBytesToAbsoluteGBs(
min(
widget.referralView.claimedStorage,
widget.userDetails.getTotalStorage(),
widget.userDetails
.getPlanPlusAddonStorage(),
),
),
leftUnitName: "GB",
rightValue: convertBytesToAbsoluteGBs(
widget.userDetails.getTotalStorage(),
widget.userDetails
.getPlanPlusAddonStorage(),
),
rightUnitName: "GB",
),

View file

@ -0,0 +1,123 @@
import "package:flutter/material.dart";
import "package:intl/intl.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/models/api/storage_bonus/bonus.dart";
import "package:photos/theme/ente_theme.dart";
import 'package:photos/ui/components/buttons/icon_button_widget.dart';
import "package:photos/ui/components/title_bar_title_widget.dart";
import "package:photos/ui/components/title_bar_widget.dart";
import "package:photos/utils/data_util.dart";
class AddOnPage extends StatelessWidget {
final BonusData bonusData;
const AddOnPage(this.bonusData, {super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
primary: false,
slivers: <Widget>[
TitleBarWidget(
flexibleSpaceTitle: TitleBarTitleWidget(
title: S.of(context).addOns,
),
flexibleSpaceCaption: S.of(context).addOnPageSubtitle,
actionIcons: [
IconButtonWidget(
icon: Icons.close_outlined,
iconButtonType: IconButtonType.secondary,
onTap: () {
Navigator.of(context).pop();
},
),
],
),
SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(delegateBuildContext, index) {
Bonus bonus = bonusData.getAddOnBonuses()[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: AddOnViewSection(
sectionName: bonus.type == 'ADD_ON_BF_2023'
? "Black friday 2023"
: bonus.type.replaceAll('_', ' '),
bonus: bonus,
),
);
},
childCount: bonusData?.getAddOnBonuses().length ?? 0,
),
),
),
],
),
);
}
}
class AddOnViewSection extends StatelessWidget {
final String sectionName;
final Bonus bonus;
const AddOnViewSection({
super.key,
required this.sectionName,
required this.bonus,
});
@override
Widget build(BuildContext context) {
final colorScheme = getEnteColorScheme(context);
final textStyle = getEnteTextTheme(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
sectionName,
style: textStyle.body.copyWith(
color: colorScheme.textMuted,
),
),
if (bonus.validTill != 0)
Text(
S.of(context).validTill(
DateFormat.yMMMd(
Localizations.localeOf(context).languageCode,
)
.format(
DateTime.fromMicrosecondsSinceEpoch(
bonus.validTill,
),
)
.toString(),
),
style: textStyle.body.copyWith(
color: colorScheme.textMuted,
),
),
],
),
const SizedBox(height: 2),
RichText(
text: TextSpan(
children: [
TextSpan(
text: convertBytesToReadableFormat(bonus.storage).toString(),
style: textStyle.h3,
),
],
),
),
const SizedBox(height: 24),
],
);
}
}

View file

@ -25,6 +25,8 @@ import 'package:photos/ui/payment/child_subscription_widget.dart';
import 'package:photos/ui/payment/skip_subscription_widget.dart';
import 'package:photos/ui/payment/subscription_common_widgets.dart';
import 'package:photos/ui/payment/subscription_plan_widget.dart';
import "package:photos/ui/payment/view_add_on_widget.dart";
import "package:photos/utils/data_util.dart";
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/toast_util.dart';
import 'package:url_launcher/url_launcher_string.dart';
@ -290,7 +292,7 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
if (!widget.isOnboarding) {
widgets.add(
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 80),
padding: const EdgeInsets.fromLTRB(16, 0, 16, 0),
child: MenuItemWidget(
captionedTextWidget: CaptionedTextWidget(
title: _isFreePlanUser()
@ -310,6 +312,8 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
),
),
);
widgets.add(ViewAddOnButton(_userDetails.bonusData));
widgets.add(const SizedBox(height: 80));
}
return SingleChildScrollView(
child: Column(
@ -490,7 +494,16 @@ class _StoreSubscriptionPageState extends State<StoreSubscriptionPage> {
if (isActive) {
return;
}
if (_userDetails.getFamilyOrPersonalUsage() > plan.storage) {
final int addOnBonus =
_userDetails.bonusData?.totalAddOnBonus() ?? 0;
if (_userDetails.getFamilyOrPersonalUsage() >
(plan.storage + addOnBonus)) {
_logger.warning(
" familyUsage ${convertBytesToReadableFormat(_userDetails.getFamilyOrPersonalUsage())}"
" plan storage ${convertBytesToReadableFormat(plan.storage)} "
"addOnBonus ${convertBytesToReadableFormat(addOnBonus)},"
"overshooting by ${convertBytesToReadableFormat(_userDetails.getFamilyOrPersonalUsage() - (plan.storage + addOnBonus))}",
);
showErrorDialog(
context,
S.of(context).sorry,

View file

@ -25,6 +25,8 @@ import 'package:photos/ui/payment/payment_web_page.dart';
import 'package:photos/ui/payment/skip_subscription_widget.dart';
import 'package:photos/ui/payment/subscription_common_widgets.dart';
import 'package:photos/ui/payment/subscription_plan_widget.dart';
import "package:photos/ui/payment/view_add_on_widget.dart";
import "package:photos/utils/data_util.dart";
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/toast_util.dart';
import 'package:step_progress_indicator/step_progress_indicator.dart';
@ -252,7 +254,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
if (!widget.isOnboarding) {
widgets.add(
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 80),
padding: const EdgeInsets.fromLTRB(16, 0, 16, 0),
child: MenuItemWidget(
captionedTextWidget: CaptionedTextWidget(
title: S.of(context).manageFamily,
@ -270,6 +272,8 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
),
),
);
widgets.add(ViewAddOnButton(_userDetails.bonusData));
widgets.add(const SizedBox(height: 80));
}
return SingleChildScrollView(
@ -446,7 +450,16 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
);
return;
}
if (_userDetails.getFamilyOrPersonalUsage() > plan.storage) {
final int addOnBonus =
_userDetails.bonusData?.totalAddOnBonus() ?? 0;
if (_userDetails.getFamilyOrPersonalUsage() >
(plan.storage + addOnBonus)) {
logger.warning(
" familyUsage ${convertBytesToReadableFormat(_userDetails.getFamilyOrPersonalUsage())}"
" plan storage ${convertBytesToReadableFormat(plan.storage)} "
"addOnBonus ${convertBytesToReadableFormat(addOnBonus)},"
"overshooting by ${convertBytesToReadableFormat(_userDetails.getFamilyOrPersonalUsage() - (plan.storage + addOnBonus))}",
);
showErrorDialog(
context,
S.of(context).sorry,

View file

@ -0,0 +1,41 @@
import "package:flutter/material.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/models/api/storage_bonus/bonus.dart";
import "package:photos/theme/colors.dart";
import "package:photos/theme/ente_theme.dart";
import "package:photos/ui/components/captioned_text_widget.dart";
import "package:photos/ui/components/menu_item_widget/menu_item_widget.dart";
import "package:photos/ui/payment/add_on_page.dart";
import "package:photos/utils/navigation_util.dart";
class ViewAddOnButton extends StatelessWidget {
final BonusData? bonusData;
const ViewAddOnButton(this.bonusData, {super.key});
@override
Widget build(BuildContext context) {
if (bonusData?.getAddOnBonuses().isEmpty ?? true) {
return const SizedBox.shrink();
}
final EnteColorScheme colorScheme = getEnteColorScheme(context);
return Padding(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 0),
child: MenuItemWidget(
captionedTextWidget: CaptionedTextWidget(
title: S.of(context).viewAddOnButton,
),
menuItemColor: colorScheme.fillFaint,
trailingWidget: Icon(
Icons.chevron_right_outlined,
color: colorScheme.strokeBase,
),
singleBorderRadius: 4,
alignCaptionedTextToLeft: true,
onTap: () async {
routeToPage(context, AddOnPage(bonusData!));
},
),
);
}
}

View file

@ -17,13 +17,14 @@ class _LockScreenState extends State<LockScreen> with WidgetsBindingObserver {
bool _isShowingLockScreen = false;
bool _hasPlacedAppInBackground = false;
bool _hasAuthenticationFailed = false;
int? lastAuthenticatingTime;
@override
void initState() {
_logger.info("initState");
_showLockScreen();
WidgetsBinding.instance.addObserver(this);
super.initState();
_showLockScreen(source: "initState");
WidgetsBinding.instance.addObserver(this);
}
@override
@ -47,7 +48,7 @@ class _LockScreenState extends State<LockScreen> with WidgetsBindingObserver {
text: S.of(context).unlock,
iconData: Icons.lock_open_outlined,
onTap: () async {
_showLockScreen();
_showLockScreen(source: "tapUnlock");
},
),
),
@ -62,14 +63,17 @@ class _LockScreenState extends State<LockScreen> with WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
_logger.info(state.toString());
if (state == AppLifecycleState.resumed) {
if (state == AppLifecycleState.resumed && !_isShowingLockScreen) {
// This is triggered either when the lock screen is dismissed or when
// the app is brought to foreground
_hasPlacedAppInBackground = false;
if (!_hasAuthenticationFailed) {
final bool didAuthInLast5Seconds = lastAuthenticatingTime != null &&
DateTime.now().millisecondsSinceEpoch - lastAuthenticatingTime! <
5000;
if (!_hasAuthenticationFailed && !didAuthInLast5Seconds) {
// Show the lock screen again only if the app is resuming from the
// background, and not when the lock screen was explicitly dismissed
_showLockScreen();
_showLockScreen(source: "lifeCycle");
} else {
_hasAuthenticationFailed = false; // Reset failure state
}
@ -90,19 +94,21 @@ class _LockScreenState extends State<LockScreen> with WidgetsBindingObserver {
super.dispose();
}
Future<void> _showLockScreen() async {
_logger.info("Showing lock screen");
Future<void> _showLockScreen({String source = ''}) async {
final int id = DateTime.now().millisecondsSinceEpoch;
_logger.info("Showing lock screen $source $id");
try {
_isShowingLockScreen = true;
final result = await requestAuthentication(
context,
S.of(context).authToViewYourMemories,
);
_logger.finest("LockScreen Result $result $id");
_isShowingLockScreen = false;
if (result) {
lastAuthenticatingTime = DateTime.now().millisecondsSinceEpoch;
AppLock.of(context)!.didUnlock();
} else {
_logger.info("Dismissed");
if (!_hasPlacedAppInBackground) {
// Treat this as a failure only if user did not explicitly
// put the app in background

View file

@ -164,7 +164,6 @@ class _DetailPageState extends State<DetailPage> {
}
Widget _buildPageView(BuildContext context) {
final bottomPadding = MediaQuery.of(context).padding.bottom;
return PageView.builder(
itemBuilder: (context, index) {
final file = _files![index];

View file

@ -11,6 +11,7 @@ import "package:photos/models/file/extensions/file_props.dart";
import 'package:photos/models/file/file.dart';
import "package:photos/services/feature_flag_service.dart";
import 'package:photos/services/files_service.dart';
import "package:photos/ui/actions/file/file_actions.dart";
import 'package:photos/ui/viewer/file/thumbnail_widget.dart';
import 'package:photos/ui/viewer/file/video_controls.dart';
import "package:photos/utils/dialog_util.dart";
@ -184,7 +185,14 @@ class _VideoWidgetState extends State<VideoWidget> {
final contentWithDetector = GestureDetector(
child: content,
onVerticalDragUpdate: (d) => {
if (d.delta.dy > dragSensitivity) {Navigator.of(context).pop()},
if (d.delta.dy > dragSensitivity)
{
Navigator.of(context).pop(),
}
else if (d.delta.dy < (dragSensitivity * -1))
{
showDetailsSheet(context, widget.file),
},
},
);
return VisibilityDetector(

View file

@ -12,6 +12,7 @@ import "package:photos/models/file/file.dart";
import "package:photos/services/files_service.dart";
import "package:photos/theme/colors.dart";
import "package:photos/theme/ente_theme.dart";
import "package:photos/ui/actions/file/file_actions.dart";
import "package:photos/ui/viewer/file/thumbnail_widget.dart";
import "package:photos/utils/dialog_util.dart";
import "package:photos/utils/file_util.dart";
@ -133,7 +134,14 @@ class _VideoWidgetNewState extends State<VideoWidgetNew>
fullscreen: const MaterialVideoControlsThemeData(),
child: GestureDetector(
onVerticalDragUpdate: (d) => {
if (d.delta.dy > dragSensitivity) {Navigator.of(context).pop()},
if (d.delta.dy > dragSensitivity)
{
Navigator.of(context).pop(),
}
else if (d.delta.dy < (dragSensitivity * -1))
{
showDetailsSheet(context, widget.file),
},
},
child: Center(
child: controller != null

View file

@ -16,6 +16,7 @@ import "package:photos/models/file/extensions/file_props.dart";
import 'package:photos/models/file/file.dart';
import "package:photos/models/metadata/file_magic.dart";
import "package:photos/services/file_magic_service.dart";
import "package:photos/ui/actions/file/file_actions.dart";
import 'package:photos/ui/common/loading_widget.dart';
import 'package:photos/utils/file_util.dart';
import 'package:photos/utils/image_util.dart';
@ -110,8 +111,17 @@ class _ZoomableImageState extends State<ZoomableImage>
final GestureDragUpdateCallback? verticalDragCallback = _isZooming
? null
: (d) => {
if (!_isZooming && d.delta.dy > dragSensitivity)
{Navigator.of(context).pop()},
if (!_isZooming)
{
if (d.delta.dy > dragSensitivity)
{
{Navigator.of(context).pop()},
}
else if (d.delta.dy < (dragSensitivity * -1))
{
showDetailsSheet(context, widget.photo),
},
},
};
return GestureDetector(
onVerticalDragUpdate: verticalDragCallback,

View file

@ -185,6 +185,12 @@ class _ZoomableLiveImageNewState extends State<ZoomableLiveImageNew>
return motionPhoto.getMotionVideoFile(
index: index,
);
} else if (_enteFile.isMotionPhoto && _enteFile.canEditMetaInfo) {
_logger.finest('Incorrectly tagged as MP, reset tag ${_enteFile.tag}');
FileMagicService.instance.updatePublicMagicMetadata(
[_enteFile],
{motionVideoIndexKey: 0},
).ignore();
}
}
return null;

View file

@ -9,6 +9,7 @@ Future showToast(
BuildContext context,
String message, {
toastLength = Toast.LENGTH_LONG,
int iosLongToastLengthInSec = 2,
}) async {
if (Platform.isAndroid) {
await Fluttertoast.cancel();
@ -30,7 +31,10 @@ Future showToast(
..loadingStyle = EasyLoadingStyle.custom;
return EasyLoading.showToast(
message,
duration: Duration(seconds: (toastLength == Toast.LENGTH_LONG ? 2 : 1)),
duration: Duration(
seconds:
(toastLength == Toast.LENGTH_LONG ? iosLongToastLengthInSec : 1),
),
toastPosition: EasyLoadingToastPosition.bottom,
dismissOnTap: false,
);

View file

@ -1275,10 +1275,10 @@ packages:
description:
path: "."
ref: HEAD
resolved-ref: c0409b2e812c7457908a6e492abae6e4f5b457a7
resolved-ref: e4edd3a158c667976660a788e908d1faf5c5a4f8
url: "https://github.com/ente-io/motion_photo.git"
source: git
version: "0.0.3"
version: "0.0.5"
motionphoto:
dependency: "direct main"
description:

View file

@ -12,7 +12,7 @@ description: ente photos application
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.7.102+502
version: 0.7.108+508
environment:
sdk: ">=3.0.0 <4.0.0"