import { compose } from 'vue-compose' import unescape from 'lodash/unescape' import get from 'lodash/get' 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 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 EmojiInput from '../emoji-input/emoji-input.vue' import withSubscription from '../../hocs/with_subscription/with_subscription' import withList from '../../hocs/with_list/with_list' const BlockList = compose( withSubscription({ fetch: (props, $store) => $store.dispatch('fetchBlocks'), select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []), childPropName: 'entries' }), withList({ getEntryProps: userId => ({ userId }) }) )(BlockCard) const MuteList = compose( withSubscription({ fetch: (props, $store) => $store.dispatch('fetchMutes'), select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []), childPropName: 'entries' }), withList({ getEntryProps: userId => ({ userId }) }) )(MuteCard) const UserSettings = { data () { return { 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, showRole: this.$store.state.users.currentUser.show_role, role: this.$store.state.users.currentUser.role, followList: null, followImportError: false, followsImported: false, enableFollowsExport: true, pickAvatarBtnVisible: true, bannerUploading: false, backgroundUploading: false, followListUploading: false, bannerPreview: null, backgroundPreview: null, bannerUploadError: null, backgroundUploadError: null, deletingAccount: false, deleteAccountConfirmPasswordInput: '', deleteAccountError: false, changePasswordInputs: [ '', '', '' ], changedPassword: false, changePasswordError: false, activeTab: 'profile' } }, created () { this.$store.dispatch('fetchTokens') }, components: { StyleSwitcher, TabSwitcher, ImageCropper, BlockList, MuteList, EmojiInput }, computed: { user () { return this.$store.state.users.currentUser }, pleromaBackend () { return this.$store.state.instance.pleromaBackend }, scopeOptionsEnabled () { return this.$store.state.instance.scopeOptionsEnabled }, 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 () { const name = this.newName const description = this.newBio const locked = this.newLocked // Backend notation. /* eslint-disable camelcase */ const default_scope = this.newDefaultScope const no_rich_text = this.newNoRichText const hide_follows = this.hideFollows const hide_followers = this.hideFollowers const show_role = this.showRole /* eslint-enable camelcase */ this.$store.state.api.backendInteractor .updateProfile({ params: { name, description, locked, // Backend notation. /* eslint-disable camelcase */ default_scope, no_rich_text, hide_follows, hide_followers, show_role /* eslint-enable camelcase */ }}).then((user) => { if (!user.error) { this.$store.commit('addNewUsers', [user]) this.$store.commit('setCurrentUser', user) } }) }, 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 } reader.readAsDataURL(file) }, submitAvatar (cropper, file) { let img if (cropper) { img = cropper.getCroppedCanvas().toDataURL(file.type) } else { img = file } return this.$store.state.api.backendInteractor.updateAvatar({ params: { img } }).then((user) => { if (!user.error) { this.$store.commit('addNewUsers', [user]) this.$store.commit('setCurrentUser', user) } else { throw new Error(this.$t('upload.error.base') + user.error) } }) }, clearUploadError (slot) { this[slot + 'UploadError'] = null }, submitBanner () { if (!this.bannerPreview) { return } let banner = this.bannerPreview // eslint-disable-next-line no-undef let imginfo = new Image() /* eslint-disable camelcase */ let offset_top, offset_left, width, height imginfo.src = banner width = imginfo.width height = imginfo.height offset_top = 0 offset_left = 0 this.bannerUploading = true this.$store.state.api.backendInteractor.updateBanner({params: {banner, offset_top, offset_left, width, height}}).then((data) => { if (!data.error) { let clone = JSON.parse(JSON.stringify(this.$store.state.users.currentUser)) clone.cover_photo = data.url this.$store.commit('addNewUsers', [clone]) this.$store.commit('setCurrentUser', clone) this.bannerPreview = null } else { this.bannerUploadError = this.$t('upload.error.base') + data.error } this.bannerUploading = false }) /* eslint-enable camelcase */ }, submitBg () { if (!this.backgroundPreview) { return } let img = this.backgroundPreview // eslint-disable-next-line no-undef let imginfo = new Image() let cropX, cropY, cropW, cropH imginfo.src = img cropX = 0 cropY = 0 cropW = imginfo.width cropH = imginfo.width this.backgroundUploading = true this.$store.state.api.backendInteractor.updateBg({params: {img, cropX, cropY, cropW, cropH}}).then((data) => { if (!data.error) { let clone = JSON.parse(JSON.stringify(this.$store.state.users.currentUser)) clone.background_image = data.url this.$store.commit('addNewUsers', [clone]) this.$store.commit('setCurrentUser', clone) this.backgroundPreview = null } else { this.backgroundUploadError = this.$t('upload.error.base') + data.error } this.backgroundUploading = false }) }, importFollows () { this.followListUploading = true const followList = this.followList this.$store.state.api.backendInteractor.followImport({params: followList}) .then((status) => { if (status) { this.followsImported = true } else { this.followImportError = true } this.followListUploading = false }) }, /* This function takes an Array of Users * and outputs a file with all the addresses for the user to download */ exportPeople (users, filename) { // Get all the friends addresses var UserAddresses = users.map(function (user) { // check is it's a local user if (user && user.is_local) { // append the instance address // eslint-disable-next-line no-undef user.screen_name += '@' + location.hostname } return user.screen_name }).join('\n') // Make the user download the file var fileToDownload = document.createElement('a') fileToDownload.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(UserAddresses)) fileToDownload.setAttribute('download', filename) fileToDownload.style.display = 'none' document.body.appendChild(fileToDownload) fileToDownload.click() document.body.removeChild(fileToDownload) }, exportFollows () { this.enableFollowsExport = false this.$store.state.api.backendInteractor .exportFriends({ id: this.$store.state.users.currentUser.id }) .then((friendList) => { this.exportPeople(friendList, 'friends.csv') setTimeout(() => { this.enableFollowsExport = true }, 2000) }) }, followListChange () { // eslint-disable-next-line no-undef let formData = new FormData() formData.append('list', this.$refs.followlist.files[0]) this.followList = formData }, dismissImported () { this.followsImported = false this.followImportError = false }, 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 } }) }, 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) } } } } export default UserSettings