avatar upload works
This commit is contained in:
parent
7d985bd475
commit
b305748a92
10 changed files with 239 additions and 207 deletions
|
|
@ -782,12 +782,6 @@ option {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.visibility-notice {
|
|
||||||
padding: 0.5em;
|
|
||||||
border: 1px solid var(--textFaint);
|
|
||||||
border-radius: var(--roundness);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-dismissible {
|
.notice-dismissible {
|
||||||
padding-right: 4rem;
|
padding-right: 4rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,6 @@ library.add(
|
||||||
|
|
||||||
const ImageCropper = {
|
const ImageCropper = {
|
||||||
props: {
|
props: {
|
||||||
trigger: {
|
|
||||||
type: [String, window.Element],
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
submitHandler: {
|
|
||||||
type: Function,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
mimes: {
|
mimes: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'
|
default: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'
|
||||||
|
|
@ -39,17 +31,7 @@ const ImageCropper = {
|
||||||
submitting: false
|
submitting: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
emits: ['submit'],
|
||||||
saveText () {
|
|
||||||
return this.saveButtonLabel || this.$t('image_cropper.save')
|
|
||||||
},
|
|
||||||
saveWithoutCroppingText () {
|
|
||||||
return this.saveWithoutCroppingButtonlabel || this.$t('image_cropper.save_without_cropping')
|
|
||||||
},
|
|
||||||
cancelText () {
|
|
||||||
return this.cancelButtonLabel || this.$t('image_cropper.cancel')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
destroy () {
|
destroy () {
|
||||||
this.$refs.input.value = ''
|
this.$refs.input.value = ''
|
||||||
|
|
@ -65,20 +47,15 @@ const ImageCropper = {
|
||||||
} else {
|
} else {
|
||||||
cropperPromise = Promise.resolve()
|
cropperPromise = Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
cropperPromise.then(canvas => {
|
cropperPromise.then(canvas => {
|
||||||
this.submitHandler(canvas, this.file)
|
this.$emit('submit', { canvas, file: this.file })
|
||||||
.then(() => this.destroy())
|
|
||||||
.finally(() => {
|
|
||||||
this.submitting = false
|
this.submitting = false
|
||||||
})
|
})
|
||||||
})
|
|
||||||
},
|
},
|
||||||
pickImage () {
|
pickImage () {
|
||||||
this.$refs.input.click()
|
this.$refs.input.click()
|
||||||
},
|
},
|
||||||
getTriggerDOM () {
|
|
||||||
return typeof this.trigger === 'object' ? this.trigger : document.querySelector(this.trigger)
|
|
||||||
},
|
|
||||||
readFile () {
|
readFile () {
|
||||||
const fileInput = this.$refs.input
|
const fileInput = this.$refs.input
|
||||||
if (fileInput.files != null && fileInput.files[0] != null) {
|
if (fileInput.files != null && fileInput.files[0] != null) {
|
||||||
|
|
@ -117,23 +94,11 @@ const ImageCropper = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
// listen for click event on trigger
|
|
||||||
const trigger = this.getTriggerDOM()
|
|
||||||
if (!trigger) {
|
|
||||||
this.$emit('error', 'No image make trigger found.', 'user')
|
|
||||||
} else {
|
|
||||||
trigger.addEventListener('click', this.pickImage)
|
|
||||||
}
|
|
||||||
// listen for input file changes
|
// listen for input file changes
|
||||||
const fileInput = this.$refs.input
|
const fileInput = this.$refs.input
|
||||||
fileInput.addEventListener('change', this.readFile)
|
fileInput.addEventListener('change', this.readFile)
|
||||||
},
|
},
|
||||||
beforeUnmount: function () {
|
beforeUnmount: function () {
|
||||||
// remove the event listeners
|
|
||||||
const trigger = this.getTriggerDOM()
|
|
||||||
if (trigger) {
|
|
||||||
trigger.removeEventListener('click', this.pickImage)
|
|
||||||
}
|
|
||||||
const fileInput = this.$refs.input
|
const fileInput = this.$refs.input
|
||||||
fileInput.removeEventListener('change', this.readFile)
|
fileInput.removeEventListener('change', this.readFile)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="image-cropper">
|
<div class="image-cropper">
|
||||||
<div v-if="dataUrl">
|
<div class="image">
|
||||||
<cropper-canvas
|
<cropper-canvas
|
||||||
ref="cropperCanvas"
|
ref="cropperCanvas"
|
||||||
background
|
background
|
||||||
class="image-cropper-canvas"
|
class="image-cropper-canvas"
|
||||||
height="25em"
|
height="100%"
|
||||||
>
|
>
|
||||||
<cropper-image
|
<cropper-image
|
||||||
|
v-if="dataUrl"
|
||||||
ref="cropperImage"
|
ref="cropperImage"
|
||||||
:src="dataUrl"
|
:src="dataUrl"
|
||||||
alt="Picture"
|
alt="Picture"
|
||||||
|
|
@ -47,41 +48,13 @@
|
||||||
<cropper-handle action="sw-resize" />
|
<cropper-handle action="sw-resize" />
|
||||||
</cropper-selection>
|
</cropper-selection>
|
||||||
</cropper-canvas>
|
</cropper-canvas>
|
||||||
<div class="image-cropper-buttons-wrapper">
|
|
||||||
<button
|
|
||||||
class="button-default btn"
|
|
||||||
type="button"
|
|
||||||
:disabled="submitting"
|
|
||||||
@click="submit()"
|
|
||||||
v-text="saveText"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
class="button-default btn"
|
|
||||||
type="button"
|
|
||||||
:disabled="submitting"
|
|
||||||
@click="destroy"
|
|
||||||
v-text="cancelText"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
class="button-default btn"
|
|
||||||
type="button"
|
|
||||||
:disabled="submitting"
|
|
||||||
@click="submit(false)"
|
|
||||||
v-text="saveWithoutCroppingText"
|
|
||||||
/>
|
|
||||||
<FAIcon
|
|
||||||
v-if="submitting"
|
|
||||||
spin
|
|
||||||
icon="circle-notch"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
ref="input"
|
ref="input"
|
||||||
type="file"
|
type="file"
|
||||||
class="input image-cropper-img-input"
|
class="input image-cropper-img-input"
|
||||||
:accept="mimes"
|
:accept="mimes"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -89,13 +62,15 @@
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.image-cropper {
|
.image-cropper {
|
||||||
&-img-input {
|
display: flex;
|
||||||
display: none;
|
flex-direction: column;
|
||||||
|
|
||||||
|
&-canvas, .image {
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-canvas {
|
& &-img-input {
|
||||||
height: 25em;
|
display: none;
|
||||||
width: 25em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-buttons-wrapper {
|
&-buttons-wrapper {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ const ProfileTab = {
|
||||||
role: this.$store.state.users.currentUser.role,
|
role: this.$store.state.users.currentUser.role,
|
||||||
bot: this.$store.state.users.currentUser.bot,
|
bot: this.$store.state.users.currentUser.bot,
|
||||||
actorType: this.$store.state.users.currentUser.actor_type,
|
actorType: this.$store.state.users.currentUser.actor_type,
|
||||||
pickAvatarBtnVisible: true,
|
|
||||||
bannerUploading: false,
|
bannerUploading: false,
|
||||||
backgroundUploading: false,
|
backgroundUploading: false,
|
||||||
banner: null,
|
banner: null,
|
||||||
|
|
@ -88,29 +87,6 @@ const ProfileTab = {
|
||||||
userSuggestor () {
|
userSuggestor () {
|
||||||
return suggestor({ store: this.$store })
|
return suggestor({ store: this.$store })
|
||||||
},
|
},
|
||||||
defaultAvatar () {
|
|
||||||
return this.$store.state.instance.server + this.$store.state.instance.defaultAvatar
|
|
||||||
},
|
|
||||||
defaultBanner () {
|
|
||||||
return this.$store.state.instance.server + this.$store.state.instance.defaultBanner
|
|
||||||
},
|
|
||||||
isDefaultAvatar () {
|
|
||||||
const baseAvatar = this.$store.state.instance.defaultAvatar
|
|
||||||
return !(this.$store.state.users.currentUser.profile_image_url) ||
|
|
||||||
this.$store.state.users.currentUser.profile_image_url.includes(baseAvatar)
|
|
||||||
},
|
|
||||||
isDefaultBanner () {
|
|
||||||
const baseBanner = this.$store.state.instance.defaultBanner
|
|
||||||
return !(this.$store.state.users.currentUser.cover_photo) ||
|
|
||||||
this.$store.state.users.currentUser.cover_photo.includes(baseBanner)
|
|
||||||
},
|
|
||||||
isDefaultBackground () {
|
|
||||||
return !(this.$store.state.users.currentUser.background_image)
|
|
||||||
},
|
|
||||||
avatarImgSrc () {
|
|
||||||
const src = this.$store.state.users.currentUser.profile_image_url_original
|
|
||||||
return (!src) ? this.defaultAvatar : src
|
|
||||||
},
|
|
||||||
bannerImgSrc () {
|
bannerImgSrc () {
|
||||||
const src = this.$store.state.users.currentUser.cover_photo
|
const src = this.$store.state.users.currentUser.cover_photo
|
||||||
return (!src) ? this.defaultBanner : src
|
return (!src) ? this.defaultBanner : src
|
||||||
|
|
@ -153,16 +129,6 @@ const ProfileTab = {
|
||||||
changeVis (visibility) {
|
changeVis (visibility) {
|
||||||
this.newDefaultScope = visibility
|
this.newDefaultScope = visibility
|
||||||
},
|
},
|
||||||
addField () {
|
|
||||||
if (this.newFields.length < this.maxFields) {
|
|
||||||
this.newFields.push({ name: '', value: '' })
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
deleteField (index) {
|
|
||||||
this.newFields.splice(index, 1)
|
|
||||||
},
|
|
||||||
uploadFile (slot, e) {
|
uploadFile (slot, e) {
|
||||||
const file = e.target.files[0]
|
const file = e.target.files[0]
|
||||||
if (!file) { return }
|
if (!file) { return }
|
||||||
|
|
@ -192,73 +158,6 @@ const ProfileTab = {
|
||||||
}
|
}
|
||||||
reader.readAsDataURL(file)
|
reader.readAsDataURL(file)
|
||||||
},
|
},
|
||||||
resetAvatar () {
|
|
||||||
const confirmed = window.confirm(this.$t('settings.reset_avatar_confirm'))
|
|
||||||
if (confirmed) {
|
|
||||||
this.submitAvatar(undefined, '')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resetBanner () {
|
|
||||||
const confirmed = window.confirm(this.$t('settings.reset_banner_confirm'))
|
|
||||||
if (confirmed) {
|
|
||||||
this.submitBanner('')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resetBackground () {
|
|
||||||
const confirmed = window.confirm(this.$t('settings.reset_background_confirm'))
|
|
||||||
if (confirmed) {
|
|
||||||
this.submitBackground('')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
submitAvatar (canvas, file) {
|
|
||||||
const that = this
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
function updateAvatar (avatar, avatarName) {
|
|
||||||
that.$store.state.api.backendInteractor.updateProfileImages({ avatar, avatarName })
|
|
||||||
.then((user) => {
|
|
||||||
that.$store.commit('addNewUsers', [user])
|
|
||||||
that.$store.commit('setCurrentUser', user)
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
that.displayUploadError(error)
|
|
||||||
reject(error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canvas) {
|
|
||||||
canvas.toBlob((data) => updateAvatar(data, file.name), file.type)
|
|
||||||
} else {
|
|
||||||
updateAvatar(file, file.name)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
submitBanner (banner) {
|
|
||||||
if (!this.bannerPreview && banner !== '') { return }
|
|
||||||
|
|
||||||
this.bannerUploading = true
|
|
||||||
this.$store.state.api.backendInteractor.updateProfileImages({ banner })
|
|
||||||
.then((user) => {
|
|
||||||
this.$store.commit('addNewUsers', [user])
|
|
||||||
this.$store.commit('setCurrentUser', user)
|
|
||||||
this.bannerPreview = null
|
|
||||||
})
|
|
||||||
.catch(this.displayUploadError)
|
|
||||||
.finally(() => { this.bannerUploading = false })
|
|
||||||
},
|
|
||||||
submitBackground (background) {
|
|
||||||
if (!this.backgroundPreview && background !== '') { return }
|
|
||||||
|
|
||||||
this.backgroundUploading = true
|
|
||||||
this.$store.state.api.backendInteractor.updateProfileImages({ background })
|
|
||||||
.then((data) => {
|
|
||||||
this.$store.commit('addNewUsers', [data])
|
|
||||||
this.$store.commit('setCurrentUser', data)
|
|
||||||
this.backgroundPreview = null
|
|
||||||
})
|
|
||||||
.catch(this.displayUploadError)
|
|
||||||
.finally(() => { this.backgroundUploading = false })
|
|
||||||
},
|
|
||||||
displayUploadError (error) {
|
displayUploadError (error) {
|
||||||
useInterfaceStore().pushGlobalNotice({
|
useInterfaceStore().pushGlobalNotice({
|
||||||
messageKey: 'upload.error.message',
|
messageKey: 'upload.error.message',
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,6 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.visibility-tray {
|
|
||||||
padding-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="file"] {
|
input[type="file"] {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
:switcher="false"
|
:switcher="false"
|
||||||
rounded="top"
|
rounded="top"
|
||||||
/>
|
/>
|
||||||
|
<p>{{ $t('settings.name') }}</p>
|
||||||
<p v-if="role === 'admin' || role === 'moderator'">
|
<p v-if="role === 'admin' || role === 'moderator'">
|
||||||
<Checkbox v-model="showRole">
|
<Checkbox v-model="showRole">
|
||||||
<template v-if="role === 'admin'">
|
<template v-if="role === 'admin'">
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||||
import UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue'
|
import UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue'
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import EmojiInput from 'src/components/emoji_input/emoji_input.vue'
|
import EmojiInput from 'src/components/emoji_input/emoji_input.vue'
|
||||||
|
import Modal from 'src/components/modal/modal.vue'
|
||||||
|
import ImageCropper from 'src/components/image_cropper/image_cropper.vue'
|
||||||
|
|
||||||
import localeService from 'src/services/locale/locale.service.js'
|
import localeService from 'src/services/locale/locale.service.js'
|
||||||
import suggestor from 'src/components/emoji_input/suggestor.js'
|
import suggestor from 'src/components/emoji_input/suggestor.js'
|
||||||
|
|
@ -34,7 +36,8 @@ import {
|
||||||
faTimes,
|
faTimes,
|
||||||
faExpandAlt,
|
faExpandAlt,
|
||||||
faBirthdayCake,
|
faBirthdayCake,
|
||||||
faSave
|
faSave,
|
||||||
|
faChevronRight
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
import { useMediaViewerStore } from '../../stores/media_viewer'
|
import { useMediaViewerStore } from '../../stores/media_viewer'
|
||||||
|
|
@ -49,7 +52,8 @@ library.add(
|
||||||
faEdit,
|
faEdit,
|
||||||
faTimes,
|
faTimes,
|
||||||
faExpandAlt,
|
faExpandAlt,
|
||||||
faBirthdayCake
|
faBirthdayCake,
|
||||||
|
faChevronRight
|
||||||
)
|
)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -66,6 +70,7 @@ export default {
|
||||||
'hasNoteEditor'
|
'hasNoteEditor'
|
||||||
],
|
],
|
||||||
components: {
|
components: {
|
||||||
|
Modal,
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
RemoteFollow,
|
RemoteFollow,
|
||||||
|
|
@ -79,7 +84,8 @@ export default {
|
||||||
UserNote,
|
UserNote,
|
||||||
UserTimedFilterModal,
|
UserTimedFilterModal,
|
||||||
ColorInput,
|
ColorInput,
|
||||||
EmojiInput
|
EmojiInput,
|
||||||
|
ImageCropper
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
const user = this.$store.state.users.currentUser
|
const user = this.$store.state.users.currentUser
|
||||||
|
|
@ -93,6 +99,7 @@ export default {
|
||||||
newName: user.name_unescaped,
|
newName: user.name_unescaped,
|
||||||
editingName: false,
|
editingName: false,
|
||||||
newActorType: user.actor_type,
|
newActorType: user.actor_type,
|
||||||
|
editImage: false,
|
||||||
newBio: unescape(user.description),
|
newBio: unescape(user.description),
|
||||||
editingBio: false,
|
editingBio: false,
|
||||||
newBirthday: user.birthday,
|
newBirthday: user.birthday,
|
||||||
|
|
@ -223,6 +230,29 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Editable stuff
|
// Editable stuff
|
||||||
|
defaultAvatar () {
|
||||||
|
return this.$store.state.instance.server + this.$store.state.instance.defaultAvatar
|
||||||
|
},
|
||||||
|
defaultBanner () {
|
||||||
|
return this.$store.state.instance.server + this.$store.state.instance.defaultBanner
|
||||||
|
},
|
||||||
|
isDefaultAvatar () {
|
||||||
|
const baseAvatar = this.$store.state.instance.defaultAvatar
|
||||||
|
return !(this.$store.state.users.currentUser.profile_image_url) ||
|
||||||
|
this.$store.state.users.currentUser.profile_image_url.includes(baseAvatar)
|
||||||
|
},
|
||||||
|
isDefaultBanner () {
|
||||||
|
const baseBanner = this.$store.state.instance.defaultBanner
|
||||||
|
return !(this.$store.state.users.currentUser.cover_photo) ||
|
||||||
|
this.$store.state.users.currentUser.cover_photo.includes(baseBanner)
|
||||||
|
},
|
||||||
|
isDefaultBackground () {
|
||||||
|
return !(this.$store.state.users.currentUser.background_image)
|
||||||
|
},
|
||||||
|
avatarImgSrc () {
|
||||||
|
const src = this.$store.state.users.currentUser.profile_image_url_original
|
||||||
|
return (!src) ? this.defaultAvatar : src
|
||||||
|
},
|
||||||
fieldsLimits () {
|
fieldsLimits () {
|
||||||
return this.$store.state.instance.fieldsLimits
|
return this.$store.state.instance.fieldsLimits
|
||||||
},
|
},
|
||||||
|
|
@ -303,6 +333,52 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Editable stuff
|
// Editable stuff
|
||||||
|
changeAvatar () {
|
||||||
|
this.editImage = 'avatar'
|
||||||
|
},
|
||||||
|
changeBanner () {
|
||||||
|
this.editImage = 'banner'
|
||||||
|
},
|
||||||
|
resetAvatar () {
|
||||||
|
const confirmed = window.confirm(this.$t('settings.reset_avatar_confirm'))
|
||||||
|
if (confirmed) {
|
||||||
|
this.submitAvatar(undefined, '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetBanner () {
|
||||||
|
const confirmed = window.confirm(this.$t('settings.reset_banner_confirm'))
|
||||||
|
if (confirmed) {
|
||||||
|
this.submitBanner('')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetBackground () {
|
||||||
|
const confirmed = window.confirm(this.$t('settings.reset_background_confirm'))
|
||||||
|
if (confirmed) {
|
||||||
|
this.submitBackground('')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submitImage ({ canvas, file }) {
|
||||||
|
const reqData = {}
|
||||||
|
if (this.editImage === 'avatar') {
|
||||||
|
if (canvas) {
|
||||||
|
return canvas.toBlob((data) => this.submitImage({ canvas: null, file: data }))
|
||||||
|
}
|
||||||
|
reqData.avatar = file
|
||||||
|
reqData.avatarName = file.name
|
||||||
|
} else {
|
||||||
|
reqData.banner = file
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.$store.state.api.backendInteractor.updateProfileImages(reqData)
|
||||||
|
.then((user) => {
|
||||||
|
this.$store.commit('addNewUsers', [user])
|
||||||
|
this.$store.commit('setCurrentUser', user)
|
||||||
|
this.editImage = false
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.displayUploadError(error)
|
||||||
|
})
|
||||||
|
},
|
||||||
addField () {
|
addField () {
|
||||||
if (this.newFields.length < this.maxFields) {
|
if (this.newFields.length < this.maxFields) {
|
||||||
this.newFields.push({ name: '', value: '' })
|
this.newFields.push({ name: '', value: '' })
|
||||||
|
|
@ -316,6 +392,9 @@ export default {
|
||||||
propsToNative (props) {
|
propsToNative (props) {
|
||||||
return propsToNative(props)
|
return propsToNative(props)
|
||||||
},
|
},
|
||||||
|
cancelImageText () {
|
||||||
|
return
|
||||||
|
},
|
||||||
updateProfile () {
|
updateProfile () {
|
||||||
const params = {
|
const params = {
|
||||||
note: this.newBio,
|
note: this.newBio,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,22 @@
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-bio {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--lightText);
|
||||||
|
display: block;
|
||||||
|
line-height: 1.3;
|
||||||
|
padding: 0.6em;
|
||||||
|
margin: 0 0.6em;
|
||||||
|
|
||||||
|
img {
|
||||||
|
object-fit: contain;
|
||||||
|
vertical-align: middle;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.user-card-bio {
|
.user-card-bio {
|
||||||
margin: 0.6em;
|
margin: 0.6em;
|
||||||
|
|
||||||
|
|
@ -101,22 +117,6 @@
|
||||||
z-index: -2;
|
z-index: -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-bio {
|
|
||||||
text-align: center;
|
|
||||||
color: var(--lightText);
|
|
||||||
display: block;
|
|
||||||
line-height: 1.3;
|
|
||||||
padding: 0.6em;
|
|
||||||
margin: 0 0.6em;
|
|
||||||
|
|
||||||
img {
|
|
||||||
object-fit: contain;
|
|
||||||
vertical-align: middle;
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 400px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.-rounded-t {
|
&.-rounded-t {
|
||||||
border-top-left-radius: var(--roundness);
|
border-top-left-radius: var(--roundness);
|
||||||
border-top-right-radius: var(--roundness);
|
border-top-right-radius: var(--roundness);
|
||||||
|
|
@ -535,3 +535,44 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-image {
|
||||||
|
.panel-body {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-avatar {
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.images-container {
|
||||||
|
display: grid;
|
||||||
|
margin: 1em;
|
||||||
|
grid-template-columns: 1fr 5em 1fr;
|
||||||
|
grid-template-rows: 20em;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
.new-image {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cropper {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 1 0 10em;
|
||||||
|
max-height: 100%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
min-width: 1.1em;
|
||||||
|
font-size: 500%;
|
||||||
|
align-self: center;
|
||||||
|
flex: 0 1 5em;
|
||||||
|
aspect-ratio: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
v-else-if="editable"
|
v-else-if="editable"
|
||||||
class="user-info-avatar button-unstyled -link"
|
class="user-info-avatar button-unstyled -link"
|
||||||
:class="{ '-editable': editable }"
|
:class="{ '-editable': editable }"
|
||||||
@click="editAvatar"
|
@click="changeAvatar"
|
||||||
>
|
>
|
||||||
<UserAvatar :user="user" />
|
<UserAvatar :user="user" />
|
||||||
<div class="user-info-avatar -link -overlay">
|
<div class="user-info-avatar -link -overlay">
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
v-if="editable"
|
v-if="editable"
|
||||||
:disabled="newName && newName.length === 0"
|
:disabled="newName && newName.length === 0"
|
||||||
class="btn button-unstyled edit-banner-button"
|
class="btn button-unstyled edit-banner-button"
|
||||||
@click="updateProfile"
|
@click="changeBanner"
|
||||||
>
|
>
|
||||||
{{ $t('settings.change_banner') }}
|
{{ $t('settings.change_banner') }}
|
||||||
<FAIcon
|
<FAIcon
|
||||||
|
|
@ -591,6 +591,85 @@
|
||||||
:is-mute="true"
|
:is-mute="true"
|
||||||
/>
|
/>
|
||||||
</teleport>
|
</teleport>
|
||||||
|
<teleport to="#modal">
|
||||||
|
<Modal
|
||||||
|
v-if="editImage"
|
||||||
|
:is-mute="true"
|
||||||
|
class="edit-image"
|
||||||
|
@backdrop-clicked="editImage = false"
|
||||||
|
>
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h1 class="title">
|
||||||
|
{{ editImage === 'avatar' ? $t('settings.change_avatar') : $t('settings.change_banner') }}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="images-container">
|
||||||
|
<img
|
||||||
|
:src="editImage === 'avatar' ? user.profile_image_url_original : newBanner"
|
||||||
|
class="current-avatar"
|
||||||
|
/>
|
||||||
|
<FAIcon
|
||||||
|
class="separator"
|
||||||
|
icon="chevron-right"
|
||||||
|
/>
|
||||||
|
<image-cropper
|
||||||
|
ref="cropper"
|
||||||
|
class="cropper"
|
||||||
|
@submit="submitImage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
id="pick-image"
|
||||||
|
class="button-default btn"
|
||||||
|
type="button"
|
||||||
|
@click="() => this.$refs.cropper.pickImage()"
|
||||||
|
>
|
||||||
|
{{ $t('settings.upload_picture') }}
|
||||||
|
</button>
|
||||||
|
<p class="visibility-notice">
|
||||||
|
{{ $t('settings.avatar_size_instruction') }}
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
:title="editImage === 'avatar' ? $t('settings.reset_avatar') : $t('settings.reset_banner')"
|
||||||
|
class="button-unstyled reset-button"
|
||||||
|
@click="resetImage"
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<div/>
|
||||||
|
<button
|
||||||
|
class="button-default btn"
|
||||||
|
type="button"
|
||||||
|
@click="destroy"
|
||||||
|
>
|
||||||
|
{{ this.$t('image_cropper.cancel') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default btn"
|
||||||
|
type="button"
|
||||||
|
@click="this.$refs.cropper.submit(true)"
|
||||||
|
>
|
||||||
|
{{ $t('image_cropper.save') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default btn"
|
||||||
|
type="button"
|
||||||
|
@click="this.$refs.cropper.submit(false)"
|
||||||
|
>
|
||||||
|
{{ $t('image_cropper.save_without_cropping') }}
|
||||||
|
</button>
|
||||||
|
<FAIcon
|
||||||
|
v-if="submitting"
|
||||||
|
spin
|
||||||
|
icon="circle-notch"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</teleport>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -396,6 +396,7 @@
|
||||||
"security": "Security",
|
"security": "Security",
|
||||||
"toggle_edit": "Toggle edit",
|
"toggle_edit": "Toggle edit",
|
||||||
"change_banner": "Change banner",
|
"change_banner": "Change banner",
|
||||||
|
"change_avatar": "Change avatar",
|
||||||
"setting_changed": "Setting is different from default",
|
"setting_changed": "Setting is different from default",
|
||||||
"setting_server_side": "This setting is tied to your profile and affects all sessions and clients",
|
"setting_server_side": "This setting is tied to your profile and affects all sessions and clients",
|
||||||
"enter_current_password_to_confirm": "Enter your current password to confirm your identity",
|
"enter_current_password_to_confirm": "Enter your current password to confirm your identity",
|
||||||
|
|
@ -726,6 +727,7 @@
|
||||||
"set_new_profile_background": "Set new profile background",
|
"set_new_profile_background": "Set new profile background",
|
||||||
"set_new_profile_banner": "Set new profile banner",
|
"set_new_profile_banner": "Set new profile banner",
|
||||||
"reset_avatar": "Reset avatar",
|
"reset_avatar": "Reset avatar",
|
||||||
|
"reset_banner": "Reset banner",
|
||||||
"reset_profile_background": "Reset profile background",
|
"reset_profile_background": "Reset profile background",
|
||||||
"reset_profile_banner": "Reset profile banner",
|
"reset_profile_banner": "Reset profile banner",
|
||||||
"reset_avatar_confirm": "Do you really want to reset the avatar?",
|
"reset_avatar_confirm": "Do you really want to reset the avatar?",
|
||||||
|
|
@ -778,6 +780,7 @@
|
||||||
"tooltipRadius": "Tooltips/alerts",
|
"tooltipRadius": "Tooltips/alerts",
|
||||||
"type_domains_to_mute": "Search domains to mute",
|
"type_domains_to_mute": "Search domains to mute",
|
||||||
"upload_a_photo": "Upload a photo",
|
"upload_a_photo": "Upload a photo",
|
||||||
|
"upload_picture": "Upload picture",
|
||||||
"user_settings": "User Settings",
|
"user_settings": "User Settings",
|
||||||
"values": {
|
"values": {
|
||||||
"false": "no",
|
"false": "no",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue