diff options
| -rw-r--r-- | CHANGELOG.md | 3 | ||||
| -rw-r--r-- | src/components/extra_buttons/extra_buttons.js | 6 | ||||
| -rw-r--r-- | src/components/extra_buttons/extra_buttons.vue | 29 | ||||
| -rw-r--r-- | src/components/global_notice_list/global_notice_list.vue | 25 | ||||
| -rw-r--r-- | src/components/image_cropper/image_cropper.js | 15 | ||||
| -rw-r--r-- | src/components/image_cropper/image_cropper.vue | 11 | ||||
| -rw-r--r-- | src/components/popover/popover.vue | 1 | ||||
| -rw-r--r-- | src/components/settings_modal/tabs/profile_tab.js | 48 | ||||
| -rw-r--r-- | src/components/settings_modal/tabs/profile_tab.vue | 23 | ||||
| -rw-r--r-- | src/components/status/status.js | 2 | ||||
| -rw-r--r-- | src/components/status/status.scss | 14 | ||||
| -rw-r--r-- | src/components/status/status.vue | 23 | ||||
| -rw-r--r-- | src/components/timeline/timeline.vue | 1 | ||||
| -rw-r--r-- | src/components/timeline_menu/timeline_menu.js | 8 | ||||
| -rw-r--r-- | src/components/timeline_menu/timeline_menu.vue | 22 | ||||
| -rw-r--r-- | src/i18n/en.json | 5 | ||||
| -rw-r--r-- | src/services/api/api.service.js | 7 |
17 files changed, 131 insertions, 112 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 79dd0773..83bcee6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fixed custom emoji not working in profile field names - Fixed pinned statuses not appearing in user profiles - Fixed some elements not being keyboard navigation friendly +- Fixed error handling when updating various profile images - Fixed your latest chat messages disappearing when closing chat view and opening it again during the same session - Fixed custom emoji not showing in poll options before voting - Fixed link color not applied to instance name in topbar @@ -26,6 +27,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - Errors when fetching are now shown with popup errors instead of "Error fetching updates" in panel headers - Made reply/fav/repeat etc buttons easier to hit +- Adjusted timeline menu clickable area to match the visible button +- Moved external source link from status heading to the ellipsis menu - Disabled horizontal textarea resize - Wallpaper is now top-aligned, horizontally centered. diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js index 1a8eef72..b5b29e8a 100644 --- a/src/components/extra_buttons/extra_buttons.js +++ b/src/components/extra_buttons/extra_buttons.js @@ -5,7 +5,8 @@ import { faBookmark, faEyeSlash, faThumbtack, - faShareAlt + faShareAlt, + faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons' import { faBookmark as faBookmarkReg @@ -17,7 +18,8 @@ library.add( faBookmarkReg, faEyeSlash, faThumbtack, - faShareAlt + faShareAlt, + faExternalLinkAlt ) const ExtraButtons = { diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue index e687d487..dc790cad 100644 --- a/src/components/extra_buttons/extra_buttons.vue +++ b/src/components/extra_buttons/extra_buttons.vue @@ -1,5 +1,6 @@ <template> <Popover + class="ExtraButtons" trigger="click" placement="top" :offset="{ y: 5 }" @@ -96,11 +97,23 @@ icon="share-alt" /><span>{{ $t("status.copy_link") }}</span> </button> + <a + v-if="!status.is_local" + class="button-default dropdown-item dropdown-item-icon" + title="Source" + :href="status.external_url" + target="_blank" + > + <FAIcon + fixed-width + icon="external-link-alt" + /><span>{{ $t("status.external_source") }}</span> + </a> </div> </div> <span slot="trigger" - class="ExtraButtons" + class="popover-trigger" > <FAIcon class="fa-scale-110 fa-old-padding" @@ -116,13 +129,15 @@ @import '../../_variables.scss'; .ExtraButtons { - position: static; - padding: 10px; - margin: -10px; + .popover-trigger { + position: static; + padding: 10px; + margin: -10px; - &:hover .svg-inline--fa { - color: $fallback--text; - color: var(--text, $fallback--text); + &:hover .svg-inline--fa { + color: $fallback--text; + color: var(--text, $fallback--text); + } } } </style> diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue index 8a33b9eb..049e23db 100644 --- a/src/components/global_notice_list/global_notice_list.vue +++ b/src/components/global_notice_list/global_notice_list.vue @@ -9,11 +9,15 @@ <div class="notice-message"> {{ $t(notice.messageKey, notice.messageArgs) }} </div> - <FAIcon - class="fa-scale-110 fa-old-padding" - icon="times" + <button + class="button-unstyled close-notice" @click="closeNotice(notice)" - /> + > + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="times" + /> + </button> </div> </div> </template> @@ -54,7 +58,7 @@ .global-error { background-color: var(--alertPopupError, $fallback--cRed); color: var(--alertPopupErrorText, $fallback--text); - i { + .svg-inline--fa { color: var(--alertPopupErrorText, $fallback--text); } } @@ -62,7 +66,7 @@ .global-warning { background-color: var(--alertPopupWarning, $fallback--cOrange); color: var(--alertPopupWarningText, $fallback--text); - i { + .svg-inline--fa { color: var(--alertPopupWarningText, $fallback--text); } } @@ -70,9 +74,16 @@ .global-info { background-color: var(--alertPopupNeutral, $fallback--fg); color: var(--alertPopupNeutralText, $fallback--text); - i { + .svg-inline--fa { color: var(--alertPopupNeutralText, $fallback--text); } } + + .close-notice { + padding-right: 0.2em; + .svg-inline--fa:hover { + opacity: 0.6; + } + } } </style> diff --git a/src/components/image_cropper/image_cropper.js b/src/components/image_cropper/image_cropper.js index 59e4d07e..e8d5ec6d 100644 --- a/src/components/image_cropper/image_cropper.js +++ b/src/components/image_cropper/image_cropper.js @@ -2,12 +2,10 @@ import Cropper from 'cropperjs' import 'cropperjs/dist/cropper.css' import { library } from '@fortawesome/fontawesome-svg-core' import { - faTimes, faCircleNotch } from '@fortawesome/free-solid-svg-icons' library.add( - faTimes, faCircleNotch ) @@ -53,8 +51,7 @@ const ImageCropper = { cropper: undefined, dataUrl: undefined, filename: undefined, - submitting: false, - submitError: null + submitting: false } }, computed: { @@ -66,9 +63,6 @@ const ImageCropper = { }, cancelText () { return this.cancelButtonLabel || this.$t('image_cropper.cancel') - }, - submitErrorMsg () { - return this.submitError && this.submitError instanceof Error ? this.submitError.toString() : this.submitError } }, methods: { @@ -82,12 +76,8 @@ const ImageCropper = { }, submit (cropping = true) { this.submitting = true - this.avatarUploadError = null this.submitHandler(cropping && this.cropper, this.file) .then(() => this.destroy()) - .catch((err) => { - this.submitError = err - }) .finally(() => { this.submitting = false }) @@ -113,9 +103,6 @@ const ImageCropper = { reader.readAsDataURL(this.file) this.$emit('changed', this.file, reader) } - }, - clearError () { - this.submitError = null } }, mounted () { diff --git a/src/components/image_cropper/image_cropper.vue b/src/components/image_cropper/image_cropper.vue index 448461b4..8c48a387 100644 --- a/src/components/image_cropper/image_cropper.vue +++ b/src/components/image_cropper/image_cropper.vue @@ -37,17 +37,6 @@ icon="circle-notch" /> </div> - <div - v-if="submitError" - class="alert error" - > - {{ submitErrorMsg }} - <FAIcon - class="fa-scale-110 fa-old-padding" - icon="times" - @click="clearError" - /> - </div> </div> <input ref="input" diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue index 020eab05..2252c68f 100644 --- a/src/components/popover/popover.vue +++ b/src/components/popover/popover.vue @@ -95,6 +95,7 @@ box-shadow: none; width: 100%; height: 100%; + box-sizing: border-box; --btnText: var(--popoverText, $fallback--text); diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js index a4fed629..9709424c 100644 --- a/src/components/settings_modal/tabs/profile_tab.js +++ b/src/components/settings_modal/tabs/profile_tab.js @@ -45,9 +45,7 @@ const ProfileTab = { banner: null, bannerPreview: null, background: null, - backgroundPreview: null, - bannerUploadError: null, - backgroundUploadError: null + backgroundPreview: null } }, components: { @@ -162,18 +160,18 @@ const ProfileTab = { 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', - { + this.$store.dispatch('pushGlobalNotice', { + messageKey: 'upload.error.message', + messageArgs: [ + this.$t('upload.error.file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit - } - ) - ].join(' ') + }) + ], + level: 'error' + }) return } // eslint-disable-next-line no-undef @@ -213,8 +211,9 @@ const ProfileTab = { that.$store.commit('setCurrentUser', user) resolve() }) - .catch((err) => { - reject(new Error(that.$t('upload.error.base') + ' ' + err.message)) + .catch((error) => { + that.displayUploadError(error) + reject(error) }) } @@ -235,24 +234,27 @@ const ProfileTab = { this.$store.commit('setCurrentUser', user) this.bannerPreview = null }) - .catch((err) => { - this.bannerUploadError = this.$t('upload.error.base') + ' ' + err.message - }) - .then(() => { this.bannerUploading = false }) + .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) => { - if (!data.error) { + this.$store.state.api.backendInteractor.updateProfileImages({ background }) + .then((data) => { 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 + }) + .catch(this.displayUploadError) + .finally(() => { this.backgroundUploading = false }) + }, + displayUploadError (error) { + this.$store.dispatch('pushGlobalNotice', { + messageKey: 'upload.error.message', + messageArgs: [error.message], + level: 'error' }) } } diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue index 66275fcc..b7ef21d7 100644 --- a/src/components/settings_modal/tabs/profile_tab.vue +++ b/src/components/settings_modal/tabs/profile_tab.vue @@ -229,17 +229,6 @@ > {{ $t('general.submit') }} </button> - <div - v-if="bannerUploadError" - class="alert error" - > - Error: {{ bannerUploadError }} - <FAIcon - class="fa-scale-110 fa-old-padding" - icon="times" - @click="clearUploadError('banner')" - /> - </div> </div> <div class="setting-item"> <h2>{{ $t('settings.profile_background') }}</h2> @@ -279,18 +268,6 @@ > {{ $t('general.submit') }} </button> - <div - v-if="backgroundUploadError" - class="alert error" - > - Error: {{ backgroundUploadError }} - <FAIcon - size="lg" - class="fa-scale-110 fa-old-padding" - icon="times" - @click="clearUploadError('background')" - /> - </div> </div> </div> </template> diff --git a/src/components/status/status.js b/src/components/status/status.js index 142e1fc6..f9c710ab 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -26,7 +26,6 @@ import { faTimes, faRetweet, faReply, - faExternalLinkSquareAlt, faPlusSquare, faSmileBeam, faEllipsisH, @@ -44,7 +43,6 @@ library.add( faTimes, faRetweet, faReply, - faExternalLinkSquareAlt, faPlusSquare, faStar, faSmileBeam, diff --git a/src/components/status/status.scss b/src/components/status/status.scss index 70c6d03d..58b55bc8 100644 --- a/src/components/status/status.scss +++ b/src/components/status/status.scss @@ -139,6 +139,20 @@ $status-margin: 0.75em; .heading-right { display: flex; flex-shrink: 0; + + .button-unstyled { + padding: 5px; + margin: -5px; + + &:hover svg { + color: $fallback--lightText; + color: var(--lightText, $fallback--lightText); + } + } + + .svg-inline--fa { + margin-left: 0.25em; + } } .timeago { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 896635ee..6ee8117f 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -184,30 +184,20 @@ :title="status.visibility | capitalize" > <FAIcon - class="fa-scale-110 fa-old-padding" + fixed-width + class="fa-scale-110" :icon="visibilityIcon(status.visibility)" /> </span> - <a - v-if="!status.is_local && !isPreview" - :href="status.external_url" - target="_blank" - class="source_url" - title="Source" - > - <FAIcon - class="fa-scale-110 fa-old-padding" - icon="external-link-square-alt" - /> - </a> <button v-if="expandable && !isPreview" class="button-unstyled" - title="Expand" + :title="$t('status.expand')" @click.prevent="toggleExpanded" > <FAIcon - class="fa-scale-110 fa-old-padding" + fixed-width + class="fa-scale-110" icon="plus-square" /> </button> @@ -217,8 +207,9 @@ @click.prevent="toggleMute" > <FAIcon + fixed-width icon="eye-slash" - class="fa-scale-110 fa-old-padding" + class="fa-scale-110" /> </button> </span> diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index 0326342b..4c43fe5c 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -102,6 +102,7 @@ .timeline-heading { max-width: 100%; flex-wrap: nowrap; + align-items: center; .loadmore-button { flex-shrink: 0; } diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js index ef8a5813..8d6a58b1 100644 --- a/src/components/timeline_menu/timeline_menu.js +++ b/src/components/timeline_menu/timeline_menu.js @@ -59,6 +59,14 @@ const TimelineMenu = { this.isOpen = true }, 25) }, + blockOpen (event) { + // For the blank area inside the button element. + // Just setting @click.stop="" makes unintuitive behavior when + // menu is open and clicking on the blank area doesn't close it. + if (!this.isOpen) { + event.stopPropagation() + } + }, timelineName () { const route = this.$route.name if (route === 'tag-timeline') { diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue index c46531be..3c86842b 100644 --- a/src/components/timeline_menu/timeline_menu.vue +++ b/src/components/timeline_menu/timeline_menu.vue @@ -65,10 +65,16 @@ slot="trigger" class="title timeline-menu-title" > - <span>{{ timelineName() }}</span> - <FAIcon - size="sm" - icon="chevron-down" + <span class="timeline-title">{{ timelineName() }}</span> + <span> + <FAIcon + size="sm" + icon="chevron-down" + /> + </span> + <span + class="click-blocker" + @click="blockOpen" /> </div> </Popover> @@ -117,8 +123,9 @@ cursor: pointer; user-select: none; width: 100%; + display: flex; - span { + .timeline-menu-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -128,6 +135,11 @@ margin-left: 0.6em; transition: transform 100ms; } + + .click-blocker { + cursor: default; + flex-grow: 1; + } } &.open .timeline-menu-title svg { diff --git a/src/i18n/en.json b/src/i18n/en.json index 815c0f2a..26dd6144 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -664,6 +664,7 @@ "unmute_conversation": "Unmute conversation", "status_unavailable": "Status unavailable", "copy_link": "Copy link to status", + "external_source": "External source", "thread_muted": "Thread muted", "thread_muted_and_words": ", has words:", "show_full_subject": "Show full subject", @@ -671,7 +672,8 @@ "show_content": "Show content", "hide_content": "Hide content", "status_deleted": "This post was deleted", - "nsfw": "NSFW" + "nsfw": "NSFW", + "expand": "Expand" }, "user_card": { "approve": "Approve", @@ -761,6 +763,7 @@ "upload": { "error": { "base": "Upload failed.", + "message": "Upload failed: {0}", "file_too_big": "File too big [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", "default": "Try again later" }, diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 8da933c4..f4483149 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -162,7 +162,12 @@ const updateProfileImages = ({ credentials, avatar = null, banner = null, backgr body: form }) .then((data) => data.json()) - .then((data) => parseUser(data)) + .then((data) => { + if (data.error) { + throw new Error(data.error) + } + return parseUser(data) + }) } const updateProfile = ({ credentials, params }) => { |
