From 50f0d216861bfce22c98caec7dd00bf8d9dc4a70 Mon Sep 17 00:00:00 2001 From: Edijs <iamedijs@hotmail.com> Date: Thu, 14 Feb 2019 09:52:23 -0700 Subject: [PATCH 01/56] Use static height if gallery has single image --- src/components/gallery/gallery.js | 4 +++- src/components/gallery/gallery.vue | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/gallery/gallery.js b/src/components/gallery/gallery.js index 7f33a81b..c713ce07 100644 --- a/src/components/gallery/gallery.js +++ b/src/components/gallery/gallery.js @@ -34,7 +34,9 @@ const Gallery = { return rows }, rowHeight () { - return itemsPerRow => ({ 'height': `${(this.width / (itemsPerRow + 0.6))}px` }) + return itemsPerRow => ({ + 'height': this.attachments.length === 1 ? '260px' : `${(this.width / (itemsPerRow + 0.6))}px` + }) }, useContainFit () { return this.$store.state.config.useContainFit diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue index 3f90caa9..90888c21 100644 --- a/src/components/gallery/gallery.vue +++ b/src/components/gallery/gallery.vue @@ -1,6 +1,11 @@ <template> <div ref="galleryContainer" style="width: 100%;"> - <div class="gallery-row" v-for="row in rows" :style="rowHeight(row.length)" :class="{ 'contain-fit': useContainFit, 'cover-fit': !useContainFit }"> + <div + v-for="(row, row_num) in rows" + :key="row_num" + :style="rowHeight(row.length)" + :class="{ 'gallery-row': true, 'contain-fit': useContainFit, 'cover-fit': !useContainFit }" + > <attachment v-for="attachment in row" :setMedia="setMedia" From 28660d698650d3a4fd8bda08b694444e88d7afda Mon Sep 17 00:00:00 2001 From: Edijs <iamedijs@hotmail.com> Date: Thu, 14 Feb 2019 10:21:10 -0700 Subject: [PATCH 02/56] Issue #335 - place hide button on right hand --- src/components/attachment/attachment.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 7e972026..a93c9014 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -160,6 +160,7 @@ .hider { position: absolute; + right: 0; white-space: nowrap; margin: 10px; padding: 5px; From 6c8ccf2733c780543a9afc5ffbe7942064ad2596 Mon Sep 17 00:00:00 2001 From: jasper <jasper92341@hotmail.com> Date: Fri, 15 Feb 2019 07:52:34 -0800 Subject: [PATCH 03/56] Remove posts immediately by blocking --- .../user_card_content/user_card_content.js | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 7a7b89d4..41e4e817 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -93,27 +93,55 @@ export default { }, methods: { followUser () { + const store = this.$store this.followRequestInProgress = true - requestFollow(this.user, this.$store).then(({sent}) => { + requestFollow(this.user, store).then(({sent}) => { this.followRequestInProgress = false this.followRequestSent = sent + + store.dispatch('stopFetching', 'friends') + store.commit('clearTimeline', { timeline: 'friends' }) + store.dispatch('startFetching', { timeline: 'friends' }) }) }, unfollowUser () { + const store = this.$store this.followRequestInProgress = true - requestUnfollow(this.user, this.$store).then(() => { + requestUnfollow(this.user, store).then(() => { this.followRequestInProgress = false + + store.dispatch('stopFetching', 'friends') + store.commit('clearTimeline', { timeline: 'friends' }) + store.dispatch('startFetching', { timeline: 'friends' }) }) }, blockUser () { const store = this.$store store.state.api.backendInteractor.blockUser(this.user.id) - .then((blockedUser) => store.commit('addNewUsers', [blockedUser])) + .then((blockedUser) => { + store.commit('addNewUsers', [blockedUser]) + + store.dispatch('stopFetching', 'friends') + store.commit('clearTimeline', { timeline: 'friends' }) + store.dispatch('startFetching', { timeline: 'friends' }) + + store.commit('clearTimeline', { timeline: 'public' }) + store.commit('clearTimeline', { timeline: 'publicAndExternal' }) + }) }, unblockUser () { const store = this.$store store.state.api.backendInteractor.unblockUser(this.user.id) - .then((unblockedUser) => store.commit('addNewUsers', [unblockedUser])) + .then((unblockedUser) => { + store.commit('addNewUsers', [unblockedUser]) + + store.dispatch('stopFetching', 'friends') + store.commit('clearTimeline', { timeline: 'friends' }) + store.dispatch('startFetching', { timeline: 'friends' }) + + store.commit('clearTimeline', { timeline: 'public' }) + store.commit('clearTimeline', { timeline: 'publicAndExternal' }) + }) }, toggleMute () { const store = this.$store From 24d7f9917b1a4a147b92bd31ec65511c1d528c6f Mon Sep 17 00:00:00 2001 From: jasper <jasper92341@hotmail.com> Date: Mon, 18 Feb 2019 11:39:35 -0800 Subject: [PATCH 04/56] Remove posts by blocking or following --- .../user_card_content/user_card_content.js | 71 ++++++++++++++----- src/modules/statuses.js | 23 ++++++ src/services/api/api.service.js | 7 +- 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 41e4e817..8bc01941 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -1,4 +1,5 @@ import UserAvatar from '../user_avatar/user_avatar.vue' +import apiService from '../../services/api/api.service.js' import { hex2rgb } from '../../services/color_convert/color_convert.js' import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -99,9 +100,24 @@ export default { this.followRequestInProgress = false this.followRequestSent = sent - store.dispatch('stopFetching', 'friends') - store.commit('clearTimeline', { timeline: 'friends' }) - store.dispatch('startFetching', { timeline: 'friends' }) + const rootState = store.rootState || store.state + const credentials = store.state.users.currentUser.credentials + const timelineData = rootState.statuses.timelines['friends'] + apiService.fetchTimeline({ + store, + credentials, + userId: this.user.id, + timeline: 'user', + between: true, + until: timelineData.maxId, + since: timelineData.minVisibleId + }).then((statuses) => { + store.dispatch('addNewStatuses', { + timeline: 'friends', + statuses, + showImmediately: true + }) + }, () => store.dispatch('setError', { value: true })) }) }, unfollowUser () { @@ -110,9 +126,7 @@ export default { requestUnfollow(this.user, store).then(() => { this.followRequestInProgress = false - store.dispatch('stopFetching', 'friends') - store.commit('clearTimeline', { timeline: 'friends' }) - store.dispatch('startFetching', { timeline: 'friends' }) + store.commit('removeStatus', { timeline: 'friends', userId: this.user.id }) }) }, blockUser () { @@ -121,12 +135,9 @@ export default { .then((blockedUser) => { store.commit('addNewUsers', [blockedUser]) - store.dispatch('stopFetching', 'friends') - store.commit('clearTimeline', { timeline: 'friends' }) - store.dispatch('startFetching', { timeline: 'friends' }) - - store.commit('clearTimeline', { timeline: 'public' }) - store.commit('clearTimeline', { timeline: 'publicAndExternal' }) + store.commit('removeStatus', { timeline: 'friends', userId: this.user.id }) + store.commit('removeStatus', { timeline: 'public', userId: this.user.id }) + store.commit('removeStatus', { timeline: 'publicAndExternal', userId: this.user.id }) }) }, unblockUser () { @@ -135,12 +146,36 @@ export default { .then((unblockedUser) => { store.commit('addNewUsers', [unblockedUser]) - store.dispatch('stopFetching', 'friends') - store.commit('clearTimeline', { timeline: 'friends' }) - store.dispatch('startFetching', { timeline: 'friends' }) - - store.commit('clearTimeline', { timeline: 'public' }) - store.commit('clearTimeline', { timeline: 'publicAndExternal' }) + const rootState = store.rootState || store.state + const credentials = store.state.users.currentUser.credentials + const timelineData = rootState.statuses.timelines['friends'] + apiService.fetchTimeline({ + store, + credentials, + userId: this.user.id, + timeline: 'user', + between: true, + until: timelineData.maxId, + since: timelineData.minVisibleId + }).then((statuses) => { + store.dispatch('addNewStatuses', { + timeline: 'public', + statuses, + showImmediately: true + }) + store.dispatch('addNewStatuses', { + timeline: 'publicAndExternal', + statuses, + showImmediately: true + }) + if (this.user.follows_you) { + store.dispatch('addNewStatuses', { + timeline: 'friends', + statuses, + showImmediately: true + }) + } + }, () => store.dispatch('setError', { value: true })) }) }, toggleMute () { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 46117fd7..57095f37 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -307,9 +307,32 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot }) } +const removeStatus = (state, { timeline, userId }) => { + const timelineObject = state.timelines[timeline] + if (userId) { + remove(timelineObject.statuses, { user: { id: userId } }) + remove(timelineObject.visibleStatuses, { user: { id: userId } }) + const statusesObject = timelineObject.statusesObject + const visibleStatusesObject = timelineObject.visibleStatusesObject + each(statusesObject, (status, key) => { + if (status.user.id === userId) { + delete statusesObject[key] + } + }) + each(visibleStatusesObject, (status, key) => { + if (status.user.id === userId) { + delete visibleStatusesObject[key] + } + }) + timelineObject.minVisibleId = (last(timeline.visibleStatuses) || {}).id + timelineObject.maxId = statuses.length > 0 ? maxBy(statuses, 'id').id : 0 + } +} + export const mutations = { addNewStatuses, addNewNotifications, + removeStatus, showNewStatuses (state, { timeline }) { const oldTimeline = (state.timelines[timeline]) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 92daa04e..d35c72b1 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -329,7 +329,7 @@ const setUserMute = ({id, credentials, muted = true}) => { }) } -const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false}) => { +const fetchTimeline = ({timeline, credentials, since = false, until = false, between = false, count = 20, userId = false, tag = false}) => { const timelineUrls = { public: PUBLIC_TIMELINE_URL, friends: FRIENDS_TIMELINE_URL, @@ -362,8 +362,9 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use if (timeline === 'media') { params.push(['only_media', 1]) } - - params.push(['count', 20]) + if (!between) { + params.push(['count', count]) + } const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') url += `?${queryString}` From 66a105a519ee3a83a2a4354ed36f59355a45f970 Mon Sep 17 00:00:00 2001 From: Edijs <iamedijs@hotmail.com> Date: Fri, 22 Feb 2019 13:53:43 -0700 Subject: [PATCH 05/56] Revert --- src/components/gallery/gallery.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/gallery/gallery.js b/src/components/gallery/gallery.js index c713ce07..7f33a81b 100644 --- a/src/components/gallery/gallery.js +++ b/src/components/gallery/gallery.js @@ -34,9 +34,7 @@ const Gallery = { return rows }, rowHeight () { - return itemsPerRow => ({ - 'height': this.attachments.length === 1 ? '260px' : `${(this.width / (itemsPerRow + 0.6))}px` - }) + return itemsPerRow => ({ 'height': `${(this.width / (itemsPerRow + 0.6))}px` }) }, useContainFit () { return this.$store.state.config.useContainFit From 4e79300232288fd9b7c4456e8b461f7c1cbac6fa Mon Sep 17 00:00:00 2001 From: jasper <jasper92341@hotmail.com> Date: Mon, 25 Feb 2019 14:35:47 -0800 Subject: [PATCH 06/56] Remove posts by blocking or unfollowing --- .../user_card_content/user_card_content.js | 51 ------------------- src/modules/statuses.js | 18 ++----- src/services/api/api.service.js | 6 +-- 3 files changed, 5 insertions(+), 70 deletions(-) diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 8bc01941..d2a97a84 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -1,5 +1,4 @@ import UserAvatar from '../user_avatar/user_avatar.vue' -import apiService from '../../services/api/api.service.js' import { hex2rgb } from '../../services/color_convert/color_convert.js' import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -99,25 +98,6 @@ export default { requestFollow(this.user, store).then(({sent}) => { this.followRequestInProgress = false this.followRequestSent = sent - - const rootState = store.rootState || store.state - const credentials = store.state.users.currentUser.credentials - const timelineData = rootState.statuses.timelines['friends'] - apiService.fetchTimeline({ - store, - credentials, - userId: this.user.id, - timeline: 'user', - between: true, - until: timelineData.maxId, - since: timelineData.minVisibleId - }).then((statuses) => { - store.dispatch('addNewStatuses', { - timeline: 'friends', - statuses, - showImmediately: true - }) - }, () => store.dispatch('setError', { value: true })) }) }, unfollowUser () { @@ -145,37 +125,6 @@ export default { store.state.api.backendInteractor.unblockUser(this.user.id) .then((unblockedUser) => { store.commit('addNewUsers', [unblockedUser]) - - const rootState = store.rootState || store.state - const credentials = store.state.users.currentUser.credentials - const timelineData = rootState.statuses.timelines['friends'] - apiService.fetchTimeline({ - store, - credentials, - userId: this.user.id, - timeline: 'user', - between: true, - until: timelineData.maxId, - since: timelineData.minVisibleId - }).then((statuses) => { - store.dispatch('addNewStatuses', { - timeline: 'public', - statuses, - showImmediately: true - }) - store.dispatch('addNewStatuses', { - timeline: 'publicAndExternal', - statuses, - showImmediately: true - }) - if (this.user.follows_you) { - store.dispatch('addNewStatuses', { - timeline: 'friends', - statuses, - showImmediately: true - }) - } - }, () => store.dispatch('setError', { value: true })) }) }, toggleMute () { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 57095f37..d144702e 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,4 @@ -import { remove, slice, each, find, maxBy, minBy, merge, last, isArray } from 'lodash' +import { remove, slice, each, find, maxBy, minBy, merge, first, last, isArray } from 'lodash' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -312,20 +312,8 @@ const removeStatus = (state, { timeline, userId }) => { if (userId) { remove(timelineObject.statuses, { user: { id: userId } }) remove(timelineObject.visibleStatuses, { user: { id: userId } }) - const statusesObject = timelineObject.statusesObject - const visibleStatusesObject = timelineObject.visibleStatusesObject - each(statusesObject, (status, key) => { - if (status.user.id === userId) { - delete statusesObject[key] - } - }) - each(visibleStatusesObject, (status, key) => { - if (status.user.id === userId) { - delete visibleStatusesObject[key] - } - }) - timelineObject.minVisibleId = (last(timeline.visibleStatuses) || {}).id - timelineObject.maxId = statuses.length > 0 ? maxBy(statuses, 'id').id : 0 + timelineObject.minVisibleId = timelineObject.visibleStatuses.length > 0 ? last(timelineObject.visibleStatuses).id : 0 + timelineObject.maxId = timelineObject.statuses.length > 0 ? first(timelineObject.statuses).id : 0 } } diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index d35c72b1..c5fdcb72 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -329,7 +329,7 @@ const setUserMute = ({id, credentials, muted = true}) => { }) } -const fetchTimeline = ({timeline, credentials, since = false, until = false, between = false, count = 20, userId = false, tag = false}) => { +const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false}) => { const timelineUrls = { public: PUBLIC_TIMELINE_URL, friends: FRIENDS_TIMELINE_URL, @@ -362,9 +362,7 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, bet if (timeline === 'media') { params.push(['only_media', 1]) } - if (!between) { - params.push(['count', count]) - } + params.push(['count', 20]) const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') url += `?${queryString}` From 7698a6fb0dad962a15855adc2a9c4134abe13de7 Mon Sep 17 00:00:00 2001 From: jasper <jasper92341@hotmail.com> Date: Tue, 26 Feb 2019 23:21:04 -0800 Subject: [PATCH 07/56] Remove posts by blocking or unfollowing --- src/components/user_card_content/user_card_content.js | 6 +----- src/services/api/api.service.js | 1 + 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index d2a97a84..35139504 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -105,7 +105,6 @@ export default { this.followRequestInProgress = true requestUnfollow(this.user, store).then(() => { this.followRequestInProgress = false - store.commit('removeStatus', { timeline: 'friends', userId: this.user.id }) }) }, @@ -114,7 +113,6 @@ export default { store.state.api.backendInteractor.blockUser(this.user.id) .then((blockedUser) => { store.commit('addNewUsers', [blockedUser]) - store.commit('removeStatus', { timeline: 'friends', userId: this.user.id }) store.commit('removeStatus', { timeline: 'public', userId: this.user.id }) store.commit('removeStatus', { timeline: 'publicAndExternal', userId: this.user.id }) @@ -123,9 +121,7 @@ export default { unblockUser () { const store = this.$store store.state.api.backendInteractor.unblockUser(this.user.id) - .then((unblockedUser) => { - store.commit('addNewUsers', [unblockedUser]) - }) + .then((unblockedUser) => store.commit('addNewUsers', [unblockedUser])) }, toggleMute () { const store = this.$store diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index c5fdcb72..92daa04e 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -362,6 +362,7 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use if (timeline === 'media') { params.push(['only_media', 1]) } + params.push(['count', 20]) const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') From 9aec49bacb033cdcc118f821706c419d384bf5fe Mon Sep 17 00:00:00 2001 From: jasper <jasper92341@hotmail.com> Date: Wed, 27 Feb 2019 17:45:08 -0800 Subject: [PATCH 08/56] Fetch activites by last id --- src/modules/statuses.js | 19 ++++++++++--------- .../timeline_fetcher.service.js | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 826b544c..4002f282 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -10,6 +10,7 @@ const emptyTl = (userId = 0) => ({ visibleStatusesObject: {}, newStatusCount: 0, maxId: 0, + minId: 0, minVisibleId: 0, loading: false, followers: [], @@ -117,11 +118,16 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us const timelineObject = state.timelines[timeline] const maxNew = statuses.length > 0 ? maxBy(statuses, 'id').id : 0 - const older = timeline && maxNew < timelineObject.maxId + const minNew = statuses.length > 0 ? minBy(statuses, 'id').id : 0 + const newer = timeline && maxNew > timelineObject.maxId && statuses.length > 0 + const older = timeline && (minNew < timelineObject.minId || timelineObject.minId === 0) && statuses.length > 0 - if (timeline && !noIdUpdate && statuses.length > 0 && !older) { + if (!noIdUpdate && newer) { timelineObject.maxId = maxNew } + if (!noIdUpdate && older) { + timelineObject.minId = minNew + } // This makes sure that user timeline won't get data meant for other // user. I.e. opening different user profiles makes request which could @@ -255,13 +261,8 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us processor(status) }) - // Keep the visible statuses sorted - if (timeline) { - sortTimeline(timelineObject) - if ((older || timelineObject.minVisibleId <= 0) && statuses.length > 0) { - timelineObject.minVisibleId = minBy(statuses, 'id').id - } - } + // Keep the visible statuses sorted + if (timeline) sortTimeline(timelineObject) } const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes }) => { diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 64f8f468..6f99616f 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -21,7 +21,7 @@ const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false const timelineData = rootState.statuses.timelines[camelCase(timeline)] if (older) { - args['until'] = until || timelineData.minVisibleId + args['until'] = until || timelineData.minId } else { args['since'] = timelineData.maxId } From 4d026baf170390ec21cd9d83231e0ce465a0c25d Mon Sep 17 00:00:00 2001 From: jasper <jasper92341@hotmail.com> Date: Wed, 27 Feb 2019 18:14:42 -0800 Subject: [PATCH 09/56] Fix fetching error by tag --- .../backend_interactor_service/backend_interactor_service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index 43c914d9..6beb5847 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -54,8 +54,8 @@ const backendInteractorService = (credentials) => { return apiService.denyUser({credentials, id}) } - const startFetching = ({timeline, store, userId = false}) => { - return timelineFetcherService.startFetching({timeline, store, credentials, userId}) + const startFetching = ({timeline, store, userId = false, tag = false}) => { + return timelineFetcherService.startFetching({timeline, store, credentials, userId, tag}) } const setUserMute = ({id, muted = true}) => { From 882e0243313e8f08e5e8995a6374b79f886c91a6 Mon Sep 17 00:00:00 2001 From: Edijs <iamedijs@hotmail.com> Date: Thu, 28 Feb 2019 11:16:16 +0800 Subject: [PATCH 10/56] Revert code --- src/components/gallery/gallery.vue | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue index 90888c21..3f90caa9 100644 --- a/src/components/gallery/gallery.vue +++ b/src/components/gallery/gallery.vue @@ -1,11 +1,6 @@ <template> <div ref="galleryContainer" style="width: 100%;"> - <div - v-for="(row, row_num) in rows" - :key="row_num" - :style="rowHeight(row.length)" - :class="{ 'gallery-row': true, 'contain-fit': useContainFit, 'cover-fit': !useContainFit }" - > + <div class="gallery-row" v-for="row in rows" :style="rowHeight(row.length)" :class="{ 'contain-fit': useContainFit, 'cover-fit': !useContainFit }"> <attachment v-for="attachment in row" :setMedia="setMedia" From ba2e05bc63e0b36d5a126b422c9f87a7ace43305 Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Thu, 28 Feb 2019 14:03:44 -0500 Subject: [PATCH 11/56] #392: stale data served to new user account --- src/components/notifications/notifications.js | 3 ++- src/modules/statuses.js | 15 ++++++++++++--- src/modules/users.js | 4 ++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 5e95631a..9fc5e38a 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -11,7 +11,8 @@ const Notifications = { const store = this.$store const credentials = store.state.users.currentUser.credentials - notificationsFetcher.startFetching({ store, credentials }) + const fetcherId = notificationsFetcher.startFetching({ store, credentials }) + this.$store.commit('setNotificationFetcher', { fetcherId }) }, data () { return { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 826b544c..2d1313a5 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,4 @@ -import { remove, slice, each, find, maxBy, minBy, merge, last, isArray } from 'lodash' +import { remove, slice, each, find, maxBy, minBy, merge, last, isArray, cloneDeep } from 'lodash' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -29,7 +29,8 @@ export const defaultState = { data: [], idStore: {}, loading: false, - error: false + error: false, + fetcherId: null }, favorites: new Set(), error: false, @@ -321,6 +322,14 @@ export const mutations = { oldTimeline.visibleStatusesObject = {} each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status }) }, + setNotificationFetcher (state, { fetcherId }) { + state.notifications.fetcherId = fetcherId + }, + resetStatuses (state) { + Object.keys(state).forEach(key => { + state[key] = cloneDeep(defaultState[key]) + }) + }, clearTimeline (state, { timeline }) { state.timelines[timeline] = emptyTl(state.timelines[timeline].userId) }, @@ -371,7 +380,7 @@ export const mutations = { } const statuses = { - state: defaultState, + state: cloneDeep(defaultState), actions: { addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) { commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId }) diff --git a/src/modules/users.js b/src/modules/users.js index 77df7168..84fe039b 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -290,6 +290,10 @@ const users = { store.commit('setToken', false) store.dispatch('stopFetching', 'friends') store.commit('setBackendInteractor', backendInteractorService()) + if (store.rootState.statuses.notifications.fetcherId) { + window.clearInterval(store.rootState.statuses.notifications.fetcherId) + } + store.commit('resetStatuses') }, loginUser (store, accessToken) { return new Promise((resolve, reject) => { From f5adb62e2e6efdf6bd9a9c7a8c2677c618de53f9 Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Thu, 28 Feb 2019 14:27:47 -0500 Subject: [PATCH 12/56] #392: update defaultState into a function --- src/modules/statuses.js | 11 +++++----- test/unit/specs/modules/statuses.spec.js | 27 ++++++++++++------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 2d1313a5..2af23a9d 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,4 @@ -import { remove, slice, each, find, maxBy, minBy, merge, last, isArray, cloneDeep } from 'lodash' +import { remove, slice, each, find, maxBy, minBy, merge, last, isArray } from 'lodash' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -18,7 +18,7 @@ const emptyTl = (userId = 0) => ({ flushMarker: 0 }) -export const defaultState = { +export const defaultState = () => ({ allStatuses: [], allStatusesObject: {}, maxId: 0, @@ -45,7 +45,7 @@ export const defaultState = { tag: emptyTl(), dms: emptyTl() } -} +}) export const prepareStatus = (status) => { // Set deleted flag @@ -326,8 +326,9 @@ export const mutations = { state.notifications.fetcherId = fetcherId }, resetStatuses (state) { + const emptyState = defaultState() Object.keys(state).forEach(key => { - state[key] = cloneDeep(defaultState[key]) + state[key] = emptyState[key] }) }, clearTimeline (state, { timeline }) { @@ -380,7 +381,7 @@ export const mutations = { } const statuses = { - state: cloneDeep(defaultState), + state: defaultState(), actions: { addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) { commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId }) diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js index 01d2ce06..864b798d 100644 --- a/test/unit/specs/modules/statuses.spec.js +++ b/test/unit/specs/modules/statuses.spec.js @@ -1,4 +1,3 @@ -import { cloneDeep } from 'lodash' import { defaultState, mutations, prepareStatus } from '../../../../src/modules/statuses.js' // eslint-disable-next-line camelcase @@ -24,7 +23,7 @@ describe('Statuses.prepareStatus', () => { describe('The Statuses module', () => { it('adds the status to allStatuses and to the given timeline', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) @@ -36,7 +35,7 @@ describe('The Statuses module', () => { }) it('counts the status as new if it has not been seen on this timeline', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) @@ -54,7 +53,7 @@ describe('The Statuses module', () => { }) it('add the statuses to allStatuses if no timeline is given', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status] }) @@ -66,7 +65,7 @@ describe('The Statuses module', () => { }) it('adds the status to allStatuses and to the given timeline, directly visible', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) @@ -78,7 +77,7 @@ describe('The Statuses module', () => { }) it('removes statuses by tag on deletion', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const otherStatus = makeMockStatus({id: '3'}) status.uri = 'xxx' @@ -96,7 +95,7 @@ describe('The Statuses module', () => { }) it('does not update the maxId when the noIdUpdate flag is set', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const secondStatus = makeMockStatus({id: '2'}) @@ -110,7 +109,7 @@ describe('The Statuses module', () => { }) it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const nonVisibleStatus = makeMockStatus({id: '1'}) const status = makeMockStatus({id: '3'}) const statusTwo = makeMockStatus({id: '2'}) @@ -130,7 +129,7 @@ describe('The Statuses module', () => { }) it('splits retweets from their status and links them', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const retweet = makeMockStatus({id: '2', type: 'retweet'}) const modStatus = makeMockStatus({id: '1', text: 'something else'}) @@ -155,7 +154,7 @@ describe('The Statuses module', () => { }) it('replaces existing statuses with the same id', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const modStatus = makeMockStatus({id: '1', text: 'something else'}) @@ -172,7 +171,7 @@ describe('The Statuses module', () => { }) it('replaces existing statuses with the same id, coming from a retweet', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const modStatus = makeMockStatus({id: '1', text: 'something else'}) const retweet = makeMockStatus({id: '2', type: 'retweet'}) @@ -193,7 +192,7 @@ describe('The Statuses module', () => { }) it('handles favorite actions', () => { - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const favorite = { @@ -241,7 +240,7 @@ describe('The Statuses module', () => { }) it('keeps userId when clearing user timeline', () => { - const state = cloneDeep(defaultState) + const state = defaultState() state.timelines.user.userId = 123 mutations.clearTimeline(state, { timeline: 'user' }) @@ -252,7 +251,7 @@ describe('The Statuses module', () => { describe('notifications', () => { it('removes a notification when the notice gets removed', () => { const user = { id: '1' } - const state = cloneDeep(defaultState) + const state = defaultState() const status = makeMockStatus({id: '1'}) const otherStatus = makeMockStatus({id: '3'}) const mentionedStatus = makeMockStatus({id: '2'}) From bbe1821be7c098f3a23bd0dd54b4ec89a159e9c1 Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Thu, 28 Feb 2019 14:44:43 -0500 Subject: [PATCH 13/56] #392: update object.entries --- src/modules/statuses.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 2af23a9d..028053cf 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -327,8 +327,8 @@ export const mutations = { }, resetStatuses (state) { const emptyState = defaultState() - Object.keys(state).forEach(key => { - state[key] = emptyState[key] + Object.entries(emptyState).forEach(([key, value]) => { + state[key] = value }) }, clearTimeline (state, { timeline }) { From bbab1b1dc686103cc1ffbb3efd24b4a91ae7969d Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Thu, 28 Feb 2019 21:03:35 -0500 Subject: [PATCH 14/56] #346: Hyperlink is not included --- src/components/post_status_form/post_status_form.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 5085570b..40d24b97 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -30,7 +30,9 @@ @drop="fileDrop" @dragover.prevent="fileDrag" @input="resize" - @paste="paste"> + @paste="paste" + :disabled="posting" + > </textarea> <div class="visibility-tray"> <span class="text-format" v-if="formattingOptionsEnabled"> From f3f9fbe3027bf5284adc05387e89d904884490b2 Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Fri, 1 Mar 2019 11:59:50 -0500 Subject: [PATCH 15/56] #392: clean up notification stopping section --- src/modules/statuses.js | 6 ++++++ src/modules/users.js | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 028053cf..77a8ec72 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -401,6 +401,12 @@ const statuses = { setNotificationsSilence ({ rootState, commit }, { value }) { commit('setNotificationsSilence', { value }) }, + stopFetchingNotifications ({ rootState, commit }) { + if (rootState.statuses.notifications.fetcherId) { + window.clearInterval(rootState.statuses.notifications.fetcherId) + } + commit('setNotificationFetcher', { fetcherId: null }) + }, deleteStatus ({ rootState, commit }, status) { commit('setDeleted', { status }) apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials }) diff --git a/src/modules/users.js b/src/modules/users.js index 84fe039b..0e1dd166 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -290,9 +290,7 @@ const users = { store.commit('setToken', false) store.dispatch('stopFetching', 'friends') store.commit('setBackendInteractor', backendInteractorService()) - if (store.rootState.statuses.notifications.fetcherId) { - window.clearInterval(store.rootState.statuses.notifications.fetcherId) - } + store.dispatch('stopFetchingNotifications') store.commit('resetStatuses') }, loginUser (store, accessToken) { From c26f32ed92d5d28512964d63926af2484cb7d495 Mon Sep 17 00:00:00 2001 From: jasper <jasper92341@hotmail.com> Date: Fri, 1 Mar 2019 10:20:25 -0800 Subject: [PATCH 16/56] Fix fetching error by tag --- .../backend_interactor_service/backend_interactor_service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index 6beb5847..2df61495 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -54,7 +54,7 @@ const backendInteractorService = (credentials) => { return apiService.denyUser({credentials, id}) } - const startFetching = ({timeline, store, userId = false, tag = false}) => { + const startFetching = ({timeline, store, userId = false, tag}) => { return timelineFetcherService.startFetching({timeline, store, credentials, userId, tag}) } From 81e89fed3d6cc247346611e1b58aae9d87667934 Mon Sep 17 00:00:00 2001 From: jasper <jasper92341@hotmail.com> Date: Fri, 1 Mar 2019 12:53:24 -0800 Subject: [PATCH 17/56] Fetch activites by last id --- src/modules/statuses.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 4002f282..96a3549d 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -262,7 +262,9 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us }) // Keep the visible statuses sorted - if (timeline) sortTimeline(timelineObject) + if (timeline) { + sortTimeline(timelineObject) + } } const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes }) => { From c4f8426349baaa7e804182e455126c9353b08744 Mon Sep 17 00:00:00 2001 From: Shpuld Shpludson <shp@cock.li> Date: Sat, 2 Mar 2019 16:35:38 +0000 Subject: [PATCH 18/56] Re-do status header a bit, add more consistent spacing to status --- src/App.scss | 26 +- src/components/attachment/attachment.vue | 2 +- src/components/gallery/gallery.vue | 3 + src/components/link-preview/link-preview.vue | 4 - src/components/status/status.js | 2 +- src/components/status/status.vue | 270 +++++++++++-------- src/i18n/en.json | 4 + src/i18n/fi.json | 4 + 8 files changed, 188 insertions(+), 127 deletions(-) diff --git a/src/App.scss b/src/App.scss index 7c6970c1..a0d1a804 100644 --- a/src/App.scss +++ b/src/App.scss @@ -628,6 +628,16 @@ nav { color: $fallback--faint; color: var(--faint, $fallback--faint); } + +.faint-link { + color: $fallback--faint; + color: var(--faint, $fallback--faint); + + &:hover { + text-decoration: underline; + } +} + @media all and (min-width: 800px) { .logo { opacity: 1 !important; @@ -661,6 +671,10 @@ nav { border-radius: var(--inputRadius, $fallback--inputRadius); } +.button-icon { + font-size: 1.2em; +} + @keyframes shakeError { 0% { transform: translateX(0); @@ -705,16 +719,6 @@ nav { margin: 0.5em 0 0.5em 0; } - .button-icon { - font-size: 1.2em; - } - - .status .status-actions { - div { - max-width: 4em; - } - } - .menu-button { display: block; margin-right: 0.8em; @@ -723,7 +727,7 @@ nav { .login-hint { text-align: center; - + @media all and (min-width: 801px) { display: none; } diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 7e972026..76affe2d 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -88,7 +88,7 @@ .attachment { position: relative; - margin: 0.5em 0.5em 0em 0em; + margin-top: 0.5em; align-self: flex-start; line-height: 0; diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue index 3f90caa9..2366ddf7 100644 --- a/src/components/gallery/gallery.vue +++ b/src/components/gallery/gallery.vue @@ -36,6 +36,9 @@ box-sizing: border-box; // to make failed images a bit more noticeable on chromium min-width: 2em; + &:last-child { + margin: 0; + } } .image-attachment { diff --git a/src/components/link-preview/link-preview.vue b/src/components/link-preview/link-preview.vue index e4a247c5..7394668c 100644 --- a/src/components/link-preview/link-preview.vue +++ b/src/components/link-preview/link-preview.vue @@ -24,10 +24,6 @@ cursor: pointer; overflow: hidden; - // TODO: clean up the random margins in attachments, this makes preview line - // up with attachments... - margin-right: 0.5em; - .card-image { flex-shrink: 0; width: 120px; diff --git a/src/components/status/status.js b/src/components/status/status.js index fab2fe62..fbbca6c4 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -23,7 +23,7 @@ const Status = { 'highlight', 'compact', 'replies', - 'noReplyLinks', + 'isPreview', 'noHeading', 'inlineExpanded' ], diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 3fc5b486..46919d7c 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -1,6 +1,6 @@ <template> <div class="status-el" v-if="!hideStatus" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]"> - <template v-if="muted && !noReplyLinks"> + <template v-if="muted && !isPreview"> <div class="media status container muted"> <small> <router-link :to="userProfileLink"> @@ -13,7 +13,7 @@ </template> <template v-else> <div v-if="retweet && !noHeading" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info"> - <UserAvatar v-if="retweet" :betterShadow="betterShadow" :src="statusoid.user.profile_image_url_original"/> + <UserAvatar class="media-left" v-if="retweet" :betterShadow="betterShadow" :src="statusoid.user.profile_image_url_original"/> <div class="media-body faint"> <span class="user-name"> <router-link v-if="retweeterHtml" :to="retweeterProfileLink" v-html="retweeterHtml"/> @@ -31,57 +31,69 @@ </router-link> </div> <div class="status-body"> - <div class="usercard media-body" v-if="userExpanded"> + <div class="usercard" v-if="userExpanded"> <user-card-content :user="status.user" :switcher="false"></user-card-content> </div> - <div v-if="!noHeading" class="media-body container media-heading"> - <div class="media-heading-left"> - <div class="name-and-links"> + <div v-if="!noHeading" class="media-heading"> + <div class="heading-name-row"> + <div class="name-and-account-name"> <h4 class="user-name" v-if="status.user.name_html" v-html="status.user.name_html"></h4> <h4 class="user-name" v-else>{{status.user.name}}</h4> - <span class="links"> - <router-link :to="userProfileLink"> - {{status.user.screen_name}} - </router-link> - <span v-if="isReply" class="faint reply-info"> - <i class="icon-right-open"></i> - <router-link :to="replyProfileLink"> - {{replyToName}} - </router-link> - </span> - <a v-if="isReply && !noReplyLinks" href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)" :aria-label="$t('tool_tip.reply')"> - <i class="button-icon icon-reply" @mouseenter="replyEnter(status.in_reply_to_status_id, $event)" @mouseout="replyLeave()"></i> + <router-link class="account-name" :to="userProfileLink"> + {{status.user.screen_name}} + </router-link> + </div> + + <span class="heading-right"> + <router-link class="timeago faint-link" :to="{ name: 'conversation', params: { id: status.id } }"> + <timeago :since="status.created_at" :auto-update="60"></timeago> + </router-link> + <div class="button-icon visibility-icon" v-if="status.visibility"> + <i :class="visibilityIcon(status.visibility)" :title="status.visibility | capitalize"></i> + </div> + <a :href="status.external_url" target="_blank" v-if="!status.is_local && !isPreview" class="source_url" title="Source"> + <i class="button-icon icon-link-ext-alt"></i> + </a> + <template v-if="expandable && !isPreview"> + <a href="#" @click.prevent="toggleExpanded" title="Expand"> + <i class="button-icon icon-plus-squared"></i> </a> + </template> + <a href="#" @click.prevent="toggleMute" v-if="unmuted"><i class="button-icon icon-eye-off"></i></a> + </span> + </div> + + <div class="heading-reply-row"> + <div v-if="isReply" class="reply-to-and-accountname"> + <a class="reply-to" + href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)" + :aria-label="$t('tool_tip.reply')" + @mouseenter.prevent.stop="replyEnter(status.in_reply_to_status_id, $event)" + @mouseleave.prevent.stop="replyLeave()" + > + <i class="button-icon icon-reply" v-if="!isPreview"></i> + <span class="faint-link reply-to-text">{{$t('status.reply_to')}}</span> + </a> + <router-link :to="replyProfileLink"> + {{replyToName}} + </router-link> + <span class="faint replies-separator" v-if="replies.length"> + - </span> </div> - <h4 class="replies" v-if="inConversation && !noReplyLinks"> - <small v-if="replies.length">Replies:</small> - <small class="reply-link" v-bind:key="reply.id" v-for="reply in replies"> - <a href="#" @click.prevent="gotoOriginal(reply.id)" @mouseenter="replyEnter(reply.id, $event)" @mouseout="replyLeave()">{{reply.name}} </a> - </small> - </h4> - </div> - <div class="media-heading-right"> - <router-link class="timeago" :to="{ name: 'conversation', params: { id: status.id } }"> - <timeago :since="status.created_at" :auto-update="60"></timeago> - </router-link> - <div class="button-icon visibility-icon" v-if="status.visibility"> - <i :class="visibilityIcon(status.visibility)" :title="status.visibility | capitalize"></i> + <div class="replies" v-if="inConversation && !isPreview"> + <span class="faint" v-if="replies.length">{{$t('status.replies_list')}}</span> + <span class="reply-link faint" v-for="reply in replies"> + <a href="#" @click.prevent="gotoOriginal(reply.id)" @mouseenter="replyEnter(reply.id, $event)" @mouseout="replyLeave()">{{reply.name}}</a> + </span> </div> - <a :href="status.external_url" target="_blank" v-if="!status.is_local" class="source_url" title="Source"> - <i class="button-icon icon-link-ext-alt"></i> - </a> - <template v-if="expandable"> - <a href="#" @click.prevent="toggleExpanded" title="Expand"> - <i class="button-icon icon-plus-squared"></i> - </a> - </template> - <a href="#" @click.prevent="toggleMute" v-if="unmuted"><i class="button-icon icon-eye-off"></i></a> </div> + + </div> <div v-if="showPreview" class="status-preview-container"> - <status class="status-preview" v-if="preview" :noReplyLinks="true" :statusoid="preview" :compact=true></status> + <status class="status-preview" v-if="preview" :isPreview="true" :statusoid="preview" :compact=true></status> <div class="status-preview status-preview-loading" v-else> <i class="icon-spin4 animate-spin"></i> </div> @@ -123,7 +135,7 @@ <link-preview :card="status.card" :size="attachmentSize" :nsfw="nsfwClickthrough" /> </div> - <div v-if="!noHeading && !noReplyLinks" class='status-actions media-body'> + <div v-if="!noHeading && !isPreview" class='status-actions media-body'> <div v-if="loggedIn"> <a href="#" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')"> <i class="button-icon icon-reply" :class="{'icon-reply-active': replying}"></i> @@ -147,6 +159,8 @@ <style lang="scss"> @import '../../_variables.scss'; +$status-margin: 0.75em; + .status-body { flex: 1; min-width: 0; @@ -202,13 +216,16 @@ } } +.media-left { + margin-right: $status-margin; +} + .status-el { hyphens: auto; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; border-left-width: 0px; - line-height: 18px; min-width: 0; border-color: $fallback--border; border-color: var(--border, $fallback--border); @@ -229,22 +246,34 @@ .media-body { flex: 1; padding: 0; - margin: 0 0 0.25em 0.8em; } .usercard { - margin-bottom: .7em + margin: 0; + margin-bottom: $status-margin; + } + + .user-name { + white-space: nowrap; + font-size: 14px; + overflow: hidden; + flex-shrink: 0; + max-width: 85%; + font-weight: bold; + + img { + width: 14px; + height: 14px; + vertical-align: middle; + object-fit: contain + } } .media-heading { - flex-wrap: nowrap; - line-height: 18px; - } - - .media-heading-left { padding: 0; vertical-align: bottom; flex-basis: 100%; + margin-bottom: 0.5em; a { display: inline-block; @@ -254,83 +283,102 @@ small { font-weight: lighter; } - h4 { - white-space: nowrap; - font-size: 14px; - margin-right: 0.25em; - overflow: hidden; - text-overflow: ellipsis; - } - .name-and-links { + + .heading-name-row { padding: 0; - flex: 1 0; display: flex; - flex-wrap: wrap; - align-items: baseline; + justify-content: space-between; + line-height: 18px; + + .name-and-account-name { + display: flex; + min-width: 0; + } .user-name { - margin-right: .45em; + flex-shrink: 1; + margin-right: 0.4em; + overflow: hidden; + text-overflow: ellipsis; + } - img { - width: 14px; - height: 14px; - vertical-align: middle; - object-fit: contain - } + .account-name { + min-width: 1.6em; + margin-right: 0.4em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1 1 0; } } - .links { + .heading-right { display: flex; + flex-shrink: 0; + } + + .timeago { + margin-right: 0.2em; + } + + .heading-reply-row { + align-content: baseline; font-size: 12px; - color: $fallback--link; - color: var(--link, $fallback--link); + line-height: 18px; max-width: 100%; + display: flex; + flex-wrap: wrap; + align-items: stretch; + a { max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } - & > span { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - & > a:last-child { - flex-shrink: 0; + } + + .reply-to-and-accountname { + display: flex; + height: 18px; + margin-right: 0.5em; + overflow: hidden; + max-width: 100%; + .icon-reply { + transform: scaleX(-1); } } + .reply-info { display: flex; } + + .reply-to { + display: flex; + } + + .reply-to-text { + overflow: hidden; + text-overflow: ellipsis; + margin: 0 0.4em 0 0.2em; + } + + .replies-separator { + margin-left: 0.4em; + } + .replies { - line-height: 16px; - } - .reply-link { - margin-right: 0.2em; - } - } - - .media-heading-right { - display: inline-flex; - flex-shrink: 0; - flex-wrap: nowrap; - margin-left: .25em; - align-self: baseline; - - .timeago { - margin-right: 0.2em; + line-height: 18px; font-size: 12px; - align-self: last baseline; + display: flex; + flex-wrap: wrap; + & > * { + margin-right: 0.4em; + } } - > * { - margin-left: 0.2em; - } - a:hover i { - color: $fallback--text; - color: var(--text, $fallback--text); + .reply-link { + height: 17px; } } @@ -366,8 +414,8 @@ } .status-content { - margin-right: 0.5em; font-family: var(--postFont, sans-serif); + line-height: 1.4em; img, video { max-width: 100%; @@ -390,9 +438,11 @@ } p { - margin: 0; - margin-top: 0.2em; - margin-bottom: 0.5em; + margin: 0 0 1em 0; + } + + p:last-child { + margin: 0 0 0 0; } h1 { @@ -417,7 +467,7 @@ } .retweet-info { - padding: 0.4em 0.6em 0 0.6em; + padding: 0.4em $status-margin; margin: 0; .avatar.still-image { @@ -488,10 +538,10 @@ .status-actions { width: 100%; display: flex; + margin-top: $status-margin; div, favorite-button { - padding-top: 0.25em; - max-width: 6em; + max-width: 4em; flex: 1; } } @@ -517,9 +567,9 @@ .status { display: flex; - padding: 0.6em; + padding: $status-margin; &.is-retweet { - padding-top: 0.1em; + padding-top: 0; } } diff --git a/src/i18n/en.json b/src/i18n/en.json index c3756374..c5a4a90d 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -360,6 +360,10 @@ "no_more_statuses": "No more statuses", "no_statuses": "No statuses" }, + "status": { + "reply_to": "Reply to", + "replies_list": "Replies:" + }, "user_card": { "approve": "Approve", "block": "Block", diff --git a/src/i18n/fi.json b/src/i18n/fi.json index c7a25fe1..4f0ffb4b 100644 --- a/src/i18n/fi.json +++ b/src/i18n/fi.json @@ -221,6 +221,10 @@ "up_to_date": "Ajantasalla", "no_more_statuses": "Ei enempää viestejä" }, + "status": { + "reply_to": "Vastaus", + "replies_list": "Vastaukset:" + }, "user_card": { "approve": "Hyväksy", "block": "Estä", From a884bbb6a6adba779957a1fc1f3b254842df5f97 Mon Sep 17 00:00:00 2001 From: Tirifto <tirifto@posteo.cz> Date: Sat, 2 Mar 2019 17:50:59 +0100 Subject: [PATCH 19/56] Update and complete the Esperanto translation --- src/i18n/eo.json | 345 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 324 insertions(+), 21 deletions(-) diff --git a/src/i18n/eo.json b/src/i18n/eo.json index ed4b50e3..2438b4d5 100644 --- a/src/i18n/eo.json +++ b/src/i18n/eo.json @@ -2,118 +2,421 @@ "chat": { "title": "Babilejo" }, + "features_panel": { + "chat": "Babilejo", + "gopher": "Gopher", + "media_proxy": "Aŭdvidaĵa prokurilo", + "scope_options": "Agordoj de amplekso", + "text_limit": "Teksta limo", + "title": "Funkcioj", + "who_to_follow": "Kiun aboni" + }, "finder": { "error_fetching_user": "Eraro alportante uzanton", "find_user": "Trovi uzanton" }, "general": { "apply": "Apliki", - "submit": "Sendi" + "submit": "Sendi", + "more": "Pli", + "generic_error": "Eraro okazis", + "optional": "Malnepra" + }, + "image_cropper": { + "crop_picture": "Tondi bildon", + "save": "Konservi", + "cancel": "Nuligi" }, "login": { - "login": "Ensaluti", - "logout": "Elsaluti", + "login": "Saluti", + "description": "Saluti per OAuth", + "logout": "Adiaŭi", "password": "Pasvorto", "placeholder": "ekz. lain", "register": "Registriĝi", - "username": "Salutnomo" + "username": "Salutnomo", + "hint": "Salutu por partopreni la diskutadon" + }, + "media_modal": { + "previous": "Antaŭa", + "next": "Sekva" }, "nav": { + "about": "Pri", + "back": "Reen", "chat": "Loka babilejo", + "friend_requests": "Abonaj petoj", "mentions": "Mencioj", + "dms": "Rektaj mesaĝoj", "public_tl": "Publika tempolinio", "timeline": "Tempolinio", - "twkn": "La tuta konata reto" + "twkn": "La tuta konata reto", + "user_search": "Serĉi uzantojn", + "who_to_follow": "Kiun aboni", + "preferences": "Agordoj" }, "notifications": { + "broken_favorite": "Nekonata stato, serĉante ĝin…", "favorited_you": "ŝatis vian staton", "followed_you": "ekabonis vin", + "load_older": "Enlegi pli malnovajn sciigojn", "notifications": "Sciigoj", "read": "Legite!", - "repeated_you": "ripetis vian staton" + "repeated_you": "ripetis vian staton", + "no_more_notifications": "Neniuj pliaj sciigoj" }, "post_status": { + "new_status": "Afiŝi novan staton", + "account_not_locked_warning": "Via konto ne estas {0}. Iu ajn povas vin aboni por vidi viajn afiŝoj nur por abonantoj.", + "account_not_locked_warning_link": "ŝlosita", + "attachments_sensitive": "Marki kunsendaĵojn kiel konsternajn", + "content_type": { + "plain_text": "Plata teksto" + }, + "content_warning": "Temo (malnepra)", "default": "Ĵus alvenis al la Universala Kongreso!", - "posting": "Afiŝante" + "direct_warning": "Ĉi tiu afiŝo estos videbla nur por ĉiuj menciitaj uzantoj.", + "posting": "Afiŝante", + "scope": { + "direct": "Rekta – Afiŝi nur al menciitaj uzantoj", + "private": "Nur abonantoj – Afiŝi nur al abonantoj", + "public": "Publika – Afiŝi al publikaj tempolinioj", + "unlisted": "Nelistigita – Ne afiŝi al publikaj tempolinioj" + } }, "registration": { "bio": "Priskribo", "email": "Retpoŝtadreso", "fullname": "Vidiga nomo", "password_confirm": "Konfirmo de pasvorto", - "registration": "Registriĝo" + "registration": "Registriĝo", + "token": "Invita ĵetono", + "captcha": "TESTO DE HOMECO", + "new_captcha": "Alklaku la bildon por akiri novan teston", + "username_placeholder": "ekz. lain", + "fullname_placeholder": "ekz. Lain Iwakura", + "bio_placeholder": "ekz.\nSaluton, mi estas Lain\nMi estas animea knabino vivante en Japanujo. Eble vi konas min de la retejo « Wired ».", + "validations": { + "username_required": "ne povas resti malplena", + "fullname_required": "ne povas resti malplena", + "email_required": "ne povas resti malplena", + "password_required": "ne povas resti malplena", + "password_confirmation_required": "ne povas resti malplena", + "password_confirmation_match": "samu la pasvorton" + } }, "settings": { + "app_name": "Nomo de aplikaĵo", "attachmentRadius": "Kunsendaĵoj", "attachments": "Kunsendaĵoj", - "autoload": "Ŝalti memfaran ŝarĝadon ĉe subo de paĝo", + "autoload": "Ŝalti memfaran enlegadon ĉe subo de paĝo", "avatar": "Profilbildo", "avatarAltRadius": "Profilbildoj (sciigoj)", "avatarRadius": "Profilbildoj", "background": "Fono", "bio": "Priskribo", + "blocks_tab": "Baroj", "btnRadius": "Butonoj", "cBlue": "Blua (Respondo, abono)", "cGreen": "Verda (Kunhavigo)", "cOrange": "Oranĝa (Ŝato)", "cRed": "Ruĝa (Nuligo)", + "change_password": "Ŝanĝi pasvorton", + "change_password_error": "Okazis eraro dum ŝanĝo de via pasvorto.", + "changed_password": "Pasvorto sukcese ŝanĝiĝis!", + "collapse_subject": "Maletendi afiŝojn kun temoj", + "composing": "Verkante", + "confirm_new_password": "Konfirmu novan pasvorton", "current_avatar": "Via nuna profilbildo", + "current_password": "Nuna pasvorto", "current_profile_banner": "Via nuna profila rubando", + "data_import_export_tab": "Enporto / Elporto de datenoj", + "default_vis": "Implicita videbleca amplekso", + "delete_account": "Forigi konton", + "delete_account_description": "Por ĉiam forigi vian konton kaj ĉiujn viajn mesaĝojn", + "delete_account_error": "Okazis eraro dum forigo de via kanto. Se tio daŭre okazados, bonvolu kontakti la administranton de via nodo.", + "delete_account_instructions": "Entajpu sube vian pasvorton por konfirmi forigon de konto.", + "avatar_size_instruction": "La rekomendata malpleja grando de profilbildoj estas 150×150 bilderoj.", + "export_theme": "Konservi antaŭagordon", "filtering": "Filtrado", - "filtering_explanation": "Ĉiuj statoj kun tiuj ĉi vortoj silentiĝos, po unu linie", + "filtering_explanation": "Ĉiuj statoj kun tiuj ĉi vortoj silentiĝos, po unu linio", + "follow_export": "Abona elporto", + "follow_export_button": "Elporti viajn abonojn al CSV-dosiero", + "follow_export_processing": "Traktante; baldaŭ vi ricevos peton elŝuti la dosieron", "follow_import": "Abona enporto", "follow_import_error": "Eraro enportante abonojn", "follows_imported": "Abonoj enportiĝis! Traktado daŭros iom.", "foreground": "Malfono", + "general": "Ĝenerala", "hide_attachments_in_convo": "Kaŝi kunsendaĵojn en interparoloj", "hide_attachments_in_tl": "Kaŝi kunsendaĵojn en tempolinio", + "max_thumbnails": "Plej multa nombro da bildetoj po afiŝo", + "hide_isp": "Kaŝi nodo-propran breton", + "preload_images": "Antaŭ-enlegi bildojn", + "use_one_click_nsfw": "Malfermi konsternajn kunsendaĵojn per nur unu klako", + "hide_post_stats": "Kaŝi statistikon de afiŝoj (ekz. nombron da ŝatoj)", + "hide_user_stats": "Kaŝi statistikon de uzantoj (ekz. nombron da abonantoj)", + "hide_filtered_statuses": "Kaŝi filtritajn statojn", "import_followers_from_a_csv_file": "Enporti abonojn el CSV-dosiero", + "import_theme": "Enlegi antaŭagordojn", + "inputRadius": "Enigaj kampoj", + "checkboxRadius": "Markbutonoj", + "instance_default": "(implicita: {value})", + "instance_default_simple": "(implicita)", + "interface": "Fasado", + "interfaceLanguage": "Lingvo de fasado", + "invalid_theme_imported": "La elektita dosiero ne estas subtenata haŭto de Pleromo. Neniuj ŝanĝoj al via haŭto okazis.", + "limited_availability": "Nehavebla en via foliumilo", "links": "Ligiloj", + "lock_account_description": "Limigi vian konton al nur abonantoj aprobitaj", + "loop_video": "Ripetadi filmojn", + "loop_video_silent_only": "Ripetadi nur filmojn sen sono (ekz. la \"GIF-ojn\" de Mastodon)", + "mutes_tab": "Silentigoj", + "play_videos_in_modal": "Ludi filmojn rekte en la aŭdvidaĵa spektilo", + "use_contain_fit": "Ne tondi la kunsendaĵon en bildetoj", "name": "Nomo", "name_bio": "Nomo kaj priskribo", + "new_password": "Nova pasvorto", + "notification_visibility": "Montrotaj specoj de sciigoj", + "notification_visibility_follows": "Abonoj", + "notification_visibility_likes": "Ŝatoj", + "notification_visibility_mentions": "Mencioj", + "notification_visibility_repeats": "Ripetoj", + "no_rich_text_description": "Forigi riĉtekstajn formojn de ĉiuj afiŝoj", + "no_blocks": "Neniuj baroj", + "no_mutes": "Neniuj silentigoj", + "hide_follows_description": "Ne montri kiun mi sekvas", + "hide_followers_description": "Ne montri kiu min sekvas", + "show_admin_badge": "Montri la insignon de administranto en mia profilo", + "show_moderator_badge": "Montri la insignon de kontrolanto en mia profilo", "nsfw_clickthrough": "Ŝalti traklakan kaŝon de konsternaj kunsendaĵoj", - "panelRadius": "Paneloj", + "oauth_tokens": "Ĵetonoj de OAuth", + "token": "Ĵetono", + "refresh_token": "Ĵetono de novigo", + "valid_until": "Valida ĝis", + "revoke_token": "Senvalidigi", + "panelRadius": "Bretoj", + "pause_on_unfocused": "Paŭzigi elsendfluon kiam langeto ne estas fokusata", "presets": "Antaŭagordoj", "profile_background": "Profila fono", "profile_banner": "Profila rubando", - "radii_help": "Agordi fasadan rondigon de randoj (rastrumere)", - "reply_link_preview": "Ŝalti respond-ligilan antaŭvidon dum ŝvebo", + "profile_tab": "Profilo", + "radii_help": "Agordi fasadan rondigon de randoj (bildere)", + "replies_in_timeline": "Respondoj en tempolinio", + "reply_link_preview": "Ŝalti respond-ligilan antaŭvidon dum musa ŝvebo", + "reply_visibility_all": "Montri ĉiujn respondojn", + "reply_visibility_following": "Montri nur respondojn por mi aŭ miaj abonatoj", + "reply_visibility_self": "Montri nur respondojn por mi", + "saving_err": "Eraro dum konservo de agordoj", + "saving_ok": "Agordoj konserviĝis", + "security_tab": "Sekureco", + "scope_copy": "Kopii amplekson por respondo (rektaj mesaĝoj ĉiam kopiiĝas)", "set_new_avatar": "Agordi novan profilbildon", "set_new_profile_background": "Agordi novan profilan fonon", "set_new_profile_banner": "Agordi novan profilan rubandon", "settings": "Agordoj", - "stop_gifs": "Movi GIF-bildojn dum ŝvebo", + "subject_input_always_show": "Ĉiam montri teman kampon", + "subject_line_behavior": "Kopii temon por respondo", + "subject_line_email": "Kiel retpoŝto: \"re: temo\"", + "subject_line_mastodon": "Kiel Mastodon: kopii senŝanĝe", + "subject_line_noop": "Ne kopii", + "post_status_content_type": "Afiŝi specon de la enhavo de la stato", + "status_content_type_plain": "Plata teksto", + "stop_gifs": "Movi GIF-bildojn dum musa ŝvebo", "streaming": "Ŝalti memfaran fluigon de novaj afiŝoj ĉe la supro de la paĝo", "text": "Teksto", - "theme": "Etoso", - "theme_help": "Uzu deksesumajn kolorkodojn (#rrvvbb) por adapti vian koloran etoson.", + "theme": "Haŭto", + "theme_help": "Uzu deksesumajn kolorkodojn (#rrvvbb) por adapti vian koloran haŭton.", + "theme_help_v2_1": "Vi ankaŭ povas superagordi la kolorojn kaj travideblecon de kelkaj eroj per marko de la markbutono; uzu la butonon \"Vakigi ĉion\" por forigi ĉîujn superagordojn.", + "theme_help_v2_2": "Bildsimboloj sub kelkaj eroj estas indikiloj de kontrasto inter fono kaj teksto; muse ŝvebu por detalaj informoj. Bonvolu memori, ke la indikilo montras la plej malbonan okazeblon dum sia uzo.", "tooltipRadius": "Ŝpruchelpiloj/avertoj", - "user_settings": "Uzantaj agordoj" + "upload_a_photo": "Alŝuti foton", + "user_settings": "Agordoj de uzanto", + "values": { + "false": "ne", + "true": "jes" + }, + "notifications": "Sciigoj", + "enable_web_push_notifications": "Ŝalti retajn puŝajn sciigojn", + "style": { + "switcher": { + "keep_color": "Konservi kolorojn", + "keep_shadows": "Konservi ombrojn", + "keep_opacity": "Konservi maltravideblecon", + "keep_roundness": "Konservi rondecon", + "keep_fonts": "Konservi tiparojn", + "save_load_hint": "Elektebloj de \"konservi\" konservas la nuntempajn agordojn dum elektado aŭ enlegado de haŭtoj. Ĝi ankaŭ konservas tiujn agordojn dum elportado de haŭto. Kun ĉiuj markbutonoj nemarkitaj, elporto de la haŭto ĉion konservos.", + "reset": "Restarigi", + "clear_all": "Vakigi ĉion", + "clear_opacity": "Vakigi maltravideblecon" + }, + "common": { + "color": "Koloro", + "opacity": "Maltravidebleco", + "contrast": { + "hint": "Proporcio de kontrasto estas {ratio}, ĝi {level} {context}", + "level": { + "aa": "plenumas la gvidilon je nivelo AA (malpleja)", + "aaa": "plenumas la gvidilon je nivela AAA (rekomendita)", + "bad": "plenumas neniujn faciluzajn gvidilojn" + }, + "context": { + "18pt": "por granda (18pt+) teksto", + "text": "por teksto" + } + } + }, + "common_colors": { + "_tab_label": "Komunaj", + "main": "Komunaj koloroj", + "foreground_hint": "Vidu langeton \"Specialaj\" por pli detalaj agordoj", + "rgbo": "Bildsimboloj, emfazoj, insignoj" + }, + "advanced_colors": { + "_tab_label": "Specialaj", + "alert": "Averta fono", + "alert_error": "Eraro", + "badge": "Insigna fono", + "badge_notification": "Sciigo", + "panel_header": "Kapo de breto", + "top_bar": "Supra breto", + "borders": "Limoj", + "buttons": "Butonoj", + "inputs": "Enigaj kampoj", + "faint_text": "Malvigla teksto" + }, + "radii": { + "_tab_label": "Rondeco" + }, + "shadows": { + "_tab_label": "Ombro kaj lumo", + "component": "Ero", + "override": "Transpasi", + "shadow_id": "Ombro #{value}", + "blur": "Malklarigo", + "spread": "Vastigo", + "inset": "Internigo", + "hint": "Por ombroj vi ankaŭ povas uzi --variable kiel koloran valoron, por uzi variantojn de CSS3. Bonvolu rimarki, ke tiuokaze agordoj de maltravidebleco ne funkcios.", + "filter_hint": { + "always_drop_shadow": "Averto: ĉi tiu ombro ĉiam uzas {0} kiam la foliumilo ĝin subtenas.", + "drop_shadow_syntax": "{0} ne subtenas parametron {1} kaj ŝlosilvorton {2}.", + "avatar_inset": "Bonvolu rimarki, ke agordi ambaŭ internajn kaj eksterajn ombrojn por profilbildoj povas redoni neatenditajn rezultojn ĉe profilbildoj travideblaj.", + "spread_zero": "Ombroj kun vastigo > 0 aperos kvazaŭ ĝi estus fakte nulo", + "inset_classic": "Internaj ombroj uzos {0}" + }, + "components": { + "panel": "Breto", + "panelHeader": "Kapo de breto", + "topBar": "Supra breto", + "avatar": "Profilbildo de uzanto (en profila vido)", + "avatarStatus": "Profilbildo de uzanto (en afiŝa vido)", + "popup": "Ŝprucaĵoj", + "button": "Butono", + "buttonHover": "Butono (je ŝvebo)", + "buttonPressed": "Butono (premita)", + "buttonPressedHover": "Butono (premita je ŝvebo)", + "input": "Eniga kampo" + } + }, + "fonts": { + "_tab_label": "Tiparoj", + "help": "Elektu tiparon uzotan por eroj de la fasado. Por \"propra\" vi devas enigi la precizan nomon de tiparo tiel, kiel ĝi aperas en la sistemo", + "components": { + "interface": "Fasado", + "input": "Enigaj kampoj", + "post": "Teksto de afiŝo", + "postCode": "Egallarĝa teksto en afiŝo (riĉteksto)" + }, + "family": "Nomo de tiparo", + "size": "Grando (en bilderoj)", + "weight": "Pezo (graseco)", + "custom": "Propra" + }, + "preview": { + "header": "Antaŭrigardo", + "content": "Enhavo", + "error": "Ekzempla eraro", + "button": "Butono", + "text": "Kelko da pliaj {0} kaj {1}", + "mono": "enhavo", + "input": "Ĵus alvenis al la Universala Kongreso!", + "faint_link": "helpan manlibron", + "fine_print": "Legu nian {0} por nenion utilan ekscii!", + "header_faint": "Tio estas en ordo", + "checkbox": "Mi legetis la kondiĉojn de uzado", + "link": "bela eta ligil’" + } + } }, "timeline": { "collapse": "Maletendi", "conversation": "Interparolo", "error_fetching": "Eraro dum ĝisdatigo", "load_older": "Montri pli malnovajn statojn", - "repeated": "ripetata", + "no_retweet_hint": "Afiŝo estas markita kiel rekta aŭ nur por abonantoj, kaj ne eblas ĝin ripeti", + "repeated": "ripetita", "show_new": "Montri novajn", - "up_to_date": "Ĝisdata" + "up_to_date": "Ĝisdata", + "no_more_statuses": "Neniuj pliaj statoj", + "no_statuses": "Neniuj statoj" }, "user_card": { + "approve": "Aprobi", "block": "Bari", "blocked": "Barita!", + "deny": "Rifuzi", + "favorites": "Ŝatataj", "follow": "Aboni", + "follow_sent": "Peto sendiĝis!", + "follow_progress": "Petanta…", + "follow_again": "Ĉu sendi peton denove?", + "follow_unfollow": "Malaboni", "followees": "Abonatoj", "followers": "Abonantoj", "following": "Abonanta!", "follows_you": "Abonas vin!", + "its_you": "Tio estas vi!", + "media": "Aŭdvidaĵoj", "mute": "Silentigi", "muted": "Silentigitaj", "per_day": "tage", "remote_follow": "Fore aboni", - "statuses": "Statoj" + "statuses": "Statoj", + "unblock": "Malbari", + "unblock_progress": "Malbaranta…", + "block_progress": "Baranta…", + "unmute": "Malsilentigi", + "unmute_progress": "Malsilentiganta…", + "mute_progress": "Silentiganta…" }, "user_profile": { - "timeline_title": "Uzanta tempolinio" + "timeline_title": "Uzanta tempolinio", + "profile_does_not_exist": "Pardonu, ĉi tiu profilo ne ekzistas.", + "profile_loading_error": "Pardonu, eraro okazis dum enlegado de ĉi tiu profilo." + }, + "who_to_follow": { + "more": "Pli", + "who_to_follow": "Kiun aboni" + }, + "tool_tip": { + "media_upload": "Alŝuti aŭdvidaĵon", + "repeat": "Ripeti", + "reply": "Respondi", + "favorite": "Ŝati", + "user_settings": "Agordoj de uzanto" + }, + "upload":{ + "error": { + "base": "Alŝuto malsukcesis.", + "file_too_big": "Dosiero estas tro granda [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", + "default": "Reprovu pli poste" + }, + "file_size_units": { + "B": "B", + "KiB": "KiB", + "MiB": "MiB", + "GiB": "GiB", + "TiB": "TiB" + } } } From 93f5f94698d9bd833bff854920a24313e57c0145 Mon Sep 17 00:00:00 2001 From: Aditoo17 <aditoo@seznam.cz> Date: Sat, 2 Mar 2019 18:25:49 +0100 Subject: [PATCH 20/56] I18n: Add Czech translation --- src/i18n/cs.json | 427 +++++++++++++++++++++++++++++++++++++++++ src/i18n/messages.js | 1 + src/main.js | 3 +- static/timeago-cs.json | 10 + 4 files changed, 440 insertions(+), 1 deletion(-) create mode 100644 src/i18n/cs.json create mode 100644 static/timeago-cs.json diff --git a/src/i18n/cs.json b/src/i18n/cs.json new file mode 100644 index 00000000..6326032c --- /dev/null +++ b/src/i18n/cs.json @@ -0,0 +1,427 @@ +{ + "chat": { + "title": "Chat" + }, + "features_panel": { + "chat": "Chat", + "gopher": "Gopher", + "media_proxy": "Mediální proxy", + "scope_options": "Možnosti rozsahů", + "text_limit": "Textový limit", + "title": "Vlastnosti", + "who_to_follow": "Koho sledovat" + }, + "finder": { + "error_fetching_user": "Chyba při načítání uživatele", + "find_user": "Najít uživatele" + }, + "general": { + "apply": "Použít", + "submit": "Odeslat", + "more": "Více", + "generic_error": "Vyskytla se chyba", + "optional": "volitelné" + }, + "image_cropper": { + "crop_picture": "Oříznout obrázek", + "save": "Uložit", + "cancel": "Zrušit" + }, + "login": { + "login": "Přihlásit", + "description": "Přihlásit pomocí OAuth", + "logout": "Odhlásit", + "password": "Heslo", + "placeholder": "např. lain", + "register": "Registrovat", + "username": "Uživatelské jméno", + "hint": "Chcete-li se přidat do diskuze, přihlaste se" + }, + "media_modal": { + "previous": "Předchozí", + "next": "Další" + }, + "nav": { + "about": "O instanci", + "back": "Zpět", + "chat": "Místní chat", + "friend_requests": "Požadavky o sledování", + "mentions": "Zmínky", + "dms": "Přímé zprávy", + "public_tl": "Veřejná časová osa", + "timeline": "Časová osa", + "twkn": "Celá známá síť", + "user_search": "Hledání uživatelů", + "who_to_follow": "Koho sledovat", + "preferences": "Předvolby" + }, + "notifications": { + "broken_favorite": "Neznámý příspěvek, hledám jej…", + "favorited_you": "si oblíbil/a váš příspěvek", + "followed_you": "vás nyní sleduje", + "load_older": "Načíst starší oznámení", + "notifications": "Oznámení", + "read": "Číst!", + "repeated_you": "zopakoval/a váš příspěvek", + "no_more_notifications": "Žádná další oznámení" + }, + "post_status": { + "new_status": "Napsat nový příspěvek", + "account_not_locked_warning": "Váš účet není {0}. Kdokoliv vás může sledovat a vidět vaše příspěvky pouze pro sledující.", + "account_not_locked_warning_link": "uzamčen", + "attachments_sensitive": "Označovat přílohy jako citlivé", + "content_type": { + "plain_text": "Prostý text" + }, + "content_warning": "Předmět (volitelný)", + "default": "Právě jsem přistál v L.A.", + "direct_warning": "Tento příspěvek uvidí pouze všichni zmínění uživatelé.", + "posting": "Přispívání", + "scope": { + "direct": "Přímý - Poslat pouze zmíněným uživatelům", + "private": "Pouze pro sledující - Poslat pouze sledujícím", + "public": "Veřejný - Poslat na veřejné časové osy", + "unlisted": "Neuvedený - Neposlat na veřejné časové osy" + } + }, + "registration": { + "bio": "O vás", + "email": "E-mail", + "fullname": "Zobrazované jméno", + "password_confirm": "Potvrzení hesla", + "registration": "Registrace", + "token": "Token pozvánky", + "captcha": "CAPTCHA", + "new_captcha": "Kliknutím na obrázek získáte novou CAPTCHA", + "username_placeholder": "např. lain", + "fullname_placeholder": "např. Lain Iwakura", + "bio_placeholder": "např.\nNazdar, jsem Lain\nJsem anime dívka a žiji v příměstském Japonsku. Možná mě znáte z Wired.", + "validations": { + "username_required": "nemůže být prázdné", + "fullname_required": "nemůže být prázdné", + "email_required": "nemůže být prázdný", + "password_required": "nemůže být prázdné", + "password_confirmation_required": "nemůže být prázdné", + "password_confirmation_match": "musí být stejné jako heslo" + } + }, + "settings": { + "app_name": "Název aplikace", + "attachmentRadius": "Přílohy", + "attachments": "Přílohy", + "autoload": "Povolit automatické načítání při rolování dolů", + "avatar": "Avatar", + "avatarAltRadius": "Avatary (oznámení)", + "avatarRadius": "Avatary", + "background": "Pozadí", + "bio": "O vás", + "blocks_tab": "Blokování", + "btnRadius": "Tlačítka", + "cBlue": "Modrá (Odpovědět, sledovat)", + "cGreen": "Zelená (Zopakovat)", + "cOrange": "Oranžová (Oblíbit)", + "cRed": "Červená (Zrušit)", + "change_password": "Změnit heslo", + "change_password_error": "Při změně vašeho hesla se vyskytla chyba.", + "changed_password": "Heslo bylo úspěšně změněno!", + "collapse_subject": "Zabalit příspěvky s předměty", + "composing": "Komponování", + "confirm_new_password": "Potvrďte nové heslo", + "current_avatar": "Váš současný avatar", + "current_password": "Současné heslo", + "current_profile_banner": "Váš současný profilový banner", + "data_import_export_tab": "Import/export dat", + "default_vis": "Výchozí rozsah viditelnosti", + "delete_account": "Smazat účet", + "delete_account_description": "Trvale smaže váš účet a všechny vaše příspěvky.", + "delete_account_error": "Při mazání vašeho účtu nastala chyba. Pokud tato chyba bude trvat, kontaktujte prosím admministrátora vaší instance.", + "delete_account_instructions": "Pro potvrzení smazání účtu napište své heslo do pole níže.", + "avatar_size_instruction": "Doporučená minimální velikost pro avatarové obrázky je 150x150 pixelů.", + "export_theme": "Uložit přednastavení", + "filtering": "Filtrování", + "filtering_explanation": "Všechny příspěvky obsahující tato slova budou skryty. Napište jedno slovo na každý řádek", + "follow_export": "Export sledovaných", + "follow_export_button": "Exportovat vaše sledované do souboru CSV", + "follow_export_processing": "Zpracovávám, brzy si budete moci stáhnout váš soubor", + "follow_import": "Import sledovaných", + "follow_import_error": "Chyba při importování sledovaných", + "follows_imported": "Sledovaní importováni! Jejich zpracování bude chvilku trvat.", + "foreground": "Popředí", + "general": "Obecné", + "hide_attachments_in_convo": "Skrývat přílohy v konverzacích", + "hide_attachments_in_tl": "Skrývat přílohy v časové ose", + "max_thumbnails": "Maximální počet miniatur na příspěvek", + "hide_isp": "Skrýt panel specifický pro instanci", + "preload_images": "Přednačítat obrázky", + "use_one_click_nsfw": "Otevírat citlivé přílohy pouze jedním kliknutím", + "hide_post_stats": "Skrývat statistiky příspěvků (např. počet oblíbení)", + "hide_user_stats": "Skrývat statistiky uživatelů (např. počet sledujících)", + "hide_filtered_statuses": "Skrývat filtrované příspěvky", + "import_followers_from_a_csv_file": "Importovat sledované ze souboru CSV", + "import_theme": "Načíst přednastavení", + "inputRadius": "Vstupní pole", + "checkboxRadius": "Zaškrtávací pole", + "instance_default": "(výchozí: {value})", + "instance_default_simple": "(výchozí)", + "interface": "Rozhraní", + "interfaceLanguage": "Jazyk rozhraní", + "invalid_theme_imported": "Zvolený soubor není podporovaný motiv Pleroma. Nebyly provedeny žádné změny s vaším motivem.", + "limited_availability": "Nedostupné ve vašem prohlížeči", + "links": "Odkazy", + "lock_account_description": "Omezit váš účet pouze na schválené sledující", + "loop_video": "Opakovat videa", + "loop_video_silent_only": "Opakovat pouze videa beze zvuku (t.j. „GIFy“ na Mastodonu)", + "mutes_tab": "Ignorování", + "play_videos_in_modal": "Přehrávat videa přímo v prohlížeči médií", + "use_contain_fit": "Neořezávat přílohu v miniaturách", + "name": "Jméno", + "name_bio": "Jméno a popis", + "new_password": "Nové heslo", + "notification_visibility": "Typy oznámení k zobrazení", + "notification_visibility_follows": "Sledující", + "notification_visibility_likes": "Oblíbení", + "notification_visibility_mentions": "Zmínky", + "notification_visibility_repeats": "Zopakování", + "no_rich_text_description": "Odstranit ze všech příspěvků formátování textu", + "no_blocks": "Žádná blokování", + "no_mutes": "Žádná ignorování", + "hide_follows_description": "Nezobrazovat, koho sleduji", + "hide_followers_description": "Nezobrazovat, kdo mě sleduje", + "show_admin_badge": "Zobrazovat v mém profilu odznak administrátora", + "show_moderator_badge": "Zobrazovat v mém profilu odznak moderátora", + "nsfw_clickthrough": "Povolit prokliknutelné skrývání citlivých příloh", + "oauth_tokens": "Tokeny OAuth", + "token": "Token", + "refresh_token": "Obnovit token", + "valid_until": "Platný do", + "revoke_token": "Odvolat", + "panelRadius": "Panely", + "pause_on_unfocused": "Pozastavit streamování, pokud není záložka prohlížeče v soustředění", + "presets": "Přednastavení", + "profile_background": "Profilové pozadí", + "profile_banner": "Profilový banner", + "profile_tab": "Profil", + "radii_help": "Nastavit zakulacení rohů rozhraní (v pixelech)", + "replies_in_timeline": "Odpovědi v časové ose", + "reply_link_preview": "Povolit náhledy odkazu pro odpověď při přejetí myši", + "reply_visibility_all": "Zobrazit všechny odpovědiShow all replies", + "reply_visibility_following": "Zobrazit pouze odpovědi směřované na mě nebo uživatele, které sleduji", + "reply_visibility_self": "Zobrazit pouze odpovědi směřované na mě", + "saving_err": "Chyba při ukládání nastavení", + "saving_ok": "Nastavení uložena", + "security_tab": "Bezpečnost", + "scope_copy": "Kopírovat rozsah při odpovídání (přímé zprávy jsou vždy kopírovány)", + "set_new_avatar": "Nastavit nový avatar", + "set_new_profile_background": "Nastavit nové profilové pozadí", + "set_new_profile_banner": "Nastavit nový profilový banner", + "settings": "Nastavení", + "subject_input_always_show": "Vždy zobrazit pole pro předmět", + "subject_line_behavior": "Kopírovat předmět při odpovídání", + "subject_line_email": "Jako u e-mailu: „re: předmět“", + "subject_line_mastodon": "Jako u Mastodonu: zkopírovat tak, jak je", + "subject_line_noop": "Nekopírovat", + "post_status_content_type": "Publikovat typ obsahu příspěvku", + "status_content_type_plain": "Prostý text", + "stop_gifs": "Přehrávat GIFy při přejetí myši", + "streaming": "Povolit automatické streamování nových příspěvků při rolování nahoru", + "text": "Text", + "theme": "Motiv", + "theme_help": "Použijte hexadecimální barevné kódy (#rrggbb) pro přizpůsobení vašeho barevného motivu.", + "theme_help_v2_1": "Zaškrtnutím pole můžete také přepsat barvy a průhlednost některých komponentů, pro smazání všech přednastavení použijte tlačítko „Smazat vše“.", + "theme_help_v2_2": "Ikony pod některými položkami jsou indikátory kontrastu pozadí/textu, pro detailní informace nad nimi přejeďte myší. Prosím berte na vědomí, že při používání kontrastu průhlednosti ukazují indikátory nejhorší možný případ.", + "tooltipRadius": "Popisky/upozornění", + "upload_a_photo": "Nahrát fotku", + "user_settings": "Uživatelská nastavení", + "values": { + "false": "ne", + "true": "ano" + }, + "notifications": "Oznámení", + "enable_web_push_notifications": "Povolit webová push oznámení", + "style": { + "switcher": { + "keep_color": "Ponechat barvy", + "keep_shadows": "Ponechat stíny", + "keep_opacity": "Ponechat průhlednost", + "keep_roundness": "Ponechat kulatost", + "keep_fonts": "Keep fonts", + "save_load_hint": "Možnosti „Ponechat“ dočasně ponechávají aktuálně nastavené možností při volení či nahrávání motivů, také tyto možnosti ukládají při exportování motivu. Pokud není žádné pole zaškrtnuto, uloží export motivu všechno.", + "reset": "Resetovat", + "clear_all": "Vymazat vše", + "clear_opacity": "Vymazat průhlednost" + }, + "common": { + "color": "Barva", + "opacity": "Průhlednost", + "contrast": { + "hint": "Poměr kontrastu je {ratio}, {level} {context}", + "level": { + "aa": "splňuje směrnici úrovně AA (minimální)", + "aaa": "splňuje směrnici úrovně AAA (doporučováno)", + "bad": "nesplňuje žádné směrnice přístupnosti" + }, + "context": { + "18pt": "pro velký (18+ bodů) text", + "text": "pro text" + } + } + }, + "common_colors": { + "_tab_label": "Obvyklé", + "main": "Obvyklé barvy", + "foreground_hint": "Pro detailnější kontrolu viz záložka „Pokročilé“", + "rgbo": "Ikony, odstíny, odznaky" + }, + "advanced_colors": { + "_tab_label": "Pokročilé", + "alert": "Pozadí upozornění", + "alert_error": "Chyba", + "badge": "Pozadí odznaků", + "badge_notification": "Oznámení", + "panel_header": "Záhlaví panelu", + "top_bar": "Vrchní pruh", + "borders": "Okraje", + "buttons": "Tlačítka", + "inputs": "Vstupní pole", + "faint_text": "Vybledlý text" + }, + "radii": { + "_tab_label": "Kulatost" + }, + "shadows": { + "_tab_label": "Stín a osvětlení", + "component": "Komponent", + "override": "Přepsat", + "shadow_id": "Stín #{value}", + "blur": "Rozmazání", + "spread": "Rozsah", + "inset": "Vsazení", + "hint": "Pro stíny můžete také použít --variable jako hodnotu barvy pro použití proměnných CSS3. Prosím berte na vědomí, že nastavení průhlednosti v tomto případě nebude fungovat.", + "filter_hint": { + "always_drop_shadow": "Varování, tento stín vždy používá {0}, když to prohlížeč podporuje.", + "drop_shadow_syntax": "{0} nepodporuje parametr {1} a klíčové slovo {2}.", + "avatar_inset": "Prosím berte na vědomí, že kombinování vsazených i nevsazených stínů u avatarů může u průhledných avatarů dát neočekávané výsledky.", + "spread_zero": "Stíny s rozsahem > 0 se zobrazí, jako kdyby byl rozsah nastaven na nulu", + "inset_classic": "Vsazené stíny budou používat {0}" + }, + "components": { + "panel": "Panel", + "panelHeader": "Záhlaví panelu", + "topBar": "Vrchní pruh", + "avatar": "Avatar uživatele (v zobrazení profilu)", + "avatarStatus": "Avatar uživatele (v zobrazení příspěvku)", + "popup": "Vyskakovací okna a popisky", + "button": "Tlačítko", + "buttonHover": "Tlačítko (přejetí myši)", + "buttonPressed": "Tlačítko (stisknuto)", + "buttonPressedHover": "Button (stisknuto+přejetí myši)", + "input": "Vstupní pole" + } + }, + "fonts": { + "_tab_label": "Písma", + "help": "Zvolte písmo, které bude použito pro prvky rozhraní. U možnosti „vlastní“ musíte zadat přesný název písma tak, jak se zobrazuje v systému.", + "components": { + "interface": "Rozhraní", + "input": "Vstupní pole", + "post": "Text příspěvků", + "postCode": "Neproporcionální text v příspěvku (formátovaný text)" + }, + "family": "Název písma", + "size": "Velikost (v pixelech)", + "weight": "Tloušťka", + "custom": "Vlastní" + }, + "preview": { + "header": "Náhled", + "content": "Obsah", + "error": "Příklad chyby", + "button": "Tlačítko", + "text": "Spousta dalšího {0} a {1}", + "mono": "obsahu", + "input": "Just landed in L.A.", + "faint_link": "pomocný manuál", + "fine_print": "Přečtěte si náš {0} a nenaučte se nic užitečného!", + "header_faint": "Tohle je v pohodě", + "checkbox": "Pročetl/a jsem podmínky používání", + "link": "hezký malý odkaz" + } + } + }, + "timeline": { + "collapse": "Zabalit", + "conversation": "Konverzace", + "error_fetching": "Chyba při načítání aktualizací", + "load_older": "Načíst starší příspěvky", + "no_retweet_hint": "Příspěvek je označen jako pouze pro sledující či přímý a nemůže být zopakován", + "repeated": "zopakoval/a", + "show_new": "Zobrazit nové", + "up_to_date": "Aktuální", + "no_more_statuses": "Žádné další příspěvky", + "no_statuses": "Žádné příspěvky" + }, + "status": { + "reply_to": "Odpovědět uživateli", + "replies_list": "Odpovědi:" + }, + + "user_card": { + "approve": "Schválit", + "block": "Blokovat", + "blocked": "Blokován/a!", + "deny": "Zamítnout", + "favorites": "Oblíbené", + "follow": "Sledovat", + "follow_sent": "Požadavek odeslán!", + "follow_progress": "Odeslílám požadavek…", + "follow_again": "Odeslat požadavek znovu?", + "follow_unfollow": "Přestat sledovat", + "followees": "Sledovaní", + "followers": "Sledující", + "following": "Sledujete!", + "follows_you": "Sleduje vás!", + "its_you": "Jste to vy!", + "media": "Média", + "mute": "Ignorovat", + "muted": "Ignorován/a", + "per_day": "za den", + "remote_follow": "Vzdálené sledování", + "statuses": "Příspěvky", + "unblock": "Odblokovat", + "unblock_progress": "Odblokuji…", + "block_progress": "Blokuji…", + "unmute": "Přestat ignorovat", + "unmute_progress": "Ruším ignorování…", + "mute_progress": "Ignoruji…" + }, + "user_profile": { + "timeline_title": "Uživatelská časová osa", + "profile_does_not_exist": "Omlouváme se, tento profil neexistuje.", + "profile_loading_error": "Omlouváme se, při načítání tohoto profilu se vyskytla chyba." + }, + "who_to_follow": { + "more": "Více", + "who_to_follow": "Koho sledovat" + }, + "tool_tip": { + "media_upload": "Nahrát média", + "repeat": "Zopakovat", + "reply": "Odpovědět", + "favorite": "Oblíbit", + "user_settings": "Uživatelské nastavení" + }, + "upload":{ + "error": { + "base": "Nahrávání selhalo.", + "file_too_big": "Soubor je úříliš velký [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", + "default": "Zkuste to znovu později" + }, + "file_size_units": { + "B": "B", + "KiB": "KiB", + "MiB": "MiB", + "GiB": "GiB", + "TiB": "TiB" + } + } +} diff --git a/src/i18n/messages.js b/src/i18n/messages.js index 1adadc32..ab697948 100644 --- a/src/i18n/messages.js +++ b/src/i18n/messages.js @@ -10,6 +10,7 @@ const messages = { ar: require('./ar.json'), ca: require('./ca.json'), + cs: require('./cs.json'), de: require('./de.json'), en: require('./en.json'), eo: require('./eo.json'), diff --git a/src/main.js b/src/main.js index 2844194e..a3265e3a 100644 --- a/src/main.js +++ b/src/main.js @@ -30,8 +30,9 @@ const currentLocale = (window.navigator.language || 'en').split('-')[0] Vue.use(Vuex) Vue.use(VueRouter) Vue.use(VueTimeago, { - locale: currentLocale === 'ja' ? 'ja' : 'en', + locale: currentLocale === 'cs' ? 'cs' : currentLocale === 'ja' ? 'ja' : 'en', locales: { + 'cs': require('../static/timeago-cs.json'), 'en': require('../static/timeago-en.json'), 'ja': require('../static/timeago-ja.json') } diff --git a/static/timeago-cs.json b/static/timeago-cs.json new file mode 100644 index 00000000..697a0397 --- /dev/null +++ b/static/timeago-cs.json @@ -0,0 +1,10 @@ +[ + "teď", + ["%s s", "%s s"], + ["%s min", "%s min"], + ["%s h", "%s h"], + ["%s d", "%s d"], + ["%s týd", "%s týd"], + ["%s měs", "%s měs"], + ["%s r", "%s l"] +] From 94b0321c719fce2614eecc90a1193609922a42d9 Mon Sep 17 00:00:00 2001 From: taehoon <th.dev91@gmail.com> Date: Sat, 2 Mar 2019 12:58:17 -0500 Subject: [PATCH 21/56] Update font-size of username in UserCardContent component --- src/components/user_card_content/user_card_content.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index 689b9ec6..702c3385 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -222,6 +222,7 @@ overflow: hidden; flex: 1 1 auto; margin-right: 1em; + font-size: 15px; img { object-fit: contain; From 6841f516fcd2c97c25b48c1ca20791808821cfd0 Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Sat, 2 Mar 2019 20:39:04 +0200 Subject: [PATCH 22/56] fix broken statuses --- src/components/status/status.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 46919d7c..ee6ef7da 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -77,13 +77,13 @@ <router-link :to="replyProfileLink"> {{replyToName}} </router-link> - <span class="faint replies-separator" v-if="replies.length"> + <span class="faint replies-separator" v-if="replies && replies.length"> - </span> </div> <div class="replies" v-if="inConversation && !isPreview"> - <span class="faint" v-if="replies.length">{{$t('status.replies_list')}}</span> - <span class="reply-link faint" v-for="reply in replies"> + <span class="faint" v-if="replies && replies.length">{{$t('status.replies_list')}}</span> + <span class="reply-link faint" v-if="replies" v-for="reply in replies"> <a href="#" @click.prevent="gotoOriginal(reply.id)" @mouseenter="replyEnter(reply.id, $event)" @mouseout="replyLeave()">{{reply.name}}</a> </span> </div> From c1ee7c028ba44244c923afac052b72ff61840da1 Mon Sep 17 00:00:00 2001 From: shpuld <shp@cock.li> Date: Sun, 3 Mar 2019 16:43:41 +0200 Subject: [PATCH 23/56] Fix bug in replies --- src/components/status/status.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 46919d7c..5c81fe27 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -77,7 +77,7 @@ <router-link :to="replyProfileLink"> {{replyToName}} </router-link> - <span class="faint replies-separator" v-if="replies.length"> + <span class="faint replies-separator" v-if="replies && replies.length"> - </span> </div> From 10711f904508fe2338c354637fd4ebd2e0abb4f0 Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Sun, 3 Mar 2019 12:15:55 -0500 Subject: [PATCH 24/56] #417: reset tab status when active user changes --- src/components/tab_switcher/tab_switcher.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/tab_switcher/tab_switcher.js b/src/components/tab_switcher/tab_switcher.js index 423df258..1de936e8 100644 --- a/src/components/tab_switcher/tab_switcher.js +++ b/src/components/tab_switcher/tab_switcher.js @@ -10,6 +10,11 @@ export default Vue.component('tab-switcher', { active: this.$slots.default.findIndex(_ => _.tag) } }, + watch: { + $route () { + this.activateTab(0) + } + }, methods: { activateTab (index) { return () => { From 3d30ad1dda8f31960586625bb6a432d6b3adde8e Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Sun, 3 Mar 2019 12:53:01 -0500 Subject: [PATCH 25/56] #417: refresh tab on user profile only --- src/components/tab_switcher/tab_switcher.js | 6 ++++-- src/components/user_profile/user_profile.vue | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/tab_switcher/tab_switcher.js b/src/components/tab_switcher/tab_switcher.js index 1de936e8..03da8249 100644 --- a/src/components/tab_switcher/tab_switcher.js +++ b/src/components/tab_switcher/tab_switcher.js @@ -4,7 +4,7 @@ import './tab_switcher.scss' export default Vue.component('tab-switcher', { name: 'TabSwitcher', - props: ['renderOnlyFocused'], + props: ['refresh', 'renderOnlyFocused'], data () { return { active: this.$slots.default.findIndex(_ => _.tag) @@ -12,7 +12,9 @@ export default Vue.component('tab-switcher', { }, watch: { $route () { - this.activateTab(0) + if (this.refresh) { + this.active = 0 + } } }, methods: { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index a3d2825f..54f1b97b 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -6,7 +6,7 @@ :switcher="true" :selected="timeline.viewing" /> - <tab-switcher :renderOnlyFocused="true"> + <tab-switcher :refresh="true" :renderOnlyFocused="true"> <Timeline :label="$t('user_card.statuses')" :disabled="!user.statuses_count" From 5a0bb29f02349aed4a7948b963ff0a78d3975237 Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Sun, 3 Mar 2019 13:38:48 -0500 Subject: [PATCH 26/56] #417: reset tab from the outside --- src/components/tab_switcher/tab_switcher.js | 9 +-------- src/components/user_profile/user_profile.js | 3 +++ src/components/user_profile/user_profile.vue | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/components/tab_switcher/tab_switcher.js b/src/components/tab_switcher/tab_switcher.js index 03da8249..423df258 100644 --- a/src/components/tab_switcher/tab_switcher.js +++ b/src/components/tab_switcher/tab_switcher.js @@ -4,19 +4,12 @@ import './tab_switcher.scss' export default Vue.component('tab-switcher', { name: 'TabSwitcher', - props: ['refresh', 'renderOnlyFocused'], + props: ['renderOnlyFocused'], data () { return { active: this.$slots.default.findIndex(_ => _.tag) } }, - watch: { - $route () { - if (this.refresh) { - this.active = 0 - } - } - }, methods: { activateTab (index) { return () => { diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 7708141c..cdf1cee9 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -141,6 +141,9 @@ const UserProfile = { } this.cleanUp() this.startUp() + }, + $route () { + this.$refs.tabSwitcher.activateTab(0)() } }, components: { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 54f1b97b..8090efa5 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -6,7 +6,7 @@ :switcher="true" :selected="timeline.viewing" /> - <tab-switcher :refresh="true" :renderOnlyFocused="true"> + <tab-switcher :renderOnlyFocused="true" ref="tabSwitcher"> <Timeline :label="$t('user_card.statuses')" :disabled="!user.statuses_count" From 3e16b5a2e02348b372ef1b281fc3305926f55ddf Mon Sep 17 00:00:00 2001 From: Exilat <quentinantonin@free.fr> Date: Sun, 3 Mar 2019 19:06:32 +0000 Subject: [PATCH 27/56] Update of the Occitan file. Not at 100% but more complete then what it was. --- src/i18n/oc.json | 234 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 173 insertions(+), 61 deletions(-) diff --git a/src/i18n/oc.json b/src/i18n/oc.json index db66bb98..261ab737 100644 --- a/src/i18n/oc.json +++ b/src/i18n/oc.json @@ -1,51 +1,82 @@ { "chat": { "title": "Messatjariá" + }, + "features_panel": { + "chat": "Chat", + "gopher": "Gopher", + "media_proxy": "Servidor mandatari mèdia", + "scope_options": "Nivèls de confidencialitat", + "text_limit": "Limita de tèxte", + "title": "Foncionalitats", + "who_to_follow": "Qual seguir" }, "finder": { - "error_fetching_user": "Error pendent la recèrca d’un utilizaire", + "error_fetching_user": "Error pendent la cèrca d’un utilizaire", "find_user": "Cercar un utilizaire" }, "general": { "apply": "Aplicar", - "submit": "Mandar" + "submit": "Mandar", + "more": "Mai", + "generic_error": "Una error s’es producha", + "optional": "opcional" + }, + "image_cropper": { + "crop_picture": "Talhar l’imatge", + "save": "Salvar", + "cancel": "Anullar" }, "login": { "login": "Connexion", + "description": "Connexion via OAuth", "logout": "Desconnexion", "password": "Senhal", "placeholder": "e.g. lain", "register": "Se marcar", - "username": "Nom d’utilizaire" + "username": "Nom d’utilizaire", + "hint": "Connectatz-vos per participar a la discutida" + }, + "media_modal": { + "previous": "Precedent", + "next": "Seguent" }, "nav": { + "about": "A prepaus", + "back": "Tornar", "chat": "Chat local", + "friend_requests": "Demandas de seguiment", "mentions": "Notificacions", + "dms": "Messatges privats", "public_tl": "Estatuts locals", "timeline": "Flux d’actualitat", "twkn": "Lo malhum conegut", - "friend_requests": "Demandas d'abonament" + "user_search": "Cèrca d’utilizaires", + "who_to_follow": "Qual seguir", + "preferences": "Preferéncias" }, "notifications": { + "broken_favorite": "Estatut desconegut, sèm a lo cercar...", "favorited_you": "a aimat vòstre estatut", "followed_you": "vos a seguit", + "load_older": "Cargar las notificaciones mai ancianas", "notifications": "Notficacions", - "read": "Legit !", + "read": "Legit !", "repeated_you": "a repetit vòstre estatut", - "broken_favorite": "Estatut desconegut, sèm a lo cercar...", - "load_older": "Cargar las notificaciones mai ancianas" + "no_more_notifications": "Pas mai de notificacions" }, "post_status": { - "content_warning": "Avís de contengut (opcional)", - "default": "Escrivètz aquí vòstre estatut.", - "posting": "Mandadís", + "new_status": "Publicar d’estatuts novèls", "account_not_locked_warning": "Vòstre compte es pas {0}. Qual que siá pòt vos seguir per veire vòstras publicacions destinadas pas qu'a vòstres seguidors.", "account_not_locked_warning_link": "clavat", "attachments_sensitive": "Marcar las pèças juntas coma sensiblas", "content_type": { "plain_text": "Tèxte brut" }, + "content_warning": "Avís de contengut (opcional)", + "default": "Escrivètz aquí vòstre estatut.", "direct_warning": "Aquesta publicacion serà pas que visibla pels utilizaires mencionats.", + "posting": "Mandadís", "scope": { "direct": "Dirècte - Publicar pels utilizaires mencionats solament", "private": "Seguidors solament - Publicar pels sols seguidors", @@ -59,9 +90,23 @@ "fullname": "Nom complèt", "password_confirm": "Confirmar lo senhal", "registration": "Inscripcion", - "token": "Geton de convidat" + "token": "Geton de convidat", + "captcha": "CAPTCHA", + "new_captcha": "Clicatz l’imatge per obténer una nòva captcha", + "username_placeholder": "e.g. lain", + "fullname_placeholder": "e.g. Lain Iwakura", + "bio_placeholder": "e.g.\nHi, Soi lo Lain\nSoi afocada d’animes e vivi al Japan. Benlèu que me coneissètz de the Wired.", + "validations": { + "username_required": "pòt pas èsser void", + "fullname_required": "pòt pas èsser void", + "email_required": "pòt pas èsser void", + "password_required": "pòt pas èsser void", + "password_confirmation_required": "pòt pas èsser void", + "password_confirmation_match": "deu èsser lo meteis senhal" + } }, "settings": { + "app_name": "Nom de l’aplicacion", "attachmentRadius": "Pèças juntas", "attachments": "Pèças juntas", "autoload": "Activar lo cargament automatic un còp arribat al cap de la pagina", @@ -70,6 +115,7 @@ "avatarRadius": "Avatars", "background": "Rèire plan", "bio": "Biografia", + "blocks_tab": "Blocatges", "btnRadius": "Botons", "cBlue": "Blau (Respondre, seguir)", "cGreen": "Verd (Repartajar)", @@ -78,15 +124,21 @@ "change_password": "Cambiar lo senhal", "change_password_error": "Una error s’es producha en cambiant lo senhal.", "changed_password": "Senhal corrèctament cambiat !", + "collapse_subject": "Replegar las publicacions amb de subjèctes", + "composing": "Escritura", "confirm_new_password": "Confirmatz lo nòu senhal", "current_avatar": "Vòstre avatar actual", "current_password": "Senhal actual", "current_profile_banner": "Bandièra actuala del perfil", + "data_import_export_tab": "Importar / Exportar las donadas", + "default_vis": "Nivèl de visibilitat per defaut", "delete_account": "Suprimir lo compte", "delete_account_description": "Suprimir vòstre compte e los messatges per sempre.", "delete_account_error": "Una error s’es producha en suprimir lo compte. S’aquò ten d’arribar mercés de contactar vòstre administrador d’instància.", "delete_account_instructions": "Picatz vòstre senhal dins lo camp tèxte çai-jos per confirmar la supression del compte.", - "filtering": "Filtre", + "avatar_size_instruction": "La talha minimum recomandada pels imatges d’avatar es 150x150 pixèls.", + "export_theme": "Enregistrar la preconfiguracion", + "filtering": "Filtratge", "filtering_explanation": "Totes los estatuts amb aqueles mots seràn en silenci, un mot per linha", "follow_export": "Exportar los abonaments", "follow_export_button": "Exportar vòstres abonaments dins un fichièr csv", @@ -95,63 +147,91 @@ "follow_import_error": "Error en important los seguidors", "follows_imported": "Seguidors importats. Lo tractament pòt trigar una estona.", "foreground": "Endavant", + "general": "General", "hide_attachments_in_convo": "Rescondre las pèças juntas dins las conversacions", "hide_attachments_in_tl": "Rescondre las pèças juntas", - "import_followers_from_a_csv_file": "Importar los seguidors d’un fichièr csv", - "inputRadius": "Camps tèxte", - "links": "Ligams", - "name": "Nom", - "name_bio": "Nom & Bio", - "new_password": "Nòu senhal", - "nsfw_clickthrough": "Activar lo clic per mostrar los imatges marcats coma pels adults o sensibles", - "panelRadius": "Panèls", - "presets": "Pre-enregistrats", - "profile_background": "Imatge de fons", - "profile_banner": "Bandièra del perfil", - "radii_help": "Configurar los caires arredondits de l’interfàcia (en pixèls)", - "reply_link_preview": "Activar l’apercebut en passar la mirga", - "set_new_avatar": "Cambiar l’avatar", - "set_new_profile_background": "Cambiar l’imatge de fons", - "set_new_profile_banner": "Cambiar de bandièra", - "settings": "Paramètres", - "stop_gifs": "Lançar los GIFs al subrevòl", - "streaming": "Activar lo cargament automatic dels novèls estatus en anar amont", - "text": "Tèxte", - "theme": "Tèma", - "theme_help": "Emplegatz los còdis de color hex (#rrggbb) per personalizar vòstre tèma de color.", - "tooltipRadius": "Astúcias/Alèrta", - "user_settings": "Paramètres utilizaire", - "collapse_subject": "Replegar las publicacions amb de subjèctes", - "data_import_export_tab": "Importar / Exportar las donadas", - "default_vis": "Nivèl de visibilitat per defaut", - "export_theme": "Enregistrar la preconfiguracion", - "general": "General", + "max_thumbnails": "Nombre maximum de vinhetas per publicacion", + "hide_isp": "Amagar lo panèl especial instància", + "preload_images": "Precargar los imatges", + "use_one_click_nsfw": "Dobrir las pèças juntas NSFW amb un clic", "hide_post_stats": "Amagar los estatistics de publicacion (ex. lo ombre de favorits)", "hide_user_stats": "Amagar las estatisticas de l’utilizaire (ex. lo nombre de seguidors)", + "hide_filtered_statuses": "Amagar los estatuts filtrats", + "import_followers_from_a_csv_file": "Importar los seguidors d’un fichièr csv", "import_theme": "Cargar un tèma", - "instance_default": "(defaut : {value})", + "inputRadius": "Camps tèxte", + "checkboxRadius": "Casas de marcar", + "instance_default": "(defaut : {value})", + "instance_default_simple": "(defaut)", + "interface": "Interfàcia", "interfaceLanguage": "Lenga de l’interfàcia", "invalid_theme_imported": "Lo fichièr seleccionat es pas un tèma Pleroma valid. Cap de cambiament es estat fach a vòstre tèma.", "limited_availability": "Pas disponible per vòstre navigador", + "links": "Ligams", "lock_account_description": "Limitar vòstre compte als seguidors acceptats solament", "loop_video": "Bocla vidèo", - "loop_video_silent_only": "Legir en bocla solament las vidèos sens son (coma los « Gifs » de Mastodon)", - "notification_visibility": "Tipes de notificacion de mostrar", + "loop_video_silent_only": "Legir en bocla solament las vidèos sens son (coma los « Gifs » de Mastodon)", + "mutes_tab": "Agamats", + "play_videos_in_modal": "Legir las vidèoas dirèctament dins la visualizaira mèdia", + "use_contain_fit": "Talhar pas las pèças juntas per las vinhetas", + "name": "Nom", + "name_bio": "Nom & Bio", + "new_password": "Nòu senhal", "notification_visibility_follows": "Abonaments", - "notification_visibility_likes": "Aiman", + "notification_visibility_likes": "Aimar", "notification_visibility_mentions": "Mencions", "notification_visibility_repeats": "Repeticions", + "notification_visibility": "Tipes de notificacion de mostrar", "no_rich_text_description": "Netejar lo format tèxte de totas las publicacions", - "oauth_tokens": "Llistats OAuth", + "no_blocks": "Cap de blocatge", + "no_mutes": "Cap d’amagat", + "hide_follows_description": "Mostrar pas qual seguissi", + "hide_followers_description": "Mostrar pas qual me seguisson", + "show_admin_badge": "Mostrar lo badge Admin badge al perfil meu", + "show_moderator_badge": "Mostrar lo badge Moderator al perfil meu", + "nsfw_clickthrough": "Activar lo clic per mostrar los imatges marcats coma pels adults o sensibles", + "oauth_tokens": "Listats OAuth", + "token": "Geton", + "refresh_token": "Actualizar lo geton", + "valid_until": "Valid fins a", + "revoke_token": "Revocar", + "panelRadius": "Panèls", "pause_on_unfocused": "Pausar la difusion quand l’onglet es pas seleccionat", + "presets": "Pre-enregistrats", + "profile_background": "Imatge de fons", + "profile_banner": "Bandièra del perfil", "profile_tab": "Perfil", + "radii_help": "Configurar los caires arredondits de l’interfàcia (en pixèls)", "replies_in_timeline": "Responsas del flux", + "reply_link_preview": "Activar l’apercebut en passar la mirga", "reply_visibility_all": "Mostrar totas las responsas", "reply_visibility_following": "Mostrar pas que las responsas que me son destinada a ieu o un utilizaire que seguissi", "reply_visibility_self": "Mostrar pas que las responsas que me son destinadas", "saving_err": "Error en enregistrant los paramètres", "saving_ok": "Paramètres enregistrats", + "scope_copy": "Copiar lo nivèl de confidencialitat per las responsas (Totjorn aissí pels Messatges Dirèctes)", "security_tab": "Seguretat", + "set_new_avatar": "Definir un nòu avatar", + "set_new_profile_background": "Definir un nòu fons de perfil", + "set_new_profile_banner": "Definir una nòva bandièra de perfil", + "settings": "Paramètres", + "subject_input_always_show": "Totjorn mostrar lo camp de subjècte", + "subject_line_behavior": "Copiar lo subjècte per las responsas", + "subject_line_email": "Coma los corrièls : \"re: subjècte\"", + "subject_line_mastodon": "Coma mastodon : copiar tal coma es", + "subject_line_noop": "Copiar pas", +"post_status_content_type": "Publicar lo tipe de contengut dels estatuts", + "status_content_type_plain": "Tèxte brut", + "stop_gifs": "Lançar los GIFs al subrevòl", + "streaming": "Activar lo cargament automatic dels novèls estatus en anar amont", + "text": "Tèxt", + "theme": "Tèma", + "theme_help_v2_1": "You can also override certain component's colors and opacity by toggling the checkbox, use \"Clear all\" button to clear all overrides.", + "theme_help_v2_2": "Icons underneath some entries are background/text contrast indicators, hover over for detailed info. Please keep in mind that when using transparency contrast indicators show the worst possible case.", + "theme_help": "Emplegatz los còdis de color hex (#rrggbb) per personalizar vòstre tèma de color.", + "tooltipRadius": "Astúcias/alèrtas", + "upload_a_photo": "Enviar una fotografia", + "user_settings": "Paramètres utilizaire", "values": { "false": "non", "true": "òc" @@ -167,36 +247,68 @@ "up_to_date": "A jorn", "no_retweet_hint": "La publicacion marcada coma pels seguidors solament o dirècte pòt pas èsser repetida" }, + "status": { + "reply_to": "Respondre à", + "replies_list": "Responsas :" + }, "user_card": { + "approve": "Validar", "block": "Blocar", "blocked": "Blocat !", + "deny": "Refusar", + "favorites": "Favorits", "follow": "Seguir", + "follow_sent": "Demanda enviada !", + "follow_progress": "Demanda…", + "follow_again": "Tornar enviar la demanda ?", + "follow_unfollow": "Quitar de seguir", "followees": "Abonaments", "followers": "Seguidors", - "following": "Seguit !", - "follows_you": "Vos sèc !", + "following": "Seguit !", + "follows_you": "Vos sèc !", + "its_you": "Sètz vos !", + "media": "Mèdia", "mute": "Amagar", "muted": "Amagat", "per_day": "per jorn", "remote_follow": "Seguir a distància", "statuses": "Estatuts", - "approve": "Validar", - "deny": "Refusar" + "unblock": "Desblocar", + "unblock_progress": "Desblocatge...", + "block_progress": "Blocatge...", + "unmute": "Tornar mostrar", + "unmute_progress": "Afichatge...", + "mute_progress": "A amagar..." }, "user_profile": { - "timeline_title": "Flux utilizaire" - }, - "features_panel": { - "chat": "Discutida", - "gopher": "Gopher", - "media_proxy": "Servidor mandatari dels mèdias", - "scope_options": "Opcions d'encastres", - "text_limit": "Limit de tèxte", - "title": "Foncionalitats", - "who_to_follow": "Qui seguir" + "timeline_title": "Flux utilizaire", + "profile_does_not_exist": "Aqueste perfil existís pas.", + "profile_loading_error": "Una error s’es producha en cargant aqueste perfil." }, "who_to_follow": { "more": "Mai", - "who_to_follow": "Qui seguir" + "who_to_follow": "Qual seguir" + } + + "tool_tip": { + "media_upload": "Enviar un mèdia", + "repeat": "Repetir", + "reply": "Respondre", + "favorite": "aimar", + "user_settings": "Paramètres utilizaire" + }, + "upload":{ + "error": { + "base": "Mandadís fracassat.", + "file_too_big": "Fichièr tròp grand [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", + "default": "Tornatz ensajar mai tard" + }, + "file_size_units": { + "B": "o", + "KiB": "Kio", + "MiB": "Mio", + "GiB": "Gio", + "TiB": "Tio" + } } } \ No newline at end of file From f392668b73ef7735036d25a8e37a76872b506322 Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Sun, 3 Mar 2019 14:11:38 -0500 Subject: [PATCH 28/56] #418: update notification timeago format --- src/components/notification/notification.vue | 6 +++++- src/components/notifications/notifications.scss | 4 ---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index a0a55cba..dd6cae38 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -25,7 +25,11 @@ <small>{{$t('notifications.followed_you')}}</small> </span> </div> - <small class="timeago"><router-link v-if="notification.status" :to="{ name: 'conversation', params: { id: notification.status.id } }"><timeago :since="notification.action.created_at" :auto-update="240"></timeago></router-link></small> + <div> + <router-link v-if="notification.status" :to="{ name: 'conversation', params: { id: notification.status.id } }" class="faint-link"> + <timeago :since="notification.action.created_at" :auto-update="240"></timeago> + </router-link> + </div> </span> <div class="follow-text" v-if="notification.type === 'follow'"> <router-link :to="userProfileLink(notification.action.user)"> diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index b3364afc..0aaef7a1 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -125,10 +125,6 @@ } } - .timeago { - font-size: 12px; - } - .icon-retweet.lit { color: $fallback--cGreen; color: var(--cGreen, $fallback--cGreen); From 0ea9e4ca14efeaa71da85054bd2cd2a6a5ee33de Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Sun, 3 Mar 2019 14:15:53 -0500 Subject: [PATCH 29/56] #418: update timeago margin to align with icons --- src/components/notification/notification.vue | 2 +- src/components/notifications/notifications.scss | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index dd6cae38..87925cfc 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -25,7 +25,7 @@ <small>{{$t('notifications.followed_you')}}</small> </span> </div> - <div> + <div class="timeago"> <router-link v-if="notification.status" :to="{ name: 'conversation', params: { id: notification.status.id } }" class="faint-link"> <timeago :since="notification.action.created_at" :auto-update="240"></timeago> </router-link> diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 0aaef7a1..2240c10a 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -125,6 +125,10 @@ } } + .timeago { + margin-right: .2em; + } + .icon-retweet.lit { color: $fallback--cGreen; color: var(--cGreen, $fallback--cGreen); From 793abed7ed65b64c8fb4afd448c04ed130059a10 Mon Sep 17 00:00:00 2001 From: rondnelly assis <rondnelly@autistici.org> Date: Sun, 3 Mar 2019 21:55:16 +0000 Subject: [PATCH 30/56] Translating more strings --- src/i18n/pt.json | 93 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 9 deletions(-) diff --git a/src/i18n/pt.json b/src/i18n/pt.json index 544eacdf..cf8aa906 100644 --- a/src/i18n/pt.json +++ b/src/i18n/pt.json @@ -2,38 +2,79 @@ "chat": { "title": "Chat" }, + + "features_panel": { + "chat": "Chat", + "gopher": "Gopher", + "media_proxy": "Proxy de mídia", + "scope_options": "Opções de privacidade", + "text_limit": "Limite de caracteres", + "title": "Funções", + "who_to_follow": "Quem seguir" + }, + "finder": { - "error_fetching_user": "Erro procurando usuário", + "error_fetching_user": "Erro ao procurar usuário", "find_user": "Buscar usuário" }, "general": { "apply": "Aplicar", - "submit": "Enviar" + "submit": "Enviar", + "more": "Mais", + "generic_error": "Houve um erro", + "optional": "opcional" + }, "login": { "login": "Entrar", + "description": "Entrar com OAuth", "logout": "Sair", "password": "Senha", "placeholder": "p.e. lain", "register": "Registrar", - "username": "Usuário" + "username": "Usuário", + "hint": "Entre para participar da discussão" + }, + + "media_modal": { + "previous": "Anterior", + "next": "Próximo" }, "nav": { + "about": "Sobre", + "back": "Voltar", "chat": "Chat local", + "friend_requests": "Solicitações de seguidores", "mentions": "Menções", + "dms": "Mensagens diretas", "public_tl": "Linha do tempo pública", "timeline": "Linha do tempo", - "twkn": "Toda a rede conhecida" + "twkn": "Toda a rede conhecida", + "user_search": "Busca de usuário", + "who_to_follow": "Quem seguir", + "preferences": "Preferências" }, "notifications": { + "broken_favorite": "Status desconhecido, buscando...", "favorited_you": "favoritou sua postagem", "followed_you": "seguiu você", + "load_older": "Carregar notificações antigas", "notifications": "Notificações", "read": "Lido!", - "repeated_you": "repetiu sua postagem" + "repeated_you": "repetiu sua postagem", + "no_more_notifications": "Mais nenhuma notificação" }, "post_status": { + "new_status": "Postar novo status", + "account_not_locked_warning": "Sua conta não está {0}. Qualquer pessoa pode te seguir para ver seus posts restritos.", + "account_not_locked_warning_link": "fechada", + "attachments_sensitive": "Marcar anexos como sensíveis", + "content_type": { + "plain_text": "Texto puro" + }, + "content_warning": "Assunto (opcional)", "default": "Acabei de chegar no Rio!", + "direct_warning": "Este post será visível apenas para os usuários mencionados.", "posting": "Publicando" }, "registration": { @@ -41,30 +82,64 @@ "email": "Correio eletrônico", "fullname": "Nome para exibição", "password_confirm": "Confirmação de senha", - "registration": "Registro" + "registration": "Registro", + "token": "Código do convite", + "captcha": "CAPTCHA", + "new_captcha": "Clique na imagem para carregar um novo captcha", + "username_placeholder": "p. ex. lain", + "fullname_placeholder": "p. ex. Lain Iwakura", + "bio_placeholder": "e.g.\nOi, sou Lain\nSou uma garota que vive no subúrbio do Japão. Você deve me conhecer da Rede.", + "validations": { + "username_required": "não pode ser deixado em branco", + "fullname_required": "não pode ser deixado em branco", + "email_required": "não pode ser deixado em branco", + "password_required": "não pode ser deixado em branco", + "password_confirmation_required": "não pode ser deixado em branco", + "password_confirmation_match": "deve ser idêntica à senha" + } }, "settings": { + "app_name": "Nome do aplicativo", "attachmentRadius": "Anexos", "attachments": "Anexos", "autoload": "Habilitar carregamento automático quando a rolagem chegar ao fim.", "avatar": "Avatar", "avatarAltRadius": "Avatares (Notificações)", "avatarRadius": "Avatares", - "background": "Plano de Fundo", + "background": "Pano de Fundo", "bio": "Biografia", + "blocks_tab": "Blocos", "btnRadius": "Botões", "cBlue": "Azul (Responder, seguir)", "cGreen": "Verde (Repetir)", "cOrange": "Laranja (Favoritar)", "cRed": "Vermelho (Cancelar)", + "change_password": "Mudar senha", + "change_password_error": "Houve um erro ao modificar sua senha.", + "changed_password": "Senha modificada com sucesso!", + "collapse_subject": "Esconder posts com assunto", + "composing": "Escrevendo", + "confirm_new_password": "Confirmar nova senha", "current_avatar": "Seu avatar atual", "current_profile_banner": "Sua capa de perfil atual", + "data_import_export_tab": "Importação/exportação de dados", + "default_vis": "Opção de privacidade padrão", + "delete_account": "Deletar conta", + "delete_account_description": "Deletar sua conta e mensagens permanentemente.", + "delete_account_error": "Houve um problema ao deletar sua conta. Se ele persistir, por favor entre em contato com o/a administrador/a da instância.", + "delete_account_instructions": "Digite sua senha no campo abaixo para confirmar a exclusão da conta.", + "avatar_size_instruction": "O tamanho mínimo recomendado para imagens de avatar é 150x150 pixels.", + "export_theme": "Salvar predefinições", "filtering": "Filtragem", "filtering_explanation": "Todas as postagens contendo estas palavras serão silenciadas, uma por linha.", - "follow_import": "Importar seguidas", + "follow_export": "Exportar quem você segue", + "follow_export_button": "Exportar quem você segue para um arquivo csv", + "follow_export_processing": "Processando. Em breve você receberá a solicitação de download do arquivo", + "follow_import": "Importar quem você segue", "follow_import_error": "Erro ao importar seguidores", "follows_imported": "Seguidores importados! O processamento pode demorar um pouco.", "foreground": "Primeiro Plano", + "general": "Geral", "hide_attachments_in_convo": "Ocultar anexos em conversas", "hide_attachments_in_tl": "Ocultar anexos na linha do tempo.", "import_followers_from_a_csv_file": "Importe seguidores a partir de um arquivo CSV", @@ -114,4 +189,4 @@ "user_profile": { "timeline_title": "Linha do tempo do usuário" } -} +} \ No newline at end of file From ae1a9a8626785da038954785dadb2f99324b02c0 Mon Sep 17 00:00:00 2001 From: aaabulafiaaa <rond.nunes@gmail.com> Date: Sun, 3 Mar 2019 19:03:11 -0300 Subject: [PATCH 31/56] More strings --- src/i18n/pt.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/i18n/pt.json b/src/i18n/pt.json index cf8aa906..88691632 100644 --- a/src/i18n/pt.json +++ b/src/i18n/pt.json @@ -133,7 +133,7 @@ "filtering": "Filtragem", "filtering_explanation": "Todas as postagens contendo estas palavras serão silenciadas, uma por linha.", "follow_export": "Exportar quem você segue", - "follow_export_button": "Exportar quem você segue para um arquivo csv", + "follow_export_button": "Exportar quem você segue para um arquivo CSV", "follow_export_processing": "Processando. Em breve você receberá a solicitação de download do arquivo", "follow_import": "Importar quem você segue", "follow_import_error": "Erro ao importar seguidores", @@ -142,6 +142,13 @@ "general": "Geral", "hide_attachments_in_convo": "Ocultar anexos em conversas", "hide_attachments_in_tl": "Ocultar anexos na linha do tempo.", + "max_thumbnails": "Maximum amount of thumbnails per post", + "hide_isp": "Hide instance-specific panel", + "preload_images": "Preload images", + "use_one_click_nsfw": "Open NSFW attachments with just one click", + "hide_post_stats": "Hide post statistics (e.g. the number of favorites)", + "hide_user_stats": "Hide user statistics (e.g. the number of followers)", + "hide_filtered_statuses": "Hide filtered statuses", "import_followers_from_a_csv_file": "Importe seguidores a partir de um arquivo CSV", "links": "Links", "name": "Nome", @@ -189,4 +196,4 @@ "user_profile": { "timeline_title": "Linha do tempo do usuário" } -} \ No newline at end of file +} From 116a51e94902ff5e6907bff33cc5d357bf70f14f Mon Sep 17 00:00:00 2001 From: aaabulafiaaa <rond.nunes@gmail.com> Date: Sun, 3 Mar 2019 19:42:02 -0300 Subject: [PATCH 32/56] more pt strings --- src/i18n/pt.json | 208 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 192 insertions(+), 16 deletions(-) diff --git a/src/i18n/pt.json b/src/i18n/pt.json index 88691632..79441d76 100644 --- a/src/i18n/pt.json +++ b/src/i18n/pt.json @@ -2,7 +2,6 @@ "chat": { "title": "Chat" }, - "features_panel": { "chat": "Chat", "gopher": "Gopher", @@ -12,7 +11,6 @@ "title": "Funções", "who_to_follow": "Quem seguir" }, - "finder": { "error_fetching_user": "Erro ao procurar usuário", "find_user": "Buscar usuário" @@ -23,7 +21,11 @@ "more": "Mais", "generic_error": "Houve um erro", "optional": "opcional" - + }, + "image_cropper": { + "crop_picture": "Cortar imagem", + "save": "Salvar", + "cancel": "Cancelar" }, "login": { "login": "Entrar", @@ -35,7 +37,6 @@ "username": "Usuário", "hint": "Entre para participar da discussão" }, - "media_modal": { "previous": "Anterior", "next": "Próximo" @@ -75,7 +76,13 @@ "content_warning": "Assunto (opcional)", "default": "Acabei de chegar no Rio!", "direct_warning": "Este post será visível apenas para os usuários mencionados.", - "posting": "Publicando" + "posting": "Publicando", + "scope": { + "direct": "Direto - Enviar somente aos usuários mencionados", + "private": "Apenas para seguidores - Enviar apenas para seguidores", + "public": "Público - Enviar a linhas do tempo públicas", + "unlisted": "Não listado - Não enviar a linhas do tempo públicas" + } }, "registration": { "bio": "Biografia", @@ -121,6 +128,7 @@ "composing": "Escrevendo", "confirm_new_password": "Confirmar nova senha", "current_avatar": "Seu avatar atual", + "current_password": "Sua senha atual", "current_profile_banner": "Sua capa de perfil atual", "data_import_export_tab": "Importação/exportação de dados", "default_vis": "Opção de privacidade padrão", @@ -142,35 +150,203 @@ "general": "Geral", "hide_attachments_in_convo": "Ocultar anexos em conversas", "hide_attachments_in_tl": "Ocultar anexos na linha do tempo.", - "max_thumbnails": "Maximum amount of thumbnails per post", - "hide_isp": "Hide instance-specific panel", - "preload_images": "Preload images", - "use_one_click_nsfw": "Open NSFW attachments with just one click", - "hide_post_stats": "Hide post statistics (e.g. the number of favorites)", - "hide_user_stats": "Hide user statistics (e.g. the number of followers)", - "hide_filtered_statuses": "Hide filtered statuses", + "max_thumbnails": "Número máximo de miniaturas por post", + "hide_isp": "Esconder painel específico da instância", + "preload_images": "Pré-carregar imagens", + "use_one_click_nsfw": "Abrir anexos sensíveis com um clique", + "hide_post_stats": "Esconder estatísticas de posts (p. ex. número de favoritos)", + "hide_user_stats": "Esconder estatísticas do usuário (p. ex. número de seguidores)", + "hide_filtered_statuses": "Esconder posts filtrados", "import_followers_from_a_csv_file": "Importe seguidores a partir de um arquivo CSV", + "import_theme": "Carregar pré-definição", + "inputRadius": "Campos de entrada", + "checkboxRadius": "Checkboxes", + "instance_default": "(padrão: {value})", + "instance_default_simple": "(padrão)", + "interface": "Interface", + "interfaceLanguage": "Idioma da interface", + "invalid_theme_imported": "O arquivo selecionado não é um tema compatível com o Pleroma. Nenhuma mudança no tema foi feita.", + "limited_availability": "Indisponível para seu navegador", "links": "Links", + "lock_account_description": "Restringir sua conta a seguidores aprovados", + "loop_video": "Repetir vídeos", + "loop_video_silent_only": "Repetir apenas vídeos sem som (como os \"gifs\" do Mastodon)", + "mutes_tab": "Silenciados", + "play_videos_in_modal": "Tocar vídeos diretamente no visualizador de mídia", + "use_contain_fit": "Não cortar o anexo na miniatura", "name": "Nome", "name_bio": "Nome & Biografia", - "nsfw_clickthrough": "Habilitar clique para ocultar anexos NSFW", + "new_password": "Nova senha", + "notification_visibility": "Tipos de notificação para mostrar", + "notification_visibility_follows": "Seguidos", + "notification_visibility_likes": "Favoritos", + "notification_visibility_mentions": "Menções", + "notification_visibility_repeats": "Repetições", + "no_rich_text_description": "Remover formatação de todos os posts", + "no_blocks": "Sem bloqueios", + "no_mutes": "Sem silenciados", + "hide_follows_description": "Não mostrar quem estou seguindo", + "hide_followers_description": "Não mostrar quem me segue", + "show_admin_badge": "Mostrar título de Administrador em meu perfil", + "show_moderator_badge": "Mostrar título de Moderador em meu perfil", + "nsfw_clickthrough": "Habilitar clique para ocultar anexos sensíveis", + "oauth_tokens": "Token OAuth", + "token": "Token", + "refresh_token": "Atualizar Token", + "valid_until": "Válido até", + "revoke_token": "Revogar", "panelRadius": "Paineis", + "pause_on_unfocused": "Parar transmissão quando a aba não estiver em primeiro plano", "presets": "Predefinições", - "profile_background": "Plano de fundo de perfil", + "profile_background": "Pano de fundo de perfil", "profile_banner": "Capa de perfil", + "profile_tab": "Perfil", "radii_help": "Arredondar arestas da interface (em píxeis)", + "replies_in_timeline": "Respostas na linha do tempo", "reply_link_preview": "Habilitar a pré-visualização de link de respostas ao passar o mouse.", + "reply_visibility_all": "Mostrar todas as respostas", + "reply_visibility_following": "Só mostrar respostas direcionadas a mim ou a usuários que sigo", + "reply_visibility_self": "Só mostrar respostas direcionadas a mim", + "saving_err": "Erro ao salvar configurações", + "saving_ok": "Configurações salvas", + "security_tab": "Segurança", + "scope_copy": "Copiar opções de privacidade ao responder (Mensagens diretas sempre copiam)", "set_new_avatar": "Alterar avatar", "set_new_profile_background": "Alterar o plano de fundo de perfil", "set_new_profile_banner": "Alterar capa de perfil", "settings": "Configurações", + "subject_input_always_show": "Sempre mostrar campo de assunto", + "subject_line_behavior": "Copiar assunto ao responder", + "subject_line_email": "Como em email: \"re: assunto\"", + "subject_line_mastodon": "Como o Mastodon: copiar como está", + "subject_line_noop": "Não copiar", + "post_status_content_type": "Postar tipo de conteúdo do status", + "status_content_type_plain": "Texto puro", "stop_gifs": "Reproduzir GIFs ao passar o cursor em cima", "streaming": "Habilitar o fluxo automático de postagens quando ao topo da página", "text": "Texto", "theme": "Tema", "theme_help": "Use cores em código hexadecimal (#rrggbb) para personalizar seu esquema de cores.", - "tooltipRadius": "Dicass/alertas", - "user_settings": "Configurações de Usuário" + "theme_help_v2_1": "Você também pode sobrescrever as cores e opacidade de alguns componentes ao modificar o checkbox, use \"Limpar todos\" para limpar todas as modificações.", + "theme_help_v2_2": "Alguns ícones sob registros são indicadores de fundo/contraste de textos, passe por cima para informações detalhadas. Tenha ciência de que os indicadores de contraste não funcionam muito bem com transparência.", + "tooltipRadius": "Dicas/alertas", + "upload_a_photo": "Enviar uma foto", + "user_settings": "Configurações de Usuário", + "values": { + "false": "não", + "true": "sim" + }, + "notifications": "Notifications", + "enable_web_push_notifications": "Habilitar notificações web push", + "style": { + "switcher": { + "keep_color": "Manter cores", + "keep_shadows": "Manter sombras", + "keep_opacity": "Manter opacidade", + "keep_roundness": "Manter arredondado", + "keep_fonts": "Manter fontes", + "save_load_hint": "Manter as opções preserva as opções atuais ao selecionar ou carregar temas; também salva as opções ao exportar um tempo. Quanto todos os campos estiverem desmarcados, tudo será salvo ao exportar o tema.", + "reset": "Voltar ao padrão", + "clear_all": "Limpar tudo", + "clear_opacity": "Limpar opacidade" + }, + "common": { + "color": "Cor", + "opacity": "Opacidade", + "contrast": { + "hint": "Contrast ratio is {ratio}, it {level} {context}", + "level": { + "aa": "meets Level AA guideline (minimal)", + "aaa": "meets Level AAA guideline (recommended)", + "bad": "doesn't meet any accessibility guidelines" + }, + "context": { + "18pt": "for large (18pt+) text", + "text": "for text" + } + } + }, + "common_colors": { + "_tab_label": "Common", + "main": "Common colors", + "foreground_hint": "See \"Advanced\" tab for more detailed control", + "rgbo": "Icons, accents, badges" + }, + "advanced_colors": { + "_tab_label": "Advanced", + "alert": "Alert background", + "alert_error": "Error", + "badge": "Badge background", + "badge_notification": "Notification", + "panel_header": "Panel header", + "top_bar": "Top bar", + "borders": "Borders", + "buttons": "Buttons", + "inputs": "Input fields", + "faint_text": "Faded text" + }, + "radii": { + "_tab_label": "Roundness" + }, + "shadows": { + "_tab_label": "Shadow and lighting", + "component": "Component", + "override": "Override", + "shadow_id": "Shadow #{value}", + "blur": "Blur", + "spread": "Spread", + "inset": "Inset", + "hint": "For shadows you can also use --variable as a color value to use CSS3 variables. Please note that setting opacity won't work in this case.", + "filter_hint": { + "always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.", + "drop_shadow_syntax": "{0} does not support {1} parameter and {2} keyword.", + "avatar_inset": "Please note that combining both inset and non-inset shadows on avatars might give unexpected results with transparent avatars.", + "spread_zero": "Shadows with spread > 0 will appear as if it was set to zero", + "inset_classic": "Inset shadows will be using {0}" + }, + "components": { + "panel": "Panel", + "panelHeader": "Panel header", + "topBar": "Top bar", + "avatar": "User avatar (in profile view)", + "avatarStatus": "User avatar (in post display)", + "popup": "Popups and tooltips", + "button": "Button", + "buttonHover": "Button (hover)", + "buttonPressed": "Button (pressed)", + "buttonPressedHover": "Button (pressed+hover)", + "input": "Input field" + } + }, + "fonts": { + "_tab_label": "Fonts", + "help": "Select font to use for elements of UI. For \"custom\" you have to enter exact font name as it appears in system.", + "components": { + "interface": "Interface", + "input": "Input fields", + "post": "Post text", + "postCode": "Monospaced text in a post (rich text)" + }, + "family": "Font name", + "size": "Size (in px)", + "weight": "Weight (boldness)", + "custom": "Custom" + }, + "preview": { + "header": "Preview", + "content": "Content", + "error": "Example error", + "button": "Button", + "text": "A bunch of more {0} and {1}", + "mono": "content", + "input": "Just landed in L.A.", + "faint_link": "helpful manual", + "fine_print": "Read our {0} to learn nothing useful!", + "header_faint": "This is fine", + "checkbox": "I have skimmed over terms and conditions", + "link": "a nice lil' link" + } + } }, "timeline": { "conversation": "Conversa", From a2a3bda1f65729e212775611490736e6737a7878 Mon Sep 17 00:00:00 2001 From: aaabulafiaaa <rond.nunes@gmail.com> Date: Mon, 4 Mar 2019 08:36:35 -0300 Subject: [PATCH 33/56] more pt strings --- src/i18n/pt.json | 195 ++++++++++++++++++++++++++++++----------------- 1 file changed, 123 insertions(+), 72 deletions(-) diff --git a/src/i18n/pt.json b/src/i18n/pt.json index 79441d76..39ff6c63 100644 --- a/src/i18n/pt.json +++ b/src/i18n/pt.json @@ -187,7 +187,7 @@ "no_mutes": "Sem silenciados", "hide_follows_description": "Não mostrar quem estou seguindo", "hide_followers_description": "Não mostrar quem me segue", - "show_admin_badge": "Mostrar título de Administrador em meu perfil", + "show_admin_badge": "Mostrar distintivo de Administrador em meu perfil", "show_moderator_badge": "Mostrar título de Moderador em meu perfil", "nsfw_clickthrough": "Habilitar clique para ocultar anexos sensíveis", "oauth_tokens": "Token OAuth", @@ -254,122 +254,173 @@ "color": "Cor", "opacity": "Opacidade", "contrast": { - "hint": "Contrast ratio is {ratio}, it {level} {context}", + "hint": "A taxa de contraste é {ratio}, {level} {context}", "level": { - "aa": "meets Level AA guideline (minimal)", - "aaa": "meets Level AAA guideline (recommended)", - "bad": "doesn't meet any accessibility guidelines" + "aa": "padrão Nível AA (mínimo)", + "aaa": "padrão Nível AAA (recomendado)", + "bad": "nenhum padrão de acessibilidade" }, "context": { - "18pt": "for large (18pt+) text", - "text": "for text" + "18pt": "para textos longos (18pt+)", + "text": "para texto" } } }, "common_colors": { - "_tab_label": "Common", - "main": "Common colors", - "foreground_hint": "See \"Advanced\" tab for more detailed control", - "rgbo": "Icons, accents, badges" + "_tab_label": "Comum", + "main": "Cores Comuns", + "foreground_hint": "Configurações mais detalhadas na aba\"Avançado\"", + "rgbo": "Ícones, acentuação, distintivos" }, "advanced_colors": { - "_tab_label": "Advanced", - "alert": "Alert background", - "alert_error": "Error", - "badge": "Badge background", - "badge_notification": "Notification", - "panel_header": "Panel header", - "top_bar": "Top bar", - "borders": "Borders", - "buttons": "Buttons", - "inputs": "Input fields", - "faint_text": "Faded text" + "_tab_label": "Avançado", + "alert": "Fundo de alerta", + "alert_error": "Erro", + "badge": "Fundo do distintivo", + "badge_notification": "Notificação", + "panel_header": "Topo do painel", + "top_bar": "Barra do topo", + "borders": "Bordas", + "buttons": "Botões", + "inputs": "Caixas de entrada", + "faint_text": "Texto esmaecido" }, "radii": { - "_tab_label": "Roundness" + "_tab_label": "Arredondado" }, "shadows": { - "_tab_label": "Shadow and lighting", - "component": "Component", - "override": "Override", - "shadow_id": "Shadow #{value}", - "blur": "Blur", - "spread": "Spread", - "inset": "Inset", - "hint": "For shadows you can also use --variable as a color value to use CSS3 variables. Please note that setting opacity won't work in this case.", + "_tab_label": "Luz e sombra", + "component": "Componente", + "override": "Sobrescrever", + "shadow_id": "Sombra #{value}", + "blur": "Borrado", + "spread": "Difusão", + "inset": "Inserção", + "hint": "Para as sombras você também pode usar --variável como valor de cor para utilizar variáveis do CSS3. Tenha em mente que configurar a opacidade não será possível neste caso.", "filter_hint": { - "always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.", - "drop_shadow_syntax": "{0} does not support {1} parameter and {2} keyword.", - "avatar_inset": "Please note that combining both inset and non-inset shadows on avatars might give unexpected results with transparent avatars.", - "spread_zero": "Shadows with spread > 0 will appear as if it was set to zero", - "inset_classic": "Inset shadows will be using {0}" + "always_drop_shadow": "Atenção, esta sombra sempre utiliza {0} quando compatível com o navegador.", + "drop_shadow_syntax": "{0} não é compatível com o parâmetro {1} e a palavra-chave {2}.", + "avatar_inset": "Tenha em mente que combinar as sombras de inserção e a não-inserção em avatares pode causar resultados inesperados em avatares transparentes.", + "spread_zero": "Sombras com uma difusão > 0 aparecerão como se fossem definidas como 0.", + "inset_classic": "Sombras de inserção utilizarão {0}" }, "components": { - "panel": "Panel", - "panelHeader": "Panel header", - "topBar": "Top bar", - "avatar": "User avatar (in profile view)", - "avatarStatus": "User avatar (in post display)", - "popup": "Popups and tooltips", - "button": "Button", - "buttonHover": "Button (hover)", - "buttonPressed": "Button (pressed)", - "buttonPressedHover": "Button (pressed+hover)", - "input": "Input field" + "panel": "Painel", + "panelHeader": "Topo do painel", + "topBar": "Barra do topo", + "avatar": "Avatar do usuário (na visualização do perfil)", + "avatarStatus": "Avatar do usuário (na exibição de posts)", + "popup": "Dicas e notificações", + "button": "Botão", + "buttonHover": "Botão (em cima)", + "buttonPressed": "Botão (pressionado)", + "buttonPressedHover": "Botão (pressionado+em cima)", + "input": "Campo de entrada" } }, "fonts": { - "_tab_label": "Fonts", - "help": "Select font to use for elements of UI. For \"custom\" you have to enter exact font name as it appears in system.", + "_tab_label": "Fontes", + "help": "Selecionar fonte dos elementos da interface. Para fonte \"personalizada\" você deve entrar exatamente o nome da fonte no sistema.", "components": { "interface": "Interface", - "input": "Input fields", - "post": "Post text", - "postCode": "Monospaced text in a post (rich text)" + "input": "Campo de entrada", + "post": "Postar texto", + "postCode": "Texto monoespaçado em post (formatação rica)" }, - "family": "Font name", - "size": "Size (in px)", - "weight": "Weight (boldness)", - "custom": "Custom" + "family": "Nome da fonte", + "size": "Tamanho (em px)", + "weight": "Peso", + "custom": "Personalizada" }, "preview": { - "header": "Preview", - "content": "Content", - "error": "Example error", - "button": "Button", - "text": "A bunch of more {0} and {1}", - "mono": "content", - "input": "Just landed in L.A.", - "faint_link": "helpful manual", - "fine_print": "Read our {0} to learn nothing useful!", - "header_faint": "This is fine", - "checkbox": "I have skimmed over terms and conditions", - "link": "a nice lil' link" + "header": "Pré-visualizar", + "content": "Conteúdo", + "error": "Erro de exemplo", + "button": "Botão", + "text": "Vários {0} e {1}", + "mono": "conteúdo", + "input": "Acabei de chegar no Rio!", + "faint_link": "manual útil", + "fine_print": "Leia nosso {0} para não aprender nada!", + "header_faint": "Está ok!", + "checkbox": "Li os termos e condições", + "link": "um belo link" } } }, "timeline": { + "collapse": "Esconder", "conversation": "Conversa", - "error_fetching": "Erro buscando atualizações", + "error_fetching": "Erro ao buscar atualizações", "load_older": "Carregar postagens antigas", + "no_retweet_hint": "Posts apenas para seguidores ou diretos não podem ser repetidos", + "repeated": "Repetido", "show_new": "Mostrar novas", - "up_to_date": "Atualizado" + "up_to_date": "Atualizado", + "no_more_statuses": "Sem mais posts", + "no_statuses": "Sem posts" + }, + "status": { + "reply_to": "Responder a", + "replies_list": "Respostas:" }, "user_card": { + "approve": "Aprovar", "block": "Bloquear", "blocked": "Bloqueado!", + "deny": "Negar", + "favorites": "Favoritos", "follow": "Seguir", + "follow_sent": "Pedido enviado!", + "follow_progress": "Enviando…", + "follow_again": "Enviar solicitação novamente?", + "follow_unfollow": "Deixar de seguir", "followees": "Seguindo", "followers": "Seguidores", "following": "Seguindo!", "follows_you": "Segue você!", + "its_you": "É você!", + "media": "Mídia", "mute": "Silenciar", "muted": "Silenciado", "per_day": "por dia", "remote_follow": "Seguidor Remoto", - "statuses": "Postagens" + "statuses": "Postagens", + "unblock": "Desbloquear", + "unblock_progress": "Desbloqueando...", + "block_progress": "Bloqueando...", + "unmute": "Retirar silêncio", + "unmute_progress": "Retirando silêncio...", + "mute_progress": "Silenciando..." }, "user_profile": { - "timeline_title": "Linha do tempo do usuário" + "timeline_title": "Linha do tempo do usuário", + "profile_does_not_exist": "Desculpe, este perfil não existe.", + "profile_loading_error": "Desculpe, houve um erro ao carregar este perfil." + }, + "who_to_follow": { + "more": "Mais", + "who_to_follow": "Quem seguir" + }, + "tool_tip": { + "media_upload": "Envio de mídia", + "repeat": "Repetir", + "reply": "Responder", + "favorite": "Favoritar", + "user_settings": "Configurações do usuário" + }, + "upload":{ + "error": { + "base": "Falha no envio.", + "file_too_big": "Arquivo grande demais [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", + "default": "Tente novamente mais tarde" + }, + "file_size_units": { + "B": "B", + "KiB": "KiB", + "MiB": "MiB", + "GiB": "GiB", + "TiB": "TiB" + } } } From bd0485ad2fdb1bd93c020f875b862bb511abdc80 Mon Sep 17 00:00:00 2001 From: shpuld <shp@cock.li> Date: Mon, 4 Mar 2019 18:56:47 +0200 Subject: [PATCH 34/56] unify spacing between gallery/link preview/attachment components --- src/components/gallery/gallery.vue | 1 - src/components/link-preview/link-preview.vue | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue index 2366ddf7..ea525c95 100644 --- a/src/components/gallery/gallery.vue +++ b/src/components/gallery/gallery.vue @@ -27,7 +27,6 @@ align-content: stretch; flex-grow: 1; margin-top: 0.5em; - margin-bottom: 0.25em; .attachments, .attachment { margin: 0 0.5em 0 0; diff --git a/src/components/link-preview/link-preview.vue b/src/components/link-preview/link-preview.vue index 7394668c..64b1a58b 100644 --- a/src/components/link-preview/link-preview.vue +++ b/src/components/link-preview/link-preview.vue @@ -23,6 +23,7 @@ flex-direction: row; cursor: pointer; overflow: hidden; + margin-top: 0.5em; .card-image { flex-shrink: 0; From 5a273d528f9e9f72726a69aeb70a281a3860cc6f Mon Sep 17 00:00:00 2001 From: Exilat <quentinantonin@free.fr> Date: Mon, 4 Mar 2019 18:53:16 +0000 Subject: [PATCH 35/56] Update oc.json --- src/i18n/oc.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/i18n/oc.json b/src/i18n/oc.json index 261ab737..ef32f83b 100644 --- a/src/i18n/oc.json +++ b/src/i18n/oc.json @@ -288,8 +288,7 @@ "who_to_follow": { "more": "Mai", "who_to_follow": "Qual seguir" - } - + }, "tool_tip": { "media_upload": "Enviar un mèdia", "repeat": "Repetir", From ff9e55ae4248504345b5ee7935b084defc4cad1c Mon Sep 17 00:00:00 2001 From: taehoon <th.dev91@gmail.com> Date: Mon, 4 Mar 2019 21:22:32 -0500 Subject: [PATCH 36/56] Generate cropped avatar image in the original file type --- src/components/image_cropper/image_cropper.js | 8 ++++---- src/components/user_settings/user_settings.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/image_cropper/image_cropper.js b/src/components/image_cropper/image_cropper.js index 990c0370..49d51846 100644 --- a/src/components/image_cropper/image_cropper.js +++ b/src/components/image_cropper/image_cropper.js @@ -67,7 +67,7 @@ const ImageCropper = { submit () { this.submitting = true this.avatarUploadError = null - this.submitHandler(this.cropper, this.filename) + this.submitHandler(this.cropper, this.file) .then(() => this.destroy()) .catch((err) => { this.submitError = err @@ -88,14 +88,14 @@ const ImageCropper = { readFile () { const fileInput = this.$refs.input if (fileInput.files != null && fileInput.files[0] != null) { + this.file = fileInput.files[0] let reader = new window.FileReader() reader.onload = (e) => { this.dataUrl = e.target.result this.$emit('open') } - reader.readAsDataURL(fileInput.files[0]) - this.filename = fileInput.files[0].name || 'unknown' - this.$emit('changed', fileInput.files[0], reader) + reader.readAsDataURL(this.file) + this.$emit('changed', this.file, reader) } }, clearError () { diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js index d6972737..c0ab759c 100644 --- a/src/components/user_settings/user_settings.js +++ b/src/components/user_settings/user_settings.js @@ -157,8 +157,8 @@ const UserSettings = { } reader.readAsDataURL(file) }, - submitAvatar (cropper) { - const img = cropper.getCroppedCanvas().toDataURL('image/jpeg') + submitAvatar (cropper, file) { + const img = cropper.getCroppedCanvas().toDataURL(file.type) return this.$store.state.api.backendInteractor.updateAvatar({ params: { img } }).then((user) => { if (!user.error) { this.$store.commit('addNewUsers', [user]) From f91e4a5801b99084894c4921746f9c47566f30dd Mon Sep 17 00:00:00 2001 From: Edijs <iamedijs@hotmail.com> Date: Mon, 4 Mar 2019 21:29:56 -0800 Subject: [PATCH 37/56] Load post status content type from instance config --- src/boot/after_store.js | 2 ++ src/components/settings/settings.js | 3 +++ src/components/settings/settings.vue | 14 +++----------- src/i18n/en.json | 5 +++++ src/modules/instance.js | 1 + 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 53ecc083..a8e2bf35 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -169,6 +169,8 @@ const afterStoreSetup = ({ store, i18n }) => { store.dispatch('setInstanceOption', { name: 'chatAvailable', value: features.includes('chat') }) store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') }) + store.dispatch('setInstanceOption', { name: 'postFormats', value: metadata.postFormats }) + store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames }) const suggestions = metadata.suggestions diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js index 6e2dff7b..979457a5 100644 --- a/src/components/settings/settings.js +++ b/src/components/settings/settings.js @@ -93,6 +93,9 @@ const settings = { currentSaveStateNotice () { return this.$store.state.interface.settings.currentSaveStateNotice }, + postFormats () { + return this.$store.state.instance.postFormats || [] + }, instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel } }, watch: { diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index 5041b3a3..c0cfe1ab 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -105,17 +105,9 @@ {{$t('settings.post_status_content_type')}} <label for="postContentType" class="select"> <select id="postContentType" v-model="postContentTypeLocal"> - <option value="text/plain"> - {{$t('settings.status_content_type_plain')}} - {{postContentTypeDefault == 'text/plain' ? $t('settings.instance_default_simple') : ''}} - </option> - <option value="text/html"> - HTML - {{postContentTypeDefault == 'text/html' ? $t('settings.instance_default_simple') : ''}} - </option> - <option value="text/markdown"> - Markdown - {{postContentTypeDefault == 'text/markdown' ? $t('settings.instance_default_simple') : ''}} + <option v-for="postFormat in postFormats" :key="postFormat" :value="postFormat"> + {{$t(`settings.post_formats["${postFormat}"]`)}} + {{postContentTypeDefault === postFormat ? $t('settings.instance_default_simple') : ''}} </option> </select> <i class="icon-down-open"/> diff --git a/src/i18n/en.json b/src/i18n/en.json index c5a4a90d..e614fb6c 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -220,6 +220,11 @@ "subject_line_email": "Like email: \"re: subject\"", "subject_line_mastodon": "Like mastodon: copy as is", "subject_line_noop": "Do not copy", + "post_formats": { + "text/plain": "Plain text", + "text/html": "HTML", + "text/markdown": "Markdown" + }, "post_status_content_type": "Post status content type", "status_content_type_plain": "Plain text", "stop_gifs": "Play-on-hover GIFs", diff --git a/src/modules/instance.js b/src/modules/instance.js index c31d02b9..24c52f9c 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -37,6 +37,7 @@ const defaultState = { emoji: [], customEmoji: [], restrictedNicknames: [], + postFormats: [], // Feature-set, apparently, not everything here is reported... mediaProxyAvailable: false, From e0a66b989d8f30f6475bc04064c8c36f9f876539 Mon Sep 17 00:00:00 2001 From: taehoon <th.dev91@gmail.com> Date: Tue, 5 Mar 2019 02:32:23 -0500 Subject: [PATCH 38/56] Re-do UserCardContent css --- .../basic_user_card/basic_user_card.vue | 21 +---- src/components/notification/notification.vue | 4 +- .../notifications/notifications.scss | 4 - src/components/side_drawer/side_drawer.vue | 11 +-- src/components/status/status.vue | 7 +- .../user_card_content/user_card_content.js | 11 ++- .../user_card_content/user_card_content.vue | 79 ++++++++++--------- src/components/user_panel/user_panel.vue | 12 +-- src/components/user_profile/user_profile.vue | 11 +-- 9 files changed, 58 insertions(+), 102 deletions(-) diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue index 77fb0aa0..a358c971 100644 --- a/src/components/basic_user_card/basic_user_card.vue +++ b/src/components/basic_user_card/basic_user_card.vue @@ -4,7 +4,7 @@ <UserAvatar class="avatar" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/> </router-link> <div class="user-card-expanded-content" v-if="userExpanded"> - <user-card-content :user="user" :switcher="false"></user-card-content> + <UserCardContent :user="user" :rounded="true" :bordered="true"/> </div> <div class="user-card-collapsed-content" v-else> <div :title="user.name" class="user-card-user-name"> @@ -29,12 +29,12 @@ .user-card { display: flex; flex: 1 0; + margin: 0; padding-top: 0.6em; padding-right: 1em; padding-bottom: 0.6em; padding-left: 1em; border-bottom: 1px solid; - margin: 0; border-bottom-color: $fallback--border; border-bottom-color: var(--border, $fallback--border); @@ -57,23 +57,6 @@ &-expanded-content { flex: 1; margin-left: 0.7em; - border-radius: $fallback--panelRadius; - border-radius: var(--panelRadius, $fallback--panelRadius); - border-style: solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - border-width: 1px; - overflow: hidden; - - .panel-heading { - background: transparent; - flex-direction: column; - align-items: stretch; - } - - p { - margin-bottom: 0; - } } } </style> diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 87925cfc..6583570e 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -5,9 +5,7 @@ <UserAvatar :compact="true" :betterShadow="betterShadow" :src="notification.action.user.profile_image_url_original"/> </a> <div class='notification-right'> - <div class="usercard notification-usercard" v-if="userExpanded"> - <user-card-content :user="notification.action.user" :switcher="false"></user-card-content> - </div> + <UserCardContent :user="notification.action.user" :rounded="true" :bordered="true" v-if="userExpanded"/> <span class="notification-details"> <div class="name-and-action"> <span class="username" v-if="!!notification.action.user.name_html" :title="'@'+notification.action.user.screen_name" v-html="notification.action.user.name_html"></span> diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 2240c10a..c0b458cc 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -45,10 +45,6 @@ } } - .notification-usercard { - margin: 0; - } - .non-mention { display: flex; flex: 1; diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index 6996380d..f346b440 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -8,7 +8,7 @@ @touchmove="touchMove" > <div class="side-drawer-heading" @click="toggleDrawer"> - <user-card-content :user="currentUser" :switcher="false" :hideBio="true" v-if="currentUser"/> + <UserCardContent :user="currentUser" :hideBio="true" v-if="currentUser"/> <div class="side-drawer-logo-wrapper" v-else> <img :src="logo"/> <span>{{sitename}}</span> @@ -181,15 +181,6 @@ display: flex; padding: 0; margin: 0; - - .profile-panel-background { - border-radius: 0; - .panel-heading { - background: transparent; - flex-direction: column; - align-items: stretch; - } - } } .side-drawer ul { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index ee6ef7da..0c110c90 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -31,9 +31,7 @@ </router-link> </div> <div class="status-body"> - <div class="usercard" v-if="userExpanded"> - <user-card-content :user="status.user" :switcher="false"></user-card-content> - </div> + <UserCardContent :user="status.user" :rounded="true" :bordered="true" class="status-usercard-content" v-if="userExpanded"/> <div v-if="!noHeading" class="media-heading"> <div class="heading-name-row"> <div class="name-and-account-name"> @@ -248,8 +246,7 @@ $status-margin: 0.75em; padding: 0; } - .usercard { - margin: 0; + .status-usercard-content { margin-bottom: $status-margin; } diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 7a7b89d4..78c7eb6b 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -4,7 +4,7 @@ import { requestFollow, requestUnfollow } from '../../services/follow_manipulate import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' export default { - props: [ 'user', 'switcher', 'selected', 'hideBio' ], + props: [ 'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered' ], data () { return { followRequestInProgress: false, @@ -16,7 +16,14 @@ export default { } }, computed: { - headingStyle () { + classes () { + return [{ + 'user-card-content-rounded': this.rounded === true, + 'user-card-content-rounded-t': this.rounded === 'top', // top only + 'user-card-content-bordered': this.bordered + }] + }, + style () { const color = this.$store.state.config.customTheme.colors ? this.$store.state.config.customTheme.colors.bg // v2 : this.$store.state.config.colors.bg // v1 diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index 702c3385..f06a0a45 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -1,6 +1,6 @@ <template> -<div id="heading" class="profile-panel-background" :style="headingStyle"> - <div class="panel-heading text-center"> +<div class="user-card-content" :class="classes" :style="style"> + <div class="panel-heading"> <div class='user-info'> <div class='container'> <router-link :to="userProfileLink(user)"> @@ -108,7 +108,7 @@ </div> </div> </div> - <div class="panel-body profile-panel-body" v-if="!hideBio"> + <div class="panel-body" v-if="!hideBio"> <div v-if="!hideUserStatsLocal && switcher" class="user-counts"> <div class="user-count" v-on:click.prevent="setProfileView('statuses')"> <h5>{{ $t('user_card.statuses') }}</h5> @@ -123,8 +123,8 @@ <span>{{user.followers_count}}</span> </div> </div> - <p @click.prevent="linkClicked" v-if="!hideBio && user.description_html" class="profile-bio" v-html="user.description_html"></p> - <p v-else-if="!hideBio" class="profile-bio">{{ user.description }}</p> + <p @click.prevent="linkClicked" v-if="!hideBio && user.description_html" class="user-card-content-bio" v-html="user.description_html"></p> + <p v-else-if="!hideBio" class="user-card-content-bio">{{ user.description }}</p> </div> </div> </template> @@ -134,30 +134,54 @@ <style lang="scss"> @import '../../_variables.scss'; -.profile-panel-background { +.user-card-content { background-size: cover; - border-radius: $fallback--panelRadius; - border-radius: var(--panelRadius, $fallback--panelRadius); overflow: hidden; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - .panel-heading { padding: .5em 0; text-align: center; box-shadow: none; + background: transparent; + flex-direction: column; + align-items: stretch; } -} -.profile-panel-body { - word-wrap: break-word; - background: linear-gradient(to bottom, rgba(0, 0, 0, 0), $fallback--bg 80%); - background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--bg, $fallback--bg) 80%); + .panel-body { + word-wrap: break-word; + background: linear-gradient(to bottom, rgba(0, 0, 0, 0), $fallback--bg 80%); + background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--bg, $fallback--bg) 80%); + } - .profile-bio { + p { + margin-bottom: 0; + } + + &-bio { text-align: center; } + + // + // Modifiers + + &-rounded { + border-radius: $fallback--panelRadius; + border-radius: var(--panelRadius, $fallback--panelRadius); + } + + &-rounded-t { + border-radius: $fallback--panelRadius; + border-radius: var(--panelRadius, $fallback--panelRadius); + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + + &-bordered { + border-width: 1px; + border-style: solid; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + } } .user-info { @@ -393,25 +417,4 @@ text-decoration: none; } } - -.usercard { - width: fill-available; - border-radius: $fallback--panelRadius; - border-radius: var(--panelRadius, $fallback--panelRadius); - border-style: solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - border-width: 1px; - overflow: hidden; - - .panel-heading { - background: transparent; - flex-direction: column; - align-items: stretch; - } - - p { - margin-bottom: 0; - } -} </style> diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue index 2d5cb500..4b3d8971 100644 --- a/src/components/user_panel/user_panel.vue +++ b/src/components/user_panel/user_panel.vue @@ -1,7 +1,7 @@ <template> <div class="user-panel"> <div v-if='user' class="panel panel-default" style="overflow: visible;"> - <user-card-content :user="user" :switcher="false" :hideBio="true"></user-card-content> + <UserCardContent :user="user" :hideBio="true" rounded="top"/> <div class="panel-footer"> <post-status-form v-if='user'></post-status-form> </div> @@ -11,13 +11,3 @@ </template> <script src="./user_panel.js"></script> - -<style lang="scss"> -.user-panel { - .profile-panel-background .panel-heading { - background: transparent; - flex-direction: column; - align-items: stretch; - } -} -</style> diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 8090efa5..c57d3409 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -1,11 +1,7 @@ <template> <div> <div v-if="user.id" class="user-profile panel panel-default"> - <user-card-content - :user="user" - :switcher="true" - :selected="timeline.viewing" - /> + <UserCardContent :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/> <tab-switcher :renderOnlyFocused="true" ref="tabSwitcher"> <Timeline :label="$t('user_card.statuses')" @@ -64,11 +60,6 @@ flex: 2; flex-basis: 500px; - .profile-panel-background .panel-heading { - background: transparent; - flex-direction: column; - align-items: stretch; - } .userlist-placeholder { display: flex; justify-content: center; From 68b2d9ef568aba94c4e3354cb733deb46544d29d Mon Sep 17 00:00:00 2001 From: Henry Jameson <me@hjkos.com> Date: Tue, 5 Mar 2019 20:15:18 +0200 Subject: [PATCH 39/56] compatibility with upcoming changes --- src/components/status/status.vue | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/status/status.vue b/src/components/status/status.vue index ee6ef7da..4dd20362 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -422,6 +422,11 @@ $status-margin: 0.75em; max-height: 400px; vertical-align: middle; object-fit: contain; + + &.emoji { + width: 32px; + height: 32px; + } } blockquote { From 43c52cb95054d3de43a3a7a5ab46836c84013d1b Mon Sep 17 00:00:00 2001 From: taehoon <th.dev91@gmail.com> Date: Tue, 5 Mar 2019 13:25:31 -0500 Subject: [PATCH 40/56] Update modifier class notation --- .../user_card_content/user_card_content.js | 6 +++--- .../user_card_content/user_card_content.vue | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 78c7eb6b..96c6036c 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -18,9 +18,9 @@ export default { computed: { classes () { return [{ - 'user-card-content-rounded': this.rounded === true, - 'user-card-content-rounded-t': this.rounded === 'top', // top only - 'user-card-content-bordered': this.bordered + 'user-card-content-rt': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius + 'user-card-content-r': this.rounded === true, // set border-radius for all sides + 'user-card-content-b': this.bordered === true // set border for all sides }] }, style () { diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index f06a0a45..e003e850 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -164,19 +164,19 @@ // // Modifiers - &-rounded { + &-rt { + border-top-left-radius: $fallback--panelRadius; + border-top-left-radius: var(--panelRadius, $fallback--panelRadius); + border-top-right-radius: $fallback--panelRadius; + border-top-right-radius: var(--panelRadius, $fallback--panelRadius); + } + + &-r { border-radius: $fallback--panelRadius; border-radius: var(--panelRadius, $fallback--panelRadius); } - &-rounded-t { - border-radius: $fallback--panelRadius; - border-radius: var(--panelRadius, $fallback--panelRadius); - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - - &-bordered { + &-b { border-width: 1px; border-style: solid; border-color: $fallback--border; From baf603a506529cfacdd4bf0b98f248cd3ed35b34 Mon Sep 17 00:00:00 2001 From: taehoon <th.dev91@gmail.com> Date: Tue, 5 Mar 2019 13:54:49 -0500 Subject: [PATCH 41/56] Update block class name of BasicUserCard component --- src/components/basic_user_card/basic_user_card.vue | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue index a358c971..f72620a3 100644 --- a/src/components/basic_user_card/basic_user_card.vue +++ b/src/components/basic_user_card/basic_user_card.vue @@ -1,18 +1,18 @@ <template> - <div class="user-card"> + <div class="basic-user-card"> <router-link :to="userProfileLink(user)"> <UserAvatar class="avatar" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/> </router-link> - <div class="user-card-expanded-content" v-if="userExpanded"> + <div class="basic-user-card-expanded-content" v-if="userExpanded"> <UserCardContent :user="user" :rounded="true" :bordered="true"/> </div> - <div class="user-card-collapsed-content" v-else> - <div :title="user.name" class="user-card-user-name"> + <div class="basic-user-card-collapsed-content" v-else> + <div :title="user.name" class="basic-user-card-user-name"> <span v-if="user.name_html" v-html="user.name_html"></span> <span v-else>{{ user.name }}</span> </div> <div> - <router-link class="user-card-screen-name" :to="userProfileLink(user)"> + <router-link class="basic-user-card-screen-name" :to="userProfileLink(user)"> @{{user.screen_name}} </router-link> </div> @@ -26,7 +26,7 @@ <style lang="scss"> @import '../../_variables.scss'; -.user-card { +.basic-user-card { display: flex; flex: 1 0; margin: 0; From 7bceabb5bda1f7d0737f8f51e0aa07879b7ce72e Mon Sep 17 00:00:00 2001 From: taehoon <th.dev91@gmail.com> Date: Tue, 5 Mar 2019 14:01:49 -0500 Subject: [PATCH 42/56] Rename UserCardContent to UserCard --- src/components/basic_user_card/basic_user_card.js | 4 ++-- src/components/basic_user_card/basic_user_card.vue | 2 +- src/components/notification/notification.js | 4 ++-- src/components/notification/notification.vue | 2 +- src/components/side_drawer/side_drawer.js | 4 ++-- src/components/side_drawer/side_drawer.vue | 2 +- src/components/status/status.js | 4 ++-- src/components/status/status.vue | 2 +- .../user_card_content.js => user_card/user_card.js} | 6 +++--- .../user_card_content.vue => user_card/user_card.vue} | 10 +++++----- src/components/user_panel/user_panel.js | 4 ++-- src/components/user_panel/user_panel.vue | 2 +- src/components/user_profile/user_profile.js | 4 ++-- src/components/user_profile/user_profile.vue | 2 +- 14 files changed, 26 insertions(+), 26 deletions(-) rename src/components/{user_card_content/user_card_content.js => user_card/user_card.js} (94%) rename src/components/{user_card_content/user_card_content.vue => user_card/user_card.vue} (97%) diff --git a/src/components/basic_user_card/basic_user_card.js b/src/components/basic_user_card/basic_user_card.js index a8441446..87085a28 100644 --- a/src/components/basic_user_card/basic_user_card.js +++ b/src/components/basic_user_card/basic_user_card.js @@ -1,4 +1,4 @@ -import UserCardContent from '../user_card_content/user_card_content.vue' +import UserCard from '../user_card/user_card.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -12,7 +12,7 @@ const BasicUserCard = { } }, components: { - UserCardContent, + UserCard, UserAvatar }, methods: { diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue index f72620a3..9b80c72b 100644 --- a/src/components/basic_user_card/basic_user_card.vue +++ b/src/components/basic_user_card/basic_user_card.vue @@ -4,7 +4,7 @@ <UserAvatar class="avatar" @click.prevent.native="toggleUserExpanded" :src="user.profile_image_url"/> </router-link> <div class="basic-user-card-expanded-content" v-if="userExpanded"> - <UserCardContent :user="user" :rounded="true" :bordered="true"/> + <UserCard :user="user" :rounded="true" :bordered="true"/> </div> <div class="basic-user-card-collapsed-content" v-else> <div :title="user.name" class="basic-user-card-user-name"> diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 7d9807de..fe5b7018 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -1,6 +1,6 @@ import Status from '../status/status.vue' import UserAvatar from '../user_avatar/user_avatar.vue' -import UserCardContent from '../user_card_content/user_card_content.vue' +import UserCard from '../user_card/user_card.vue' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -13,7 +13,7 @@ const Notification = { }, props: [ 'notification' ], components: { - Status, UserAvatar, UserCardContent + Status, UserAvatar, UserCard }, methods: { toggleUserExpanded () { diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 6583570e..5e9cef97 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -5,7 +5,7 @@ <UserAvatar :compact="true" :betterShadow="betterShadow" :src="notification.action.user.profile_image_url_original"/> </a> <div class='notification-right'> - <UserCardContent :user="notification.action.user" :rounded="true" :bordered="true" v-if="userExpanded"/> + <UserCard :user="notification.action.user" :rounded="true" :bordered="true" v-if="userExpanded"/> <span class="notification-details"> <div class="name-and-action"> <span class="username" v-if="!!notification.action.user.name_html" :title="'@'+notification.action.user.screen_name" v-html="notification.action.user.name_html"></span> diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js index b5c49059..ad3738d1 100644 --- a/src/components/side_drawer/side_drawer.js +++ b/src/components/side_drawer/side_drawer.js @@ -1,4 +1,4 @@ -import UserCardContent from '../user_card_content/user_card_content.vue' +import UserCard from '../user_card/user_card.vue' import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils' // TODO: separate touch gesture stuff into their own utils if more components want them @@ -12,7 +12,7 @@ const SideDrawer = { closed: true, touchCoord: [0, 0] }), - components: { UserCardContent }, + components: { UserCard }, computed: { currentUser () { return this.$store.state.users.currentUser diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index f346b440..b608b008 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -8,7 +8,7 @@ @touchmove="touchMove" > <div class="side-drawer-heading" @click="toggleDrawer"> - <UserCardContent :user="currentUser" :hideBio="true" v-if="currentUser"/> + <UserCard :user="currentUser" :hideBio="true" v-if="currentUser"/> <div class="side-drawer-logo-wrapper" v-else> <img :src="logo"/> <span>{{sitename}}</span> diff --git a/src/components/status/status.js b/src/components/status/status.js index fbbca6c4..9e18fe15 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -3,7 +3,7 @@ import FavoriteButton from '../favorite_button/favorite_button.vue' import RetweetButton from '../retweet_button/retweet_button.vue' import DeleteButton from '../delete_button/delete_button.vue' import PostStatusForm from '../post_status_form/post_status_form.vue' -import UserCardContent from '../user_card_content/user_card_content.vue' +import UserCard from '../user_card/user_card.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import Gallery from '../gallery/gallery.vue' import LinkPreview from '../link-preview/link-preview.vue' @@ -259,7 +259,7 @@ const Status = { RetweetButton, DeleteButton, PostStatusForm, - UserCardContent, + UserCard, UserAvatar, Gallery, LinkPreview diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 0c110c90..8fda68f5 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -31,7 +31,7 @@ </router-link> </div> <div class="status-body"> - <UserCardContent :user="status.user" :rounded="true" :bordered="true" class="status-usercard-content" v-if="userExpanded"/> + <UserCard :user="status.user" :rounded="true" :bordered="true" class="status-usercard-content" v-if="userExpanded"/> <div v-if="!noHeading" class="media-heading"> <div class="heading-name-row"> <div class="name-and-account-name"> diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card/user_card.js similarity index 94% rename from src/components/user_card_content/user_card_content.js rename to src/components/user_card/user_card.js index 96c6036c..0cb616f4 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card/user_card.js @@ -18,9 +18,9 @@ export default { computed: { classes () { return [{ - 'user-card-content-rt': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius - 'user-card-content-r': this.rounded === true, // set border-radius for all sides - 'user-card-content-b': this.bordered === true // set border for all sides + 'user-card-rt': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius + 'user-card-r': this.rounded === true, // set border-radius for all sides + 'user-card-b': this.bordered === true // set border for all sides }] }, style () { diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card/user_card.vue similarity index 97% rename from src/components/user_card_content/user_card_content.vue rename to src/components/user_card/user_card.vue index e003e850..b90f57a1 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card/user_card.vue @@ -1,5 +1,5 @@ <template> -<div class="user-card-content" :class="classes" :style="style"> +<div class="user-card" :class="classes" :style="style"> <div class="panel-heading"> <div class='user-info'> <div class='container'> @@ -123,18 +123,18 @@ <span>{{user.followers_count}}</span> </div> </div> - <p @click.prevent="linkClicked" v-if="!hideBio && user.description_html" class="user-card-content-bio" v-html="user.description_html"></p> - <p v-else-if="!hideBio" class="user-card-content-bio">{{ user.description }}</p> + <p @click.prevent="linkClicked" v-if="!hideBio && user.description_html" class="user-card-bio" v-html="user.description_html"></p> + <p v-else-if="!hideBio" class="user-card-bio">{{ user.description }}</p> </div> </div> </template> -<script src="./user_card_content.js"></script> +<script src="./user_card.js"></script> <style lang="scss"> @import '../../_variables.scss'; -.user-card-content { +.user-card { background-size: cover; overflow: hidden; diff --git a/src/components/user_panel/user_panel.js b/src/components/user_panel/user_panel.js index 15804b88..d4478290 100644 --- a/src/components/user_panel/user_panel.js +++ b/src/components/user_panel/user_panel.js @@ -1,6 +1,6 @@ import LoginForm from '../login_form/login_form.vue' import PostStatusForm from '../post_status_form/post_status_form.vue' -import UserCardContent from '../user_card_content/user_card_content.vue' +import UserCard from '../user_card/user_card.vue' const UserPanel = { computed: { @@ -9,7 +9,7 @@ const UserPanel = { components: { LoginForm, PostStatusForm, - UserCardContent + UserCard } } diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue index 4b3d8971..8310f30e 100644 --- a/src/components/user_panel/user_panel.vue +++ b/src/components/user_panel/user_panel.vue @@ -1,7 +1,7 @@ <template> <div class="user-panel"> <div v-if='user' class="panel panel-default" style="overflow: visible;"> - <UserCardContent :user="user" :hideBio="true" rounded="top"/> + <UserCard :user="user" :hideBio="true" rounded="top"/> <div class="panel-footer"> <post-status-form v-if='user'></post-status-form> </div> diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index cdf1cee9..54126514 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -1,6 +1,6 @@ import { compose } from 'vue-compose' import get from 'lodash/get' -import UserCardContent from '../user_card_content/user_card_content.vue' +import UserCard from '../user_card/user_card.vue' import FollowCard from '../follow_card/follow_card.vue' import Timeline from '../timeline/timeline.vue' import withLoadMore from '../../hocs/with_load_more/with_load_more' @@ -147,7 +147,7 @@ const UserProfile = { } }, components: { - UserCardContent, + UserCard, Timeline, FollowerList, FriendList diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index c57d3409..7d4a8b1f 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -1,7 +1,7 @@ <template> <div> <div v-if="user.id" class="user-profile panel panel-default"> - <UserCardContent :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/> + <UserCard :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/> <tab-switcher :renderOnlyFocused="true" ref="tabSwitcher"> <Timeline :label="$t('user_card.statuses')" From 7cb13df8adcd9ad24b017a3399137a53acb3eb4c Mon Sep 17 00:00:00 2001 From: taehoon <th.dev91@gmail.com> Date: Tue, 5 Mar 2019 14:13:22 -0500 Subject: [PATCH 43/56] Update tests --- test/unit/specs/boot/routes.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/specs/boot/routes.spec.js b/test/unit/specs/boot/routes.spec.js index 383ba90f..a415aeaf 100644 --- a/test/unit/specs/boot/routes.spec.js +++ b/test/unit/specs/boot/routes.spec.js @@ -24,7 +24,7 @@ describe('routes', () => { const matchedComponents = router.getMatchedComponents() - expect(matchedComponents[0].components.hasOwnProperty('UserCardContent')).to.eql(true) + expect(matchedComponents[0].components.hasOwnProperty('UserCard')).to.eql(true) }) it('user\'s profile at /users', () => { @@ -32,6 +32,6 @@ describe('routes', () => { const matchedComponents = router.getMatchedComponents() - expect(matchedComponents[0].components.hasOwnProperty('UserCardContent')).to.eql(true) + expect(matchedComponents[0].components.hasOwnProperty('UserCard')).to.eql(true) }) }) From 37acb51df416a2475f467c0402a5e18821c5934d Mon Sep 17 00:00:00 2001 From: taehoon <th.dev91@gmail.com> Date: Tue, 5 Mar 2019 21:48:07 -0500 Subject: [PATCH 44/56] Update classname --- src/components/status/status.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 8fda68f5..c234bc03 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -31,7 +31,7 @@ </router-link> </div> <div class="status-body"> - <UserCard :user="status.user" :rounded="true" :bordered="true" class="status-usercard-content" v-if="userExpanded"/> + <UserCard :user="status.user" :rounded="true" :bordered="true" class="status-usercard" v-if="userExpanded"/> <div v-if="!noHeading" class="media-heading"> <div class="heading-name-row"> <div class="name-and-account-name"> @@ -246,7 +246,7 @@ $status-margin: 0.75em; padding: 0; } - .status-usercard-content { + .status-usercard { margin-bottom: $status-margin; } From 5f51fe897dcd7d32c46a67a8d31c8b4ffc8858b2 Mon Sep 17 00:00:00 2001 From: taehoon <th.dev91@gmail.com> Date: Tue, 5 Mar 2019 21:52:04 -0500 Subject: [PATCH 45/56] Revert modifier class notation --- src/components/user_card/user_card.js | 6 +++--- src/components/user_card/user_card.vue | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index 0cb616f4..81c938c0 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -18,9 +18,9 @@ export default { computed: { classes () { return [{ - 'user-card-rt': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius - 'user-card-r': this.rounded === true, // set border-radius for all sides - 'user-card-b': this.bordered === true // set border for all sides + 'user-card-rounded-t': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius + 'user-card-rounded': this.rounded === true, // set border-radius for all sides + 'user-card-bordered': this.bordered === true // set border for all sides }] }, style () { diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index b90f57a1..cc2ce6b8 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -161,22 +161,21 @@ text-align: center; } - // // Modifiers - &-rt { + &-rounded-t { border-top-left-radius: $fallback--panelRadius; border-top-left-radius: var(--panelRadius, $fallback--panelRadius); border-top-right-radius: $fallback--panelRadius; border-top-right-radius: var(--panelRadius, $fallback--panelRadius); } - &-r { + &-rounded { border-radius: $fallback--panelRadius; border-radius: var(--panelRadius, $fallback--panelRadius); } - &-b { + &-bordered { border-width: 1px; border-style: solid; border-color: $fallback--border; From aca3b37134ef797f9c112f9584bf42dee1d9c87e Mon Sep 17 00:00:00 2001 From: taehoon <th.dev91@gmail.com> Date: Wed, 6 Mar 2019 12:03:42 -0500 Subject: [PATCH 46/56] Use a cross browser safe solution to get scroll top --- src/components/timeline/timeline.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 655bfb3f..c45f8947 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -132,7 +132,9 @@ const Timeline = { } if (count > 0) { // only 'stream' them when you're scrolled to the top - if (window.pageYOffset < 15 && + const doc = document.documentElement + const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0) + if (top < 15 && !this.paused && !(this.unfocused && this.$store.state.config.pauseOnUnfocused) ) { From 292cdfb24b2d42d38728e8ff9ab16e2ad9e66410 Mon Sep 17 00:00:00 2001 From: Edijs <iamedijs@hotmail.com> Date: Wed, 6 Mar 2019 20:13:04 -0800 Subject: [PATCH 47/56] Merge content types translations --- src/components/post_status_form/post_status_form.js | 3 +++ src/components/post_status_form/post_status_form.vue | 6 +++--- src/components/settings/settings.vue | 2 +- src/i18n/en.json | 10 +++------- src/i18n/eo.json | 1 - src/i18n/es.json | 1 - src/i18n/ja.json | 1 - src/i18n/oc.json | 1 - src/i18n/pt.json | 1 - 9 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index c28c51bf..23a2c7e2 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -171,6 +171,9 @@ const PostStatusForm = { }, formattingOptionsEnabled () { return this.$store.state.instance.formattingOptionsEnabled + }, + postFormats () { + return this.$store.state.instance.postFormats || [] } }, methods: { diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 5085570b..93f78f65 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -36,9 +36,9 @@ <span class="text-format" v-if="formattingOptionsEnabled"> <label for="post-content-type" class="select"> <select id="post-content-type" v-model="newStatus.contentType" class="form-control"> - <option value="text/plain">{{$t('post_status.content_type.plain_text')}}</option> - <option value="text/html">HTML</option> - <option value="text/markdown">Markdown</option> + <option v-for="postFormat in postFormats" :key="postFormat" :value="postFormat"> + {{$t(`post_status.content_type["${postFormat}"]`)}} + </option> </select> <i class="icon-down-open"></i> </label> diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index c0cfe1ab..d2346747 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -106,7 +106,7 @@ <label for="postContentType" class="select"> <select id="postContentType" v-model="postContentTypeLocal"> <option v-for="postFormat in postFormats" :key="postFormat" :value="postFormat"> - {{$t(`settings.post_formats["${postFormat}"]`)}} + {{$t(`post_status.content_type["${postFormat}"]`)}} {{postContentTypeDefault === postFormat ? $t('settings.instance_default_simple') : ''}} </option> </select> diff --git a/src/i18n/en.json b/src/i18n/en.json index e614fb6c..01fe2fba 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -71,7 +71,9 @@ "account_not_locked_warning_link": "locked", "attachments_sensitive": "Mark attachments as sensitive", "content_type": { - "plain_text": "Plain text" + "text/plain": "Plain text", + "text/html": "HTML", + "text/markdown": "Markdown" }, "content_warning": "Subject (optional)", "default": "Just landed in L.A.", @@ -220,13 +222,7 @@ "subject_line_email": "Like email: \"re: subject\"", "subject_line_mastodon": "Like mastodon: copy as is", "subject_line_noop": "Do not copy", - "post_formats": { - "text/plain": "Plain text", - "text/html": "HTML", - "text/markdown": "Markdown" - }, "post_status_content_type": "Post status content type", - "status_content_type_plain": "Plain text", "stop_gifs": "Play-on-hover GIFs", "streaming": "Enable automatic streaming of new posts when scrolled to the top", "text": "Text", diff --git a/src/i18n/eo.json b/src/i18n/eo.json index 2438b4d5..34851a44 100644 --- a/src/i18n/eo.json +++ b/src/i18n/eo.json @@ -221,7 +221,6 @@ "subject_line_mastodon": "Kiel Mastodon: kopii senŝanĝe", "subject_line_noop": "Ne kopii", "post_status_content_type": "Afiŝi specon de la enhavo de la stato", - "status_content_type_plain": "Plata teksto", "stop_gifs": "Movi GIF-bildojn dum musa ŝvebo", "streaming": "Ŝalti memfaran fluigon de novaj afiŝoj ĉe la supro de la paĝo", "text": "Teksto", diff --git a/src/i18n/es.json b/src/i18n/es.json index 167e8c42..fe96dd08 100644 --- a/src/i18n/es.json +++ b/src/i18n/es.json @@ -202,7 +202,6 @@ "subject_line_mastodon": "Tipo mastodon: copiar como es", "subject_line_noop": "No copiar", "post_status_content_type": "Formato de publicación", - "status_content_type_plain": "Texto plano", "stop_gifs": "Iniciar GIFs al pasar el ratón", "streaming": "Habilite la transmisión automática de nuevas publicaciones cuando se desplaza hacia la parte superior", "text": "Texto", diff --git a/src/i18n/ja.json b/src/i18n/ja.json index b51fa7fd..f39a5a7c 100644 --- a/src/i18n/ja.json +++ b/src/i18n/ja.json @@ -202,7 +202,6 @@ "subject_line_mastodon": "マストドンふう: そのままコピー", "subject_line_noop": "コピーしない", "post_status_content_type": "とうこうのコンテントタイプ", - "status_content_type_plain": "プレーンテキスト", "stop_gifs": "カーソルをかさねたとき、GIFをうごかす", "streaming": "うえまでスクロールしたとき、じどうてきにストリーミングする", "text": "もじ", diff --git a/src/i18n/oc.json b/src/i18n/oc.json index ef32f83b..fd5ccc97 100644 --- a/src/i18n/oc.json +++ b/src/i18n/oc.json @@ -221,7 +221,6 @@ "subject_line_mastodon": "Coma mastodon : copiar tal coma es", "subject_line_noop": "Copiar pas", "post_status_content_type": "Publicar lo tipe de contengut dels estatuts", - "status_content_type_plain": "Tèxte brut", "stop_gifs": "Lançar los GIFs al subrevòl", "streaming": "Activar lo cargament automatic dels novèls estatus en anar amont", "text": "Tèxt", diff --git a/src/i18n/pt.json b/src/i18n/pt.json index 39ff6c63..cbc2c9a3 100644 --- a/src/i18n/pt.json +++ b/src/i18n/pt.json @@ -221,7 +221,6 @@ "subject_line_mastodon": "Como o Mastodon: copiar como está", "subject_line_noop": "Não copiar", "post_status_content_type": "Postar tipo de conteúdo do status", - "status_content_type_plain": "Texto puro", "stop_gifs": "Reproduzir GIFs ao passar o cursor em cima", "streaming": "Habilitar o fluxo automático de postagens quando ao topo da página", "text": "Texto", From 3468c0fd042544c28c5b24fd3fe4048114046957 Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Fri, 8 Mar 2019 13:53:46 -0500 Subject: [PATCH 48/56] #432 - prevent post status form textarea keydown event propagation --- src/components/post_status_form/post_status_form.js | 3 +++ src/components/post_status_form/post_status_form.vue | 1 + 2 files changed, 4 insertions(+) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 23a2c7e2..1f0df35a 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -222,6 +222,9 @@ const PostStatusForm = { this.highlighted = 0 } }, + onKeydown (e) { + e.stopPropagation() + }, setCaret ({target: {selectionStart}}) { this.caret = selectionStart }, diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 0ddde4ea..3d1df91b 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -20,6 +20,7 @@ ref="textarea" @click="setCaret" @keyup="setCaret" v-model="newStatus.status" :placeholder="$t('post_status.default')" rows="1" class="form-control" + @keydown="onKeydown" @keydown.down="cycleForward" @keydown.up="cycleBackward" @keydown.shift.tab="cycleBackward" From 07a46f7736eb881a62669f27af355713f28bee78 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me> Date: Sun, 10 Mar 2019 01:56:21 +0100 Subject: [PATCH 49/56] =?UTF-8?q?user=5Fcard.vue:=20Set=20img.emoji=20to?= =?UTF-8?q?=2032=C3=9732px?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related to https://git.pleroma.social/pleroma/pleroma/merge_requests/792 --- src/components/user_card/user_card.vue | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index cc2ce6b8..7ea96e80 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -159,6 +159,11 @@ &-bio { text-align: center; + + img.emoji { + width: 32px; + height: 32px; + } } // Modifiers From a67881b096dc4e49db804b7267c3bf49ff78bca6 Mon Sep 17 00:00:00 2001 From: slice <ryaneft@gmail.com> Date: Sun, 10 Mar 2019 01:54:26 -0800 Subject: [PATCH 50/56] Check for websocket token before connecting to chat Closes #403. Previously, a socket to the chat channel would be opened if chat is enabled, regardless if the user is logged in or not. This patch only allows a connection to be opened if a wsToken (websocket token) is present, which prevents websocket errors from unauthenticated users. --- src/modules/api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/api.js b/src/modules/api.js index 31cb55c6..dc5278f8 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -50,7 +50,7 @@ const api = { }, initializeSocket (store) { // Set up websocket connection - if (!store.state.chatDisabled) { + if (!store.state.chatDisabled && store.state.wsToken) { const token = store.state.wsToken const socket = new Socket('/socket', {params: {token}}) socket.connect() From e618c6ffb0b974156b89f3e54737e738e94b12b3 Mon Sep 17 00:00:00 2001 From: slice <ryaneft@gmail.com> Date: Sun, 10 Mar 2019 11:23:27 -0700 Subject: [PATCH 51/56] Only connect to chat when authenticating in the first place To avoid duplication of the connection, the chat socket is destroyed upon logging out. --- src/boot/after_store.js | 4 +--- src/modules/api.js | 2 +- src/modules/chat.js | 10 +++++++++- src/modules/users.js | 4 ++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/boot/after_store.js b/src/boot/after_store.js index a8e2bf35..cd88c188 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -89,10 +89,8 @@ const afterStoreSetup = ({ store, i18n }) => { copyInstanceOption('noAttachmentLinks') copyInstanceOption('showFeaturesPanel') - if ((config.chatDisabled)) { + if (config.chatDisabled) { store.dispatch('disableChat') - } else { - store.dispatch('initializeSocket') } return store.dispatch('setTheme', config['theme']) diff --git a/src/modules/api.js b/src/modules/api.js index dc5278f8..31cb55c6 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -50,7 +50,7 @@ const api = { }, initializeSocket (store) { // Set up websocket connection - if (!store.state.chatDisabled && store.state.wsToken) { + if (!store.state.chatDisabled) { const token = store.state.wsToken const socket = new Socket('/socket', {params: {token}}) socket.connect() diff --git a/src/modules/chat.js b/src/modules/chat.js index 383ac75c..2804e577 100644 --- a/src/modules/chat.js +++ b/src/modules/chat.js @@ -1,12 +1,16 @@ const chat = { state: { messages: [], - channel: {state: ''} + channel: {state: ''}, + socket: null }, mutations: { setChannel (state, channel) { state.channel = channel }, + setSocket (state, socket) { + state.socket = socket + }, addMessage (state, message) { state.messages.push(message) state.messages = state.messages.slice(-19, 20) @@ -16,8 +20,12 @@ const chat = { } }, actions: { + disconnectFromChat (store) { + store.state.socket.disconnect() + }, initializeChat (store, socket) { const channel = socket.channel('chat:public') + store.commit('setSocket', socket) channel.on('new_msg', (msg) => { store.commit('addMessage', msg) }) diff --git a/src/modules/users.js b/src/modules/users.js index 4159964c..26884750 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -292,6 +292,7 @@ const users = { logout (store) { store.commit('clearCurrentUser') + store.dispatch('disconnectFromChat') store.commit('setToken', false) store.dispatch('stopFetching', 'friends') store.commit('setBackendInteractor', backendInteractorService()) @@ -321,6 +322,9 @@ const users = { if (user.token) { store.dispatch('setWsToken', user.token) + + // Initialize the chat socket. + store.dispatch('initializeSocket') } // Start getting fresh posts. From 70d7ed36076081f22368bceaa42dd0548fd1c89a Mon Sep 17 00:00:00 2001 From: shpuld <shp@cock.li> Date: Sun, 10 Mar 2019 22:40:48 +0200 Subject: [PATCH 52/56] Make minId reset with minVisibleId to prevent gaps when showing new --- src/modules/statuses.js | 1 + test/unit/specs/modules/statuses.spec.js | 460 ++++++++++++----------- 2 files changed, 241 insertions(+), 220 deletions(-) diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 7571b62a..6b512fa3 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -333,6 +333,7 @@ export const mutations = { oldTimeline.newStatusCount = 0 oldTimeline.visibleStatuses = slice(oldTimeline.statuses, 0, 50) oldTimeline.minVisibleId = last(oldTimeline.visibleStatuses).id + oldTimeline.minId = oldTimeline.minVisibleId oldTimeline.visibleStatusesObject = {} each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status }) }, diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js index 864b798d..0bbcb25a 100644 --- a/test/unit/specs/modules/statuses.spec.js +++ b/test/unit/specs/modules/statuses.spec.js @@ -14,238 +14,258 @@ const makeMockStatus = ({id, text, type = 'status'}) => { } } -describe('Statuses.prepareStatus', () => { - it('sets deleted flag to false', () => { - const aStatus = makeMockStatus({id: '1', text: 'Hello oniichan'}) - expect(prepareStatus(aStatus).deleted).to.eq(false) - }) -}) - -describe('The Statuses module', () => { - it('adds the status to allStatuses and to the given timeline', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - - mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) - - expect(state.allStatuses).to.eql([status]) - expect(state.timelines.public.statuses).to.eql([status]) - expect(state.timelines.public.visibleStatuses).to.eql([]) - expect(state.timelines.public.newStatusCount).to.equal(1) +describe('Statuses module', () => { + describe('prepareStatus', () => { + it('sets deleted flag to false', () => { + const aStatus = makeMockStatus({id: '1', text: 'Hello oniichan'}) + expect(prepareStatus(aStatus).deleted).to.eq(false) + }) }) - it('counts the status as new if it has not been seen on this timeline', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) + describe('addNewStatuses', () => { + it('adds the status to allStatuses and to the given timeline', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) - mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) - mutations.addNewStatuses(state, { statuses: [status], timeline: 'friends' }) + mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) - expect(state.allStatuses).to.eql([status]) - expect(state.timelines.public.statuses).to.eql([status]) - expect(state.timelines.public.visibleStatuses).to.eql([]) - expect(state.timelines.public.newStatusCount).to.equal(1) + expect(state.allStatuses).to.eql([status]) + expect(state.timelines.public.statuses).to.eql([status]) + expect(state.timelines.public.visibleStatuses).to.eql([]) + expect(state.timelines.public.newStatusCount).to.equal(1) + }) - expect(state.allStatuses).to.eql([status]) - expect(state.timelines.friends.statuses).to.eql([status]) - expect(state.timelines.friends.visibleStatuses).to.eql([]) - expect(state.timelines.friends.newStatusCount).to.equal(1) + it('counts the status as new if it has not been seen on this timeline', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + + mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [status], timeline: 'friends' }) + + expect(state.allStatuses).to.eql([status]) + expect(state.timelines.public.statuses).to.eql([status]) + expect(state.timelines.public.visibleStatuses).to.eql([]) + expect(state.timelines.public.newStatusCount).to.equal(1) + + expect(state.allStatuses).to.eql([status]) + expect(state.timelines.friends.statuses).to.eql([status]) + expect(state.timelines.friends.visibleStatuses).to.eql([]) + expect(state.timelines.friends.newStatusCount).to.equal(1) + }) + + it('add the statuses to allStatuses if no timeline is given', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + + mutations.addNewStatuses(state, { statuses: [status] }) + + expect(state.allStatuses).to.eql([status]) + expect(state.timelines.public.statuses).to.eql([]) + expect(state.timelines.public.visibleStatuses).to.eql([]) + expect(state.timelines.public.newStatusCount).to.equal(0) + }) + + it('adds the status to allStatuses and to the given timeline, directly visible', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + + expect(state.allStatuses).to.eql([status]) + expect(state.timelines.public.statuses).to.eql([status]) + expect(state.timelines.public.visibleStatuses).to.eql([status]) + expect(state.timelines.public.newStatusCount).to.equal(0) + }) + + it('removes statuses by tag on deletion', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + const otherStatus = makeMockStatus({id: '3'}) + status.uri = 'xxx' + const deletion = makeMockStatus({id: '2', type: 'deletion'}) + deletion.text = 'Dolus deleted notice {{tag:gs.smuglo.li,2016-11-18:noticeId=1038007:objectType=note}}.' + deletion.uri = 'xxx' + + mutations.addNewStatuses(state, { statuses: [status, otherStatus], showImmediately: true, timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [deletion], showImmediately: true, timeline: 'public' }) + + expect(state.allStatuses).to.eql([otherStatus]) + expect(state.timelines.public.statuses).to.eql([otherStatus]) + expect(state.timelines.public.visibleStatuses).to.eql([otherStatus]) + expect(state.timelines.public.maxId).to.eql('3') + }) + + it('does not update the maxId when the noIdUpdate flag is set', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + const secondStatus = makeMockStatus({id: '2'}) + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + expect(state.timelines.public.maxId).to.eql('1') + + mutations.addNewStatuses(state, { statuses: [secondStatus], showImmediately: true, timeline: 'public', noIdUpdate: true }) + expect(state.timelines.public.statuses).to.eql([secondStatus, status]) + expect(state.timelines.public.visibleStatuses).to.eql([secondStatus, status]) + expect(state.timelines.public.maxId).to.eql('1') + }) + + it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => { + const state = defaultState() + const nonVisibleStatus = makeMockStatus({id: '1'}) + const status = makeMockStatus({id: '3'}) + const statusTwo = makeMockStatus({id: '2'}) + const statusThree = makeMockStatus({id: '4'}) + + mutations.addNewStatuses(state, { statuses: [nonVisibleStatus], showImmediately: false, timeline: 'public' }) + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [statusTwo], showImmediately: true, timeline: 'public' }) + + expect(state.timelines.public.minVisibleId).to.equal('2') + + mutations.addNewStatuses(state, { statuses: [statusThree], showImmediately: true, timeline: 'public' }) + + expect(state.timelines.public.statuses).to.eql([statusThree, status, statusTwo, nonVisibleStatus]) + expect(state.timelines.public.visibleStatuses).to.eql([statusThree, status, statusTwo]) + }) + + it('splits retweets from their status and links them', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + const retweet = makeMockStatus({id: '2', type: 'retweet'}) + const modStatus = makeMockStatus({id: '1', text: 'something else'}) + + retweet.retweeted_status = status + + // It adds both statuses, but only the retweet to visible. + mutations.addNewStatuses(state, { statuses: [retweet], timeline: 'public', showImmediately: true }) + expect(state.timelines.public.visibleStatuses).to.have.length(1) + expect(state.timelines.public.statuses).to.have.length(1) + expect(state.allStatuses).to.have.length(2) + expect(state.allStatuses[0].id).to.equal('1') + expect(state.allStatuses[1].id).to.equal('2') + + // It refers to the modified status. + mutations.addNewStatuses(state, { statuses: [modStatus], timeline: 'public' }) + expect(state.allStatuses).to.have.length(2) + expect(state.allStatuses[0].id).to.equal('1') + expect(state.allStatuses[0].text).to.equal(modStatus.text) + expect(state.allStatuses[1].id).to.equal('2') + expect(retweet.retweeted_status.text).to.eql(modStatus.text) + }) + + it('replaces existing statuses with the same id', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + const modStatus = makeMockStatus({id: '1', text: 'something else'}) + + // Add original status + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + expect(state.timelines.public.visibleStatuses).to.have.length(1) + expect(state.allStatuses).to.have.length(1) + + // Add new version of status + mutations.addNewStatuses(state, { statuses: [modStatus], showImmediately: true, timeline: 'public' }) + expect(state.timelines.public.visibleStatuses).to.have.length(1) + expect(state.allStatuses).to.have.length(1) + expect(state.allStatuses[0].text).to.eql(modStatus.text) + }) + + it('replaces existing statuses with the same id, coming from a retweet', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + const modStatus = makeMockStatus({id: '1', text: 'something else'}) + const retweet = makeMockStatus({id: '2', type: 'retweet'}) + retweet.retweeted_status = modStatus + + // Add original status + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + expect(state.timelines.public.visibleStatuses).to.have.length(1) + expect(state.allStatuses).to.have.length(1) + + // Add new version of status + mutations.addNewStatuses(state, { statuses: [retweet], showImmediately: false, timeline: 'public' }) + expect(state.timelines.public.visibleStatuses).to.have.length(1) + // Don't add the retweet itself if the tweet is visible + expect(state.timelines.public.statuses).to.have.length(1) + expect(state.allStatuses).to.have.length(2) + expect(state.allStatuses[0].text).to.eql(modStatus.text) + }) + + it('handles favorite actions', () => { + const state = defaultState() + const status = makeMockStatus({id: '1'}) + + const favorite = { + id: '2', + type: 'favorite', + in_reply_to_status_id: '1', // The API uses strings here... + uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00', + text: 'a favorited something by b', + user: { id: '99' } + } + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' }) + + expect(state.timelines.public.visibleStatuses.length).to.eql(1) + expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) + expect(state.timelines.public.maxId).to.eq(favorite.id) + + // Adding it again does nothing + mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' }) + + expect(state.timelines.public.visibleStatuses.length).to.eql(1) + expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) + expect(state.timelines.public.maxId).to.eq(favorite.id) + + // If something is favorited by the current user, it also sets the 'favorited' property but does not increment counter to avoid over-counting. Counter is incremented (updated, really) via response to the favorite request. + const user = { + id: '1' + } + + const ownFavorite = { + id: '3', + type: 'favorite', + in_reply_to_status_id: '1', // The API uses strings here... + uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00', + text: 'a favorited something by b', + user + } + + mutations.addNewStatuses(state, { statuses: [ownFavorite], showImmediately: true, timeline: 'public', user }) + + expect(state.timelines.public.visibleStatuses.length).to.eql(1) + expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) + expect(state.timelines.public.visibleStatuses[0].favorited).to.eql(true) + }) }) - it('add the statuses to allStatuses if no timeline is given', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) + describe('showNewStatuses', () => { + it('resets the minId to the min of the visible statuses when adding new to visible statuses', () => { + const state = defaultState() + const status = makeMockStatus({ id: '10' }) + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + const newStatus = makeMockStatus({ id: '20' }) + mutations.addNewStatuses(state, { statuses: [newStatus], showImmediately: false, timeline: 'public' }) + state.timelines.public.minId = '5' + mutations.showNewStatuses(state, { timeline: 'public' }) - mutations.addNewStatuses(state, { statuses: [status] }) - - expect(state.allStatuses).to.eql([status]) - expect(state.timelines.public.statuses).to.eql([]) - expect(state.timelines.public.visibleStatuses).to.eql([]) - expect(state.timelines.public.newStatusCount).to.equal(0) + expect(state.timelines.public.visibleStatuses.length).to.eql(2) + expect(state.timelines.public.minVisibleId).to.eql('10') + expect(state.timelines.public.minId).to.eql('10') + }) }) - it('adds the status to allStatuses and to the given timeline, directly visible', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) + describe('clearTimeline', () => { + it('keeps userId when clearing user timeline', () => { + const state = defaultState() + state.timelines.user.userId = 123 - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.clearTimeline(state, { timeline: 'user' }) - expect(state.allStatuses).to.eql([status]) - expect(state.timelines.public.statuses).to.eql([status]) - expect(state.timelines.public.visibleStatuses).to.eql([status]) - expect(state.timelines.public.newStatusCount).to.equal(0) - }) - - it('removes statuses by tag on deletion', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - const otherStatus = makeMockStatus({id: '3'}) - status.uri = 'xxx' - const deletion = makeMockStatus({id: '2', type: 'deletion'}) - deletion.text = 'Dolus deleted notice {{tag:gs.smuglo.li,2016-11-18:noticeId=1038007:objectType=note}}.' - deletion.uri = 'xxx' - - mutations.addNewStatuses(state, { statuses: [status, otherStatus], showImmediately: true, timeline: 'public' }) - mutations.addNewStatuses(state, { statuses: [deletion], showImmediately: true, timeline: 'public' }) - - expect(state.allStatuses).to.eql([otherStatus]) - expect(state.timelines.public.statuses).to.eql([otherStatus]) - expect(state.timelines.public.visibleStatuses).to.eql([otherStatus]) - expect(state.timelines.public.maxId).to.eql('3') - }) - - it('does not update the maxId when the noIdUpdate flag is set', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - const secondStatus = makeMockStatus({id: '2'}) - - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.maxId).to.eql('1') - - mutations.addNewStatuses(state, { statuses: [secondStatus], showImmediately: true, timeline: 'public', noIdUpdate: true }) - expect(state.timelines.public.statuses).to.eql([secondStatus, status]) - expect(state.timelines.public.visibleStatuses).to.eql([secondStatus, status]) - expect(state.timelines.public.maxId).to.eql('1') - }) - - it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => { - const state = defaultState() - const nonVisibleStatus = makeMockStatus({id: '1'}) - const status = makeMockStatus({id: '3'}) - const statusTwo = makeMockStatus({id: '2'}) - const statusThree = makeMockStatus({id: '4'}) - - mutations.addNewStatuses(state, { statuses: [nonVisibleStatus], showImmediately: false, timeline: 'public' }) - - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - mutations.addNewStatuses(state, { statuses: [statusTwo], showImmediately: true, timeline: 'public' }) - - expect(state.timelines.public.minVisibleId).to.equal('2') - - mutations.addNewStatuses(state, { statuses: [statusThree], showImmediately: true, timeline: 'public' }) - - expect(state.timelines.public.statuses).to.eql([statusThree, status, statusTwo, nonVisibleStatus]) - expect(state.timelines.public.visibleStatuses).to.eql([statusThree, status, statusTwo]) - }) - - it('splits retweets from their status and links them', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - const retweet = makeMockStatus({id: '2', type: 'retweet'}) - const modStatus = makeMockStatus({id: '1', text: 'something else'}) - - retweet.retweeted_status = status - - // It adds both statuses, but only the retweet to visible. - mutations.addNewStatuses(state, { statuses: [retweet], timeline: 'public', showImmediately: true }) - expect(state.timelines.public.visibleStatuses).to.have.length(1) - expect(state.timelines.public.statuses).to.have.length(1) - expect(state.allStatuses).to.have.length(2) - expect(state.allStatuses[0].id).to.equal('1') - expect(state.allStatuses[1].id).to.equal('2') - - // It refers to the modified status. - mutations.addNewStatuses(state, { statuses: [modStatus], timeline: 'public' }) - expect(state.allStatuses).to.have.length(2) - expect(state.allStatuses[0].id).to.equal('1') - expect(state.allStatuses[0].text).to.equal(modStatus.text) - expect(state.allStatuses[1].id).to.equal('2') - expect(retweet.retweeted_status.text).to.eql(modStatus.text) - }) - - it('replaces existing statuses with the same id', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - const modStatus = makeMockStatus({id: '1', text: 'something else'}) - - // Add original status - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.visibleStatuses).to.have.length(1) - expect(state.allStatuses).to.have.length(1) - - // Add new version of status - mutations.addNewStatuses(state, { statuses: [modStatus], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.visibleStatuses).to.have.length(1) - expect(state.allStatuses).to.have.length(1) - expect(state.allStatuses[0].text).to.eql(modStatus.text) - }) - - it('replaces existing statuses with the same id, coming from a retweet', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - const modStatus = makeMockStatus({id: '1', text: 'something else'}) - const retweet = makeMockStatus({id: '2', type: 'retweet'}) - retweet.retweeted_status = modStatus - - // Add original status - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - expect(state.timelines.public.visibleStatuses).to.have.length(1) - expect(state.allStatuses).to.have.length(1) - - // Add new version of status - mutations.addNewStatuses(state, { statuses: [retweet], showImmediately: false, timeline: 'public' }) - expect(state.timelines.public.visibleStatuses).to.have.length(1) - // Don't add the retweet itself if the tweet is visible - expect(state.timelines.public.statuses).to.have.length(1) - expect(state.allStatuses).to.have.length(2) - expect(state.allStatuses[0].text).to.eql(modStatus.text) - }) - - it('handles favorite actions', () => { - const state = defaultState() - const status = makeMockStatus({id: '1'}) - - const favorite = { - id: '2', - type: 'favorite', - in_reply_to_status_id: '1', // The API uses strings here... - uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00', - text: 'a favorited something by b', - user: { id: '99' } - } - - mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) - mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' }) - - expect(state.timelines.public.visibleStatuses.length).to.eql(1) - expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) - expect(state.timelines.public.maxId).to.eq(favorite.id) - - // Adding it again does nothing - mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' }) - - expect(state.timelines.public.visibleStatuses.length).to.eql(1) - expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) - expect(state.timelines.public.maxId).to.eq(favorite.id) - - // If something is favorited by the current user, it also sets the 'favorited' property but does not increment counter to avoid over-counting. Counter is incremented (updated, really) via response to the favorite request. - const user = { - id: '1' - } - - const ownFavorite = { - id: '3', - type: 'favorite', - in_reply_to_status_id: '1', // The API uses strings here... - uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00', - text: 'a favorited something by b', - user - } - - mutations.addNewStatuses(state, { statuses: [ownFavorite], showImmediately: true, timeline: 'public', user }) - - expect(state.timelines.public.visibleStatuses.length).to.eql(1) - expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1) - expect(state.timelines.public.visibleStatuses[0].favorited).to.eql(true) - }) - - it('keeps userId when clearing user timeline', () => { - const state = defaultState() - state.timelines.user.userId = 123 - - mutations.clearTimeline(state, { timeline: 'user' }) - - expect(state.timelines.user.userId).to.eql(123) + expect(state.timelines.user.userId).to.eql(123) + }) }) describe('notifications', () => { From d0e78df22062105435f81b1d147434af8cce1530 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me> Date: Mon, 11 Mar 2019 05:14:49 +0100 Subject: [PATCH 53/56] user_card.vue: Copy over .status-content img styling --- src/components/user_card/user_card.vue | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 7ea96e80..002cb48f 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -160,9 +160,16 @@ &-bio { text-align: center; - img.emoji { - width: 32px; - height: 32px; + img { + object-fit: contain; + vertical-align: middle; + max-width: 100%; + max-height: 400px; + + .emoji { + width: 32px; + height: 32px; + } } } From 3414fce53b416658b20446f66fcc77167b68b6c8 Mon Sep 17 00:00:00 2001 From: Lorem Ipsum <aditoo@seznam.cz> Date: Mon, 11 Mar 2019 14:28:44 +0000 Subject: [PATCH 54/56] I18n: Update Czech translation --- src/i18n/cs.json | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/i18n/cs.json b/src/i18n/cs.json index 6326032c..51e9d342 100644 --- a/src/i18n/cs.json +++ b/src/i18n/cs.json @@ -71,7 +71,9 @@ "account_not_locked_warning_link": "uzamčen", "attachments_sensitive": "Označovat přílohy jako citlivé", "content_type": { - "plain_text": "Prostý text" + "plain_text": "Prostý text", + "text/html": "HTML", + "text/markdown": "Markdown" }, "content_warning": "Předmět (volitelný)", "default": "Právě jsem přistál v L.A.", @@ -95,7 +97,7 @@ "new_captcha": "Kliknutím na obrázek získáte novou CAPTCHA", "username_placeholder": "např. lain", "fullname_placeholder": "např. Lain Iwakura", - "bio_placeholder": "např.\nNazdar, jsem Lain\nJsem anime dívka a žiji v příměstském Japonsku. Možná mě znáte z Wired.", + "bio_placeholder": "např.\nNazdar, jsem Lain\nJsem anime dívka žijící v příměstském Japonsku. Možná mě znáte z Wired.", "validations": { "username_required": "nemůže být prázdné", "fullname_required": "nemůže být prázdné", @@ -204,7 +206,7 @@ "radii_help": "Nastavit zakulacení rohů rozhraní (v pixelech)", "replies_in_timeline": "Odpovědi v časové ose", "reply_link_preview": "Povolit náhledy odkazu pro odpověď při přejetí myši", - "reply_visibility_all": "Zobrazit všechny odpovědiShow all replies", + "reply_visibility_all": "Zobrazit všechny odpovědi", "reply_visibility_following": "Zobrazit pouze odpovědi směřované na mě nebo uživatele, které sleduji", "reply_visibility_self": "Zobrazit pouze odpovědi směřované na mě", "saving_err": "Chyba při ukládání nastavení", @@ -221,7 +223,6 @@ "subject_line_mastodon": "Jako u Mastodonu: zkopírovat tak, jak je", "subject_line_noop": "Nekopírovat", "post_status_content_type": "Publikovat typ obsahu příspěvku", - "status_content_type_plain": "Prostý text", "stop_gifs": "Přehrávat GIFy při přejetí myši", "streaming": "Povolit automatické streamování nových příspěvků při rolování nahoru", "text": "Text", @@ -339,7 +340,7 @@ "button": "Tlačítko", "text": "Spousta dalšího {0} a {1}", "mono": "obsahu", - "input": "Just landed in L.A.", + "input": "Právě jsem přistál v L.A.", "faint_link": "pomocný manuál", "fine_print": "Přečtěte si náš {0} a nenaučte se nic užitečného!", "header_faint": "Tohle je v pohodě", @@ -361,7 +362,7 @@ "no_statuses": "Žádné příspěvky" }, "status": { - "reply_to": "Odpovědět uživateli", + "reply_to": "Odpověď uživateli", "replies_list": "Odpovědi:" }, @@ -413,7 +414,7 @@ "upload":{ "error": { "base": "Nahrávání selhalo.", - "file_too_big": "Soubor je úříliš velký [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", + "file_too_big": "Soubor je příliš velký [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", "default": "Zkuste to znovu později" }, "file_size_units": { From d8e938bb5eb4cd538a55203ff5548ce2d2ffc66d Mon Sep 17 00:00:00 2001 From: taehoon <th.dev91@gmail.com> Date: Wed, 20 Feb 2019 13:40:34 -0500 Subject: [PATCH 55/56] Update user settings icon to pencil --- src/components/user_card/user_card.vue | 2 +- static/font/LICENSE.txt | 0 static/font/README.txt | 0 static/font/config.json | 6 ++++++ static/font/css/animation.css | 0 static/font/css/fontello-codes.css | 1 + static/font/css/fontello-embedded.css | 13 +++++++------ static/font/css/fontello-ie7-codes.css | 1 + static/font/css/fontello-ie7.css | 1 + static/font/css/fontello.css | 15 ++++++++------- static/font/demo.html | 17 +++++++++-------- static/font/font/fontello.eot | Bin 17472 -> 17760 bytes static/font/font/fontello.svg | 2 ++ static/font/font/fontello.ttf | Bin 17304 -> 17592 bytes static/font/font/fontello.woff | Bin 10572 -> 10752 bytes static/font/font/fontello.woff2 | Bin 8932 -> 9148 bytes 16 files changed, 36 insertions(+), 22 deletions(-) mode change 100644 => 100755 static/font/LICENSE.txt mode change 100644 => 100755 static/font/README.txt mode change 100644 => 100755 static/font/config.json mode change 100644 => 100755 static/font/css/animation.css mode change 100644 => 100755 static/font/css/fontello-codes.css mode change 100644 => 100755 static/font/css/fontello-embedded.css mode change 100644 => 100755 static/font/css/fontello-ie7-codes.css mode change 100644 => 100755 static/font/css/fontello-ie7.css mode change 100644 => 100755 static/font/css/fontello.css mode change 100644 => 100755 static/font/demo.html mode change 100644 => 100755 static/font/font/fontello.eot mode change 100644 => 100755 static/font/font/fontello.svg mode change 100644 => 100755 static/font/font/fontello.ttf mode change 100644 => 100755 static/font/font/fontello.woff mode change 100644 => 100755 static/font/font/fontello.woff2 diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 002cb48f..690e1bde 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -11,7 +11,7 @@ <div :title="user.name" class='user-name' v-if="user.name_html" v-html="user.name_html"></div> <div :title="user.name" class='user-name' v-else>{{user.name}}</div> <router-link :to="{ name: 'user-settings' }" v-if="!isOtherUser"> - <i class="button-icon icon-cog usersettings" :title="$t('tool_tip.user_settings')"></i> + <i class="button-icon icon-pencil usersettings" :title="$t('tool_tip.user_settings')"></i> </router-link> <a :href="user.statusnet_profile_url" target="_blank" v-if="isOtherUser && !user.is_local"> <i class="icon-link-ext usersettings"></i> diff --git a/static/font/LICENSE.txt b/static/font/LICENSE.txt old mode 100644 new mode 100755 diff --git a/static/font/README.txt b/static/font/README.txt old mode 100644 new mode 100755 diff --git a/static/font/config.json b/static/font/config.json old mode 100644 new mode 100755 index f16b8029..d72b622c --- a/static/font/config.json +++ b/static/font/config.json @@ -233,6 +233,12 @@ "css": "play-circled", "code": 61764, "src": "fontawesome" + }, + { + "uid": "d35a1d35efeb784d1dc9ac18b9b6c2b6", + "css": "pencil", + "code": 59416, + "src": "fontawesome" } ] } \ No newline at end of file diff --git a/static/font/css/animation.css b/static/font/css/animation.css old mode 100644 new mode 100755 diff --git a/static/font/css/fontello-codes.css b/static/font/css/fontello-codes.css old mode 100644 new mode 100755 index cdc21ef3..49175c8f --- a/static/font/css/fontello-codes.css +++ b/static/font/css/fontello-codes.css @@ -23,6 +23,7 @@ .icon-plus:before { content: '\e815'; } /* '' */ .icon-adjust:before { content: '\e816'; } /* '' */ .icon-edit:before { content: '\e817'; } /* '' */ +.icon-pencil:before { content: '\e818'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ diff --git a/static/font/css/fontello-embedded.css b/static/font/css/fontello-embedded.css old mode 100644 new mode 100755 index b24597b2..c43ad321 --- a/static/font/css/fontello-embedded.css +++ b/static/font/css/fontello-embedded.css @@ -1,15 +1,15 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?50735214'); - src: url('../font/fontello.eot?50735214#iefix') format('embedded-opentype'), - url('../font/fontello.svg?50735214#fontello') format('svg'); + src: url('../font/fontello.eot?21048049'); + src: url('../font/fontello.eot?21048049#iefix') format('embedded-opentype'), + url('../font/fontello.svg?21048049#fontello') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'fontello'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,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') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,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') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -17,7 +17,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?50735214#fontello') format('svg'); + src: url('../font/fontello.svg?21048049#fontello') format('svg'); } } */ @@ -76,6 +76,7 @@ .icon-plus:before { content: '\e815'; } /* '' */ .icon-adjust:before { content: '\e816'; } /* '' */ .icon-edit:before { content: '\e817'; } /* '' */ +.icon-pencil:before { content: '\e818'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ diff --git a/static/font/css/fontello-ie7-codes.css b/static/font/css/fontello-ie7-codes.css old mode 100644 new mode 100755 index 638813cd..56e11447 --- a/static/font/css/fontello-ie7-codes.css +++ b/static/font/css/fontello-ie7-codes.css @@ -23,6 +23,7 @@ .icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-adjust { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-edit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-pencil { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/static/font/css/fontello-ie7.css b/static/font/css/fontello-ie7.css old mode 100644 new mode 100755 index decbcc43..edced9cb --- a/static/font/css/fontello-ie7.css +++ b/static/font/css/fontello-ie7.css @@ -34,6 +34,7 @@ .icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-adjust { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-edit { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-pencil { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin3 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-spin4 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } diff --git a/static/font/css/fontello.css b/static/font/css/fontello.css old mode 100644 new mode 100755 index 63630d13..64a7a938 --- a/static/font/css/fontello.css +++ b/static/font/css/fontello.css @@ -1,11 +1,11 @@ @font-face { font-family: 'fontello'; - src: url('../font/fontello.eot?94672585'); - src: url('../font/fontello.eot?94672585#iefix') format('embedded-opentype'), - url('../font/fontello.woff2?94672585') format('woff2'), - url('../font/fontello.woff?94672585') format('woff'), - url('../font/fontello.ttf?94672585') format('truetype'), - url('../font/fontello.svg?94672585#fontello') format('svg'); + src: url('../font/fontello.eot?40679575'); + src: url('../font/fontello.eot?40679575#iefix') format('embedded-opentype'), + url('../font/fontello.woff2?40679575') format('woff2'), + url('../font/fontello.woff?40679575') format('woff'), + url('../font/fontello.ttf?40679575') format('truetype'), + url('../font/fontello.svg?40679575#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -15,7 +15,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'fontello'; - src: url('../font/fontello.svg?94672585#fontello') format('svg'); + src: url('../font/fontello.svg?40679575#fontello') format('svg'); } } */ @@ -79,6 +79,7 @@ .icon-plus:before { content: '\e815'; } /* '' */ .icon-adjust:before { content: '\e816'; } /* '' */ .icon-edit:before { content: '\e817'; } /* '' */ +.icon-pencil:before { content: '\e818'; } /* '' */ .icon-spin3:before { content: '\e832'; } /* '' */ .icon-spin4:before { content: '\e834'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ diff --git a/static/font/demo.html b/static/font/demo.html old mode 100644 new mode 100755 index 9015b359..2c89a505 --- a/static/font/demo.html +++ b/static/font/demo.html @@ -229,11 +229,11 @@ body { } @font-face { font-family: 'fontello'; - src: url('./font/fontello.eot?28736547'); - src: url('./font/fontello.eot?28736547#iefix') format('embedded-opentype'), - url('./font/fontello.woff?28736547') format('woff'), - url('./font/fontello.ttf?28736547') format('truetype'), - url('./font/fontello.svg?28736547#fontello') format('svg'); + src: url('./font/fontello.eot?50378338'); + src: url('./font/fontello.eot?50378338#iefix') format('embedded-opentype'), + url('./font/fontello.woff?50378338') format('woff'), + url('./font/fontello.ttf?50378338') format('truetype'), + url('./font/fontello.svg?50378338#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -334,24 +334,25 @@ body { <div class="the-icons span3" title="Code: 0xe817"><i class="demo-icon icon-edit"></i> <span class="i-name">icon-edit</span><span class="i-code">0xe817</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xe818"><i class="demo-icon icon-pencil"></i> <span class="i-name">icon-pencil</span><span class="i-code">0xe818</span></div> <div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div> <div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin"></i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div> <div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div> - <div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xf08f"><i class="demo-icon icon-link-ext-alt"></i> <span class="i-name">icon-link-ext-alt</span><span class="i-code">0xf08f</span></div> <div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div> <div class="the-icons span3" title="Code: 0xf0e0"><i class="demo-icon icon-mail-alt"></i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div> <div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div> - <div class="the-icons span3" title="Code: 0xf0fe"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xf0fe</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xf0fe"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xf0fe</span></div> <div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div> <div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div> <div class="the-icons span3" title="Code: 0xf144"><i class="demo-icon icon-play-circled"></i> <span class="i-name">icon-play-circled</span><span class="i-code">0xf144</span></div> - <div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div> </div> <div class="row"> + <div class="the-icons span3" title="Code: 0xf164"><i class="demo-icon icon-thumbs-up-alt"></i> <span class="i-name">icon-thumbs-up-alt</span><span class="i-code">0xf164</span></div> <div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div> <div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div> </div> diff --git a/static/font/font/fontello.eot b/static/font/font/fontello.eot old mode 100644 new mode 100755 index 30867c9265516fa35bf7f841b7a2b4b4c58e7e45..a72671b0d0dfe2fc4d11a57efae27916b5d236e6 GIT binary patch delta 916 zcmZXSOK1~O6o&sZ_hu6NPMk?wwQbr|Q?1&P)TE$Dgi3{AEiS4>i-wq{O45v4L<Q-h zSfomAog(4`3W6wzsM!=$$ij`N3tfn~G5CN8iuJiFsVB}#oXa=={O8_t@41J`o}t_X zsr$DAb6wMFQ2vNKa!!ZOj-CXl7eHSu6*Zbex4yE!35d_JV<R4QvG5b<oaK4Xppi&T zOi%T*&jO`JB6`%|coq9gtd2zT#NhSpgU5hv23S`z6ptRz?!WyG_-A=OIK%<_6Diog zX5T!N8aX}{>05lqfdvlCCeyL#`!BBsfxuPvJ5$l)2AwLu!G4~-XE>UQKfBh@2z2nF z%5x)qbfmw0*FD}l$3@Q>N8-lEEv*lMu6A>l_Rtk`ffB17AkGFU5P!ecvX*eZ0}`)z zqMolHP?4`O&nW}7Jt&Ue|A(oF9puaOigH@=p|~MGzRaNX(r68kXHd)Px4<&i4ht*? z<|i$%g7v%w>R6vxa21%(S)hUSqXj&y1q<-azqu^X#JbtS4=;aoS)iHqfCW~v@<v6l z3i!?i72&$u)p*^g1QzGScA-Lr#??1tJX~E*imlFFVV6Rw6^ei$BFOH@U03h!un#uH zmWCpfH1|xTxvhklN+c!iWFff=l4N9gc-8+ak^Xn#XmMqKm~YhRsj052T;kw5RLySF zTLU4VT@UEg;cc>$s%rs%h=RUq@@^oll6;|{zk%FO{bPY`w6Dx23om53O;Vev&llSB zV!JkwAJ8_s$F#s$U{klEmdV0P$!}UO59bSQpXhR(KJdx5SJ%hfnHlfxiWSaBkstdi zH~m7X$a916+U(&DTWap*(_73Lf1LWw-~RN<j;fNX%g#>cMdyMm=_)Kev^1-&(UQ7L sf8##QNyY!IB{Lo<6`Xb<*rU*DGZ(yLSB>~^>|oNI3|+`Hb~cp#0rXPT00000 delta 638 zcmZXRPe@cz6vn?hZ+b=JpZQmlr8(xLMQ+r|sW6$ajS%K)EJETj^PCB0@{vPKF`>)B zq-MIC78x!|q#zdqty;7zAvY4?DkK;ftc8k!RA}Gx(yDj)-S2+qa1Q6*w>l->7la<Y z3_R^G={?S6b6~g>eK?T^#1Md3JY!gut-wY4c|iLbzncx}yE|JzWRm&dsFlo2m5SHt zX91U$G{!B)-Sj!?fn@sL=w|1KIIyn(bm%G5NcdwHCV|c(^F1jB_Afde^dIR%sZ2IE zy&2p6#=s8-o~6g)#?suE5ul3=FqAQJmdMx6(XY@4ZyOo&(@b*<{WjoOvc|@<(ZGZ6 zz*#mF3)UUes$4qr4(NBwS#d+m$Uh=^T8Ch@y=i-=4(dVH>u};T6UTW44RzIkoOX;f zT!Fm?|6kIfU1bAZ8rrtBG*^AiURQfE`)ezwoULM_3#bw_P{Rrw1=b%aaE$s`fkx`0 z0!`Fa1qZ@<MS*7Ojsiho!>a%vVWUrh6IA|In^S1zA)!Es%0@Og32bs88?*uCUfpae zzYM*uYaAXhmv7eh{)9`bGKrPM?_2*=rn@7eUB2ixWo<a!Qg9z}zxDV%_dRRgytnG> m^UeF#{KZ4p4y&haFFy~vG#0#y49Z`TH*(?JlXCQYu;wo=Wv0pi diff --git a/static/font/font/fontello.svg b/static/font/font/fontello.svg old mode 100644 new mode 100755 index b5a6725a..91aba5ef --- a/static/font/font/fontello.svg +++ b/static/font/font/fontello.svg @@ -54,6 +54,8 @@ <glyph glyph-name="edit" unicode="" d="M496 196l64 65-85 85-64-65v-31h53v-54h32z m245 402q-9 9-18 0l-196-196q-9-9 0-18t18 0l196 196q9 9 0 18z m45-331v-106q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14 9-4 10-13 2-10-5-16l-27-28q-8-8-18-4-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v70q0 7 5 12l36 36q8 8 20 4t11-16z m-54 411l161-160-375-375h-161v160z m248-73l-51-52-161 161 51 52q16 15 38 15t38-15l85-85q16-16 16-38t-16-38z" horiz-adv-x="1000" /> +<glyph glyph-name="pencil" unicode="" d="M203 0l50 51-131 131-51-51v-60h72v-71h60z m291 518q0 12-12 12-5 0-9-4l-303-302q-4-4-4-10 0-12 13-12 5 0 9 4l303 302q3 4 3 10z m-30 107l232-232-464-465h-232v233z m381-54q0-29-20-50l-93-93-232 233 93 92q20 21 50 21 29 0 51-21l131-131q20-22 20-51z" horiz-adv-x="857.1" /> + <glyph glyph-name="spin3" unicode="" d="M494 857c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" /> <glyph glyph-name="spin4" unicode="" d="M498 857c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" /> diff --git a/static/font/font/fontello.ttf b/static/font/font/fontello.ttf old mode 100644 new mode 100755 index 87c3d0d9b8a2dab90fa7e350d42fb5f953975df6..9d36bc11823cf06fd839b4e954b8710de81bd438 GIT binary patch delta 910 zcmZXST}V@57{{OIyyu+FZ#U1Gnjf2PI?YPm<|ZUz5Eu-XNNB4>#+<X*+$kkA2rs11 zi_%VnSb-2kK~%Obgg)@18>uepLZpk5(wJdnR?vmao^`Im^YT0Y_y4}{^PJ~>I1}e_ z<|2++M@|D^8vt|!qXEU{x%Zv?T>!QkJT+($H<P~r8mDOA(yN4{m&V8Z<YxgglyG1` zq4iwy6QufZWVrYC?9-P3!Zm=M>3yL<kM#KCI)Gz}_T7E7pqt|v`33T}zUbi4n73o| z6D@4e!c-&{41E4J-wWWpNxnTA7*g;|)*bR|<SqSyXz2AVa~Xh(KFYmOVgrNztj=l5 zy+}pRC?`Y8^4^+f0Q>63{~W;^>Ol-w>j5bkkOTH_wu3aC!u7zzJZ+TH4H#r63e+X+ zD)d2Wv~3;gVeWunr#zxM%_Y@3PEa2*F>NW8n;v`o1+(Hv0YHL)LQ;nYY$J7PKoP*& zWeq4My`ljnq;ncn5Ued~fSGhz11zLT4WJ(UFls<K=^hOSSZT6P18k%{8c;z>iBdo% zz&aI_g6h5?gi1zufXx}!%0v)BqUvpMt~tLHIibXqt>YP2$T$WJ5rJ><J~Fl)YPN&G z2{GUp7o+yE7<C3AC?fJawv!;=2|V)NqUOr2Cn5I#!I9KSe>2^v%u<k_o0F!eIz&k) z$Td!nT_-zbbXm)FD9V!4;X$`OAFT~2<)Gc;c9_xh$}#HPkKLI9pM1+_2|U)a(nPW@ z!CIxRM3=PNG%7hqolPyAn8_#K@$|-B{E0-eb_FL(<gOLrs4S0~;;ZHNvx^PSy+6Bi zntnqDOHhMgLG@9GHC3O~d$3Aftq-AJ^*Le{uH5w8NkgOIx?#f@F(&hl=gmsnrHE{l s-<!@-Q0mvKihpoqFbezH;N!4bZFE1-iAtzHcr2oRabJxOd(4@C0iXHP>i_@% delta 620 zcmZXQPe>F|9LK+JM#pH)_Frw5=IELhd2y{)qNJ!x5GE0u6%xblxEt)`kV|Z_p|>oe z)~17pgdh+hC}JQwbm$PIke7(?6cH^f)<G8wq0+wNqf_(vyzl$|e*Av#H}B;wF>zBA zqay`C^Z`gDEyFG?M-J0Z1L~*b)ts)atu6!cQRekQJ8j(_FP@`+1o-T<F>EvLr_WP2 zq_fusE1hqXK=l~Vp=C@X6;2!+1v-n&AILCJGh3z5&(ODKtX%$1C9(E}f$t1F%nl`u zxv7r>K#UF0XBl~06gExK&(rId4a<Bt+0;zG0w{C#&~WZZ<i<B(KO2f?_7&4E9o_p1 zIOZQ;xgaK;Ga|i5gJ7|@sq3c>>PD7psKN&(cJdEY)RiO7S20k365i_le>vxsQ*59; zey)fh%WBt~dUJVSoBLXgijf#lCa9<GlVCfrcvpfQ)O!*%P-i7*q<)rg9xRq5Xriu4 zpaV<1*aLimr5*`dsK+I{5#=EzK`WJwJg^I>a3Bw~0q%opv(fz%eNxwO_JsNV;^sp? z;8V*?Vm|fj(m!?H#Nwje`5iMIvn$&?=HKRj5eNsa2Nr^bU^&zinhq_5i(C4)%9m|- Ze|Gs)7AWyv=XCt3Q`3Flo#@tU{{nuJp*H{k diff --git a/static/font/font/fontello.woff b/static/font/font/fontello.woff old mode 100644 new mode 100755 index 3a87b4b62144c53b65864a7c9c905ab5beed720e..35eea15d73495423f65271dfa0d9dbc0459951cb GIT binary patch delta 8534 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 KAa#a0;Qs(+^Gv4z delta 8318 zcmV-^Ac5b2RLoKocTYw}00961001dW01p5F002XnkrY3Fd}D24Z~y=S*Z=?lK>z>) z-sSWcJ!5usAOHXZ8~^|S6951JAO`>b^k#5pZ2$lRFaQ7rkN^M+aEg(TS!ZE$Z~y=Z zzyJUM2mk;82mk;85NB+8W&i*P$N&HwM*sjU2J$h-V`ybzWB>pq5C8xGG5`PoHWXGm zfM{rCVE_PsB}4!K03ZMW03-*=1OjMnba(&&C1d~q09OD20Gy2S|Ke<KV_^UQCA<It z08jt`08q`Q)?IC3cyIs!CJX=o03ZMW03ZQ;4W@2kZDjxeCM*B|0e1iZ0?o{wA0cpW zb94XzC!_!X0V)6h0k1$8ed};?WpDrhD9``^0D1tEQ2|K-%afY{Ie)+mGXQv;<<dEB z0znW);m2aU;SDc%-%Uhh5fc$wJ_YB2bCEl6NHS7|-5db?H&vm{1w5l~m_?dtQ4iPx z8q$|0)LYbaRt}BzSzo_<>-$Z<bbZgU?i^az-ffVkX_n@xO3U2I!#v94TrCEx6&UO1 zlfRyC*lPO5$9n(#Wq&Wv>smomD{ryQ4!c_4K5Y&-<VdSKp~ER>oO3~!ORl))hFk9R z-Ul9e;+Yp-d84P7wKlHhe{Ah?sC~y(uT0OC0xalX$3iK_LaD|=smX$-g9XhA3z`@f zG&?Mm$}DJ}SSYKrpc!L9)5e14js+Ef1+{<$Re=Tdfd!R<1xYo71=WKEb%X^KrM8{g z!Uj&2ktR;Pk)}@Nk!DT}lIBh|k}9VzNlT|f$sMOw$wQ}V$s?zJ$z!LI$(2*nEC!C= z5B=zk^Z)>}9|O4sR5|zAx8Jw#?RoX|bWeBBi_xg3A3_qN)^h|5Xe0(PBZNE%Nf?c! z0Tvq;5FtiOLChk;5<;zOv6WzI6B~z>9Gk>3Y<a=n$VnBB*MjmURax8R5|b<mCV%wb z`CstgXZ<YG=*o4(49Pf63gV0}5~>fm-L5OO^l-tAlAc>dhPo6h8>pl%wQ?`j)x|@# z@A1EgPQ^xIFTI4zR7`!wE|;UxmtKnA@2Q)wel_~>hS5>gfJw~hHE~K*naSd8kTux0 z#^x&CYcY;lO)^Wj7ERI(eUTZ$SbszZE@w0nm@XG=o=M4P@rX}dvpzqb&y@>TrxUi; zGhC<?BRoU3`qF30)$@J1VyRlG`QsT{N99_5yzB{mm{4WpRTVC+ReJoan{j!%GyVH+ zHo?8_^tP<`;Sc=`Wxe;ETK;6N7JlE$e#=UqbfYJuE}is~@e6jwzL4mTcz-;RO~_7X z>E_8ZUN-CDLcIe6y&2l&T|f^na$y8r><e**AGJ3>f*dAUFY9S^CZe_!n%W$;yf;0r zFEoFcAp2y>jZm;V#cHLVEU9ZD*cr_er=n-aoRsrFFF2k<V?T`aQu>il);Uh;Ed9ob zzSVrs3B|}bal%O0(j@<_sDG2v2AaO#9Kaiw=V4(tN{#;R4mT3EOheZMIaf}o(C>SR zn9$r|s;?qwgi-gCt=Z*spE5iC&_Dh4;k*9R*9L$5<0j_m+n+qo!IyJC`AP2OUoS4w z>DH9HKV`!BQFFc|$M|Eck8Ng~8XIyXb*0vrf<gLGi*#w|a>0b28-K)&d8Ku72CBnm zH9}!(Q^V^kr2D+oKr+xBS1&<TR?#Sp$Md;SYAc0OlT@F&Te6R0rM8Z$eXYBCU)k%W z45i}GD}L5CFTSVg9M(k(*l+U;7I)h6A`7(9(&i)`G*3I(&>0g?ny;%Tw#m~n=&%s+ z5{5%uNOEY0*JYn`oPTq6w@c^rBihfx_PKEQoZaP}GZwV4jb<7*Mf0==SwJ9{&WiK= zHSAv}n_yemE$o)YOr?u6d6N#4+O?SzpWZZBf>G%tCz&R-MTijQL`NCv_*-DQz~2HB z;@ApNF(s*~>yx7${cew;bq*Kmqg1cdb;F}d(a7m3*DKdxUVr1TEH|Zdh$x>6c7Igi zM}30E%YGfzkdkM3Nr*G)`7SmoLV48Ii=}!7xI(qzu{D%?^yYnZI2PJ=Am+ukjD=!n z*Zpc;w`Ln#td8l&%c0Qj5C37g+^g9l66z0;m72ZwkK~1rSDN|v4-ek|=UX@4map#5 zhVI^xKYY#RwSOB=JVSRv?GJ1X#bTkcEwRr@x~sY8uCi6qZDXkafg9pOiN{{BYnHCN zI%&;|H$K`$>5hHLWdBw3hp)3w+;yO_p?`lZ34ObC>C!#05f_-%$7ULKFU%nVNaB;% zzuGrF+whdsNCKUR2~J`jSQh5jb~So{S^VzHwL;JgBYzUJOputG`?jBVlL<}h7^cc7 z>8>|H<SI3T97p+t%y38xxck_%Z$FDa%8aaa-#GBl^s}FB@byQYe(C8W>uKv7DSGnG zXZiDIU(law{$bBh>W!@vhoAXJPaj(=HywKZhKCNkk!st_YhsT`U=4@a*2boTb5k27 zlk03wtba1l6_c1zOF5t=OfD*jTEvWL5la%#7=CE)tvBtwcKh&9E}Kjknukdja}k0L z6ac8$e8clS7kgV$E5I%TX@Ll(VhPBAYoHPJz{(UT!^Y~xB{k_q0lxrgr7KXHvfm!e z2uzHx+x5UMzWcu2)NLA{wL{4PU5ieK4daH6PJhdgv4>43*5&WeWBN8vYUY3)H4hmk z+1h8#h+k;6HE&3FTBe9U3`>i4`#ZF#vE7x@YBjd$;JTTa&(F*}pz33pR99J#=&5Pa z)`!ih?wD=dX*ugPy^+x(x>JsJcSXrDg0>x<*{cl4aHm%^4Xx9(&E4&$oiRXZum_BZ z1%G~)mEiY)TP~mi0IPG2>)KJK39i8krC>s`qsl^c;`6E?W?-AjA_bfB@*RbtLdnp& zfLmUaAR@T2YP=FiFXac1O8J~_#9hxHFK0NpK*e0~8p^8+aBCdm_k8l`Ah3Z<^YrF@ z`!=68ZK77OP@!7?H+2sD&_m51g}S{95q}T<%j*u&s!)|Dw1~yUzRmQ+=6$vqvUCU+ z%FuiO9VItuBob=AZM)G^UgR8*@Dv=Qg$GN1?|tztKE_<u(eT0qbC`q_81zi>5kH}P z9uPcNq80`5tu`2ouQm6><2Lt)Lbu_Q252DE?d%QF^UXU#A^M|G#@-tWHQ&YK(0|@g zH{NxrdFdhXnz)m#X1$GW_3ps@*erCO*c7p$LAIJ!E05;qN&q*i`3jH>CBb41McnGX zS68>tRTMJ3{q%!FTei#H^qc8@Bio$L>1J_YKAX`;XewRlY<_bj?Ko+Vep=41uhp7~ zP4d9w*U@iORczPqZQu5%`_Qq|*?%@NuR7Y<^noF|v9pp!$4-+=)$#MONV%DqdVIfZ z(EI68)ltb$0*hXgs{%WYf;+5YH#c_mxB#6f))WalmgKBE1%f1%=T_|LD1%oNqzeU4 zKz&7`G%WymZB|+QR6I61T+Da)vEF!ZGMO*~))7%IE<=>+tJZxg^tBWUK7X%PidTw$ z9B2-oS&z5;ARV08Gl4(8{=@fA@1Y*beE1k_%F)Gfn25czQuy$({u)&Z;&`FL(^pOK zO}jVAwap6`7S7C3&nY<1J*uI}UpDQ;#XW%v@|V>I3n}~!*4$e02DXEJ2H^B}*h%(f z_RrXxjgDs;+%lhdbUrIJ`G3F~fbR4dAePBCK-XC+;c(MRne$1qVk9+7&d0+X_Q{nu zosW{RAwLkzkO6IUXPN7UT_E_x|6NblrJ3as$eo&N6u<Gcm;dpLUwZzzr=NQK#3K)X z{&$Wo96GS?*4;BVUSF+Ni}<aMd*C2`6+RVK+e5CWxGEsB2>cAn;D4<_xzw(QRo4ic z0Dh}uBfN2J3Vo#iQrSqg$^z|;c6%S~{r32(UX5RGkFUypyS%c0J>KHeOB;U9jc!+< zfD3)%EnnwFnl}ZH=#{8@@vX~MBJM>d14@kRPp)Y8&Wf5(yKcWinffok_7|6@@yiu` znzvI%RWyHwEC2hGQGe9OCUJl9i<kS-cc>?*X#QOF`PZL3)Gsb~zVl+DP^lF7`+<%s zT>AmPPh1aN^Bb-e;2K+kweAFmzqHyEvTiLw8&HiO2B39^ZUcf1G~aDQ+lw~c-Mrnl zZ$k|YDx6adRKq0<{Q-Yr>0NZ?yL>-*7xx0ns!+7v22lQV^M4&0Y>lc=6tBtHx7qwp zn(sE>32trra`5_K8tOgpIN#zow6I#EE5iZb4Y0bPy!-Of+N%;@+Y30xxbL>t_M%$J zi=lh>;vIu{o2}|G0lU2@fi>MD-V}S710K7Hz1MIU=sQggbb%mmyZ+S(%y`%!OoFt7 z0gT;&PJ}2$#ean6EHh1KJ6MW7%XB@gU)$BP{E;iV@P*&jryqR=KdldktJe1}ca^}0 zRUd6@!hf4`{&F8(=QHhhaD8fSZm!|<<`M&5JfBQhFnp~7w_LB}lpU4}eZ_cXl;<Km z71LZ0C|3nl%D+#@It&lwdV;)^Yrsvp89^7VzA;Ly9e=QzPw{jAptG{;$gWB!e|f0q z0;tS|o}up1vHk=<ahH}I$!dqcKwfTaY|b3(v#i0j^iTen2D{g<$>r9p?{2>H$2~(J zK<kEj(&d?bPuwu`Y|IY%8J<gp?AWt2H$FbUtFpvl_+!9g16VxND1o$Kqp<X&SR}#+ zqQJAzEPp63P34n?S~8|1cu4jEP$J}OZN~v8g18(+r0f|fga~J-2U8|`JL_G1FNnM2 z&;6U{c>=dzKeBFyPhWGY`MQVaDcXd{`pBW@o;!3T!^EYF;I(s@k3-+3zv1^^|Ee`T zyKz1HF8dC94esH2_5{<Fdcj#>mbm*V`!U$n9DmyhHJf1LEXz8WjpcB9kzSzZ=_&dm zJxHIUgLDUA{jb=6gi7iNByXY{XaN09rqc)Xm-Hk0A^j=6PHSi!&qzIIlTaZW@88^h z`xEfMDrWtziVSe~|9%bzo5Y$4?;^IX>;Io1=H`N(XjBmb8G;)}nV}0q#hRwjP1p$u zlYbU~T*rWwAh$F4U}9F29Q1dpCC0{DN$|0nILNr6abp2vXst1{))?C5F*L0;hPE4P z*RJdOTfTX2ZevHl6yK$D^e^b!bPMffKVWaMf6l(i{)Byl{Vw}FWTZomKq&m%c%3^; z<&2_CDr_S~$;%T|MYL1%iz?9BKzgz28h@3dULBPRCsnq@9U?cU=ZspZm<PKYALXE+ zcnql4Gq?eKs~AYn;ijnKCqw<0i^c@y)u1I$g;21caj#M;2Mu&zHO31{7#5?JimIF; zL{o^@bGm0B_VSdQM?hAu_$A#atD*e5kIsf?U<T-_8yW7_JtGLhj8ake#??5zn14`R z?-dA3bv0fUt-y^-qr9pjIB?@}%)6YCy}~PFRCKQAf&>ZBs#e7)xTw_^Yks+g<ziW` zp32vhGsJTvXGBC1Q&8oSniq&i1?%u?7zq>h>KP8{)ao8kVS*4_R!3FFFOWtVEpnI* z!bwkE>t3xmL8*EzujZ%(T&_YO0)HS1S5-qCs{W#cm8BrBQS4fjinXGWdreP8D20F^ zhz1bJ`mXNLEBAf>-h01){?Cu;5B@7ka#IkOBA!Zu=5kXP*fJ??O_yW>5QUKV(Mbn+ z)TEA<WICjEOTiIi;bq*g&;;=kI@(-nVIkZ|QksbQh-((7q@_!)={DR6lz(677Dfa6 z)C9srNf9Fyl`(-)rAemxz`z2uKcNZ7!Rxux(Is?EOKKt{!y&v#m!`Dj&azbDrl7Qq zIcQSNQ^ldgZNo@N!&3f>M^NSwgb_Sy3JfhYl5pJ^L30c)Oko+Gu4|?llP<==;36U< z0&X*IbNrCTQR2c8hzFIZV1I}qyoZ~vK%mLhx;01%e<ahw5}1Gp^N13MM2(I)Ku%H` zre-)21q8!dFcC-c1iEt-gmT-2sC3=XEXTg<cc#gqF#4wyY$!o%4s0Gjq-Jhohd4xy zMwo~UMTuKBiNt+x{_4&9f=ly1kjYh=%oJJ(%`gOF5+jf+aoy2$$bS_sP6Q8d&rKy( z!pgDNhA<7=keaSJN;R-F$AVBaEJ4J%h?wfRu&}K{r--yMq=seL(l89NG}AC4B%x#p zb+CnqsA^3joUqL(7YfWG5P;P2i%DFyL#kbudep`o5XVI<H^fQr<nXdmM^r0B3^JCc zW=aaB!<vItOUI1J2!Gij7csjAkz+3s!j{rfxl20`FprrDrN5YmZA5~tguG)~RADg> zVbLhcbi|5imP)-s)DSw%MB`ED2&t5&Kolvt2|-4<ZEGsG8L~8`8rTD@S;8P70-dlt zbX5C}OEa9hS*=+m6O|o6)Z7*bwKPZ<A(v{S^hu4Qb(?W3Vt+YY#*84p^;L0F^aGK7 zmTUBa7W25KxCtmL@x^6++~ws0+Un}19AKiP7zh}vq0&-duk`je9@@EO%PyLIaF$-o z_BFrf?pi|=*?m8H_*EJx{k>h+%+AtZXZK~B->c8KsDSUd^w;1Z|4qyxKJ9}aJ<xEv zu;1JYc+q5wRDbp`$PP+*RJmXkE@DCu>t6URM+1?}F$i~2<qH=tx4<^5goT_{O%Y65 z{+bfJh2m3%74e~p*GkDsNj)(1l;4Wz1bHfdrnpwI4!A5MB(seph8<jBs)q3(9I53P zcbbk#Uw2bhF7e^ZiJX;k=@lzi%<VaB+O~-cIqwib?SFtEm*8@B9{ce9d_JB)D3s5O zMBH`TaS4Qx02j-%!A228SO#LhTf=IYP^UN|$h`<Ql^-wU$Ag8a)0k4eeI!$_=37TL zL5@l#w0uPlMI!5+&w2|We&+*jcs}DV;6@c*S5MyynBjZvr|S6Se3tQm8-6C>sxBLD z3@iy&Du4T@8L2>TR%yo+V~MyELOUa_X{q5tJf{wkmOVP0JH2w#Q`3Cr2~JBc;Ddkp zNR`j;dg`U8cG1}9+6Z#rn?X(<%l!r3t3xBM2DjQyMkCdjSQmq#7)(~wVb<hESjx5* z&lzI|K-Q2)nIh2aASNoAkn<)Ax<>kEm_mXX=zofuuf`QW3aLn?-QJ-8gAVDx)r%Ud zS9Xz%j{ep@D#q~`+p_tZ^{Yn*i<vGj2}$U#r6gM~f$pc2<JA>^Nw$yas;#sFb`KS$ zAhTh#GZohae83Yv<*Q_r+6R*Ko5szJDy1yzO)G)_{(YO9V=9kHZ|5_XFuF`T>@>%M zJbx0sU8rdNX1ac=`Q$0SIDTq8HWIrz_U6W$H}=-(*`*<xuODs=uxTIQIjMI8T`Pt5 z7~2etVA9F2G@twmjaE)oqS2dUBY}?HBYq9rFR^L%Y9rF`feG-bO%>%e+v%qw3m{S) z#XbS(<bpcuoYi432tQj{I4!I#g`fG4wSSf>OSkuOCv~(i_|aB+E4jx%mfgc{_=5w) zCbzC$H84Fm?Is+Xm8fK@gFHp{bi?&}Nzo&fgNM2KhOW}^6QpwWAVsAj<x)XLU!Ct@ z2NZLPkfL@Q2rO^BtUA^!cuem<cF)nxTQLJUlhmr?H{Ei_j;AYYE$;kH$hGBKo`0}5 zZrQV!#)Hb;cT8{JT3u)I(En)H+KnxHZaegadyZ`i#t?IjiG^c-*909(?7wN}s?`%~ zuC|h*EG#efb2Fr`-&P!G%2ri2^YJ>>`wOPY187p`IQzw~uvfioBfGYt95AEF)kIrb zl22Yf7o%fBNt!{@QK7=6#kLorLw|FPFtNU@n`FJzE0=`08WK=++4mG(%#b?ks3|h5 zgck@|_geLk)M}JU0jIC2Mn(Fsx9ptRvir#4I}h*J)YqpMBAv1ExUhMi3dLvk-P+XB zQAH;Ed4F;Htq<M*yT@-=jTg{3s})Q=k`Qw}nX9*^+)Q@IrrkH4**Vx1qkp)F>U;id z?zU%&h35M)shdG*`>p-CbjQvWZBu=b1Y16zI3MWp26nuW902IY;l%2rASHe9y|V4h z`WcuhNXqh=1tcVcvkH~T*~@g@(IL4J1c>_L|E}$dbB(!%y|$}Rtrf=A(FR?K3m)8@ z{t-|J+(R-RoQyB0pGw6_ZGXH^d5mQ|cv5-1=Kae^V4MA>ea`C+?QcG(#bg6~@5ug; z7onc0OE;ZfhJc{mvi+ZiE2N*Q<REv@QP&$<B<R@fdCTW(Di4un1FYOwl>j+qLAtD^ zQ4(!F;S8`^cRR*|6To<-Si%B(u@)5qmh03qtYkZ3vLp$9){QnlNPj02)6H|C(A6rh zHnh`@>SpTXzRefkQ`6`E=6#6L9jxc7m}-`>ug2_$rbEJ}>c#U|-26tKRv)aDd&6Z9 z|6x17UT;(eNNOf)u_q0VTnjQ<im%VFI7U$}e@dmWi{k+$Nwrfu)z(Z!s&%d&d<COj z&aW)bxv>xbOORX@@qZw%`l+Rb6**XnEoW`XrHIPKMuN2Ll8wJ6-s5Lk2V29gY7D9G zl?d!Z>kzX=1h1GsV5@)axK!tv%V!$OPBR=NN*OVJ@f=}cgd=he&M#GjJ*uc<platU zg%8j6*QlfS>{Pb6rHglO8tDDZx3lTm;J>R>om?i&olHCv&VT8DFrUb;r_o_iL-T*z z+}hH~?{s-j*3+Hc)Y<KCdDwenWV+|se91~6^sp0Vw>Y>d;_vDoUfa&U!QY(6`h0c` z+uv|13fzhz745XWjSmMvOUKcQieu|USil}LN(y(cs8%e9Et8<RMx5BXwf(uCu0)La z<m&;rfkP_)0DlBSoUL?nf{zB&2+ly^TEN&4I0t!-3BExQ>B<CU!I?g|_xtzJ^tEHr zaOcfi)7fGUW&XhT=+Vc2`9x{x*dKKD3p0XH0&$`YyM`MxqO%9+@n2Hxmyh$uZagtH zaqnPvb$qmceM)FIo_OKJjm@9k{?fd>y=Y1Y^coI2s((d1v%5PvRDN~_Rr4>cw8cEw z_{PSBI%Vu7<&V|DAK+XZ)l|%;e5(N=87dzPa;u!C%F{}Hs#?vDd-?vnsdW#xj#8G- zNAgRDCQBzHRgmpZveGjxg*_dp?&((HOvHQo3NziP)c8!$<V;Ja&nSgH<I;wY8Y%_r zIxnhhSby>BB37gnTSBqH%obG|b}>lLt1u_;C$I4puoPS)!du_Xfht!kqgnv2%O?XW z{hIZm#gk+sRz>(u!p*Ij*uA@c+|63epNB%!6Y5U$<Mi~N%=z2CEE6$lJ75o@*t=#= zV=R-<Pe!~DWz^9@#&)A8|LXcyEWjTVd$0$GT7OO>3&jH=VMh@GGSXNp2M6k-pil%s zP~nrC2((e2LJ=APLeoeR%l3Aq-DE6c=`2rqLuo3A%SV&T8R%~7YY3HMPWMy%h9G=c znPGOL{ShxnJ%2spuS_wYx$`;x%w1|TR9^fHYsV8ou9<bRVk5sy3@AwPtDHBdDPkRI zLw{!uIRQCXnHa29N`<1Hio3qT_bc*A(}_g$?RbB}N|;w<Br5iheL9qKn}6trARH0s z!13VxH_dKlH#V-nj!dJsOC@xHX{+Oyhn!@FX)H2Rn2S0>1JV9Ahvf3^6-KYA2zzSP zNUDEBvD!L}s8>`TEJH1Q&M!%_>}jsyrGGrb3$lc&MiHA*P(Q(ekhn~U@p4A$K31zV z4xLVVJ<#hOcaIzUN}%CiiF*9GUW+;`%ks2r|8?67yURmclc;pN)^-;Yc0@<;A4^3$ zhSIKyxZg1ymE`!+a6^5kDj1JOnqLY=ql1AT7|VAghH}06-qb{Cm=cj_da16FUw@sj zb8gy8=R7CTna(D{sS(eWPDF3CzdLnLz;|MZ4-4>oSF^ut_~V1zFu`_sPb%zydW%U( znu^pExL&0@A|%P+2B{Md&EVPs1~N3ZV3A>%vo@(iB?<ixvn579^68ya!(%IY8nA-$ zZ)uK(Xg{Oc8@tihG_C{3&&K17)qkty)mIG<l=^eMnXZnw8+VfltS(v)BlZgX+p>iu z;k@GcI4wU=|Knx9kjl41Z*BRGp7bK^lhH5AFn#&y;BZ$J@%Nv@vN`dVZJsi1y1(_w zUvAEzrujx|pL*!L(|mxQXgaOK)d+3G|JR*YjvrSEhTz&h1AI+BAd=AS5r4LW-QT!x zq`++>+Zz#rmy=wYVv>;olrwB&F+$7^+bn#Pg~B`>;z!{xSvX{e=XK)1caw{GW=d(! zFw>N?mP!G_IB&TAy6s!HY}`;AU%hH@pg-5$<);!c+XC#EWJUv*SDc^>*R^p4j_&0z zj0WGJS<X%<v+#q{`BaOlR(~cmUq!IxAd2$gCsOp}+#~$KZ#|%&pg(&v_<qBij=o^p zZw23tfUp*D(>yrTbFz4KGrf72bP}23+P+X|WahxkNGNpe*zukrI{3(|kMf7V`QWu5 z@7o%*`9{wO{a*Ktn=@-R)z;)Xxt+t0T^s6Q{|mOndCdTLoMT{QU@Tw&;&9K&N%8zP zUm3WWUjRiIZlwIqgwg-M{!e15U`_{eIT)Bgq5y574YHH!B^(A00HZ$!(vu7(JsRbK z^h=;wAa*1azd+(+=m)x?jDZ0FiK8wxlV>J2A&3wd5VjE%5qc5o5_}Tq6Fw8D6Y3O@ z6<8Jn0096104KA;CYJ$!c7rewWNeBdc6#sKx#*PqkPvI5SV%-A4u2o8Uv8Ph)_JqD z(J0!Yi2lbMBa9IvL5d6$<S4L%DQ4Kk9`<p7Lmc54Cpg6!&T)ZDT;UowxWygr@qkA> z;TbP@#T;)@D!3M;($FfytJryNiy<&-THj@s{F=yT)Dbg_Me2xuTTzdNS*FS?P4IbT z)|#1?bjrQwqRv_=e6W;!%Zf~4<+ApheXeM+t+LQ=a$J&9u`*(nER`uKDQ$3d-s_O| z(&)HV!KJ+V49>??NuRh@>i13jm?)`NjDGy|uV7sHxFH=T4VP-@X2LZ6z;B<K4sy(W z1#YQIEVZhe4aYYrUIuzk;2jgv3Psh-*P$sL>%I1?qLkW*ptyB;aAet_6-B?87<Fj? I0F#6%d~$&)EdT%j diff --git a/static/font/font/fontello.woff2 b/static/font/font/fontello.woff2 old mode 100644 new mode 100755 index 009e5e85129e96b79a4ec9a3475434776047a811..c88c4b24f60e58032feb8176557ed610cd47b99d GIT binary patch literal 9148 zcmV;tBSYMGPew8T0RR9103*Br4*&oF07SR|03%`m0RR9100000000000000000000 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 G0002L)|8(B literal 8932 zcmV<AA{*UzPew8T0RR9103zf74*&oF07IAn03wV40RR9100000000000000000000 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 From 4a27c6d8d3f736e0bd46e9d0ca3dbaaa2108b9bc Mon Sep 17 00:00:00 2001 From: Shpuld Shpludson <shp@cock.li> Date: Mon, 11 Mar 2019 16:51:37 +0000 Subject: [PATCH 56/56] Add floating post-status button on mobile --- src/App.js | 4 +- src/App.scss | 25 +++++ src/App.vue | 1 + src/boot/routes.js | 2 - src/components/media_modal/media_modal.vue | 15 +-- .../mobile_post_status_modal.js | 91 +++++++++++++++++++ .../mobile_post_status_modal.vue | 76 ++++++++++++++++ src/components/side_drawer/side_drawer.vue | 15 +-- 8 files changed, 203 insertions(+), 26 deletions(-) create mode 100644 src/components/mobile_post_status_modal/mobile_post_status_modal.js create mode 100644 src/components/mobile_post_status_modal/mobile_post_status_modal.vue diff --git a/src/App.js b/src/App.js index 214e0f48..5c27a3df 100644 --- a/src/App.js +++ b/src/App.js @@ -8,6 +8,7 @@ import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_pan import ChatPanel from './components/chat_panel/chat_panel.vue' import MediaModal from './components/media_modal/media_modal.vue' import SideDrawer from './components/side_drawer/side_drawer.vue' +import MobilePostStatusModal from './components/mobile_post_status_modal/mobile_post_status_modal.vue' import { unseenNotificationsFromStore } from './services/notification_utils/notification_utils' export default { @@ -22,7 +23,8 @@ export default { WhoToFollowPanel, ChatPanel, MediaModal, - SideDrawer + SideDrawer, + MobilePostStatusModal }, data: () => ({ mobileActivePanel: 'timeline', diff --git a/src/App.scss b/src/App.scss index a0d1a804..598735d9 100644 --- a/src/App.scss +++ b/src/App.scss @@ -671,6 +671,31 @@ nav { border-radius: var(--inputRadius, $fallback--inputRadius); } +@keyframes modal-background-fadein { + from { + background-color: rgba(0, 0, 0, 0); + } + to { + background-color: rgba(0, 0, 0, 0.5); + } +} + +.modal-view { + z-index: 1000; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + justify-content: center; + align-items: center; + overflow: auto; + animation-duration: 0.2s; + background-color: rgba(0, 0, 0, 0.5); + animation-name: modal-background-fadein; +} + .button-icon { font-size: 1.2em; } diff --git a/src/App.vue b/src/App.vue index acbbeb75..4fff3d1d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -50,6 +50,7 @@ <media-modal></media-modal> </div> <chat-panel :floating="true" v-if="currentUser && chat" class="floating-chat mobile-hidden"></chat-panel> + <MobilePostStatusModal /> </div> </template> diff --git a/src/boot/routes.js b/src/boot/routes.js index cfbcb1fe..7e54a98b 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -13,7 +13,6 @@ import FollowRequests from 'components/follow_requests/follow_requests.vue' import OAuthCallback from 'components/oauth_callback/oauth_callback.vue' import UserSearch from 'components/user_search/user_search.vue' import Notifications from 'components/notifications/notifications.vue' -import UserPanel from 'components/user_panel/user_panel.vue' import LoginForm from 'components/login_form/login_form.vue' import ChatPanel from 'components/chat_panel/chat_panel.vue' import WhoToFollow from 'components/who_to_follow/who_to_follow.vue' @@ -43,7 +42,6 @@ export default (store) => { { name: 'friend-requests', path: '/friend-requests', component: FollowRequests }, { name: 'user-settings', path: '/user-settings', component: UserSettings }, { name: 'notifications', path: '/:username/notifications', component: Notifications }, - { name: 'new-status', path: '/:username/new-status', component: UserPanel }, { name: 'login', path: '/login', component: LoginForm }, { name: 'chat', path: '/chat', component: ChatPanel, props: () => ({ floating: false }) }, { name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) }, diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue index 427bf12b..7f666603 100644 --- a/src/components/media_modal/media_modal.vue +++ b/src/components/media_modal/media_modal.vue @@ -1,5 +1,5 @@ <template> - <div class="modal-view" v-if="showing" @click.prevent="hide"> + <div class="modal-view media-modal-view" v-if="showing" @click.prevent="hide"> <img class="modal-image" v-if="type === 'image'" :src="currentMedia.url"></img> <VideoAttachment class="modal-image" @@ -32,18 +32,7 @@ <style lang="scss"> @import '../../_variables.scss'; -.modal-view { - z-index: 1000; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - justify-content: center; - align-items: center; - background-color: rgba(0, 0, 0, 0.5); - +.media-modal-view { &:hover { .modal-view-button-arrow { opacity: 0.75; diff --git a/src/components/mobile_post_status_modal/mobile_post_status_modal.js b/src/components/mobile_post_status_modal/mobile_post_status_modal.js new file mode 100644 index 00000000..2f24dd08 --- /dev/null +++ b/src/components/mobile_post_status_modal/mobile_post_status_modal.js @@ -0,0 +1,91 @@ +import PostStatusForm from '../post_status_form/post_status_form.vue' +import { throttle } from 'lodash' + +const MobilePostStatusModal = { + components: { + PostStatusForm + }, + data () { + return { + hidden: false, + postFormOpen: false, + scrollingDown: false, + inputActive: false, + oldScrollPos: 0, + amountScrolled: 0 + } + }, + created () { + window.addEventListener('scroll', this.handleScroll) + window.addEventListener('resize', this.handleOSK) + }, + destroyed () { + window.removeEventListener('scroll', this.handleScroll) + window.removeEventListener('resize', this.handleOSK) + }, + computed: { + currentUser () { + return this.$store.state.users.currentUser + }, + isHidden () { + return this.hidden || this.inputActive + } + }, + methods: { + openPostForm () { + this.postFormOpen = true + this.hidden = true + + const el = this.$el.querySelector('textarea') + this.$nextTick(function () { + el.focus() + }) + }, + closePostForm () { + this.postFormOpen = false + this.hidden = false + }, + handleOSK () { + // This is a big hack: we're guessing from changed window sizes if the + // on-screen keyboard is active or not. This is only really important + // for phones in portrait mode and it's more important to show the button + // in normal scenarios on all phones, than it is to hide it when the + // keyboard is active. + // Guesswork based on https://www.mydevice.io/#compare-devices + + // for example, iphone 4 and android phones from the same time period + const smallPhone = window.innerWidth < 350 + const smallPhoneKbOpen = smallPhone && window.innerHeight < 345 + + const biggerPhone = !smallPhone && window.innerWidth < 450 + const biggerPhoneKbOpen = biggerPhone && window.innerHeight < 560 + if (smallPhoneKbOpen || biggerPhoneKbOpen) { + this.inputActive = true + } else { + this.inputActive = false + } + }, + handleScroll: throttle(function () { + const scrollAmount = window.scrollY - this.oldScrollPos + const scrollingDown = scrollAmount > 0 + + if (scrollingDown !== this.scrollingDown) { + this.amountScrolled = 0 + this.scrollingDown = scrollingDown + if (!scrollingDown) { + this.hidden = false + } + } else if (scrollingDown) { + this.amountScrolled += scrollAmount + if (this.amountScrolled > 100 && !this.hidden) { + this.hidden = true + } + } + + this.oldScrollPos = window.scrollY + this.scrollingDown = scrollingDown + }, 100) + } +} + +export default MobilePostStatusModal diff --git a/src/components/mobile_post_status_modal/mobile_post_status_modal.vue b/src/components/mobile_post_status_modal/mobile_post_status_modal.vue new file mode 100644 index 00000000..0a451c28 --- /dev/null +++ b/src/components/mobile_post_status_modal/mobile_post_status_modal.vue @@ -0,0 +1,76 @@ +<template> +<div v-if="currentUser"> + <div + class="post-form-modal-view modal-view" + v-show="postFormOpen" + @click="closePostForm" + > + <div class="post-form-modal-panel panel" @click.stop=""> + <div class="panel-heading">{{$t('post_status.new_status')}}</div> + <PostStatusForm class="panel-body" @posted="closePostForm"/> + </div> + </div> + <button + class="new-status-button" + :class="{ 'hidden': isHidden }" + @click="openPostForm" + > + <i class="icon-edit" /> + </button> +</div> +</template> + +<script src="./mobile_post_status_modal.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.post-form-modal-view { + max-height: 100%; + display: block; +} + +.post-form-modal-panel { + flex-shrink: 0; + margin: 25% 0 4em 0; + width: 100%; +} + +.new-status-button { + width: 5em; + height: 5em; + border-radius: 100%; + position: fixed; + bottom: 1.5em; + right: 1.5em; + // TODO: this needs its own color, it has to stand out enough and link color + // is not very optimal for this particular use. + background-color: $fallback--fg; + background-color: var(--btn, $fallback--fg); + display: flex; + justify-content: center; + align-items: center; + box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3), 0px 4px 6px rgba(0, 0, 0, 0.3); + z-index: 10; + + transition: 0.35s transform; + transition-timing-function: cubic-bezier(0, 1, 0.5, 1); + + &.hidden { + transform: translateY(150%); + } + + i { + font-size: 1.5em; + color: $fallback--text; + color: var(--text, $fallback--text); + } +} + +@media all and (min-width: 801px) { + .new-status-button { + display: none; + } +} + +</style> diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index b608b008..95ee21b4 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -15,12 +15,7 @@ </div> </div> <ul> - <li v-if="currentUser" @click="toggleDrawer"> - <router-link :to="{ name: 'new-status', params: { username: currentUser.screen_name } }"> - {{ $t("post_status.new_status") }} - </router-link> - </li> - <li v-else @click="toggleDrawer"> + <li v-if="!currentUser" @click="toggleDrawer"> <router-link :to="{ name: 'login' }"> {{ $t("login.login") }} </router-link> @@ -119,14 +114,14 @@ } .side-drawer-container-open { - transition-delay: 0.0s; - transition-property: left; + transition: 0.35s; + transition-property: background-color; + background-color: rgba(0, 0, 0, 0.5); } .side-drawer-container-closed { left: -100%; - transition-delay: 0.5s; - transition-property: left; + background-color: rgba(0, 0, 0, 0); } .side-drawer-click-outside {