diff --git a/src/components/DataDownloader.vue b/src/components/DataDownloader.vue index bc068b31d..fea9a9127 100644 --- a/src/components/DataDownloader.vue +++ b/src/components/DataDownloader.vue @@ -65,6 +65,8 @@ {{ errorMessage }} + + {{ strings?.['retry_download_dialog_title'] }} @@ -113,7 +115,9 @@ import {firstAtLeastBlobFromReadableStream} from "@/utils/firstAtLeastBlobFromRe import {decideFileName} from "@/piping-ui-utils/decideFileName"; import {readableBytesString} from "@/utils/readableBytesString"; import {getReadableStreamWithProgress} from "@/utils/getReadableStreamWithProgress"; +import {shouldUpdateApp} from "@/piping-ui-utils/shouldUpdateApp"; +const UpdateAppButton = () => import('@/components/UpdateAppButton.vue'); const FileSaverAsync = () => import('file-saver').then(p => p.default); const swDownloadAsync = () => import("@/sw-download"); const openPgpUtilsAsync = () => import("@/utils/openpgp-utils"); @@ -178,6 +182,7 @@ const progressPercentage = computed(() => { } return progressSetting.value.loadedBytes / progressSetting.value.totalBytes * 100; }); +const showsUpdateAppButton = ref(false); // NOTE: Automatically download when mounted onMounted(async () => { @@ -207,6 +212,7 @@ onMounted(async () => { case "key_exchange_error": { const errorCode = keyExchangeRes.error.keyExchangeError; updateErrorMessage(() => strings.value?.["key_exchange_error"](errorCode)); + showsUpdateAppButton.value = shouldUpdateApp(keyExchangeRes.error.keyExchangeError); break; } case "sender_not_verified": diff --git a/src/components/DataUploader.vue b/src/components/DataUploader.vue index f2c3cdc48..57c26480f 100644 --- a/src/components/DataUploader.vue +++ b/src/components/DataUploader.vue @@ -109,6 +109,8 @@ v-html="errorMessage" :value="hasError" /> + + @@ -147,6 +149,9 @@ import {useErrorMessage} from "@/composables/useErrorMessage"; import {strings} from "@/strings/strings"; import {ecdsaP384SigningKeyPairPromise} from "@/states/ecdsaP384SigningKeyPairPromise"; import * as fileType from 'file-type/browser'; +import {shouldUpdateApp} from "@/piping-ui-utils/shouldUpdateApp"; + +const UpdateAppButton = () => import('@/components/UpdateAppButton.vue'); const props = defineProps<{ composedProps: DataUploaderProps }>(); @@ -169,6 +174,7 @@ const isCompressing = ref(false); const isNonStreamingEncrypting = ref(false); const verificationStep = ref({type: 'initial'}); const pipingUiAuthVerificationCode = ref(); +const showsUpdateAppButton = ref(false); const progressPercentage = computed(() => { if (progressSetting.value.totalBytes === undefined) { @@ -292,6 +298,7 @@ onMounted(async () => { if (keyExchangeRes.type === 'error') { verificationStep.value = {type: 'error'}; updateErrorMessage(() => strings.value?.['key_exchange_error'](keyExchangeRes.keyExchangeError)); + showsUpdateAppButton.value = shouldUpdateApp(keyExchangeRes.keyExchangeError); return; } const {key, mainPath, verificationCode} = keyExchangeRes; diff --git a/src/components/DataViewer.vue b/src/components/DataViewer.vue index 443beac21..08553f49a 100644 --- a/src/components/DataViewer.vue +++ b/src/components/DataViewer.vue @@ -161,6 +161,8 @@ {{ errorMessage }} + + @@ -204,6 +206,9 @@ import {getReadableStreamWithProgress} from "@/utils/getReadableStreamWithProgre import {strings} from "@/strings/strings"; import {ecdsaP384SigningKeyPairPromise} from "@/states/ecdsaP384SigningKeyPairPromise"; import {decideFileName} from "@/piping-ui-utils/decideFileName"; +import {shouldUpdateApp} from "@/piping-ui-utils/shouldUpdateApp"; + +const UpdateAppButton = () => import('@/components/UpdateAppButton.vue'); // eslint-disable-next-line no-undef const props = defineProps<{ composedProps: DataViewerProps }>(); @@ -233,6 +238,7 @@ let blob = new Blob(); const showsCopied = ref(false); const isDecrypting = ref(false); const pipingUiAuthVerificationCode = ref(); +const showsUpdateAppButton = ref(false); let topPriorityDataMeta: { fileName: string | undefined, fileExtension: string | undefined } | undefined; const progressPercentage = computed(() => { @@ -330,6 +336,7 @@ onMounted(async () => { case "key_exchange_error": { const errorCode = keyExchangeRes.error.keyExchangeError; updateErrorMessage(() => strings.value?.["key_exchange_error"](errorCode)); + showsUpdateAppButton.value = shouldUpdateApp(keyExchangeRes.error.keyExchangeError); break; } case "sender_not_verified": diff --git a/src/components/UpdateAppButton.vue b/src/components/UpdateAppButton.vue new file mode 100644 index 000000000..f9be31e48 --- /dev/null +++ b/src/components/UpdateAppButton.vue @@ -0,0 +1,15 @@ + + + diff --git a/src/piping-ui-utils/shouldUpdateApp.ts b/src/piping-ui-utils/shouldUpdateApp.ts new file mode 100644 index 000000000..2a859d2db --- /dev/null +++ b/src/piping-ui-utils/shouldUpdateApp.ts @@ -0,0 +1,9 @@ +import * as pipingUiAuth from "@/piping-ui-auth"; +import {KEY_EXCHANGE_VERSION} from "@/piping-ui-auth/KEY_EXCHANGE_VERSION"; + +export function shouldUpdateApp(keyExchangeError: pipingUiAuth.KeyExchangeError): boolean { + if (keyExchangeError.code !== "key_exchange_version_mismatch") { + return false; + } + return KEY_EXCHANGE_VERSION < keyExchangeError.peerVersion; +} diff --git a/src/strings/en.ts b/src/strings/en.ts index f4912309d..96bd9ee75 100644 --- a/src/strings/en.ts +++ b/src/strings/en.ts @@ -107,6 +107,7 @@ curl https://ppng.io/mypath | gpg }, data_uploader_xhr_upload_error: 'An error occurred while uploading', cancel: 'Cancel', + update_the_app: 'Update the app', view_in_viewer: 'View', download_url: 'Download URL', waiting_for_sender: 'Waiting for sender...', diff --git a/src/strings/ja.ts b/src/strings/ja.ts index cd606b2fc..2ca63c3d2 100644 --- a/src/strings/ja.ts +++ b/src/strings/ja.ts @@ -106,6 +106,7 @@ curl https://ppng.io/mypath | gpg return sanitizeHtmlAllowingATag(`エラーが発生しました。サーバーが0.9.4より低い可能性があります。 ${versionUrl} でバージョンの確認できます。`); }, data_uploader_xhr_upload_error: 'アップロード中にエラーが発生しました', + update_the_app: 'アプリを更新', cancel: 'キャンセル', view_in_viewer: '表示', decrypting: '復号中...',