aboutsummaryrefslogtreecommitdiff
path: root/src/components/user_settings
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/user_settings')
-rw-r--r--src/components/user_settings/confirm.js9
-rw-r--r--src/components/user_settings/confirm.vue22
-rw-r--r--src/components/user_settings/mfa.js155
-rw-r--r--src/components/user_settings/mfa.vue173
-rw-r--r--src/components/user_settings/mfa_backup_codes.js17
-rw-r--r--src/components/user_settings/mfa_backup_codes.vue33
-rw-r--r--src/components/user_settings/mfa_totp.js49
-rw-r--r--src/components/user_settings/mfa_totp.vue43
-rw-r--r--src/components/user_settings/user_settings.js374
-rw-r--r--src/components/user_settings/user_settings.vue646
10 files changed, 0 insertions, 1521 deletions
diff --git a/src/components/user_settings/confirm.js b/src/components/user_settings/confirm.js
deleted file mode 100644
index 0f4ddfc9..00000000
--- a/src/components/user_settings/confirm.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const Confirm = {
- props: ['disabled'],
- data: () => ({}),
- methods: {
- confirm () { this.$emit('confirm') },
- cancel () { this.$emit('cancel') }
- }
-}
-export default Confirm
diff --git a/src/components/user_settings/confirm.vue b/src/components/user_settings/confirm.vue
deleted file mode 100644
index 69b3811b..00000000
--- a/src/components/user_settings/confirm.vue
+++ /dev/null
@@ -1,22 +0,0 @@
-<template>
- <div>
- <slot />
- <button
- class="btn btn-default"
- :disabled="disabled"
- @click="confirm"
- >
- {{ $t('general.confirm') }}
- </button>
- <button
- class="btn btn-default"
- :disabled="disabled"
- @click="cancel"
- >
- {{ $t('general.cancel') }}
- </button>
- </div>
-</template>
-
-<script src="./confirm.js">
-</script>
diff --git a/src/components/user_settings/mfa.js b/src/components/user_settings/mfa.js
deleted file mode 100644
index 3090138a..00000000
--- a/src/components/user_settings/mfa.js
+++ /dev/null
@@ -1,155 +0,0 @@
-import RecoveryCodes from './mfa_backup_codes.vue'
-import TOTP from './mfa_totp.vue'
-import Confirm from './confirm.vue'
-import VueQrcode from '@chenfengyuan/vue-qrcode'
-import { mapState } from 'vuex'
-
-const Mfa = {
- data: () => ({
- settings: { // current settings of MFA
- available: false,
- enabled: false,
- totp: false
- },
- setupState: { // setup mfa
- state: '', // state of setup. '' -> 'getBackupCodes' -> 'setupOTP' -> 'complete'
- setupOTPState: '' // state of setup otp. '' -> 'prepare' -> 'confirm' -> 'complete'
- },
- backupCodes: {
- getNewCodes: false,
- inProgress: false, // progress of fetch codes
- codes: []
- },
- otpSettings: { // pre-setup setting of OTP. secret key, qrcode url.
- provisioning_uri: '',
- key: ''
- },
- currentPassword: null,
- otpConfirmToken: null,
- error: null,
- readyInit: false
- }),
- components: {
- 'recovery-codes': RecoveryCodes,
- 'totp-item': TOTP,
- 'qrcode': VueQrcode,
- 'confirm': Confirm
- },
- computed: {
- canSetupOTP () {
- return (
- (this.setupInProgress && this.backupCodesPrepared) ||
- this.settings.enabled
- ) && !this.settings.totp && !this.setupOTPInProgress
- },
- setupInProgress () {
- return this.setupState.state !== '' && this.setupState.state !== 'complete'
- },
- setupOTPInProgress () {
- return this.setupState.state === 'setupOTP' && !this.completedOTP
- },
- prepareOTP () {
- return this.setupState.setupOTPState === 'prepare'
- },
- confirmOTP () {
- return this.setupState.setupOTPState === 'confirm'
- },
- completedOTP () {
- return this.setupState.setupOTPState === 'completed'
- },
- backupCodesPrepared () {
- return !this.backupCodes.inProgress && this.backupCodes.codes.length > 0
- },
- confirmNewBackupCodes () {
- return this.backupCodes.getNewCodes
- },
- ...mapState({
- backendInteractor: (state) => state.api.backendInteractor
- })
- },
-
- methods: {
- activateOTP () {
- if (!this.settings.enabled) {
- this.setupState.state = 'getBackupcodes'
- this.fetchBackupCodes()
- }
- },
- fetchBackupCodes () {
- this.backupCodes.inProgress = true
- this.backupCodes.codes = []
-
- return this.backendInteractor.generateMfaBackupCodes()
- .then((res) => {
- this.backupCodes.codes = res.codes
- this.backupCodes.inProgress = false
- })
- },
- getBackupCodes () { // get a new backup codes
- this.backupCodes.getNewCodes = true
- },
- confirmBackupCodes () { // confirm getting new backup codes
- this.fetchBackupCodes().then((res) => {
- this.backupCodes.getNewCodes = false
- })
- },
- cancelBackupCodes () { // cancel confirm form of new backup codes
- this.backupCodes.getNewCodes = false
- },
-
- // Setup OTP
- setupOTP () { // prepare setup OTP
- this.setupState.state = 'setupOTP'
- this.setupState.setupOTPState = 'prepare'
- this.backendInteractor.mfaSetupOTP()
- .then((res) => {
- this.otpSettings = res
- this.setupState.setupOTPState = 'confirm'
- })
- },
- doConfirmOTP () { // handler confirm enable OTP
- this.error = null
- this.backendInteractor.mfaConfirmOTP({
- token: this.otpConfirmToken,
- password: this.currentPassword
- })
- .then((res) => {
- if (res.error) {
- this.error = res.error
- return
- }
- this.completeSetup()
- })
- },
-
- completeSetup () {
- this.setupState.setupOTPState = 'complete'
- this.setupState.state = 'complete'
- this.currentPassword = null
- this.error = null
- this.fetchSettings()
- },
- cancelSetup () { // cancel setup
- this.setupState.setupOTPState = ''
- this.setupState.state = ''
- this.currentPassword = null
- this.error = null
- },
- // end Setup OTP
-
- // fetch settings from server
- async fetchSettings () {
- let result = await this.backendInteractor.fetchSettingsMFA()
- if (result.error) return
- this.settings = result.settings
- this.settings.available = true
- return result
- }
- },
- mounted () {
- this.fetchSettings().then(() => {
- this.readyInit = true
- })
- }
-}
-export default Mfa
diff --git a/src/components/user_settings/mfa.vue b/src/components/user_settings/mfa.vue
deleted file mode 100644
index 14ea10a1..00000000
--- a/src/components/user_settings/mfa.vue
+++ /dev/null
@@ -1,173 +0,0 @@
-<template>
- <div
- v-if="readyInit && settings.available"
- class="setting-item mfa-settings"
- >
- <div class="mfa-heading">
- <h2>{{ $t('settings.mfa.title') }}</h2>
- </div>
-
- <div>
- <div
- v-if="!setupInProgress"
- class="setting-item"
- >
- <!-- Enabled methods -->
- <h3>{{ $t('settings.mfa.authentication_methods') }}</h3>
- <totp-item
- :settings="settings"
- @deactivate="fetchSettings"
- @activate="activateOTP"
- />
- <br>
-
- <div v-if="settings.enabled">
- <!-- backup codes block-->
- <recovery-codes
- v-if="!confirmNewBackupCodes"
- :backup-codes="backupCodes"
- />
- <button
- v-if="!confirmNewBackupCodes"
- class="btn btn-default"
- @click="getBackupCodes"
- >
- {{ $t('settings.mfa.generate_new_recovery_codes') }}
- </button>
-
- <div v-if="confirmNewBackupCodes">
- <confirm
- :disabled="backupCodes.inProgress"
- @confirm="confirmBackupCodes"
- @cancel="cancelBackupCodes"
- >
- <p class="warning">
- {{ $t('settings.mfa.warning_of_generate_new_codes') }}
- </p>
- </confirm>
- </div>
- </div>
- </div>
-
- <div v-if="setupInProgress">
- <!-- setup block-->
-
- <h3>{{ $t('settings.mfa.setup_otp') }}</h3>
-
- <recovery-codes
- v-if="!setupOTPInProgress"
- :backup-codes="backupCodes"
- />
-
- <button
- v-if="canSetupOTP"
- class="btn btn-default"
- @click="cancelSetup"
- >
- {{ $t('general.cancel') }}
- </button>
-
- <button
- v-if="canSetupOTP"
- class="btn btn-default"
- @click="setupOTP"
- >
- {{ $t('settings.mfa.setup_otp') }}
- </button>
-
- <template v-if="setupOTPInProgress">
- <i v-if="prepareOTP">{{ $t('settings.mfa.wait_pre_setup_otp') }}</i>
-
- <div v-if="confirmOTP">
- <div class="setup-otp">
- <div class="qr-code">
- <h4>{{ $t('settings.mfa.scan.title') }}</h4>
- <p>{{ $t('settings.mfa.scan.desc') }}</p>
- <qrcode
- :value="otpSettings.provisioning_uri"
- :options="{ width: 200 }"
- />
- <p>
- {{ $t('settings.mfa.scan.secret_code') }}:
- {{ otpSettings.key }}
- </p>
- </div>
-
- <div class="verify">
- <h4>{{ $t('general.verify') }}</h4>
- <p>{{ $t('settings.mfa.verify.desc') }}</p>
- <input
- v-model="otpConfirmToken"
- type="text"
- >
-
- <p>{{ $t('settings.enter_current_password_to_confirm') }}:</p>
- <input
- v-model="currentPassword"
- type="password"
- >
- <div class="confirm-otp-actions">
- <button
- class="btn btn-default"
- @click="doConfirmOTP"
- >
- {{ $t('settings.mfa.confirm_and_enable') }}
- </button>
- <button
- class="btn btn-default"
- @click="cancelSetup"
- >
- {{ $t('general.cancel') }}
- </button>
- </div>
- <div
- v-if="error"
- class="alert error"
- >
- {{ error }}
- </div>
- </div>
- </div>
- </div>
- </template>
- </div>
- </div>
- </div>
-</template>
-
-<script src="./mfa.js"></script>
-<style lang="scss">
-@import '../../_variables.scss';
-.warning {
- color: $fallback--cOrange;
- color: var(--cOrange, $fallback--cOrange);
-}
-.mfa-settings {
- .mfa-heading, .method-item {
- overflow: hidden;
- display: flex;
- flex-wrap: wrap;
- justify-content: space-between;
- align-items: baseline;
- }
-
- .setup-otp {
- display: flex;
- justify-content: center;
- flex-wrap: wrap;
- .qr-code {
- flex: 1;
- padding-right: 10px;
- }
- .verify { flex: 1; }
- .error { margin: 4px 0 0 0; }
- .confirm-otp-actions {
- button {
- width: 15em;
- margin-top: 5px;
- }
-
- }
- }
-}
-</style>
diff --git a/src/components/user_settings/mfa_backup_codes.js b/src/components/user_settings/mfa_backup_codes.js
deleted file mode 100644
index f0a984ec..00000000
--- a/src/components/user_settings/mfa_backup_codes.js
+++ /dev/null
@@ -1,17 +0,0 @@
-export default {
- props: {
- backupCodes: {
- type: Object,
- default: () => ({
- inProgress: false,
- codes: []
- })
- }
- },
- data: () => ({}),
- computed: {
- inProgress () { return this.backupCodes.inProgress },
- ready () { return this.backupCodes.codes.length > 0 },
- displayTitle () { return this.inProgress || this.ready }
- }
-}
diff --git a/src/components/user_settings/mfa_backup_codes.vue b/src/components/user_settings/mfa_backup_codes.vue
deleted file mode 100644
index e6c8ede2..00000000
--- a/src/components/user_settings/mfa_backup_codes.vue
+++ /dev/null
@@ -1,33 +0,0 @@
-<template>
- <div>
- <h4 v-if="displayTitle">
- {{ $t('settings.mfa.recovery_codes') }}
- </h4>
- <i v-if="inProgress">{{ $t('settings.mfa.waiting_a_recovery_codes') }}</i>
- <template v-if="ready">
- <p class="alert warning">
- {{ $t('settings.mfa.recovery_codes_warning') }}
- </p>
- <ul class="backup-codes">
- <li
- v-for="code in backupCodes.codes"
- :key="code"
- >
- {{ code }}
- </li>
- </ul>
- </template>
- </div>
-</template>
-<script src="./mfa_backup_codes.js"></script>
-<style lang="scss">
-@import '../../_variables.scss';
-
-.warning {
- color: $fallback--cOrange;
- color: var(--cOrange, $fallback--cOrange);
-}
-.backup-codes {
- font-family: var(--postCodeFont, monospace);
-}
-</style>
diff --git a/src/components/user_settings/mfa_totp.js b/src/components/user_settings/mfa_totp.js
deleted file mode 100644
index 8408d8e9..00000000
--- a/src/components/user_settings/mfa_totp.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import Confirm from './confirm.vue'
-import { mapState } from 'vuex'
-
-export default {
- props: ['settings'],
- data: () => ({
- error: false,
- currentPassword: '',
- deactivate: false,
- inProgress: false // progress peform request to disable otp method
- }),
- components: {
- 'confirm': Confirm
- },
- computed: {
- isActivated () {
- return this.settings.totp
- },
- ...mapState({
- backendInteractor: (state) => state.api.backendInteractor
- })
- },
- methods: {
- doActivate () {
- this.$emit('activate')
- },
- cancelDeactivate () { this.deactivate = false },
- doDeactivate () {
- this.error = null
- this.deactivate = true
- },
- confirmDeactivate () { // confirm deactivate TOTP method
- this.error = null
- this.inProgress = true
- this.backendInteractor.mfaDisableOTP({
- password: this.currentPassword
- })
- .then((res) => {
- this.inProgress = false
- if (res.error) {
- this.error = res.error
- return
- }
- this.deactivate = false
- this.$emit('deactivate')
- })
- }
- }
-}
diff --git a/src/components/user_settings/mfa_totp.vue b/src/components/user_settings/mfa_totp.vue
deleted file mode 100644
index c6f2cc7b..00000000
--- a/src/components/user_settings/mfa_totp.vue
+++ /dev/null
@@ -1,43 +0,0 @@
-<template>
- <div>
- <div class="method-item">
- <strong>{{ $t('settings.mfa.otp') }}</strong>
- <button
- v-if="!isActivated"
- class="btn btn-default"
- @click="doActivate"
- >
- {{ $t('general.enable') }}
- </button>
-
- <button
- v-if="isActivated"
- class="btn btn-default"
- :disabled="deactivate"
- @click="doDeactivate"
- >
- {{ $t('general.disable') }}
- </button>
- </div>
-
- <confirm
- v-if="deactivate"
- :disabled="inProgress"
- @confirm="confirmDeactivate"
- @cancel="cancelDeactivate"
- >
- {{ $t('settings.enter_current_password_to_confirm') }}:
- <input
- v-model="currentPassword"
- type="password"
- >
- </confirm>
- <div
- v-if="error"
- class="alert error"
- >
- {{ error }}
- </div>
- </div>
-</template>
-<script src="./mfa_totp.js"></script>
diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js
deleted file mode 100644
index 3fdc5340..00000000
--- a/src/components/user_settings/user_settings.js
+++ /dev/null
@@ -1,374 +0,0 @@
-import unescape from 'lodash/unescape'
-import get from 'lodash/get'
-import map from 'lodash/map'
-import reject from 'lodash/reject'
-import TabSwitcher from '../tab_switcher/tab_switcher.js'
-import ImageCropper from '../image_cropper/image_cropper.vue'
-import StyleSwitcher from '../style_switcher/style_switcher.vue'
-import ScopeSelector from '../scope_selector/scope_selector.vue'
-import fileSizeFormatService from '../../services/file_size_format/file_size_format.js'
-import BlockCard from '../block_card/block_card.vue'
-import MuteCard from '../mute_card/mute_card.vue'
-import SelectableList from '../selectable_list/selectable_list.vue'
-import ProgressButton from '../progress_button/progress_button.vue'
-import EmojiInput from '../emoji_input/emoji_input.vue'
-import suggestor from '../emoji_input/suggestor.js'
-import Autosuggest from '../autosuggest/autosuggest.vue'
-import Importer from '../importer/importer.vue'
-import Exporter from '../exporter/exporter.vue'
-import withSubscription from '../../hocs/with_subscription/with_subscription'
-import Checkbox from '../checkbox/checkbox.vue'
-import Mfa from './mfa.vue'
-
-const BlockList = withSubscription({
- fetch: (props, $store) => $store.dispatch('fetchBlocks'),
- select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []),
- childPropName: 'items'
-})(SelectableList)
-
-const MuteList = withSubscription({
- fetch: (props, $store) => $store.dispatch('fetchMutes'),
- select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []),
- childPropName: 'items'
-})(SelectableList)
-
-const UserSettings = {
- data () {
- return {
- newEmail: '',
- newName: this.$store.state.users.currentUser.name,
- newBio: unescape(this.$store.state.users.currentUser.description),
- newLocked: this.$store.state.users.currentUser.locked,
- newNoRichText: this.$store.state.users.currentUser.no_rich_text,
- newDefaultScope: this.$store.state.users.currentUser.default_scope,
- hideFollows: this.$store.state.users.currentUser.hide_follows,
- hideFollowers: this.$store.state.users.currentUser.hide_followers,
- hideFollowsCount: this.$store.state.users.currentUser.hide_follows_count,
- hideFollowersCount: this.$store.state.users.currentUser.hide_followers_count,
- showRole: this.$store.state.users.currentUser.show_role,
- role: this.$store.state.users.currentUser.role,
- discoverable: this.$store.state.users.currentUser.discoverable,
- pickAvatarBtnVisible: true,
- bannerUploading: false,
- backgroundUploading: false,
- banner: null,
- bannerPreview: null,
- background: null,
- backgroundPreview: null,
- bannerUploadError: null,
- backgroundUploadError: null,
- changeEmailError: false,
- changeEmailPassword: '',
- changedEmail: false,
- deletingAccount: false,
- deleteAccountConfirmPasswordInput: '',
- deleteAccountError: false,
- changePasswordInputs: [ '', '', '' ],
- changedPassword: false,
- changePasswordError: false,
- activeTab: 'profile',
- notificationSettings: this.$store.state.users.currentUser.notification_settings
- }
- },
- created () {
- this.$store.dispatch('fetchTokens')
- },
- components: {
- StyleSwitcher,
- ScopeSelector,
- TabSwitcher,
- ImageCropper,
- BlockList,
- MuteList,
- EmojiInput,
- Autosuggest,
- BlockCard,
- MuteCard,
- ProgressButton,
- Importer,
- Exporter,
- Mfa,
- Checkbox
- },
- computed: {
- user () {
- return this.$store.state.users.currentUser
- },
- emojiUserSuggestor () {
- return suggestor({
- emoji: [
- ...this.$store.state.instance.emoji,
- ...this.$store.state.instance.customEmoji
- ],
- users: this.$store.state.users.users,
- updateUsersList: (input) => this.$store.dispatch('searchUsers', input)
- })
- },
- emojiSuggestor () {
- return suggestor({ emoji: [
- ...this.$store.state.instance.emoji,
- ...this.$store.state.instance.customEmoji
- ] })
- },
- pleromaBackend () {
- return this.$store.state.instance.pleromaBackend
- },
- minimalScopesMode () {
- return this.$store.state.instance.minimalScopesMode
- },
- vis () {
- return {
- public: { selected: this.newDefaultScope === 'public' },
- unlisted: { selected: this.newDefaultScope === 'unlisted' },
- private: { selected: this.newDefaultScope === 'private' },
- direct: { selected: this.newDefaultScope === 'direct' }
- }
- },
- currentSaveStateNotice () {
- return this.$store.state.interface.settings.currentSaveStateNotice
- },
- oauthTokens () {
- return this.$store.state.oauthTokens.tokens.map(oauthToken => {
- return {
- id: oauthToken.id,
- appName: oauthToken.app_name,
- validUntil: new Date(oauthToken.valid_until).toLocaleDateString()
- }
- })
- }
- },
- methods: {
- updateProfile () {
- this.$store.state.api.backendInteractor
- .updateProfile({
- params: {
- note: this.newBio,
- locked: this.newLocked,
- // Backend notation.
- /* eslint-disable camelcase */
- display_name: this.newName,
- default_scope: this.newDefaultScope,
- no_rich_text: this.newNoRichText,
- hide_follows: this.hideFollows,
- hide_followers: this.hideFollowers,
- discoverable: this.discoverable,
- hide_follows_count: this.hideFollowsCount,
- hide_followers_count: this.hideFollowersCount,
- show_role: this.showRole
- /* eslint-enable camelcase */
- } }).then((user) => {
- this.$store.commit('addNewUsers', [user])
- this.$store.commit('setCurrentUser', user)
- })
- },
- updateNotificationSettings () {
- this.$store.state.api.backendInteractor
- .updateNotificationSettings({ settings: this.notificationSettings })
- },
- changeVis (visibility) {
- this.newDefaultScope = visibility
- },
- uploadFile (slot, e) {
- const file = e.target.files[0]
- if (!file) { return }
- if (file.size > this.$store.state.instance[slot + 'limit']) {
- const filesize = fileSizeFormatService.fileSizeFormat(file.size)
- const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit'])
- this[slot + 'UploadError'] = this.$t('upload.error.base') + ' ' + this.$t('upload.error.file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit })
- return
- }
- // eslint-disable-next-line no-undef
- const reader = new FileReader()
- reader.onload = ({ target }) => {
- const img = target.result
- this[slot + 'Preview'] = img
- this[slot] = file
- }
- reader.readAsDataURL(file)
- },
- submitAvatar (cropper, file) {
- const that = this
- return new Promise((resolve, reject) => {
- function updateAvatar (avatar) {
- that.$store.state.api.backendInteractor.updateAvatar({ avatar })
- .then((user) => {
- that.$store.commit('addNewUsers', [user])
- that.$store.commit('setCurrentUser', user)
- resolve()
- })
- .catch((err) => {
- reject(new Error(that.$t('upload.error.base') + ' ' + err.message))
- })
- }
-
- if (cropper) {
- cropper.getCroppedCanvas().toBlob(updateAvatar, file.type)
- } else {
- updateAvatar(file)
- }
- })
- },
- clearUploadError (slot) {
- this[slot + 'UploadError'] = null
- },
- submitBanner () {
- if (!this.bannerPreview) { return }
-
- this.bannerUploading = true
- this.$store.state.api.backendInteractor.updateBanner({ banner: this.banner })
- .then((user) => {
- this.$store.commit('addNewUsers', [user])
- this.$store.commit('setCurrentUser', user)
- this.bannerPreview = null
- })
- .catch((err) => {
- this.bannerUploadError = this.$t('upload.error.base') + ' ' + err.message
- })
- .then(() => { this.bannerUploading = false })
- },
- submitBg () {
- if (!this.backgroundPreview) { return }
- let background = this.background
- this.backgroundUploading = true
- this.$store.state.api.backendInteractor.updateBg({ background }).then((data) => {
- if (!data.error) {
- this.$store.commit('addNewUsers', [data])
- this.$store.commit('setCurrentUser', data)
- this.backgroundPreview = null
- } else {
- this.backgroundUploadError = this.$t('upload.error.base') + data.error
- }
- this.backgroundUploading = false
- })
- },
- importFollows (file) {
- return this.$store.state.api.backendInteractor.importFollows(file)
- .then((status) => {
- if (!status) {
- throw new Error('failed')
- }
- })
- },
- importBlocks (file) {
- return this.$store.state.api.backendInteractor.importBlocks(file)
- .then((status) => {
- if (!status) {
- throw new Error('failed')
- }
- })
- },
- generateExportableUsersContent (users) {
- // Get addresses
- return users.map((user) => {
- // check is it's a local user
- if (user && user.is_local) {
- // append the instance address
- // eslint-disable-next-line no-undef
- return user.screen_name + '@' + location.hostname
- }
- return user.screen_name
- }).join('\n')
- },
- getFollowsContent () {
- return this.$store.state.api.backendInteractor.exportFriends({ id: this.$store.state.users.currentUser.id })
- .then(this.generateExportableUsersContent)
- },
- getBlocksContent () {
- return this.$store.state.api.backendInteractor.fetchBlocks()
- .then(this.generateExportableUsersContent)
- },
- confirmDelete () {
- this.deletingAccount = true
- },
- deleteAccount () {
- this.$store.state.api.backendInteractor.deleteAccount({ password: this.deleteAccountConfirmPasswordInput })
- .then((res) => {
- if (res.status === 'success') {
- this.$store.dispatch('logout')
- this.$router.push({ name: 'root' })
- } else {
- this.deleteAccountError = res.error
- }
- })
- },
- changePassword () {
- const params = {
- password: this.changePasswordInputs[0],
- newPassword: this.changePasswordInputs[1],
- newPasswordConfirmation: this.changePasswordInputs[2]
- }
- this.$store.state.api.backendInteractor.changePassword(params)
- .then((res) => {
- if (res.status === 'success') {
- this.changedPassword = true
- this.changePasswordError = false
- this.logout()
- } else {
- this.changedPassword = false
- this.changePasswordError = res.error
- }
- })
- },
- changeEmail () {
- const params = {
- email: this.newEmail,
- password: this.changeEmailPassword
- }
- this.$store.state.api.backendInteractor.changeEmail(params)
- .then((res) => {
- if (res.status === 'success') {
- this.changedEmail = true
- this.changeEmailError = false
- } else {
- this.changedEmail = false
- this.changeEmailError = res.error
- }
- })
- },
- activateTab (tabName) {
- this.activeTab = tabName
- },
- logout () {
- this.$store.dispatch('logout')
- this.$router.replace('/')
- },
- revokeToken (id) {
- if (window.confirm(`${this.$i18n.t('settings.revoke_token')}?`)) {
- this.$store.dispatch('revokeToken', id)
- }
- },
- filterUnblockedUsers (userIds) {
- return reject(userIds, (userId) => {
- const user = this.$store.getters.findUser(userId)
- return !user || user.statusnet_blocking || user.id === this.$store.state.users.currentUser.id
- })
- },
- filterUnMutedUsers (userIds) {
- return reject(userIds, (userId) => {
- const user = this.$store.getters.findUser(userId)
- return !user || user.muted || user.id === this.$store.state.users.currentUser.id
- })
- },
- queryUserIds (query) {
- return this.$store.dispatch('searchUsers', query)
- .then((users) => map(users, 'id'))
- },
- blockUsers (ids) {
- return this.$store.dispatch('blockUsers', ids)
- },
- unblockUsers (ids) {
- return this.$store.dispatch('unblockUsers', ids)
- },
- muteUsers (ids) {
- return this.$store.dispatch('muteUsers', ids)
- },
- unmuteUsers (ids) {
- return this.$store.dispatch('unmuteUsers', ids)
- },
- identity (value) {
- return value
- }
- }
-}
-
-export default UserSettings
diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue
deleted file mode 100644
index 8c18cf49..00000000
--- a/src/components/user_settings/user_settings.vue
+++ /dev/null
@@ -1,646 +0,0 @@
-<template>
- <div class="settings panel panel-default">
- <div class="panel-heading">
- <div class="title">
- {{ $t('settings.user_settings') }}
- </div>
- <transition name="fade">
- <template v-if="currentSaveStateNotice">
- <div
- v-if="currentSaveStateNotice.error"
- class="alert error"
- @click.prevent
- >
- {{ $t('settings.saving_err') }}
- </div>
-
- <div
- v-if="!currentSaveStateNotice.error"
- class="alert transparent"
- @click.prevent
- >
- {{ $t('settings.saving_ok') }}
- </div>
- </template>
- </transition>
- </div>
- <div class="panel-body profile-edit">
- <tab-switcher>
- <div :label="$t('settings.profile_tab')">
- <div class="setting-item">
- <h2>{{ $t('settings.name_bio') }}</h2>
- <p>{{ $t('settings.name') }}</p>
- <EmojiInput
- v-model="newName"
- enable-emoji-picker
- :suggest="emojiSuggestor"
- >
- <input
- id="username"
- v-model="newName"
- classname="name-changer"
- >
- </EmojiInput>
- <p>{{ $t('settings.bio') }}</p>
- <EmojiInput
- v-model="newBio"
- enable-emoji-picker
- :suggest="emojiUserSuggestor"
- >
- <textarea
- v-model="newBio"
- classname="bio"
- />
- </EmojiInput>
- <p>
- <Checkbox v-model="newLocked">
- {{ $t('settings.lock_account_description') }}
- </Checkbox>
- </p>
- <div>
- <label for="default-vis">{{ $t('settings.default_vis') }}</label>
- <div
- id="default-vis"
- class="visibility-tray"
- >
- <scope-selector
- :show-all="true"
- :user-default="newDefaultScope"
- :initial-scope="newDefaultScope"
- :on-scope-change="changeVis"
- />
- </div>
- </div>
- <p>
- <Checkbox v-model="newNoRichText">
- {{ $t('settings.no_rich_text_description') }}
- </Checkbox>
- </p>
- <p>
- <Checkbox v-model="hideFollows">
- {{ $t('settings.hide_follows_description') }}
- </Checkbox>
- </p>
- <p class="setting-subitem">
- <Checkbox
- v-model="hideFollowsCount"
- :disabled="!hideFollows"
- >
- {{ $t('settings.hide_follows_count_description') }}
- </Checkbox>
- </p>
- <p>
- <Checkbox
- v-model="hideFollowers"
- >
- {{ $t('settings.hide_followers_description') }}
- </Checkbox>
- </p>
- <p class="setting-subitem">
- <Checkbox
- v-model="hideFollowersCount"
- :disabled="!hideFollowers"
- >
- {{ $t('settings.hide_followers_count_description') }}
- </Checkbox>
- </p>
- <p>
- <Checkbox v-model="showRole">
- <template v-if="role === 'admin'">
- {{ $t('settings.show_admin_badge') }}
- </template>
- <template v-if="role === 'moderator'">
- {{ $t('settings.show_moderator_badge') }}
- </template>
- </Checkbox>
- </p>
- <p>
- <Checkbox v-model="discoverable">
- {{ $t('settings.discoverable') }}
- </Checkbox>
- </p>
- <button
- :disabled="newName && newName.length === 0"
- class="btn btn-default"
- @click="updateProfile"
- >
- {{ $t('general.submit') }}
- </button>
- </div>
- <div class="setting-item">
- <h2>{{ $t('settings.avatar') }}</h2>
- <p class="visibility-notice">
- {{ $t('settings.avatar_size_instruction') }}
- </p>
- <p>{{ $t('settings.current_avatar') }}</p>
- <img
- :src="user.profile_image_url_original"
- class="current-avatar"
- >
- <p>{{ $t('settings.set_new_avatar') }}</p>
- <button
- v-show="pickAvatarBtnVisible"
- id="pick-avatar"
- class="btn"
- type="button"
- >
- {{ $t('settings.upload_a_photo') }}
- </button>
- <image-cropper
- trigger="#pick-avatar"
- :submit-handler="submitAvatar"
- @open="pickAvatarBtnVisible=false"
- @close="pickAvatarBtnVisible=true"
- />
- </div>
- <div class="setting-item">
- <h2>{{ $t('settings.profile_banner') }}</h2>
- <p>{{ $t('settings.current_profile_banner') }}</p>
- <img
- :src="user.cover_photo"
- class="banner"
- >
- <p>{{ $t('settings.set_new_profile_banner') }}</p>
- <img
- v-if="bannerPreview"
- class="banner"
- :src="bannerPreview"
- >
- <div>
- <input
- type="file"
- @change="uploadFile('banner', $event)"
- >
- </div>
- <i
- v-if="bannerUploading"
- class=" icon-spin4 animate-spin uploading"
- />
- <button
- v-else-if="bannerPreview"
- class="btn btn-default"
- @click="submitBanner"
- >
- {{ $t('general.submit') }}
- </button>
- <div
- v-if="bannerUploadError"
- class="alert error"
- >
- Error: {{ bannerUploadError }}
- <i
- class="button-icon icon-cancel"
- @click="clearUploadError('banner')"
- />
- </div>
- </div>
- <div class="setting-item">
- <h2>{{ $t('settings.profile_background') }}</h2>
- <p>{{ $t('settings.set_new_profile_background') }}</p>
- <img
- v-if="backgroundPreview"
- class="bg"
- :src="backgroundPreview"
- >
- <div>
- <input
- type="file"
- @change="uploadFile('background', $event)"
- >
- </div>
- <i
- v-if="backgroundUploading"
- class=" icon-spin4 animate-spin uploading"
- />
- <button
- v-else-if="backgroundPreview"
- class="btn btn-default"
- @click="submitBg"
- >
- {{ $t('general.submit') }}
- </button>
- <div
- v-if="backgroundUploadError"
- class="alert error"
- >
- Error: {{ backgroundUploadError }}
- <i
- class="button-icon icon-cancel"
- @click="clearUploadError('background')"
- />
- </div>
- </div>
- </div>
-
- <div :label="$t('settings.security_tab')">
- <div class="setting-item">
- <h2>{{ $t('settings.change_email') }}</h2>
- <div>
- <p>{{ $t('settings.new_email') }}</p>
- <input
- v-model="newEmail"
- type="email"
- autocomplete="email"
- >
- </div>
- <div>
- <p>{{ $t('settings.current_password') }}</p>
- <input
- v-model="changeEmailPassword"
- type="password"
- autocomplete="current-password"
- >
- </div>
- <button
- class="btn btn-default"
- @click="changeEmail"
- >
- {{ $t('general.submit') }}
- </button>
- <p v-if="changedEmail">
- {{ $t('settings.changed_email') }}
- </p>
- <template v-if="changeEmailError !== false">
- <p>{{ $t('settings.change_email_error') }}</p>
- <p>{{ changeEmailError }}</p>
- </template>
- </div>
-
- <div class="setting-item">
- <h2>{{ $t('settings.change_password') }}</h2>
- <div>
- <p>{{ $t('settings.current_password') }}</p>
- <input
- v-model="changePasswordInputs[0]"
- type="password"
- >
- </div>
- <div>
- <p>{{ $t('settings.new_password') }}</p>
- <input
- v-model="changePasswordInputs[1]"
- type="password"
- >
- </div>
- <div>
- <p>{{ $t('settings.confirm_new_password') }}</p>
- <input
- v-model="changePasswordInputs[2]"
- type="password"
- >
- </div>
- <button
- class="btn btn-default"
- @click="changePassword"
- >
- {{ $t('general.submit') }}
- </button>
- <p v-if="changedPassword">
- {{ $t('settings.changed_password') }}
- </p>
- <p v-else-if="changePasswordError !== false">
- {{ $t('settings.change_password_error') }}
- </p>
- <p v-if="changePasswordError">
- {{ changePasswordError }}
- </p>
- </div>
-
- <div class="setting-item">
- <h2>{{ $t('settings.oauth_tokens') }}</h2>
- <table class="oauth-tokens">
- <thead>
- <tr>
- <th>{{ $t('settings.app_name') }}</th>
- <th>{{ $t('settings.valid_until') }}</th>
- <th />
- </tr>
- </thead>
- <tbody>
- <tr
- v-for="oauthToken in oauthTokens"
- :key="oauthToken.id"
- >
- <td>{{ oauthToken.appName }}</td>
- <td>{{ oauthToken.validUntil }}</td>
- <td class="actions">
- <button
- class="btn btn-default"
- @click="revokeToken(oauthToken.id)"
- >
- {{ $t('settings.revoke_token') }}
- </button>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- <mfa />
- <div class="setting-item">
- <h2>{{ $t('settings.delete_account') }}</h2>
- <p v-if="!deletingAccount">
- {{ $t('settings.delete_account_description') }}
- </p>
- <div v-if="deletingAccount">
- <p>{{ $t('settings.delete_account_instructions') }}</p>
- <p>{{ $t('login.password') }}</p>
- <input
- v-model="deleteAccountConfirmPasswordInput"
- type="password"
- >
- <button
- class="btn btn-default"
- @click="deleteAccount"
- >
- {{ $t('settings.delete_account') }}
- </button>
- </div>
- <p v-if="deleteAccountError !== false">
- {{ $t('settings.delete_account_error') }}
- </p>
- <p v-if="deleteAccountError">
- {{ deleteAccountError }}
- </p>
- <button
- v-if="!deletingAccount"
- class="btn btn-default"
- @click="confirmDelete"
- >
- {{ $t('general.submit') }}
- </button>
- </div>
- </div>
-
- <div
- v-if="pleromaBackend"
- :label="$t('settings.notifications')"
- >
- <div class="setting-item">
- <div class="select-multiple">
- <span class="label">{{ $t('settings.notification_setting') }}</span>
- <ul class="option-list">
- <li>
- <Checkbox v-model="notificationSettings.follows">
- {{ $t('settings.notification_setting_follows') }}
- </Checkbox>
- </li>
- <li>
- <Checkbox v-model="notificationSettings.followers">
- {{ $t('settings.notification_setting_followers') }}
- </Checkbox>
- </li>
- <li>
- <Checkbox v-model="notificationSettings.non_follows">
- {{ $t('settings.notification_setting_non_follows') }}
- </Checkbox>
- </li>
- <li>
- <Checkbox v-model="notificationSettings.non_followers">
- {{ $t('settings.notification_setting_non_followers') }}
- </Checkbox>
- </li>
- </ul>
- </div>
- <p>{{ $t('settings.notification_mutes') }}</p>
- <p>{{ $t('settings.notification_blocks') }}</p>
- <button
- class="btn btn-default"
- @click="updateNotificationSettings"
- >
- {{ $t('general.submit') }}
- </button>
- </div>
- </div>
-
- <div
- v-if="pleromaBackend"
- :label="$t('settings.data_import_export_tab')"
- >
- <div class="setting-item">
- <h2>{{ $t('settings.follow_import') }}</h2>
- <p>{{ $t('settings.import_followers_from_a_csv_file') }}</p>
- <Importer
- :submit-handler="importFollows"
- :success-message="$t('settings.follows_imported')"
- :error-message="$t('settings.follow_import_error')"
- />
- </div>
- <div class="setting-item">
- <h2>{{ $t('settings.follow_export') }}</h2>
- <Exporter
- :get-content="getFollowsContent"
- filename="friends.csv"
- :export-button-label="$t('settings.follow_export_button')"
- />
- </div>
- <div class="setting-item">
- <h2>{{ $t('settings.block_import') }}</h2>
- <p>{{ $t('settings.import_blocks_from_a_csv_file') }}</p>
- <Importer
- :submit-handler="importBlocks"
- :success-message="$t('settings.blocks_imported')"
- :error-message="$t('settings.block_import_error')"
- />
- </div>
- <div class="setting-item">
- <h2>{{ $t('settings.block_export') }}</h2>
- <Exporter
- :get-content="getBlocksContent"
- filename="blocks.csv"
- :export-button-label="$t('settings.block_export_button')"
- />
- </div>
- </div>
-
- <div :label="$t('settings.blocks_tab')">
- <div class="profile-edit-usersearch-wrapper">
- <Autosuggest
- :filter="filterUnblockedUsers"
- :query="queryUserIds"
- :placeholder="$t('settings.search_user_to_block')"
- >
- <BlockCard
- slot-scope="row"
- :user-id="row.item"
- />
- </Autosuggest>
- </div>
- <BlockList
- :refresh="true"
- :get-key="identity"
- >
- <template
- slot="header"
- slot-scope="{selected}"
- >
- <div class="profile-edit-bulk-actions">
- <ProgressButton
- v-if="selected.length > 0"
- class="btn btn-default"
- :click="() => blockUsers(selected)"
- >
- {{ $t('user_card.block') }}
- <template slot="progress">
- {{ $t('user_card.block_progress') }}
- </template>
- </ProgressButton>
- <ProgressButton
- v-if="selected.length > 0"
- class="btn btn-default"
- :click="() => unblockUsers(selected)"
- >
- {{ $t('user_card.unblock') }}
- <template slot="progress">
- {{ $t('user_card.unblock_progress') }}
- </template>
- </ProgressButton>
- </div>
- </template>
- <template
- slot="item"
- slot-scope="{item}"
- >
- <BlockCard :user-id="item" />
- </template>
- <template slot="empty">
- {{ $t('settings.no_blocks') }}
- </template>
- </BlockList>
- </div>
-
- <div :label="$t('settings.mutes_tab')">
- <div class="profile-edit-usersearch-wrapper">
- <Autosuggest
- :filter="filterUnMutedUsers"
- :query="queryUserIds"
- :placeholder="$t('settings.search_user_to_mute')"
- >
- <MuteCard
- slot-scope="row"
- :user-id="row.item"
- />
- </Autosuggest>
- </div>
- <MuteList
- :refresh="true"
- :get-key="identity"
- >
- <template
- slot="header"
- slot-scope="{selected}"
- >
- <div class="profile-edit-bulk-actions">
- <ProgressButton
- v-if="selected.length > 0"
- class="btn btn-default"
- :click="() => muteUsers(selected)"
- >
- {{ $t('user_card.mute') }}
- <template slot="progress">
- {{ $t('user_card.mute_progress') }}
- </template>
- </ProgressButton>
- <ProgressButton
- v-if="selected.length > 0"
- class="btn btn-default"
- :click="() => unmuteUsers(selected)"
- >
- {{ $t('user_card.unmute') }}
- <template slot="progress">
- {{ $t('user_card.unmute_progress') }}
- </template>
- </ProgressButton>
- </div>
- </template>
- <template
- slot="item"
- slot-scope="{item}"
- >
- <MuteCard :user-id="item" />
- </template>
- <template slot="empty">
- {{ $t('settings.no_mutes') }}
- </template>
- </MuteList>
- </div>
- </tab-switcher>
- </div>
- </div>
-</template>
-
-<script src="./user_settings.js">
-</script>
-
-<style lang="scss">
-@import '../../_variables.scss';
-
-.profile-edit {
- .bio {
- margin: 0;
- }
-
- .visibility-tray {
- padding-top: 5px;
- }
-
- input[type=file] {
- padding: 5px;
- height: auto;
- }
-
- .banner {
- max-width: 100%;
- }
-
- .uploading {
- font-size: 1.5em;
- margin: 0.25em;
- }
-
- .name-changer {
- width: 100%;
- }
-
- .bg {
- max-width: 100%;
- }
-
- .current-avatar {
- display: block;
- width: 150px;
- height: 150px;
- border-radius: $fallback--avatarRadius;
- border-radius: var(--avatarRadius, $fallback--avatarRadius);
- }
-
- .oauth-tokens {
- width: 100%;
-
- th {
- text-align: left;
- }
-
- .actions {
- text-align: right;
- }
- }
-
- &-usersearch-wrapper {
- padding: 1em;
- }
-
- &-bulk-actions {
- text-align: right;
- padding: 0 1em;
- min-height: 28px;
-
- button {
- width: 10em;
- }
- }
-
- .setting-subitem {
- margin-left: 1.75em;
- }
-}
-</style>