Merge remote-tracking branch 'origin/develop' into shigusegubu-vue3
* origin/develop: (41 commits) lint Apply 1 suggestion(s) to 1 file(s) Use feed role for notifs and timelines Update dependency chai to v4.3.7 Make notification panel a list of articles Handle properly 2-col and 3-col desktop notifications Make user panel and notification <aside> Make timeline a list of articles Add labels to mobile navs Add labels for timeline header Use <main> for main content Add title to mobile post button Update dependency eslint-plugin-n to v15.5.0 Update dependency eslint-plugin-promise to v6.1.1 Update Node.js to v16.18.1 Update dependency eslint-plugin-vue to v9.7.0 Update dependency eslint-plugin-n to v15.4.0 Update dependency eslint to v8.26.0 Update vue monorepo Update dependency css-minimizer-webpack-plugin to v4.2.2 ...
This commit is contained in:
commit
d16d754127
43 changed files with 925 additions and 513 deletions
25
.gitlab/issue_templates/Bug.md
Normal file
25
.gitlab/issue_templates/Bug.md
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Environment info
|
||||||
|
|
||||||
|
<!-- Everything is optional and where applicable but the more information the better. -->
|
||||||
|
* Browser, version, OS, platform:
|
||||||
|
* Instance URL:
|
||||||
|
* Frontend version (see settings -> about):
|
||||||
|
* Backend version (see settings -> about):
|
||||||
|
* Browser extensions (ublock, rikaichamp etc):
|
||||||
|
* Known instance/user customizations (i.e. pleromafe mods/forks, instance styles etc)
|
||||||
|
|
||||||
|
# Bug description & reproduction steps
|
||||||
|
|
||||||
|
<!-- Type out here how to reproduce the bug, what goes wrong and what should go right -->
|
||||||
|
<!-- Screenshots and videos help a lot ;) any observations might also help -->
|
||||||
|
<!-- Also mention if there any errors in browser's console if relevant -->
|
||||||
|
|
||||||
|
# Bug seriousness
|
||||||
|
|
||||||
|
<!-- Everything is optional and free-form -->
|
||||||
|
* How annoying it is:
|
||||||
|
* How often does it happen:
|
||||||
|
* How many people does it affect:
|
||||||
|
* Is there a workaround for it:
|
||||||
|
|
||||||
|
/label ~Bug
|
||||||
11
.gitlab/issue_templates/Suggestion.md
Normal file
11
.gitlab/issue_templates/Suggestion.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Behavior suggestion/Feature request
|
||||||
|
<!--
|
||||||
|
Type out what you want to see changed or what feature you want to see added to
|
||||||
|
PleormaFE. Please also explain how it would benefit users (or admins/moderators)
|
||||||
|
and what intended usecase is. Any background information (i.e. porting behavior
|
||||||
|
from other frontends/services, specific situations, personal preferences etc.)
|
||||||
|
as well as examples would be greatly appreciated.
|
||||||
|
-->
|
||||||
|
|
||||||
|
/label ~suggestion
|
||||||
|
|
||||||
7
.gitlab/issue_templates/default.md
Normal file
7
.gitlab/issue_templates/default.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<!--
|
||||||
|
please use one of the templates if applicable, otherwise - type out here
|
||||||
|
in free-form
|
||||||
|
-->
|
||||||
|
|
||||||
|
/label ~needs-triage
|
||||||
|
|
||||||
30
.gitlab/merge_request_templates/default.md
Normal file
30
.gitlab/merge_request_templates/default.md
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<!--
|
||||||
|
Feel free to submit merge requests that are work-in-progress, but mark them as
|
||||||
|
Draft: or WIP:.
|
||||||
|
Merge requests that have Draft or WIP status will not be merged and have less chances
|
||||||
|
of being reviewed, but you can still ask people to take a look if you need advice.
|
||||||
|
-->
|
||||||
|
# Changes
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
|
||||||
|
<!-- List what your merge request changes and how -->
|
||||||
|
<!--
|
||||||
|
Try to not to break existing behavior, if your changes do break existing behavior
|
||||||
|
make it configurable to toggle between old behavior and new. Which one should be
|
||||||
|
default is up to discussion.
|
||||||
|
-->
|
||||||
|
<!-- If your merge request resolves some issue link it like so: "Closes #99999" -->
|
||||||
|
<!--
|
||||||
|
If merge request adds some new feature that depends on backend:
|
||||||
|
|
||||||
|
1. Make sure it gracefully degrades if backend hasn't been updated to support the feature,
|
||||||
|
we try to make PleromaFE compatible with older versions of BE so that people can still
|
||||||
|
update frontend safely without updating backend since it's costly and much riskier.
|
||||||
|
2. Link related BE merge request here
|
||||||
|
-->
|
||||||
|
<!-- Screenshots are welcome -->
|
||||||
|
|
||||||
|
/label ~needs-review
|
||||||
|
|
@ -1 +1 @@
|
||||||
16.16.0
|
16.18.1
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Enabled users to zoom and pan images in media viewer with mouse and touch
|
- Enabled users to zoom and pan images in media viewer with mouse and touch
|
||||||
- Timelines/panels and conversations have sticky headers now
|
- Timelines/panels and conversations have sticky headers now
|
||||||
- Added frontend ui for account migration
|
- Added frontend ui for account migration
|
||||||
|
- Implemented remote interaction with statuses
|
||||||
|
|
||||||
|
|
||||||
## [2.4.2] - 2022-01-09
|
## [2.4.2] - 2022-01-09
|
||||||
|
|
|
||||||
42
package.json
42
package.json
|
|
@ -16,7 +16,7 @@
|
||||||
"lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs"
|
"lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.18.9",
|
"@babel/runtime": "7.20.0",
|
||||||
"@chenfengyuan/vue-qrcode": "2.0.0",
|
"@chenfengyuan/vue-qrcode": "2.0.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "6.2.0",
|
"@fortawesome/fontawesome-svg-core": "6.2.0",
|
||||||
"@fortawesome/free-regular-svg-icons": "6.2.0",
|
"@fortawesome/free-regular-svg-icons": "6.2.0",
|
||||||
|
|
@ -43,43 +43,43 @@
|
||||||
"querystring-es3": "0.2.1",
|
"querystring-es3": "0.2.1",
|
||||||
"url": "0.11.0",
|
"url": "0.11.0",
|
||||||
"utf8": "3.0.0",
|
"utf8": "3.0.0",
|
||||||
"vue": "3.2.38",
|
"vue": "3.2.41",
|
||||||
"vue-i18n": "9.2.2",
|
"vue-i18n": "9.2.2",
|
||||||
"vue-router": "4.1.5",
|
"vue-router": "4.1.6",
|
||||||
"vue-template-compiler": "2.7.10",
|
"vue-template-compiler": "2.7.13",
|
||||||
"vuex": "4.0.2"
|
"vuex": "4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.18.13",
|
"@babel/core": "7.19.6",
|
||||||
"@babel/eslint-parser": "7.18.9",
|
"@babel/eslint-parser": "7.19.1",
|
||||||
"@babel/plugin-transform-runtime": "7.18.10",
|
"@babel/plugin-transform-runtime": "7.19.6",
|
||||||
"@babel/preset-env": "7.18.10",
|
"@babel/preset-env": "7.19.4",
|
||||||
"@babel/register": "7.18.9",
|
"@babel/register": "7.18.9",
|
||||||
"@intlify/vue-i18n-loader": "5.0.0",
|
"@intlify/vue-i18n-loader": "5.0.0",
|
||||||
"@ungap/event-target": "0.2.3",
|
"@ungap/event-target": "0.2.3",
|
||||||
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
|
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
|
||||||
"@vue/babel-plugin-jsx": "1.1.1",
|
"@vue/babel-plugin-jsx": "1.1.1",
|
||||||
"@vue/compiler-sfc": "3.2.38",
|
"@vue/compiler-sfc": "3.2.41",
|
||||||
"@vue/test-utils": "2.0.2",
|
"@vue/test-utils": "2.2.1",
|
||||||
"autoprefixer": "10.4.12",
|
"autoprefixer": "10.4.12",
|
||||||
"babel-loader": "8.2.5",
|
"babel-loader": "8.2.5",
|
||||||
"babel-plugin-lodash": "3.3.4",
|
"babel-plugin-lodash": "3.3.4",
|
||||||
"chai": "4.3.6",
|
"chai": "4.3.7",
|
||||||
"chalk": "1.1.3",
|
"chalk": "1.1.3",
|
||||||
"chromedriver": "104.0.0",
|
"chromedriver": "104.0.0",
|
||||||
"connect-history-api-fallback": "2.0.0",
|
"connect-history-api-fallback": "2.0.0",
|
||||||
"copy-webpack-plugin": "11.0.0",
|
"copy-webpack-plugin": "11.0.0",
|
||||||
"cross-spawn": "7.0.3",
|
"cross-spawn": "7.0.3",
|
||||||
"css-loader": "6.7.1",
|
"css-loader": "6.7.1",
|
||||||
"css-minimizer-webpack-plugin": "4.0.0",
|
"css-minimizer-webpack-plugin": "4.2.2",
|
||||||
"custom-event-polyfill": "1.0.7",
|
"custom-event-polyfill": "1.0.7",
|
||||||
"eslint": "8.23.0",
|
"eslint": "8.26.0",
|
||||||
"eslint-config-standard": "17.0.0",
|
"eslint-config-standard": "17.0.0",
|
||||||
"eslint-formatter-friendly": "7.0.0",
|
"eslint-formatter-friendly": "7.0.0",
|
||||||
"eslint-plugin-import": "2.26.0",
|
"eslint-plugin-import": "2.26.0",
|
||||||
"eslint-plugin-n": "15.2.5",
|
"eslint-plugin-n": "15.5.0",
|
||||||
"eslint-plugin-promise": "6.0.1",
|
"eslint-plugin-promise": "6.1.1",
|
||||||
"eslint-plugin-vue": "9.4.0",
|
"eslint-plugin-vue": "9.7.0",
|
||||||
"eslint-webpack-plugin": "3.2.0",
|
"eslint-webpack-plugin": "3.2.0",
|
||||||
"eventsource-polyfill": "0.9.6",
|
"eventsource-polyfill": "0.9.6",
|
||||||
"express": "4.18.2",
|
"express": "4.18.2",
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
"http-proxy-middleware": "2.0.6",
|
"http-proxy-middleware": "2.0.6",
|
||||||
"iso-639-1": "2.1.15",
|
"iso-639-1": "2.1.15",
|
||||||
"json-loader": "0.5.7",
|
"json-loader": "0.5.7",
|
||||||
"karma": "6.4.0",
|
"karma": "6.4.1",
|
||||||
"karma-coverage": "2.2.0",
|
"karma-coverage": "2.2.0",
|
||||||
"karma-firefox-launcher": "2.1.2",
|
"karma-firefox-launcher": "2.1.2",
|
||||||
"karma-mocha": "2.0.1",
|
"karma-mocha": "2.0.1",
|
||||||
|
|
@ -108,16 +108,16 @@
|
||||||
"sass": "1.55.0",
|
"sass": "1.55.0",
|
||||||
"sass-loader": "13.0.2",
|
"sass-loader": "13.0.2",
|
||||||
"selenium-server": "2.53.1",
|
"selenium-server": "2.53.1",
|
||||||
"semver": "7.3.7",
|
"semver": "7.3.8",
|
||||||
"serviceworker-webpack5-plugin": "2.0.0",
|
"serviceworker-webpack5-plugin": "2.0.0",
|
||||||
"shelljs": "0.8.5",
|
"shelljs": "0.8.5",
|
||||||
"sinon": "14.0.0",
|
"sinon": "14.0.1",
|
||||||
"sinon-chai": "3.7.0",
|
"sinon-chai": "3.7.0",
|
||||||
"stylelint": "13.13.1",
|
"stylelint": "13.13.1",
|
||||||
"stylelint-config-standard": "20.0.0",
|
"stylelint-config-standard": "20.0.0",
|
||||||
"stylelint-rscss": "0.4.0",
|
"stylelint-rscss": "0.4.0",
|
||||||
"vue-loader": "17.0.0",
|
|
||||||
"unicode-emoji-json": "^0.3.0",
|
"unicode-emoji-json": "^0.3.0",
|
||||||
|
"vue-loader": "17.0.1",
|
||||||
"vue-style-loader": "4.1.3",
|
"vue-style-loader": "4.1.3",
|
||||||
"webpack": "5.74.0",
|
"webpack": "5.74.0",
|
||||||
"webpack-dev-middleware": "3.7.3",
|
"webpack-dev-middleware": "3.7.3",
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
<div id="notifs-sidebar" />
|
<div id="notifs-sidebar" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<main
|
||||||
id="main-scroller"
|
id="main-scroller"
|
||||||
class="column main"
|
class="column main"
|
||||||
:class="{ '-full-height': isChats || isListEdit }"
|
:class="{ '-full-height': isChats || isListEdit }"
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<router-view />
|
<router-view />
|
||||||
</div>
|
</main>
|
||||||
<div
|
<div
|
||||||
id="notifs-column"
|
id="notifs-column"
|
||||||
class="column -scrollable"
|
class="column -scrollable"
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
v-if="shouldShowAncestors"
|
v-if="shouldShowAncestors"
|
||||||
class="thread-ancestors"
|
class="thread-ancestors"
|
||||||
>
|
>
|
||||||
<div
|
<article
|
||||||
v-for="status in ancestorsOf(diveRoot)"
|
v-for="status in ancestorsOf(diveRoot)"
|
||||||
:key="status.id"
|
:key="status.id"
|
||||||
class="thread-ancestor"
|
class="thread-ancestor"
|
||||||
|
|
@ -130,7 +130,7 @@
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
<thread-tree
|
<thread-tree
|
||||||
v-for="status in showingTopLevel"
|
v-for="status in showingTopLevel"
|
||||||
|
|
@ -168,34 +168,36 @@
|
||||||
v-if="isLinearView"
|
v-if="isLinearView"
|
||||||
class="thread-body"
|
class="thread-body"
|
||||||
>
|
>
|
||||||
<status
|
<article>
|
||||||
v-for="status in conversation"
|
<status
|
||||||
:key="status.id"
|
v-for="status in conversation"
|
||||||
ref="statusComponent"
|
:key="status.id"
|
||||||
:inline-expanded="collapsable && isExpanded"
|
ref="statusComponent"
|
||||||
:statusoid="status"
|
:inline-expanded="collapsable && isExpanded"
|
||||||
:expandable="!isExpanded"
|
:statusoid="status"
|
||||||
:show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
|
:expandable="!isExpanded"
|
||||||
:focused="focused(status.id)"
|
:show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
|
||||||
:in-conversation="isExpanded"
|
:focused="focused(status.id)"
|
||||||
:highlight="getHighlight()"
|
:in-conversation="isExpanded"
|
||||||
:replies="getReplies(status.id)"
|
:highlight="getHighlight()"
|
||||||
:in-profile="inProfile"
|
:replies="getReplies(status.id)"
|
||||||
:profile-user-id="profileUserId"
|
:in-profile="inProfile"
|
||||||
class="conversation-status status-fadein panel-body"
|
:profile-user-id="profileUserId"
|
||||||
|
class="conversation-status status-fadein panel-body"
|
||||||
|
|
||||||
:toggle-thread-display="toggleThreadDisplay"
|
:toggle-thread-display="toggleThreadDisplay"
|
||||||
:thread-display-status="threadDisplayStatus"
|
:thread-display-status="threadDisplayStatus"
|
||||||
:show-thread-recursively="showThreadRecursively"
|
:show-thread-recursively="showThreadRecursively"
|
||||||
:total-reply-count="totalReplyCount"
|
:total-reply-count="totalReplyCount"
|
||||||
:total-reply-depth="totalReplyDepth"
|
:total-reply-depth="totalReplyDepth"
|
||||||
:status-content-properties="statusContentProperties"
|
:status-content-properties="statusContentProperties"
|
||||||
:set-status-content-property="setStatusContentProperty"
|
:set-status-content-property="setStatusContentProperty"
|
||||||
:toggle-status-content-property="toggleStatusContentProperty"
|
:toggle-status-content-property="toggleStatusContentProperty"
|
||||||
|
|
||||||
@goto="setHighlight"
|
@goto="setHighlight"
|
||||||
@toggleExpanded="toggleExpanded"
|
@toggleExpanded="toggleExpanded"
|
||||||
/>
|
/>
|
||||||
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,16 @@
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
<!-- TODO: make the 'x' disappear if at the end maybe? -->
|
<!-- TODO: make the 'x' disappear if at the end maybe? -->
|
||||||
<div class="hidden-overlay" :style="overlayStyle" ref="hiddenOverlay">
|
<div
|
||||||
|
ref="hiddenOverlay"
|
||||||
|
class="hidden-overlay"
|
||||||
|
:style="overlayStyle"
|
||||||
|
>
|
||||||
<span>{{ preText }}</span>
|
<span>{{ preText }}</span>
|
||||||
<span class="caret" ref="hiddenOverlayCaret">x</span>
|
<span
|
||||||
|
ref="hiddenOverlayCaret"
|
||||||
|
class="caret"
|
||||||
|
>x</span>
|
||||||
<span>{{ postText }}</span>
|
<span>{{ postText }}</span>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="enableEmojiPicker">
|
<template v-if="enableEmojiPicker">
|
||||||
|
|
@ -33,9 +40,9 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<Popover
|
<Popover
|
||||||
|
ref="suggestorPopover"
|
||||||
class="autocomplete-panel"
|
class="autocomplete-panel"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
ref="suggestorPopover"
|
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<Popover
|
<Popover
|
||||||
|
ref="popover"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
popover-class="emoji-picker popover-default"
|
popover-class="emoji-picker popover-default"
|
||||||
ref="popover"
|
|
||||||
@show="onPopoverShown"
|
@show="onPopoverShown"
|
||||||
@close="onPopoverClosed"
|
@close="onPopoverClosed"
|
||||||
>
|
>
|
||||||
|
|
@ -66,12 +66,12 @@
|
||||||
>
|
>
|
||||||
<div class="emoji-search">
|
<div class="emoji-search">
|
||||||
<input
|
<input
|
||||||
|
ref="search"
|
||||||
v-model="keyword"
|
v-model="keyword"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="$t('emoji.search_emoji')"
|
:placeholder="$t('emoji.search_emoji')"
|
||||||
@input="$event.target.composing = false"
|
@input="$event.target.composing = false"
|
||||||
ref="search"
|
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,7 @@ const ExtraButtons = {
|
||||||
currentUser () { return this.$store.state.users.currentUser },
|
currentUser () { return this.$store.state.users.currentUser },
|
||||||
canDelete () {
|
canDelete () {
|
||||||
if (!this.currentUser) { return }
|
if (!this.currentUser) { return }
|
||||||
const superuser = this.currentUser.rights.moderator || this.currentUser.rights.admin
|
return this.currentUser.privileges.includes('messages_delete') || this.status.user.id === this.currentUser.id
|
||||||
return superuser || this.status.user.id === this.currentUser.id
|
|
||||||
},
|
},
|
||||||
ownStatus () {
|
ownStatus () {
|
||||||
return this.status.user.id === this.currentUser.id
|
return this.status.user.id === this.currentUser.id
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,10 @@ const FavoriteButton = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['mergedConfig'])
|
...mapGetters(['mergedConfig']),
|
||||||
|
remoteInteractionLink () {
|
||||||
|
return this.$store.getters.remoteInteractionLink({ statusId: this.status.id })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,19 @@
|
||||||
/>
|
/>
|
||||||
</FALayers>
|
</FALayers>
|
||||||
</button>
|
</button>
|
||||||
<span v-else>
|
<a
|
||||||
|
v-else
|
||||||
|
class="button-unstyled interactive"
|
||||||
|
target="_blank"
|
||||||
|
role="button"
|
||||||
|
:href="remoteInteractionLink"
|
||||||
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
class="fa-scale-110 fa-old-padding"
|
class="fa-scale-110 fa-old-padding"
|
||||||
:title="$t('tool_tip.favorite')"
|
:title="$t('tool_tip.favorite')"
|
||||||
:icon="['far', 'star']"
|
:icon="['far', 'star']"
|
||||||
/>
|
/>
|
||||||
</span>
|
</a>
|
||||||
<span
|
<span
|
||||||
v-if="!mergedConfig.hidePostStats && status.fave_num > 0"
|
v-if="!mergedConfig.hidePostStats && status.fave_num > 0"
|
||||||
class="action-counter"
|
class="action-counter"
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ const Interactions = {
|
||||||
return {
|
return {
|
||||||
allowFollowingMove: this.$store.state.users.currentUser.allow_following_move,
|
allowFollowingMove: this.$store.state.users.currentUser.allow_following_move,
|
||||||
filterMode: tabModeDict.mentions,
|
filterMode: tabModeDict.mentions,
|
||||||
canSeeReports: ['moderator', 'admin'].includes(this.$store.state.users.currentUser.role)
|
canSeeReports: this.$store.state.users.currentUser.privileges.includes('reports_manage_reports')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<button
|
<button
|
||||||
class="button-unstyled mobile-nav-button"
|
class="button-unstyled mobile-nav-button"
|
||||||
|
:title="$t('nav.mobile_sidebar')"
|
||||||
|
:aria-expanaded="$refs.sideDrawer && !$refs.sideDrawer.closed"
|
||||||
@click.stop.prevent="toggleMobileSidebar()"
|
@click.stop.prevent="toggleMobileSidebar()"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
|
|
@ -26,6 +28,7 @@
|
||||||
<button
|
<button
|
||||||
v-if="currentUser"
|
v-if="currentUser"
|
||||||
class="button-unstyled mobile-nav-button"
|
class="button-unstyled mobile-nav-button"
|
||||||
|
:title="unseenNotificationsCount ? $t('nav.mobile_notifications_unread_active') : $t('nav.mobile_notifications')"
|
||||||
@click.stop.prevent="openMobileNotifications()"
|
@click.stop.prevent="openMobileNotifications()"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
|
|
@ -39,7 +42,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div
|
<aside
|
||||||
v-if="currentUser"
|
v-if="currentUser"
|
||||||
class="mobile-notifications-drawer"
|
class="mobile-notifications-drawer"
|
||||||
:class="{ '-closed': !notificationsOpen }"
|
:class="{ '-closed': !notificationsOpen }"
|
||||||
|
|
@ -48,10 +51,11 @@
|
||||||
>
|
>
|
||||||
<div class="mobile-notifications-header">
|
<div class="mobile-notifications-header">
|
||||||
<span class="title">{{ $t('notifications.notifications') }}</span>
|
<span class="title">{{ $t('notifications.notifications') }}</span>
|
||||||
<span class="spacer"/>
|
<span class="spacer" />
|
||||||
<button
|
<button
|
||||||
v-if="notificationsAtTop"
|
v-if="notificationsAtTop"
|
||||||
class="button-unstyled mobile-nav-button"
|
class="button-unstyled mobile-nav-button"
|
||||||
|
:title="$t('general.scroll_to_top')"
|
||||||
@click.stop.prevent="scrollMobileNotificationsToTop"
|
@click.stop.prevent="scrollMobileNotificationsToTop"
|
||||||
>
|
>
|
||||||
<FALayers class="fa-scale-110 fa-old-padding-layer">
|
<FALayers class="fa-scale-110 fa-old-padding-layer">
|
||||||
|
|
@ -64,6 +68,7 @@
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled mobile-nav-button"
|
class="button-unstyled mobile-nav-button"
|
||||||
|
:title="$t('nav.mobile_notifications_close')"
|
||||||
@click.stop.prevent="closeMobileNotifications(true)"
|
@click.stop.prevent="closeMobileNotifications(true)"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
|
|
@ -74,11 +79,11 @@
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
id="mobile-notifications"
|
id="mobile-notifications"
|
||||||
class="mobile-notifications"
|
|
||||||
ref="mobileNotifications"
|
ref="mobileNotifications"
|
||||||
|
class="mobile-notifications"
|
||||||
@scroll="onScroll"
|
@scroll="onScroll"
|
||||||
/>
|
/>
|
||||||
</div>
|
</aside>
|
||||||
<SideDrawer
|
<SideDrawer
|
||||||
ref="sideDrawer"
|
ref="sideDrawer"
|
||||||
:logout="logout"
|
:logout="logout"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
v-if="isLoggedIn"
|
v-if="isLoggedIn"
|
||||||
class="MobilePostButton button-default new-status-button"
|
class="MobilePostButton button-default new-status-button"
|
||||||
:class="{ 'hidden': isHidden, 'always-show': isPersistent }"
|
:class="{ 'hidden': isHidden, 'always-show': isPersistent }"
|
||||||
|
:title="$t('post_status.new_status')"
|
||||||
@click="openPostForm"
|
@click="openPostForm"
|
||||||
>
|
>
|
||||||
<FAIcon icon="pen" />
|
<FAIcon icon="pen" />
|
||||||
|
|
|
||||||
|
|
@ -41,14 +41,26 @@ const ModerationTools = {
|
||||||
tagsSet () {
|
tagsSet () {
|
||||||
return new Set(this.user.tags)
|
return new Set(this.user.tags)
|
||||||
},
|
},
|
||||||
hasTagPolicy () {
|
canGrantRole () {
|
||||||
return this.$store.state.instance.tagPolicyAvailable
|
return this.user.is_local && !this.user.deactivated && this.$store.state.users.currentUser.role === 'admin'
|
||||||
|
},
|
||||||
|
canChangeActivationState () {
|
||||||
|
return this.privileged('users_manage_activation_state')
|
||||||
|
},
|
||||||
|
canDeleteAccount () {
|
||||||
|
return this.privileged('users_delete')
|
||||||
|
},
|
||||||
|
canUseTagPolicy () {
|
||||||
|
return this.$store.state.instance.tagPolicyAvailable && this.privileged('users_manage_tags')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
hasTag (tagName) {
|
hasTag (tagName) {
|
||||||
return this.tagsSet.has(tagName)
|
return this.tagsSet.has(tagName)
|
||||||
},
|
},
|
||||||
|
privileged (privilege) {
|
||||||
|
return this.$store.state.users.currentUser.privileges.includes(privilege)
|
||||||
|
},
|
||||||
toggleTag (tag) {
|
toggleTag (tag) {
|
||||||
const store = this.$store
|
const store = this.$store
|
||||||
if (this.tagsSet.has(tag)) {
|
if (this.tagsSet.has(tag)) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<span v-if="user.is_local">
|
<span v-if="canGrantRole">
|
||||||
<button
|
<button
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
@click="toggleRight("admin")"
|
@click="toggleRight("admin")"
|
||||||
|
|
@ -24,28 +24,31 @@
|
||||||
{{ $t(!!user.rights.moderator ? 'user_card.admin_menu.revoke_moderator' : 'user_card.admin_menu.grant_moderator') }}
|
{{ $t(!!user.rights.moderator ? 'user_card.admin_menu.revoke_moderator' : 'user_card.admin_menu.grant_moderator') }}
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
|
v-if="canChangeActivationState || canDeleteAccount"
|
||||||
role="separator"
|
role="separator"
|
||||||
class="dropdown-divider"
|
class="dropdown-divider"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
|
v-if="canChangeActivationState"
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
@click="toggleActivationStatus()"
|
@click="toggleActivationStatus()"
|
||||||
>
|
>
|
||||||
{{ $t(!!user.deactivated ? 'user_card.admin_menu.activate_account' : 'user_card.admin_menu.deactivate_account') }}
|
{{ $t(!!user.deactivated ? 'user_card.admin_menu.activate_account' : 'user_card.admin_menu.deactivate_account') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
v-if="canDeleteAccount"
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
@click="deleteUserDialog(true)"
|
@click="deleteUserDialog(true)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.admin_menu.delete_account') }}
|
{{ $t('user_card.admin_menu.delete_account') }}
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
v-if="hasTagPolicy"
|
v-if="canUseTagPolicy"
|
||||||
role="separator"
|
role="separator"
|
||||||
class="dropdown-divider"
|
class="dropdown-divider"
|
||||||
/>
|
/>
|
||||||
<span v-if="hasTagPolicy">
|
<span v-if="canUseTagPolicy">
|
||||||
<button
|
<button
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
@click="toggleTag(tags.FORCE_NSFW)"
|
@click="toggleTag(tags.FORCE_NSFW)"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<Status
|
<article
|
||||||
v-if="notification.type === 'mention'"
|
v-if="notification.type === 'mention'"
|
||||||
class="Notification"
|
>
|
||||||
:compact="true"
|
<Status
|
||||||
:statusoid="notification.status"
|
class="Notification"
|
||||||
/>
|
:compact="true"
|
||||||
<div v-else>
|
:statusoid="notification.status"
|
||||||
|
/>
|
||||||
|
</article>
|
||||||
|
<article v-else>
|
||||||
<div
|
<div
|
||||||
v-if="needMute && !unmuted"
|
v-if="needMute && !unmuted"
|
||||||
class="Notification container -muted"
|
class="Notification container -muted"
|
||||||
|
|
@ -226,7 +229,7 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./notification.js"></script>
|
<script src="./notification.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
:disabled="minimalMode || disableTeleport"
|
:disabled="minimalMode || disableTeleport"
|
||||||
:to="teleportTarget"
|
:to="teleportTarget"
|
||||||
>
|
>
|
||||||
<div
|
<component
|
||||||
|
:is="noHeading ? 'div' : 'aside'"
|
||||||
ref="root"
|
ref="root"
|
||||||
:class="{ minimal: minimalMode }"
|
:class="{ minimal: minimalMode }"
|
||||||
class="Notifications"
|
class="Notifications"
|
||||||
|
|
@ -21,8 +22,8 @@
|
||||||
>{{ unseenCount }}</span>
|
>{{ unseenCount }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="rightside-button"
|
|
||||||
v-if="showScrollTop"
|
v-if="showScrollTop"
|
||||||
|
class="rightside-button"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled scroll-to-top-button"
|
class="button-unstyled scroll-to-top-button"
|
||||||
|
|
@ -49,10 +50,14 @@
|
||||||
</button>
|
</button>
|
||||||
<NotificationFilters class="rightside-button" />
|
<NotificationFilters class="rightside-button" />
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div
|
||||||
|
class="panel-body"
|
||||||
|
role="feed"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-for="notification in notificationsToDisplay"
|
v-for="notification in notificationsToDisplay"
|
||||||
:key="notification.id"
|
:key="notification.id"
|
||||||
|
role="listitem"
|
||||||
class="notification"
|
class="notification"
|
||||||
:class="{unseen: !minimalMode && !notification.seen}"
|
:class="{unseen: !minimalMode && !notification.seen}"
|
||||||
>
|
>
|
||||||
|
|
@ -88,7 +93,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</component>
|
||||||
</teleport>
|
</teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,12 @@ const Popover = {
|
||||||
overlayCentersSelector: String,
|
overlayCentersSelector: String,
|
||||||
|
|
||||||
// Lets hover popover stay when clicking inside of it
|
// Lets hover popover stay when clicking inside of it
|
||||||
stayOnClick: Boolean
|
stayOnClick: Boolean,
|
||||||
|
|
||||||
|
triggerAttrs: {
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
inject: ['popoversZLayer'], // override popover z layer
|
inject: ['popoversZLayer'], // override popover z layer
|
||||||
data () {
|
data () {
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,15 @@
|
||||||
ref="trigger"
|
ref="trigger"
|
||||||
class="button-unstyled popover-trigger-button"
|
class="button-unstyled popover-trigger-button"
|
||||||
type="button"
|
type="button"
|
||||||
|
v-bind="triggerAttrs"
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
>
|
>
|
||||||
<slot name="trigger" />
|
<slot name="trigger" />
|
||||||
</button>
|
</button>
|
||||||
<teleport :disabled="!teleport" to="#popovers">
|
<teleport
|
||||||
|
:disabled="!teleport"
|
||||||
|
to="#popovers"
|
||||||
|
>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div
|
<div
|
||||||
v-if="!hidden"
|
v-if="!hidden"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
trigger="click"
|
trigger="click"
|
||||||
class="QuickFilterSettings"
|
class="QuickFilterSettings"
|
||||||
:bound-to="{ x: 'container' }"
|
:bound-to="{ x: 'container' }"
|
||||||
|
:trigger-attrs="{ title: $t('timeline.quick_filter_settings') }"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
|
|
@ -79,9 +80,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<button class="button-unstyled">
|
<FAIcon icon="filter" />
|
||||||
<FAIcon icon="filter" />
|
|
||||||
</button>
|
|
||||||
</template>
|
</template>
|
||||||
</Popover>
|
</Popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
trigger="click"
|
trigger="click"
|
||||||
class="QuickViewSettings"
|
class="QuickViewSettings"
|
||||||
:bound-to="{ x: 'container' }"
|
:bound-to="{ x: 'container' }"
|
||||||
|
:trigger-attrs="{ title: $t('timeline.quick_view_settings') }"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
|
|
@ -66,9 +67,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<button class="button-unstyled">
|
<FAIcon icon="bars" />
|
||||||
<FAIcon icon="bars" />
|
|
||||||
</button>
|
|
||||||
</template>
|
</template>
|
||||||
</Popover>
|
</Popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ const ReplyButton = {
|
||||||
computed: {
|
computed: {
|
||||||
loggedIn () {
|
loggedIn () {
|
||||||
return !!this.$store.state.users.currentUser
|
return !!this.$store.state.users.currentUser
|
||||||
|
},
|
||||||
|
remoteInteractionLink () {
|
||||||
|
return this.$store.getters.remoteInteractionLink({ statusId: this.status.id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,19 @@
|
||||||
/>
|
/>
|
||||||
</FALayers>
|
</FALayers>
|
||||||
</button>
|
</button>
|
||||||
<span v-else>
|
<a
|
||||||
|
v-else
|
||||||
|
class="button-unstyled interactive"
|
||||||
|
target="_blank"
|
||||||
|
role="button"
|
||||||
|
:href="remoteInteractionLink"
|
||||||
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="reply"
|
icon="reply"
|
||||||
class="fa-scale-110 fa-old-padding"
|
class="fa-scale-110 fa-old-padding"
|
||||||
:title="$t('tool_tip.reply')"
|
:title="$t('tool_tip.reply')"
|
||||||
/>
|
/>
|
||||||
</span>
|
</a>
|
||||||
<span
|
<span
|
||||||
v-if="status.replies_count > 0"
|
v-if="status.replies_count > 0"
|
||||||
class="action-counter"
|
class="action-counter"
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,9 @@ const RetweetButton = {
|
||||||
computed: {
|
computed: {
|
||||||
mergedConfig () {
|
mergedConfig () {
|
||||||
return this.$store.getters.mergedConfig
|
return this.$store.getters.mergedConfig
|
||||||
|
},
|
||||||
|
remoteInteractionLink () {
|
||||||
|
return this.$store.getters.remoteInteractionLink({ statusId: this.status.id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,13 +40,19 @@
|
||||||
:title="$t('timeline.no_retweet_hint')"
|
:title="$t('timeline.no_retweet_hint')"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<a
|
||||||
|
v-else
|
||||||
|
class="button-unstyled interactive"
|
||||||
|
target="_blank"
|
||||||
|
role="button"
|
||||||
|
:href="remoteInteractionLink"
|
||||||
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
class="fa-scale-110 fa-old-padding"
|
class="fa-scale-110 fa-old-padding"
|
||||||
icon="retweet"
|
icon="retweet"
|
||||||
:title="$t('tool_tip.repeat')"
|
:title="$t('tool_tip.repeat')"
|
||||||
/>
|
/>
|
||||||
</span>
|
</a>
|
||||||
<span
|
<span
|
||||||
v-if="!mergedConfig.hidePostStats && status.repeat_num > 0"
|
v-if="!mergedConfig.hidePostStats && status.repeat_num > 0"
|
||||||
class="no-event"
|
class="no-event"
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
faCircleNotch,
|
faCircleNotch,
|
||||||
faSearch
|
faSearch
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { uniqBy } from 'lodash'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faCircleNotch,
|
faCircleNotch,
|
||||||
|
|
@ -32,7 +33,11 @@ const Search = {
|
||||||
userIds: [],
|
userIds: [],
|
||||||
statuses: [],
|
statuses: [],
|
||||||
hashtags: [],
|
hashtags: [],
|
||||||
currenResultTab: 'statuses'
|
currenResultTab: 'statuses',
|
||||||
|
|
||||||
|
statusesOffset: 0,
|
||||||
|
lastStatusFetchCount: 0,
|
||||||
|
lastQuery: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -61,26 +66,42 @@ const Search = {
|
||||||
this.$router.push({ name: 'search', query: { query } })
|
this.$router.push({ name: 'search', query: { query } })
|
||||||
this.$refs.searchInput.focus()
|
this.$refs.searchInput.focus()
|
||||||
},
|
},
|
||||||
search (query) {
|
search (query, searchType = null) {
|
||||||
if (!query) {
|
if (!query) {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.userIds = []
|
|
||||||
this.statuses = []
|
|
||||||
this.hashtags = []
|
|
||||||
this.$refs.searchInput.blur()
|
this.$refs.searchInput.blur()
|
||||||
|
if (this.lastQuery !== query) {
|
||||||
|
this.userIds = []
|
||||||
|
this.hashtags = []
|
||||||
|
this.statuses = []
|
||||||
|
|
||||||
this.$store.dispatch('search', { q: query, resolve: true })
|
this.statusesOffset = 0
|
||||||
|
this.lastStatusFetchCount = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.dispatch('search', { q: query, resolve: true, offset: this.statusesOffset, type: searchType })
|
||||||
.then(data => {
|
.then(data => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.userIds = map(data.accounts, 'id')
|
|
||||||
this.statuses = data.statuses
|
const oldLength = this.statuses.length
|
||||||
this.hashtags = data.hashtags
|
|
||||||
|
// Always append to old results. If new results are empty, this doesn't change anything
|
||||||
|
this.userIds = this.userIds.concat(map(data.accounts, 'id'))
|
||||||
|
this.statuses = uniqBy(this.statuses.concat(data.statuses), 'id')
|
||||||
|
this.hashtags = this.hashtags.concat(data.hashtags)
|
||||||
|
|
||||||
this.currenResultTab = this.getActiveTab()
|
this.currenResultTab = this.getActiveTab()
|
||||||
this.loaded = true
|
this.loaded = true
|
||||||
|
|
||||||
|
// Offset from whatever we already have
|
||||||
|
this.statusesOffset = this.statuses.length
|
||||||
|
// Because the amount of new statuses can actually be zero, compare to old lenght instead
|
||||||
|
this.lastStatusFetchCount = this.statuses.length - oldLength
|
||||||
|
this.lastQuery = query
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
resultCount (tabName) {
|
resultCount (tabName) {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="loading"
|
v-if="loading && statusesOffset == 0"
|
||||||
class="text-center loading-icon"
|
class="text-center loading-icon"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
|
|
@ -55,12 +55,6 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div v-if="currenResultTab === 'statuses'">
|
<div v-if="currenResultTab === 'statuses'">
|
||||||
<div
|
|
||||||
v-if="visibleStatuses.length === 0 && !loading && loaded"
|
|
||||||
class="search-result-heading"
|
|
||||||
>
|
|
||||||
<h4>{{ $t('search.no_results') }}</h4>
|
|
||||||
</div>
|
|
||||||
<Status
|
<Status
|
||||||
v-for="status in visibleStatuses"
|
v-for="status in visibleStatuses"
|
||||||
:key="status.id"
|
:key="status.id"
|
||||||
|
|
@ -71,6 +65,33 @@
|
||||||
:statusoid="status"
|
:statusoid="status"
|
||||||
:no-heading="false"
|
:no-heading="false"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
v-if="!loading && loaded && lastStatusFetchCount > 0"
|
||||||
|
class="more-statuses-button button-unstyled -link -fullwidth"
|
||||||
|
@click.prevent="search(searchTerm, 'statuses')"
|
||||||
|
>
|
||||||
|
<div class="new-status-notification text-center">
|
||||||
|
{{ $t('search.load_more') }}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
v-else-if="loading && statusesOffset > 0"
|
||||||
|
class="text-center loading-icon"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
icon="circle-notch"
|
||||||
|
spin
|
||||||
|
size="lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="(visibleStatuses.length === 0 || lastStatusFetchCount === 0) && !loading && loaded"
|
||||||
|
class="search-result-heading"
|
||||||
|
>
|
||||||
|
<h4>
|
||||||
|
{{ visibleStatuses.length === 0 ? $t('search.no_results') : $t('search.no_more_results') }}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="currenResultTab === 'people'">
|
<div v-else-if="currenResultTab === 'people'">
|
||||||
<div
|
<div
|
||||||
|
|
@ -208,6 +229,11 @@
|
||||||
color: $fallback--text;
|
color: $fallback--text;
|
||||||
color: var(--text, $fallback--text);
|
color: var(--text, $fallback--text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.more-statuses-button {
|
||||||
|
height: 3.5em;
|
||||||
|
line-height: 3.5em;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ const StaffPanel = {
|
||||||
computed: {
|
computed: {
|
||||||
groupedStaffAccounts () {
|
groupedStaffAccounts () {
|
||||||
const staffAccounts = map(this.staffAccounts, this.findUserByName).filter(_ => _)
|
const staffAccounts = map(this.staffAccounts, this.findUserByName).filter(_ => _)
|
||||||
console.log(staffAccounts)
|
|
||||||
const groupedStaffAccounts = groupBy(staffAccounts, 'role')
|
const groupedStaffAccounts = groupBy(staffAccounts, 'role')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="thread-tree">
|
<article class="thread-tree">
|
||||||
<status
|
<status
|
||||||
:key="status.id"
|
:key="status.id"
|
||||||
ref="statusComponent"
|
ref="statusComponent"
|
||||||
|
|
@ -113,7 +113,7 @@
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./thread_tree.js"></script>
|
<script src="./thread_tree.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
:timeline-name="timelineName"
|
:timeline-name="timelineName"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="rightside-button"
|
|
||||||
v-if="showScrollTop && !embedded"
|
v-if="showScrollTop && !embedded"
|
||||||
|
class="rightside-button"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled scroll-to-top-button"
|
class="button-unstyled scroll-to-top-button"
|
||||||
|
|
@ -26,8 +26,8 @@
|
||||||
</div>
|
</div>
|
||||||
<template v-if="mobileLayout && !embedded">
|
<template v-if="mobileLayout && !embedded">
|
||||||
<div
|
<div
|
||||||
class="rightside-button"
|
|
||||||
v-if="showLoadButton"
|
v-if="showLoadButton"
|
||||||
|
class="rightside-button"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled loadmore-button"
|
class="button-unstyled loadmore-button"
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
v-else-if="!embedded"
|
v-else-if="!embedded"
|
||||||
class="loadmore-text faint veryfaint rightside-icon"
|
class="loadmore-text faint veryfaint rightside-icon"
|
||||||
:title="$t('timeline.up_to_date')"
|
:title="$t('timeline.up_to_date')"
|
||||||
|
:aria-disabled="true"
|
||||||
@click.prevent
|
@click.prevent
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
|
|
@ -71,17 +72,25 @@
|
||||||
{{ $t('timeline.up_to_date') }}
|
{{ $t('timeline.up_to_date') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<QuickFilterSettings v-if="!embedded" class="rightside-button"/>
|
<QuickFilterSettings
|
||||||
<QuickViewSettings v-if="!embedded" class="rightside-button"/>
|
v-if="!embedded"
|
||||||
|
class="rightside-button"
|
||||||
|
/>
|
||||||
|
<QuickViewSettings
|
||||||
|
v-if="!embedded"
|
||||||
|
class="rightside-button"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div :class="classes.body">
|
<div :class="classes.body">
|
||||||
<div
|
<div
|
||||||
ref="timeline"
|
ref="timeline"
|
||||||
class="timeline"
|
class="timeline"
|
||||||
|
role="feed"
|
||||||
>
|
>
|
||||||
<conversation
|
<conversation
|
||||||
v-for="statusId in filteredPinnedStatusIds"
|
v-for="statusId in filteredPinnedStatusIds"
|
||||||
:key="statusId + '-pinned'"
|
:key="statusId + '-pinned'"
|
||||||
|
role="listitem"
|
||||||
class="status-fadein"
|
class="status-fadein"
|
||||||
:status-id="statusId"
|
:status-id="statusId"
|
||||||
:collapsable="true"
|
:collapsable="true"
|
||||||
|
|
@ -92,6 +101,7 @@
|
||||||
<conversation
|
<conversation
|
||||||
v-for="status in filteredVisibleStatuses"
|
v-for="status in filteredVisibleStatuses"
|
||||||
:key="status.id"
|
:key="status.id"
|
||||||
|
role="listitem"
|
||||||
class="status-fadein"
|
class="status-fadein"
|
||||||
:status-id="status.id"
|
:status-id="status.id"
|
||||||
:collapsable="true"
|
:collapsable="true"
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,10 @@ export default {
|
||||||
hideFollowersCount () {
|
hideFollowersCount () {
|
||||||
return this.isOtherUser && this.user.hide_followers_count
|
return this.isOtherUser && this.user.hide_followers_count
|
||||||
},
|
},
|
||||||
|
showModerationMenu () {
|
||||||
|
const privileges = this.loggedIn.privileges
|
||||||
|
return this.loggedIn.role === 'admin' || privileges.includes('users_manage_activation_state') || privileges.includes('users_delete') || privileges.includes('users_manage_tags')
|
||||||
|
},
|
||||||
...mapGetters(['mergedConfig'])
|
...mapGetters(['mergedConfig'])
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ModerationTools
|
<ModerationTools
|
||||||
v-if="loggedIn.role === "admin""
|
v-if="showModerationMenu"
|
||||||
:user="user"
|
:user="user"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="user-panel">
|
<aside class="user-panel">
|
||||||
<div
|
<div
|
||||||
v-if="signedIn"
|
v-if="signedIn"
|
||||||
key="user-panel-signed"
|
key="user-panel-signed"
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
v-else
|
v-else
|
||||||
key="user-panel"
|
key="user-panel"
|
||||||
/>
|
/>
|
||||||
</div>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./user_panel.js"></script>
|
<script src="./user_panel.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,11 @@
|
||||||
"lists": "Lists",
|
"lists": "Lists",
|
||||||
"edit_nav_mobile": "Customize navigation bar",
|
"edit_nav_mobile": "Customize navigation bar",
|
||||||
"edit_pinned": "Edit pinned items",
|
"edit_pinned": "Edit pinned items",
|
||||||
"edit_finish": "Done editing"
|
"edit_finish": "Done editing",
|
||||||
|
"mobile_sidebar": "Toggle mobile sidebar",
|
||||||
|
"mobile_notifications": "Open notifications",
|
||||||
|
"mobile_notifications": "Open notifications (there are unread ones)",
|
||||||
|
"mobile_notifications_close": "Close notifications"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"broken_favorite": "Unknown status, searching for it…",
|
"broken_favorite": "Unknown status, searching for it…",
|
||||||
|
|
@ -807,7 +811,9 @@
|
||||||
"no_more_statuses": "No more statuses",
|
"no_more_statuses": "No more statuses",
|
||||||
"no_statuses": "No statuses",
|
"no_statuses": "No statuses",
|
||||||
"socket_reconnected": "Realtime connection established",
|
"socket_reconnected": "Realtime connection established",
|
||||||
"socket_broke": "Realtime connection lost: CloseEvent code {0}"
|
"socket_broke": "Realtime connection lost: CloseEvent code {0}",
|
||||||
|
"quick_view_settings": "Quick view settings",
|
||||||
|
"quick_filter_settings": "Quick filter settings"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"favorites": "Favorites",
|
"favorites": "Favorites",
|
||||||
|
|
@ -980,7 +986,9 @@
|
||||||
"hashtags": "Hashtags",
|
"hashtags": "Hashtags",
|
||||||
"person_talking": "{count} person talking",
|
"person_talking": "{count} person talking",
|
||||||
"people_talking": "{count} people talking",
|
"people_talking": "{count} people talking",
|
||||||
"no_results": "No results"
|
"no_results": "No results",
|
||||||
|
"no_more_results": "No more results",
|
||||||
|
"load_more": "Load more results"
|
||||||
},
|
},
|
||||||
"password_reset": {
|
"password_reset": {
|
||||||
"forgot_password": "Forgot password?",
|
"forgot_password": "Forgot password?",
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ const REGIONAL_INDICATORS = (() => {
|
||||||
return res
|
return res
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
const REMOTE_INTERACTION_URL = '/main/ostatus'
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
// Stuff from apiConfig
|
// Stuff from apiConfig
|
||||||
name: 'Pleroma FE',
|
name: 'Pleroma FE',
|
||||||
|
|
@ -214,6 +216,18 @@ const instance = {
|
||||||
},
|
},
|
||||||
instanceDomain (state) {
|
instanceDomain (state) {
|
||||||
return new URL(state.server).hostname
|
return new URL(state.server).hostname
|
||||||
|
},
|
||||||
|
remoteInteractionLink (state) {
|
||||||
|
const server = state.server.endsWith('/') ? state.server.slice(0, -1) : state.server
|
||||||
|
const link = server + REMOTE_INTERACTION_URL
|
||||||
|
|
||||||
|
return ({ statusId, nickname }) => {
|
||||||
|
if (statusId) {
|
||||||
|
return `${link}?status_id=${statusId}`
|
||||||
|
} else {
|
||||||
|
return `${link}?nickname=${nickname}`
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
||||||
|
|
@ -761,8 +761,8 @@ const statuses = {
|
||||||
rootState.api.backendInteractor.fetchRebloggedByUsers({ id })
|
rootState.api.backendInteractor.fetchRebloggedByUsers({ id })
|
||||||
.then(rebloggedByUsers => commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser }))
|
.then(rebloggedByUsers => commit('addRepeats', { id, rebloggedByUsers, currentUser: rootState.users.currentUser }))
|
||||||
},
|
},
|
||||||
search (store, { q, resolve, limit, offset, following }) {
|
search (store, { q, resolve, limit, offset, following, type }) {
|
||||||
return store.rootState.api.backendInteractor.search2({ q, resolve, limit, offset, following })
|
return store.rootState.api.backendInteractor.search2({ q, resolve, limit, offset, following, type })
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
store.commit('addNewUsers', data.accounts)
|
store.commit('addNewUsers', data.accounts)
|
||||||
store.commit('addNewStatuses', { statuses: data.statuses })
|
store.commit('addNewStatuses', { statuses: data.statuses })
|
||||||
|
|
|
||||||
|
|
@ -1278,7 +1278,7 @@ const searchUsers = ({ credentials, query }) => {
|
||||||
.then((data) => data.map(parseUser))
|
.then((data) => data.map(parseUser))
|
||||||
}
|
}
|
||||||
|
|
||||||
const search2 = ({ credentials, q, resolve, limit, offset, following }) => {
|
const search2 = ({ credentials, q, resolve, limit, offset, following, type }) => {
|
||||||
let url = MASTODON_SEARCH_2
|
let url = MASTODON_SEARCH_2
|
||||||
const params = []
|
const params = []
|
||||||
|
|
||||||
|
|
@ -1302,6 +1302,10 @@ const search2 = ({ credentials, q, resolve, limit, offset, following }) => {
|
||||||
params.push(['following', true])
|
params.push(['following', true])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type) {
|
||||||
|
params.push(['following', type])
|
||||||
|
}
|
||||||
|
|
||||||
params.push(['with_relationships', true])
|
params.push(['with_relationships', true])
|
||||||
|
|
||||||
const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
|
const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,34 @@ export const parseUser = (data) => {
|
||||||
} else {
|
} else {
|
||||||
output.role = 'member'
|
output.role = 'member'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.pleroma.privileges) {
|
||||||
|
output.privileges = data.pleroma.privileges
|
||||||
|
} else if (data.pleroma.is_admin) {
|
||||||
|
output.privileges = [
|
||||||
|
'users_read',
|
||||||
|
'users_manage_invites',
|
||||||
|
'users_manage_activation_state',
|
||||||
|
'users_manage_tags',
|
||||||
|
'users_manage_credentials',
|
||||||
|
'users_delete',
|
||||||
|
'messages_read',
|
||||||
|
'messages_delete',
|
||||||
|
'instances_delete',
|
||||||
|
'reports_manage_reports',
|
||||||
|
'moderation_log_read',
|
||||||
|
'announcements_manage_announcements',
|
||||||
|
'emoji_manage_emoji',
|
||||||
|
'statistics_read'
|
||||||
|
]
|
||||||
|
} else if (data.pleroma.is_moderator) {
|
||||||
|
output.privileges = [
|
||||||
|
'messages_delete',
|
||||||
|
'reports_manage_reports'
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
output.privileges = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.source) {
|
if (data.source) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue