summaryrefslogtreecommitdiff
path: root/pse-dashboard/src/views/SettingsView.vue
diff options
context:
space:
mode:
Diffstat (limited to 'pse-dashboard/src/views/SettingsView.vue')
-rw-r--r--pse-dashboard/src/views/SettingsView.vue347
1 files changed, 347 insertions, 0 deletions
diff --git a/pse-dashboard/src/views/SettingsView.vue b/pse-dashboard/src/views/SettingsView.vue
new file mode 100644
index 0000000..4e5e486
--- /dev/null
+++ b/pse-dashboard/src/views/SettingsView.vue
@@ -0,0 +1,347 @@
+<script setup>
+import {
+ DashboardLayout,
+ FloatingLabelInput,
+ PasswordInput,
+ PasswordValidator,
+} from '@/components'
+import { ref } from 'vue';
+import { store } from '@/store.js'
+import useGPodder from '@/api/gpodder.js'
+import {
+ changePassword,
+ deleteAccount,
+ getEpisodeActions,
+ getTitles,
+ postEpisodeActions,
+ putSubscriptions,
+} from '@/api/pse-squared.js'
+import JSZip from 'jszip';
+import router from '@/router.js'
+import { useLogger } from '@/logger.js'
+import { saveAs } from 'file-saver';
+
+/******************************************************************************/
+/* change password */
+/******************************************************************************/
+
+const changePasswordOld = ref(null);
+const changePasswordNew = ref(null);
+
+const logger = useLogger();
+
+async function formChangePassword() {
+ if(!changePasswordNew.value.valid) {
+ logger.passwordRequirements();
+ return;
+ }
+
+ try {
+ await changePassword({
+ password: changePasswordOld.value,
+ new_password: changePasswordNew.value.password
+ });
+
+ await store.login({
+ username: store.username,
+ password: changePasswordNew.value.password
+ }, false);
+
+ changePasswordOld.value = "";
+ changePasswordNew.value = {password: "", valid: false};
+
+ logger.passwordChanged();
+ } catch (err) {}
+}
+
+/******************************************************************************/
+/* delete account */
+/******************************************************************************/
+
+const deletePassword = ref("");
+
+async function formDelete() {
+
+ // FIXME: backend
+ if (store.password != deletePassword.value) {
+ logger.badRequestError();
+ return;
+ }
+
+ try {
+ await deleteAccount({
+ password: deletePassword.value
+ });
+
+ logger.accountDeleted();
+ store.logout();
+ router.push("/login");
+ } catch (err) {}
+}
+
+/******************************************************************************/
+/* import from another GPodder instance */
+/******************************************************************************/
+
+const gPodderInstance = ref("");
+const gPodderUsername = ref("");
+const gPodderPassword = ref("");
+
+async function formImportGPodder() {
+ const instance = useGPodder({
+ baseURL: gPodderInstance.value
+ });
+
+ await instance.login({
+ username: gPodderUsername.value,
+ password: gPodderPassword.value
+ });
+
+ // download titles from instance and upload to pse-squared
+ try {
+ const response = await instance.getTitles();
+ const subscriptions = response.data.map(sub => sub.url);
+
+ await putSubscriptions(subscriptions);
+ } catch (err) {
+ console.error(err);
+ return;
+ }
+
+ // download episodes from instance and upload to pse-squared
+ try {
+ const response = await instance.getEpisodeActions();
+ const episodes = response.data.actions;
+
+ await postEpisodeActions(episodes);
+ } catch (err) {
+ console.error(err);
+ return;
+ }
+
+ logger.gpodderImport();
+}
+
+
+/******************************************************************************/
+/* import and export data */
+/******************************************************************************/
+
+async function importData(e) {
+ const file = e.target.files[0];
+
+ const zip = await JSZip.loadAsync(file);
+
+ // read and upload subscriptions
+ try {
+ const subscriptionsText = await zip.files["subscriptions.json"].async("string");
+ const subscriptions = JSON.parse(subscriptionsText).map(sub => sub.url);
+ console.log({subscriptions});
+
+ await putSubscriptions(subscriptions);
+ } catch(err) {
+ console.error(err);
+ }
+
+ // read and upload episodeActions
+ try {
+ const episodeActionsText = await zip.files["episodeActions.json"].async("string");
+ const episodeActions = JSON.parse(episodeActionsText).actions;
+
+ await postEpisodeActions(episodeActions);
+ } catch(err) {
+ console.error(err);
+ }
+}
+
+async function exportData() {
+ const zip = new JSZip();
+
+ // load subscriptions
+ try {
+ const subscriptionResponse = await getTitles();
+ zip.file("subscriptions.json", JSON.stringify(subscriptionResponse.data));
+ } catch(err) {
+ console.error(err);
+ }
+
+ // load episodeActions
+ try {
+ const episodeActionsResponse = await getEpisodeActions();
+ zip.file("episode-actions.json", JSON.stringify(episodeActionsResponse.data));
+ } catch(err) {
+ console.error(err);
+ }
+
+ // generate and save zip
+ zip.generateAsync({type:"blob"}).then(function(content) {
+ saveAs(content, "pse-export.zip");
+ });
+}
+
+</script>
+<template>
+ <DashboardLayout>
+ <!-- Überschrift -->
+ <h1 class="h1 mb-4">
+ {{ $t("message.settings") }}
+ </h1>
+
+ <!-- Passwort ändern Sektion -->
+ <form
+ class="mb-4"
+ @submit.prevent="formChangePassword"
+ >
+ <h2>{{ $t("message.changePassword") }}</h2>
+ <!-- Altes Passwort -->
+ <PasswordInput
+ v-model="changePasswordOld"
+ :label="$t('message.oldPassword')"
+ />
+
+ <!-- Neues Passwort -->
+ <PasswordValidator v-model="changePasswordNew" />
+
+ <button
+ type="submit"
+ class="btn btn-primary"
+ >
+ {{ $t("message.changePassword") }}
+ </button>
+ </form>
+
+ <!-- Gpodder-Import Sektion -->
+ <form
+ class="mb-4"
+ @submit.prevent="formImportGPodder"
+ >
+ <h2>Gpodder-{{ $t("message.import") }}</h2>
+
+ <!-- Gpodder Instanz -->
+ <FloatingLabelInput
+ v-model="gPodderInstance"
+ :label="$t('message.instance')"
+ />
+
+ <!-- Nutzername -->
+ <FloatingLabelInput
+ v-model="gPodderUsername"
+ :label="$t('form.username')"
+ />
+
+ <!-- Passwort -->
+ <PasswordInput
+ v-model="gPodderPassword"
+ :label="$t('form.password')"
+ />
+
+ <button
+ type="submit"
+ class="btn btn-primary"
+ >
+ {{ $t("message.importData") }}
+ </button>
+ </form>
+
+ <!-- Personenbezogene Daten im-/exportieren -->
+ <div class="mb-4">
+ <h2>{{ $t("message.personalData") }}</h2>
+ <label
+ for="file"
+ class="btn btn-success"
+ >
+ {{ $t("message.importData") }}
+ </label>
+ <input
+ id="file"
+ type="file"
+ accept=".zip,application/zip"
+ hidden
+ @change="importData"
+ >
+ <button
+ class="btn btn-warning mx-2"
+ @click="exportData"
+ >
+ {{ $t("message.exportData") }}
+ </button>
+ </div>
+
+ <!-- Account löschen -->
+ <div class="mb-4">
+ <h2>{{ $t("message.deleteAccount") }}</h2>
+
+ <button
+ class="btn btn-danger"
+ data-bs-toggle="modal"
+ data-bs-target="#delete-user"
+ >
+ {{ $t("message.deleteAccount") }}
+ </button>
+ </div>
+ </DashboardLayout>
+
+ <!-- Modal for confirming deletion of user -->
+ <div
+ id="delete-user"
+ class="modal"
+ tabindex="-1"
+ >
+ <div class="modal-dialog modal-dialog-centered">
+ <div class="modal-content">
+ <!-- Title of Modal -->
+ <div class="modal-header">
+ <h5 class="modal-title">
+ {{ $t('message.deleteAccount') }}
+ </h5>
+ <button
+ type="button"
+ class="btn-close"
+ data-bs-dismiss="modal"
+ aria-label="Close"
+ />
+ </div>
+
+ <!-- list podcasts to unsubscribe from -->
+ <div class="modal-body">
+ <div
+ class="alert alert-warning d-flex align-items-center"
+ role="alert"
+ >
+ <i class="fs-2 me-3 fa fa-warning" />
+ <div>
+ {{ $t('message.deleteAccountWarning', {username: store.username}) }}
+ </div>
+ </div>
+
+ <PasswordInput
+ v-model="deletePassword"
+ :label="$t('form.password')"
+ />
+ </div>
+
+ <!-- buttons to dismiss or finaly unsubscribe -->
+ <div class="modal-footer">
+ <button
+ type="button"
+ class="btn btn-secondary"
+ data-bs-dismiss="modal"
+ >
+ {{ $t('message.close') }}
+ </button>
+ <button
+ type="button"
+ data-bs-dismiss="modal"
+ class="btn btn-danger"
+ @click="formDelete"
+ >
+ {{ $t('message.deleteAccount') }}
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+<style scoped>
+</style>
+