Merge branch 'fix/merge-conflicts/report-notifications' into 'feat/report-notification'
Fixing merge conflicts and lint for report notifications MR See merge request pleroma/pleroma-fe!1585
This commit is contained in:
commit
572f28d7c9
|
@ -1,7 +1,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: 'babel-eslint',
|
parser: '@babel/eslint-parser',
|
||||||
sourceType: 'module'
|
sourceType: 'module'
|
||||||
},
|
},
|
||||||
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
|
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
|
||||||
|
@ -21,6 +21,7 @@ module.exports = {
|
||||||
'generator-star-spacing': 0,
|
'generator-star-spacing': 0,
|
||||||
// allow debugger during development
|
// allow debugger during development
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||||
'vue/require-prop-types': 0
|
'vue/require-prop-types': 0,
|
||||||
|
'vue/multi-word-component-names': 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# This file is a template, and might need editing before it works on your project.
|
# This file is a template, and might need editing before it works on your project.
|
||||||
# Official framework image. Look for the different tagged releases at:
|
# Official framework image. Look for the different tagged releases at:
|
||||||
# https://hub.docker.com/r/library/node/tags/
|
# https://hub.docker.com/r/library/node/tags/
|
||||||
image: node:12
|
image: node:16
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- lint
|
- lint
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
7.2.1
|
16.16.0
|
||||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -16,17 +16,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Attachments are ALWAYS in same order as user uploaded, no more "videos first"
|
- Attachments are ALWAYS in same order as user uploaded, no more "videos first"
|
||||||
- Attachment description is prefilled with backend-provided default when uploading
|
- Attachment description is prefilled with backend-provided default when uploading
|
||||||
- Proper visual feedback that next image is loading when browsing
|
- Proper visual feedback that next image is loading when browsing
|
||||||
|
- UI no longer lags when switching between mobile and desktop mode
|
||||||
|
- Popovers no longer constrained by DOM hierarchy, shouldn't be cut off by anything
|
||||||
|
- "Always show mobile button" is working now
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- Using Vue 3 now
|
||||||
- (You)s are optional (opt-in) now, bolding your nickname is also optional (opt-out)
|
- (You)s are optional (opt-in) now, bolding your nickname is also optional (opt-out)
|
||||||
- User highlight background now also covers the `@`
|
- User highlight background now also covers the `@`
|
||||||
- Reverted back to textual `@`, svg version is opt-in.
|
- Reverted back to textual `@`, svg version is opt-in.
|
||||||
- Settings window has been throughly rearranged to make make more sense and make navication settings easier.
|
- Settings window has been thoroughly rearranged to make more sense and make navigation settings easier.
|
||||||
- Uploaded attachments are uniform with displayed attachments
|
- Uploaded attachments are uniform with displayed attachments
|
||||||
- Flash is watchable in media-modal (takes up nearly full screen though due to sizing issues)
|
- Flash is watchable in media-modal (takes up nearly full screen though due to sizing issues)
|
||||||
- Notifications about likes/repeats/emoji reacts are now minimized so they always take up same amount of space irrelevant to size of post.
|
- Notifications about likes/repeats/emoji reacts are now minimized so they always take up same amount of space irrelevant to size of post.
|
||||||
|
- Slight width/spacing adjustments
|
||||||
|
- More sizing stuff is font-size dependent now
|
||||||
|
- Scrollbars are styled/colorized now
|
||||||
|
- Scrollbars are toggleable (for stuff that didn't have visible scrollbars before) (opt-in)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- 3 column mode: only enables when there's space for it (opt-out, customizable)
|
||||||
- Options to show domains in mentions
|
- Options to show domains in mentions
|
||||||
- Option to show user avatars in mention links (opt-in)
|
- Option to show user avatars in mention links (opt-in)
|
||||||
- Option to disable the tooltip for mentions
|
- Option to disable the tooltip for mentions
|
||||||
|
@ -37,6 +46,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Media modal now also displays description and counter position in gallery (i.e. 1/5)
|
- Media modal now also displays description and counter position in gallery (i.e. 1/5)
|
||||||
- Ability to rearrange order of attachments when uploading
|
- Ability to rearrange order of attachments when uploading
|
||||||
- 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
|
||||||
- Added frontend ui for account migration
|
- Added frontend ui for account migration
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ var projectRoot = path.resolve(__dirname, '../')
|
||||||
var ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin')
|
var ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin')
|
||||||
var CopyPlugin = require('copy-webpack-plugin');
|
var CopyPlugin = require('copy-webpack-plugin');
|
||||||
var { VueLoaderPlugin } = require('vue-loader')
|
var { VueLoaderPlugin } = require('vue-loader')
|
||||||
|
var ESLintPlugin = require('eslint-webpack-plugin');
|
||||||
|
|
||||||
|
|
||||||
var env = process.env.NODE_ENV
|
var env = process.env.NODE_ENV
|
||||||
// check env & config/index.js to decide weither to enable CSS Sourcemaps for the
|
// check env & config/index.js to decide weither to enable CSS Sourcemaps for the
|
||||||
|
@ -30,7 +32,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.js', '.jsx', '.vue'],
|
extensions: ['.mjs', '.js', '.jsx', '.vue'],
|
||||||
modules: [
|
modules: [
|
||||||
path.join(__dirname, '../node_modules')
|
path.join(__dirname, '../node_modules')
|
||||||
],
|
],
|
||||||
|
@ -45,20 +47,6 @@ module.exports = {
|
||||||
module: {
|
module: {
|
||||||
noParse: /node_modules\/localforage\/dist\/localforage.js/,
|
noParse: /node_modules\/localforage\/dist\/localforage.js/,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
|
||||||
enforce: 'pre',
|
|
||||||
test: /\.(js|vue)$/,
|
|
||||||
include: projectRoot,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
use: {
|
|
||||||
loader: 'eslint-loader',
|
|
||||||
options: {
|
|
||||||
formatter: require('eslint-friendly-formatter'),
|
|
||||||
sourceMap: config.build.productionSourceMap,
|
|
||||||
extract: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
test: /\.(json5?|ya?ml)$/, // target json, json5, yaml and yml files
|
test: /\.(json5?|ya?ml)$/, // target json, json5, yaml and yml files
|
||||||
|
@ -108,6 +96,11 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
test: /\.mjs$/,
|
||||||
|
include: /node_modules/,
|
||||||
|
type: 'javascript/auto'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -115,12 +108,16 @@ module.exports = {
|
||||||
entry: path.join(__dirname, '..', 'src/sw.js'),
|
entry: path.join(__dirname, '..', 'src/sw.js'),
|
||||||
filename: 'sw-pleroma.js'
|
filename: 'sw-pleroma.js'
|
||||||
}),
|
}),
|
||||||
|
new ESLintPlugin({
|
||||||
|
extensions: ['js', 'vue'],
|
||||||
|
formatter: require('eslint-formatter-friendly')
|
||||||
|
}),
|
||||||
new VueLoaderPlugin(),
|
new VueLoaderPlugin(),
|
||||||
// This copies Ruffle's WASM to a directory so that JS side can access it
|
// This copies Ruffle's WASM to a directory so that JS side can access it
|
||||||
new CopyPlugin({
|
new CopyPlugin({
|
||||||
patterns: [
|
patterns: [
|
||||||
{
|
{
|
||||||
from: "node_modules/ruffle-mirror/*",
|
from: "node_modules/@ruffle-rs/ruffle/*",
|
||||||
to: "static/ruffle",
|
to: "static/ruffle",
|
||||||
flatten: true
|
flatten: true
|
||||||
},
|
},
|
||||||
|
|
59
package.json
59
package.json
|
@ -16,16 +16,17 @@
|
||||||
"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.6",
|
"@babel/runtime": "7.18.9",
|
||||||
"@chenfengyuan/vue-qrcode": "2.0.0",
|
"@chenfengyuan/vue-qrcode": "2.0.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "1.3.0",
|
"@fortawesome/fontawesome-svg-core": "6.1.2",
|
||||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
"@fortawesome/free-regular-svg-icons": "6.1.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
"@fortawesome/free-solid-svg-icons": "6.1.2",
|
||||||
"@fortawesome/vue-fontawesome": "3.0.1",
|
"@fortawesome/vue-fontawesome": "3.0.1",
|
||||||
"@kazvmoe-infra/pinch-zoom-element": "1.2.0",
|
"@kazvmoe-infra/pinch-zoom-element": "1.2.0",
|
||||||
"@vuelidate/core": "2.0.0-alpha.42",
|
"@ruffle-rs/ruffle": "^0.1.0-nightly.2022.7.12",
|
||||||
"@vuelidate/validators": "2.0.0-alpha.30",
|
"@vuelidate/core": "2.0.0-alpha.43",
|
||||||
"body-scroll-lock": "2.7.1",
|
"@vuelidate/validators": "2.0.0-alpha.31",
|
||||||
|
"body-scroll-lock": "3.1.5",
|
||||||
"chromatism": "3.0.0",
|
"chromatism": "3.0.0",
|
||||||
"click-outside-vue3": "4.0.1",
|
"click-outside-vue3": "4.0.1",
|
||||||
"cropperjs": "1.5.12",
|
"cropperjs": "1.5.12",
|
||||||
|
@ -37,46 +38,44 @@
|
||||||
"phoenix": "1.6.2",
|
"phoenix": "1.6.2",
|
||||||
"punycode.js": "2.1.0",
|
"punycode.js": "2.1.0",
|
||||||
"qrcode": "1",
|
"qrcode": "1",
|
||||||
"ruffle-mirror": "2021.12.31",
|
|
||||||
"utf8": "^3.0.0",
|
"utf8": "^3.0.0",
|
||||||
"vue": "^3.2.31",
|
"vue": "3.2.37",
|
||||||
"vue-i18n": "^9.2.0-beta.34",
|
"vue-i18n": "9.2.0",
|
||||||
"vue-router": "4.0.14",
|
"vue-router": "4.1.3",
|
||||||
"vue-template-compiler": "2.6.11",
|
"vue-template-compiler": "2.7.8",
|
||||||
"vuex": "4.0.2"
|
"vuex": "4.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.18.6",
|
"@babel/core": "7.18.9",
|
||||||
"@babel/plugin-transform-runtime": "7.18.6",
|
"@babel/plugin-transform-runtime": "7.18.9",
|
||||||
"@babel/preset-env": "7.18.6",
|
"@babel/preset-env": "7.18.9",
|
||||||
"@babel/register": "7.18.6",
|
"@babel/register": "7.18.9",
|
||||||
|
"@babel/eslint-parser": "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.2.1",
|
"@vue/babel-helper-vue-jsx-merge-props": "1.2.1",
|
||||||
"@vue/babel-plugin-jsx": "1.1.1",
|
"@vue/babel-plugin-jsx": "1.1.1",
|
||||||
"@vue/compiler-sfc": "^3.1.0",
|
"@vue/compiler-sfc": "3.2.37",
|
||||||
"@vue/test-utils": "2.0.0-rc.17",
|
"@vue/test-utils": "2.0.2",
|
||||||
"autoprefixer": "6.7.7",
|
"autoprefixer": "6.7.7",
|
||||||
"babel-eslint": "7.2.3",
|
|
||||||
"babel-loader": "8.2.5",
|
"babel-loader": "8.2.5",
|
||||||
"babel-plugin-lodash": "3.3.4",
|
"babel-plugin-lodash": "3.3.4",
|
||||||
"chai": "3.5.0",
|
"chai": "3.5.0",
|
||||||
"chalk": "1.1.3",
|
"chalk": "1.1.3",
|
||||||
"chromedriver": "87.0.7",
|
"chromedriver": "103.0.0",
|
||||||
"connect-history-api-fallback": "1.6.0",
|
"connect-history-api-fallback": "1.6.0",
|
||||||
"copy-webpack-plugin": "6.4.1",
|
"copy-webpack-plugin": "6.4.1",
|
||||||
"cross-spawn": "4.0.2",
|
"cross-spawn": "4.0.2",
|
||||||
"css-loader": "0.28.11",
|
"css-loader": "0.28.11",
|
||||||
"custom-event-polyfill": "1.0.7",
|
"custom-event-polyfill": "1.0.7",
|
||||||
"eslint": "5.16.0",
|
"eslint": "8.20.0",
|
||||||
"eslint-config-standard": "12.0.0",
|
"eslint-config-standard": "17.0.0",
|
||||||
"eslint-friendly-formatter": "2.0.7",
|
"eslint-formatter-friendly": "7.0.0",
|
||||||
"eslint-loader": "2.2.1",
|
"eslint-webpack-plugin": "2.7.0",
|
||||||
"eslint-plugin-import": "2.26.0",
|
"eslint-plugin-import": "2.26.0",
|
||||||
"eslint-plugin-node": "7.0.1",
|
"eslint-plugin-n": "15.2.4",
|
||||||
"eslint-plugin-promise": "4.3.1",
|
"eslint-plugin-promise": "6.0.0",
|
||||||
"eslint-plugin-standard": "4.1.0",
|
"eslint-plugin-vue": "9.3.0",
|
||||||
"eslint-plugin-vue": "5.2.3",
|
|
||||||
"eventsource-polyfill": "0.9.6",
|
"eventsource-polyfill": "0.9.6",
|
||||||
"express": "4.18.1",
|
"express": "4.18.1",
|
||||||
"file-loader": "3.0.1",
|
"file-loader": "3.0.1",
|
||||||
|
@ -105,7 +104,7 @@
|
||||||
"ora": "0.4.1",
|
"ora": "0.4.1",
|
||||||
"postcss-loader": "3.0.0",
|
"postcss-loader": "3.0.0",
|
||||||
"raw-loader": "0.5.1",
|
"raw-loader": "0.5.1",
|
||||||
"sass": "1.53.0",
|
"sass": "1.54.0",
|
||||||
"sass-loader": "7.3.1",
|
"sass-loader": "7.3.1",
|
||||||
"selenium-server": "2.53.1",
|
"selenium-server": "2.53.1",
|
||||||
"semver": "5.7.1",
|
"semver": "5.7.1",
|
||||||
|
@ -118,7 +117,7 @@
|
||||||
"stylelint-rscss": "0.4.0",
|
"stylelint-rscss": "0.4.0",
|
||||||
"url-loader": "1.1.2",
|
"url-loader": "1.1.2",
|
||||||
"vue-loader": "^16.0.0",
|
"vue-loader": "^16.0.0",
|
||||||
"vue-style-loader": "4.1.2",
|
"vue-style-loader": "4.1.3",
|
||||||
"webpack": "4.46.0",
|
"webpack": "4.46.0",
|
||||||
"webpack-dev-middleware": "3.7.3",
|
"webpack-dev-middleware": "3.7.3",
|
||||||
"webpack-hot-middleware": "2.25.1",
|
"webpack-hot-middleware": "2.25.1",
|
||||||
|
|
|
@ -4,7 +4,6 @@ import InstanceSpecificPanel from './components/instance_specific_panel/instance
|
||||||
import FeaturesPanel from './components/features_panel/features_panel.vue'
|
import FeaturesPanel from './components/features_panel/features_panel.vue'
|
||||||
import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'
|
import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'
|
||||||
import ShoutPanel from './components/shout_panel/shout_panel.vue'
|
import ShoutPanel from './components/shout_panel/shout_panel.vue'
|
||||||
import SettingsModal from './components/settings_modal/settings_modal.vue'
|
|
||||||
import MediaModal from './components/media_modal/media_modal.vue'
|
import MediaModal from './components/media_modal/media_modal.vue'
|
||||||
import SideDrawer from './components/side_drawer/side_drawer.vue'
|
import SideDrawer from './components/side_drawer/side_drawer.vue'
|
||||||
import MobilePostStatusButton from './components/mobile_post_status_button/mobile_post_status_button.vue'
|
import MobilePostStatusButton from './components/mobile_post_status_button/mobile_post_status_button.vue'
|
||||||
|
@ -32,7 +31,7 @@ export default {
|
||||||
MobilePostStatusButton,
|
MobilePostStatusButton,
|
||||||
MobileNav,
|
MobileNav,
|
||||||
DesktopNav,
|
DesktopNav,
|
||||||
SettingsModal,
|
SettingsModal: defineAsyncComponent(() => import('./components/settings_modal/settings_modal.vue')),
|
||||||
UserReportingModal,
|
UserReportingModal,
|
||||||
PostStatusModal,
|
PostStatusModal,
|
||||||
GlobalNoticeList
|
GlobalNoticeList
|
||||||
|
|
11
src/App.scss
11
src/App.scss
|
@ -4,6 +4,13 @@
|
||||||
:root {
|
:root {
|
||||||
--navbar-height: 3.5rem;
|
--navbar-height: 3.5rem;
|
||||||
--post-line-height: 1.4;
|
--post-line-height: 1.4;
|
||||||
|
// Z-Index stuff
|
||||||
|
--ZI_media_modal: 90000;
|
||||||
|
--ZI_modals_popovers: 85000;
|
||||||
|
--ZI_modals: 80000;
|
||||||
|
--ZI_navbar_popovers: 75000;
|
||||||
|
--ZI_navbar: 70000;
|
||||||
|
--ZI_popovers: 60000;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
|
@ -117,7 +124,7 @@ i[class*=icon-],
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
z-index: 1000;
|
z-index: var(--ZI_navbar);
|
||||||
color: var(--topBarText);
|
color: var(--topBarText);
|
||||||
background-color: $fallback--fg;
|
background-color: $fallback--fg;
|
||||||
background-color: var(--topBar, $fallback--fg);
|
background-color: var(--topBar, $fallback--fg);
|
||||||
|
@ -828,7 +835,7 @@ option {
|
||||||
// Vue transitions
|
// Vue transitions
|
||||||
.fade-enter-active,
|
.fade-enter-active,
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-enter-from,
|
.fade-enter-from,
|
||||||
|
|
23
src/App.vue
23
src/App.vue
|
@ -15,8 +15,12 @@
|
||||||
class="app-layout container"
|
class="app-layout container"
|
||||||
:class="classes"
|
:class="classes"
|
||||||
>
|
>
|
||||||
<div class="underlay"/>
|
<div class="underlay" />
|
||||||
<div id="sidebar" class="column -scrollable" :class="{ '-show-scrollbar': showScrollbars }">
|
<div
|
||||||
|
id="sidebar"
|
||||||
|
class="column -scrollable"
|
||||||
|
:class="{ '-show-scrollbar': showScrollbars }"
|
||||||
|
>
|
||||||
<user-panel />
|
<user-panel />
|
||||||
<template v-if="layoutType !== 'mobile'">
|
<template v-if="layoutType !== 'mobile'">
|
||||||
<nav-panel />
|
<nav-panel />
|
||||||
|
@ -26,7 +30,11 @@
|
||||||
<div id="notifs-sidebar" />
|
<div id="notifs-sidebar" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div id="main-scroller" class="column main" :class="{ '-full-height': isChats }">
|
<div
|
||||||
|
id="main-scroller"
|
||||||
|
class="column main"
|
||||||
|
:class="{ '-full-height': isChats }"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-if="!currentUser"
|
v-if="!currentUser"
|
||||||
class="login-hint panel panel-default"
|
class="login-hint panel panel-default"
|
||||||
|
@ -40,9 +48,13 @@
|
||||||
</div>
|
</div>
|
||||||
<router-view />
|
<router-view />
|
||||||
</div>
|
</div>
|
||||||
<div id="notifs-column" class="column -scrollable" :class="{ '-show-scrollbar': showScrollbars }"/>
|
<div
|
||||||
|
id="notifs-column"
|
||||||
|
class="column -scrollable"
|
||||||
|
:class="{ '-show-scrollbar': showScrollbars }"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<media-modal />
|
<MediaModal />
|
||||||
<shout-panel
|
<shout-panel
|
||||||
v-if="currentUser && shout && !hideShoutbox"
|
v-if="currentUser && shout && !hideShoutbox"
|
||||||
:floating="true"
|
:floating="true"
|
||||||
|
@ -55,6 +67,7 @@
|
||||||
<SettingsModal />
|
<SettingsModal />
|
||||||
<div id="modal" />
|
<div id="modal" />
|
||||||
<GlobalNoticeList />
|
<GlobalNoticeList />
|
||||||
|
<div id="popovers" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
|
||||||
copyInstanceOption('hideSitename')
|
copyInstanceOption('hideSitename')
|
||||||
copyInstanceOption('sidebarRight')
|
copyInstanceOption('sidebarRight')
|
||||||
|
|
||||||
return store.dispatch('setTheme', config['theme'])
|
return store.dispatch('setTheme', config.theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTOS = async ({ store }) => {
|
const getTOS = async ({ store }) => {
|
||||||
|
@ -197,7 +197,7 @@ const getStickers = async ({ store }) => {
|
||||||
const stickers = (await Promise.all(
|
const stickers = (await Promise.all(
|
||||||
Object.entries(values).map(async ([name, path]) => {
|
Object.entries(values).map(async ([name, path]) => {
|
||||||
const resPack = await window.fetch(path + 'pack.json')
|
const resPack = await window.fetch(path + 'pack.json')
|
||||||
var meta = {}
|
let meta = {}
|
||||||
if (resPack.ok) {
|
if (resPack.ok) {
|
||||||
meta = await resPack.json()
|
meta = await resPack.json()
|
||||||
}
|
}
|
||||||
|
@ -319,6 +319,7 @@ const setConfig = async ({ store }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkOAuthToken = async ({ store }) => {
|
const checkOAuthToken = async ({ store }) => {
|
||||||
|
// eslint-disable-next-line no-async-promise-executor
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
if (store.getters.getUserToken()) {
|
if (store.getters.getUserToken()) {
|
||||||
try {
|
try {
|
||||||
|
@ -396,6 +397,9 @@ const afterStoreSetup = async ({ store, i18n }) => {
|
||||||
app.component('FAIcon', FontAwesomeIcon)
|
app.component('FAIcon', FontAwesomeIcon)
|
||||||
app.component('FALayers', FontAwesomeLayers)
|
app.component('FALayers', FontAwesomeLayers)
|
||||||
|
|
||||||
|
// remove after vue 3.3
|
||||||
|
app.config.unwrapInjectedRef = true
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
|
@ -31,7 +31,8 @@ export default (store) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let routes = [
|
let routes = [
|
||||||
{ name: 'root',
|
{
|
||||||
|
name: 'root',
|
||||||
path: '/',
|
path: '/',
|
||||||
redirect: _to => {
|
redirect: _to => {
|
||||||
return (store.state.users.currentUser
|
return (store.state.users.currentUser
|
||||||
|
@ -45,12 +46,14 @@ export default (store) => {
|
||||||
{ name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline },
|
{ name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline },
|
||||||
{ name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline },
|
{ name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline },
|
||||||
{ name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
|
{ name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
|
||||||
{ name: 'remote-user-profile-acct',
|
{
|
||||||
|
name: 'remote-user-profile-acct',
|
||||||
path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)',
|
path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)',
|
||||||
component: RemoteUserResolver,
|
component: RemoteUserResolver,
|
||||||
beforeEnter: validateAuthenticatedRoute
|
beforeEnter: validateAuthenticatedRoute
|
||||||
},
|
},
|
||||||
{ name: 'remote-user-profile',
|
{
|
||||||
|
name: 'remote-user-profile',
|
||||||
path: '/remote-users/:hostname/:username',
|
path: '/remote-users/:hostname/:username',
|
||||||
component: RemoteUserResolver,
|
component: RemoteUserResolver,
|
||||||
beforeEnter: validateAuthenticatedRoute
|
beforeEnter: validateAuthenticatedRoute
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./about.js" ></script>
|
<script src="./about.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
:bound-to="{ x: 'container' }"
|
:bound-to="{ x: 'container' }"
|
||||||
remove-padding
|
remove-padding
|
||||||
>
|
>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<template v-if="relationship.following">
|
<template v-if="relationship.following">
|
||||||
<button
|
<button
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:trigger>
|
<template #trigger>
|
||||||
<button class="button-unstyled ellipsis-button">
|
<button class="button-unstyled ellipsis-button">
|
||||||
<FAIcon
|
<FAIcon
|
||||||
class="icon"
|
class="icon"
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./avatar_list.js" ></script>
|
<script src="./avatar_list.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import UserCard from '../user_card/user_card.vue'
|
import UserPopover from '../user_popover/user_popover.vue'
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
|
@ -7,20 +7,12 @@ const BasicUserCard = {
|
||||||
props: [
|
props: [
|
||||||
'user'
|
'user'
|
||||||
],
|
],
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
userExpanded: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
UserCard,
|
UserPopover,
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
RichContent
|
RichContent
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleUserExpanded () {
|
|
||||||
this.userExpanded = !this.userExpanded
|
|
||||||
},
|
|
||||||
userProfileLink (user) {
|
userProfileLink (user) {
|
||||||
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
|
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,22 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="basic-user-card">
|
<div class="basic-user-card">
|
||||||
<router-link :to="userProfileLink(user)">
|
<router-link
|
||||||
<UserAvatar
|
:to="userProfileLink(user)"
|
||||||
class="avatar"
|
@click.prevent
|
||||||
:user="user"
|
>
|
||||||
@click.prevent="toggleUserExpanded"
|
<UserPopover
|
||||||
/>
|
:user-id="user.id"
|
||||||
|
:overlay-centers="true"
|
||||||
|
overlay-centers-selector=".avatar"
|
||||||
|
>
|
||||||
|
<UserAvatar
|
||||||
|
class="user-avatar avatar"
|
||||||
|
:user="user"
|
||||||
|
@click.prevent
|
||||||
|
/>
|
||||||
|
</UserPopover>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div
|
<div
|
||||||
v-if="userExpanded"
|
|
||||||
class="basic-user-card-expanded-content"
|
|
||||||
>
|
|
||||||
<UserCard
|
|
||||||
:user-id="user.id"
|
|
||||||
:rounded="true"
|
|
||||||
:bordered="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="basic-user-card-collapsed-content"
|
class="basic-user-card-collapsed-content"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -53,6 +51,8 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0.6em 1em;
|
padding: 0.6em 1em;
|
||||||
|
|
||||||
|
--emoji-size: 14px;
|
||||||
|
|
||||||
&-collapsed-content {
|
&-collapsed-content {
|
||||||
margin-left: 0.7em;
|
margin-left: 0.7em;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
|
@ -107,7 +107,7 @@ const Chat = {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
'$route': function () {
|
$route: function () {
|
||||||
this.startFetching()
|
this.startFetching()
|
||||||
},
|
},
|
||||||
mastoUserSocketStatus (newValue) {
|
mastoUserSocketStatus (newValue) {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
class="timeline"
|
class="timeline"
|
||||||
>
|
>
|
||||||
<List :items="sortedChatList">
|
<List :items="sortedChatList">
|
||||||
<template v-slot:item="{item}">
|
<template #item="{item}">
|
||||||
<ChatListItem
|
<ChatListItem
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:compact="false"
|
:compact="false"
|
||||||
|
|
|
@ -6,7 +6,7 @@ import Gallery from '../gallery/gallery.vue'
|
||||||
import LinkPreview from '../link-preview/link-preview.vue'
|
import LinkPreview from '../link-preview/link-preview.vue'
|
||||||
import StatusContent from '../status_content/status_content.vue'
|
import StatusContent from '../status_content/status_content.vue'
|
||||||
import ChatMessageDate from '../chat_message_date/chat_message_date.vue'
|
import ChatMessageDate from '../chat_message_date/chat_message_date.vue'
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import { defineAsyncComponent } from 'vue'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faTimes,
|
faTimes,
|
||||||
|
@ -35,7 +35,8 @@ const ChatMessage = {
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
Gallery,
|
Gallery,
|
||||||
LinkPreview,
|
LinkPreview,
|
||||||
ChatMessageDate
|
ChatMessageDate,
|
||||||
|
UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue'))
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
// Returns HH:MM (hours and minutes) in local time.
|
// Returns HH:MM (hours and minutes) in local time.
|
||||||
|
@ -49,9 +50,6 @@ const ChatMessage = {
|
||||||
message () {
|
message () {
|
||||||
return this.chatViewItem.data
|
return this.chatViewItem.data
|
||||||
},
|
},
|
||||||
userProfileLink () {
|
|
||||||
return generateProfileLink(this.author.id, this.author.screen_name, this.$store.state.instance.restrictedNicknames)
|
|
||||||
},
|
|
||||||
isMessage () {
|
isMessage () {
|
||||||
return this.chatViewItem.type === 'message'
|
return this.chatViewItem.type === 'message'
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,16 +14,16 @@
|
||||||
v-if="!isCurrentUser"
|
v-if="!isCurrentUser"
|
||||||
class="avatar-wrapper"
|
class="avatar-wrapper"
|
||||||
>
|
>
|
||||||
<router-link
|
<UserPopover
|
||||||
v-if="chatViewItem.isHead"
|
v-if="chatViewItem.isHead"
|
||||||
:to="userProfileLink"
|
:user-id="author.id"
|
||||||
>
|
>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
:compact="true"
|
:compact="true"
|
||||||
:better-shadow="betterShadow"
|
:better-shadow="betterShadow"
|
||||||
:user="author"
|
:user="author"
|
||||||
/>
|
/>
|
||||||
</router-link>
|
</UserPopover>
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-message-inner">
|
<div class="chat-message-inner">
|
||||||
<div
|
<div
|
||||||
|
@ -44,13 +44,13 @@
|
||||||
<Popover
|
<Popover
|
||||||
trigger="click"
|
trigger="click"
|
||||||
placement="top"
|
placement="top"
|
||||||
:bound-to-selector="isCurrentUser ? '' : '.scrollable-message-list'"
|
bound-to-selector=".chat-view-inner"
|
||||||
:bound-to="{ x: 'container' }"
|
:bound-to="{ x: 'container' }"
|
||||||
:margin="popoverMarginStyle"
|
:margin="popoverMarginStyle"
|
||||||
@show="menuOpened = true"
|
@show="menuOpened = true"
|
||||||
@close="menuOpened = false"
|
@close="menuOpened = false"
|
||||||
>
|
>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<button
|
<button
|
||||||
class="button-default dropdown-item dropdown-item-icon"
|
class="button-default dropdown-item dropdown-item-icon"
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:trigger>
|
<template #trigger>
|
||||||
<button
|
<button
|
||||||
class="button-default menu-icon"
|
class="button-default menu-icon"
|
||||||
:title="$t('chats.more')"
|
:title="$t('chats.more')"
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
:status="messageForStatusContent"
|
:status="messageForStatusContent"
|
||||||
:full-content="true"
|
:full-content="true"
|
||||||
>
|
>
|
||||||
<template v-slot:footer>
|
<template #footer>
|
||||||
<span
|
<span
|
||||||
class="created-at"
|
class="created-at"
|
||||||
>
|
>
|
||||||
|
@ -96,7 +96,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./chat_message.js" ></script>
|
<script src="./chat_message.js"></script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import './chat_message.scss';
|
@import './chat_message.scss';
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ChatTitle',
|
name: 'ChatTitle',
|
||||||
components: {
|
components: {
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
RichContent
|
RichContent,
|
||||||
|
UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue'))
|
||||||
},
|
},
|
||||||
props: [
|
props: [
|
||||||
'user', 'withAvatar'
|
'user', 'withAvatar'
|
||||||
|
@ -18,10 +19,5 @@ export default {
|
||||||
htmlTitle () {
|
htmlTitle () {
|
||||||
return this.user ? this.user.name_html : ''
|
return this.user ? this.user.name_html : ''
|
||||||
}
|
}
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getUserProfileLink (user) {
|
|
||||||
return generateProfileLink(user.id, user.screen_name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
class="chat-title"
|
class="chat-title"
|
||||||
:title="title"
|
:title="title"
|
||||||
>
|
>
|
||||||
<router-link
|
<UserPopover
|
||||||
class="avatar-container"
|
|
||||||
v-if="withAvatar && user"
|
v-if="withAvatar && user"
|
||||||
:to="getUserProfileLink(user)"
|
class="avatar-container"
|
||||||
|
:user-id="user.id"
|
||||||
>
|
>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
class="titlebar-avatar"
|
class="titlebar-avatar"
|
||||||
:user="user"
|
:user="user"
|
||||||
/>
|
/>
|
||||||
</router-link>
|
</UserPopover>
|
||||||
<RichContent
|
<RichContent
|
||||||
v-if="user"
|
v-if="user"
|
||||||
class="username"
|
class="username"
|
||||||
|
|
|
@ -22,12 +22,12 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
emits: ['update:modelValue'],
|
|
||||||
props: [
|
props: [
|
||||||
'modelValue',
|
'modelValue',
|
||||||
'indeterminate',
|
'indeterminate',
|
||||||
'disabled'
|
'disabled'
|
||||||
]
|
],
|
||||||
|
emits: ['update:modelValue']
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" src="./color_input.scss"></style>
|
|
||||||
<script>
|
<script>
|
||||||
import Checkbox from '../checkbox/checkbox.vue'
|
import Checkbox from '../checkbox/checkbox.vue'
|
||||||
import { hex2rgb } from '../../services/color_convert/color_convert.js'
|
import { hex2rgb } from '../../services/color_convert/color_convert.js'
|
||||||
|
@ -108,6 +107,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss" src="./color_input.scss"></style>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.color-control {
|
.color-control {
|
||||||
|
|
|
@ -271,7 +271,7 @@ const conversation = {
|
||||||
result[irid] = result[irid] || []
|
result[irid] = result[irid] || []
|
||||||
result[irid].push({
|
result[irid].push({
|
||||||
name: `#${i}`,
|
name: `#${i}`,
|
||||||
id: id
|
id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
|
|
|
@ -31,8 +31,8 @@
|
||||||
keypath="status.show_all_conversation_with_icon"
|
keypath="status.show_all_conversation_with_icon"
|
||||||
tag="button"
|
tag="button"
|
||||||
class="button-unstyled -link"
|
class="button-unstyled -link"
|
||||||
@click.prevent="diveToTopLevel"
|
|
||||||
scope="global"
|
scope="global"
|
||||||
|
@click.prevent="diveToTopLevel"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
|
|
|
@ -46,23 +46,27 @@ export default {
|
||||||
enableMask () { return this.supportsMask && this.$store.state.instance.logoMask },
|
enableMask () { return this.supportsMask && this.$store.state.instance.logoMask },
|
||||||
logoStyle () {
|
logoStyle () {
|
||||||
return {
|
return {
|
||||||
'visibility': this.enableMask ? 'hidden' : 'visible'
|
visibility: this.enableMask ? 'hidden' : 'visible'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
logoMaskStyle () {
|
logoMaskStyle () {
|
||||||
return this.enableMask ? {
|
return this.enableMask
|
||||||
'mask-image': `url(${this.$store.state.instance.logo})`
|
? {
|
||||||
} : {
|
'mask-image': `url(${this.$store.state.instance.logo})`
|
||||||
'background-color': this.enableMask ? '' : 'transparent'
|
}
|
||||||
}
|
: {
|
||||||
|
'background-color': this.enableMask ? '' : 'transparent'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
logoBgStyle () {
|
logoBgStyle () {
|
||||||
return Object.assign({
|
return Object.assign({
|
||||||
'margin': `${this.$store.state.instance.logoMargin} 0`,
|
margin: `${this.$store.state.instance.logoMargin} 0`,
|
||||||
opacity: this.searchBarHidden ? 1 : 0
|
opacity: this.searchBarHidden ? 1 : 0
|
||||||
}, this.enableMask ? {} : {
|
}, this.enableMask
|
||||||
'background-color': this.enableMask ? '' : 'transparent'
|
? {}
|
||||||
})
|
: {
|
||||||
|
'background-color': this.enableMask ? '' : 'transparent'
|
||||||
|
})
|
||||||
},
|
},
|
||||||
logo () { return this.$store.state.instance.logo },
|
logo () { return this.$store.state.instance.logo },
|
||||||
sitename () { return this.$store.state.instance.name },
|
sitename () { return this.$store.state.instance.name },
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
.DesktopNav {
|
.DesktopNav {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
z-index: var(--ZI_navbar);
|
||||||
|
|
||||||
input {
|
input {
|
||||||
color: var(--inputTopbarText, var(--inputText));
|
color: var(--inputTopbarText, var(--inputText));
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled nav-icon"
|
class="button-unstyled nav-icon"
|
||||||
@click.stop="openSettingsModal"
|
@click="openSettingsModal"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
fixed-width
|
fixed-width
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
>
|
>
|
||||||
{{ $t('domain_mute_card.unmute') }}
|
{{ $t('domain_mute_card.unmute') }}
|
||||||
<template v-slot:progress>
|
<template #progress>
|
||||||
{{ $t('domain_mute_card.unmute_progress') }}
|
{{ $t('domain_mute_card.unmute_progress') }}
|
||||||
</template>
|
</template>
|
||||||
</ProgressButton>
|
</ProgressButton>
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
>
|
>
|
||||||
{{ $t('domain_mute_card.mute') }}
|
{{ $t('domain_mute_card.mute') }}
|
||||||
<template v-slot:progress>
|
<template #progress>
|
||||||
{{ $t('domain_mute_card.mute_progress') }}
|
{{ $t('domain_mute_card.mute_progress') }}
|
||||||
</template>
|
</template>
|
||||||
</ProgressButton>
|
</ProgressButton>
|
||||||
|
|
|
@ -321,7 +321,7 @@ const EmojiInput = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scrollIntoView () {
|
scrollIntoView () {
|
||||||
const rootRef = this.$refs['picker'].$el
|
const rootRef = this.$refs.picker.$el
|
||||||
/* Scroller is either `window` (replies in TL), sidebar (main post form,
|
/* Scroller is either `window` (replies in TL), sidebar (main post form,
|
||||||
* replies in notifs) or mobile post form. Note that getting and setting
|
* replies in notifs) or mobile post form. Note that getting and setting
|
||||||
* scroll is different for `Window` and `Element`s
|
* scroll is different for `Window` and `Element`s
|
||||||
|
|
|
@ -25,7 +25,7 @@ const filterByKeyword = (list, keyword = '') => {
|
||||||
if (keyword === '') return list
|
if (keyword === '') return list
|
||||||
|
|
||||||
const keywordLowercase = keyword.toLowerCase()
|
const keywordLowercase = keyword.toLowerCase()
|
||||||
let orderedEmojiList = []
|
const orderedEmojiList = []
|
||||||
for (const emoji of list) {
|
for (const emoji of list) {
|
||||||
const indexOfKeyword = emoji.displayText.toLowerCase().indexOf(keywordLowercase)
|
const indexOfKeyword = emoji.displayText.toLowerCase().indexOf(keywordLowercase)
|
||||||
if (indexOfKeyword > -1) {
|
if (indexOfKeyword > -1) {
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
z-index: 100;
|
// TODO: actually use popover in emoji picker
|
||||||
|
z-index: var(--ZI_popovers);
|
||||||
background-color: $fallback--bg;
|
background-color: $fallback--bg;
|
||||||
background-color: var(--popover, $fallback--bg);
|
background-color: var(--popover, $fallback--bg);
|
||||||
color: $fallback--link;
|
color: $fallback--link;
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./emoji_reactions.js" ></script>
|
<script src="./emoji_reactions.js"></script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ library.add(
|
||||||
)
|
)
|
||||||
|
|
||||||
const ExtraButtons = {
|
const ExtraButtons = {
|
||||||
props: [ 'status' ],
|
props: ['status'],
|
||||||
components: { Popover },
|
components: { Popover },
|
||||||
methods: {
|
methods: {
|
||||||
deleteStatus () {
|
deleteStatus () {
|
||||||
|
@ -89,6 +89,9 @@ const ExtraButtons = {
|
||||||
canMute () {
|
canMute () {
|
||||||
return !!this.currentUser
|
return !!this.currentUser
|
||||||
},
|
},
|
||||||
|
canBookmark () {
|
||||||
|
return !!this.currentUser
|
||||||
|
},
|
||||||
statusLink () {
|
statusLink () {
|
||||||
return `${this.$store.state.instance.server}${this.$router.resolve({ name: 'conversation', params: { id: this.status.id } }).href}`
|
return `${this.$store.state.instance.server}${this.$router.resolve({ name: 'conversation', params: { id: this.status.id } }).href}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
:bound-to="{ x: 'container' }"
|
:bound-to="{ x: 'container' }"
|
||||||
remove-padding
|
remove-padding
|
||||||
>
|
>
|
||||||
<template v-slot:content="{close}">
|
<template #content="{close}">
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<button
|
<button
|
||||||
v-if="canMute && !status.thread_muted"
|
v-if="canMute && !status.thread_muted"
|
||||||
|
@ -51,28 +51,30 @@
|
||||||
icon="thumbtack"
|
icon="thumbtack"
|
||||||
/><span>{{ $t("status.unpin") }}</span>
|
/><span>{{ $t("status.unpin") }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<template v-if="canBookmark">
|
||||||
v-if="!status.bookmarked"
|
<button
|
||||||
class="button-default dropdown-item dropdown-item-icon"
|
v-if="!status.bookmarked"
|
||||||
@click.prevent="bookmarkStatus"
|
class="button-default dropdown-item dropdown-item-icon"
|
||||||
@click="close"
|
@click.prevent="bookmarkStatus"
|
||||||
>
|
@click="close"
|
||||||
<FAIcon
|
>
|
||||||
fixed-width
|
<FAIcon
|
||||||
:icon="['far', 'bookmark']"
|
fixed-width
|
||||||
/><span>{{ $t("status.bookmark") }}</span>
|
:icon="['far', 'bookmark']"
|
||||||
</button>
|
/><span>{{ $t("status.bookmark") }}</span>
|
||||||
<button
|
</button>
|
||||||
v-if="status.bookmarked"
|
<button
|
||||||
class="button-default dropdown-item dropdown-item-icon"
|
v-if="status.bookmarked"
|
||||||
@click.prevent="unbookmarkStatus"
|
class="button-default dropdown-item dropdown-item-icon"
|
||||||
@click="close"
|
@click.prevent="unbookmarkStatus"
|
||||||
>
|
@click="close"
|
||||||
<FAIcon
|
>
|
||||||
fixed-width
|
<FAIcon
|
||||||
icon="bookmark"
|
fixed-width
|
||||||
/><span>{{ $t("status.unbookmark") }}</span>
|
icon="bookmark"
|
||||||
</button>
|
/><span>{{ $t("status.unbookmark") }}</span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
<button
|
<button
|
||||||
v-if="canDelete"
|
v-if="canDelete"
|
||||||
class="button-default dropdown-item dropdown-item-icon"
|
class="button-default dropdown-item dropdown-item-icon"
|
||||||
|
@ -118,18 +120,18 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:trigger>
|
<template #trigger>
|
||||||
<button class="button-unstyled popover-trigger">
|
<span class="button-unstyled popover-trigger">
|
||||||
<FAIcon
|
<FAIcon
|
||||||
class="fa-scale-110 fa-old-padding"
|
class="fa-scale-110 fa-old-padding"
|
||||||
icon="ellipsis-h"
|
icon="ellipsis-h"
|
||||||
/>
|
/>
|
||||||
</button>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</Popover>
|
</Popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./extra_buttons.js" ></script>
|
<script src="./extra_buttons.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./favorite_button.js" ></script>
|
<script src="./favorite_button.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./features_panel.js" ></script>
|
<script src="./features_panel.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.features-panel li {
|
.features-panel li {
|
||||||
|
|
|
@ -11,7 +11,7 @@ library.add(
|
||||||
)
|
)
|
||||||
|
|
||||||
const Flash = {
|
const Flash = {
|
||||||
props: [ 'src' ],
|
props: ['src'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
player: false, // can be true, "hidden", false. hidden = element exists
|
player: false, // can be true, "hidden", false. hidden = element exists
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./font_control.js" ></script>
|
<script src="./font_control.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
top: 50px;
|
top: 50px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 1001;
|
z-index: var(--ZI_popovers);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -14,6 +14,6 @@
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./hashtag_link.js"/>
|
<script src="./hashtag_link.js" />
|
||||||
|
|
||||||
<style lang="scss" src="./hashtag_link.scss"/>
|
<style lang="scss" src="./hashtag_link.scss" />
|
||||||
|
|
|
@ -95,7 +95,7 @@ const ImageCropper = {
|
||||||
const fileInput = this.$refs.input
|
const fileInput = this.$refs.input
|
||||||
if (fileInput.files != null && fileInput.files[0] != null) {
|
if (fileInput.files != null && fileInput.files[0] != null) {
|
||||||
this.file = fileInput.files[0]
|
this.file = fileInput.files[0]
|
||||||
let reader = new window.FileReader()
|
const reader = new window.FileReader()
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
this.dataUrl = e.target.result
|
this.dataUrl = e.target.result
|
||||||
this.$emit('open')
|
this.$emit('open')
|
||||||
|
|
|
@ -10,4 +10,4 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./instance_specific_panel.js" ></script>
|
<script src="./instance_specific_panel.js"></script>
|
||||||
|
|
|
@ -14,7 +14,7 @@ const Interactions = {
|
||||||
data () {
|
data () {
|
||||||
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: ['moderator', 'admin'].includes(this.$store.state.users.currentUser.role)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,6 +25,7 @@ import Select from '../select/select.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
// eslint-disable-next-line vue/no-reserved-component-names
|
||||||
Select
|
Select
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -83,7 +83,7 @@ const LoginForm = {
|
||||||
},
|
},
|
||||||
clearError () { this.error = false },
|
clearError () { this.error = false },
|
||||||
focusOnPasswordInput () {
|
focusOnPasswordInput () {
|
||||||
let passwordInput = this.$refs.passwordInput
|
const passwordInput = this.$refs.passwordInput
|
||||||
passwordInput.focus()
|
passwordInput.focus()
|
||||||
passwordInput.setSelectionRange(0, passwordInput.value.length)
|
passwordInput.setSelectionRange(0, passwordInput.value.length)
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./login_form.js" ></script>
|
<script src="./login_form.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
|
@ -121,7 +121,7 @@ $modal-view-button-icon-width: 3em;
|
||||||
$modal-view-button-icon-margin: 0.5em;
|
$modal-view-button-icon-margin: 0.5em;
|
||||||
|
|
||||||
.modal-view.media-modal-view {
|
.modal-view.media-modal-view {
|
||||||
z-index: 9000;
|
z-index: var(--ZI_media_modal);
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.modal-view-button-arrow,
|
.modal-view-button-arrow,
|
||||||
|
|
|
@ -42,7 +42,8 @@ const mediaUpload = {
|
||||||
.then((fileData) => {
|
.then((fileData) => {
|
||||||
self.$emit('uploaded', fileData)
|
self.$emit('uploaded', fileData)
|
||||||
self.decreaseUploadCount()
|
self.decreaseUploadCount()
|
||||||
}, (error) => { // eslint-disable-line handle-callback-err
|
}, (error) => {
|
||||||
|
console.error('Error uploading file', error)
|
||||||
self.$emit('upload-failed', 'default')
|
self.$emit('upload-failed', 'default')
|
||||||
self.decreaseUploadCount()
|
self.decreaseUploadCount()
|
||||||
})
|
})
|
||||||
|
@ -73,7 +74,7 @@ const mediaUpload = {
|
||||||
'disabled'
|
'disabled'
|
||||||
],
|
],
|
||||||
watch: {
|
watch: {
|
||||||
'dropFiles': function (fileInfos) {
|
dropFiles: function (fileInfos) {
|
||||||
if (!this.uploading) {
|
if (!this.uploading) {
|
||||||
this.multiUpload(fileInfos)
|
this.multiUpload(fileInfos)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./media_upload.js" ></script>
|
<script src="./media_upload.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
|
@ -2,6 +2,7 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_p
|
||||||
import { mapGetters, mapState } from 'vuex'
|
import { mapGetters, mapState } from 'vuex'
|
||||||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faAt
|
faAt
|
||||||
|
@ -14,7 +15,8 @@ library.add(
|
||||||
const MentionLink = {
|
const MentionLink = {
|
||||||
name: 'MentionLink',
|
name: 'MentionLink',
|
||||||
components: {
|
components: {
|
||||||
UserAvatar
|
UserAvatar,
|
||||||
|
UserPopover: defineAsyncComponent(() => import('../user_popover/user_popover.vue'))
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
url: {
|
url: {
|
||||||
|
@ -34,15 +36,30 @@ const MentionLink = {
|
||||||
type: String
|
type: String
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
hasSelection: false
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onClick () {
|
onClick () {
|
||||||
|
if (this.shouldShowTooltip) return
|
||||||
const link = generateProfileLink(
|
const link = generateProfileLink(
|
||||||
this.userId || this.user.id,
|
this.userId || this.user.id,
|
||||||
this.userScreenName || this.user.screen_name
|
this.userScreenName || this.user.screen_name
|
||||||
)
|
)
|
||||||
this.$router.push(link)
|
this.$router.push(link)
|
||||||
|
},
|
||||||
|
handleSelection () {
|
||||||
|
this.hasSelection = document.getSelection().containsNode(this.$refs.full, true)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted () {
|
||||||
|
document.addEventListener('selectionchange', this.handleSelection)
|
||||||
|
},
|
||||||
|
unmounted () {
|
||||||
|
document.removeEventListener('selectionchange', this.handleSelection)
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user () {
|
user () {
|
||||||
return this.url && this.$store && this.$store.getters.findUserByUrl(this.url)
|
return this.url && this.$store && this.$store.getters.findUserByUrl(this.url)
|
||||||
|
@ -88,7 +105,8 @@ const MentionLink = {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'-you': this.isYou && this.shouldBoldenYou,
|
'-you': this.isYou && this.shouldBoldenYou,
|
||||||
'-highlighted': this.highlight
|
'-highlighted': this.highlight,
|
||||||
|
'-has-selection': this.hasSelection
|
||||||
},
|
},
|
||||||
this.highlightType
|
this.highlightType
|
||||||
]
|
]
|
||||||
|
@ -110,7 +128,7 @@ const MentionLink = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
shouldShowTooltip () {
|
shouldShowTooltip () {
|
||||||
return this.mergedConfig.mentionLinkShowTooltip && this.mergedConfig.mentionLinkDisplay === 'short' && this.isRemote
|
return this.mergedConfig.mentionLinkShowTooltip
|
||||||
},
|
},
|
||||||
shouldShowAvatar () {
|
shouldShowAvatar () {
|
||||||
return this.mergedConfig.mentionLinkShowAvatar
|
return this.mergedConfig.mentionLinkShowAvatar
|
||||||
|
|
|
@ -55,11 +55,14 @@
|
||||||
|
|
||||||
.new {
|
.new {
|
||||||
&.-you {
|
&.-you {
|
||||||
& .shortName,
|
.shortName {
|
||||||
& .full {
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.-has-selection {
|
||||||
|
color: var(--alertNeutralText, $fallback--text);
|
||||||
|
background-color: var(--alertNeutral, $fallback--fg);
|
||||||
|
}
|
||||||
|
|
||||||
.at {
|
.at {
|
||||||
color: var(--link);
|
color: var(--link);
|
||||||
|
@ -72,8 +75,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.-striped {
|
&.-striped {
|
||||||
& .shortName,
|
& .shortName {
|
||||||
& .full {
|
|
||||||
background-image:
|
background-image:
|
||||||
repeating-linear-gradient(
|
repeating-linear-gradient(
|
||||||
135deg,
|
135deg,
|
||||||
|
@ -86,30 +88,29 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.-solid {
|
&.-solid {
|
||||||
& .shortName,
|
.shortName {
|
||||||
& .full {
|
|
||||||
background-image: linear-gradient(var(--____highlight-tintColor2), var(--____highlight-tintColor2));
|
background-image: linear-gradient(var(--____highlight-tintColor2), var(--____highlight-tintColor2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.-side {
|
&.-side {
|
||||||
& .shortName,
|
.shortName {
|
||||||
& .userNameFull {
|
|
||||||
box-shadow: 0 -5px 3px -4px inset var(--____highlight-solidColor);
|
box-shadow: 0 -5px 3px -4px inset var(--____highlight-solidColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover .new .full {
|
.full {
|
||||||
opacity: 1;
|
pointer-events: none;
|
||||||
pointer-events: initial;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.serverName.-faded {
|
.serverName.-faded {
|
||||||
color: var(--faintLink, $fallback--link);
|
color: var(--faintLink, $fallback--link);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.full .-faded {
|
|
||||||
color: var(--faint, $fallback--faint);
|
.mention-link-popover {
|
||||||
}
|
max-width: 70ch;
|
||||||
|
max-height: 20rem;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,69 +9,64 @@
|
||||||
class="original"
|
class="original"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
v-html="content"
|
v-html="content"
|
||||||
/><!-- eslint-enable vue/no-v-html --><span
|
/><!-- eslint-enable vue/no-v-html -->
|
||||||
v-if="user"
|
<UserPopover
|
||||||
class="new"
|
v-else
|
||||||
:style="style"
|
:user-id="user.id"
|
||||||
:class="classnames"
|
:disabled="!shouldShowTooltip"
|
||||||
>
|
>
|
||||||
<a
|
<span
|
||||||
class="short button-unstyled"
|
v-if="user"
|
||||||
:class="{ '-with-tooltip': shouldShowTooltip }"
|
class="new"
|
||||||
:href="url"
|
:style="style"
|
||||||
@click.prevent="onClick"
|
:class="classnames"
|
||||||
>
|
>
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<a
|
||||||
<UserAvatar
|
class="short button-unstyled"
|
||||||
v-if="shouldShowAvatar"
|
:class="{ '-with-tooltip': shouldShowTooltip }"
|
||||||
class="mention-avatar"
|
:href="url"
|
||||||
:user="user"
|
@click.prevent="onClick"
|
||||||
/><span
|
|
||||||
class="shortName"
|
|
||||||
><FAIcon
|
|
||||||
v-if="useAtIcon"
|
|
||||||
size="sm"
|
|
||||||
icon="at"
|
|
||||||
class="at"
|
|
||||||
/>{{ !useAtIcon ? '@' : '' }}<span
|
|
||||||
class="userName"
|
|
||||||
v-html="userName"
|
|
||||||
/><span
|
|
||||||
v-if="shouldShowFullUserName"
|
|
||||||
class="serverName"
|
|
||||||
:class="{ '-faded': shouldFadeDomain }"
|
|
||||||
v-html="'@' + serverName"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-if="isYou && shouldShowYous"
|
|
||||||
:class="{ '-you': shouldBoldenYou }"
|
|
||||||
> {{ ' ' + $t('status.you') }}</span>
|
|
||||||
<!-- eslint-enable vue/no-v-html -->
|
|
||||||
</a><span
|
|
||||||
v-if="shouldShowTooltip"
|
|
||||||
class="full popover-default"
|
|
||||||
:class="[highlightType]"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="userNameFull"
|
|
||||||
>
|
>
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
@<span
|
<UserAvatar
|
||||||
|
v-if="shouldShowAvatar"
|
||||||
|
class="mention-avatar"
|
||||||
|
:user="user"
|
||||||
|
/><span
|
||||||
|
class="shortName"
|
||||||
|
><FAIcon
|
||||||
|
v-if="useAtIcon"
|
||||||
|
size="sm"
|
||||||
|
icon="at"
|
||||||
|
class="at"
|
||||||
|
/>{{ !useAtIcon ? '@' : '' }}<span
|
||||||
class="userName"
|
class="userName"
|
||||||
v-html="userName"
|
v-html="userName"
|
||||||
/><span
|
/><span
|
||||||
|
v-if="shouldShowFullUserName"
|
||||||
class="serverName"
|
class="serverName"
|
||||||
:class="{ '-faded': shouldFadeDomain }"
|
:class="{ '-faded': shouldFadeDomain }"
|
||||||
v-html="'@' + serverName"
|
v-html="'@' + serverName"
|
||||||
/>
|
/>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="isYou && shouldShowYous"
|
||||||
|
:class="{ '-you': shouldBoldenYou }"
|
||||||
|
> {{ ' ' + $t('status.you') }}</span>
|
||||||
|
<!-- eslint-enable vue/no-v-html -->
|
||||||
|
</a><span
|
||||||
|
ref="full"
|
||||||
|
class="full"
|
||||||
|
>
|
||||||
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
|
@<span v-html="userName" /><span v-html="'@' + serverName" />
|
||||||
<!-- eslint-enable vue/no-v-html -->
|
<!-- eslint-enable vue/no-v-html -->
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</UserPopover>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./mention_link.js"/>
|
<script src="./mention_link.js" />
|
||||||
|
|
||||||
<style lang="scss" src="./mention_link.scss"/>
|
<style lang="scss" src="./mention_link.scss" />
|
||||||
|
|
|
@ -13,14 +13,13 @@
|
||||||
<span
|
<span
|
||||||
v-if="expanded"
|
v-if="expanded"
|
||||||
class="fullExtraMentions"
|
class="fullExtraMentions"
|
||||||
>
|
>{{ ' ' }}<MentionLink
|
||||||
<MentionLink
|
v-for="mention in extraMentions"
|
||||||
v-for="mention in extraMentions"
|
:key="mention.index"
|
||||||
:key="mention.index"
|
class="mention-link"
|
||||||
class="mention-link"
|
:content="mention.content"
|
||||||
:content="mention.content"
|
:url="mention.url"
|
||||||
:url="mention.url"
|
/>
|
||||||
/>
|
|
||||||
</span><button
|
</span><button
|
||||||
v-if="!expanded"
|
v-if="!expanded"
|
||||||
class="button-unstyled showMoreLess"
|
class="button-unstyled showMoreLess"
|
||||||
|
@ -37,5 +36,5 @@
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<script src="./mentions_line.js" ></script>
|
<script src="./mentions_line.js"></script>
|
||||||
<style lang="scss" src="./mentions_line.scss" />
|
<style lang="scss" src="./mentions_line.scss" />
|
||||||
|
|
|
@ -69,4 +69,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script src="./recovery_form.js" ></script>
|
<script src="./recovery_form.js"></script>
|
||||||
|
|
|
@ -67,11 +67,10 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mobile-notifications"
|
|
||||||
id="mobile-notifications"
|
id="mobile-notifications"
|
||||||
|
class="mobile-notifications"
|
||||||
@scroll="onScroll"
|
@scroll="onScroll"
|
||||||
>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<SideDrawer
|
<SideDrawer
|
||||||
ref="sideDrawer"
|
ref="sideDrawer"
|
||||||
|
@ -86,6 +85,8 @@
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
.MobileNav {
|
.MobileNav {
|
||||||
|
z-index: var(--ZI_navbar);
|
||||||
|
|
||||||
.mobile-nav {
|
.mobile-nav {
|
||||||
display: grid;
|
display: grid;
|
||||||
line-height: var(--navbar-height);
|
line-height: var(--navbar-height);
|
||||||
|
@ -147,7 +148,7 @@
|
||||||
transition-property: transform;
|
transition-property: transform;
|
||||||
transition-duration: 0.25s;
|
transition-duration: 0.25s;
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
z-index: 1001;
|
z-index: var(--ZI_navbar);
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
&.-closed {
|
&.-closed {
|
||||||
|
@ -160,7 +161,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
z-index: 1;
|
z-index: calc(var(--ZI_navbar) + 100);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
|
provide: {
|
||||||
|
popoversZLayer: 'modals'
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
isOpen: {
|
isOpen: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -26,7 +29,7 @@ export default {
|
||||||
classes () {
|
classes () {
|
||||||
return {
|
return {
|
||||||
'modal-background': !this.noBackground,
|
'modal-background': !this.noBackground,
|
||||||
'open': this.isOpen
|
open: this.isOpen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +38,7 @@ export default {
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.modal-view {
|
.modal-view {
|
||||||
z-index: 2000;
|
z-index: var(--ZI_modals);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
@show="setToggled(true)"
|
@show="setToggled(true)"
|
||||||
@close="setToggled(false)"
|
@close="setToggled(false)"
|
||||||
>
|
>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<span v-if="user.is_local">
|
<span v-if="user.is_local">
|
||||||
<button
|
<button
|
||||||
|
@ -122,7 +122,7 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:trigger>
|
<template #trigger>
|
||||||
<button
|
<button
|
||||||
class="btn button-default btn-block moderation-tools-button"
|
class="btn button-default btn-block moderation-tools-button"
|
||||||
:class="{ toggled }"
|
:class="{ toggled }"
|
||||||
|
@ -137,11 +137,11 @@
|
||||||
v-if="showDeleteUserDialog"
|
v-if="showDeleteUserDialog"
|
||||||
:on-cancel="deleteUserDialog.bind(this, false)"
|
:on-cancel="deleteUserDialog.bind(this, false)"
|
||||||
>
|
>
|
||||||
<template v-slot:header>
|
<template #header>
|
||||||
{{ $t('user_card.admin_menu.delete_user') }}
|
{{ $t('user_card.admin_menu.delete_user') }}
|
||||||
</template>
|
</template>
|
||||||
<p>{{ $t('user_card.admin_menu.delete_user_confirmation') }}</p>
|
<p>{{ $t('user_card.admin_menu.delete_user_confirmation') }}</p>
|
||||||
<template v-slot:footer>
|
<template #footer>
|
||||||
<button
|
<button
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
@click="deleteUserDialog(false)"
|
@click="deleteUserDialog(false)"
|
||||||
|
|
|
@ -9,10 +9,10 @@ import { get } from 'lodash'
|
||||||
*/
|
*/
|
||||||
const toInstanceReasonObject = (instances, info, key) => {
|
const toInstanceReasonObject = (instances, info, key) => {
|
||||||
return instances.map(instance => {
|
return instances.map(instance => {
|
||||||
if (info[key] && info[key][instance] && info[key][instance]['reason']) {
|
if (info[key] && info[key][instance] && info[key][instance].reason) {
|
||||||
return { instance: instance, reason: info[key][instance]['reason'] }
|
return { instance, reason: info[key][instance].reason }
|
||||||
}
|
}
|
||||||
return { instance: instance, reason: '' }
|
return { instance, reason: '' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./nav_panel.js" ></script>
|
<script src="./nav_panel.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
@ -113,7 +113,9 @@
|
||||||
border-color: $fallback--border;
|
border-color: $fallback--border;
|
||||||
border-color: var(--border, $fallback--border);
|
border-color: var(--border, $fallback--border);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> li {
|
||||||
&:first-child .menu-item {
|
&:first-child .menu-item {
|
||||||
border-top-right-radius: $fallback--panelRadius;
|
border-top-right-radius: $fallback--panelRadius;
|
||||||
border-top-right-radius: var(--panelRadius, $fallback--panelRadius);
|
border-top-right-radius: var(--panelRadius, $fallback--panelRadius);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import UserCard from '../user_card/user_card.vue'
|
||||||
import Timeago from '../timeago/timeago.vue'
|
import Timeago from '../timeago/timeago.vue'
|
||||||
import Report from '../report/report.vue'
|
import Report from '../report/report.vue'
|
||||||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||||
|
import UserPopover from '../user_popover/user_popover.vue'
|
||||||
import { isStatusNotification } from '../../services/notification_utils/notification_utils.js'
|
import { isStatusNotification } from '../../services/notification_utils/notification_utils.js'
|
||||||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
|
@ -40,7 +41,7 @@ const Notification = {
|
||||||
unmuted: false
|
unmuted: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: [ 'notification' ],
|
props: ['notification'],
|
||||||
components: {
|
components: {
|
||||||
StatusContent,
|
StatusContent,
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
|
@ -48,7 +49,8 @@ const Notification = {
|
||||||
Timeago,
|
Timeago,
|
||||||
Status,
|
Status,
|
||||||
Report,
|
Report,
|
||||||
RichContent
|
RichContent,
|
||||||
|
UserPopover
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleUserExpanded () {
|
toggleUserExpanded () {
|
||||||
|
|
|
@ -34,21 +34,22 @@
|
||||||
<a
|
<a
|
||||||
class="avatar-container"
|
class="avatar-container"
|
||||||
:href="$router.resolve(userProfileLink).href"
|
:href="$router.resolve(userProfileLink).href"
|
||||||
@click.stop.prevent.capture="toggleUserExpanded"
|
@click.prevent
|
||||||
>
|
>
|
||||||
<UserAvatar
|
<UserPopover
|
||||||
:compact="true"
|
:user-id="notification.from_profile.id"
|
||||||
:better-shadow="betterShadow"
|
:overlay-centers="true"
|
||||||
:user="notification.from_profile"
|
>
|
||||||
/>
|
<UserAvatar
|
||||||
|
class="post-avatar"
|
||||||
|
:bot="botIndicator"
|
||||||
|
:compact="true"
|
||||||
|
:better-shadow="betterShadow"
|
||||||
|
:user="notification.from_profile"
|
||||||
|
/>
|
||||||
|
</UserPopover>
|
||||||
</a>
|
</a>
|
||||||
<div class="notification-right">
|
<div class="notification-right">
|
||||||
<UserCard
|
|
||||||
v-if="userExpanded"
|
|
||||||
:user-id="getUser(notification).id"
|
|
||||||
:rounded="true"
|
|
||||||
:bordered="true"
|
|
||||||
/>
|
|
||||||
<span class="notification-details">
|
<span class="notification-details">
|
||||||
<div class="name-and-action">
|
<div class="name-and-action">
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
:bound-to="{ x: 'container' }"
|
:bound-to="{ x: 'container' }"
|
||||||
>
|
>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<button
|
<button
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:trigger>
|
<template #trigger>
|
||||||
<button class="filter-trigger-button button-unstyled">
|
<button class="filter-trigger-button button-unstyled">
|
||||||
<FAIcon icon="filter" />
|
<FAIcon icon="filter" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { computed } from 'vue'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import Notification from '../notification/notification.vue'
|
import Notification from '../notification/notification.vue'
|
||||||
import NotificationFilters from './notification_filters.vue'
|
import NotificationFilters from './notification_filters.vue'
|
||||||
|
@ -40,6 +41,11 @@ const Notifications = {
|
||||||
seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT
|
seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
provide () {
|
||||||
|
return {
|
||||||
|
popoversZLayer: computed(() => this.popoversZLayer)
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
mainClass () {
|
mainClass () {
|
||||||
return this.minimalMode ? '' : 'panel panel-default'
|
return this.minimalMode ? '' : 'panel panel-default'
|
||||||
|
@ -77,6 +83,10 @@ const Notifications = {
|
||||||
}
|
}
|
||||||
return map[layoutType] || '#notifs-sidebar'
|
return map[layoutType] || '#notifs-sidebar'
|
||||||
},
|
},
|
||||||
|
popoversZLayer () {
|
||||||
|
const { layoutType } = this.$store.state.interface
|
||||||
|
return layoutType === 'mobile' ? 'navbar' : null
|
||||||
|
},
|
||||||
notificationsToDisplay () {
|
notificationsToDisplay () {
|
||||||
return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount)
|
return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount)
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<teleport :disabled="minimalMode || disableTeleport" :to="teleportTarget">
|
<teleport
|
||||||
|
:disabled="minimalMode || disableTeleport"
|
||||||
|
:to="teleportTarget"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
:class="{ minimal: minimalMode }"
|
:class="{ minimal: minimalMode }"
|
||||||
class="Notifications"
|
class="Notifications"
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
:key="unit"
|
:key="unit"
|
||||||
:value="unit"
|
:value="unit"
|
||||||
>
|
>
|
||||||
{{ $t(`time.${unit}_short`, ['']) }}
|
{{ $tc(`time.unit.${unit}_short`, expiryAmount, ['']) }}
|
||||||
</option>
|
</option>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -31,13 +31,35 @@ const Popover = {
|
||||||
|
|
||||||
// If true, subtract padding when calculating position for the popover,
|
// If true, subtract padding when calculating position for the popover,
|
||||||
// use it when popover offset looks to be different on top vs bottom.
|
// use it when popover offset looks to be different on top vs bottom.
|
||||||
removePadding: Boolean
|
removePadding: Boolean,
|
||||||
|
|
||||||
|
// self-explanatory (i hope)
|
||||||
|
disabled: Boolean,
|
||||||
|
|
||||||
|
// Instead of putting popover next to anchor, overlay popover's center on top of anchor's center
|
||||||
|
overlayCenters: Boolean,
|
||||||
|
|
||||||
|
// What selector (witin popover!) to use for determining center of popover
|
||||||
|
overlayCentersSelector: String,
|
||||||
|
|
||||||
|
// Lets hover popover stay when clicking inside of it
|
||||||
|
stayOnClick: Boolean
|
||||||
},
|
},
|
||||||
|
inject: ['popoversZLayer'], // override popover z layer
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
// lockReEntry is a flag that is set when mouse cursor is leaving the popover's content
|
||||||
|
// so that if mouse goes back into popover it won't be re-shown again to prevent annoyance
|
||||||
|
// with popovers refusing to be hidden when user wants to interact with something in below popover
|
||||||
|
lockReEntry: false,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
styles: { opacity: 0 },
|
styles: {},
|
||||||
oldSize: { width: 0, height: 0 }
|
oldSize: { width: 0, height: 0 },
|
||||||
|
scrollable: null,
|
||||||
|
// used to avoid blinking if hovered onto popover
|
||||||
|
graceTimeout: null,
|
||||||
|
parentPopover: null,
|
||||||
|
childrenShown: new Set()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -47,9 +69,7 @@ const Popover = {
|
||||||
},
|
},
|
||||||
updateStyles () {
|
updateStyles () {
|
||||||
if (this.hidden) {
|
if (this.hidden) {
|
||||||
this.styles = {
|
this.styles = {}
|
||||||
opacity: 0
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,14 +77,26 @@ const Popover = {
|
||||||
// its children are what are inside the slot. Expect only one v-slot:trigger.
|
// its children are what are inside the slot. Expect only one v-slot:trigger.
|
||||||
const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el
|
const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el
|
||||||
// SVGs don't have offsetWidth/Height, use fallback
|
// SVGs don't have offsetWidth/Height, use fallback
|
||||||
const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth
|
|
||||||
const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight
|
const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight
|
||||||
const screenBox = anchorEl.getBoundingClientRect()
|
const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth
|
||||||
// Screen position of the origin point for popover
|
const anchorScreenBox = anchorEl.getBoundingClientRect()
|
||||||
const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top }
|
|
||||||
|
const anchorStyle = getComputedStyle(anchorEl)
|
||||||
|
const topPadding = parseFloat(anchorStyle.paddingTop)
|
||||||
|
const bottomPadding = parseFloat(anchorStyle.paddingBottom)
|
||||||
|
|
||||||
|
// Screen position of the origin point for popover = center of the anchor
|
||||||
|
const origin = {
|
||||||
|
x: anchorScreenBox.left + anchorWidth * 0.5,
|
||||||
|
y: anchorScreenBox.top + anchorHeight * 0.5
|
||||||
|
}
|
||||||
const content = this.$refs.content
|
const content = this.$refs.content
|
||||||
|
const overlayCenter = this.overlayCenters
|
||||||
|
? this.$refs.content.querySelector(this.overlayCentersSelector)
|
||||||
|
: null
|
||||||
|
|
||||||
// Minor optimization, don't call a slow reflow call if we don't have to
|
// Minor optimization, don't call a slow reflow call if we don't have to
|
||||||
const parentBounds = this.boundTo &&
|
const parentScreenBox = this.boundTo &&
|
||||||
(this.boundTo.x === 'container' || this.boundTo.y === 'container') &&
|
(this.boundTo.x === 'container' || this.boundTo.y === 'container') &&
|
||||||
this.containerBoundingClientRect()
|
this.containerBoundingClientRect()
|
||||||
|
|
||||||
|
@ -72,82 +104,156 @@ const Popover = {
|
||||||
|
|
||||||
// What are the screen bounds for the popover? Viewport vs container
|
// What are the screen bounds for the popover? Viewport vs container
|
||||||
// when using viewport, using default margin values to dodge the navbar
|
// when using viewport, using default margin values to dodge the navbar
|
||||||
const xBounds = this.boundTo && this.boundTo.x === 'container' ? {
|
const xBounds = this.boundTo && this.boundTo.x === 'container'
|
||||||
min: parentBounds.left + (margin.left || 0),
|
? {
|
||||||
max: parentBounds.right - (margin.right || 0)
|
min: parentScreenBox.left + (margin.left || 0),
|
||||||
} : {
|
max: parentScreenBox.right - (margin.right || 0)
|
||||||
min: 0 + (margin.left || 10),
|
}
|
||||||
max: window.innerWidth - (margin.right || 10)
|
: {
|
||||||
}
|
min: 0 + (margin.left || 10),
|
||||||
|
max: window.innerWidth - (margin.right || 10)
|
||||||
|
}
|
||||||
|
|
||||||
const yBounds = this.boundTo && this.boundTo.y === 'container' ? {
|
const yBounds = this.boundTo && this.boundTo.y === 'container'
|
||||||
min: parentBounds.top + (margin.top || 0),
|
? {
|
||||||
max: parentBounds.bottom - (margin.bottom || 0)
|
min: parentScreenBox.top + (margin.top || 0),
|
||||||
} : {
|
max: parentScreenBox.bottom - (margin.bottom || 0)
|
||||||
min: 0 + (margin.top || 50),
|
}
|
||||||
max: window.innerHeight - (margin.bottom || 5)
|
: {
|
||||||
}
|
min: 0 + (margin.top || 50),
|
||||||
|
max: window.innerHeight - (margin.bottom || 5)
|
||||||
|
}
|
||||||
|
|
||||||
let horizOffset = 0
|
let horizOffset = 0
|
||||||
|
let vertOffset = 0
|
||||||
|
|
||||||
|
if (overlayCenter) {
|
||||||
|
const box = content.getBoundingClientRect()
|
||||||
|
const overlayCenterScreenBox = overlayCenter.getBoundingClientRect()
|
||||||
|
const leftInnerOffset = overlayCenterScreenBox.left - box.left
|
||||||
|
const topInnerOffset = overlayCenterScreenBox.top - box.top
|
||||||
|
horizOffset = -leftInnerOffset - overlayCenter.offsetWidth * 0.5
|
||||||
|
vertOffset = -topInnerOffset - overlayCenter.offsetHeight * 0.5
|
||||||
|
} else {
|
||||||
|
horizOffset = content.offsetWidth * -0.5
|
||||||
|
vertOffset = content.offsetHeight * -0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
const leftBorder = origin.x + horizOffset
|
||||||
|
const rightBorder = leftBorder + content.offsetWidth
|
||||||
|
const topBorder = origin.y + vertOffset
|
||||||
|
const bottomBorder = topBorder + content.offsetHeight
|
||||||
|
|
||||||
// If overflowing from left, move it so that it doesn't
|
// If overflowing from left, move it so that it doesn't
|
||||||
if ((origin.x - content.offsetWidth * 0.5) < xBounds.min) {
|
if (leftBorder < xBounds.min) {
|
||||||
horizOffset += -(origin.x - content.offsetWidth * 0.5) + xBounds.min
|
horizOffset += xBounds.min - leftBorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// If overflowing from right, move it so that it doesn't
|
// If overflowing from right, move it so that it doesn't
|
||||||
if ((origin.x + horizOffset + content.offsetWidth * 0.5) > xBounds.max) {
|
if (rightBorder > xBounds.max) {
|
||||||
horizOffset -= (origin.x + horizOffset + content.offsetWidth * 0.5) - xBounds.max
|
horizOffset -= rightBorder - xBounds.max
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to whatever user wished with placement prop
|
// If overflowing from top, move it so that it doesn't
|
||||||
let usingTop = this.placement !== 'bottom'
|
if (topBorder < yBounds.min) {
|
||||||
|
vertOffset += yBounds.min - topBorder
|
||||||
// Handle special cases, first force to displaying on top if there's not space on bottom,
|
|
||||||
// regardless of what placement value was. Then check if there's not space on top, and
|
|
||||||
// force to bottom, again regardless of what placement value was.
|
|
||||||
if (origin.y + content.offsetHeight > yBounds.max) usingTop = true
|
|
||||||
if (origin.y - content.offsetHeight < yBounds.min) usingTop = false
|
|
||||||
|
|
||||||
let vPadding = 0
|
|
||||||
if (this.removePadding && usingTop) {
|
|
||||||
const anchorStyle = getComputedStyle(anchorEl)
|
|
||||||
vPadding = parseFloat(anchorStyle.paddingTop) + parseFloat(anchorStyle.paddingBottom)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const yOffset = (this.offset && this.offset.y) || 0
|
// If overflowing from bottom, move it so that it doesn't
|
||||||
const translateY = usingTop
|
if (bottomBorder > yBounds.max) {
|
||||||
? -anchorHeight + vPadding - yOffset - content.offsetHeight
|
vertOffset -= bottomBorder - yBounds.max
|
||||||
: yOffset
|
}
|
||||||
|
|
||||||
const xOffset = (this.offset && this.offset.x) || 0
|
let translateX = 0
|
||||||
const translateX = anchorWidth * 0.5 - content.offsetWidth * 0.5 + horizOffset + xOffset
|
let translateY = 0
|
||||||
|
|
||||||
|
if (overlayCenter) {
|
||||||
|
translateX = origin.x + horizOffset
|
||||||
|
translateY = origin.y + vertOffset
|
||||||
|
} else {
|
||||||
|
// Default to whatever user wished with placement prop
|
||||||
|
let usingTop = this.placement !== 'bottom'
|
||||||
|
|
||||||
|
// Handle special cases, first force to displaying on top if there's not space on bottom,
|
||||||
|
// regardless of what placement value was. Then check if there's not space on top, and
|
||||||
|
// force to bottom, again regardless of what placement value was.
|
||||||
|
const topBoundary = origin.y - anchorHeight * 0.5 + (this.removePadding ? topPadding : 0)
|
||||||
|
const bottomBoundary = origin.y + anchorHeight * 0.5 - (this.removePadding ? bottomPadding : 0)
|
||||||
|
if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true
|
||||||
|
if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false
|
||||||
|
|
||||||
|
const yOffset = (this.offset && this.offset.y) || 0
|
||||||
|
translateY = usingTop
|
||||||
|
? topBoundary - yOffset - content.offsetHeight
|
||||||
|
: bottomBoundary + yOffset
|
||||||
|
|
||||||
|
const xOffset = (this.offset && this.offset.x) || 0
|
||||||
|
translateX = origin.x + horizOffset + xOffset
|
||||||
|
}
|
||||||
|
|
||||||
// Note, separate translateX and translateY avoids blurry text on chromium,
|
|
||||||
// single translate or translate3d resulted in blurry text.
|
|
||||||
this.styles = {
|
this.styles = {
|
||||||
opacity: 1,
|
left: `${Math.round(translateX)}px`,
|
||||||
transform: `translateX(${Math.round(translateX)}px) translateY(${Math.round(translateY)}px)`
|
top: `${Math.round(translateY)}px`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.popoversZLayer) {
|
||||||
|
this.styles['--ZI_popover_override'] = `var(--ZI_${this.popoversZLayer}_popovers)`
|
||||||
|
}
|
||||||
|
if (parentScreenBox) {
|
||||||
|
this.styles.maxWidth = `${Math.round(parentScreenBox.width)}px`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showPopover () {
|
showPopover () {
|
||||||
|
if (this.disabled) return
|
||||||
const wasHidden = this.hidden
|
const wasHidden = this.hidden
|
||||||
this.hidden = false
|
this.hidden = false
|
||||||
|
this.parentPopover && this.parentPopover.onChildPopoverState(this, true)
|
||||||
|
if (this.trigger === 'click' || this.stayOnClick) {
|
||||||
|
document.addEventListener('click', this.onClickOutside)
|
||||||
|
}
|
||||||
|
this.scrollable.addEventListener('scroll', this.onScroll)
|
||||||
|
this.scrollable.addEventListener('resize', this.onResize)
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (wasHidden) this.$emit('show')
|
if (wasHidden) this.$emit('show')
|
||||||
this.updateStyles()
|
this.updateStyles()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
hidePopover () {
|
hidePopover () {
|
||||||
|
if (this.disabled) return
|
||||||
if (!this.hidden) this.$emit('close')
|
if (!this.hidden) this.$emit('close')
|
||||||
this.hidden = true
|
this.hidden = true
|
||||||
this.styles = { opacity: 0 }
|
this.parentPopover && this.parentPopover.onChildPopoverState(this, false)
|
||||||
|
if (this.trigger === 'click') {
|
||||||
|
document.removeEventListener('click', this.onClickOutside)
|
||||||
|
}
|
||||||
|
this.scrollable.removeEventListener('scroll', this.onScroll)
|
||||||
|
this.scrollable.removeEventListener('resize', this.onResize)
|
||||||
},
|
},
|
||||||
onMouseenter (e) {
|
onMouseenter (e) {
|
||||||
if (this.trigger === 'hover') this.showPopover()
|
if (this.trigger === 'hover') {
|
||||||
|
this.lockReEntry = false
|
||||||
|
clearTimeout(this.graceTimeout)
|
||||||
|
this.graceTimeout = null
|
||||||
|
this.showPopover()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onMouseleave (e) {
|
onMouseleave (e) {
|
||||||
if (this.trigger === 'hover') this.hidePopover()
|
if (this.trigger === 'hover' && this.childrenShown.size === 0) {
|
||||||
|
this.graceTimeout = setTimeout(() => this.hidePopover(), 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMouseenterContent (e) {
|
||||||
|
if (this.trigger === 'hover' && !this.lockReEntry) {
|
||||||
|
this.lockReEntry = true
|
||||||
|
clearTimeout(this.graceTimeout)
|
||||||
|
this.graceTimeout = null
|
||||||
|
this.showPopover()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMouseleaveContent (e) {
|
||||||
|
if (this.trigger === 'hover' && this.childrenShown.size === 0) {
|
||||||
|
this.graceTimeout = setTimeout(() => this.hidePopover(), 1)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onClick (e) {
|
onClick (e) {
|
||||||
if (this.trigger === 'click') {
|
if (this.trigger === 'click') {
|
||||||
|
@ -160,8 +266,24 @@ const Popover = {
|
||||||
},
|
},
|
||||||
onClickOutside (e) {
|
onClickOutside (e) {
|
||||||
if (this.hidden) return
|
if (this.hidden) return
|
||||||
|
if (this.$refs.content && this.$refs.content.contains(e.target)) return
|
||||||
if (this.$el.contains(e.target)) return
|
if (this.$el.contains(e.target)) return
|
||||||
|
if (this.childrenShown.size > 0) return
|
||||||
this.hidePopover()
|
this.hidePopover()
|
||||||
|
if (this.parentPopover) this.parentPopover.onClickOutside(e)
|
||||||
|
},
|
||||||
|
onScroll (e) {
|
||||||
|
this.updateStyles()
|
||||||
|
},
|
||||||
|
onResize (e) {
|
||||||
|
this.updateStyles()
|
||||||
|
},
|
||||||
|
onChildPopoverState (childRef, state) {
|
||||||
|
if (state) {
|
||||||
|
this.childrenShown.add(childRef)
|
||||||
|
} else {
|
||||||
|
this.childrenShown.delete(childRef)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updated () {
|
updated () {
|
||||||
|
@ -175,11 +297,18 @@ const Popover = {
|
||||||
this.oldSize = { width: content.offsetWidth, height: content.offsetHeight }
|
this.oldSize = { width: content.offsetWidth, height: content.offsetHeight }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
mounted () {
|
||||||
document.addEventListener('click', this.onClickOutside)
|
let scrollable = this.$refs.trigger.closest('.column.-scrollable') ||
|
||||||
|
this.$refs.trigger.closest('.mobile-notifications')
|
||||||
|
if (!scrollable) scrollable = window
|
||||||
|
this.scrollable = scrollable
|
||||||
|
let parent = this.$parent
|
||||||
|
while (parent && parent.$.type.name !== 'Popover') {
|
||||||
|
parent = parent.$parent
|
||||||
|
}
|
||||||
|
this.parentPopover = parent
|
||||||
},
|
},
|
||||||
unmounted () {
|
beforeUnmount () {
|
||||||
document.removeEventListener('click', this.onClickOutside)
|
|
||||||
this.hidePopover()
|
this.hidePopover()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<span
|
||||||
@mouseenter="onMouseenter"
|
@mouseenter="onMouseenter"
|
||||||
@mouseleave="onMouseleave"
|
@mouseleave="onMouseleave"
|
||||||
>
|
>
|
||||||
|
@ -11,20 +11,27 @@
|
||||||
>
|
>
|
||||||
<slot name="trigger" />
|
<slot name="trigger" />
|
||||||
</button>
|
</button>
|
||||||
<div
|
<teleport to="#popovers">
|
||||||
v-if="!hidden"
|
<transition name="fade">
|
||||||
ref="content"
|
<div
|
||||||
:style="styles"
|
v-if="!hidden"
|
||||||
class="popover"
|
ref="content"
|
||||||
:class="popoverClass || 'popover-default'"
|
:style="styles"
|
||||||
>
|
class="popover"
|
||||||
<slot
|
:class="popoverClass || 'popover-default'"
|
||||||
name="content"
|
@mouseenter="onMouseenterContent"
|
||||||
class="popover-inner"
|
@mouseleave="onMouseleaveContent"
|
||||||
:close="hidePopover"
|
@click="onClickContent"
|
||||||
/>
|
>
|
||||||
</div>
|
<slot
|
||||||
</div>
|
name="content"
|
||||||
|
class="popover-inner"
|
||||||
|
:close="hidePopover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</teleport>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./popover.js" />
|
<script src="./popover.js" />
|
||||||
|
@ -37,14 +44,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover {
|
.popover {
|
||||||
z-index: 500;
|
z-index: var(--ZI_popover_override, var(--ZI_popovers));
|
||||||
position: absolute;
|
position: fixed;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
max-width: calc(100vw - 20px);
|
||||||
|
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
|
||||||
|
box-shadow: var(--popupShadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover-default {
|
.popover-default {
|
||||||
transition: opacity 0.3s;
|
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -80,7 +88,7 @@
|
||||||
text-align: left;
|
text-align: left;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
z-index: 200;
|
z-index: var(--ZI_popover_override, var(--ZI_popovers));
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
.dropdown-divider {
|
.dropdown-divider {
|
||||||
|
|
|
@ -41,7 +41,7 @@ const buildMentionsString = ({ user, attentions = [] }, currentUser) => {
|
||||||
allAttentions = uniqBy(allAttentions, 'id')
|
allAttentions = uniqBy(allAttentions, 'id')
|
||||||
allAttentions = reject(allAttentions, { id: currentUser.id })
|
allAttentions = reject(allAttentions, { id: currentUser.id })
|
||||||
|
|
||||||
let mentions = map(allAttentions, (attention) => {
|
const mentions = map(allAttentions, (attention) => {
|
||||||
return `@${attention.screen_name}`
|
return `@${attention.screen_name}`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ const PostStatusForm = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'newStatus': {
|
newStatus: {
|
||||||
deep: true,
|
deep: true,
|
||||||
handler () {
|
handler () {
|
||||||
this.statusChanged()
|
this.statusChanged()
|
||||||
|
@ -273,7 +273,7 @@ const PostStatusForm = {
|
||||||
this.$refs.textarea.focus()
|
this.$refs.textarea.focus()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
let el = this.$el.querySelector('textarea')
|
const el = this.$el.querySelector('textarea')
|
||||||
el.style.height = 'auto'
|
el.style.height = 'auto'
|
||||||
el.style.height = undefined
|
el.style.height = undefined
|
||||||
this.error = null
|
this.error = null
|
||||||
|
@ -392,7 +392,7 @@ const PostStatusForm = {
|
||||||
this.$emit('resize', { delayed: true })
|
this.$emit('resize', { delayed: true })
|
||||||
},
|
},
|
||||||
removeMediaFile (fileInfo) {
|
removeMediaFile (fileInfo) {
|
||||||
let index = this.newStatus.files.indexOf(fileInfo)
|
const index = this.newStatus.files.indexOf(fileInfo)
|
||||||
this.newStatus.files.splice(index, 1)
|
this.newStatus.files.splice(index, 1)
|
||||||
this.$emit('resize')
|
this.$emit('resize')
|
||||||
},
|
},
|
||||||
|
@ -462,7 +462,7 @@ const PostStatusForm = {
|
||||||
},
|
},
|
||||||
onEmojiInputInput (e) {
|
onEmojiInputInput (e) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.resize(this.$refs['textarea'])
|
this.resize(this.$refs.textarea)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
resize (e) {
|
resize (e) {
|
||||||
|
@ -477,8 +477,8 @@ const PostStatusForm = {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const formRef = this.$refs['form']
|
const formRef = this.$refs.form
|
||||||
const bottomRef = this.$refs['bottom']
|
const bottomRef = this.$refs.bottom
|
||||||
/* Scroller is either `window` (replies in TL), sidebar (main post form,
|
/* Scroller is either `window` (replies in TL), sidebar (main post form,
|
||||||
* replies in notifs) or mobile post form. Note that getting and setting
|
* replies in notifs) or mobile post form. Note that getting and setting
|
||||||
* scroll is different for `Window` and `Element`s
|
* scroll is different for `Window` and `Element`s
|
||||||
|
@ -564,7 +564,7 @@ const PostStatusForm = {
|
||||||
this.$refs['emoji-input'].resize()
|
this.$refs['emoji-input'].resize()
|
||||||
},
|
},
|
||||||
showEmojiPicker () {
|
showEmojiPicker () {
|
||||||
this.$refs['textarea'].focus()
|
this.$refs.textarea.focus()
|
||||||
this.$refs['emoji-input'].triggerShowPicker()
|
this.$refs['emoji-input'].triggerShowPicker()
|
||||||
},
|
},
|
||||||
clearError () {
|
clearError () {
|
||||||
|
|
|
@ -45,7 +45,7 @@ const ReactButton = {
|
||||||
emojis () {
|
emojis () {
|
||||||
if (this.filterWord !== '') {
|
if (this.filterWord !== '') {
|
||||||
const filterWordLowercase = trim(this.filterWord.toLowerCase())
|
const filterWordLowercase = trim(this.filterWord.toLowerCase())
|
||||||
let orderedEmojiList = []
|
const orderedEmojiList = []
|
||||||
for (const emoji of this.$store.state.instance.emoji) {
|
for (const emoji of this.$store.state.instance.emoji) {
|
||||||
if (emoji.replacement === this.filterWord) return [emoji]
|
if (emoji.replacement === this.filterWord) return [emoji]
|
||||||
|
|
||||||
|
|
|
@ -6,15 +6,16 @@
|
||||||
:offset="{ y: 5 }"
|
:offset="{ y: 5 }"
|
||||||
:bound-to="{ x: 'container' }"
|
:bound-to="{ x: 'container' }"
|
||||||
remove-padding
|
remove-padding
|
||||||
|
popover-class="ReactButton popover-default"
|
||||||
@show="focusInput"
|
@show="focusInput"
|
||||||
>
|
>
|
||||||
<template v-slot:content="{close}">
|
<template #content="{close}">
|
||||||
<div class="reaction-picker-filter">
|
<div class="reaction-picker-filter">
|
||||||
<input
|
<input
|
||||||
v-model="filterWord"
|
v-model="filterWord"
|
||||||
@input="$event.target.composing = false"
|
|
||||||
size="1"
|
size="1"
|
||||||
:placeholder="$t('emoji.search_emoji')"
|
:placeholder="$t('emoji.search_emoji')"
|
||||||
|
@input="$event.target.composing = false"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="reaction-picker">
|
<div class="reaction-picker">
|
||||||
|
@ -40,8 +41,8 @@
|
||||||
<div class="reaction-bottom-fader" />
|
<div class="reaction-bottom-fader" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:trigger>
|
<template #trigger>
|
||||||
<button
|
<span
|
||||||
class="button-unstyled popover-trigger"
|
class="button-unstyled popover-trigger"
|
||||||
:title="$t('tool_tip.add_reaction')"
|
:title="$t('tool_tip.add_reaction')"
|
||||||
>
|
>
|
||||||
|
@ -49,12 +50,12 @@
|
||||||
class="fa-scale-110 fa-old-padding"
|
class="fa-scale-110 fa-old-padding"
|
||||||
:icon="['far', 'smile-beam']"
|
:icon="['far', 'smile-beam']"
|
||||||
/>
|
/>
|
||||||
</button>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</Popover>
|
</Popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./react_button.js" ></script>
|
<script src="./react_button.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
v-model.trim="v$.user.username.$model"
|
v-model.trim="v$.user.username.$model"
|
||||||
:disabled="isPending"
|
:disabled="isPending"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:aria-required="true"
|
||||||
:placeholder="$t('registration.username_placeholder')"
|
:placeholder="$t('registration.username_placeholder')"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,6 +51,7 @@
|
||||||
v-model.trim="v$.user.fullname.$model"
|
v-model.trim="v$.user.fullname.$model"
|
||||||
:disabled="isPending"
|
:disabled="isPending"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:aria-required="true"
|
||||||
:placeholder="$t('registration.fullname_placeholder')"
|
:placeholder="$t('registration.fullname_placeholder')"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,13 +73,14 @@
|
||||||
<label
|
<label
|
||||||
class="form--label"
|
class="form--label"
|
||||||
for="email"
|
for="email"
|
||||||
>{{ $t('registration.email') }}</label>
|
>{{ accountActivationRequired ? $t('registration.email') : $t('registration.email_optional') }}</label>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
v-model="v$.user.email.$model"
|
v-model="v$.user.email.$model"
|
||||||
:disabled="isPending"
|
:disabled="isPending"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
type="email"
|
type="email"
|
||||||
|
:aria-required="accountActivationRequired"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -95,7 +98,7 @@
|
||||||
<label
|
<label
|
||||||
class="form--label"
|
class="form--label"
|
||||||
for="bio"
|
for="bio"
|
||||||
>{{ $t('registration.bio') }} ({{ $t('general.optional') }})</label>
|
>{{ $t('registration.bio_optional') }}</label>
|
||||||
<textarea
|
<textarea
|
||||||
id="bio"
|
id="bio"
|
||||||
v-model="user.bio"
|
v-model="user.bio"
|
||||||
|
@ -119,6 +122,7 @@
|
||||||
:disabled="isPending"
|
:disabled="isPending"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
type="password"
|
type="password"
|
||||||
|
:aria-required="true"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -146,6 +150,7 @@
|
||||||
:disabled="isPending"
|
:disabled="isPending"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
type="password"
|
type="password"
|
||||||
|
:aria-required="true"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
props: [ 'user' ],
|
props: ['user'],
|
||||||
computed: {
|
computed: {
|
||||||
subscribeUrl () {
|
subscribeUrl () {
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./retweet_button.js" ></script>
|
<script src="./retweet_button.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
|
@ -16,7 +16,7 @@ const SearchBar = {
|
||||||
error: false
|
error: false
|
||||||
}),
|
}),
|
||||||
watch: {
|
watch: {
|
||||||
'$route': function (route) {
|
$route: function (route) {
|
||||||
if (route.name === 'search') {
|
if (route.name === 'search') {
|
||||||
this.searchTerm = route.query.query
|
this.searchTerm = route.query.query
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
:items="items"
|
:items="items"
|
||||||
:get-key="getKey"
|
:get-key="getKey"
|
||||||
>
|
>
|
||||||
<template v-slot:item="{item}">
|
<template #item="{item}">
|
||||||
<div
|
<div
|
||||||
class="selectable-list-item-inner"
|
class="selectable-list-item-inner"
|
||||||
:class="{ 'selectable-list-item-selected-inner': isSelected(item) }"
|
:class="{ 'selectable-list-item-selected-inner': isSelected(item) }"
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:empty>
|
<template #empty>
|
||||||
<slot name="empty" />
|
<slot name="empty" />
|
||||||
</template>
|
</template>
|
||||||
</List>
|
</List>
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
<Popover
|
<Popover
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
>
|
>
|
||||||
<template v-slot:trigger>
|
<template #trigger>
|
||||||
|
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="wrench"
|
icon="wrench"
|
||||||
:aria-label="$t('settings.setting_changed')"
|
:aria-label="$t('settings.setting_changed')"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<div class="modified-tooltip">
|
<div class="modified-tooltip">
|
||||||
{{ $t('settings.setting_changed') }}
|
{{ $t('settings.setting_changed') }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,11 +41,11 @@ export default {
|
||||||
.ModifiedIndicator {
|
.ModifiedIndicator {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.modified-tooltip {
|
.modified-tooltip {
|
||||||
margin: 0.5em 1em;
|
margin: 0.5em 1em;
|
||||||
min-width: 10em;
|
min-width: 10em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
<Popover
|
<Popover
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
>
|
>
|
||||||
<template v-slot:trigger>
|
<template #trigger>
|
||||||
|
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="server"
|
icon="server"
|
||||||
:aria-label="$t('settings.setting_server_side')"
|
:aria-label="$t('settings.setting_server_side')"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content>
|
<template #content>
|
||||||
<div class="serverside-tooltip">
|
<div class="serverside-tooltip">
|
||||||
{{ $t('settings.setting_server_side') }}
|
{{ $t('settings.setting_server_side') }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,11 +41,11 @@ export default {
|
||||||
.ServerSideIndicator {
|
.ServerSideIndicator {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.serverside-tooltip {
|
.serverside-tooltip {
|
||||||
margin: 0.5em 1em;
|
margin: 0.5em 1em;
|
||||||
min-width: 10em;
|
min-width: 10em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
:bound-to="{ x: 'container' }"
|
:bound-to="{ x: 'container' }"
|
||||||
remove-padding
|
remove-padding
|
||||||
>
|
>
|
||||||
<template v-slot:trigger>
|
<template #trigger>
|
||||||
<button
|
<button
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
:title="$t('general.close')"
|
:title="$t('general.close')"
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:content="{close}">
|
<template #content="{close}">
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<button
|
<button
|
||||||
class="button-default dropdown-item dropdown-item-icon"
|
class="button-default dropdown-item dropdown-item-icon"
|
||||||
|
|
|
@ -74,6 +74,22 @@
|
||||||
{{ $t('settings.show_scrollbars') }}
|
{{ $t('settings.show_scrollbars') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting
|
||||||
|
path="userPopoverZoom"
|
||||||
|
expert="1"
|
||||||
|
>
|
||||||
|
{{ $t('settings.user_popover_avatar_zoom') }}
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting
|
||||||
|
path="userPopoverOverlay"
|
||||||
|
expert="1"
|
||||||
|
>
|
||||||
|
{{ $t('settings.user_popover_avatar_overlay') }}
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<ChoiceSetting
|
<ChoiceSetting
|
||||||
v-if="user"
|
v-if="user"
|
||||||
|
@ -261,18 +277,14 @@
|
||||||
{{ $t('settings.mention_link_display') }}
|
{{ $t('settings.mention_link_display') }}
|
||||||
</ChoiceSetting>
|
</ChoiceSetting>
|
||||||
</li>
|
</li>
|
||||||
<ul
|
<li>
|
||||||
class="setting-list suboptions"
|
<BooleanSetting
|
||||||
>
|
path="mentionLinkShowTooltip"
|
||||||
<li v-if="mentionLinkDisplay === 'short'">
|
expert="1"
|
||||||
<BooleanSetting
|
>
|
||||||
path="mentionLinkShowTooltip"
|
{{ $t('settings.mention_link_use_tooltip') }}
|
||||||
expert="1"
|
</BooleanSetting>
|
||||||
>
|
</li>
|
||||||
{{ $t('settings.mention_link_show_tooltip') }}
|
|
||||||
</BooleanSetting>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
path="useAtIcon"
|
path="useAtIcon"
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
:query="queryUserIds"
|
:query="queryUserIds"
|
||||||
:placeholder="$t('settings.search_user_to_block')"
|
:placeholder="$t('settings.search_user_to_block')"
|
||||||
>
|
>
|
||||||
<template v-slot="row">
|
<template #default="row">
|
||||||
<BlockCard
|
<BlockCard
|
||||||
:user-id="row.item"
|
:user-id="row.item"
|
||||||
/>
|
/>
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
:refresh="true"
|
:refresh="true"
|
||||||
:get-key="i => i"
|
:get-key="i => i"
|
||||||
>
|
>
|
||||||
<template v-slot:header="{selected}">
|
<template #header="{selected}">
|
||||||
<div class="bulk-actions">
|
<div class="bulk-actions">
|
||||||
<ProgressButton
|
<ProgressButton
|
||||||
v-if="selected.length > 0"
|
v-if="selected.length > 0"
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
:click="() => blockUsers(selected)"
|
:click="() => blockUsers(selected)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.block') }}
|
{{ $t('user_card.block') }}
|
||||||
<template v-slot:progress>
|
<template #progress>
|
||||||
{{ $t('user_card.block_progress') }}
|
{{ $t('user_card.block_progress') }}
|
||||||
</template>
|
</template>
|
||||||
</ProgressButton>
|
</ProgressButton>
|
||||||
|
@ -39,16 +39,16 @@
|
||||||
:click="() => unblockUsers(selected)"
|
:click="() => unblockUsers(selected)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.unblock') }}
|
{{ $t('user_card.unblock') }}
|
||||||
<template v-slot:progress>
|
<template #progress>
|
||||||
{{ $t('user_card.unblock_progress') }}
|
{{ $t('user_card.unblock_progress') }}
|
||||||
</template>
|
</template>
|
||||||
</ProgressButton>
|
</ProgressButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:item="{item}">
|
<template #item="{item}">
|
||||||
<BlockCard :user-id="item" />
|
<BlockCard :user-id="item" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:empty>
|
<template #empty>
|
||||||
{{ $t('settings.no_blocks') }}
|
{{ $t('settings.no_blocks') }}
|
||||||
</template>
|
</template>
|
||||||
</BlockList>
|
</BlockList>
|
||||||
|
@ -63,7 +63,7 @@
|
||||||
:query="queryUserIds"
|
:query="queryUserIds"
|
||||||
:placeholder="$t('settings.search_user_to_mute')"
|
:placeholder="$t('settings.search_user_to_mute')"
|
||||||
>
|
>
|
||||||
<template v-slot="row">
|
<template #default="row">
|
||||||
<MuteCard
|
<MuteCard
|
||||||
:user-id="row.item"
|
:user-id="row.item"
|
||||||
/>
|
/>
|
||||||
|
@ -74,7 +74,7 @@
|
||||||
:refresh="true"
|
:refresh="true"
|
||||||
:get-key="i => i"
|
:get-key="i => i"
|
||||||
>
|
>
|
||||||
<template v-slot:header="{selected}">
|
<template #header="{selected}">
|
||||||
<div class="bulk-actions">
|
<div class="bulk-actions">
|
||||||
<ProgressButton
|
<ProgressButton
|
||||||
v-if="selected.length > 0"
|
v-if="selected.length > 0"
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
:click="() => muteUsers(selected)"
|
:click="() => muteUsers(selected)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.mute') }}
|
{{ $t('user_card.mute') }}
|
||||||
<template v-slot:progress>
|
<template #progress>
|
||||||
{{ $t('user_card.mute_progress') }}
|
{{ $t('user_card.mute_progress') }}
|
||||||
</template>
|
</template>
|
||||||
</ProgressButton>
|
</ProgressButton>
|
||||||
|
@ -92,16 +92,16 @@
|
||||||
:click="() => unmuteUsers(selected)"
|
:click="() => unmuteUsers(selected)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.unmute') }}
|
{{ $t('user_card.unmute') }}
|
||||||
<template v-slot:progress>
|
<template #progress>
|
||||||
{{ $t('user_card.unmute_progress') }}
|
{{ $t('user_card.unmute_progress') }}
|
||||||
</template>
|
</template>
|
||||||
</ProgressButton>
|
</ProgressButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:item="{item}">
|
<template #item="{item}">
|
||||||
<MuteCard :user-id="item" />
|
<MuteCard :user-id="item" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:empty>
|
<template #empty>
|
||||||
{{ $t('settings.no_mutes') }}
|
{{ $t('settings.no_mutes') }}
|
||||||
</template>
|
</template>
|
||||||
</MuteList>
|
</MuteList>
|
||||||
|
@ -114,7 +114,7 @@
|
||||||
:query="queryKnownDomains"
|
:query="queryKnownDomains"
|
||||||
:placeholder="$t('settings.type_domains_to_mute')"
|
:placeholder="$t('settings.type_domains_to_mute')"
|
||||||
>
|
>
|
||||||
<template v-slot="row">
|
<template #default="row">
|
||||||
<DomainMuteCard
|
<DomainMuteCard
|
||||||
:domain="row.item"
|
:domain="row.item"
|
||||||
/>
|
/>
|
||||||
|
@ -125,7 +125,7 @@
|
||||||
:refresh="true"
|
:refresh="true"
|
||||||
:get-key="i => i"
|
:get-key="i => i"
|
||||||
>
|
>
|
||||||
<template v-slot:header="{selected}">
|
<template #header="{selected}">
|
||||||
<div class="bulk-actions">
|
<div class="bulk-actions">
|
||||||
<ProgressButton
|
<ProgressButton
|
||||||
v-if="selected.length > 0"
|
v-if="selected.length > 0"
|
||||||
|
@ -133,16 +133,16 @@
|
||||||
:click="() => unmuteDomains(selected)"
|
:click="() => unmuteDomains(selected)"
|
||||||
>
|
>
|
||||||
{{ $t('domain_mute_card.unmute') }}
|
{{ $t('domain_mute_card.unmute') }}
|
||||||
<template v-slot:progress>
|
<template #progress>
|
||||||
{{ $t('domain_mute_card.unmute_progress') }}
|
{{ $t('domain_mute_card.unmute_progress') }}
|
||||||
</template>
|
</template>
|
||||||
</ProgressButton>
|
</ProgressButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:item="{item}">
|
<template #item="{item}">
|
||||||
<DomainMuteCard :domain="item" />
|
<DomainMuteCard :domain="item" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:empty>
|
<template #empty>
|
||||||
{{ $t('settings.no_mutes') }}
|
{{ $t('settings.no_mutes') }}
|
||||||
</template>
|
</template>
|
||||||
</DomainMuteList>
|
</DomainMuteList>
|
||||||
|
|
|
@ -71,10 +71,12 @@ const ProfileTab = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
emojiSuggestor () {
|
emojiSuggestor () {
|
||||||
return suggestor({ emoji: [
|
return suggestor({
|
||||||
...this.$store.state.instance.emoji,
|
emoji: [
|
||||||
...this.$store.state.instance.customEmoji
|
...this.$store.state.instance.emoji,
|
||||||
] })
|
...this.$store.state.instance.customEmoji
|
||||||
|
]
|
||||||
|
})
|
||||||
},
|
},
|
||||||
userSuggestor () {
|
userSuggestor () {
|
||||||
return suggestor({ store: this.$store })
|
return suggestor({ store: this.$store })
|
||||||
|
|
|
@ -117,8 +117,8 @@
|
||||||
<button
|
<button
|
||||||
v-if="!isDefaultAvatar && pickAvatarBtnVisible"
|
v-if="!isDefaultAvatar && pickAvatarBtnVisible"
|
||||||
:title="$t('settings.reset_avatar')"
|
:title="$t('settings.reset_avatar')"
|
||||||
@click="resetAvatar"
|
|
||||||
class="button-unstyled reset-button"
|
class="button-unstyled reset-button"
|
||||||
|
@click="resetAvatar"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="times"
|
icon="times"
|
||||||
|
|
|
@ -32,8 +32,8 @@ const Mfa = {
|
||||||
components: {
|
components: {
|
||||||
'recovery-codes': RecoveryCodes,
|
'recovery-codes': RecoveryCodes,
|
||||||
'totp-item': TOTP,
|
'totp-item': TOTP,
|
||||||
'qrcode': VueQrcode,
|
qrcode: VueQrcode,
|
||||||
'confirm': Confirm
|
confirm: Confirm
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
canSetupOTP () {
|
canSetupOTP () {
|
||||||
|
@ -139,7 +139,7 @@ const Mfa = {
|
||||||
|
|
||||||
// fetch settings from server
|
// fetch settings from server
|
||||||
async fetchSettings () {
|
async fetchSettings () {
|
||||||
let result = await this.backendInteractor.settingsMFA()
|
const result = await this.backendInteractor.settingsMFA()
|
||||||
if (result.error) return
|
if (result.error) return
|
||||||
this.settings = result.settings
|
this.settings = result.settings
|
||||||
this.settings.available = true
|
this.settings.available = true
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default {
|
||||||
inProgress: false // progress peform request to disable otp method
|
inProgress: false // progress peform request to disable otp method
|
||||||
}),
|
}),
|
||||||
components: {
|
components: {
|
||||||
'confirm': Confirm
|
confirm: Confirm
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isActivated () {
|
isActivated () {
|
||||||
|
|
|
@ -13,7 +13,7 @@ const SecurityTab = {
|
||||||
deletingAccount: false,
|
deletingAccount: false,
|
||||||
deleteAccountConfirmPasswordInput: '',
|
deleteAccountConfirmPasswordInput: '',
|
||||||
deleteAccountError: false,
|
deleteAccountError: false,
|
||||||
changePasswordInputs: [ '', '', '' ],
|
changePasswordInputs: ['', '', ''],
|
||||||
changedPassword: false,
|
changedPassword: false,
|
||||||
changePasswordError: false,
|
changePasswordError: false,
|
||||||
moveAccountTarget: '',
|
moveAccountTarget: '',
|
||||||
|
|
|
@ -29,7 +29,10 @@
|
||||||
{{ $t('settings.style.preview.content') }}
|
{{ $t('settings.style.preview.content') }}
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<i18n-t scope="global" keypath="settings.style.preview.text">
|
<i18n-t
|
||||||
|
scope="global"
|
||||||
|
keypath="settings.style.preview.text"
|
||||||
|
>
|
||||||
<code style="font-family: var(--postCodeFont)">
|
<code style="font-family: var(--postCodeFont)">
|
||||||
{{ $t('settings.style.preview.mono') }}
|
{{ $t('settings.style.preview.mono') }}
|
||||||
</code>
|
</code>
|
||||||
|
|
|
@ -95,11 +95,11 @@ export default {
|
||||||
|
|
||||||
...Object.keys(SLOT_INHERITANCE)
|
...Object.keys(SLOT_INHERITANCE)
|
||||||
.map(key => [key, ''])
|
.map(key => [key, ''])
|
||||||
.reduce((acc, [key, val]) => ({ ...acc, [ key + 'ColorLocal' ]: val }), {}),
|
.reduce((acc, [key, val]) => ({ ...acc, [key + 'ColorLocal']: val }), {}),
|
||||||
|
|
||||||
...Object.keys(OPACITIES)
|
...Object.keys(OPACITIES)
|
||||||
.map(key => [key, ''])
|
.map(key => [key, ''])
|
||||||
.reduce((acc, [key, val]) => ({ ...acc, [ key + 'OpacityLocal' ]: val }), {}),
|
.reduce((acc, [key, val]) => ({ ...acc, [key + 'OpacityLocal']: val }), {}),
|
||||||
|
|
||||||
shadowSelected: undefined,
|
shadowSelected: undefined,
|
||||||
shadowsLocal: {},
|
shadowsLocal: {},
|
||||||
|
@ -212,12 +212,12 @@ export default {
|
||||||
currentColors () {
|
currentColors () {
|
||||||
return Object.keys(SLOT_INHERITANCE)
|
return Object.keys(SLOT_INHERITANCE)
|
||||||
.map(key => [key, this[key + 'ColorLocal']])
|
.map(key => [key, this[key + 'ColorLocal']])
|
||||||
.reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {})
|
.reduce((acc, [key, val]) => ({ ...acc, [key]: val }), {})
|
||||||
},
|
},
|
||||||
currentOpacity () {
|
currentOpacity () {
|
||||||
return Object.keys(OPACITIES)
|
return Object.keys(OPACITIES)
|
||||||
.map(key => [key, this[key + 'OpacityLocal']])
|
.map(key => [key, this[key + 'OpacityLocal']])
|
||||||
.reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {})
|
.reduce((acc, [key, val]) => ({ ...acc, [key]: val }), {})
|
||||||
},
|
},
|
||||||
currentRadii () {
|
currentRadii () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -112,9 +112,11 @@ export default {
|
||||||
return hex2rgb(this.selected.color)
|
return hex2rgb(this.selected.color)
|
||||||
},
|
},
|
||||||
style () {
|
style () {
|
||||||
return this.ready ? {
|
return this.ready
|
||||||
boxShadow: getCssShadow(this.fallback)
|
? {
|
||||||
} : {}
|
boxShadow: getCssShadow(this.fallback)
|
||||||
|
}
|
||||||
|
: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./shadow_control.js" ></script>
|
<script src="./shadow_control.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
|
@ -11,7 +11,7 @@ library.add(
|
||||||
)
|
)
|
||||||
|
|
||||||
const shoutPanel = {
|
const shoutPanel = {
|
||||||
props: [ 'floating' ],
|
props: ['floating'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
currentMessage: '',
|
currentMessage: '',
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
.floating-shout {
|
.floating-shout {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0.5em;
|
bottom: 0.5em;
|
||||||
z-index: 1000;
|
z-index: var(--ZI_popovers);
|
||||||
max-width: 25em;
|
max-width: 25em;
|
||||||
|
|
||||||
&.-left {
|
&.-left {
|
||||||
|
|
|
@ -32,7 +32,7 @@ library.add(
|
||||||
)
|
)
|
||||||
|
|
||||||
const SideDrawer = {
|
const SideDrawer = {
|
||||||
props: [ 'logout' ],
|
props: ['logout'],
|
||||||
data: () => ({
|
data: () => ({
|
||||||
closed: true,
|
closed: true,
|
||||||
closeGesture: undefined
|
closeGesture: undefined
|
||||||
|
|
|
@ -204,14 +204,14 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./side_drawer.js" ></script>
|
<script src="./side_drawer.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
.side-drawer-container {
|
.side-drawer-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 1000;
|
z-index: var(--ZI_navbar);
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -17,8 +17,8 @@ const StaffPanel = {
|
||||||
const groupedStaffAccounts = groupBy(staffAccounts, 'role')
|
const groupedStaffAccounts = groupBy(staffAccounts, 'role')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ role: 'admin', users: groupedStaffAccounts['admin'] },
|
{ role: 'admin', users: groupedStaffAccounts.admin },
|
||||||
{ role: 'moderator', users: groupedStaffAccounts['moderator'] }
|
{ role: 'moderator', users: groupedStaffAccounts.moderator }
|
||||||
].filter(group => group.users)
|
].filter(group => group.users)
|
||||||
},
|
},
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./staff_panel.js" ></script>
|
<script src="./staff_panel.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,13 @@ import ReactButton from '../react_button/react_button.vue'
|
||||||
import RetweetButton from '../retweet_button/retweet_button.vue'
|
import RetweetButton from '../retweet_button/retweet_button.vue'
|
||||||
import ExtraButtons from '../extra_buttons/extra_buttons.vue'
|
import ExtraButtons from '../extra_buttons/extra_buttons.vue'
|
||||||
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||||
import UserCard from '../user_card/user_card.vue'
|
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
import AvatarList from '../avatar_list/avatar_list.vue'
|
import AvatarList from '../avatar_list/avatar_list.vue'
|
||||||
import Timeago from '../timeago/timeago.vue'
|
import Timeago from '../timeago/timeago.vue'
|
||||||
import StatusContent from '../status_content/status_content.vue'
|
import StatusContent from '../status_content/status_content.vue'
|
||||||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||||
import StatusPopover from '../status_popover/status_popover.vue'
|
import StatusPopover from '../status_popover/status_popover.vue'
|
||||||
|
import UserPopover from '../user_popover/user_popover.vue'
|
||||||
import UserListPopover from '../user_list_popover/user_list_popover.vue'
|
import UserListPopover from '../user_list_popover/user_list_popover.vue'
|
||||||
import EmojiReactions from '../emoji_reactions/emoji_reactions.vue'
|
import EmojiReactions from '../emoji_reactions/emoji_reactions.vue'
|
||||||
import MentionsLine from 'src/components/mentions_line/mentions_line.vue'
|
import MentionsLine from 'src/components/mentions_line/mentions_line.vue'
|
||||||
|
@ -105,7 +105,6 @@ const Status = {
|
||||||
RetweetButton,
|
RetweetButton,
|
||||||
ExtraButtons,
|
ExtraButtons,
|
||||||
PostStatusForm,
|
PostStatusForm,
|
||||||
UserCard,
|
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
AvatarList,
|
AvatarList,
|
||||||
Timeago,
|
Timeago,
|
||||||
|
@ -115,7 +114,8 @@ const Status = {
|
||||||
StatusContent,
|
StatusContent,
|
||||||
RichContent,
|
RichContent,
|
||||||
MentionLink,
|
MentionLink,
|
||||||
MentionsLine
|
MentionsLine,
|
||||||
|
UserPopover
|
||||||
},
|
},
|
||||||
props: [
|
props: [
|
||||||
'statusoid',
|
'statusoid',
|
||||||
|
@ -361,6 +361,7 @@ const Status = {
|
||||||
return uniqBy(combinedUsers, 'id')
|
return uniqBy(combinedUsers, 'id')
|
||||||
},
|
},
|
||||||
tags () {
|
tags () {
|
||||||
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
return this.status.tags.filter(tagObj => tagObj.hasOwnProperty('name')).map(tagObj => tagObj.name).join(' ')
|
return this.status.tags.filter(tagObj => tagObj.hasOwnProperty('name')).map(tagObj => tagObj.name).join(' ')
|
||||||
},
|
},
|
||||||
hidePostStats () {
|
hidePostStats () {
|
||||||
|
@ -448,7 +449,7 @@ const Status = {
|
||||||
scrollIfHighlighted (highlightId) {
|
scrollIfHighlighted (highlightId) {
|
||||||
const id = highlightId
|
const id = highlightId
|
||||||
if (this.status.id === id) {
|
if (this.status.id === id) {
|
||||||
let rect = this.$el.getBoundingClientRect()
|
const rect = this.$el.getBoundingClientRect()
|
||||||
if (rect.top < 100) {
|
if (rect.top < 100) {
|
||||||
// Post is above screen, match its top to screen top
|
// Post is above screen, match its top to screen top
|
||||||
window.scrollBy(0, rect.top - 100)
|
window.scrollBy(0, rect.top - 100)
|
||||||
|
@ -463,7 +464,7 @@ const Status = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'highlight': function (id) {
|
highlight: function (id) {
|
||||||
this.scrollIfHighlighted(id)
|
this.scrollIfHighlighted(id)
|
||||||
},
|
},
|
||||||
'status.repeat_num': function (num) {
|
'status.repeat_num': function (num) {
|
||||||
|
@ -478,7 +479,7 @@ const Status = {
|
||||||
this.$store.dispatch('fetchFavs', this.status.id)
|
this.$store.dispatch('fetchFavs', this.status.id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'isSuspendable': function (val) {
|
isSuspendable: function (val) {
|
||||||
this.suspendable = val
|
this.suspendable = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,25 +124,23 @@
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
:href="$router.resolve(userProfileLink).href"
|
:href="$router.resolve(userProfileLink).href"
|
||||||
@click.stop.prevent.capture="toggleUserExpanded"
|
@click.prevent
|
||||||
>
|
>
|
||||||
<UserAvatar
|
<UserPopover
|
||||||
class="post-avatar"
|
:user-id="status.user.id"
|
||||||
:bot="botIndicator"
|
:overlay-centers="true"
|
||||||
:compact="compact"
|
>
|
||||||
:better-shadow="betterShadow"
|
<UserAvatar
|
||||||
:user="status.user"
|
class="post-avatar"
|
||||||
/>
|
:bot="botIndicator"
|
||||||
|
:compact="compact"
|
||||||
|
:better-shadow="betterShadow"
|
||||||
|
:user="status.user"
|
||||||
|
/>
|
||||||
|
</UserPopover>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-side">
|
<div class="right-side">
|
||||||
<UserCard
|
|
||||||
v-if="userExpanded"
|
|
||||||
:user-id="status.user.id"
|
|
||||||
:rounded="true"
|
|
||||||
:bordered="true"
|
|
||||||
class="usercard"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
v-if="!noHeading"
|
v-if="!noHeading"
|
||||||
class="status-heading"
|
class="status-heading"
|
||||||
|
@ -322,6 +320,7 @@
|
||||||
class="mentions-line-first"
|
class="mentions-line-first"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
{{ ' ' }}
|
||||||
<MentionsLine
|
<MentionsLine
|
||||||
v-if="hasMentionsLine"
|
v-if="hasMentionsLine"
|
||||||
:mentions="mentionsLine.slice(1)"
|
:mentions="mentionsLine.slice(1)"
|
||||||
|
@ -492,6 +491,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./status.js" ></script>
|
<script src="./status.js"></script>
|
||||||
|
|
||||||
<style src="./status.scss" lang="scss"></style>
|
<style src="./status.scss" lang="scss"></style>
|
||||||
|
|
|
@ -96,5 +96,5 @@
|
||||||
<slot v-if="!hideSubjectStatus" />
|
<slot v-if="!hideSubjectStatus" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script src="./status_body.js" ></script>
|
<script src="./status_body.js"></script>
|
||||||
<style lang="scss" src="./status_body.scss" />
|
<style lang="scss" src="./status_body.scss" />
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue