From 2c6ec37e6a796d2d7be7be58ac0e3c6c8bbd6a18 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Mon, 18 Feb 2019 05:03:26 +0000 Subject: [PATCH 01/53] media description support --- src/components/attachment/attachment.vue | 2 +- src/components/media_modal/media_modal.vue | 2 ++ src/components/post_status_form/post_status_form.js | 3 +++ src/components/post_status_form/post_status_form.vue | 6 ++++++ src/components/still-image/still-image.js | 3 ++- src/components/still-image/still-image.vue | 2 ++ src/i18n/en.json | 1 + src/services/api/api.service.js | 4 +++- src/services/status_poster/status_poster.service.js | 5 +++-- 9 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index a7e217c1..98f772c0 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -51,7 +51,6 @@ :class="{'hidden': hidden && preloadImage }" :href="attachment.url" target="_blank" - :title="attachment.description" @click="openModal" > diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue index 80d2a8b9..d52f00a5 100644 --- a/src/components/media_modal/media_modal.vue +++ b/src/components/media_modal/media_modal.vue @@ -8,6 +8,8 @@ v-if="type === 'image'" class="modal-image" :src="currentMedia.url" + :alt="currentMedia.description" + :title="currentMedia.description" @touchstart.stop="mediaTouchStart" @touchmove.stop="mediaTouchMove" @click="hide" diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 9027566f..df2999f2 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -78,6 +78,7 @@ const PostStatusForm = { nsfw: false, files: [], poll: {}, + mediaDescriptions: {}, visibility: scope, contentType }, @@ -190,6 +191,7 @@ const PostStatusForm = { visibility: newStatus.visibility, sensitive: newStatus.nsfw, media: newStatus.files, + mediaDescriptions: newStatus.mediaDescriptions || {}, store: this.$store, inReplyToStatusId: this.replyTo, contentType: newStatus.contentType, @@ -200,6 +202,7 @@ const PostStatusForm = { status: '', spoilerText: '', files: [], + mediaDescriptions: {}, visibility: newStatus.visibility, contentType: newStatus.contentType, poll: {} diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index e3d8d087..551df948 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -266,6 +266,7 @@ :href="file.url" >{{ file.url }} +
{ const form = new FormData() const pollOptions = poll.options || [] @@ -644,6 +645,7 @@ const postStatus = ({ form.append('poll[options][]', option) }) } + form.append('descriptions', JSON.stringify(mediaDescriptions)) if (inReplyToStatusId) { form.append('in_reply_to_id', inReplyToStatusId) } diff --git a/src/services/status_poster/status_poster.service.js b/src/services/status_poster/status_poster.service.js index 9e904d3a..090ff673 100644 --- a/src/services/status_poster/status_poster.service.js +++ b/src/services/status_poster/status_poster.service.js @@ -1,7 +1,7 @@ import { map } from 'lodash' import apiService from '../api/api.service.js' -const postStatus = ({ store, status, spoilerText, visibility, sensitive, poll, media = [], inReplyToStatusId = undefined, contentType = 'text/plain' }) => { +const postStatus = ({ store, status, spoilerText, visibility, sensitive, poll, media = [], inReplyToStatusId = undefined, contentType = 'text/plain', mediaDescriptions = {} }) => { const mediaIds = map(media, 'id') return apiService.postStatus({ @@ -13,7 +13,8 @@ const postStatus = ({ store, status, spoilerText, visibility, sensitive, poll, m mediaIds, inReplyToStatusId, contentType, - poll }) + poll, + mediaDescriptions }) .then((data) => { if (!data.error) { store.dispatch('addNewStatuses', { From 1ff55e17b1931f0578458256f4ca912e3c50e0cc Mon Sep 17 00:00:00 2001 From: xenofem Date: Sun, 28 Jun 2020 02:16:58 -0400 Subject: [PATCH 02/53] fix invalid end tag on input element --- src/components/post_status_form/post_status_form.vue | 2 +- 1 file changed, 1 insertion(+), 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 551df948..6eefc853 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -266,7 +266,7 @@ :href="file.url" >{{ file.url }}
- +
Date: Sun, 28 Jun 2020 02:31:57 -0400 Subject: [PATCH 03/53] fix vue style warnings --- src/components/post_status_form/post_status_form.vue | 6 +++++- src/components/still-image/still-image.vue | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 6eefc853..49291455 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -266,7 +266,11 @@ :href="file.url" >{{ file.url }}
- +
Date: Sun, 28 Jun 2020 12:16:41 +0300 Subject: [PATCH 04/53] add rich text preview --- .../post_status_form/post_status_form.js | 52 +++++++- .../post_status_form/post_status_form.vue | 113 ++++++++++++++++-- src/i18n/en.json | 5 +- src/services/api/api.service.js | 14 +-- .../status_poster/status_poster.service.js | 19 ++- 5 files changed, 180 insertions(+), 23 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 9027566f..3f7e36a6 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -3,6 +3,7 @@ import MediaUpload from '../media_upload/media_upload.vue' import ScopeSelector from '../scope_selector/scope_selector.vue' import EmojiInput from '../emoji_input/emoji_input.vue' import PollForm from '../poll/poll_form.vue' +import StatusContent from '../status_content/status_content.vue' import fileTypeService from '../../services/file_type/file_type.service.js' import { findOffset } from '../../services/offset_finder/offset_finder.service.js' import { reject, map, uniqBy } from 'lodash' @@ -38,7 +39,8 @@ const PostStatusForm = { EmojiInput, PollForm, ScopeSelector, - Checkbox + Checkbox, + StatusContent }, mounted () { this.resize(this.$refs.textarea) @@ -84,7 +86,9 @@ const PostStatusForm = { caret: 0, pollFormVisible: false, showDropIcon: 'hide', - dropStopTimeout: null + dropStopTimeout: null, + preview: null, + previewLoading: false } }, computed: { @@ -163,8 +167,20 @@ const PostStatusForm = { this.newStatus.poll && this.newStatus.poll.error }, + showPreview () { + return !!this.preview || this.previewLoading + }, ...mapGetters(['mergedConfig']) }, + watch: { + 'newStatus.contentType': function (newType) { + if (newType === 'text/plain') { + this.closePreview() + } else if (this.preview) { + this.previewStatus(this.newStatus) + } + } + }, methods: { postStatus (newStatus) { if (this.posting) { return } @@ -218,6 +234,38 @@ const PostStatusForm = { this.posting = false }) }, + previewStatus (newStatus) { + this.previewLoading = true + statusPoster.postStatus({ + status: newStatus.status, + spoilerText: newStatus.spoilerText || null, + visibility: newStatus.visibility, + sensitive: newStatus.nsfw, + media: newStatus.files, + store: this.$store, + inReplyToStatusId: this.replyTo, + contentType: newStatus.contentType, + poll: {}, + preview: true + }).then((data) => { + // Don't apply preview if not loading, because it means + // user has closed the preview manually. + if (!this.previewLoading) return + if (!data.error) { + this.preview = data + } else { + this.preview = { error: data.error } + } + }).catch((error) => { + this.preview = { error } + }).finally(() => { + this.previewLoading = false + }) + }, + closePreview () { + this.preview = null + this.previewLoading = false + }, addMediaFile (fileInfo) { this.newStatus.files.push(fileInfo) }, diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index e3d8d087..9931f5ca 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -16,6 +16,58 @@ @drop.stop="fileDrop" />
+ + {{ $t('status.preview') }} + +
+ + + {{ $t('status.status_preview') }} + + + + {{ $t('status.preview_update') }} + + + + + +
+ {{ $t('general.loading') }} +
+
+ {{ preview.error }} +
+ +
{ const form = new FormData() const pollOptions = poll.options || [] @@ -647,6 +648,9 @@ const postStatus = ({ if (inReplyToStatusId) { form.append('in_reply_to_id', inReplyToStatusId) } + if (preview) { + form.append('preview', 'true') + } return fetch(MASTODON_POST_STATUS_URL, { body: form, @@ -654,13 +658,7 @@ const postStatus = ({ headers: authHeaders(credentials) }) .then((response) => { - if (response.ok) { - return response.json() - } else { - return { - error: response - } - } + return response.json() }) .then((data) => data.error ? data : parseStatus(data)) } diff --git a/src/services/status_poster/status_poster.service.js b/src/services/status_poster/status_poster.service.js index 9e904d3a..86fc5601 100644 --- a/src/services/status_poster/status_poster.service.js +++ b/src/services/status_poster/status_poster.service.js @@ -1,7 +1,18 @@ import { map } from 'lodash' import apiService from '../api/api.service.js' -const postStatus = ({ store, status, spoilerText, visibility, sensitive, poll, media = [], inReplyToStatusId = undefined, contentType = 'text/plain' }) => { +const postStatus = ({ + store, + status, + spoilerText, + visibility, + sensitive, + poll, + media = [], + inReplyToStatusId = undefined, + contentType = 'text/plain', + preview = false +}) => { const mediaIds = map(media, 'id') return apiService.postStatus({ @@ -13,9 +24,11 @@ const postStatus = ({ store, status, spoilerText, visibility, sensitive, poll, m mediaIds, inReplyToStatusId, contentType, - poll }) + poll, + preview + }) .then((data) => { - if (!data.error) { + if (!data.error && !preview) { store.dispatch('addNewStatuses', { statuses: [data], timeline: 'friends', From 3d5da3caf99e4a81e1837ecf2c7dc94defbfb569 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sun, 28 Jun 2020 12:35:05 +0300 Subject: [PATCH 05/53] update styles in preview heading --- .../post_status_form/post_status_form.vue | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 9931f5ca..848d6af7 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -18,7 +18,7 @@
@@ -29,7 +29,7 @@ class="preview-container" > - + {{ $t('status.status_preview') }} {{ $t('status.preview_update') }} @@ -382,9 +382,6 @@ .preview-start { margin-left: auto; cursor: pointer; - &:hover { - text-decoration: underline; - } } .preview-container { @@ -394,6 +391,8 @@ .preview-heading { display: flex; width: 100%; + color: $fallback--faint; + color: var(--faint, $fallback--faint); } .preview-title { @@ -406,15 +405,12 @@ .preview-update { cursor: pointer; - &:hover { - text-decoration: underline; - } } .preview-error { + font-style: italic; color: $fallback--faint; color: var(--faint, $fallback--faint); - font-style: italic; } .preview-status { From 9d6f2332024a86a6d51ec92fd2fb77e27f749fd1 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sun, 28 Jun 2020 12:48:17 +0300 Subject: [PATCH 06/53] fix preview attachments getting wrong styles --- src/components/post_status_form/post_status_form.vue | 2 +- 1 file changed, 1 insertion(+), 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 848d6af7..31fac47b 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -499,7 +499,7 @@ flex-direction: column; } - .attachments { + .media-upload-wrapper .attachments { padding: 0 0.5em; .attachment { From cecf3d4f8999d247378bfb4ee444348d5d4eba0a Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sun, 28 Jun 2020 13:03:25 +0300 Subject: [PATCH 07/53] remove unnecessary type=button --- src/components/post_status_form/post_status_form.vue | 1 - 1 file changed, 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 31fac47b..f1455cfd 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -19,7 +19,6 @@ {{ $t('status.preview') }} From 6e2de2036755287de0a6123236644842a91b9fea Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sun, 28 Jun 2020 13:17:04 +0300 Subject: [PATCH 08/53] show preview with plaintext too --- src/components/post_status_form/post_status_form.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index f1455cfd..0bee8bb5 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -17,14 +17,14 @@ />
{{ $t('status.preview') }}
From 0287d5c07e28f54be09825dcc5280dbef1c740c2 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sun, 28 Jun 2020 13:29:07 +0300 Subject: [PATCH 09/53] update changelog with status previews --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 887588f3..feaa354d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Autocomplete domains from list of known instances - 'Bot' settings option and badge - Added profile meta data fields that can be set in profile settings +- Added status preview option to preview your statuses before posting ### Changed - Registration page no longer requires email if the server is configured not to require it From 3c47036101c4767b4e2fb316d9ea35535197af79 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sun, 28 Jun 2020 15:14:01 +0300 Subject: [PATCH 10/53] add automatic updating --- .../post_status_form/post_status_form.js | 14 +++++++++++++- .../post_status_form/post_status_form.vue | 7 ------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 3f7e36a6..75b02ffb 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -6,7 +6,7 @@ import PollForm from '../poll/poll_form.vue' import StatusContent from '../status_content/status_content.vue' import fileTypeService from '../../services/file_type/file_type.service.js' import { findOffset } from '../../services/offset_finder/offset_finder.service.js' -import { reject, map, uniqBy } from 'lodash' +import { reject, map, uniqBy, debounce } from 'lodash' import suggestor from '../emoji_input/suggestor.js' import { mapGetters } from 'vuex' import Checkbox from '../checkbox/checkbox.vue' @@ -228,6 +228,7 @@ const PostStatusForm = { el.style.height = 'auto' el.style.height = undefined this.error = null + this.closePreview() } else { this.error = data.error } @@ -262,16 +263,25 @@ const PostStatusForm = { this.previewLoading = false }) }, + debouncePreviewStatus: debounce(function (newStatus) { + this.previewStatus(newStatus) + }, 750), + autoPreview () { + this.previewLoading = true + this.debouncePreviewStatus(this.newStatus) + }, closePreview () { this.preview = null this.previewLoading = false }, addMediaFile (fileInfo) { this.newStatus.files.push(fileInfo) + this.autoPreview() }, removeMediaFile (fileInfo) { let index = this.newStatus.files.indexOf(fileInfo) this.newStatus.files.splice(index, 1) + this.autoPreview() }, uploadFailed (errString, templateArgs) { templateArgs = templateArgs || {} @@ -287,6 +297,7 @@ const PostStatusForm = { return fileTypeService.fileType(fileInfo.mimetype) }, paste (e) { + this.autoPreview() this.resize(e) if (e.clipboardData.files.length > 0) { // prevent pasting of file as text @@ -321,6 +332,7 @@ const PostStatusForm = { } }, onEmojiInputInput (e) { + this.autoPreview() this.$nextTick(() => { this.resize(this.$refs['textarea']) }) diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 0bee8bb5..31a4b388 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -35,13 +35,6 @@ v-if="previewLoading" class="icon-spin3 animate-spin" /> - - {{ $t('status.preview_update') }} - Date: Sun, 28 Jun 2020 15:43:08 +0300 Subject: [PATCH 11/53] make line sizes match for errors/loading and statuses, make X hitbox bigger, remove attachments, add shorter custom message for empty status preview. fix auto update triggering --- .../post_status_form/post_status_form.js | 23 +++++++++++-------- .../post_status_form/post_status_form.vue | 16 +++++-------- src/i18n/en.json | 2 +- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 75b02ffb..bb5dbf97 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -170,6 +170,9 @@ const PostStatusForm = { showPreview () { return !!this.preview || this.previewLoading }, + emptyStatus () { + return this.newStatus.status === '' && this.newStatus.files.length === 0 + }, ...mapGetters(['mergedConfig']) }, watch: { @@ -185,12 +188,9 @@ const PostStatusForm = { postStatus (newStatus) { if (this.posting) { return } if (this.submitDisabled) { return } - - if (this.newStatus.status === '') { - if (this.newStatus.files.length === 0) { - this.error = 'Cannot post an empty status with no files' - return - } + if (this.emptyStatus) { + this.error = 'Cannot post an empty status with no files' + return } const poll = this.pollFormVisible ? this.newStatus.poll : {} @@ -236,13 +236,19 @@ const PostStatusForm = { }) }, previewStatus (newStatus) { + if (this.emptyStatus) { + this.preview = { error: this.$t('status.preview_empty') } + this.previewLoading = false + return + } + this.previewLoading = true statusPoster.postStatus({ status: newStatus.status, spoilerText: newStatus.spoilerText || null, visibility: newStatus.visibility, sensitive: newStatus.nsfw, - media: newStatus.files, + media: [], store: this.$store, inReplyToStatusId: this.replyTo, contentType: newStatus.contentType, @@ -267,6 +273,7 @@ const PostStatusForm = { this.previewStatus(newStatus) }, 750), autoPreview () { + if (!this.preview) return this.previewLoading = true this.debouncePreviewStatus(this.newStatus) }, @@ -276,12 +283,10 @@ const PostStatusForm = { }, addMediaFile (fileInfo) { this.newStatus.files.push(fileInfo) - this.autoPreview() }, removeMediaFile (fileInfo) { let index = this.newStatus.files.indexOf(fileInfo) this.newStatus.files.splice(index, 1) - this.autoPreview() }, uploadFailed (errString, templateArgs) { templateArgs = templateArgs || {} diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 31a4b388..8201911e 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -35,12 +35,10 @@ v-if="previewLoading" class="icon-spin3 animate-spin" /> - - - + />
Date: Sun, 28 Jun 2020 16:40:39 +0300 Subject: [PATCH 12/53] remove panel-footer in userpanel, simplify preview header, fix word-wrap in preview --- .../post_status_form/post_status_form.js | 17 +++-- .../post_status_form/post_status_form.vue | 74 +++++++++---------- src/components/status/status.vue | 3 - .../status_content/status_content.vue | 3 + src/components/user_panel/user_panel.vue | 4 +- src/i18n/en.json | 1 - 6 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index bb5dbf97..732691e7 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -235,13 +235,13 @@ const PostStatusForm = { this.posting = false }) }, - previewStatus (newStatus) { + previewStatus () { if (this.emptyStatus) { this.preview = { error: this.$t('status.preview_empty') } this.previewLoading = false return } - + const newStatus = this.newStatus this.previewLoading = true statusPoster.postStatus({ status: newStatus.status, @@ -269,18 +269,23 @@ const PostStatusForm = { this.previewLoading = false }) }, - debouncePreviewStatus: debounce(function (newStatus) { - this.previewStatus(newStatus) - }, 750), + debouncePreviewStatus: debounce(function () { this.previewStatus() }, 750), autoPreview () { if (!this.preview) return this.previewLoading = true - this.debouncePreviewStatus(this.newStatus) + this.debouncePreviewStatus() }, closePreview () { this.preview = null this.previewLoading = false }, + togglePreview () { + if (this.showPreview) { + this.closePreview() + } else { + this.previewStatus() + } + }, addMediaFile (fileInfo) { this.newStatus.files.push(fileInfo) }, diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 8201911e..0cebd36e 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -16,30 +16,26 @@ @drop.stop="fileDrop" />
- - {{ $t('status.preview') }} - +
- - - {{ $t('status.status_preview') }} - - - -
- +
Date: Sun, 28 Jun 2020 16:05:22 -0400 Subject: [PATCH 13/53] suppress enter key on media description input field --- src/components/post_status_form/post_status_form.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 49291455..76fa30d8 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -270,6 +270,7 @@ v-model="newStatus.mediaDescriptions[file.id]" type="text" :placeholder="$t('post_status.media_description')" + @keydown.enter.prevent="" >
From 96d2c86d3bc1c26da2ca46ec04fb27a9de348e47 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Mon, 29 Jun 2020 14:48:22 +0300 Subject: [PATCH 14/53] change placeholders to use descriptions, use icons with placeholders, change uploads to use attachment component --- src/components/attachment/attachment.js | 28 ++++++--- src/components/attachment/attachment.vue | 28 +++++---- src/components/media_modal/media_modal.vue | 8 +++ .../post_status_form/post_status_form.js | 4 +- .../post_status_form/post_status_form.vue | 60 ++++++------------- .../video_attachment/video_attachment.vue | 2 + src/modules/media_viewer.js | 2 +- static/fontello.json | 6 ++ 8 files changed, 75 insertions(+), 63 deletions(-) diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index b832e10f..561a5a98 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -8,7 +8,6 @@ const Attachment = { props: [ 'attachment', 'nsfw', - 'statusId', 'size', 'allowPlay', 'setMedia', @@ -30,9 +29,21 @@ const Attachment = { VideoAttachment }, computed: { - usePlaceHolder () { + usePlaceholder () { return this.size === 'hide' || this.type === 'unknown' }, + placeholderName () { + if (this.attachment.description === '' || !this.attachment.description) { + return this.type.toUpperCase() + } + return this.attachment.description + }, + placeholderIconClass () { + if (this.type === 'image') return 'icon-picture' + if (this.type === 'video') return 'icon-video' + if (this.type === 'audio') return 'icon-music' + return 'icon-link' + }, referrerpolicy () { return this.$store.state.instance.mediaProxyAvailable ? '' : 'no-referrer' }, @@ -51,6 +62,12 @@ const Attachment = { fullwidth () { return this.type === 'html' || this.type === 'audio' }, + useModal () { + return this.size === 'hide' ? ['image', 'video', 'audio'] + : this.mergedConfig.playVideosInModal + ? ['image', 'video'] + : ['image'] + }, ...mapGetters(['mergedConfig']) }, methods: { @@ -60,12 +77,7 @@ const Attachment = { } }, openModal (event) { - const modalTypes = this.mergedConfig.playVideosInModal - ? ['image', 'video'] - : ['image'] - if (fileTypeService.fileMatchesSomeType(modalTypes, this.attachment) || - this.usePlaceHolder - ) { + if (this.useModal) { event.stopPropagation() event.preventDefault() this.setMedia() diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 98f772c0..a18bf186 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -1,6 +1,6 @@ diff --git a/src/components/global_notice_list/global_notice_list.js b/src/components/global_notice_list/global_notice_list.js new file mode 100644 index 00000000..3af29c23 --- /dev/null +++ b/src/components/global_notice_list/global_notice_list.js @@ -0,0 +1,15 @@ + +const GlobalNoticeList = { + computed: { + notices () { + return this.$store.state.interface.globalNotices + } + }, + methods: { + closeNotice (notice) { + this.$store.dispatch('removeGlobalNotice', notice) + } + } +} + +export default GlobalNoticeList diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue new file mode 100644 index 00000000..0e4285cc --- /dev/null +++ b/src/components/global_notice_list/global_notice_list.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/main.js b/src/main.js index a7294ea0..5bddc76e 100644 --- a/src/main.js +++ b/src/main.js @@ -62,14 +62,14 @@ const persistedStateOptions = { }; (async () => { - let persistedState - let storageError = 'none' + let storageError = false + const plugins = [pushNotifications] try { - persistedState = await createPersistedState(persistedStateOptions) + const persistedState = await createPersistedState(persistedStateOptions) + plugins.push(persistedState) } catch (e) { console.error(e) - storageError = 'show' - persistedState = _ => _ + storageError = true } const store = new Vuex.Store({ modules: { @@ -93,11 +93,13 @@ const persistedStateOptions = { polls: pollsModule, postStatus: postStatusModule }, - plugins: [persistedState, pushNotifications], + plugins, strict: false // Socket modifies itself, let's ignore this for now. // strict: process.env.NODE_ENV !== 'production' }) - store.dispatch('setStorageError', storageError) + if (storageError) { + store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' }) + } afterStoreSetup({ store, i18n }) })() diff --git a/src/modules/interface.js b/src/modules/interface.js index 4b5b5b5d..338ef651 100644 --- a/src/modules/interface.js +++ b/src/modules/interface.js @@ -8,14 +8,14 @@ const defaultState = { noticeClearTimeout: null, notificationPermission: null }, - storageError: 'none', browserSupport: { cssFilter: window.CSS && window.CSS.supports && ( window.CSS.supports('filter', 'drop-shadow(0 0)') || window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)') ) }, - mobileLayout: false + mobileLayout: false, + globalNotices: [] } const interfaceMod = { @@ -60,8 +60,11 @@ const interfaceMod = { state.settingsModalLoaded = true } }, - setStorageError (state, value) { - state.storageError = value + pushGlobalNotice (state, notice) { + state.globalNotices.push(notice) + }, + removeGlobalNotice (state, notice) { + state.globalNotices = state.globalNotices.filter(n => n !== notice) } }, actions: { @@ -86,8 +89,26 @@ const interfaceMod = { togglePeekSettingsModal ({ commit }) { commit('togglePeekSettingsModal') }, - setStorageError ({ commit }, value) { - commit('setStorageError', value) + pushGlobalNotice ( + { commit, dispatch }, + { + messageKey, + messageArgs = {}, + level = 'error', + timeout = 0 + }) { + const notice = { + messageKey, + messageArgs, + level + } + if (timeout) { + setTimeout(() => dispatch('removeGlobalNotice', notice), timeout) + } + commit('pushGlobalNotice', notice) + }, + removeGlobalNotice ({ commit }, notice) { + commit('removeGlobalNotice', notice) } } } diff --git a/src/services/theme_data/pleromafe.js b/src/services/theme_data/pleromafe.js index b577cfab..83c878ed 100644 --- a/src/services/theme_data/pleromafe.js +++ b/src/services/theme_data/pleromafe.js @@ -34,7 +34,8 @@ export const DEFAULT_OPACITY = { alert: 0.5, input: 0.5, faint: 0.5, - underlay: 0.15 + underlay: 0.15, + alertPopup: 0.85 } /** SUBJECT TO CHANGE IN THE FUTURE, this is all beta @@ -627,6 +628,39 @@ export const SLOT_INHERITANCE = { textColor: true }, + alertPopupError: { + depends: ['alertError'], + opacity: 'alertPopup' + }, + alertPopupErrorText: { + depends: ['alertErrorText'], + layer: 'popover', + variant: 'alertPopupError', + textColor: true + }, + + alertPopupWarning: { + depends: ['alertWarning'], + opacity: 'alertPopup' + }, + alertPopupWarningText: { + depends: ['alertWarningText'], + layer: 'popover', + variant: 'alertPopupWarning', + textColor: true + }, + + alertPopupNeutral: { + depends: ['alertNeutral'], + opacity: 'alertPopup' + }, + alertPopupNeutralText: { + depends: ['alertNeutralText'], + layer: 'popover', + variant: 'alertPopupNeutral', + textColor: true + }, + badgeNotification: '--cRed', badgeNotificationText: { depends: ['text', 'badgeNotification'], From 685ab4f33ee57e1dc4997b58314e5c0d38581e9d Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Thu, 2 Jul 2020 10:46:43 +0300 Subject: [PATCH 29/53] make the addNotice dispatch return the notice --- src/modules/interface.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/interface.js b/src/modules/interface.js index 338ef651..e31630fc 100644 --- a/src/modules/interface.js +++ b/src/modules/interface.js @@ -106,6 +106,7 @@ const interfaceMod = { setTimeout(() => dispatch('removeGlobalNotice', notice), timeout) } commit('pushGlobalNotice', notice) + return notice }, removeGlobalNotice ({ commit }, notice) { commit('removeGlobalNotice', notice) From b8ed904c0c4a387dccc1b107452bc0d669c174e2 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Thu, 2 Jul 2020 14:28:35 +0300 Subject: [PATCH 30/53] change px to em --- src/components/post_status_form/post_status_form.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 0b79c9d8..442b9297 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -374,7 +374,7 @@ .media-upload-wrapper { margin-right: .2em; margin-bottom: .5em; - width: 260px; + width: 18em; .icon-cancel { display: inline-block; @@ -391,11 +391,11 @@ img, video { object-fit: contain; - max-height: 140px; + max-height: 10em; } .video { - max-height: 140px; + max-height: 10em; } input { From 9cac5d94dd78ea331254307871e876da7e266a6f Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Thu, 2 Jul 2020 15:17:58 +0300 Subject: [PATCH 31/53] change alert popup alpha --- src/services/theme_data/pleromafe.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/theme_data/pleromafe.js b/src/services/theme_data/pleromafe.js index 83c878ed..6b25cd6f 100644 --- a/src/services/theme_data/pleromafe.js +++ b/src/services/theme_data/pleromafe.js @@ -35,7 +35,7 @@ export const DEFAULT_OPACITY = { input: 0.5, faint: 0.5, underlay: 0.15, - alertPopup: 0.85 + alertPopup: 0.95 } /** SUBJECT TO CHANGE IN THE FUTURE, this is all beta From 08b593746faf54ac41df25bb2cc5a1f72ae1a9b5 Mon Sep 17 00:00:00 2001 From: Ilja Date: Fri, 3 Jul 2020 10:17:42 +0000 Subject: [PATCH 32/53] FE part of BE issue 1586 provide index md * I added an index.md which will be the landing page for the docs. It has an explanation of Pleroma-FE from the user point of view * See also BE MR: https://git.pleroma.social/pleroma/pleroma/-/merge_requests/2669 * And issue: https://git.pleroma.social/pleroma/pleroma/-/issues/1586 --- docs/USER_GUIDE.md | 2 -- docs/index.md | 8 ++++++++ 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 docs/index.md diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index f417f33d..241ad331 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -8,8 +8,6 @@ > > --Catbag -Pleroma-FE user interface is modeled after Qvitter which is modeled after older Twitter design. It provides a simple 2-column interface for microblogging. While being simple by default it also provides many powerful customization options. - ## Posting, reading, basic functions. After registering and logging in you're presented with your timeline in right column and new post form with timeline list and notifications in the left column. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..8764f9ab --- /dev/null +++ b/docs/index.md @@ -0,0 +1,8 @@ +# Introduction to Pleroma-FE +## What is Pleroma-FE? + +Pleroma-FE is the default user-facing frontend for Pleroma. It's user interface is modeled after Qvitter which is modeled after an older Twitter design. It provides a simple 2-column interface for microblogging. While being simple by default it also provides many powerful customization options. + +## How can I use it? + +If your instance uses Pleroma-FE, you can acces it by going to your instance (e.g. ). You can read more about it's basic functionality in the [Pleroma-FE User Guide](./USER_GUIDE.md). We also have [a guide for administrators](./CONFIGURATION.md) and for [hackers/contributors](./HACKING.md). From de291e2e33f1d9e04b27ed30ba3b012d73178e63 Mon Sep 17 00:00:00 2001 From: Eugenij Date: Fri, 3 Jul 2020 19:45:49 +0000 Subject: [PATCH 33/53] Add bookmarks Co-authored-by: jared --- CHANGELOG.md | 1 + package.json | 1 + src/boot/routes.js | 2 + .../bookmark_timeline/bookmark_timeline.js | 17 ++++++++ .../bookmark_timeline/bookmark_timeline.vue | 9 ++++ src/components/extra_buttons/extra_buttons.js | 10 +++++ .../extra_buttons/extra_buttons.vue | 16 +++++++ src/components/nav_panel/nav_panel.vue | 5 +++ src/components/side_drawer/side_drawer.vue | 8 ++++ src/components/timeline/timeline.js | 2 +- src/i18n/en.json | 6 ++- src/i18n/ru.json | 7 ++- src/modules/statuses.js | 43 +++++++++++++++---- src/services/api/api.service.js | 34 +++++++++++++-- .../entity_normalizer.service.js | 16 +++++++ .../notifications_fetcher.service.js | 2 +- .../timeline_fetcher.service.js | 17 +++++--- static/fontello.json | 12 ++++++ .../entity_normalizer.spec.js | 22 +++++++++- yarn.lock | 7 +++ 20 files changed, 213 insertions(+), 24 deletions(-) create mode 100644 src/components/bookmark_timeline/bookmark_timeline.js create mode 100644 src/components/bookmark_timeline/bookmark_timeline.vue mode change 100755 => 100644 static/fontello.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d978d362..2595af1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Ability to change user's email - About page - Added remote user redirect +- Bookmarks ### Changed - changed the way fading effects for user profile/long statuses works, now uses css-mask instead of gradient background hacks which weren't exactly compatible with semi-transparent themes ### Fixed diff --git a/package.json b/package.json index c0665f6e..96231171 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "cropperjs": "^1.4.3", "diff": "^3.0.1", "escape-html": "^1.0.3", + "parse-link-header": "^1.0.1", "localforage": "^1.5.0", "phoenix": "^1.3.0", "portal-vue": "^2.1.4", diff --git a/src/boot/routes.js b/src/boot/routes.js index d98a3b50..f63d8adf 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -2,6 +2,7 @@ import PublicTimeline from 'components/public_timeline/public_timeline.vue' import PublicAndExternalTimeline from 'components/public_and_external_timeline/public_and_external_timeline.vue' import FriendsTimeline from 'components/friends_timeline/friends_timeline.vue' import TagTimeline from 'components/tag_timeline/tag_timeline.vue' +import BookmarkTimeline from 'components/bookmark_timeline/bookmark_timeline.vue' import ConversationPage from 'components/conversation-page/conversation-page.vue' import Interactions from 'components/interactions/interactions.vue' import DMs from 'components/dm_timeline/dm_timeline.vue' @@ -40,6 +41,7 @@ export default (store) => { { name: 'public-timeline', path: '/main/public', component: PublicTimeline }, { name: 'friends', path: '/main/friends', component: FriendsTimeline, beforeEnter: validateAuthenticatedRoute }, { name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline }, + { name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline }, { name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } }, { name: 'remote-user-profile-acct', path: '/remote-users/(@?):username([^/@]+)@:hostname([^/@]+)', diff --git a/src/components/bookmark_timeline/bookmark_timeline.js b/src/components/bookmark_timeline/bookmark_timeline.js new file mode 100644 index 00000000..64b69e5d --- /dev/null +++ b/src/components/bookmark_timeline/bookmark_timeline.js @@ -0,0 +1,17 @@ +import Timeline from '../timeline/timeline.vue' + +const Bookmarks = { + computed: { + timeline () { + return this.$store.state.statuses.timelines.bookmarks + } + }, + components: { + Timeline + }, + destroyed () { + this.$store.commit('clearTimeline', { timeline: 'bookmarks' }) + } +} + +export default Bookmarks diff --git a/src/components/bookmark_timeline/bookmark_timeline.vue b/src/components/bookmark_timeline/bookmark_timeline.vue new file mode 100644 index 00000000..8da6884b --- /dev/null +++ b/src/components/bookmark_timeline/bookmark_timeline.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js index e4b19d01..5e0c36bb 100644 --- a/src/components/extra_buttons/extra_buttons.js +++ b/src/components/extra_buttons/extra_buttons.js @@ -34,6 +34,16 @@ const ExtraButtons = { navigator.clipboard.writeText(this.statusLink) .then(() => this.$emit('onSuccess')) .catch(err => this.$emit('onError', err.error.error)) + }, + bookmarkStatus () { + this.$store.dispatch('bookmark', { id: this.status.id }) + .then(() => this.$emit('onSuccess')) + .catch(err => this.$emit('onError', err.error.error)) + }, + unbookmarkStatus () { + this.$store.dispatch('unbookmark', { id: this.status.id }) + .then(() => this.$emit('onSuccess')) + .catch(err => this.$emit('onError', err.error.error)) } }, computed: { diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue index 68db6fd8..7a4e8642 100644 --- a/src/components/extra_buttons/extra_buttons.vue +++ b/src/components/extra_buttons/extra_buttons.vue @@ -40,6 +40,22 @@ > {{ $t("status.unpin") }} + +