aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--CHANGELOG.md25
-rw-r--r--build/webpack.prod.conf.js13
-rw-r--r--changelog.d/add-taiwanese-aka-hokkien-i18n-support.add1
-rw-r--r--changelog.d/adminfe.add1
-rw-r--r--changelog.d/check-changelog.skip0
-rw-r--r--changelog.d/custom-emoji-notif-width.fix1
-rw-r--r--changelog.d/edit-profile-button.fix1
-rw-r--r--changelog.d/mention-twice.fix1
-rw-r--r--changelog.d/mentionsline-shouldbreak.fix1
-rw-r--r--changelog.d/parser.fix1
-rw-r--r--changelog.d/react-button-safari.fix1
-rw-r--r--changelog.d/react-button.fix1
-rw-r--r--changelog.d/scroll-emoji-selector-safari.fix1
-rw-r--r--package.json8
-rw-r--r--src/boot/after_store.js1
-rw-r--r--src/components/emoji_picker/emoji_picker.vue3
-rw-r--r--src/components/post_status_form/post_status_form.js38
-rw-r--r--src/components/post_status_form/post_status_form.vue34
-rw-r--r--src/components/report/report.js5
-rw-r--r--src/components/status/status.js28
-rw-r--r--src/components/status/status.scss18
-rw-r--r--src/components/status/status.vue39
-rw-r--r--src/components/status_content/status_content.js4
-rw-r--r--src/components/status_content/status_content.vue2
-rw-r--r--src/components/timeline/timeline.js3
-rw-r--r--src/i18n/en.json7
-rw-r--r--src/modules/instance.js1
-rw-r--r--src/modules/statuses.js4
-rw-r--r--src/modules/users.js6
-rw-r--r--src/services/api/api.service.js4
-rw-r--r--src/services/entity_normalizer/entity_normalizer.service.js4
-rw-r--r--src/services/html_converter/utility.service.js2
-rw-r--r--src/services/matcher/matcher.service.js7
-rw-r--r--src/services/status_poster/status_poster.service.js2
-rw-r--r--test/unit/specs/services/matcher/matcher.spec.js6
-rwxr-xr-xtools/collect-changelog27
-rw-r--r--yarn.lock24
38 files changed, 286 insertions, 40 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..c5b9ea10
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+/build/webpack.prod.conf.js export-subst
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3fb72931..fe382b96 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,31 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+## 2.6.0
+### Added
+- add the initial i18n translation file for Taiwanese (Hokkien), and modify some related files.
+- Implemented a very basic instance administration screen
+- Implement quoting
+
+### Fixed
+- Keep aspect ratio of custom emoji reaction in notification
+- Fix openSettingsModalTab so that it correctly opens Settings modal instead of Admin modal
+- Add alt text to emoji picker buttons
+- Use export-subst gitattribute to allow tarball builds
+- fix reports now showing reason/content
+- Fix HTML attribute parsing, discard attributes not strating with a letter
+- Make MentionsLine aware of line breaking by non-br elements
+- Fix a bug where mentioning a user twice will not fill the mention into the textarea
+- Fix parsing non-ascii tags
+- Fix OAuth2 token lingering after revocation
+- fix regex issue in HTML parser/renderer
+- don't display quoted status twice
+- fix typo in code that prevented cards from showing at all
+- Fix react button not working if reaction accounts are not loaded
+- Fix react button misalignment on safari ios
+- Fix pinned statuses gone when reloading user timeline
+- Fix scrolling emoji selector in modal in safari ios
+
## 2.5.1
### Fixed
- Checkboxes in settings can now work with screenreaders
diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js
index 7de93721..7a108f68 100644
--- a/build/webpack.prod.conf.js
+++ b/build/webpack.prod.conf.js
@@ -11,9 +11,16 @@ var env = process.env.NODE_ENV === 'testing'
? require('../config/test.env')
: config.build.env
-let commitHash = require('child_process')
- .execSync('git rev-parse --short HEAD')
- .toString();
+let commitHash = (() => {
+ const subst = "$Format:%h$";
+ if(!subst.match(/Format:/)) {
+ return subst;
+ } else {
+ return require('child_process')
+ .execSync('git rev-parse --short HEAD')
+ .toString();
+ }
+})();
var webpackConfig = merge(baseWebpackConfig, {
mode: 'production',
diff --git a/changelog.d/add-taiwanese-aka-hokkien-i18n-support.add b/changelog.d/add-taiwanese-aka-hokkien-i18n-support.add
deleted file mode 100644
index 53d89805..00000000
--- a/changelog.d/add-taiwanese-aka-hokkien-i18n-support.add
+++ /dev/null
@@ -1 +0,0 @@
-add the initial i18n translation file for Taiwanese (Hokkien), and modify some related files. \ No newline at end of file
diff --git a/changelog.d/adminfe.add b/changelog.d/adminfe.add
deleted file mode 100644
index 188c4555..00000000
--- a/changelog.d/adminfe.add
+++ /dev/null
@@ -1 +0,0 @@
-Implemented a very basic instance administration screen
diff --git a/changelog.d/check-changelog.skip b/changelog.d/check-changelog.skip
deleted file mode 100644
index e69de29b..00000000
--- a/changelog.d/check-changelog.skip
+++ /dev/null
diff --git a/changelog.d/custom-emoji-notif-width.fix b/changelog.d/custom-emoji-notif-width.fix
deleted file mode 100644
index da118f6b..00000000
--- a/changelog.d/custom-emoji-notif-width.fix
+++ /dev/null
@@ -1 +0,0 @@
-Keep aspect ratio of custom emoji reaction in notification
diff --git a/changelog.d/edit-profile-button.fix b/changelog.d/edit-profile-button.fix
deleted file mode 100644
index 5a92765c..00000000
--- a/changelog.d/edit-profile-button.fix
+++ /dev/null
@@ -1 +0,0 @@
-Fix openSettingsModalTab so that it correctly opens Settings modal instead of Admin modal
diff --git a/changelog.d/mention-twice.fix b/changelog.d/mention-twice.fix
deleted file mode 100644
index 0e4b71df..00000000
--- a/changelog.d/mention-twice.fix
+++ /dev/null
@@ -1 +0,0 @@
-Fix a bug where mentioning a user twice will not fill the mention into the textarea
diff --git a/changelog.d/mentionsline-shouldbreak.fix b/changelog.d/mentionsline-shouldbreak.fix
deleted file mode 100644
index 33ee8d2c..00000000
--- a/changelog.d/mentionsline-shouldbreak.fix
+++ /dev/null
@@ -1 +0,0 @@
-Make MentionsLine aware of line breaking by non-br elements
diff --git a/changelog.d/parser.fix b/changelog.d/parser.fix
deleted file mode 100644
index 13bac0bf..00000000
--- a/changelog.d/parser.fix
+++ /dev/null
@@ -1 +0,0 @@
-fix regex issue in HTML parser/renderer
diff --git a/changelog.d/react-button-safari.fix b/changelog.d/react-button-safari.fix
deleted file mode 100644
index 9846d50d..00000000
--- a/changelog.d/react-button-safari.fix
+++ /dev/null
@@ -1 +0,0 @@
-Fix react button misalignment on safari ios
diff --git a/changelog.d/react-button.fix b/changelog.d/react-button.fix
deleted file mode 100644
index c2222fb6..00000000
--- a/changelog.d/react-button.fix
+++ /dev/null
@@ -1 +0,0 @@
-Fix react button not working if reaction accounts are not loaded
diff --git a/changelog.d/scroll-emoji-selector-safari.fix b/changelog.d/scroll-emoji-selector-safari.fix
deleted file mode 100644
index 3f5dda7d..00000000
--- a/changelog.d/scroll-emoji-selector-safari.fix
+++ /dev/null
@@ -1 +0,0 @@
-Fix scrolling emoji selector in modal in safari ios
diff --git a/package.json b/package.json
index 4824df82..5d3b50ad 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "pleroma_fe",
- "version": "2.5.0",
+ "version": "2.6.0",
"description": "Pleroma frontend, the default frontend of Pleroma social network server",
"author": "Pleroma contributors <https://git.pleroma.social/pleroma/pleroma-fe/-/blob/develop/CONTRIBUTORS.md>",
"private": false,
@@ -35,9 +35,9 @@
"js-cookie": "3.0.1",
"localforage": "1.10.0",
"parse-link-header": "2.0.0",
- "phoenix": "1.6.2",
+ "phoenix": "1.7.7",
"punycode.js": "2.3.0",
- "qrcode": "1.5.1",
+ "qrcode": "1.5.3",
"querystring-es3": "0.2.1",
"url": "0.11.0",
"utf8": "3.0.0",
@@ -97,7 +97,7 @@
"karma-spec-reporter": "0.0.36",
"karma-webpack": "5.0.0",
"lodash": "4.17.21",
- "mini-css-extract-plugin": "2.7.5",
+ "mini-css-extract-plugin": "2.7.6",
"mocha": "10.2.0",
"nightwatch": "2.6.20",
"opn": "5.5.0",
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index 9c1f007b..395d4834 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -259,6 +259,7 @@ const getNodeInfo = async ({ store }) => {
store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') })
store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits })
store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })
+ store.dispatch('setInstanceOption', { name: 'quotingAvailable', value: features.includes('quote_posting') })
const uploadLimits = metadata.uploadLimits
store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) })
diff --git a/src/components/emoji_picker/emoji_picker.vue b/src/components/emoji_picker/emoji_picker.vue
index 227d7718..b8d33309 100644
--- a/src/components/emoji_picker/emoji_picker.vue
+++ b/src/components/emoji_picker/emoji_picker.vue
@@ -28,6 +28,7 @@
active: activeGroupView === group.id
}"
:title="group.text"
+ role="button"
@click.prevent="highlight(group.id)"
>
<span
@@ -116,6 +117,7 @@
:key="group.id + emoji.displayText"
:title="maybeLocalizedEmojiName(emoji)"
class="emoji-item"
+ role="button"
@click.stop.prevent="onEmoji(emoji)"
>
<span
@@ -126,6 +128,7 @@
v-else
class="emoji-picker-emoji -custom"
loading="lazy"
+ :alt="maybeLocalizedEmojiName(emoji)"
:src="emoji.imageUrl"
:data-emoji-name="group.id + emoji.displayText"
/>
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index b75fee69..ba49961d 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -156,11 +156,13 @@ const PostStatusForm = {
poll: this.statusPoll || {},
mediaDescriptions: this.statusMediaDescriptions || {},
visibility: this.statusScope || scope,
- contentType: statusContentType
+ contentType: statusContentType,
+ quoting: false
}
}
return {
+ randomSeed: `${Math.random()}`.replace('.', '-'),
dropFiles: [],
uploadingFiles: false,
error: null,
@@ -265,6 +267,30 @@ const PostStatusForm = {
isEdit () {
return typeof this.statusId !== 'undefined' && this.statusId.trim() !== ''
},
+ quotable () {
+ if (!this.$store.state.instance.quotingAvailable) {
+ return false
+ }
+
+ if (!this.replyTo) {
+ return false
+ }
+
+ const repliedStatus = this.$store.state.statuses.allStatusesObject[this.replyTo]
+ if (!repliedStatus) {
+ return false
+ }
+
+ if (repliedStatus.visibility === 'public' ||
+ repliedStatus.visibility === 'unlisted' ||
+ repliedStatus.visibility === 'local') {
+ return true
+ } else if (repliedStatus.visibility === 'private') {
+ return repliedStatus.user.id === this.$store.state.users.currentUser.id
+ }
+
+ return false
+ },
...mapGetters(['mergedConfig']),
...mapState({
mobileLayout: state => state.interface.mobileLayout
@@ -292,7 +318,8 @@ const PostStatusForm = {
visibility: newStatus.visibility,
contentType: newStatus.contentType,
poll: {},
- mediaDescriptions: {}
+ mediaDescriptions: {},
+ quoting: false
}
this.pollFormVisible = false
this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile()
@@ -340,6 +367,8 @@ const PostStatusForm = {
return
}
+ const replyOrQuoteAttr = newStatus.quoting ? 'quoteId' : 'inReplyToStatusId'
+
const postingOptions = {
status: newStatus.status,
spoilerText: newStatus.spoilerText || null,
@@ -347,7 +376,7 @@ const PostStatusForm = {
sensitive: newStatus.nsfw,
media: newStatus.files,
store: this.$store,
- inReplyToStatusId: this.replyTo,
+ [replyOrQuoteAttr]: this.replyTo,
contentType: newStatus.contentType,
poll,
idempotencyKey: this.idempotencyKey
@@ -373,6 +402,7 @@ const PostStatusForm = {
}
const newStatus = this.newStatus
this.previewLoading = true
+ const replyOrQuoteAttr = newStatus.quoting ? 'quoteId' : 'inReplyToStatusId'
statusPoster.postStatus({
status: newStatus.status,
spoilerText: newStatus.spoilerText || null,
@@ -380,7 +410,7 @@ const PostStatusForm = {
sensitive: newStatus.nsfw,
media: [],
store: this.$store,
- inReplyToStatusId: this.replyTo,
+ [replyOrQuoteAttr]: this.replyTo,
contentType: newStatus.contentType,
poll: {},
preview: true
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 86c1f907..9b108a5a 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -126,6 +126,36 @@
class="preview-status"
/>
</div>
+ <div
+ v-if="quotable"
+ role="radiogroup"
+ class="btn-group reply-or-quote-selector"
+ >
+ <button
+ :id="`reply-or-quote-option-${randomSeed}-reply`"
+ class="btn button-default reply-or-quote-option"
+ :class="{ toggled: !newStatus.quoting }"
+ tabindex="0"
+ role="radio"
+ :aria-labelledby="`reply-or-quote-option-${randomSeed}-reply`"
+ :aria-checked="!newStatus.quoting"
+ @click="newStatus.quoting = false"
+ >
+ {{ $t('post_status.reply_option') }}
+ </button>
+ <button
+ :id="`reply-or-quote-option-${randomSeed}-quote`"
+ class="btn button-default reply-or-quote-option"
+ :class="{ toggled: newStatus.quoting }"
+ tabindex="0"
+ role="radio"
+ :aria-labelledby="`reply-or-quote-option-${randomSeed}-quote`"
+ :aria-checked="newStatus.quoting"
+ @click="newStatus.quoting = true"
+ >
+ {{ $t('post_status.quote_option') }}
+ </button>
+ </div>
<EmojiInput
v-if="!disableSubject && (newStatus.spoilerText || alwaysShowSubject)"
v-model="newStatus.spoilerText"
@@ -420,6 +450,10 @@
margin: 0;
}
+ .reply-or-quote-selector {
+ margin-bottom: 0.5em;
+ }
+
.text-format {
.only-format {
color: $fallback--faint;
diff --git a/src/components/report/report.js b/src/components/report/report.js
index 76055764..5685aa25 100644
--- a/src/components/report/report.js
+++ b/src/components/report/report.js
@@ -1,6 +1,7 @@
import Select from '../select/select.vue'
import StatusContent from '../status_content/status_content.vue'
import Timeago from '../timeago/timeago.vue'
+import RichContent from 'src/components/rich_content/rich_content.jsx'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
const Report = {
@@ -10,10 +11,12 @@ const Report = {
components: {
Select,
StatusContent,
- Timeago
+ Timeago,
+ RichContent
},
computed: {
report () {
+ console.log(this.$store.state.reports.reports[this.reportId] || {})
return this.$store.state.reports.reports[this.reportId] || {}
},
state: {
diff --git a/src/components/status/status.js b/src/components/status/status.js
index 9a9bca7a..e722a635 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -133,6 +133,7 @@ const Status = {
'showPinned',
'inProfile',
'profileUserId',
+ 'inQuote',
'simpleTree',
'controlledThreadDisplayStatus',
@@ -159,7 +160,8 @@ const Status = {
uncontrolledMediaPlaying: [],
suspendable: true,
error: null,
- headTailLinks: null
+ headTailLinks: null,
+ displayQuote: !this.inQuote
}
},
computed: {
@@ -401,6 +403,18 @@ const Status = {
},
editingAvailable () {
return this.$store.state.instance.editingAvailable
+ },
+ hasVisibleQuote () {
+ return this.status.quote_url && this.status.quote_visible
+ },
+ hasInvisibleQuote () {
+ return this.status.quote_url && !this.status.quote_visible
+ },
+ quotedStatus () {
+ return this.status.quote_id ? this.$store.state.statuses.allStatusesObject[this.status.quote_id] : undefined
+ },
+ shouldDisplayQuote () {
+ return this.quotedStatus && this.displayQuote
}
},
methods: {
@@ -469,6 +483,18 @@ const Status = {
window.scrollBy(0, rect.bottom - window.innerHeight + 50)
}
}
+ },
+ toggleDisplayQuote () {
+ if (this.shouldDisplayQuote) {
+ this.displayQuote = false
+ } else if (!this.quotedStatus) {
+ this.$store.dispatch('fetchStatus', this.status.quote_id)
+ .then(() => {
+ this.displayQuote = true
+ })
+ } else {
+ this.displayQuote = true
+ }
}
},
watch: {
diff --git a/src/components/status/status.scss b/src/components/status/status.scss
index 44812867..760c6ac1 100644
--- a/src/components/status/status.scss
+++ b/src/components/status/status.scss
@@ -422,4 +422,22 @@
}
}
}
+
+ .quoted-status {
+ margin-top: 0.5em;
+ border: 1px solid var(--border, $fallback--border);
+ border-radius: var(--attachmentRadius, $fallback--attachmentRadius);
+
+ &.-unavailable-prompt {
+ padding: 0.5em;
+ }
+ }
+
+ .display-quoted-status-button {
+ margin: 0.5em;
+
+ &-icon {
+ color: inherit;
+ }
+ }
}
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 35b15362..c49a9e7b 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -364,6 +364,45 @@
@parseReady="setHeadTailLinks"
/>
+ <article
+ v-if="hasVisibleQuote"
+ class="quoted-status"
+ >
+ <button
+ class="button-unstyled -link display-quoted-status-button"
+ :aria-expanded="shouldDisplayQuote"
+ @click="toggleDisplayQuote"
+ >
+ {{ shouldDisplayQuote ? $t('status.hide_quote') : $t('status.display_quote') }}
+ <FAIcon
+ class="display-quoted-status-button-icon"
+ :icon="shouldDisplayQuote ? 'chevron-up' : 'chevron-down'"
+ />
+ </button>
+ <Status
+ v-if="shouldDisplayQuote"
+ :statusoid="quotedStatus"
+ :in-quote="true"
+ />
+ </article>
+ <p
+ v-else-if="hasInvisibleQuote"
+ class="quoted-status -unavailable-prompt"
+ >
+ <i18n-t keypath="status.invisible_quote">
+ <template #link>
+ <bdi>
+ <a
+ :href="status.quote_url"
+ target="_blank"
+ >
+ {{ status.quote_url }}
+ </a>
+ </bdi>
+ </template>
+ </i18n-t>
+ </p>
+
<div
v-if="inConversation && !isPreview && replies && replies.length"
class="replies"
diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js
index 89f0aa51..8d8a91dc 100644
--- a/src/components/status_content/status_content.js
+++ b/src/components/status_content/status_content.js
@@ -73,6 +73,10 @@ const StatusContent = {
},
computed: {
...controlledOrUncontrolledGetters(['showingTall', 'expandingSubject', 'showingLongSubject']),
+ statusCard () {
+ if (!this.status.card) return null
+ return this.status.card.url === this.status.quote_url ? null : this.status.card
+ },
hideAttachments () {
return (this.mergedConfig.hideAttachments && !this.inConversation) ||
(this.mergedConfig.hideAttachmentsInConv && this.inConversation)
diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue
index c0e5c0b9..e977d489 100644
--- a/src/components/status_content/status_content.vue
+++ b/src/components/status_content/status_content.vue
@@ -43,7 +43,7 @@
/>
<div
- v-if="status.card && !noHeading && !compact"
+ v-if="statusCard && !noHeading && !compact"
class="link-preview media-body"
>
<link-preview
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index b7414610..1050b87a 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -160,6 +160,9 @@ const Timeline = {
if (this.timeline.flushMarker !== 0) {
this.$store.commit('clearTimeline', { timeline: this.timelineName, excludeUserId: true })
this.$store.commit('queueFlush', { timeline: this.timelineName, id: 0 })
+ if (this.timelineName === 'user') {
+ this.$store.dispatch('fetchPinnedStatuses', this.userId)
+ }
this.fetchOlderStatuses()
} else {
this.blockClicksTemporarily()
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 90e33648..1658a25d 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -267,6 +267,8 @@
"post_status": {
"edit_status": "Edit status",
"new_status": "Post new status",
+ "reply_option": "Reply to this status",
+ "quote_option": "Quote this status",
"account_not_locked_warning": "Your account is not {0}. Anyone can follow you to view your follower-only posts.",
"account_not_locked_warning_link": "locked",
"attachments_sensitive": "Mark attachments as sensitive",
@@ -1039,7 +1041,10 @@
"show_all_conversation": "Show full conversation ({numStatus} other status) | Show full conversation ({numStatus} other statuses)",
"show_only_conversation_under_this": "Only show replies to this status",
"status_history": "Status history",
- "reaction_count_label": "{num} person reacted | {num} people reacted"
+ "reaction_count_label": "{num} person reacted | {num} people reacted",
+ "hide_quote": "Hide the quoted status",
+ "display_quote": "Display the quoted status",
+ "invisible_quote": "Quoted status unavailable: {link}"
},
"user_card": {
"approve": "Approve",
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 3972bd29..034348ff 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -133,6 +133,7 @@ const defaultState = {
mediaProxyAvailable: false,
suggestionsEnabled: false,
suggestionsWeb: '',
+ quotingAvailable: false,
// Html stuff
instanceSpecificPanelContent: '',
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index ed21a730..186bba3c 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -229,6 +229,10 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
timelineObject.newStatusCount += 1
}
+ if (status.quote) {
+ addStatus(status.quote, /* showImmediately = */ false, /* addToTimeline = */ false)
+ }
+
return status
}
diff --git a/src/modules/users.js b/src/modules/users.js
index e976d875..50b4cb84 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -651,6 +651,12 @@ const users = {
const response = data.error
// Authentication failed
commit('endLogin')
+
+ // remove authentication token on client/authentication errors
+ if ([400, 401, 403, 422].includes(response.status)) {
+ commit('clearToken')
+ }
+
if (response.status === 401) {
reject(new Error('Wrong username or password'))
} else {
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index ac715678..c6bca10b 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -827,6 +827,7 @@ const postStatus = ({
poll,
mediaIds = [],
inReplyToStatusId,
+ quoteId,
contentType,
preview,
idempotencyKey
@@ -859,6 +860,9 @@ const postStatus = ({
if (inReplyToStatusId) {
form.append('in_reply_to_id', inReplyToStatusId)
}
+ if (quoteId) {
+ form.append('quote_id', quoteId)
+ }
if (preview) {
form.append('preview', 'true')
}
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index adefc5a5..610ba1ab 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -325,6 +325,10 @@ export const parseStatus = (data) => {
output.thread_muted = pleroma.thread_muted
output.emoji_reactions = pleroma.emoji_reactions
output.parent_visible = pleroma.parent_visible === undefined ? true : pleroma.parent_visible
+ output.quote = pleroma.quote ? parseStatus(pleroma.quote) : undefined
+ output.quote_id = pleroma.quote_id ? pleroma.quote_id : (output.quote ? output.quote.id : undefined)
+ output.quote_url = pleroma.quote_url
+ output.quote_visible = pleroma.quote_visible
} else {
output.text = data.content
output.summary = data.spoiler_text
diff --git a/src/services/html_converter/utility.service.js b/src/services/html_converter/utility.service.js
index a1301353..f8e62dfe 100644
--- a/src/services/html_converter/utility.service.js
+++ b/src/services/html_converter/utility.service.js
@@ -22,7 +22,7 @@ export const getAttrs = (tag, filter) => {
.replace(new RegExp('^' + getTagName(tag)), '')
.replace(/\/?$/, '')
.trim()
- const attrs = Array.from(innertag.matchAll(/([a-z0-9-]+)(?:=("[^"]+?"|'[^']+?'))?/gi))
+ const attrs = Array.from(innertag.matchAll(/([a-z]+[a-z0-9-]*)(?:=("[^"]+?"|'[^']+?'))?/gi))
.map(([trash, key, value]) => [key, value])
.map(([k, v]) => {
if (!v) return [k, true]
diff --git a/src/services/matcher/matcher.service.js b/src/services/matcher/matcher.service.js
index b6c4e909..54f02d31 100644
--- a/src/services/matcher/matcher.service.js
+++ b/src/services/matcher/matcher.service.js
@@ -14,8 +14,11 @@ export const mentionMatchesUrl = (attention, url) => {
* @param {string} url
*/
export const extractTagFromUrl = (url) => {
- const regex = /tag[s]*\/(\w+)$/g
- const result = regex.exec(url)
+ const decoded = decodeURI(url)
+ // https://git.pleroma.social/pleroma/elixir-libraries/linkify/-/blob/master/lib/linkify/parser.ex
+ // https://www.pcre.org/original/doc/html/pcrepattern.html
+ const regex = /tag[s]*\/([\p{L}\p{N}_]*[\p{Alphabetic}_·\u{200c}][\p{L}\p{N}_·\p{M}\u{200c}]*)$/ug
+ const result = regex.exec(decoded)
if (!result) {
return false
}
diff --git a/src/services/status_poster/status_poster.service.js b/src/services/status_poster/status_poster.service.js
index 1eb10bb6..aaef5a7a 100644
--- a/src/services/status_poster/status_poster.service.js
+++ b/src/services/status_poster/status_poster.service.js
@@ -10,6 +10,7 @@ const postStatus = ({
poll,
media = [],
inReplyToStatusId = undefined,
+ quoteId = undefined,
contentType = 'text/plain',
preview = false,
idempotencyKey = ''
@@ -24,6 +25,7 @@ const postStatus = ({
sensitive,
mediaIds,
inReplyToStatusId,
+ quoteId,
contentType,
poll,
preview,
diff --git a/test/unit/specs/services/matcher/matcher.spec.js b/test/unit/specs/services/matcher/matcher.spec.js
index 7a2494f0..c6e9719d 100644
--- a/test/unit/specs/services/matcher/matcher.spec.js
+++ b/test/unit/specs/services/matcher/matcher.spec.js
@@ -78,5 +78,11 @@ describe('MatcherService', () => {
expect(MatcherService.extractTagFromUrl(url)).to.eql(false)
})
+
+ it('should return tag name from non-ascii tags', () => {
+ const url = encodeURI('https://website.com/tag/喵喵喵')
+
+ expect(MatcherService.extractTagFromUrl(url)).to.eql('喵喵喵')
+ })
})
})
diff --git a/tools/collect-changelog b/tools/collect-changelog
new file mode 100755
index 00000000..1e12d564
--- /dev/null
+++ b/tools/collect-changelog
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+collectType() {
+ local suffix="$1"
+ local header="$2"
+ local printed=0
+ for file in changelog.d/*."$suffix"; do
+ if [ '!' -f "$file" ]; then
+ continue
+ fi
+ if [ "$printed" = 0 ]; then
+ echo
+ echo "### $header"
+ printed=1
+ fi
+ # Normalize any trailing newlines/spaces, etc.
+ echo "- $(cat "$file")"
+ done
+}
+
+collectType security Security
+collectType change Changed
+collectType add Added
+collectType fix Fixed
+collectType remove Removed
+
+rm changelog.d/*
diff --git a/yarn.lock b/yarn.lock
index fdf6634d..2eb513a5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6523,10 +6523,10 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
-mini-css-extract-plugin@2.7.5:
- version "2.7.5"
- resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.5.tgz#afbb344977659ec0f1f6e050c7aea456b121cfc5"
- integrity sha512-9HaR++0mlgom81s95vvNjxkg52n2b5s//3ZTI1EtzFb98awsLSivs2LMsVqnQ3ay0PVhqWcGNyDaTE961FOcjQ==
+mini-css-extract-plugin@2.7.6:
+ version "2.7.6"
+ resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz#282a3d38863fddcd2e0c220aaed5b90bc156564d"
+ integrity sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==
dependencies:
schema-utils "^4.0.0"
@@ -7141,10 +7141,10 @@ pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
-phoenix@1.6.2:
- version "1.6.2"
- resolved "https://registry.yarnpkg.com/phoenix/-/phoenix-1.6.2.tgz#8d1d9f06e51cb893d08059e80488cd0de328e01a"
- integrity sha512-VjR27NETvrLSj8rI6DlpVAfo7pCYth/9+1OCoTof4LKEbq0141ze/tdxFHHZzVQSok3gqJUo2h/tqbxR3r8eyw==
+phoenix@1.7.7:
+ version "1.7.7"
+ resolved "https://registry.yarnpkg.com/phoenix/-/phoenix-1.7.7.tgz#829817ea65a83ef78a3a88e3e074125f502a034f"
+ integrity sha512-moAN6e4Z16x/x1nswUpnTR2v5gm7HsI7eluZ2YnYUUsBNzi3cY/5frmiJfXIEi877IQAafzTfp8hd6vEUMme+w==
picocolors@^1.0.0:
version "1.0.0"
@@ -7622,10 +7622,10 @@ qjobs@^1.2.0:
resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071"
integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==
-qrcode@1.5.1:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.1.tgz#0103f97317409f7bc91772ef30793a54cd59f0cb"
- integrity sha512-nS8NJ1Z3md8uTjKtP+SGGhfqmTCs5flU/xR623oI0JX+Wepz9R8UrRVCTBTJm3qGw3rH6jJ6MUHjkDx15cxSSg==
+qrcode@1.5.3:
+ version "1.5.3"
+ resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.3.tgz#03afa80912c0dccf12bc93f615a535aad1066170"
+ integrity sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==
dependencies:
dijkstrajs "^1.0.1"
encode-utf8 "^1.0.3"