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 @@
+
+
+ {{ mdiRefresh }}
+ {{ strings?.['update_the_app'] }}
+
+
+
+
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: '復号中...',