diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/notification/notification.js | 14 | ||||
| -rw-r--r-- | src/components/notification/notification.vue | 20 | ||||
| -rw-r--r-- | src/components/notifications/notifications.js | 3 | ||||
| -rw-r--r-- | src/components/notifications/notifications.scss | 10 | ||||
| -rw-r--r-- | src/components/registration/registration.vue | 4 | ||||
| -rw-r--r-- | src/components/settings_modal/helpers/boolean_setting.js | 6 | ||||
| -rw-r--r-- | src/components/settings_modal/tabs/filtering_tab.js | 9 | ||||
| -rw-r--r-- | src/components/user_card/user_card.js | 13 | ||||
| -rw-r--r-- | src/components/user_card/user_card.scss | 4 | ||||
| -rw-r--r-- | src/components/user_card/user_card.vue | 6 | ||||
| -rw-r--r-- | src/components/user_note/user_note.js | 45 | ||||
| -rw-r--r-- | src/components/user_note/user_note.vue | 88 | ||||
| -rw-r--r-- | src/components/user_profile/user_profile.vue | 1 | ||||
| -rw-r--r-- | src/i18n/en.json | 11 | ||||
| -rw-r--r-- | src/modules/users.js | 8 | ||||
| -rw-r--r-- | src/services/api/api.service.js | 13 |
16 files changed, 232 insertions, 23 deletions
diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index ddba560e..265aaee0 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -20,7 +20,9 @@ import { faUserPlus, faEyeSlash, faUser, - faSuitcaseRolling + faSuitcaseRolling, + faExpandAlt, + faCompressAlt } from '@fortawesome/free-solid-svg-icons' library.add( @@ -31,13 +33,15 @@ library.add( faUserPlus, faUser, faEyeSlash, - faSuitcaseRolling + faSuitcaseRolling, + faExpandAlt, + faCompressAlt ) const Notification = { data () { return { - userExpanded: false, + statusExpanded: false, betterShadow: this.$store.state.interface.browserSupport.cssFilter, unmuted: false } @@ -55,8 +59,8 @@ const Notification = { UserLink }, methods: { - toggleUserExpanded () { - this.userExpanded = !this.userExpanded + toggleStatusExpanded () { + this.statusExpanded = !this.statusExpanded }, generateUserProfileLink (user) { return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 84f3f7de..f1aa5420 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -144,13 +144,25 @@ <router-link v-if="notification.status" :to="{ name: 'conversation', params: { id: notification.status.id } }" - class="faint-link" + class="timeago-link faint-link" > <Timeago :time="notification.created_at" :auto-update="240" /> </router-link> + <button + class="button-unstyled expand-icon" + @click.prevent="toggleStatusExpanded" + :title="$t('tool_tip.toggle_expand')" + :aria-expanded="statusExpanded" + > + <FAIcon + class="fa-scale-110" + fixed-width + :icon="statusExpanded ? 'compress-alt' : 'expand-alt'" + /> + </button> </div> <div v-else @@ -166,6 +178,8 @@ <button v-if="needMute" class="button-unstyled" + :title="$t('tool_tip.toggle_mute')" + :aria-expanded="!unmuted" @click.prevent="toggleMute" > <FAIcon @@ -222,8 +236,8 @@ /> <template v-else> <StatusContent - class="faint" - :compact="true" + :class="{ faint: !statusExpanded }" + :compact="!statusExpanded" :status="notification.action" /> </template> diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index dde9c93e..d499d3d6 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -101,6 +101,9 @@ const Notifications = { if (!this.scrollerRef) { this.scrollerRef = this.$refs.root.closest('.mobile-notifications') } + if (!this.scrollerRef) { + this.scrollerRef = this.$refs.root.closest('.column.main') + } this.scrollerRef.addEventListener('scroll', this.updateScrollPosition) }, unmounted () { diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index f71f9b76..9b241565 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -112,6 +112,16 @@ min-width: 3em; text-align: right; } + + .timeago-link { + margin-right: 0.2em; + } + + .expand-icon { + .svg-inline--fa { + margin-left: 0.25em; + } + } } .emoji-reaction-emoji { diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue index d78d8da9..24d9b59b 100644 --- a/src/components/registration/registration.vue +++ b/src/components/registration/registration.vue @@ -158,10 +158,10 @@ class="form-error" > <ul> - <li v-if="!v$.user.confirm.required"> + <li v-if="v$.user.confirm.required.$invalid"> <span>{{ $t('registration.validations.password_confirmation_required') }}</span> </li> - <li v-if="!v$.user.confirm.sameAsPassword"> + <li v-if="v$.user.confirm.sameAs.$invalid"> <span>{{ $t('registration.validations.password_confirmation_match') }}</span> </li> </ul> diff --git a/src/components/settings_modal/helpers/boolean_setting.js b/src/components/settings_modal/helpers/boolean_setting.js index dc832044..2e6992cb 100644 --- a/src/components/settings_modal/helpers/boolean_setting.js +++ b/src/components/settings_modal/helpers/boolean_setting.js @@ -41,7 +41,13 @@ export default { }, methods: { update (e) { + const [firstSegment, ...rest] = this.path.split('.') set(this.$parent, this.path, e) + // Updating nested properties does not trigger update on its parent. + // probably still not as reliable, but works for depth=1 at least + if (rest.length > 0) { + set(this.$parent, firstSegment, { ...get(this.$parent, firstSegment) }) + } }, reset () { set(this.$parent, this.path, this.defaultState) diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js index 73413b48..5354e5db 100644 --- a/src/components/settings_modal/tabs/filtering_tab.js +++ b/src/components/settings_modal/tabs/filtering_tab.js @@ -38,15 +38,6 @@ const FilteringTab = { }, // Updating nested properties watch: { - notificationVisibility: { - handler (value) { - this.$store.dispatch('setOption', { - name: 'notificationVisibility', - value: this.$store.getters.mergedConfig.notificationVisibility - }) - }, - deep: true - }, replyVisibility () { this.$store.dispatch('queueFlushAll') } diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index 8b64a07e..67879307 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -4,6 +4,7 @@ import ProgressButton from '../progress_button/progress_button.vue' import FollowButton from '../follow_button/follow_button.vue' import ModerationTools from '../moderation_tools/moderation_tools.vue' import AccountActions from '../account_actions/account_actions.vue' +import UserNote from '../user_note/user_note.vue' import Select from '../select/select.vue' import UserLink from '../user_link/user_link.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' @@ -39,7 +40,8 @@ export default { 'rounded', 'bordered', 'avatarAction', // default - open profile, 'zoom' - zoom, function - call function - 'onClose' + 'onClose', + 'hasNoteEditor' ], data () { return { @@ -129,6 +131,12 @@ export default { const privileges = this.loggedIn.privileges return this.loggedIn.role === 'admin' || privileges.includes('users_manage_activation_state') || privileges.includes('users_delete') || privileges.includes('users_manage_tags') }, + hasNote () { + return this.relationship.note + }, + supportsNote () { + return 'note' in this.relationship + }, ...mapGetters(['mergedConfig']) }, components: { @@ -140,7 +148,8 @@ export default { FollowButton, Select, RichContent, - UserLink + UserLink, + UserNote }, methods: { muteUser () { diff --git a/src/components/user_card/user_card.scss b/src/components/user_card/user_card.scss index a0bbc6a6..cdb8cb57 100644 --- a/src/components/user_card/user_card.scss +++ b/src/components/user_card/user_card.scss @@ -315,6 +315,10 @@ margin: 0; } } + + .user-note { + margin: 0 .75em .6em 0; + } } .sidebar .edit-profile-button { diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 897d89f9..349c7cb1 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -268,6 +268,12 @@ > <RemoteFollow :user="user" /> </div> + <UserNote + v-if="loggedIn && isOtherUser && (hasNote || (hasNoteEditor && supportsNote))" + :user="user" + :relationship="relationship" + :editable="hasNoteEditor" + /> </div> </div> <div diff --git a/src/components/user_note/user_note.js b/src/components/user_note/user_note.js new file mode 100644 index 00000000..830b2e59 --- /dev/null +++ b/src/components/user_note/user_note.js @@ -0,0 +1,45 @@ +const UserNote = { + props: { + user: Object, + relationship: Object, + editable: Boolean + }, + data () { + return { + localNote: '', + editing: false, + frozen: false + } + }, + computed: { + shouldShow () { + return this.relationship.note || this.editing + } + }, + methods: { + startEditing () { + this.localNote = this.relationship.note + this.editing = true + }, + cancelEditing () { + this.editing = false + }, + finalizeEditing () { + this.frozen = true + + this.$store.dispatch('editUserNote', { + id: this.user.id, + comment: this.localNote + }) + .then(() => { + this.frozen = false + this.editing = false + }) + .catch(() => { + this.frozen = false + }) + } + } +} + +export default UserNote diff --git a/src/components/user_note/user_note.vue b/src/components/user_note/user_note.vue new file mode 100644 index 00000000..4286e017 --- /dev/null +++ b/src/components/user_note/user_note.vue @@ -0,0 +1,88 @@ +<template> + <div + class="user-note" + > + <div class="heading"> + <span>{{ $t('user_card.note') }}</span> + <div class="buttons"> + <button + v-show="!editing && editable" + class="button-default btn" + @click="startEditing" + > + {{ $t('user_card.edit_note') }} + </button> + <button + v-show="editing" + class="button-default btn" + :disabled="frozen" + @click="finalizeEditing" + > + {{ $t('user_card.edit_note_apply') }} + </button> + <button + v-show="editing" + class="button-default btn" + :disabled="frozen" + @click="cancelEditing" + > + {{ $t('user_card.edit_note_cancel') }} + </button> + </div> + </div> + <textarea + v-show="editing" + v-model="localNote" + class="note-text" + /> + <span + v-show="!editing" + class="note-text" + :class="{ '-blank': !relationship.note }" + > + {{ relationship.note || $t('user_card.note_blank') }} + </span> + </div> +</template> + +<script src="./user_note.js"></script> + +<style lang="scss"> +@import '../../variables'; + +.user-note { + display: flex; + flex-direction: column; + + .heading { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + margin-bottom: 0.75em; + + .btn { + min-width: 95px; + } + + .buttons { + display: flex; + flex-direction: row; + justify-content: right; + + .btn { + margin-left: 0.5em; + } + } + } + + .note-text { + align-self: stretch; + } + + .note-text.-blank { + font-style: italic; + color: var(--faint, $fallback--faint); + } +} +</style> diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index d0da2b5b..d5e8d230 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -10,6 +10,7 @@ :selected="timeline.viewing" avatar-action="zoom" rounded="top" + :has-note-editor="true" /> <div v-if="user.fields_html && user.fields_html.length > 0" diff --git a/src/i18n/en.json b/src/i18n/en.json index 5793c3c8..59ee1c17 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -957,7 +957,12 @@ "solid": "Solid bg", "striped": "Striped bg", "side": "Side stripe" - } + }, + "note": "Note", + "note_blank": "(None)", + "edit_note": "Edit note", + "edit_note_apply": "Apply", + "edit_note_cancel": "Cancel" }, "user_profile": { "timeline_title": "User timeline", @@ -986,7 +991,9 @@ "user_settings": "User Settings", "accept_follow_request": "Accept follow request", "reject_follow_request": "Reject follow request", - "bookmark": "Bookmark" + "bookmark": "Bookmark", + "toggle_expand": "Expand or collapse notification to show post in full", + "toggle_mute": "Expand or collapse notification to reveal muted content" }, "upload": { "error": { diff --git a/src/modules/users.js b/src/modules/users.js index 10b4603f..053e44b6 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -56,6 +56,11 @@ const removeUserFromFollowers = (store, id) => { .then((relationship) => store.commit('updateUserRelationship', [relationship])) } +const editUserNote = (store, { id, comment }) => { + return store.rootState.api.backendInteractor.editUserNote({ id, comment }) + .then((relationship) => store.commit('updateUserRelationship', [relationship])) +} + const muteUser = (store, id) => { const predictedRelationship = store.state.relationships[id] || { id } predictedRelationship.muting = true @@ -335,6 +340,9 @@ const users = { unblockUsers (store, ids = []) { return Promise.all(ids.map(id => unblockUser(store, id))) }, + editUserNote (store, args) { + return editUserNote(store, args) + }, fetchMutes (store) { return store.rootState.api.backendInteractor.fetchMutes() .then((mutes) => { diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index df652ae1..7174cc5d 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -70,6 +70,7 @@ const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute` const MASTODON_REMOVE_USER_FROM_FOLLOWERS = id => `/api/v1/accounts/${id}/remove_from_followers` const MASTODON_SUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/subscribe` const MASTODON_UNSUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/unsubscribe` +const MASTODON_USER_NOTE_URL = id => `/api/v1/accounts/${id}/note` const MASTODON_BOOKMARK_STATUS_URL = id => `/api/v1/statuses/${id}/bookmark` const MASTODON_UNBOOKMARK_STATUS_URL = id => `/api/v1/statuses/${id}/unbookmark` const MASTODON_POST_STATUS_URL = '/api/v1/statuses' @@ -321,6 +322,17 @@ const removeUserFromFollowers = ({ id, credentials }) => { }).then((data) => data.json()) } +const editUserNote = ({ id, credentials, comment }) => { + return promisedRequest({ + url: MASTODON_USER_NOTE_URL(id), + credentials, + payload: { + comment + }, + method: 'POST' + }) +} + const approveUser = ({ id, credentials }) => { const url = MASTODON_APPROVE_USER_URL(id) return fetch(url, { @@ -1667,6 +1679,7 @@ const apiService = { blockUser, unblockUser, removeUserFromFollowers, + editUserNote, fetchUser, fetchUserByName, fetchUserRelationship, |
