diff options
author | Louis <6653109+artonge@users.noreply.github.com> | 2023-04-20 20:03:40 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-20 20:03:40 +0200 |
commit | 281373117b2b2758863a2e3657316e3f9bbca93d (patch) | |
tree | efa1cf251996a920a4ca6f524623bd09dee609ba | |
parent | 7c80556ae4257ccc196428b7ef6d6abf170a4288 (diff) | |
parent | 8418364b9fa76e1ddc5ca611c80ca11c4df0aade (diff) |
Merge pull request #1744 from nextcloud/artonge/fix/fixes
Front-end fixes
-rw-r--r-- | src/components/Composer/Composer.vue | 20 | ||||
-rw-r--r-- | src/components/Composer/SubmitStatusButton.vue | 2 | ||||
-rw-r--r-- | src/components/FollowButton.vue | 4 | ||||
-rw-r--r-- | src/components/ProfileInfo.vue | 2 | ||||
-rw-r--r-- | src/components/TimelineAvatar.vue | 4 | ||||
-rw-r--r-- | src/components/TimelineEntry.vue | 101 | ||||
-rw-r--r-- | src/components/TimelinePost.vue | 48 | ||||
-rw-r--r-- | src/components/UserEntry.vue | 7 | ||||
-rw-r--r-- | src/components/Visibility/VisibilitiesInfos.js | 2 | ||||
-rw-r--r-- | src/store/account.js | 4 | ||||
-rw-r--r-- | src/store/timeline.js | 56 | ||||
-rw-r--r-- | src/views/Profile.vue | 2 |
12 files changed, 126 insertions, 126 deletions
diff --git a/src/components/Composer/Composer.vue b/src/components/Composer/Composer.vue index e0a77ecc..d3c73e7e 100644 --- a/src/components/Composer/Composer.vue +++ b/src/components/Composer/Composer.vue @@ -68,7 +68,7 @@ :contenteditable="!loading" class="message" placeholder="What would you like to share?" - :class="{'icon-loading': loading}" + :class="{'icon-loading': loading, 'too-long': statusIsTooLong}" @keyup.prevent.enter="keyup" @input="updateStatusContent" @tribute-replaced="updatePostFromTribute" /> @@ -277,12 +277,24 @@ export default { return true } - return !this.statusIsEmpty + if (this.statusIsTooLong) { + return false + } + + if (this.statusIsEmpty) { + return false + } + + return true }, /** @return {boolean} */ statusIsEmpty() { return this.statusContent.length === 0 || this.statusContent === '<br>' }, + + statusIsTooLong() { + return this.statusContent.length > 500 + }, }, mounted() { this.$root.$on('composer-reply', (/** @type {import('../../types/Mastodon.js').Status} */data) => { @@ -518,6 +530,10 @@ export default { min-width: 2px; display: block; + &.too-long { + color: var(--color-error); + } + :deep(.mention) { color: var(--color-primary-element); background-color: var(--color-background-dark); diff --git a/src/components/Composer/SubmitStatusButton.vue b/src/components/Composer/SubmitStatusButton.vue index f426efd3..caf09655 100644 --- a/src/components/Composer/SubmitStatusButton.vue +++ b/src/components/Composer/SubmitStatusButton.vue @@ -65,7 +65,7 @@ export default { case 'followers': return t('social', 'Post to followers') case 'direct': - return t('social', 'Post to mentioned users') + return t('social', 'Send message to mentioned users') } return '' }, diff --git a/src/components/FollowButton.vue b/src/components/FollowButton.vue index 3390c1fc..01b17c0e 100644 --- a/src/components/FollowButton.vue +++ b/src/components/FollowButton.vue @@ -72,10 +72,6 @@ export default { currentUser, ], props: { - account: { - type: String, - default: '', - }, uid: { type: String, default: '', diff --git a/src/components/ProfileInfo.vue b/src/components/ProfileInfo.vue index ed2e384b..c40d1452 100644 --- a/src/components/ProfileInfo.vue +++ b/src/components/ProfileInfo.vue @@ -59,7 +59,7 @@ <!-- Hack to render note safely --> <MessageContent v-if="accountInfo.note" class="user-profile__note user-profile__info" :item="{content: accountInfo.note, tag: [], mentions: []}" /> - <FollowButton class="user-profile__info" :account="accountInfo.acct" :uid="uid" /> + <FollowButton class="user-profile__info" :uid="uid" /> <NcButton v-if="serverData.public" class="user-profile__info primary" @click="followRemote"> diff --git a/src/components/TimelineAvatar.vue b/src/components/TimelineAvatar.vue index 11aeb658..17e6bcc0 100644 --- a/src/components/TimelineAvatar.vue +++ b/src/components/TimelineAvatar.vue @@ -48,9 +48,5 @@ export default { padding: 5px 10px 10px 5px; height: 52px; width: 52px; - - .avatardiv { - position: static; - } } </style> diff --git a/src/components/TimelineEntry.vue b/src/components/TimelineEntry.vue index 80812f1a..6e4fd50b 100644 --- a/src/components/TimelineEntry.vue +++ b/src/components/TimelineEntry.vue @@ -5,39 +5,49 @@ <img :src="notification.account.avatar"> <Heart v-if="notification.type === 'favourite'" :size="16" /> <Repeat v-if="notification.type === 'reblog'" :size="16" /> + <AccountPlusOutline v-if="notification.type === 'follow'" :size="16" /> + <AccountQuestion v-if="notification.type === 'follow_request'" :size="16" /> + <At v-if="notification.type === 'mention'" :size="16" /> + <MessageOutline v-if="notification.type === 'status'" :size="16" /> + <MessagePlusOutline v-if="notification.type === 'update'" :size="16" /> + <Poll v-if="notification.type === 'poll'" :size="16" /> {{ actionSummary }} </span> <span class="notification__details"> - <router-link :to="{ name: 'single-post', params: { + <router-link v-if="!notificationIsAboutAnAccount" + :to="{ name: 'single-post', params: { account: item.account.display_name, id: notification.status.id, type: 'single-post', } }" :data-timestamp="notification.created_at" - class="post-timestamp live-relative-timestamp" + class="post-timestamp" :title="notificationFormattedDate"> {{ notificationRelativeTimestamp }} </router-link> + <span v-else + class="post-timestamp" + :data-timestamp="notification.created_at" + :title="notificationFormattedDate"> + {{ notificationRelativeTimestamp }} + </span> </span> </div> <template v-else-if="isBoost"> - <div class="container-icon-boost boost"> - <span class="icon-boost" /> - </div> <div class="boost"> + <Repeat :size="16" /> <router-link :to="{ name: 'profile', params: { account: item.account.acct } }"> + <img :src="item.account.avatar"> <span :title="item.account.acct" class="post-author"> - {{ item.account.display_name }} + {{ item.account.display_name }}  </span> </router-link> {{ t('social', 'boosted') }} </div> </template> - <UserEntry v-if="isNotification && notificationIsAboutAnAccount" - :key="item.account.id" - :item="item.account" /> + <UserEntry v-if="isNotification && notificationIsAboutAnAccount" :item="item.account" /> <template v-else> - <div class="wrapper"> + <div v-if="entryContent" class="wrapper"> <TimelineAvatar v-if="!isNotification" class="entry__avatar" :item="entryContent" /> <TimelinePost class="entry__content" :item="entryContent" @@ -50,8 +60,13 @@ <script> import Bell from 'vue-material-design-icons/Bell.vue' import Repeat from 'vue-material-design-icons/Repeat.vue' -import Reply from 'vue-material-design-icons/Reply.vue' import Heart from 'vue-material-design-icons/Heart.vue' +import AccountPlusOutline from 'vue-material-design-icons/AccountPlusOutline.vue' +import AccountQuestion from 'vue-material-design-icons/AccountQuestion.vue' +import At from 'vue-material-design-icons/At.vue' +import Poll from 'vue-material-design-icons/Poll.vue' +import MessageOutline from 'vue-material-design-icons/MessageOutline.vue' +import MessagePlusOutline from 'vue-material-design-icons/MessagePlusOutline.vue' import { translate } from '@nextcloud/l10n' import moment from '@nextcloud/moment' import TimelinePost from './TimelinePost.vue' @@ -67,8 +82,13 @@ export default { UserEntry, Bell, Repeat, - Reply, Heart, + AccountPlusOutline, + AccountQuestion, + At, + Poll, + MessageOutline, + MessagePlusOutline, }, props: { /** @type {import('vue').PropType<import('../types/Mastodon.js').Status|import('../types/Mastodon.js').Notification>} */ @@ -93,7 +113,8 @@ export default { if (this.isNotification) { return this.notification.status } else if (this.isBoost) { - return this.status.reblog + // We use the object stored in the store so that actions on it are reflected. + return this.$store.getters.getStatus(this.item.reblog.id) } else { return this.item } @@ -124,7 +145,7 @@ export default { }, /** @return {boolean} */ notificationIsAboutAnAccount() { - return this.notification.type in ['follow', 'follow_request', 'admin.sign_up', 'admin.report'] + return ['follow', 'follow_request', 'admin.sign_up', 'admin.report'].includes(this.notification.type) }, /** * @return {boolean} @@ -191,7 +212,6 @@ export default { width: 32px; border-radius: 50%; overflow: hidden; - margin-right: 3px; vertical-align: middle; margin-top: -1px; margin-right: 8px; @@ -206,12 +226,12 @@ export default { border-radius: 50%; border: 1px solid var(--color-background-dark); } - } - &__details a { + &__details .post-timestamp { color: var(--color-text-lighter); - + } + &__details a { &:hover { text-decoration: underline; } @@ -226,38 +246,25 @@ export default { display: none; } } - } - - .icon-boost { - display: inline-block; - vertical-align: middle; - } - .icon-favorite { - display: inline-block; - vertical-align: middle; - } - - .icon-user { - display: inline-block; - vertical-align: middle; - } - - .container-icon-boost { - display: inline-block; - padding-right: 6px; - } - - .icon-boost { - display: inline-block; - width: 38px; - height: 17px; - opacity: .5; - background-position: right center; - vertical-align: middle; + :deep(.user-entry) { + .user-avatar { + display: none; + } + } } .boost { - opacity: .5; + color: var(--color-text-lighter); + display: flex; + margin-left: 21px; // To align with status' text. + + img { + width: 16px; + border-radius: 50%; + vertical-align: middle; + margin-top: -4px; + margin-left: 4px; + } } </style> diff --git a/src/components/TimelinePost.vue b/src/components/TimelinePost.vue index 1b23b79b..6cd0c10b 100644 --- a/src/components/TimelinePost.vue +++ b/src/components/TimelinePost.vue @@ -38,10 +38,8 @@ <template #icon> <Reply :size="20" /> </template> - <template #default> - <span v-if="item.replies_count !== 0"> - {{ item.replies_count }} - </span> + <template> + {{ item.replies_count > 0 ? item.replies_count : '' }} </template> </NcButton> <NcButton v-if="item.visibility === 'public' || item.visibility === 'unlisted'" @@ -51,10 +49,8 @@ <template #icon> <Repeat :size="20" :fill-color="isBoosted ? 'var(--color-primary)' : 'var(--color-main-text)'" /> </template> - <template #default> - <span v-if="item.reblogs_count !== 0"> - {{ item.reblogs_count }} - </span> + <template> + {{ item.reblogs_count > 0 ? item.reblogs_count : '' }} </template> </NcButton> <NcButton v-if="!isLiked" @@ -64,10 +60,8 @@ <template #icon> <HeartOutline :size="20" /> </template> - <template #default> - <span v-if="item.favourites_count !== 0"> - {{ item.favourites_count }} - </span> + <template> + {{ item.favourites_count > 0 ? item.favourites_count : '' }} </template> </NcButton> <NcButton v-if="isLiked" @@ -77,6 +71,9 @@ <template #icon> <Heart :size="20" :fill-color="'var(--color-error)'" /> </template> + <template> + {{ item.favourites_count > 0 ? item.favourites_count : '' }} + </template> </NcButton> <NcActions> <NcActionButton v-if="item.account.acct === currentAccount?.acct" @@ -327,33 +324,8 @@ export default { margin: 4px; .button-vue:hover { + // Else hover state is the same as the background. background: var(--color-background-dark); } - - .post-actions-more { - position: relative; - width: 44px; - height: 34px; - display: inline-block; - } - - .icon-reply, - .icon-boost, - .icon-boosted, - .icon-starred, - .icon-favorite, - .icon-more { - display: inline-block; - width: 44px; - height: 34px; - opacity: .5; - &:hover, &:focus { - opacity: 1; - } - } - - .icon-boosted { - opacity: 1; - } } </style> diff --git a/src/components/UserEntry.vue b/src/components/UserEntry.vue index 370753ca..ca52c85e 100644 --- a/src/components/UserEntry.vue +++ b/src/components/UserEntry.vue @@ -53,7 +53,7 @@ <!-- eslint-disable-next-line vue/no-v-html --> <p v-html="item.note" /> </div> - <FollowButton :account="item.acct" :uid="cloudId" /> + <FollowButton :uid="item.acct" /> </div> </div> </template> @@ -92,6 +92,11 @@ export default { return !this.item.acct.includes('@') }, }, + async mounted() { + if (this.relationship === undefined) { + await this.$store.dispatch('fetchAccountRelationshipInfo', [this.item.id]) + } + }, } </script> <style scoped lang="scss"> diff --git a/src/components/Visibility/VisibilitiesInfos.js b/src/components/Visibility/VisibilitiesInfos.js index af2c64c0..e7955d30 100644 --- a/src/components/Visibility/VisibilitiesInfos.js +++ b/src/components/Visibility/VisibilitiesInfos.js @@ -26,7 +26,7 @@ export default [ }, { id: 'direct', - text: t('social', 'Direct'), + text: t('social', 'Direct message'), description: t('social', 'Visible to mentioned users only'), }, ] diff --git a/src/store/account.js b/src/store/account.js index 01f59336..43ea3b26 100644 --- a/src/store/account.js +++ b/src/store/account.js @@ -201,7 +201,7 @@ const actions = { commit('setCurrentAccount', account) dispatch('fetchAccountInfo', account) }, - async followAccount(context, { currentAccount, accountToFollow }) { + async followAccount(context, { accountToFollow }) { try { const response = await axios.put(generateUrl('/apps/social/api/v1/current/follow?account=' + accountToFollow)) if (response.data.status === -1) { @@ -214,7 +214,7 @@ const actions = { logger.error(`Failed to follow user ${accountToFollow}`, { error }) } }, - async unfollowAccount(context, { currentAccount, accountToUnfollow }) { + async unfollowAccount(context, { accountToUnfollow }) { try { const response = await axios.delete(generateUrl('/apps/social/api/v1/current/follow?account=' + accountToUnfollow)) if (response.data.status === -1) { diff --git a/src/store/timeline.js b/src/store/timeline.js index e972c0a1..44f138c8 100644 --- a/src/store/timeline.js +++ b/src/store/timeline.js @@ -69,6 +69,18 @@ const state = { composerDisplayStatus: false, } +/** + * + * @param {typeof state} state + * @param {import ('../types/Mastodon.js').Status} status + */ +function addToStatuses(state, status) { + Vue.set(state.statuses, status.id, status) + if (status.reblog !== undefined && status.reblog !== null) { + Vue.set(state.statuses, status.reblog.id, status.reblog) + } +} + /** @type {import('vuex').MutationTree<state>} */ const mutations = { /** @@ -76,7 +88,7 @@ const mutations = { * @param {import ('../types/Mastodon.js').Status} status */ addToStatuses(state, status) { - Vue.set(state.statuses, status.id, status) + addToStatuses(state, status) }, /** * @param state @@ -84,13 +96,13 @@ const mutations = { */ addToTimeline(state, data) { if (Array.isArray(data)) { - data.forEach(status => Vue.set(state.statuses, status.id, status)) + data.forEach(status => addToStatuses(state, status)) data .filter(status => state.timeline.indexOf(status.id) === -1) .forEach(status => state.timeline.push(status.id)) } else { - data.descendants.forEach(status => Vue.set(state.statuses, status.id, status)) - data.ancestors.forEach(status => Vue.set(state.statuses, status.id, status)) + data.descendants.forEach(status => addToStatuses(state, status)) + data.ancestors.forEach(status => addToStatuses(state, status)) data.descendants .filter(status => state.timeline.indexOf(status.id) === -1) @@ -102,20 +114,9 @@ const mutations = { }, /** * @param state - * @param {import ('../types/Mastodon.js').Status[]} data - */ - updateInTimelines(state, data) { - data.forEach((status) => { - if (state.statuses[status.id] !== undefined) { - Vue.set(state.statuses, status.id, status) - } - }) - }, - /** - * @param state * @param {import('../types/Mastodon.js').Status} status */ - removeStatusf(state, status) { + removeStatus(state, status) { const timelineIndex = state.timeline.indexOf(status.id) if (timelineIndex !== -1) { state.timeline.splice(timelineIndex, 1) @@ -161,6 +162,7 @@ const mutations = { likeStatus(state, { status }) { if (state.statuses[status.id] !== undefined) { Vue.set(state.statuses[status.id], 'favourited', true) + state.statuses[status.id].favourites_count++ } }, /** @@ -171,6 +173,7 @@ const mutations = { unlikeStatus(state, { status }) { if (state.statuses[status.id] !== undefined) { Vue.set(state.statuses[status.id], 'favourited', false) + state.statuses[status.id].favourites_count-- } }, /** @@ -181,6 +184,7 @@ const mutations = { boostStatus(state, { status }) { if (state.statuses[status.id] !== undefined) { Vue.set(state.statuses[status.id], 'reblogged', true) + state.statuses[status.id].reblogs_count++ } }, /** @@ -191,6 +195,7 @@ const mutations = { unboostStatus(state, { status }) { if (state.statuses[status.id] !== undefined) { Vue.set(state.statuses[status.id], 'reblogged', false) + state.statuses[status.id].reblogs_count-- } }, } @@ -210,11 +215,14 @@ const getters = { .map(statusId => state.statuses[statusId]) .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()) }, + getStatus(state) { + return (statusId) => state.statuses[statusId] + }, getSinglePost(state) { return state.statuses[state.params.singlePost] }, getPostFromTimeline(state) { - return (/** @type {string} */ statusId) => { + return (statusId) => { if (state.statuses[statusId] !== undefined) { return state.statuses[statusId] } else { @@ -280,11 +288,11 @@ const actions = { */ async postDelete(context, status) { try { - context.commit('removeStatusf', status) + context.commit('removeStatus', status) const response = await axios.delete(generateUrl(`apps/social/api/v1/post?id=${status.uri}`)) logger.info('Post deleted with token ' + response.data.result.token) } catch (error) { - context.commit('updateInTimelines', [status]) + context.commit('addToStatuses', status) showError('Failed to delete the status') logger.error('Failed to delete the status', { error }) } @@ -299,7 +307,7 @@ const actions = { context.commit('likeStatus', { status }) const response = await axios.post(generateUrl(`apps/social/api/v1/statuses/${status.id}/favourite`)) logger.info('Post liked') - context.commit('updateInTimelines', [response.data]) + context.commit('addToStatuses', response.data) return response } catch (error) { context.commit('unlikeStatus', { status }) @@ -316,12 +324,12 @@ const actions = { try { // Remove status from list if we are in the 'liked' timeline if (state.type === 'liked') { - context.commit('removeStatusf', status) + context.commit('removeStatus', status) } context.commit('unlikeStatus', { status }) const response = await axios.post(generateUrl(`apps/social/api/v1/statuses/${status.id}/unfavourite`)) logger.info('Post unliked') - context.commit('updateInTimelines', [response.data]) + context.commit('addToStatuses', response.data) return response } catch (error) { // Readd status from list if we are in the 'liked' timeline @@ -343,7 +351,7 @@ const actions = { context.commit('boostStatus', { status }) const response = await axios.post(generateUrl(`apps/social/api/v1/statuses/${status.id}/reblog`)) logger.info('Post boosted') - context.commit('updateInTimelines', [response.data]) + context.commit('addToStatuses', response.data) return response } catch (error) { context.commit('unboostStatus', { status }) @@ -361,7 +369,7 @@ const actions = { context.commit('unboostStatus', { status }) const response = await axios.post(generateUrl(`apps/social/api/v1/statuses/${status.id}/unreblog`)) logger.info('Boost deleted') - context.commit('updateInTimelines', [response.data]) + context.commit('addToStatuses', response.data) return response } catch (error) { context.commit('boostStatus', { status }) diff --git a/src/views/Profile.vue b/src/views/Profile.vue index f0fae400..fbb29955 100644 --- a/src/views/Profile.vue +++ b/src/views/Profile.vue @@ -24,7 +24,7 @@ <div :class="{'icon-loading': !accountLoaded}" class="social__wrapper"> <ProfileInfo v-if="accountLoaded && accountInfo" :uid="uid" /> - <Composer v-if="accountInfo" :initial-mention="accountInfo.acct === currentAccount.acct ? null : accountInfo" default-visibility="direct" /> + <Composer v-if="accountInfo && $route.name === 'profile'" :initial-mention="accountInfo.acct === currentAccount.acct ? null : accountInfo" default-visibility="direct" /> <!-- TODO: we have no details, timeline and follower list for non-local accounts for now --> <router-view v-if="accountLoaded && accountInfo && isLocal" name="details" /> |