Browse Source

refactor: improve camera controls and modularize data export

- Add detailed error logging for image upload failures in PhotoDialog and SharedPhotoView
- Extract DataExportSection into standalone component with proper prop handling
- Fix Backup Identifier Seed visibility by passing activeDid prop
cross-platform-factory
Matthew Raymer 3 weeks ago
parent
commit
94bd649003
  1. 114
      src/components/DataExportSection.vue
  2. 21
      src/components/PhotoDialog.vue
  3. 50
      src/views/AccountViewView.vue
  4. 21
      src/views/SharedPhotoView.vue

114
src/components/DataExportSection.vue

@ -0,0 +1,114 @@
<template>
<div
id="sectionDataExport"
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
>
<div class="mb-2 font-bold">Data Export</div>
<router-link
v-if="activeDid"
:to="{ name: 'seed-backup' }"
class="block w-full text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2 mt-2"
>
Backup Identifier Seed
</router-link>
<button
:class="computedStartDownloadLinkClassNames()"
class="block w-full text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
@click="exportDatabase()"
>
Download Settings & Contacts
<br />
(excluding Identifier Data)
</button>
<a
ref="downloadLink"
:class="computedDownloadLinkClassNames()"
class="block w-full text-center text-md bg-gradient-to-b from-green-500 to-green-800 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-6"
>
If no download happened yet, click again here to download now.
</a>
<div class="mt-4">
<p>
After the download, you can save the file in your preferred storage
location.
</p>
<ul>
<li class="list-disc list-outside ml-4">
On iOS: Choose "More..." and select a place in iCloud, or go "Back"
and save to another location.
</li>
<li class="list-disc list-outside ml-4">
On Android: Choose "Open" and then share
<font-awesome icon="share-nodes" class="fa-fw" />
to your prefered place.
</li>
</ul>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-facing-decorator";
import { NotificationIface } from "../constants/app";
import { db } from "../db/index";
import { logger } from "../utils/logger";
@Component
export default class DataExportSection extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void;
@Prop({ required: true }) readonly activeDid!: string;
downloadUrl = "";
beforeUnmount() {
if (this.downloadUrl) {
URL.revokeObjectURL(this.downloadUrl);
}
}
public async exportDatabase() {
try {
const blob = await db.export({ prettyJson: true });
this.downloadUrl = URL.createObjectURL(blob);
const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement;
downloadAnchor.href = this.downloadUrl;
downloadAnchor.download = `${db.name}-backup.json`;
downloadAnchor.click();
this.$notify(
{
group: "alert",
type: "success",
title: "Download Started",
text: "See your downloads directory for the backup. It is in the Dexie format.",
},
-1,
);
setTimeout(() => URL.revokeObjectURL(this.downloadUrl), 1000);
} catch (error) {
logger.error("Export Error:", error);
this.$notify(
{
group: "alert",
type: "danger",
title: "Export Error",
text: "There was an error exporting the data.",
},
3000,
);
}
}
public computedStartDownloadLinkClassNames() {
return {
hidden: this.downloadUrl,
};
}
public computedDownloadLinkClassNames() {
return {
hidden: !this.downloadUrl,
};
}
}
</script>

21
src/components/PhotoDialog.vue

@ -273,14 +273,14 @@ export default class PhotoDialog extends Vue {
} catch (error) {
// Log the raw error first
logger.error("Raw error object:", JSON.stringify(error, null, 2));
let errorMessage = "There was an error saving the picture.";
if (axios.isAxiosError(error)) {
const status = error.response?.status;
const statusText = error.response?.statusText;
const data = error.response?.data;
// Log detailed error information
logger.error("Upload error details:", {
status,
@ -290,16 +290,17 @@ export default class PhotoDialog extends Vue {
config: {
url: error.config?.url,
method: error.config?.method,
headers: error.config?.headers
}
headers: error.config?.headers,
},
});
if (status === 401) {
errorMessage = "Authentication failed. Please try logging in again.";
} else if (status === 413) {
errorMessage = "Image file is too large. Please try a smaller image.";
} else if (status === 415) {
errorMessage = "Unsupported image format. Please try a different image.";
errorMessage =
"Unsupported image format. Please try a different image.";
} else if (status && status >= 500) {
errorMessage = "Server error. Please try again later.";
} else if (data?.message) {
@ -311,16 +312,16 @@ export default class PhotoDialog extends Vue {
name: error.name,
message: error.message,
stack: error.stack,
error: JSON.stringify(error, Object.getOwnPropertyNames(error), 2)
error: JSON.stringify(error, Object.getOwnPropertyNames(error), 2),
});
} else {
// Log any other type of error
logger.error("Unknown error type:", {
error: JSON.stringify(error, null, 2),
type: typeof error
type: typeof error,
});
}
this.$notify(
{
group: "alert",

50
src/views/AccountViewView.vue

@ -420,53 +420,7 @@
</button>
</div>
<div
id="sectionDataExport"
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
>
<div class="mb-2 font-bold">Data Export</div>
<router-link
v-if="activeDid"
:to="{ name: 'seed-backup' }"
class="block w-full text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2 mt-2"
>
Backup Identifier Seed
</router-link>
<button
:class="computedStartDownloadLinkClassNames()"
class="block w-full text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
@click="exportDatabase()"
>
Download Settings & Contacts
<br />
(excluding Identifier Data)
</button>
<a
ref="downloadLink"
:class="computedDownloadLinkClassNames()"
class="block w-full text-center text-md bg-gradient-to-b from-green-500 to-green-800 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-6"
>
If no download happened yet, click again here to download now.
</a>
<div class="mt-4">
<p>
After the download, you can save the file in your preferred storage
location.
</p>
<ul>
<li class="list-disc list-outside ml-4">
On iOS: Choose "More..." and select a place in iCloud, or go "Back"
and save to another location.
</li>
<li class="list-disc list-outside ml-4">
On Android: Choose "Open" and then share
<font-awesome icon="share-nodes" class="fa-fw" />
to your prefered place.
</li>
</ul>
</div>
</div>
<DataExportSection :active-did="activeDid" />
<!-- id used by puppeteer test script -->
<h3
@ -946,6 +900,7 @@ import PushNotificationPermission from "../components/PushNotificationPermission
import QuickNav from "../components/QuickNav.vue";
import TopMessage from "../components/TopMessage.vue";
import UserNameDialog from "../components/UserNameDialog.vue";
import DataExportSection from "../components/DataExportSection.vue";
import {
AppString,
DEFAULT_IMAGE_API_SERVER,
@ -999,6 +954,7 @@ const inputImportFileNameRef = ref<Blob>();
QuickNav,
TopMessage,
UserNameDialog,
DataExportSection,
},
})
export default class AccountViewView extends Vue {

21
src/views/SharedPhotoView.vue

@ -223,14 +223,14 @@ export default class SharedPhotoView extends Vue {
} catch (error) {
// Log the raw error first
logger.error("Raw error object:", JSON.stringify(error, null, 2));
let errorMessage = "There was an error saving the picture.";
if (axios.isAxiosError(error)) {
const status = error.response?.status;
const statusText = error.response?.statusText;
const data = error.response?.data;
// Log detailed error information
logger.error("Upload error details:", {
status,
@ -240,16 +240,17 @@ export default class SharedPhotoView extends Vue {
config: {
url: error.config?.url,
method: error.config?.method,
headers: error.config?.headers
}
headers: error.config?.headers,
},
});
if (status === 401) {
errorMessage = "Authentication failed. Please try logging in again.";
} else if (status === 413) {
errorMessage = "Image file is too large. Please try a smaller image.";
} else if (status === 415) {
errorMessage = "Unsupported image format. Please try a different image.";
errorMessage =
"Unsupported image format. Please try a different image.";
} else if (status && status >= 500) {
errorMessage = "Server error. Please try again later.";
} else if (data?.message) {
@ -261,16 +262,16 @@ export default class SharedPhotoView extends Vue {
name: error.name,
message: error.message,
stack: error.stack,
error: JSON.stringify(error, Object.getOwnPropertyNames(error), 2)
error: JSON.stringify(error, Object.getOwnPropertyNames(error), 2),
});
} else {
// Log any other type of error
logger.error("Unknown error type:", {
error: JSON.stringify(error, null, 2),
type: typeof error
type: typeof error,
});
}
this.$notify(
{
group: "alert",

Loading…
Cancel
Save