forked from AkkomaGang/akkoma-fe
resolve merge conflicts
This commit is contained in:
commit
b6280ae97d
83 changed files with 3622 additions and 3523 deletions
10
.eslintrc.js
10
.eslintrc.js
|
@ -1,17 +1,17 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
parser: '@babel/eslint-parser',
|
||||
sourceType: 'module'
|
||||
},
|
||||
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
|
||||
extends: [
|
||||
'standard',
|
||||
'plugin:vue/recommended'
|
||||
],
|
||||
// required to lint *.vue files
|
||||
plugins: [
|
||||
'vue'
|
||||
'vue',
|
||||
'import'
|
||||
],
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
|
@ -23,6 +23,8 @@ module.exports = {
|
|||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||
'vue/require-prop-types': 0,
|
||||
'vue/no-unused-vars': 0,
|
||||
'no-tabs': 0
|
||||
'no-tabs': 0,
|
||||
'vue/multi-word-component-names': 0,
|
||||
'vue/no-reserved-component-names': 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ pipeline:
|
|||
when:
|
||||
event:
|
||||
- pull_request
|
||||
image: node:16
|
||||
image: node:18
|
||||
commands:
|
||||
- yarn
|
||||
- yarn lint
|
||||
|
@ -13,7 +13,7 @@ pipeline:
|
|||
when:
|
||||
event:
|
||||
- pull_request
|
||||
image: node:16
|
||||
image: node:18
|
||||
commands:
|
||||
- apt update
|
||||
- apt install firefox-esr -y --no-install-recommends
|
||||
|
@ -27,7 +27,7 @@ pipeline:
|
|||
branch:
|
||||
- develop
|
||||
- stable
|
||||
image: node:16
|
||||
image: node:18
|
||||
commands:
|
||||
- yarn
|
||||
- yarn build
|
||||
|
@ -39,7 +39,7 @@ pipeline:
|
|||
branch:
|
||||
- develop
|
||||
- stable
|
||||
image: node:16
|
||||
image: node:18
|
||||
secrets:
|
||||
- SCW_ACCESS_KEY
|
||||
- SCW_SECRET_KEY
|
||||
|
|
24
CODE_OF_CONDUCT.md
Normal file
24
CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Akkoma Code of Conduct
|
||||
|
||||
The Akkoma project aims to be **enjoyable** for anyone to participate in, regardless of their identity or level of expertise. To achieve this, the community must create an environment which is **safe** and **equitable**; the following guidelines have been created with these goals in mind.
|
||||
|
||||
1. **Treat individuals with respect.** Differing experiences and viewpoints deserve to be respected, and bigotry and harassment are not tolerated under any circumstances.
|
||||
- Individuals should at all times be treated as equals, regardless of their age, gender, sexuality, race, ethnicity, _or any other characteristic_, intrinsic or otherwise.
|
||||
- Behaviour that is harmful in nature should be addressed and corrected *regardless of intent*.
|
||||
- Respect personal boundaries and ask for clarification whenever they are unclear.
|
||||
- (Obviously, hate does not count as merely a "differing viewpoint", because it is harmful in nature.)
|
||||
|
||||
2. **Be understanding of differences in communication.** Not everyone is aware of unspoken social cues, and speech that is not intended to be offensive should not be treated as such simply due to an atypical manner of communication.
|
||||
- Somebody who speaks bluntly is not necessarily rude, and somebody who swears a lot is not necessarily volatile.
|
||||
- Try to confirm your interpretation of their intent rather than assuming bad faith.
|
||||
- Someone may not communicate as, or come across as a picture of "professionalism", but this should not be seen as a reason to dismiss them. This is a **casual** space, and communication styles can reflect that.
|
||||
|
||||
3. **"Uncomfortable" does not mean "unsafe".** In an ideal world, the community would be safe, equitable, enjoyable, *and* comfortable for all members at all times. Unfortunately, this is not always possible in reality.
|
||||
- Safety and equity will be prioritized over comfort whenever it is necessary to do so.
|
||||
- Weaponizing one's own discomfort to deflect accountability or censor an individual (e.g. "white fragility") is a form of discriminatory conduct.
|
||||
|
||||
4. **Let people grow from their mistakes.** Nobody is perfect; even the most well-meaning individual can do something hurtful. Everyone should be given a fair opportunity to explain themselves and correct their behaviour. Portraying someone as inherently malicious prevents improvement and shifts focus away from the *action* that was problematic.
|
||||
- Avoid bringing up past events that do not accurately reflect an individual's current actions or beliefs. (This is, of course, different from providing evidence of a recurring pattern of behaviour.)
|
||||
|
||||
---
|
||||
This document was adapted from one created by ~keith as part of punks default repository template, and is licensed under CC-BY-SA 4.0. The original template is here: <https://bytes.keithhacks.cyou/keith/default-template>
|
|
@ -1,49 +0,0 @@
|
|||
```
|
||||
o$$$$$$oo
|
||||
o$" "$oo
|
||||
$ o""""$o "$o
|
||||
"$ o "o "o $
|
||||
"$ $o $ $ o$
|
||||
"$ o$"$ o$
|
||||
"$ooooo$$ $ o$
|
||||
o$ """ $ " $$$ " $
|
||||
o$ $o $$" " "
|
||||
$$ $ " $ $$$o"$ o o$"
|
||||
$" o "" $ $" " o" $$
|
||||
$o " " $ o$" o" o$"
|
||||
"$o $$ $ o" o$$"
|
||||
""o$o"$" $oo" o$"
|
||||
o$$ $ $$$ o$$
|
||||
o" o oo"" "" "$o
|
||||
o$o" "" $
|
||||
$" " o" " " " "o
|
||||
$$ " " o$ o$o " $
|
||||
o$ $ $ o$$ " " ""
|
||||
o $ $" " "o o$
|
||||
$ o $o$oo$""
|
||||
$o $ o o o"$$
|
||||
$o o $ $ "$o
|
||||
$o $ o $ $ "o
|
||||
$ $ "o $ "o"$o
|
||||
$ " o $ o $$
|
||||
$o$o$o$o$$o$$$o$$o$o$$o$$o$$$o$o$o$o$o$o$o$o$o$ooo
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ " $$$$$
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "$$$$
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o$$$$"
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ooooo$$$$
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"""""
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"
|
||||
"$o$o$o$o$o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"
|
||||
"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"
|
||||
"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"""
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
```
|
|
@ -28,18 +28,6 @@ var devMiddleware = require('webpack-dev-middleware')(compiler, {
|
|||
})
|
||||
|
||||
var hotMiddleware = require('webpack-hot-middleware')(compiler)
|
||||
// force page reload when html-webpack-plugin template changes
|
||||
compiler.plugin('compilation', function (compilation) {
|
||||
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
|
||||
// FIXME: This supposed to reload whole page when index.html is changed,
|
||||
// however now it reloads entire page on every breath, i suppose the order
|
||||
// of plugins changed or something. It's a minor thing and douesn't hurt
|
||||
// disabling it, constant reloads hurt much more
|
||||
|
||||
// hotMiddleware.publish({ action: 'reload' })
|
||||
// cb()
|
||||
})
|
||||
})
|
||||
|
||||
// proxy api requests
|
||||
Object.keys(proxyTable).forEach(function (context) {
|
||||
|
|
|
@ -2,8 +2,8 @@ var path = require('path')
|
|||
var config = require('../config')
|
||||
var utils = require('./utils')
|
||||
var projectRoot = path.resolve(__dirname, '../')
|
||||
var ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin')
|
||||
var { VueLoaderPlugin } = require("vue-loader");
|
||||
const WorkboxPlugin = require('workbox-webpack-plugin');
|
||||
var { VueLoaderPlugin } = require('vue-loader')
|
||||
|
||||
var env = process.env.NODE_ENV
|
||||
// check env & config/index.js to decide weither to enable CSS Sourcemaps for the
|
||||
|
@ -19,6 +19,7 @@ module.exports = {
|
|||
app: './src/main.js'
|
||||
},
|
||||
output: {
|
||||
hashFunction: "sha256", // Workaround for builds with OpenSSL 3.
|
||||
path: config.build.assetsRoot,
|
||||
publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
|
||||
filename: '[name].js'
|
||||
|
@ -33,6 +34,9 @@ module.exports = {
|
|||
modules: [
|
||||
path.join(__dirname, '../node_modules')
|
||||
],
|
||||
fallback: {
|
||||
"url": require.resolve("url/"),
|
||||
},
|
||||
alias: {
|
||||
'static': path.resolve(__dirname, '../static'),
|
||||
'src': path.resolve(__dirname, '../src'),
|
||||
|
@ -115,9 +119,10 @@ module.exports = {
|
|||
]
|
||||
},
|
||||
plugins: [
|
||||
new ServiceWorkerWebpackPlugin({
|
||||
entry: path.join(__dirname, '..', 'src/sw.js'),
|
||||
filename: 'sw-pleroma.js'
|
||||
new WorkboxPlugin.InjectManifest({
|
||||
swSrc: path.join(__dirname, '..', 'src/sw.js'),
|
||||
swDest: 'sw-pleroma.js',
|
||||
maximumFileSizeToCacheInBytes: 15 * 1024 * 1024,
|
||||
}),
|
||||
new VueLoaderPlugin()
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var config = require('../config')
|
||||
var webpack = require('webpack')
|
||||
var merge = require('webpack-merge')
|
||||
var { merge } = require('webpack-merge')
|
||||
var utils = require('./utils')
|
||||
var baseWebpackConfig = require('./webpack.base.conf')
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
@ -16,7 +16,7 @@ module.exports = merge(baseWebpackConfig, {
|
|||
},
|
||||
mode: 'development',
|
||||
// eval-source-map is faster for development
|
||||
devtool: '#eval-source-map',
|
||||
devtool: 'eval-source-map',
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': config.dev.env,
|
||||
|
|
|
@ -2,7 +2,7 @@ var path = require('path')
|
|||
var config = require('../config')
|
||||
var utils = require('./utils')
|
||||
var webpack = require('webpack')
|
||||
var merge = require('webpack-merge')
|
||||
var { merge } = require('webpack-merge')
|
||||
var baseWebpackConfig = require('./webpack.base.conf')
|
||||
var MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
@ -19,7 +19,7 @@ var webpackConfig = merge(baseWebpackConfig, {
|
|||
module: {
|
||||
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, extract: true })
|
||||
},
|
||||
devtool: config.build.productionSourceMap ? '#source-map' : false,
|
||||
devtool: 'source-map',
|
||||
optimization: {
|
||||
minimize: true,
|
||||
splitChunks: {
|
||||
|
@ -62,7 +62,7 @@ var webpackConfig = merge(baseWebpackConfig, {
|
|||
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||
},
|
||||
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||
chunksSortMode: 'dependency'
|
||||
chunksSortMode: 'auto'
|
||||
}),
|
||||
// split vendor js into its own file
|
||||
// extract webpack runtime and module manifest to its own file in order to
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var merge = require('webpack-merge')
|
||||
var { merge } = require('webpack-merge')
|
||||
var prodEnv = require('./prod.env')
|
||||
|
||||
module.exports = merge(prodEnv, {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var merge = require('webpack-merge')
|
||||
var { merge } = require('webpack-merge')
|
||||
var devEnv = require('./dev.env')
|
||||
|
||||
module.exports = merge(devEnv, {
|
||||
|
|
|
@ -70,9 +70,6 @@ Default post formatting option (markdown/bbcode/plaintext/etc...)
|
|||
### `redirectRootNoLogin`, `redirectRootLogin`
|
||||
These two settings should point to where FE should redirect visitor when they login/open up website root
|
||||
|
||||
### `scopeCopy`
|
||||
Copy post scope (visibility) when replying to a post. Instance-default.
|
||||
|
||||
### `sidebarRight`
|
||||
Change alignment of sidebar and panels to the right. Defaults to `false`.
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
|
||||
<title>Pleroma</title>
|
||||
<title>Akkoma</title>
|
||||
<link rel="stylesheet" href="/static/font/css/fontello.css">
|
||||
<link rel="stylesheet" href="/static/font/css/animation.css">
|
||||
<link rel="stylesheet" href="/static/font/tiresias.css">
|
||||
|
@ -13,7 +13,7 @@
|
|||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
</head>
|
||||
<body class="hidden">
|
||||
<noscript>To use Pleroma, please enable JavaScript.</noscript>
|
||||
<noscript>To use Akkoma, please enable JavaScript.</noscript>
|
||||
<div id="app"></div>
|
||||
<div id="modal"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
|
|
69
package.json
69
package.json
|
@ -33,13 +33,13 @@
|
|||
"escape-html": "1.0.3",
|
||||
"js-cookie": "^3.0.1",
|
||||
"localforage": "1.10.0",
|
||||
"marked": "^4.0.17",
|
||||
"marked": "^4.2.2",
|
||||
"marked-mfm": "^0.5.0",
|
||||
"parse-link-header": "1.0.1",
|
||||
"parse-link-header": "^2.0.0",
|
||||
"phoenix": "1.6.2",
|
||||
"punycode.js": "2.1.0",
|
||||
"qrcode": "1",
|
||||
"ruffle-mirror": "2021.12.31",
|
||||
"url": "^0.11.0",
|
||||
"vue": "^3.2.31",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "4.0.14",
|
||||
|
@ -48,6 +48,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.17.8",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@babel/plugin-transform-runtime": "7.17.0",
|
||||
"@babel/preset-env": "7.16.11",
|
||||
"@babel/register": "7.17.7",
|
||||
|
@ -58,31 +59,29 @@
|
|||
"@vue/compiler-sfc": "^3.1.0",
|
||||
"@vue/test-utils": "^2.0.2",
|
||||
"autoprefixer": "6.7.7",
|
||||
"babel-eslint": "7.2.3",
|
||||
"babel-loader": "8.2.4",
|
||||
"babel-loader": "^9.1.0",
|
||||
"babel-plugin-lodash": "3.3.4",
|
||||
"chai": "3.5.0",
|
||||
"chai": "^4.3.7",
|
||||
"chalk": "1.1.3",
|
||||
"chromedriver": "87.0.7",
|
||||
"connect-history-api-fallback": "1.6.0",
|
||||
"copy-webpack-plugin": "6.4.1",
|
||||
"cross-spawn": "4.0.2",
|
||||
"css-loader": "0.28.11",
|
||||
"custom-event-polyfill": "1.0.7",
|
||||
"eslint": "5.16.0",
|
||||
"eslint-config-standard": "12.0.0",
|
||||
"eslint-friendly-formatter": "2.0.7",
|
||||
"eslint-loader": "2.2.1",
|
||||
"eslint-plugin-import": "2.25.4",
|
||||
"eslint-plugin-node": "7.0.1",
|
||||
"eslint-plugin-promise": "4.3.1",
|
||||
"eslint-plugin-standard": "4.1.0",
|
||||
"eslint-plugin-vue": "5.2.3",
|
||||
"chromedriver": "^107.0.3",
|
||||
"connect-history-api-fallback": "^2.0.0",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"css-loader": "^6.7.2",
|
||||
"custom-event-polyfill": "^1.0.7",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-friendly-formatter": "^4.0.1",
|
||||
"eslint-loader": "^4.0.2",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"eslint-plugin-vue": "^9.7.0",
|
||||
"eventsource-polyfill": "0.9.6",
|
||||
"express": "4.17.3",
|
||||
"file-loader": "3.0.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"function-bind": "1.1.1",
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"http-proxy-middleware": "0.21.0",
|
||||
"inject-loader": "2.0.1",
|
||||
"iso-639-1": "2.1.15",
|
||||
|
@ -96,7 +95,7 @@
|
|||
"karma-sinon-chai": "2.0.2",
|
||||
"karma-sourcemap-loader": "0.3.8",
|
||||
"karma-spec-reporter": "0.0.33",
|
||||
"karma-webpack": "4.0.2",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"lodash": "4.17.21",
|
||||
"lolex": "1.6.0",
|
||||
"mini-css-extract-plugin": "0.12.0",
|
||||
|
@ -106,27 +105,27 @@
|
|||
"ora": "0.4.1",
|
||||
"postcss-loader": "3.0.0",
|
||||
"raw-loader": "0.5.1",
|
||||
"sass": "1.53.0",
|
||||
"sass-loader": "7.3.1",
|
||||
"sass": "^1.56.0",
|
||||
"sass-loader": "^13.2.0",
|
||||
"selenium-server": "2.53.1",
|
||||
"semver": "5.7.1",
|
||||
"serviceworker-webpack-plugin": "1.0.1",
|
||||
"shelljs": "0.8.5",
|
||||
"sinon": "2.4.1",
|
||||
"sinon-chai": "2.14.0",
|
||||
"stylelint": "13.6.1",
|
||||
"stylelint-config-standard": "20.0.0",
|
||||
"stylelint-rscss": "0.4.0",
|
||||
"url-loader": "1.1.2",
|
||||
"vue-loader": "^16.0.0",
|
||||
"vue-style-loader": "4.1.2",
|
||||
"webpack": "4.46.0",
|
||||
"webpack-dev-middleware": "3.7.3",
|
||||
"webpack-hot-middleware": "2.25.1",
|
||||
"webpack-merge": "0.20.0"
|
||||
"url-loader": "^4.1.1",
|
||||
"vue-loader": "^17.0.0",
|
||||
"vue-style-loader": "^4.1.2",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-dev-middleware": "^5.3.3",
|
||||
"webpack-hot-middleware": "^2.25.1",
|
||||
"webpack-merge": "^5.8.0",
|
||||
"workbox-webpack-plugin": "^6.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.0.0",
|
||||
"node": ">= 16.0.0",
|
||||
"npm": ">= 3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import FeaturesPanel from './components/features_panel/features_panel.vue'
|
|||
import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'
|
||||
import SettingsModal from './components/settings_modal/settings_modal.vue'
|
||||
import MediaModal from './components/media_modal/media_modal.vue'
|
||||
import ModModal from './components/mod_modal/mod_modal.vue'
|
||||
import SideDrawer from './components/side_drawer/side_drawer.vue'
|
||||
import MobilePostStatusButton from './components/mobile_post_status_button/mobile_post_status_button.vue'
|
||||
import MobileNav from './components/mobile_nav/mobile_nav.vue'
|
||||
|
@ -33,6 +34,7 @@ export default {
|
|||
MobileNav,
|
||||
DesktopNav,
|
||||
SettingsModal,
|
||||
ModModal,
|
||||
UserReportingModal,
|
||||
PostStatusModal,
|
||||
EditStatusModal,
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
<EditStatusModal v-if="editingAvailable" />
|
||||
<StatusHistoryModal v-if="editingAvailable" />
|
||||
<SettingsModal />
|
||||
<ModModal />
|
||||
<GlobalNoticeList />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -150,6 +150,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
|
|||
copyInstanceOption('showWiderShortcuts')
|
||||
copyInstanceOption('showNavShortcuts')
|
||||
copyInstanceOption('showPanelNavShortcuts')
|
||||
copyInstanceOption('stopGifs')
|
||||
copyInstanceOption('logo')
|
||||
|
||||
store.dispatch('setInstanceOption', {
|
||||
|
@ -171,10 +172,8 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
|
|||
copyInstanceOption('redirectRootNoLogin')
|
||||
copyInstanceOption('redirectRootLogin')
|
||||
copyInstanceOption('showInstanceSpecificPanel')
|
||||
copyInstanceOption('minimalScopesMode')
|
||||
copyInstanceOption('hideMutedPosts')
|
||||
copyInstanceOption('collapseMessageWithSubject')
|
||||
copyInstanceOption('scopeCopy')
|
||||
copyInstanceOption('subjectLineBehavior')
|
||||
copyInstanceOption('postContentType')
|
||||
copyInstanceOption('alwaysShowSubjectInput')
|
||||
|
@ -398,6 +397,7 @@ const afterStoreSetup = async ({ store, i18n }) => {
|
|||
// Start fetching things that don't need to block the UI
|
||||
store.dispatch('fetchMutes')
|
||||
store.dispatch('startFetchingAnnouncements')
|
||||
store.dispatch('startFetchingReports')
|
||||
getTOS({ store })
|
||||
getStickers({ store })
|
||||
|
||||
|
|
|
@ -225,7 +225,7 @@ const Attachment = {
|
|||
this.$emit('resize', newHeight)
|
||||
},
|
||||
postStatus (event) {
|
||||
console.log(this.statusForm.postStatus(event, this.statusForm.newStatus))
|
||||
this.statusForm.postStatus(event, this.statusForm.newStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
display: flex;
|
||||
padding-top: 0.5em;
|
||||
z-index: 1;
|
||||
max-height: 50%;
|
||||
|
||||
p {
|
||||
flex: 1;
|
||||
|
@ -36,7 +37,7 @@
|
|||
white-space: pre-line;
|
||||
word-break: break-word;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
&.-static {
|
||||
|
|
|
@ -16,7 +16,8 @@ import {
|
|||
faUsers,
|
||||
faCommentMedical,
|
||||
faBookmark,
|
||||
faInfoCircle
|
||||
faInfoCircle,
|
||||
faUserTie
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
|
@ -34,7 +35,8 @@ library.add(
|
|||
faUsers,
|
||||
faCommentMedical,
|
||||
faBookmark,
|
||||
faInfoCircle
|
||||
faInfoCircle,
|
||||
faUserTie
|
||||
)
|
||||
|
||||
export default {
|
||||
|
@ -98,6 +100,9 @@ export default {
|
|||
privateMode () { return this.$store.state.instance.private },
|
||||
shouldConfirmLogout () {
|
||||
return this.$store.getters.mergedConfig.modalOnLogout
|
||||
},
|
||||
showBubbleTimeline () {
|
||||
return this.$store.state.instance.localBubbleInstances.length > 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -109,6 +114,9 @@ export default {
|
|||
},
|
||||
openSettingsModal () {
|
||||
this.$store.dispatch('openSettingsModal')
|
||||
},
|
||||
openModModal () {
|
||||
this.$store.dispatch('openModModal')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
/>
|
||||
</router-link>
|
||||
<router-link
|
||||
v-if="currentUser"
|
||||
v-if="currentUser && showBubbleTimeline"
|
||||
:to="{ name: 'bubble-timeline' }"
|
||||
class="nav-icon"
|
||||
>
|
||||
|
@ -151,6 +151,18 @@
|
|||
:title="$t('nav.preferences')"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
v-if="currentUser && currentUser.role === 'admin' || currentUser.role === 'moderator'"
|
||||
class="button-unstyled nav-icon"
|
||||
@click.stop="openModModal"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
icon="user-tie"
|
||||
:title="$t('nav.moderation')"
|
||||
/>
|
||||
</button>
|
||||
<a
|
||||
v-if="currentUser && currentUser.role === 'admin'"
|
||||
href="/pleroma/admin/#/login-pleroma"
|
||||
|
|
|
@ -178,7 +178,7 @@ const EmojiInput = {
|
|||
textAtCaret: async function (newWord) {
|
||||
const firstchar = newWord.charAt(0)
|
||||
this.suggestions = []
|
||||
if (newWord === firstchar) return
|
||||
if (newWord === firstchar && firstchar !== '$') return
|
||||
const matchedSuggestions = await this.suggest(newWord)
|
||||
// Async: cancel if textAtCaret has changed during wait
|
||||
if (this.textAtCaret !== newWord) return
|
||||
|
@ -277,7 +277,6 @@ const EmojiInput = {
|
|||
},
|
||||
replaceText (e, suggestion) {
|
||||
const len = this.suggestions.length || 0
|
||||
if (this.textAtCaret.length === 1) { return }
|
||||
if (len > 0 || suggestion) {
|
||||
const chosenSuggestion = suggestion || this.suggestions[this.highlighted]
|
||||
const replacement = chosenSuggestion.replacement
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
:class="{ highlighted: index === highlighted }"
|
||||
@click.stop.prevent="onClick($event, suggestion)"
|
||||
>
|
||||
<span class="image">
|
||||
<span v-if="!suggestion.mfm" class="image">
|
||||
<img
|
||||
v-if="suggestion.img"
|
||||
:src="suggestion.img"
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
const MFM_TAGS = ['blur', 'bounce', 'flip', 'font', 'jelly', 'jump', 'rainbow', 'rotate', 'shake', 'sparkle', 'spin', 'tada', 'twitch', 'x2', 'x3', 'x4']
|
||||
.map(tag => ({ displayText: tag, detailText: '$[' + tag + ' ]', replacement: '$[' + tag + ' ]', mfm: true }))
|
||||
|
||||
/**
|
||||
* suggest - generates a suggestor function to be used by emoji-input
|
||||
* data: object providing source information for specific types of suggestions:
|
||||
|
@ -21,6 +24,10 @@ export default data => {
|
|||
if (firstChar === '@' && usersCurry) {
|
||||
return usersCurry(input)
|
||||
}
|
||||
if (firstChar === '$') {
|
||||
return MFM_TAGS
|
||||
.filter(({ replacement }) => replacement.toLowerCase().indexOf(input) !== -1)
|
||||
}
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
@import '../../_variables.scss';
|
||||
|
||||
.Notification {
|
||||
.emoji-picker {
|
||||
min-width: 160%;
|
||||
width: 150%;
|
||||
overflow: hidden;
|
||||
left: -70%;
|
||||
max-width: 100%;
|
||||
@media (min-width: 800px) and (max-width: 1300px) {
|
||||
left: -50%;
|
||||
min-width: 50%;
|
||||
max-width: 130%;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
left: -10%;
|
||||
min-width: 50%;
|
||||
max-width: 130%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.emoji-picker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.picked-reaction {
|
||||
.button-default.picked-reaction {
|
||||
border: 1px solid var(--accent, $fallback--link);
|
||||
margin-left: -1px; // offset the border, can't use inset shadows either
|
||||
margin-right: calc(0.5em - 1px);
|
||||
|
|
|
@ -4,7 +4,6 @@ const FeaturesPanel = {
|
|||
computed: {
|
||||
whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled },
|
||||
mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable },
|
||||
minimalScopesMode: function () { return this.$store.state.instance.minimalScopesMode },
|
||||
textlimit: function () { return this.$store.state.instance.textlimit },
|
||||
uploadlimit: function () { return fileSizeFormatService.fileSizeFormat(this.$store.state.instance.uploadlimit) }
|
||||
}
|
||||
|
|
58
src/components/mod_modal/mod_modal.js
Normal file
58
src/components/mod_modal/mod_modal.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
import Modal from 'src/components/modal/modal.vue'
|
||||
import PanelLoading from 'src/components/panel_loading/panel_loading.vue'
|
||||
import AsyncComponentError from 'src/components/async_component_error/async_component_error.vue'
|
||||
import getResettableAsyncComponent from 'src/services/resettable_async_component.js'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faTimes,
|
||||
faChevronDown
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import {
|
||||
faWindowMinimize
|
||||
} from '@fortawesome/free-regular-svg-icons'
|
||||
|
||||
library.add(
|
||||
faTimes,
|
||||
faWindowMinimize,
|
||||
faChevronDown
|
||||
)
|
||||
|
||||
const ModModal = {
|
||||
components: {
|
||||
Modal,
|
||||
ModModalContent: getResettableAsyncComponent(
|
||||
() => import('./mod_modal_content.vue'),
|
||||
{
|
||||
loadingComponent: PanelLoading,
|
||||
errorComponent: AsyncComponentError,
|
||||
delay: 0
|
||||
}
|
||||
)
|
||||
},
|
||||
methods: {
|
||||
closeModal () {
|
||||
this.$store.dispatch('closeModModal')
|
||||
},
|
||||
peekModal () {
|
||||
this.$store.dispatch('togglePeekModModal')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
moderator () {
|
||||
return this.$store.state.users.currentUser &&
|
||||
(this.$store.state.users.currentUser.role === 'admin' ||
|
||||
this.$store.state.users.currentUser.role === 'moderator')
|
||||
},
|
||||
modalActivated () {
|
||||
return this.$store.state.interface.modModalState !== 'hidden'
|
||||
},
|
||||
modalOpenedOnce () {
|
||||
return this.$store.state.interface.modModalLoaded
|
||||
},
|
||||
modalPeeked () {
|
||||
return this.$store.state.interface.modModalState === 'minimized'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ModModal
|
44
src/components/mod_modal/mod_modal.scss
Normal file
44
src/components/mod_modal/mod_modal.scss
Normal file
|
@ -0,0 +1,44 @@
|
|||
@import 'src/_variables.scss';
|
||||
.mod-modal {
|
||||
overflow: hidden;
|
||||
|
||||
&.peek {
|
||||
.mod-modal-panel {
|
||||
/* Explanation:
|
||||
* Modal is positioned vertically centered.
|
||||
* 100vh - 100% = Distance between modal's top+bottom boundaries and screen
|
||||
* (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen
|
||||
* + 100% - we move modal completely off-screen, it's top boundary touches
|
||||
* bottom of the screen
|
||||
* - 50px - leaving tiny amount of space so that titlebar + tiny amount of modal is visible
|
||||
*/
|
||||
transform: translateY(calc(((100vh - 100%) / 2 + 100%) - 50px));
|
||||
|
||||
@media all and (max-width: 800px) {
|
||||
/* For mobile, the modal takes 100% of the available screen.
|
||||
This ensures the minimized modal is always 50px above the browser bottom bar regardless of whether or not it is visible.
|
||||
*/
|
||||
transform: translateY(calc(100% - 50px));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mod-modal-panel {
|
||||
overflow: hidden;
|
||||
transition: transform;
|
||||
transition-timing-function: ease-in-out;
|
||||
transition-duration: 300ms;
|
||||
width: 1000px;
|
||||
max-width: 90vw;
|
||||
height: 90vh;
|
||||
|
||||
@media all and (max-width: 800px) {
|
||||
max-width: 100vw;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
height: inherit;
|
||||
}
|
||||
}
|
||||
}
|
43
src/components/mod_modal/mod_modal.vue
Normal file
43
src/components/mod_modal/mod_modal.vue
Normal file
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<Modal
|
||||
v-if="moderator"
|
||||
:is-open="modalActivated"
|
||||
class="mod-modal"
|
||||
:class="{ peek: modalPeeked }"
|
||||
:no-background="modalPeeked"
|
||||
>
|
||||
<div class="mod-modal-panel panel">
|
||||
<div class="panel-heading">
|
||||
<span class="title">
|
||||
{{ $t('moderation.moderation') }}
|
||||
</span>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:title="$t('general.peek')"
|
||||
@click="peekModal"
|
||||
>
|
||||
<FAIcon
|
||||
:icon="['far', 'window-minimize']"
|
||||
fixed-width
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:title="$t('general.close')"
|
||||
@click="closeModal"
|
||||
>
|
||||
<FAIcon
|
||||
icon="times"
|
||||
fixed-width
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<ModModalContent v-if="modalOpenedOnce" />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script src="./mod_modal.js"></script>
|
||||
<style src="./mod_modal.scss" lang="scss"></style>
|
63
src/components/mod_modal/mod_modal_content.js
Normal file
63
src/components/mod_modal/mod_modal_content.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||
|
||||
import ReportsTab from './tabs/reports_tab/reports_tab.vue'
|
||||
// import StatusesTab from './tabs/statuses_tab.vue'
|
||||
// import UsersTab from './tabs/users_tab.vue'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faFlag,
|
||||
faMessage,
|
||||
faUsers
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faFlag,
|
||||
faMessage,
|
||||
faUsers
|
||||
)
|
||||
|
||||
const ModModalContent = {
|
||||
components: {
|
||||
TabSwitcher,
|
||||
|
||||
ReportsTab
|
||||
// StatusesTab,
|
||||
// UsersTab
|
||||
},
|
||||
computed: {
|
||||
open () {
|
||||
return this.$store.state.interface.modModalState !== 'hidden'
|
||||
},
|
||||
bodyLock () {
|
||||
return this.$store.state.interface.modModalState === 'visible'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen () {
|
||||
const targetTab = this.$store.state.interface.modModalTargetTab
|
||||
// We're being told to open in specific tab
|
||||
if (targetTab) {
|
||||
const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => {
|
||||
return elm.props && elm.props['data-tab-name'] === targetTab
|
||||
})
|
||||
if (tabIndex >= 0) {
|
||||
this.$refs.tabSwitcher.setTab(tabIndex)
|
||||
}
|
||||
}
|
||||
// Clear the state of target tab, so that next time moderation is opened
|
||||
// it doesn't force it.
|
||||
this.$store.dispatch('clearModModalTargetTab')
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.onOpen()
|
||||
},
|
||||
watch: {
|
||||
open: function (value) {
|
||||
if (value) this.onOpen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ModModalContent
|
21
src/components/mod_modal/mod_modal_content.scss
Normal file
21
src/components/mod_modal/mod_modal_content.scss
Normal file
|
@ -0,0 +1,21 @@
|
|||
@import 'src/_variables.scss';
|
||||
.mod_tab-switcher {
|
||||
height: 100%;
|
||||
|
||||
.content {
|
||||
margin: 1em 1em 1.4em;
|
||||
|
||||
> div {
|
||||
margin-bottom: .5em;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
}
|
20
src/components/mod_modal/mod_modal_content.vue
Normal file
20
src/components/mod_modal/mod_modal_content.vue
Normal file
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<tab-switcher
|
||||
ref="tabSwitcher"
|
||||
class="mod_tab-switcher"
|
||||
:side-tab-bar="true"
|
||||
:scrollable-tabs="true"
|
||||
:body-scroll-lock="bodyLock"
|
||||
>
|
||||
<div
|
||||
:label="$t('moderation.reports.reports')"
|
||||
icon="flag"
|
||||
data-tab-name="reports"
|
||||
>
|
||||
<ReportsTab />
|
||||
</div>
|
||||
</tab-switcher>
|
||||
</template>
|
||||
|
||||
<script src="./mod_modal_content.js"></script>
|
||||
<style src="./mod_modal_content.scss" lang="scss"></style>
|
124
src/components/mod_modal/tabs/reports_tab/report_card.js
Normal file
124
src/components/mod_modal/tabs/reports_tab/report_card.js
Normal file
|
@ -0,0 +1,124 @@
|
|||
import Popover from 'src/components/popover/popover.vue'
|
||||
import Status from 'src/components/status/status.vue'
|
||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||
import ReportNote from './report_note.vue'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faChevronDown,
|
||||
faChevronUp
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faChevronDown,
|
||||
faChevronUp
|
||||
)
|
||||
|
||||
const FORCE_NSFW = 'mrf_tag:media-force-nsfw'
|
||||
const STRIP_MEDIA = 'mrf_tag:media-strip'
|
||||
const FORCE_UNLISTED = 'mrf_tag:force-unlisted'
|
||||
const SANDBOX = 'mrf_tag:sandbox'
|
||||
|
||||
const ReportCard = {
|
||||
data () {
|
||||
return {
|
||||
hidden: true,
|
||||
statusesHidden: true,
|
||||
notesHidden: true,
|
||||
note: null,
|
||||
tags: {
|
||||
FORCE_NSFW,
|
||||
STRIP_MEDIA,
|
||||
FORCE_UNLISTED,
|
||||
SANDBOX
|
||||
}
|
||||
}
|
||||
},
|
||||
props: [
|
||||
'account',
|
||||
'actor',
|
||||
'content',
|
||||
'id',
|
||||
'notes',
|
||||
'state',
|
||||
'statuses'
|
||||
],
|
||||
components: {
|
||||
ReportNote,
|
||||
Popover,
|
||||
Status,
|
||||
UserAvatar
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('fetchUser', this.account.id)
|
||||
},
|
||||
computed: {
|
||||
isOpen () {
|
||||
return this.state === 'open'
|
||||
},
|
||||
tagPolicyEnabled () {
|
||||
return this.$store.state.instance.federationPolicy.mrf_policies.includes('TagPolicy')
|
||||
},
|
||||
user () {
|
||||
return this.$store.getters.findUser(this.account.id)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleHidden () {
|
||||
this.hidden = !this.hidden
|
||||
},
|
||||
decode (content) {
|
||||
content = content.replaceAll('<br/>', '\n')
|
||||
const textarea = document.createElement('textarea')
|
||||
textarea.innerHTML = content
|
||||
return textarea.value
|
||||
},
|
||||
updateReportState (state) {
|
||||
this.$store.dispatch('updateReportStates', { reports: [{ id: this.id, state }] })
|
||||
},
|
||||
toggleNotes () {
|
||||
this.notesHidden = !this.notesHidden
|
||||
},
|
||||
addNoteToReport () {
|
||||
if (this.note.length > 0) {
|
||||
this.$store.dispatch('addNoteToReport', { id: this.id, note: this.note })
|
||||
this.note = null
|
||||
}
|
||||
},
|
||||
toggleStatuses () {
|
||||
this.statusesHidden = !this.statusesHidden
|
||||
},
|
||||
hasTag (tag) {
|
||||
return this.user.tags.includes(tag)
|
||||
},
|
||||
toggleTag (tag) {
|
||||
if (this.hasTag(tag)) {
|
||||
this.$store.state.api.backendInteractor.untagUser({ user: this.user, tag }).then(response => {
|
||||
if (!response.ok) { return }
|
||||
this.$store.commit('untagUser', { user: this.user, tag })
|
||||
})
|
||||
} else {
|
||||
this.$store.state.api.backendInteractor.tagUser({ user: this.user, tag }).then(response => {
|
||||
if (!response.ok) { return }
|
||||
this.$store.commit('tagUser', { user: this.user, tag })
|
||||
})
|
||||
}
|
||||
},
|
||||
toggleActivationStatus () {
|
||||
this.$store.dispatch('toggleActivationStatus', { user: this.user })
|
||||
},
|
||||
deleteUser () {
|
||||
this.$store.state.backendInteractor.deleteUser({ user: this.user })
|
||||
.then(e => {
|
||||
this.$store.dispatch('markStatusesAsDeleted', status => this.user.id === status.user.id)
|
||||
const isProfile = this.$route.name === 'external-user-profile' || this.$route.name === 'user-profile'
|
||||
const isTargetUser = this.$route.params.name === this.user.name || this.$route.params.id === this.user.id
|
||||
if (isProfile && isTargetUser) {
|
||||
window.history.back()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ReportCard
|
202
src/components/mod_modal/tabs/reports_tab/report_card.vue
Normal file
202
src/components/mod_modal/tabs/reports_tab/report_card.vue
Normal file
|
@ -0,0 +1,202 @@
|
|||
<template>
|
||||
<div class="report-card panel">
|
||||
<div
|
||||
class="panel-heading"
|
||||
@click="toggleHidden"
|
||||
>
|
||||
<h4>{{ $t('moderation.reports.report') + ' ' + this.account.screen_name }}</h4>
|
||||
<button
|
||||
v-if="isOpen"
|
||||
class="button-default"
|
||||
@click.stop="updateReportState('closed')"
|
||||
>
|
||||
{{ $t('moderation.reports.close') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="isOpen"
|
||||
class="button-default"
|
||||
@click.stop="updateReportState('resolved')"
|
||||
>
|
||||
{{ $t('moderation.reports.resolve') }}
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="button-default"
|
||||
@click.stop="updateReportState('open')"
|
||||
>
|
||||
{{ $t('moderation.reports.reopen') }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="!hidden"
|
||||
class="panel-body report-body"
|
||||
>
|
||||
<div class="report-content">
|
||||
<div v-if="content">
|
||||
{{ decode(content) }}
|
||||
</div>
|
||||
<i v-else class="faint">
|
||||
{{ $t('moderation.reports.no_content') }}
|
||||
</i>
|
||||
<div class="report-author">
|
||||
<UserAvatar
|
||||
class="small-avatar"
|
||||
:user="actor"
|
||||
/>
|
||||
{{ this.actor.screen_name }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="dropdown"
|
||||
v-if="!hidden && this.statuses.length > 0"
|
||||
>
|
||||
<button
|
||||
class="button button-unstyled dropdown-header"
|
||||
@click="toggleStatuses"
|
||||
>
|
||||
{{ $tc('moderation.reports.statuses', statuses.length - 1, { count: statuses.length }) }}
|
||||
<FAIcon
|
||||
class="timelines-chevron"
|
||||
fixed-width
|
||||
:icon="statusesHidden ? 'chevron-down' : 'chevron-up'"
|
||||
/>
|
||||
</button>
|
||||
<div v-if="!statusesHidden">
|
||||
<Status
|
||||
v-for="status in statuses"
|
||||
:key="status.id"
|
||||
:collapsable="false"
|
||||
:expandable="false"
|
||||
:compact="false"
|
||||
:statusoid="status"
|
||||
:no-heading="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="dropdown"
|
||||
v-if="!hidden && this.notes.length > 0"
|
||||
>
|
||||
<button
|
||||
class="button button-unstyled dropdown-header"
|
||||
@click="toggleNotes"
|
||||
>
|
||||
{{ $tc('moderation.reports.notes', notes.length - 1, { count: notes.length }) }}
|
||||
<FAIcon
|
||||
class="timelines-chevron"
|
||||
fixed-width
|
||||
:icon="notesHidden ? 'chevron-down' : 'chevron-up'"
|
||||
/>
|
||||
</button>
|
||||
<div v-if="!notesHidden">
|
||||
<ReportNote
|
||||
v-for="note in notes"
|
||||
:key="note.id"
|
||||
:report_id="id"
|
||||
v-bind="note"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="report-add-note">
|
||||
<textarea
|
||||
rows="1"
|
||||
cols="1"
|
||||
v-model.trim="note"
|
||||
:placeholder="$t('moderation.reports.note_placeholder')"
|
||||
/>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click.stop="addNoteToReport"
|
||||
>
|
||||
{{ $t('moderation.reports.add_note') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!hidden"
|
||||
class="panel-footer"
|
||||
>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click.stop="toggleActivationStatus"
|
||||
>
|
||||
{{ $t(!!user.deactivated ? 'user_card.admin_menu.activate_account' : 'user_card.admin_menu.deactivate_account') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click.stop="deleteUser"
|
||||
>
|
||||
{{ $t('user_card.admin_menu.delete_account') }}
|
||||
</button>
|
||||
<Popover
|
||||
trigger="click"
|
||||
placement="top"
|
||||
:offset="{ y: 5 }"
|
||||
remove-padding
|
||||
>
|
||||
<template v-slot:trigger>
|
||||
<button
|
||||
class="btn button-default"
|
||||
:disabled="!tagPolicyEnabled"
|
||||
:title="tagPolicyEnabled ? '' : $t('moderation.reports.account.tag_policy_notice')"
|
||||
>
|
||||
<span>{{ $t("moderation.reports.tags") }}</span>
|
||||
{{ ' ' }}
|
||||
<FAIcon
|
||||
icon="chevron-down"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
<template v-slot:content="{close}">
|
||||
<div
|
||||
class="dropdown-menu"
|
||||
:disabled="!tagPolicyEnabled"
|
||||
>
|
||||
<button
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
@click.prevent="toggleTag(tags.FORCE_NSFW)"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_NSFW) }"
|
||||
/>
|
||||
{{ $t('user_card.admin_menu.force_nsfw') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
@click.prevent="toggleTag(tags.STRIP_MEDIA)"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': hasTag(tags.STRIP_MEDIA) }"
|
||||
/>
|
||||
{{ $t('user_card.admin_menu.strip_media') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
@click.prevent="toggleTag(tags.FORCE_UNLISTED)"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_UNLISTED) }"
|
||||
/>
|
||||
{{ $t('user_card.admin_menu.force_unlisted') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
@click.prevent="toggleTag(tags.SANDBOX)"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': hasTag(tags.SANDBOX) }"
|
||||
/>
|
||||
{{ $t('user_card.admin_menu.sandbox') }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</popover>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./report_card.js"></script>
|
37
src/components/mod_modal/tabs/reports_tab/report_note.js
Normal file
37
src/components/mod_modal/tabs/reports_tab/report_note.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
|
||||
import Timeago from 'src/components/timeago/timeago.vue'
|
||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||
|
||||
const ReportNote = {
|
||||
data () {
|
||||
return {
|
||||
showingDeleteDialog: false
|
||||
}
|
||||
},
|
||||
props: [
|
||||
'content',
|
||||
'created_at',
|
||||
'user',
|
||||
'report_id',
|
||||
'id'
|
||||
],
|
||||
components: {
|
||||
ConfirmModal,
|
||||
Timeago,
|
||||
UserAvatar
|
||||
},
|
||||
methods: {
|
||||
deleteNoteFromReport () {
|
||||
this.$store.dispatch('deleteNoteFromReport', { id: this.report_id, note: this.id })
|
||||
this.showingDeleteDialog = false
|
||||
},
|
||||
showDeleteDialog () {
|
||||
this.showingDeleteDialog = true
|
||||
},
|
||||
hideDeleteDialog () {
|
||||
this.showingDeleteDialog = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ReportNote
|
43
src/components/mod_modal/tabs/reports_tab/report_note.vue
Normal file
43
src/components/mod_modal/tabs/reports_tab/report_note.vue
Normal file
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<div class="report-note">
|
||||
<div class="note-header">
|
||||
<div class="note-author">
|
||||
<UserAvatar
|
||||
class="small-avatar"
|
||||
:user="user"
|
||||
/>
|
||||
{{ this.user.screen_name }}
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<Timeago
|
||||
class="faint"
|
||||
:time="created_at"
|
||||
:auto-update="60"
|
||||
:long-format="true"
|
||||
:with-direction="true"
|
||||
/>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click.stop="showDeleteDialog"
|
||||
>
|
||||
{{ $t('moderation.reports.delete_note') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note-content">
|
||||
{{ content }}
|
||||
</div>
|
||||
<confirm-modal
|
||||
v-if="showingDeleteDialog"
|
||||
:title="$t('moderation.reports.delete_note_title')"
|
||||
:confirm-text="$t('moderation.reports.delete_note_accept')"
|
||||
:cancel-text="$t('moderation.reports.delete_note_cancel')"
|
||||
@accepted="deleteNoteFromReport"
|
||||
@cancelled="hideDeleteDialog"
|
||||
>
|
||||
{{ $t('moderation.reports.delete_note_confirm') }}
|
||||
</confirm-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./report_note.js"></script>
|
26
src/components/mod_modal/tabs/reports_tab/reports_tab.js
Normal file
26
src/components/mod_modal/tabs/reports_tab/reports_tab.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { filter } from 'lodash'
|
||||
|
||||
import ReportCard from './report_card.vue'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
|
||||
const ReportsTab = {
|
||||
data () {
|
||||
return {
|
||||
showClosed: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Checkbox,
|
||||
ReportCard
|
||||
},
|
||||
computed: {
|
||||
reports () {
|
||||
return this.$store.state.reports.reports
|
||||
},
|
||||
openReports () {
|
||||
return filter(this.reports, { state: 'open' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ReportsTab
|
83
src/components/mod_modal/tabs/reports_tab/reports_tab.scss
Normal file
83
src/components/mod_modal/tabs/reports_tab/reports_tab.scss
Normal file
|
@ -0,0 +1,83 @@
|
|||
@import '../../../../_variables.scss';
|
||||
.report-card {
|
||||
.report-body {
|
||||
& > * {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
& > :not(:last-child) {
|
||||
border-bottom: 1px solid;
|
||||
border-bottom-color: var(--border, #222);
|
||||
}
|
||||
|
||||
.report-content {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.report-author {
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
.small-avatar {
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
padding-right: 0.4em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
|
||||
.dropdown-header {
|
||||
padding: 1em;
|
||||
color: var(--link, $fallback--link);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--selectedMenu, $fallback--lightBg);
|
||||
color: var(--selectedMenuText, $fallback--link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.report-note {
|
||||
padding: 1em;
|
||||
|
||||
.note-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.report-add-note {
|
||||
textarea {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
button {
|
||||
min-height: 2em;
|
||||
min-width: 10em;
|
||||
padding: 0 2em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel-footer {
|
||||
display: flex;
|
||||
& > * {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reports-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
25
src/components/mod_modal/tabs/reports_tab/reports_tab.vue
Normal file
25
src/components/mod_modal/tabs/reports_tab/reports_tab.vue
Normal file
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<div :label="$t('moderation.reports.reports')">
|
||||
<div class="content">
|
||||
<div class="reports-header">
|
||||
<h2>{{ $t('moderation.reports.reports') }}</h2>
|
||||
<Checkbox v-model="showClosed">
|
||||
{{ $t('moderation.reports.show_closed') }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div class="reports">
|
||||
<div v-if="(openReports.length === 0 && !showClosed) || reports.length === 0">
|
||||
<p>{{ $t('moderation.reports.no_reports') }}</p>
|
||||
</div>
|
||||
<ReportCard
|
||||
v-for="report in (showClosed ? reports : openReports)"
|
||||
:key="report.id"
|
||||
v-bind="report"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./reports_tab.js"></script>
|
||||
<style src="./reports_tab.scss" lang="scss"></style>
|
|
@ -148,7 +148,7 @@ const PostStatusForm = {
|
|||
spoilerText: this.subject || '',
|
||||
status: this.statusText || '',
|
||||
sensitiveIfSubject,
|
||||
nsfw: this.statusIsSensitive || !!sensitiveByDefault,
|
||||
nsfw: this.statusIsSensitive || (sensitiveIfSubject && this.subject) || !!sensitiveByDefault,
|
||||
files: this.statusFiles || [],
|
||||
poll: this.statusPoll || {},
|
||||
mediaDescriptions: this.statusMediaDescriptions || {},
|
||||
|
@ -181,9 +181,6 @@ const PostStatusForm = {
|
|||
userDefaultScope () {
|
||||
return this.$store.state.users.currentUser.default_scope
|
||||
},
|
||||
showAllScopes () {
|
||||
return !this.mergedConfig.minimalScopesMode
|
||||
},
|
||||
emojiUserSuggestor () {
|
||||
return suggestor({
|
||||
emoji: [
|
||||
|
@ -225,9 +222,6 @@ const PostStatusForm = {
|
|||
isOverLengthLimit () {
|
||||
return this.hasStatusLengthLimit && (this.charactersLeft < 0)
|
||||
},
|
||||
minimalScopesMode () {
|
||||
return this.$store.state.instance.minimalScopesMode
|
||||
},
|
||||
alwaysShowSubject () {
|
||||
return this.mergedConfig.alwaysShowSubjectInput
|
||||
},
|
||||
|
@ -419,7 +413,7 @@ const PostStatusForm = {
|
|||
this.$emit('resize')
|
||||
this.newStatus.files.push(fileInfo)
|
||||
|
||||
if (this.newStatus.sensitiveIfSubject && this.newStatus.spoilerText !== '') {
|
||||
if (this.$store.getters.mergedConfig.sensitiveIfSubject && this.newStatus.spoilerText !== '') {
|
||||
this.newStatus.nsfw = true
|
||||
}
|
||||
this.$emit('resize', { delayed: true })
|
||||
|
@ -500,7 +494,7 @@ const PostStatusForm = {
|
|||
})
|
||||
},
|
||||
onSubjectInput (e) {
|
||||
if (this.newStatus.sensitiveIfSubject) {
|
||||
if (this.$store.getters.mergedConfig.sensitiveIfSubject) {
|
||||
this.newStatus.nsfw = true
|
||||
}
|
||||
},
|
||||
|
@ -649,10 +643,8 @@ const PostStatusForm = {
|
|||
if (this.copyMessageScope === 'direct') {
|
||||
return this.copyMessageScope
|
||||
}
|
||||
if (this.$store.getters.mergedConfig.scopeCopy) {
|
||||
if (this.copyMessageScope !== 'public' && this.$store.state.users.currentUser.default_scope !== 'private') {
|
||||
return this.copyMessageScope
|
||||
}
|
||||
if (this.copyMessageScope !== 'public' && this.$store.state.users.currentUser.default_scope !== 'private') {
|
||||
return this.copyMessageScope
|
||||
}
|
||||
}
|
||||
return this.$store.state.users.currentUser.default_scope
|
||||
|
|
|
@ -188,7 +188,6 @@
|
|||
>
|
||||
<scope-selector
|
||||
v-if="!disableVisibilitySelector"
|
||||
:show-all="showAllScopes"
|
||||
:user-default="userDefaultScope"
|
||||
:original-scope="copyMessageScope"
|
||||
:initial-scope="newStatus.visibility"
|
||||
|
|
|
@ -123,51 +123,6 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
const renderMisskeyMarkdown = (content) => {
|
||||
// Untangle code blocks from <br> tags and other html encodings
|
||||
const codeblocks = content.match(/(<br\/>)?(~~~|```)\w*<br\/>.+?<br\/>\2\1?/g)
|
||||
if (codeblocks) {
|
||||
codeblocks.forEach((pre) => {
|
||||
content = content.replace(pre,
|
||||
pre.replaceAll('<br/>', '\n')
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>')
|
||||
.replaceAll('"', '"')
|
||||
.replaceAll(''', "'")
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
marked.use(markedMfm, {
|
||||
mangle: false,
|
||||
gfm: false,
|
||||
breaks: true
|
||||
})
|
||||
const mfmHtml = document.createElement('template')
|
||||
mfmHtml.innerHTML = marked.parse(content)
|
||||
|
||||
// Add options with set values to CSS
|
||||
if (mfmHtml.content.firstChild) {
|
||||
Array.from(mfmHtml.content.firstChild.getElementsByClassName('mfm')).map((el) => {
|
||||
if (el.dataset.speed) {
|
||||
el.style.animationDuration = el.dataset.speed
|
||||
}
|
||||
if (el.dataset.deg) {
|
||||
el.style.transform = `rotate(${el.dataset.deg}deg)`
|
||||
}
|
||||
if (Array.from(el.classList).includes('_mfm_font_')) {
|
||||
const font = Object.keys(el.dataset)[0]
|
||||
if (['serif', 'monospace', 'cursive', 'fantasy', 'emoji', 'math'].includes(font)) {
|
||||
el.style.fontFamily = font
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return mfmHtml.innerHTML
|
||||
}
|
||||
|
||||
// Processor to use with html_tree_converter
|
||||
const processItem = (item, index, array, what) => {
|
||||
// Handle text nodes - just add emoji
|
||||
|
@ -305,7 +260,7 @@ export default {
|
|||
return item
|
||||
}
|
||||
|
||||
const pass1 = convertHtmlToTree(this.mfm ? renderMisskeyMarkdown(html) : html).map(processItem)
|
||||
const pass1 = convertHtmlToTree(html).map(processItem)
|
||||
const pass2 = [...pass1].reverse().map(processItemReverse).reverse()
|
||||
// DO NOT USE SLOTS they cause a re-render feedback loop here.
|
||||
// slots updated -> rerender -> emit -> update up the tree -> rerender -> ...
|
||||
|
|
|
@ -13,6 +13,14 @@ library.add(
|
|||
faLockOpen
|
||||
)
|
||||
|
||||
const SCOPE_LEVELS = {
|
||||
'direct': 0,
|
||||
'private': 1,
|
||||
'local': 2,
|
||||
'unlisted': 2,
|
||||
'public': 3
|
||||
}
|
||||
|
||||
const ScopeSelector = {
|
||||
props: [
|
||||
'showAll',
|
||||
|
@ -57,11 +65,15 @@ const ScopeSelector = {
|
|||
},
|
||||
methods: {
|
||||
shouldShow (scope) {
|
||||
return this.showAll ||
|
||||
this.currentScope === scope ||
|
||||
this.originalScope === scope ||
|
||||
this.userDefault === scope ||
|
||||
scope === 'direct'
|
||||
if (!this.originalScope) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (this.originalScope === 'local') {
|
||||
return scope === 'direct' || scope === 'local'
|
||||
}
|
||||
|
||||
return SCOPE_LEVELS[scope] <= SCOPE_LEVELS[this.originalScope]
|
||||
},
|
||||
changeVis (scope) {
|
||||
this.currentScope = scope
|
||||
|
|
|
@ -32,6 +32,7 @@ const SearchBar = {
|
|||
this.$emit('toggled', this.hidden)
|
||||
this.$nextTick(() => {
|
||||
if (!this.hidden) {
|
||||
this.searchTerm = undefined
|
||||
this.$refs.searchInput.focus()
|
||||
}
|
||||
})
|
||||
|
|
|
@ -175,7 +175,6 @@ const SettingsModal = {
|
|||
return this.$store.state.config.expertLevel > 0
|
||||
},
|
||||
set (value) {
|
||||
console.log(value)
|
||||
this.$store.dispatch('setOption', { name: 'expertLevel', value: value ? 1 : 0 })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { filter, trim } from 'lodash'
|
||||
import { filter, trim, debounce } from 'lodash'
|
||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||
|
@ -27,13 +27,13 @@ const FilteringTab = {
|
|||
get () {
|
||||
return this.muteWordsStringLocal
|
||||
},
|
||||
set (value) {
|
||||
set: debounce(function (value) {
|
||||
this.muteWordsStringLocal = value
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'muteWords',
|
||||
value: filter(value.split('\n'), (word) => trim(word).length > 0)
|
||||
})
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
// Updating nested properties
|
||||
|
|
|
@ -105,8 +105,12 @@ const GeneralTab = {
|
|||
return this.$store.getters.mergedConfig.profileVersion
|
||||
},
|
||||
translationLanguages () {
|
||||
const langs = this.$store.state.instance.translationLanguages || []
|
||||
return (langs || []).map(lang => ({ key: lang.code, value: lang.code, label: lang.name }))
|
||||
const langs = this.$store.state.instance.supportedTranslationLanguages
|
||||
if (langs && langs.source) {
|
||||
return langs.source.map(lang => ({ key: lang.code, value: lang.code, label: lang.name }))
|
||||
}
|
||||
|
||||
return []
|
||||
},
|
||||
translationLanguage: {
|
||||
get: function () { return this.$store.getters.mergedConfig.translationLanguage },
|
||||
|
|
|
@ -567,18 +567,12 @@
|
|||
{{ $t('settings.default_vis') }} <ServerSideIndicator :server-side="true" />
|
||||
<ScopeSelector
|
||||
class="scope-selector"
|
||||
:show-all="true"
|
||||
:user-default="serverSide_defaultScope"
|
||||
:initial-scope="serverSide_defaultScope"
|
||||
:on-scope-change="changeDefaultScope"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="minimalScopesMode">
|
||||
{{ $t('settings.minimal_scopes_mode') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="sensitiveByDefault">
|
||||
{{ $t('settings.sensitive_by_default') }}
|
||||
|
@ -589,14 +583,6 @@
|
|||
{{ $t('settings.sensitive_if_subject') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="scopeCopy"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.scope_copy') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="alwaysShowSubjectInput"
|
||||
|
@ -624,14 +610,6 @@
|
|||
{{ $t('settings.post_status_content_type') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="minimalScopesMode"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.minimal_scopes_mode') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="alwaysShowNewPostButton"
|
||||
|
|
|
@ -151,7 +151,7 @@ const ProfileTab = {
|
|||
return false
|
||||
},
|
||||
deleteField (index, event) {
|
||||
this.$delete(this.newFields, index)
|
||||
this.newFields.splice(index, 1)
|
||||
},
|
||||
uploadFile (slot, e) {
|
||||
const file = e.target.files[0]
|
||||
|
|
|
@ -753,7 +753,6 @@ export default {
|
|||
selected () {
|
||||
this.selectedTheme = Object.entries(this.availableStyles).find(([k, s]) => {
|
||||
if (Array.isArray(s)) {
|
||||
console.log(s[0] === this.selected, this.selected)
|
||||
return s[0] === this.selected
|
||||
} else {
|
||||
return s.name === this.selected
|
||||
|
|
|
@ -15,7 +15,8 @@ import {
|
|||
faTachometerAlt,
|
||||
faCog,
|
||||
faInfoCircle,
|
||||
faList
|
||||
faList,
|
||||
faUserTie
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
|
@ -30,7 +31,8 @@ library.add(
|
|||
faTachometerAlt,
|
||||
faCog,
|
||||
faInfoCircle,
|
||||
faList
|
||||
faList,
|
||||
faUserTie
|
||||
)
|
||||
|
||||
const SideDrawer = {
|
||||
|
@ -102,6 +104,9 @@ const SideDrawer = {
|
|||
},
|
||||
openSettingsModal () {
|
||||
this.$store.dispatch('openSettingsModal')
|
||||
},
|
||||
openModModal () {
|
||||
this.$store.dispatch('openModModal')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,6 +143,21 @@
|
|||
/> {{ $t("nav.about") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser && currentUser.role === 'admin' || currentUser.role === 'moderator'"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
<button
|
||||
class="button-unstyled -link -fullwidth"
|
||||
@click="openModModal"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
icon="user-tie"
|
||||
/> {{ $t("nav.moderation") }}
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser && currentUser.role === 'admin'"
|
||||
@click="toggleDrawer"
|
||||
|
|
|
@ -460,6 +460,16 @@ const Status = {
|
|||
return 'globe'
|
||||
}
|
||||
},
|
||||
faviconAlt (status) {
|
||||
if (!status.user.instance) {
|
||||
return ''
|
||||
}
|
||||
const software = ((status.user.instance) && (status.user.instance.nodeinfo) && (status.user.instance.nodeinfo.software)) || {}
|
||||
if (software.name) {
|
||||
return `${status.user.instance.name} (${software.name || ''} ${software.version || ''})`
|
||||
}
|
||||
return ''
|
||||
},
|
||||
showError (error) {
|
||||
this.error = error
|
||||
},
|
||||
|
|
|
@ -177,6 +177,7 @@
|
|||
v-if="!!(status.user && status.user.favicon)"
|
||||
class="status-favicon"
|
||||
:src="status.user.favicon"
|
||||
:title="faviconAlt(status)"
|
||||
>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div
|
||||
class="StatusBody"
|
||||
:class="{ '-compact': compact }"
|
||||
:class="{ '-compact': compact, 'mfm-disabled': !renderMisskeyMarkdown }"
|
||||
>
|
||||
<div class="body">
|
||||
<div
|
||||
|
|
|
@ -82,9 +82,16 @@
|
|||
}
|
||||
}
|
||||
&.mfm-disabled {
|
||||
span {
|
||||
font-size: 100% !important;
|
||||
}
|
||||
.mfm {
|
||||
animation: none !important;
|
||||
}
|
||||
.emoji {
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,8 +64,12 @@ export default {
|
|||
settingsModalVisible () {
|
||||
return this.settingsModalState === 'visible'
|
||||
},
|
||||
modModalVisible () {
|
||||
return this.modModalState === 'visible'
|
||||
},
|
||||
...mapState({
|
||||
settingsModalState: state => state.interface.settingsModalState
|
||||
settingsModalState: state => state.interface.settingsModalState,
|
||||
modModalState: state => state.interface.modModalState
|
||||
})
|
||||
},
|
||||
beforeUpdate () {
|
||||
|
|
|
@ -126,7 +126,7 @@ const Timeline = {
|
|||
this.$store.commit('setLoading', { timeline: this.timelineName, value: false })
|
||||
},
|
||||
methods: {
|
||||
stopBlockingClicks: debounce(function () {
|
||||
stopBlockingClicks: debounce( function() {
|
||||
this.blockingClicks = false
|
||||
}, 1000),
|
||||
blockClicksTemporarily () {
|
||||
|
@ -154,7 +154,7 @@ const Timeline = {
|
|||
window.scrollTo({ top: 0 })
|
||||
}
|
||||
},
|
||||
fetchOlderStatuses: throttle(function () {
|
||||
fetchOlderStatuses: throttle( function () {
|
||||
const store = this.$store
|
||||
const credentials = store.state.users.currentUser.credentials
|
||||
store.commit('setLoading', { timeline: this.timelineName, value: true })
|
||||
|
@ -188,7 +188,7 @@ const Timeline = {
|
|||
|
||||
const centerOfScreen = window.pageYOffset + (window.innerHeight * 0.5)
|
||||
|
||||
// Start from approximating the index of some visible status by using the
|
||||
// Start from approximating the index of some visible status by using
|
||||
// the center of the screen on the timeline.
|
||||
let approxIndex = Math.floor(statuses.length * (centerOfScreen / height))
|
||||
let err = statuses[approxIndex].getBoundingClientRect().y
|
||||
|
@ -226,7 +226,7 @@ const Timeline = {
|
|||
this.fetchOlderStatuses()
|
||||
}
|
||||
},
|
||||
handleScroll: throttle(function (e) {
|
||||
handleScroll: throttle( function (e) {
|
||||
this.determineVisibleStatuses()
|
||||
this.scrollLoad(e)
|
||||
}, 200),
|
||||
|
|
|
@ -71,7 +71,6 @@ export default {
|
|||
return `${serverUrl.protocol}//${serverUrl.host}/main/ostatus`
|
||||
},
|
||||
loggedIn () {
|
||||
console.log({ ...this.$store.state.users.currentUser })
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
dailyAvg () {
|
||||
|
|
|
@ -235,7 +235,7 @@
|
|||
line-height: 22px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.following {
|
||||
.following, .requested_by {
|
||||
flex: 1 0 auto;
|
||||
margin: 0;
|
||||
margin-bottom: .25em;
|
||||
|
|
|
@ -122,6 +122,12 @@
|
|||
>
|
||||
{{ $t('user_card.follows_you') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="relationship.requested_by && loggedIn && isOtherUser"
|
||||
class="requested_by"
|
||||
>
|
||||
{{ $t('user_card.requested_by') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="isOtherUser && (loggedIn || !switcher)"
|
||||
class="highlighter"
|
||||
|
|
182
src/i18n/ca.json
182
src/i18n/ca.json
|
@ -164,6 +164,74 @@
|
|||
"load_older": "Carrega interaccions més antigues",
|
||||
"moves": "Migracions d'usuari"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Àrab",
|
||||
"az": "Azerbaidjanès",
|
||||
"bg": "Búlgar",
|
||||
"cs": "Txec",
|
||||
"da": "Danès",
|
||||
"de": "Alemany",
|
||||
"el": "Grec",
|
||||
"en": "Anglès",
|
||||
"eo": "Esperanto",
|
||||
"es": "Espanyol",
|
||||
"fa": "Persa",
|
||||
"fi": "Finès",
|
||||
"fr": "Francès",
|
||||
"ga": "Irlandès",
|
||||
"he": "Hebreu",
|
||||
"hi": "Indi",
|
||||
"hu": "Hungarès",
|
||||
"id": "Indonesi",
|
||||
"it": "Italià",
|
||||
"ja": "Japonès",
|
||||
"ko": "Coreà",
|
||||
"lt": "Lituà",
|
||||
"lv": "Latvià",
|
||||
"nl": "Holandès",
|
||||
"pl": "Polonès",
|
||||
"pt": "Portuguès",
|
||||
"ru": "Rus",
|
||||
"sk": "Eslovè",
|
||||
"sv": "Suec",
|
||||
"tr": "Turc",
|
||||
"translated_from": {
|
||||
"ar": "Traduït de @:languages.ar",
|
||||
"az": "Traduït de @:languages.az",
|
||||
"bg": "Traduït de @:languages.bg",
|
||||
"cs": "Traduït de @:languages.cs",
|
||||
"da": "Traduït de @:languages.da",
|
||||
"de": "Traduït de @:languages.de",
|
||||
"el": "Traduït de @:languages.el",
|
||||
"en": "Traduït de @:languages.en",
|
||||
"eo": "Traduït de @:languages.eo",
|
||||
"es": "Traduït de @:languages.es",
|
||||
"fa": "Traduït de @:languages.fa",
|
||||
"fi": "Traduït de @:languages.en",
|
||||
"fr": "Traduït de @:languages.fr",
|
||||
"ga": "Traduït de @:languages.ga",
|
||||
"he": "Traduït de @:languages.he",
|
||||
"hi": "Traduït de @:languages.hi",
|
||||
"hu": "Traduït de @:languages.hu",
|
||||
"id": "Traduït de @:languages.id",
|
||||
"it": "Traduït de @:languages.it",
|
||||
"ja": "Traduït de @:languages.ja",
|
||||
"ko": "Traduït de @:languages.ko",
|
||||
"lt": "Traduït de @:languages.lt",
|
||||
"lv": "Traduït de @:languages.lv",
|
||||
"nl": "Traduït de @:languages.nl",
|
||||
"pl": "Traduït de @:languages.pl",
|
||||
"pt": "Traduït de @:languages.pt",
|
||||
"ru": "Traduït de @:languages.ru",
|
||||
"sk": "Traduït de @:languages.sk",
|
||||
"sv": "Traduït de @:languages.sv",
|
||||
"tr": "Traduït de @:languages.fr",
|
||||
"uk": "Traduït de @:languages.uk",
|
||||
"zh": "Traduït de @:languages.zh"
|
||||
},
|
||||
"uk": "Ucraïnès",
|
||||
"zh": "Xinès"
|
||||
},
|
||||
"lists": {
|
||||
"create": "Crea",
|
||||
"delete": "Esborra la llista",
|
||||
|
@ -279,10 +347,13 @@
|
|||
"text/plain": "Text pla",
|
||||
"text/x.misskeymarkdown": "MFM"
|
||||
},
|
||||
"content_warning": "Assumpte (opcional)",
|
||||
"content_warning": "Avís de contingut (opcional)",
|
||||
"default": "Just ara he arribat a Catalunya",
|
||||
"direct_warning_to_all": "Aquest apunt serà visible per a tots els usuaris mencionats.",
|
||||
"direct_warning_to_first_only": "Aquest apunt només serà visible per als usuaris mencionats al principi del missatge.",
|
||||
"edit_remote_warning": "Els canvis fets en aquest apunt poden no ser visibles en algunes instàncies!",
|
||||
"edit_status": "Edita l'apunt",
|
||||
"edit_unsupported_warning": "Les enquestes i les mencions no es canviaran al editar.",
|
||||
"empty_status_error": "No es pot enviar un apunt buit i sense fitxers adjunts",
|
||||
"media_description": "Descripció multimèdia",
|
||||
"media_description_error": "Ha fallat la pujada del Mèdia, prova de nou",
|
||||
|
@ -313,7 +384,7 @@
|
|||
"email": "Adreça de Correu",
|
||||
"email_language": "En quina llengua vols rebre els correus del servidor?",
|
||||
"fullname": "Nom a mostrar",
|
||||
"fullname_placeholder": "p. ex. Lain Iwakura",
|
||||
"fullname_placeholder": "p. ex. Atsuko Kagari",
|
||||
"new_captcha": "Clica a la imatge per a obtenir un nou captcha",
|
||||
"password_confirm": "Confirma la contrasenya",
|
||||
"reason": "Raó per a registrar-se",
|
||||
|
@ -321,7 +392,7 @@
|
|||
"register": "Registre",
|
||||
"registration": "Registre",
|
||||
"token": "Codi d'invitació",
|
||||
"username_placeholder": "p. ex. lain",
|
||||
"username_placeholder": "p. ex. akko",
|
||||
"validations": {
|
||||
"email_required": "no es pot deixar en blanc",
|
||||
"fullname_required": "no es pot deixar en blanc",
|
||||
|
@ -392,9 +463,19 @@
|
|||
"changed_password": "S'ha canviat la contrasenya correctament!",
|
||||
"chatMessageRadius": "Missatge de xat",
|
||||
"checkboxRadius": "Caselles",
|
||||
"collapse_subject": "Replega els apunts amb assumpte",
|
||||
"collapse_subject": "Replega els apunts amb avís de contingut",
|
||||
"columns": "Columnes",
|
||||
"composing": "Composant",
|
||||
"confirm_dialogs": "Requereix confirmació per:",
|
||||
"confirm_dialogs_approve_follow": "Acceptant peticions de seguiment",
|
||||
"confirm_dialogs_block": "Bloquejant algú",
|
||||
"confirm_dialogs_delete": "Esborrant un apunt",
|
||||
"confirm_dialogs_deny_follow": "Rebutjant una sol·licitud de seguiment",
|
||||
"confirm_dialogs_mute": "Silenciant algú",
|
||||
"confirm_dialogs_repeat": "Repetint un apunt",
|
||||
"confirm_dialogs_unfollow": "Deixant de seguir a algú",
|
||||
"confirm_new_password": "Confirma la nova contrasenya",
|
||||
"confirmation_dialogs": "Opcions de confirmació",
|
||||
"conversation_display": "Estil de visualització de la conversa",
|
||||
"conversation_display_linear": "Estil linear",
|
||||
"conversation_display_tree": "Estil d'arbre",
|
||||
|
@ -426,8 +507,8 @@
|
|||
"backup_settings_theme": "Còpia de seguretat de la configuració i tema a un fitxer",
|
||||
"errors": {
|
||||
"file_slightly_new": "La versió menor del fitxer és diferent, alguns paràmetres podrien no carregar-se",
|
||||
"file_too_new": "Versió important incompatible: {fileMajor}, aquest PleromaFE (configuració versió {feMajor}) és massa antiga per gestionar-lo",
|
||||
"file_too_old": "Versió important incompatible: {fileMajor}, la versió del fitxer és massa antiga i no està suportada (min. set. ver. {feMajor})",
|
||||
"file_too_new": "Versió major incompatible: {fileMajor}, aquest PleromaFE (configuració versió {feMajor}) és massa antiga per gestionar-lo",
|
||||
"file_too_old": "Versió major incompatible: {fileMajor}, la versió del fitxer és massa antiga i no està suportada (min. set. ver. {feMajor})",
|
||||
"invalid_file": "El fitxer seleccionat no és suportat per Akkoma com a còpia de seguretat de la configuració. No s'ha realitzat cap canvi."
|
||||
},
|
||||
"restore_settings": "Restaurar configuració des d'un fitxer"
|
||||
|
@ -520,7 +601,7 @@
|
|||
"more_settings": "Més opcions",
|
||||
"move_account": "Mou el compte",
|
||||
"move_account_error": "Error al moure el compte: {error}",
|
||||
"move_account_notes": "Si vols moure el compte a un altre lloc has d'anar a aquest altre compte i afegir un àlies que apunti a aquest.",
|
||||
"move_account_notes": "Si vols moure aquest compte a un altre lloc, has d'anar a aquest altre compte i afegir un àlies que apunti a aquest.",
|
||||
"move_account_target": "Compte destí (p.ex. {example})",
|
||||
"moved_account": "El compte s'ha mogut.",
|
||||
"mute_bot_posts": "Silencia apunts de bots",
|
||||
|
@ -604,7 +685,7 @@
|
|||
"security": "Seguretat",
|
||||
"security_tab": "Seguretat",
|
||||
"sensitive_by_default": "Marca apunts com a sensibles per defecte",
|
||||
"sensitive_if_subject": "Marca automàticament les imatges com a sensibles si s'ha especificat la línia assumpte",
|
||||
"sensitive_if_subject": "Marca automàticament les imatges com a sensibles si s'ha especificat un avís de contingut",
|
||||
"set_new_avatar": "Establir un nou avatar",
|
||||
"set_new_mascot": "Establir una nova mascota",
|
||||
"set_new_profile_background": "Canvia el fons del perfil",
|
||||
|
@ -612,6 +693,19 @@
|
|||
"setting_changed": "La configuració és diferent a la predeterminada",
|
||||
"setting_server_side": "Aquest ajust està lligat al teu perfil i afectarà a totes les sessions i clients",
|
||||
"settings": "Configuració",
|
||||
"settings_profile": "Perfils de configuracions",
|
||||
"settings_profile_creation": "Crea nou perfil",
|
||||
"settings_profile_creation_new_name_label": "Nom",
|
||||
"settings_profile_creation_submit": "Crea",
|
||||
"settings_profile_currently": "Actualment usant {name} (versió: {version})",
|
||||
"settings_profile_delete": "Esborra",
|
||||
"settings_profile_delete_confirm": "Vols de debò esborrar aquest perfil?",
|
||||
"settings_profile_force_sync": "Sincronitza",
|
||||
"settings_profile_in_use": "En ús",
|
||||
"settings_profile_use": "Usa",
|
||||
"settings_profiles_refresh": "Recarrega els perfils de configuracions",
|
||||
"settings_profiles_show": "Mostra tots els perfils de configuracions",
|
||||
"settings_profiles_unshow": "Amaga tots els perfils de configuracions",
|
||||
"show_admin_badge": "Mostra l'insígnia \"Administrador\" en el meu perfil",
|
||||
"show_moderator_badge": "Mostra l'insígnia \"Moderador\" en el meu perfil",
|
||||
"show_nav_shortcuts": "Mostra els accessos directes addicionals en el panell superior",
|
||||
|
@ -767,9 +861,9 @@
|
|||
"use_source": "Nova versió"
|
||||
}
|
||||
},
|
||||
"subject_input_always_show": "Sempre mostrar el camp del assumpte",
|
||||
"subject_line_behavior": "Copiar l'assumpte en les respostes",
|
||||
"subject_line_email": "Com a l'email: \"re: assumpte\"",
|
||||
"subject_input_always_show": "Sempre mostra el camp d'avís de contingut",
|
||||
"subject_line_behavior": "Copiar l'avís de contingut en les respostes",
|
||||
"subject_line_email": "Com en el correu: \"re: avís\"",
|
||||
"subject_line_mastodon": "Com a mastodon: copiar com és",
|
||||
"subject_line_noop": "No copiar",
|
||||
"text": "Text",
|
||||
|
@ -783,7 +877,8 @@
|
|||
"third_column_mode_postform": "Formulari de publicació principal i navegació",
|
||||
"token": "Token",
|
||||
"tooltipRadius": "Globus/alertes",
|
||||
"tree_advanced": "Permet una navegació més flexible en la vista d'arbre",
|
||||
"translation_language": "Traducció Automàtica de la Llengua",
|
||||
"tree_advanced": "Mostra els botons extra per a obrir i tancar les cadenes en els fils",
|
||||
"tree_fade_ancestors": "Mostra els avantpassats del apunt actual en text dèbil",
|
||||
"type_domains_to_mute": "Buscar dominis per a silenciar",
|
||||
"upload_a_photo": "Pujar una foto",
|
||||
|
@ -793,6 +888,7 @@
|
|||
"use_contain_fit": "No retallar els adjunts en miniatures",
|
||||
"use_one_click_nsfw": "Obre els adjunts NSFW amb només un clic",
|
||||
"user_mutes": "Usuaris",
|
||||
"user_profile_default_tab": "Pestanya per defecte en el Perfil d'Usuari",
|
||||
"user_profiles": "Perfils d'usuari",
|
||||
"user_settings": "Configuració d'usuari",
|
||||
"valid_until": "Vàlid fins",
|
||||
|
@ -809,6 +905,12 @@
|
|||
"word_filter": "Filtre de paraules",
|
||||
"wordfilter": "Filtre de paraules"
|
||||
},
|
||||
"settings_profile": {
|
||||
"creating": "Creant nou perfil de configuracions \"{profile}\"...",
|
||||
"synchronization_error": "No s'han pogut sincronitzar les configuracions: {err}",
|
||||
"synchronized": "Configuracions sincronitzades!",
|
||||
"synchronizing": "Sincronitzant el perfil de configuració \"{profile}\"..."
|
||||
},
|
||||
"status": {
|
||||
"ancestor_follow": "Veure {numReplies} altres respostes sota aquest apunt | Veure {numReplies} altres respostes sota aquest apunt",
|
||||
"ancestor_follow_with_icon": "{icon} {text}",
|
||||
|
@ -818,12 +920,19 @@
|
|||
"copy_link": "Copia l'enllaç a l'apunt",
|
||||
"delete": "Esborra l'apunt",
|
||||
"delete_confirm": "Segur que vols esborrar aquest apunt?",
|
||||
"delete_confirm_accept_button": "Sí, esborra'l",
|
||||
"delete_confirm_cancel_button": "No, desa'l",
|
||||
"delete_confirm_title": "Confirma esborrat",
|
||||
"edit": "Edita",
|
||||
"edit_history": "Historial d'edició",
|
||||
"edit_history_modal_title": "Editat {historyCount} vegada | Editat {historyCount} vegades",
|
||||
"edited_at": "Editat {time}",
|
||||
"expand": "Expandeix",
|
||||
"external_source": "Font externa",
|
||||
"favorites": "Favorits",
|
||||
"hide_attachment": "Amaga l'adjunt",
|
||||
"hide_content": "Amaga el contingut",
|
||||
"hide_full_subject": "Amaga tot l'assumpte",
|
||||
"hide_full_subject": "Amaga tot l'avís de contingut",
|
||||
"many_attachments": "L'apunt té {number} adjunt | L'apunt té {number} adjunts",
|
||||
"mentions": "Menciona",
|
||||
"move_down": "Mou l'adjunt a la dreta",
|
||||
|
@ -831,32 +940,44 @@
|
|||
"mute_conversation": "Silencia la conversa",
|
||||
"nsfw": "No segur per a entorns laborals",
|
||||
"open_gallery": "Obre la galeria",
|
||||
"override_translation_source_language": "Anul·la la llengua d'origen",
|
||||
"pin": "Destaca al perfil",
|
||||
"pinned": "Destacat",
|
||||
"plus_more": "+{number} més",
|
||||
"redraft": "Esborra i torna a escriure",
|
||||
"redraft_confirm": "De debò vols esborrar i tornar a escriure aquest apunt? Les interaccions del apunt original no es mantindran.",
|
||||
"redraft_confirm_accept_button": "Sí, esborra i redacta de nou",
|
||||
"redraft_confirm_cancel_button": "No, manté l'original",
|
||||
"redraft_confirm_title": "Confirma esborra i re escriu",
|
||||
"remove_attachment": "Elimina l'adjunt",
|
||||
"repeat_confirm": "Vols de debò repetir aquest apunt?",
|
||||
"repeat_confirm_accept_button": "Sí, repeteix-lo",
|
||||
"repeat_confirm_cancel_button": "No, no el repeteixis",
|
||||
"repeat_confirm_title": "Confirma repetició",
|
||||
"repeats": "Repeticions",
|
||||
"replies_list": "Respostes:",
|
||||
"replies_list_with_others": "Respostes (+{numReplies} altre): | Respostes (+{numReplies} altres):",
|
||||
"replies_list_with_others": "Veure (+{numReplies} resposta més): | Veure (+{numReplies} respostes més):",
|
||||
"reply_to": "Respon a",
|
||||
"show_all_attachments": "Mostra tots els adjunts",
|
||||
"show_all_conversation": "Mostra la conversa sencera ({numStatus} altre apunt) | Mostra la conversa sencera ({numStatus} altres apunts)",
|
||||
"show_all_conversation_with_icon": "{icon} {text}",
|
||||
"show_attachment_description": "Descripció prèvia (obre l'adjunt per a descripció sencera)",
|
||||
"show_attachment_in_modal": "Mostra en el modal de Mèdia",
|
||||
"show_attachment_in_modal": "Mostra l'adjunt en un finestra",
|
||||
"show_content": "Mostra el contingut",
|
||||
"show_full_subject": "Mostra tot l'assumpte",
|
||||
"show_full_subject": "Mostra tot l'avís de contingut",
|
||||
"show_only_conversation_under_this": "Només mostra respostes a aquest apunt",
|
||||
"status_deleted": "Aquest apunt ha estat esborrat",
|
||||
"status_unavailable": "Apunt no disponible",
|
||||
"thread_follow": "Mira la part restant d'aquest fil ({numStatus} apunt en total) | Mira la part restant d'aquest fil ({numStatus} apunts en total)",
|
||||
"thread_follow": "Veure {numStatus} resposta més | Veure {numStatus} respostes més",
|
||||
"thread_follow_with_icon": "{icon} {text}",
|
||||
"thread_hide": "Amaga aquest fil",
|
||||
"thread_muted": "Fil silenciat",
|
||||
"thread_muted_and_words": ", té les paraules:",
|
||||
"thread_show": "Mostra aquest fil",
|
||||
"thread_show_full": "Mostra-ho tot sota aquest fil ({numStatus} apunt en total, màx. profunditat {depth}) | Mostra-ho tot sota aquest fil ({numStatus} apunts en total, màx. profunditat {depth})",
|
||||
"thread_show_full": "Mostra {numStatus} resposta | Mostra totes {numStatus} respostes",
|
||||
"thread_show_full_with_icon": "{icon} {text}",
|
||||
"translate": "Tradueix",
|
||||
"translated_from": "Tradueix des de {language}",
|
||||
"unbookmark": "Desmarca",
|
||||
"unmute_conversation": "Deixa de silenciar la conversa",
|
||||
"unpin": "Deixa de destacar al perfil",
|
||||
|
@ -899,6 +1020,9 @@
|
|||
"socket_reconnected": "Connexió a temps real establerta",
|
||||
"up_to_date": "Actualitzat"
|
||||
},
|
||||
"toast": {
|
||||
"no_translation_target_set": "No s'ha configurat una llengua objectiu -això podria fallar. Si us plau configura una llengua objectiu en la teva configuració."
|
||||
},
|
||||
"tool_tip": {
|
||||
"accept_follow_request": "Accepta la sol·licitud de seguiment",
|
||||
"add_reaction": "Reacciona",
|
||||
|
@ -947,12 +1071,24 @@
|
|||
"strip_media": "Esborra els Mèdia dels apunts"
|
||||
},
|
||||
"approve": "Aprova",
|
||||
"approve_confirm": "Estàs segur que vols deixar que et segueixi aquest usuari?",
|
||||
"approve_confirm_accept_button": "Sí, accepta",
|
||||
"approve_confirm_cancel_button": "No, cancel·la",
|
||||
"approve_confirm_title": "Aprova la sol·licitud de seguiment",
|
||||
"block": "Bloqueja",
|
||||
"block_confirm": "Estàs segur que vols bloquejar a {user}?",
|
||||
"block_confirm_accept_button": "Sí, bloqueja'l",
|
||||
"block_confirm_cancel_button": "No, no el bloquegis",
|
||||
"block_confirm_title": "Bloqueja l'usuari",
|
||||
"block_progress": "Bloquejant…",
|
||||
"blocked": "Bloquejat!",
|
||||
"bot": "Bot",
|
||||
"deactivated": "Desactivat",
|
||||
"deny": "Denega",
|
||||
"deny_confirm": "Estàs segur que vols denegar la sol·licitud de seguiment d'aquest usuari?",
|
||||
"deny_confirm_accept_button": "Sí, denega",
|
||||
"deny_confirm_cancel_button": "No, cancel·la",
|
||||
"deny_confirm_title": "Denega sol·licitud de seguiment",
|
||||
"domain_muted": "Desbloqueja el domini",
|
||||
"edit_profile": "Edita el perfil",
|
||||
"favorites": "Favorits",
|
||||
|
@ -978,18 +1114,28 @@
|
|||
"mention": "Menció",
|
||||
"message": "Missatge",
|
||||
"mute": "Silencia",
|
||||
"mute_confirm": "Estàs segur que vols silenciar a {user}?",
|
||||
"mute_confirm_accept_button": "Sí, silencia'l",
|
||||
"mute_confirm_cancel_button": "No, no el silenciïs",
|
||||
"mute_confirm_title": "Silencia usuari",
|
||||
"mute_domain": "Bloqueja el domini",
|
||||
"mute_progress": "Silenciant…",
|
||||
"muted": "Silenciat",
|
||||
"note": "Nota privada",
|
||||
"per_day": "per dia",
|
||||
"remote_follow": "Seguiment remot",
|
||||
"remove_follower": "Esborra seguidor",
|
||||
"replies": "Amb respostes",
|
||||
"report": "Informa",
|
||||
"show_repeats": "Mostra les repeticions",
|
||||
"statuses": "Apunts",
|
||||
"subscribe": "Subscriu-te",
|
||||
"unblock": "Desbloqueja",
|
||||
"unblock_progress": "Desbloquejant…",
|
||||
"unfollow_confirm": "Estàs segur que vols deixar de seguir a {user}?",
|
||||
"unfollow_confirm_accept_button": "Sí, deixa'l de seguir",
|
||||
"unfollow_confirm_cancel_button": "No, no el deixis de seguir",
|
||||
"unfollow_confirm_title": "Deixa de seguir l'usuari",
|
||||
"unmute": "Deixa de silenciar",
|
||||
"unmute_progress": "Deixant de silenciar…",
|
||||
"unsubscribe": "Anul·la la subscripció"
|
||||
|
|
|
@ -258,7 +258,11 @@
|
|||
"placeholder": "myusername",
|
||||
"recovery_code": "Recovery code",
|
||||
"register": "Register",
|
||||
"username": "Username"
|
||||
"username": "Username",
|
||||
"logout_confirm_cancel_button": "Cancel",
|
||||
"logout_confirm_accept_button": "Log out",
|
||||
"logout_confirm": "Are you sure you want to log out?",
|
||||
"logout_confirm_title": "Log out"
|
||||
},
|
||||
"media_modal": {
|
||||
"counter": "{current} / {total}",
|
||||
|
@ -266,6 +270,32 @@
|
|||
"next": "Next",
|
||||
"previous": "Previous"
|
||||
},
|
||||
"moderation": {
|
||||
"moderation": "Moderation",
|
||||
"reports": {
|
||||
"no_reports": "No reports to show",
|
||||
"add_note": "Add note",
|
||||
"close": "Close",
|
||||
"delete_note": "Delete",
|
||||
"delete_note_accept": "Yes, delete it",
|
||||
"delete_note_cancel": "No, keep it",
|
||||
"delete_note_confirm": "Are you sure you want to delete this note?",
|
||||
"delete_note_title": "Confirm deletion",
|
||||
"no_content": "No description given",
|
||||
"note_placeholder": "Leave a note...",
|
||||
"notes": "{ count } note | { count } notes",
|
||||
"reopen": "Reopen",
|
||||
"report": "Report on",
|
||||
"reports": "Reports",
|
||||
"resolve": "Resolve",
|
||||
"show_closed": "Show closed",
|
||||
"statuses": "{ count } status | { count } statuses",
|
||||
"tag_policy_notice": "Enable the TagPolicy MRF to set post restrictions",
|
||||
"tags": "Set post restrictions"
|
||||
},
|
||||
"statuses": "Statuses",
|
||||
"users": "Users"
|
||||
},
|
||||
"nav": {
|
||||
"about": "About",
|
||||
"administration": "Administration",
|
||||
|
@ -282,6 +312,7 @@
|
|||
"interactions": "Interactions",
|
||||
"lists": "Lists",
|
||||
"mentions": "Mentions",
|
||||
"moderation": "Moderation",
|
||||
"preferences": "Preferences",
|
||||
"public_timeline_description": "Public posts from this instance",
|
||||
"public_tl": "Public timeline",
|
||||
|
@ -1101,6 +1132,7 @@
|
|||
"followers": "Followers",
|
||||
"following": "Following!",
|
||||
"follows_you": "Follows you!",
|
||||
"requested_by": "Has requested to follow you",
|
||||
"hidden": "Hidden",
|
||||
"hide_repeats": "Hide repeats",
|
||||
"highlight": {
|
||||
|
|
183
src/i18n/es.json
183
src/i18n/es.json
|
@ -164,9 +164,78 @@
|
|||
"load_older": "Cargar interacciones más antiguas",
|
||||
"moves": "Usuario migrado"
|
||||
},
|
||||
"languages": {
|
||||
"ar": "Árabe",
|
||||
"az": "Azerbaiyán",
|
||||
"bg": "Búlgaro",
|
||||
"cs": "Checo",
|
||||
"da": "Danés",
|
||||
"de": "Alemán",
|
||||
"el": "Griego",
|
||||
"en": "Inglés",
|
||||
"eo": "Esperanto",
|
||||
"es": "Español",
|
||||
"fa": "Persa",
|
||||
"fi": "Finlandés",
|
||||
"fr": "Francés",
|
||||
"ga": "Irlandés",
|
||||
"he": "Hebreo",
|
||||
"hi": "Hindi",
|
||||
"hu": "Húngaro",
|
||||
"id": "Indonesio",
|
||||
"it": "Italiano",
|
||||
"ja": "Japonés",
|
||||
"ko": "Coreano",
|
||||
"lt": "Lituano",
|
||||
"lv": "Letón",
|
||||
"nl": "Alemán",
|
||||
"pl": "Polaco",
|
||||
"pt": "Portugués",
|
||||
"ru": "Ruso",
|
||||
"sk": "Eslovaco",
|
||||
"sv": "Sueco",
|
||||
"tr": "Turco",
|
||||
"translated_from": {
|
||||
"ar": "Traducido del @:languages.ar",
|
||||
"az": "Traducido del @:languages.az",
|
||||
"bg": "Traducido del @:languages.bg",
|
||||
"cs": "Traducido del @:languages.cs",
|
||||
"da": "Traducido del @:languages.da",
|
||||
"de": "Traducido del @:languages.de",
|
||||
"el": "Traducido del @:languages.el",
|
||||
"en": "Traducido del @:languages.en",
|
||||
"eo": "Traducido del @:languages.eo",
|
||||
"es": "Traducido del @:languages.es",
|
||||
"fa": "Traducido del @:languages.fa",
|
||||
"fi": "Traducido del @:languages.fi",
|
||||
"fr": "Traducido del @:languages.fr",
|
||||
"ga": "Traducido del @:languages.ga",
|
||||
"he": "Traducido del @:languages.he",
|
||||
"hi": "Traducido del @:languages.hi",
|
||||
"hu": "Traducido del @:languages.hu",
|
||||
"id": "Traducido del @:languages.id",
|
||||
"it": "Traducido del @:languages.it",
|
||||
"ja": "Traducido del @:languages.ja",
|
||||
"ko": "Traducido del @:languages.ko",
|
||||
"lt": "Traducido del @:languages.it",
|
||||
"lv": "Traducido del @:languages.lv",
|
||||
"nl": "Traducido del @:languages.nl",
|
||||
"pl": "Traducido del @:languages.pl",
|
||||
"pt": "Traducido del @:languages.pt",
|
||||
"ru": "Traducido del @:languages.ru",
|
||||
"sk": "Traducido del @:languages.sk",
|
||||
"sv": "Traducido del @:languages.sv",
|
||||
"tr": "Traducido del @:languages.tr",
|
||||
"uk": "Traducido del @:languages.uk",
|
||||
"zh": "Traducido del @:languages.zh"
|
||||
},
|
||||
"uk": "Ucraniano",
|
||||
"zh": "Chino"
|
||||
},
|
||||
"lists": {
|
||||
"create": "Crear",
|
||||
"delete": "Eliminar lista",
|
||||
"following_only": "Limitar a seguidores",
|
||||
"lists": "Listas",
|
||||
"new": "Nueva Lista",
|
||||
"save": "Guardar cambios",
|
||||
|
@ -186,28 +255,59 @@
|
|||
"login": "Identificarse",
|
||||
"logout": "Cerrar sesión",
|
||||
"password": "Contraseña",
|
||||
"placeholder": "p.ej. lain",
|
||||
"placeholder": "miusuario",
|
||||
"recovery_code": "Código de recuperación",
|
||||
"register": "Registrarse",
|
||||
"username": "Usuario"
|
||||
},
|
||||
"media_modal": {
|
||||
"counter": "{current} / {total}",
|
||||
"hide": "Cerrar visor de medios",
|
||||
"next": "Siguiente",
|
||||
"previous": "Anterior"
|
||||
},
|
||||
"moderation": {
|
||||
"moderation": "Moderación",
|
||||
"reports": {
|
||||
"add_note": "Añadir nota",
|
||||
"close": "Cerrar",
|
||||
"delete_note": "Eliminar",
|
||||
"delete_note_accept": "Sí, eliminarlo",
|
||||
"delete_note_cancel": "No, mantenerlo",
|
||||
"delete_note_confirm": "¿Estás seguro que quieres eliminar esta nota?",
|
||||
"delete_note_title": "Confirma la eliminación",
|
||||
"no_content": "Sin descripción dada",
|
||||
"note_placeholder": "Dejar una nota...",
|
||||
"notes": "notas",
|
||||
"reopen": "Reabrir",
|
||||
"report": "Reportar",
|
||||
"reports": "Reportes",
|
||||
"resolve": "Resolver",
|
||||
"show_closed": "Mostrar cerrados",
|
||||
"statuses": "estados",
|
||||
"tag_policy_notice": "Habilitar TagPolicy MRF para establecer restricciones de publicación",
|
||||
"tags": "Establecer restricciones de publicación"
|
||||
},
|
||||
"statuses": "Estados",
|
||||
"users": "Usuarios"
|
||||
},
|
||||
"nav": {
|
||||
"about": "Acerca de",
|
||||
"administration": "Administración",
|
||||
"announcements": "Anuncios",
|
||||
"back": "Volver",
|
||||
"bookmarks": "Marcadores",
|
||||
"bubble_timeline": "Linea temporal burbuja",
|
||||
"bubble_timeline_description": "Publicaciones de instancias cercanas a la tuya, recomendadas por los/las administradores/as",
|
||||
"chats": "Chats",
|
||||
"dms": "Mensajes directos",
|
||||
"friend_requests": "Solicitudes de seguimiento",
|
||||
"home_timeline": "Línea temporal personal",
|
||||
"home_timeline_description": "Publicaciones de personas que sigues",
|
||||
"interactions": "Interacciones",
|
||||
"lists": "Listas",
|
||||
"mentions": "Menciones",
|
||||
"moderation": "Moderación",
|
||||
"preferences": "Preferencias",
|
||||
"public_timeline_description": "Publicaciones públicas de esta instancia",
|
||||
"public_tl": "Línea temporal pública",
|
||||
|
@ -220,18 +320,19 @@
|
|||
"who_to_follow": "A quién seguir"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "Estado desconocido, buscándolo…",
|
||||
"broken_favorite": "Publicación desconocida, buscándola…",
|
||||
"error": "Error obteniendo notificaciones:{0}",
|
||||
"favorited_you": "le gusta tu estado",
|
||||
"favorited_you": "le gusta tu publicación",
|
||||
"follow_request": "quiere seguirte",
|
||||
"followed_you": "empezó a seguirte",
|
||||
"load_older": "Cargar notificaciones antiguas",
|
||||
"migrated_to": "migrado a",
|
||||
"no_more_notifications": "No hay más notificaciones",
|
||||
"notifications": "Notificaciones",
|
||||
"poll_ended": "La encuesta ha terminado",
|
||||
"reacted_with": "reaccionó con {0}",
|
||||
"read": "¡Leído!",
|
||||
"repeated_you": "repitió tu estado"
|
||||
"repeated_you": "repitió tu publicación"
|
||||
},
|
||||
"password_reset": {
|
||||
"check_email": "Revise su correo electrónico para obtener un enlace para restablecer su contraseña.",
|
||||
|
@ -269,27 +370,34 @@
|
|||
"text/bbcode": "BBCode",
|
||||
"text/html": "HTML",
|
||||
"text/markdown": "Markdown",
|
||||
"text/plain": "Texto Plano"
|
||||
"text/plain": "Texto Plano",
|
||||
"text/x.misskeymarkdown": "MFM"
|
||||
},
|
||||
"content_warning": "Tema (opcional)",
|
||||
"content_warning": "Advertencia de contenido (opcional)",
|
||||
"default": "Acabo de aterrizar en L.A.",
|
||||
"direct_warning_to_all": "Esta publicación será visible para todos los usuarios mencionados.",
|
||||
"direct_warning_to_first_only": "Esta publicación solo será visible para los usuarios mencionados al comienzo del mensaje.",
|
||||
"empty_status_error": "No se puede publicar un estado vacío y sin archivos adjuntos",
|
||||
"edit_remote_warning": "¡Los cambios realizados en la publicación pueden no ser visibles en algunas instancias!",
|
||||
"edit_status": "Editar estado",
|
||||
"edit_unsupported_warning": "Las encuestas y menciones no se modificarán al editar.",
|
||||
"empty_status_error": "No se puede enviar una publicación sin contenido ni archivos adjuntos",
|
||||
"media_description": "Descripción multimedia",
|
||||
"media_description_error": "Error al actualizar el archivo, inténtalo de nuevo",
|
||||
"new_status": "Publicar un nuevo estado",
|
||||
"media_not_sensitive_warning": "¡Tiene una advertencia de contenido, pero los archivos adjuntos no están marcados como contenido sensible!",
|
||||
"new_status": "Nueva publicación",
|
||||
"post": "Publicar",
|
||||
"posting": "Publicando",
|
||||
"preview": "Vista previa",
|
||||
"preview_empty": "Vacío",
|
||||
"scope": {
|
||||
"direct": "Directo - solo para los usuarios mencionados",
|
||||
"local": "Local- no federar esta publicación",
|
||||
"private": "Solo-seguidores - solo tus seguidores leerán la publicación",
|
||||
"public": "Público - publicaciones visibles en las líneas temporales públicas",
|
||||
"unlisted": "Sin listar -publicaciones no visibles en las líneas temporales públicas"
|
||||
},
|
||||
"scope_notice": {
|
||||
"local": "Esta publicación no será visible en otras instancias",
|
||||
"private": "Esta publicación solo será visible para tus seguidores",
|
||||
"public": "Esta publicación será visible para todo el mundo",
|
||||
"unlisted": "Esta publicación no será visible en la Línea Temporal Pública ni en Toda La Red Conocida"
|
||||
|
@ -297,11 +405,12 @@
|
|||
},
|
||||
"registration": {
|
||||
"bio": "Biografía",
|
||||
"bio_placeholder": "e.g.\nHola, soy un ejemplo.\nAquí puedes poner algo representativo tuyo... o no.",
|
||||
"bio_placeholder": "p. ej.\nHola, soy un ejemplo.\nAquí puedes poner algo representativo tuyo... o no.",
|
||||
"captcha": "CAPTCHA",
|
||||
"email": "Correo electrónico",
|
||||
"email_language": "¿En qué idioma desea recibir correos electrónicos del servidor?",
|
||||
"fullname": "Nombre a mostrar",
|
||||
"fullname_placeholder": "p.ej. Lain Iwakura",
|
||||
"fullname_placeholder": "p. ej. Atsuko Kagari",
|
||||
"new_captcha": "Haz click en la imagen para obtener un nuevo captcha",
|
||||
"password_confirm": "Confirmar contraseña",
|
||||
"reason": "Razón para registrarse",
|
||||
|
@ -309,7 +418,7 @@
|
|||
"register": "Registrarse",
|
||||
"registration": "Registro",
|
||||
"token": "Token de invitación",
|
||||
"username_placeholder": "p.ej. lain",
|
||||
"username_placeholder": "p. ej. akko",
|
||||
"validations": {
|
||||
"email_required": "no puede estar vacío",
|
||||
"fullname_required": "no puede estar vacío",
|
||||
|
@ -336,6 +445,16 @@
|
|||
},
|
||||
"settings": {
|
||||
"accent": "Acento",
|
||||
"account_alias_table_head": "Alias",
|
||||
"account_backup": "Copia de seguridad de la cuenta",
|
||||
"account_backup_description": "Esto le permite descargar un archivo con la información de su cuenta y sus publicaciones, pero aún no se pueden importar a una cuenta de Pleroma.",
|
||||
"account_backup_table_head": "Copia de seguridad",
|
||||
"account_privacy": "Privacidad",
|
||||
"add_alias_error": "Error añadiendo el alias: {error}",
|
||||
"add_backup": "Crear una nueva copia de seguridad",
|
||||
"add_backup_error": "Error al agregar una nueva copia de seguridad: {error}",
|
||||
"added_alias": "El alias ha sido añadido.",
|
||||
"added_backup": "La copia de seguridad ha sido agregada.",
|
||||
"allow_following_move": "Permitir el seguimiento automático, cuando la cuenta que sigues se traslada a otra instancia",
|
||||
"always_show_post_button": "Muestra siempre el botón flotante de Nueva Plubicación",
|
||||
"app_name": "Nombre de la aplicación",
|
||||
|
@ -347,6 +466,7 @@
|
|||
"avatarRadius": "Avatares",
|
||||
"avatar_size_instruction": "El tamaño mínimo recomendado para el avatar es de 150X150 píxeles.",
|
||||
"background": "Fondo",
|
||||
"backup_not_ready": "Esta copia de seguridad aún no está lista.",
|
||||
"bio": "Biografía",
|
||||
"block_export": "Exportar usuarios bloqueados",
|
||||
"block_export_button": "Exporta la lista de tus usuarios bloqueados a un archivo csv",
|
||||
|
@ -368,10 +488,18 @@
|
|||
"changed_password": "¡Contraseña cambiada correctamente!",
|
||||
"chatMessageRadius": "Mensaje de chat",
|
||||
"checkboxRadius": "Casillas de verificación",
|
||||
"collapse_subject": "Colapsar publicaciones con tema",
|
||||
"collapse_subject": "Ocultar publicaciones con advertencias de contenido",
|
||||
"columns": "Columnas",
|
||||
"composing": "Redactando",
|
||||
"confirm_dialogs": "Requiere confirmación de:",
|
||||
"confirm_new_password": "Confirmar la nueva contraseña",
|
||||
"conversation_display_linear": "Lineal",
|
||||
"conversation_display_tree": "Ramificado",
|
||||
"conversation_other_replies_button": "Mostrar el botón \"otras respuestas\"",
|
||||
"conversation_other_replies_button_below": "Debajo de las publicaciones",
|
||||
"conversation_other_replies_button_inside": "Dentro de las publicaciones",
|
||||
"current_avatar": "Tu avatar actual",
|
||||
"current_mascot": "Tu mascota actual",
|
||||
"current_password": "Contraseña actual",
|
||||
"data_import_export_tab": "Importar / Exportar datos",
|
||||
"default_vis": "Alcance de visibilidad por defecto",
|
||||
|
@ -379,11 +507,15 @@
|
|||
"delete_account_description": "Eliminar para siempre los datos y desactivar la cuenta.",
|
||||
"delete_account_error": "Hubo un error al eliminar tu cuenta. Si el fallo persiste, ponte en contacto con el/la administrador/a de tu instancia.",
|
||||
"delete_account_instructions": "Escribe tu contraseña para confirmar la eliminación de tu cuenta.",
|
||||
"disable_sticky_headers": "No insertar encabezados de columna en la parte superior de la pantalla",
|
||||
"discoverable": "Permitir la aparición de esta cuenta en los resultados de búsqueda y otros servicios",
|
||||
"domain_mutes": "Dominios",
|
||||
"download_backup": "Descargar",
|
||||
"email_language": "Idioma para recibir correos electrónicos del servidor",
|
||||
"emoji_reactions_on_timeline": "Mostrar las reacciones de emoji en la línea de tiempo",
|
||||
"enable_web_push_notifications": "Habilitar las notificiaciones en el navegador",
|
||||
"enter_current_password_to_confirm": "Introduce la contraseña actual para confirmar tu identidad",
|
||||
"expert_mode": "Mostrar avanzados",
|
||||
"export_theme": "Exportar tema",
|
||||
"file_export_import": {
|
||||
"backup_restore": "Copia de seguridad de la configuración",
|
||||
|
@ -398,7 +530,7 @@
|
|||
"restore_settings": "Restaurar ajustes desde archivo"
|
||||
},
|
||||
"filtering": "Filtrado",
|
||||
"filtering_explanation": "Todos los estados que contengan estas palabras serán silenciados, una por línea",
|
||||
"filtering_explanation": "Todos las publicaciones que contengan estas palabras serán silenciadas. Una por línea",
|
||||
"follow_export": "Exportar personas que tú sigues",
|
||||
"follow_export_button": "Exporta tus seguidores a un fichero csv",
|
||||
"follow_import": "Importar personas que tú sigues",
|
||||
|
@ -411,18 +543,25 @@
|
|||
"hide_all_muted_posts": "Ocultar las publicaciones silenciadas",
|
||||
"hide_attachments_in_convo": "Ocultar adjuntos en las conversaciones",
|
||||
"hide_attachments_in_tl": "Ocultar adjuntos en la línea temporal",
|
||||
"hide_favorites_description": "No mostrar la lista de mis favoritos (las personas aún serán notificadas)",
|
||||
"hide_filtered_statuses": "Ocultar estados filtrados",
|
||||
"hide_followers_count_description": "No mostrar el número de cuentas que me siguen",
|
||||
"hide_followers_description": "No mostrar quién me sigue",
|
||||
"hide_follows_count_description": "No mostrar el número de cuentas que sigo",
|
||||
"hide_follows_description": "No mostrar a quién sigo",
|
||||
"hide_isp": "Ocultar el panel específico de la instancia",
|
||||
"hide_list_aliases_error_action": "Cerrar",
|
||||
"hide_media_previews": "Ocultar la vista previa multimedia",
|
||||
"hide_muted_posts": "Ocultar las publicaciones de los usuarios silenciados",
|
||||
"hide_muted_threads": "Ocultar conversaciones silenciadas",
|
||||
"hide_post_stats": "Ocultar las estadísticas de las publicaciones (p.ej. el número de favoritos)",
|
||||
"hide_shoutbox": "Ocultar cuadro de diálogo de la instancia",
|
||||
"hide_site_favicon": "Ocultar \"favicon\" de la instancia en el \"top panel\"",
|
||||
"hide_site_name": "Ocultar el nombre de la instancia en el \"top panel\"",
|
||||
"hide_threads_with_blocked_users": "Ocultar conversaciones que mencionen usuarios bloqueados",
|
||||
"hide_user_stats": "Ocultar las estadísticas del usuario (p.ej. el número de seguidores)",
|
||||
"hide_wallpaper": "Ocultar el fondo de pantalla de la instancia",
|
||||
"hide_wordfiltered_statuses": "Ocultar publicaciones filtradas por palabras",
|
||||
"import_blocks_from_a_csv_file": "Importar lista de usuarios bloqueados dese un archivo csv",
|
||||
"import_followers_from_a_csv_file": "Importar personas que tú sigues a partir de un archivo csv",
|
||||
"import_mutes_from_a_csv_file": "Importar silenciados desde un archivo csv",
|
||||
|
@ -435,10 +574,21 @@
|
|||
"invalid_theme_imported": "El archivo importado no es un tema válido de Pleroma. No se han realizado cambios.",
|
||||
"limited_availability": "No disponible en tu navegador",
|
||||
"links": "Enlaces",
|
||||
"list_aliases_error": "Error al obtener alias: {error}",
|
||||
"list_backups_error": "Error al obtener la lista de copias de seguridad: {error}",
|
||||
"lock_account_description": "Restringir el acceso a tu cuenta solo a seguidores admitidos",
|
||||
"loop_video": "Vídeos en bucle",
|
||||
"loop_video_silent_only": "Bucle solo en vídeos sin sonido (p.ej. \"gifs\" de Mastodon)",
|
||||
"mascot": "Mascota de Mastodon FE",
|
||||
"max_depth_in_thread": "Número máximo de niveles a mostrar en la conversación por defecto",
|
||||
"max_thumbnails": "Cantidad máxima de miniaturas por publicación",
|
||||
"mention_link_bolden_you": "Resaltar la mención de ti cuando te mencionen",
|
||||
"mention_link_display": "Mostrar enlaces de mención",
|
||||
"mention_link_display_full": "siempre como nombre completo (p. ej. {'@'}foo{'@'}example.org)",
|
||||
"mention_link_display_full_for_remote": "como nombre completo solo para usuarios remotos (p. ej. {'@'}foo{'@'}example.org)",
|
||||
"mention_link_display_short": "siempre como nombres cortos (p. ej. {'@'}foo)",
|
||||
"mention_link_show_avatar": "Mostrar el avatar del usuario detrás del enlace",
|
||||
"mention_link_show_tooltip": "Mostrar el nombre completo de los usuarios remotos como información emergente",
|
||||
"mfa": {
|
||||
"authentication_methods": "Métodos de autentificación",
|
||||
"confirm_and_enable": "Confirmar y habilitar OTP",
|
||||
|
@ -462,6 +612,12 @@
|
|||
},
|
||||
"minimal_scopes_mode": "Minimizar las opciones de publicación",
|
||||
"more_settings": "Más opciones",
|
||||
"move_account": "Trasladar la cuenta",
|
||||
"move_account_error": "Error trasladando la cuenta: {error}",
|
||||
"move_account_notes": "Si desea trasladar esta cuenta a otro lugar, debe ir a su cuenta de destino y agregar un alias que apunte aquí.",
|
||||
"move_account_target": "Cuenta de destino (p. ej. {example})",
|
||||
"moved_account": "La cuenta ha sido trasladada.",
|
||||
"mute_bot_posts": "Silenciar publicaciones de \"bots\"",
|
||||
"mute_export": "Exportar silenciados",
|
||||
"mute_export_button": "Exportar los silenciados a un archivo csv",
|
||||
"mute_import": "Importar silenciados",
|
||||
|
@ -471,6 +627,7 @@
|
|||
"mutes_tab": "Silenciados",
|
||||
"name": "Nombre",
|
||||
"name_bio": "Nombre y biografía",
|
||||
"new_alias_target": "Añadir un nuevo alias (p. ej. {example})",
|
||||
"new_email": "Nuevo correo electrónico",
|
||||
"new_password": "Nueva contraseña",
|
||||
"no_blocks": "No hay usuarios bloqueados",
|
||||
|
|
|
@ -273,23 +273,23 @@
|
|||
"back": "Retour",
|
||||
"bookmarks": "Marques-Pages",
|
||||
"bubble_timeline": "Flux de cette bulle",
|
||||
"bubble_timeline_description": "Les status des instances proches de celle-ci, choisies par l'administration",
|
||||
"bubble_timeline_description": "Les statuts des instances proches de celle-ci, choisies par l'administration",
|
||||
"chats": "Chats",
|
||||
"dms": "Messages directs",
|
||||
"friend_requests": "Demandes de suivi",
|
||||
"home_timeline": "Flux personnel",
|
||||
"home_timeline_description": "Les status de vos abonnements",
|
||||
"home_timeline_description": "Les statuts de vos abonnements",
|
||||
"interactions": "Interactions",
|
||||
"lists": "Listes",
|
||||
"mentions": "Mentions",
|
||||
"preferences": "Préférences",
|
||||
"public_timeline_description": "Tous les status publics de cette instance",
|
||||
"public_timeline_description": "Tous les statuts publics de cette instance",
|
||||
"public_tl": "Flux publique",
|
||||
"search": "Recherche",
|
||||
"timeline": "Flux personnel",
|
||||
"timelines": "Flux",
|
||||
"twkn": "Réseau connu",
|
||||
"twkn_timeline_description": "Les status du réseau entier",
|
||||
"twkn_timeline_description": "Les statuts du réseau entier",
|
||||
"user_search": "Recherche de comptes",
|
||||
"who_to_follow": "Suggestion de suivi"
|
||||
},
|
||||
|
@ -337,7 +337,7 @@
|
|||
"votes_count": "{count} vote | {count} votes"
|
||||
},
|
||||
"post_status": {
|
||||
"account_not_locked_warning": "Votre compte n'est pas {0}. N'importe qui peut vous suivre pour voir vos billets en Abonné·e·s uniquement.",
|
||||
"account_not_locked_warning": "Votre compte n'est pas {0}. N'importe qui peut vous suivre et voir vos statuts réservés aux abonné·es.",
|
||||
"account_not_locked_warning_link": "verrouillé",
|
||||
"attachments_sensitive": "Marquer les pièce-jointes comme sensible",
|
||||
"content_type": {
|
||||
|
@ -693,6 +693,19 @@
|
|||
"setting_changed": "Préférence modifiée",
|
||||
"setting_server_side": "Modifier cette préférence répercute sur tous vos clients",
|
||||
"settings": "Paramètres",
|
||||
"settings_profile": "Profils de paramètres",
|
||||
"settings_profile_creation": "Créer un profil",
|
||||
"settings_profile_creation_new_name_label": "Nom",
|
||||
"settings_profile_creation_submit": "Créer",
|
||||
"settings_profile_currently": "Profil actuel : {name} (version {version})",
|
||||
"settings_profile_delete": "Supprimer",
|
||||
"settings_profile_delete_confirm": "Voulez-vous vraiment supprimer ce profil ?",
|
||||
"settings_profile_force_sync": "Synchroniser",
|
||||
"settings_profile_in_use": "Actuel",
|
||||
"settings_profile_use": "Utiliser",
|
||||
"settings_profiles_refresh": "Recharger",
|
||||
"settings_profiles_show": "Afficher touts les profils",
|
||||
"settings_profiles_unshow": "Cacher les profils",
|
||||
"show_admin_badge": "Afficher le badge d'Admin sur mon profil",
|
||||
"show_moderator_badge": "Afficher le badge de Modo' sur mon profil",
|
||||
"show_nav_shortcuts": "Afficher plus de raccourcis de navigations dans le panneau supérieur",
|
||||
|
@ -892,6 +905,12 @@
|
|||
"word_filter": "Filtrage par mots",
|
||||
"wordfilter": "Filtrage par mot-clé"
|
||||
},
|
||||
"settings_profile": {
|
||||
"creating": "Création du profil « {profil} » …",
|
||||
"synchronization_error": "Impossible de synchroniser les paramètres : {err}",
|
||||
"synchronized": "Paramètres synchronisés !",
|
||||
"synchronizing": "Synchronisation du profil « {profile} » …"
|
||||
},
|
||||
"status": {
|
||||
"ancestor_follow": "Voir {numReplies} autre réponse en dessous de ce status | Voir {numReplies} autres réponses en dessous de ce status",
|
||||
"ancestor_follow_with_icon": "{icon} {text}",
|
||||
|
@ -925,6 +944,11 @@
|
|||
"pin": "Agrafer sur le profil",
|
||||
"pinned": "Agraffé",
|
||||
"plus_more": "+{number} autre | +{number} autres",
|
||||
"redraft": "Supprimer et réécrire",
|
||||
"redraft_confirm": "Supprimer et réécrire ce status ? Les interactions avec l'original seront perdues.",
|
||||
"redraft_confirm_accept_button": "Oui : supprimer et réécrire",
|
||||
"redraft_confirm_cancel_button": "Non : garder l'original",
|
||||
"redraft_confirm_title": "Confirmer la suppression et réécriture",
|
||||
"remove_attachment": "Supprimer la pièce jointe",
|
||||
"repeat_confirm": "Partager ce statut ?",
|
||||
"repeat_confirm_accept_button": "Partager",
|
||||
|
@ -1047,12 +1071,24 @@
|
|||
"strip_media": "Supprimer les medias des statuts"
|
||||
},
|
||||
"approve": "Accepter",
|
||||
"approve_confirm": "Voulez-vous vraiment approuver cet abonnement ?",
|
||||
"approve_confirm_accept_button": "Oui : accepter",
|
||||
"approve_confirm_cancel_button": "Non : rejeter",
|
||||
"approve_confirm_title": "Approuver une demande d'abonnement",
|
||||
"block": "Bloquer",
|
||||
"block_confirm": "Êtes-vous sûr de vouloir bloquer {user} ?",
|
||||
"block_confirm_accept_button": "Oui",
|
||||
"block_confirm_cancel_button": "Non",
|
||||
"block_confirm_title": "Bloquer l'utilisateur",
|
||||
"block_progress": "Blocage…",
|
||||
"blocked": "Bloqué !",
|
||||
"bot": "Robot",
|
||||
"deactivated": "Désactivé",
|
||||
"deny": "Rejeter",
|
||||
"deny_confirm": "Êtes-vous sûr de vouloir rejeter cette demande d'abonnement ?",
|
||||
"deny_confirm_accept_button": "Oui : rejeter",
|
||||
"deny_confirm_cancel_button": "Non : annuler",
|
||||
"deny_confirm_title": "Rejeter une demande d'abonnement",
|
||||
"domain_muted": "Débloquer la domaine",
|
||||
"edit_profile": "Éditer le profil",
|
||||
"favorites": "Favoris",
|
||||
|
@ -1061,8 +1097,8 @@
|
|||
"follow_progress": "Demande en cours…",
|
||||
"follow_sent": "Demande envoyée !",
|
||||
"follow_unfollow": "Désabonner",
|
||||
"followees": "Suivis",
|
||||
"followers": "Vous suivent",
|
||||
"followees": "Abonnements",
|
||||
"followers": "Abonné·es",
|
||||
"following": "Suivi !",
|
||||
"follows_you": "Vous suit !",
|
||||
"hidden": "Caché",
|
||||
|
@ -1078,18 +1114,28 @@
|
|||
"mention": "Mention",
|
||||
"message": "Message",
|
||||
"mute": "Masquer",
|
||||
"mute_confirm": "Êtes-vous sûr de vouloir masquer {user} ?",
|
||||
"mute_confirm_accept_button": "Oui",
|
||||
"mute_confirm_cancel_button": "Non",
|
||||
"mute_confirm_title": "Masquer",
|
||||
"mute_domain": "Bloquer la domaine",
|
||||
"mute_progress": "Masquage…",
|
||||
"muted": "Masqué",
|
||||
"note": "Note privée",
|
||||
"per_day": "par jour",
|
||||
"remote_follow": "Suivre d'une autre instance",
|
||||
"remove_follower": "Désabonner",
|
||||
"replies": "Statuts et réponses",
|
||||
"report": "Signalement",
|
||||
"show_repeats": "Montrer les partages",
|
||||
"statuses": "Statuts",
|
||||
"subscribe": "Abonner",
|
||||
"unblock": "Débloquer",
|
||||
"unblock_progress": "Déblocage…",
|
||||
"unfollow_confirm": "Êtes-vous sûr de vouloir vous désabonner de {user} ?",
|
||||
"unfollow_confirm_accept_button": "Oui : me désabonner",
|
||||
"unfollow_confirm_cancel_button": "Non : garder l'abonnement",
|
||||
"unfollow_confirm_title": "Désabonner",
|
||||
"unmute": "Démasquer",
|
||||
"unmute_progress": "Démasquage…",
|
||||
"unsubscribe": "Désabonner"
|
||||
|
|
|
@ -254,6 +254,10 @@
|
|||
"hint": "会話に加わるには、ログインしてください",
|
||||
"login": "ログイン",
|
||||
"logout": "ログアウト",
|
||||
"logout_confirm": "ログアウトしますか?",
|
||||
"logout_confirm_accept_button": "ログアウト",
|
||||
"logout_confirm_cancel_button": "キャンセル",
|
||||
"logout_confirm_title": "ログアウト",
|
||||
"password": "パスワード",
|
||||
"placeholder": "例: user",
|
||||
"recovery_code": "リカバリーコード",
|
||||
|
@ -266,6 +270,27 @@
|
|||
"next": "次",
|
||||
"previous": "前"
|
||||
},
|
||||
"moderation": {
|
||||
"moderation": "管理",
|
||||
"reports": {
|
||||
"add_note": "OK",
|
||||
"close": "閉じる",
|
||||
"delete_note": "削除",
|
||||
"delete_note_accept": "削除",
|
||||
"delete_note_cancel": "キャンセル",
|
||||
"delete_note_confirm": "削除しますか?",
|
||||
"delete_note_title": "確認してください",
|
||||
"no_content": "説明なし",
|
||||
"no_reports": "通報なし",
|
||||
"note_placeholder": "メモ",
|
||||
"notes": "メモ",
|
||||
"reopen": "再開",
|
||||
"report": "通報:",
|
||||
"reports": "通報",
|
||||
"resolve": "完了",
|
||||
"show_closed": "完了した通報を表示"
|
||||
}
|
||||
},
|
||||
"nav": {
|
||||
"about": "このインスタンスについて",
|
||||
"administration": "管理",
|
||||
|
@ -282,6 +307,7 @@
|
|||
"interactions": "インタラクション",
|
||||
"lists": "リスト",
|
||||
"mentions": "通知",
|
||||
"moderation": "管理",
|
||||
"preferences": "設定",
|
||||
"public_timeline_description": "このインスタンスからの公開投稿",
|
||||
"public_tl": "公開タイムライン",
|
||||
|
@ -1124,8 +1150,10 @@
|
|||
"note": "私的なメモ",
|
||||
"per_day": "/日",
|
||||
"remote_follow": "リモートフォロー",
|
||||
"remove_follower": "フォロワーやめさせる",
|
||||
"replies": "投稿と返信",
|
||||
"report": "通報",
|
||||
"requested_by": "あなたにフォローリクエストを送りました",
|
||||
"show_repeats": "リピートを見る",
|
||||
"statuses": "投稿",
|
||||
"subscribe": "購読",
|
||||
|
|
|
@ -62,7 +62,7 @@ export default function createPersistedState ({
|
|||
}
|
||||
loaded = true
|
||||
} catch (e) {
|
||||
console.log("Couldn't load state")
|
||||
console.error("Couldn't load state")
|
||||
console.error(e)
|
||||
loaded = true
|
||||
}
|
||||
|
@ -83,8 +83,8 @@ export default function createPersistedState ({
|
|||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Couldn't persist state:")
|
||||
console.log(e)
|
||||
console.error("Couldn't persist state:")
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -163,6 +163,7 @@ const api = {
|
|||
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||
dispatch('startFetchingNotifications')
|
||||
dispatch('startFetchingAnnouncements')
|
||||
dispatch('startFetchingReports')
|
||||
dispatch('pushGlobalNotice', {
|
||||
level: 'error',
|
||||
messageKey: 'timeline.socket_broke',
|
||||
|
@ -280,6 +281,19 @@ const api = {
|
|||
if (!fetcher) return
|
||||
store.commit('removeFetcher', { fetcherName: 'announcements', fetcher })
|
||||
},
|
||||
|
||||
// Reports
|
||||
startFetchingReports (store) {
|
||||
if (store.state.fetchers['reports']) return
|
||||
const fetcher = store.state.backendInteractor.startFetchingReports({ store })
|
||||
store.commit('addFetcher', { fetcherName: 'reports', fetcher })
|
||||
},
|
||||
stopFetchingReports (store) {
|
||||
const fetcher = store.state.fetchers.reports
|
||||
if (!fetcher) return
|
||||
store.commit('removeFetcher', { fetcherName: 'reports', fetcher })
|
||||
},
|
||||
|
||||
getSupportedTranslationlanguages (store) {
|
||||
store.state.backendInteractor.getSupportedTranslationlanguages({ store })
|
||||
.then((data) => {
|
||||
|
|
|
@ -59,7 +59,7 @@ export const defaultState = {
|
|||
alwaysShowNewPostButton: false,
|
||||
autohideFloatingPostButton: false,
|
||||
pauseOnUnfocused: true,
|
||||
stopGifs: true,
|
||||
stopGifs: undefined,
|
||||
replyVisibility: 'all',
|
||||
thirdColumnMode: 'notifications',
|
||||
notificationVisibility: {
|
||||
|
@ -80,11 +80,9 @@ export const defaultState = {
|
|||
hideScopeNotice: false,
|
||||
useStreamingApi: false,
|
||||
sidebarRight: undefined, // instance default
|
||||
scopeCopy: undefined, // instance default
|
||||
subjectLineBehavior: undefined, // instance default
|
||||
alwaysShowSubjectInput: undefined, // instance default
|
||||
postContentType: undefined, // instance default
|
||||
minimalScopesMode: undefined, // instance default
|
||||
// This hides statuses filtered via a word filter
|
||||
hideFilteredStatuses: undefined, // instance default
|
||||
modalOnRepeat: undefined, // instance default
|
||||
|
|
|
@ -53,12 +53,10 @@ const defaultState = {
|
|||
logoMargin: '.2em',
|
||||
logoMask: true,
|
||||
logoLeft: false,
|
||||
minimalScopesMode: false,
|
||||
nsfwCensorImage: undefined,
|
||||
postContentType: 'text/plain',
|
||||
redirectRootLogin: '/main/friends',
|
||||
redirectRootNoLogin: '/main/all',
|
||||
scopeCopy: true,
|
||||
showFeaturesPanel: true,
|
||||
showInstanceSpecificPanel: false,
|
||||
showNavShortcuts: true,
|
||||
|
|
|
@ -2,6 +2,9 @@ const defaultState = {
|
|||
settingsModalState: 'hidden',
|
||||
settingsModalLoaded: false,
|
||||
settingsModalTargetTab: null,
|
||||
modModalState: 'hidden',
|
||||
modModalLoaded: false,
|
||||
modModalTargetTab: null,
|
||||
settings: {
|
||||
currentSaveStateNotice: null,
|
||||
noticeClearTimeout: null,
|
||||
|
@ -63,6 +66,30 @@ const interfaceMod = {
|
|||
setSettingsModalTargetTab (state, value) {
|
||||
state.settingsModalTargetTab = value
|
||||
},
|
||||
closeModModal (state) {
|
||||
state.modModalState = 'hidden'
|
||||
},
|
||||
togglePeekModModal (state) {
|
||||
switch (state.modModalState) {
|
||||
case 'minimized':
|
||||
state.modModalState = 'visible'
|
||||
return
|
||||
case 'visible':
|
||||
state.modModalState = 'minimized'
|
||||
return
|
||||
default:
|
||||
throw new Error('Illegal minimization state of mod modal')
|
||||
}
|
||||
},
|
||||
openModModal (state) {
|
||||
state.modModalState = 'visible'
|
||||
if (!state.modModalLoaded) {
|
||||
state.modModalLoaded = true
|
||||
}
|
||||
},
|
||||
setModModalTargetTab (state, value) {
|
||||
state.modModalTargetTab = value
|
||||
},
|
||||
pushGlobalNotice (state, notice) {
|
||||
state.globalNotices.push(notice)
|
||||
},
|
||||
|
@ -105,6 +132,18 @@ const interfaceMod = {
|
|||
commit('setSettingsModalTargetTab', value)
|
||||
commit('openSettingsModal')
|
||||
},
|
||||
closeModModal ({ commit }) {
|
||||
commit('closeModModal')
|
||||
},
|
||||
openModModal ({ commit }) {
|
||||
commit('openModModal')
|
||||
},
|
||||
togglePeekModModal ({ commit }) {
|
||||
commit('togglePeekModModal')
|
||||
},
|
||||
clearModModalTargetTab ({ commit }) {
|
||||
commit('setModModalTargetTab', null)
|
||||
},
|
||||
pushGlobalNotice (
|
||||
{ commit, dispatch, state },
|
||||
{
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import filter from 'lodash/filter'
|
||||
import { filter, find, forEach, remove } from 'lodash'
|
||||
|
||||
const getReport = (state, id) => find(state.reports, { id })
|
||||
const updateReport = (state, { report, param, value }) => {
|
||||
getReport(state, report.id)[param] = value
|
||||
}
|
||||
|
||||
const reports = {
|
||||
state: {
|
||||
userId: null,
|
||||
statuses: [],
|
||||
preTickedIds: [],
|
||||
modalActivated: false
|
||||
modalActivated: false,
|
||||
reports: []
|
||||
},
|
||||
mutations: {
|
||||
openUserReportingModal (state, { userId, statuses, preTickedIds }) {
|
||||
|
@ -16,6 +22,38 @@ const reports = {
|
|||
},
|
||||
closeUserReportingModal (state) {
|
||||
state.modalActivated = false
|
||||
},
|
||||
setReport (state, { report }) {
|
||||
let existing = getReport(state, report.id)
|
||||
if (existing) {
|
||||
existing = report
|
||||
} else {
|
||||
state.reports.push(report)
|
||||
}
|
||||
},
|
||||
updateReportStates (state, { reports }) {
|
||||
forEach(reports, (report) => {
|
||||
updateReport(state, { report, param: 'state', value: report.state })
|
||||
})
|
||||
},
|
||||
addNoteToReport (state, { id, note, user }) {
|
||||
// akkoma doesn't return the note from this API endpoint, and there's no
|
||||
// good way to get it. the note data is spoofed in the frontend until
|
||||
// reload.
|
||||
// definitely worth adding this to the backend at some point
|
||||
const report = getReport(state, id)
|
||||
const date = new Date()
|
||||
|
||||
report.notes.push({
|
||||
content: note,
|
||||
user,
|
||||
created_at: date.toISOString(),
|
||||
id: date.getTime()
|
||||
})
|
||||
},
|
||||
deleteNoteFromReport (state, { id, note }) {
|
||||
const report = getReport(state, id)
|
||||
remove(report.notes, { id: note })
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
@ -31,6 +69,22 @@ const reports = {
|
|||
},
|
||||
closeUserReportingModal ({ commit }) {
|
||||
commit('closeUserReportingModal')
|
||||
},
|
||||
updateReportStates ({ rootState, commit }, { reports }) {
|
||||
commit('updateReportStates', { reports })
|
||||
return rootState.api.backendInteractor.updateReportStates({ reports })
|
||||
},
|
||||
getReport ({ rootState, commit }, { id }) {
|
||||
return rootState.api.backendInteractor.getReport({ id })
|
||||
.then(report => commit('setReport', { report }))
|
||||
},
|
||||
addNoteToReport ({ rootState, commit }, { id, note }) {
|
||||
commit('addNoteToReport', { id, note, user: rootState.users.currentUser })
|
||||
return rootState.api.backendInteractor.addNoteToReport({ id, note })
|
||||
},
|
||||
deleteNoteFromReport ({ rootState, commit }, { id, note }) {
|
||||
commit('deleteNoteFromReport', { id, note })
|
||||
return rootState.api.backendInteractor.deleteNoteFromReport({ id, note })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { each, map, concat, last, get } from 'lodash'
|
||||
import { parseStatus, parseSource, parseUser, parseNotification, parseAttachment, parseLinkHeaderPagination } from '../entity_normalizer/entity_normalizer.service.js'
|
||||
import { parseStatus, parseSource, parseUser, parseNotification, parseReport, parseAttachment, parseLinkHeaderPagination } from '../entity_normalizer/entity_normalizer.service.js'
|
||||
import { RegistrationError, StatusCodeError } from '../errors/errors'
|
||||
|
||||
/* eslint-env browser */
|
||||
|
@ -19,6 +19,9 @@ const ADMIN_USERS_URL = '/api/pleroma/admin/users'
|
|||
const SUGGESTIONS_URL = '/api/v1/suggestions'
|
||||
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
|
||||
const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read'
|
||||
const ADMIN_REPORTS_URL = '/api/v1/pleroma/admin/reports'
|
||||
const ADMIN_REPORT_NOTES_URL = id => `/api/v1/pleroma/admin/reports/${id}/notes`
|
||||
const ADMIN_REPORT_NOTE_URL = (report, note) => `/api/v1/pleroma/admin/reports/${report}/notes/${note}`
|
||||
|
||||
const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa'
|
||||
const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes'
|
||||
|
@ -342,7 +345,7 @@ const fetchUserRelationship = ({ id, credentials }) => {
|
|||
return new Promise((resolve, reject) => response.json()
|
||||
.then((json) => {
|
||||
if (!response.ok) {
|
||||
return reject(new StatusCodeError(response.status, json, { url }, response))
|
||||
return reject(new StatusCodeError(400, json, { url }, response))
|
||||
}
|
||||
return resolve(json)
|
||||
}))
|
||||
|
@ -635,6 +638,57 @@ const deleteUser = ({ credentials, user }) => {
|
|||
})
|
||||
}
|
||||
|
||||
const getReports = ({ state, limit, page, pageSize, credentials }) => {
|
||||
let url = ADMIN_REPORTS_URL
|
||||
const args = [
|
||||
state && `state=${state}`,
|
||||
limit && `limit=${limit}`,
|
||||
page && `page=${page}`,
|
||||
pageSize && `page_size=${pageSize}`
|
||||
].filter(_ => _).join('&')
|
||||
|
||||
url = url + (args ? '?' + args : '')
|
||||
return fetch(url, { headers: authHeaders(credentials) })
|
||||
.then((data) => data.json())
|
||||
.then((data) => data.reports.map(parseReport))
|
||||
}
|
||||
|
||||
const updateReportStates = ({ credentials, reports }) => {
|
||||
// reports syntax: [{ id: int, state: string }...]
|
||||
const updates = {
|
||||
reports: reports.map(report => {
|
||||
return {
|
||||
id: report.id.toString(),
|
||||
state: report.state
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return promisedRequest({
|
||||
url: ADMIN_REPORTS_URL,
|
||||
method: 'PATCH',
|
||||
payload: updates,
|
||||
credentials
|
||||
})
|
||||
}
|
||||
|
||||
const addNoteToReport = ({ id, note, credentials }) => {
|
||||
return promisedRequest({
|
||||
url: ADMIN_REPORT_NOTES_URL(id),
|
||||
method: 'POST',
|
||||
payload: { content: note },
|
||||
credentials
|
||||
})
|
||||
}
|
||||
|
||||
const deleteNoteFromReport = ({ report, note, credentials }) => {
|
||||
return promisedRequest({
|
||||
url: ADMIN_REPORT_NOTE_URL(report, note),
|
||||
method: 'DELETE',
|
||||
credentials
|
||||
})
|
||||
}
|
||||
|
||||
const fetchTimeline = ({
|
||||
timeline,
|
||||
credentials,
|
||||
|
@ -1726,7 +1780,11 @@ const apiService = {
|
|||
getSettingsProfile,
|
||||
saveSettingsProfile,
|
||||
listSettingsProfiles,
|
||||
deleteSettingsProfile
|
||||
deleteSettingsProfile,
|
||||
getReports,
|
||||
updateReportStates,
|
||||
addNoteToReport,
|
||||
deleteNoteFromReport
|
||||
}
|
||||
|
||||
export default apiService
|
||||
|
|
|
@ -5,6 +5,7 @@ import followRequestFetcher from '../../services/follow_request_fetcher/follow_r
|
|||
import listsFetcher from '../../services/lists_fetcher/lists_fetcher.service.js'
|
||||
import announcementsFetcher from '../../services/announcements_fetcher/announcements_fetcher.service.js'
|
||||
import configFetcher from '../config_fetcher/config_fetcher.service.js'
|
||||
import reportsFetcher from '../reports_fetcher/reports_fetcher.service.js'
|
||||
|
||||
const backendInteractorService = credentials => ({
|
||||
startFetchingTimeline ({ timeline, store, userId = false, listId = false, tag }) {
|
||||
|
@ -39,6 +40,10 @@ const backendInteractorService = credentials => ({
|
|||
return announcementsFetcher.startFetching({ store, credentials })
|
||||
},
|
||||
|
||||
startFetchingReports ({ store, state, limit, page, pageSize }) {
|
||||
return reportsFetcher.startFetching({ store, credentials, state, limit, page, pageSize })
|
||||
},
|
||||
|
||||
startUserSocket ({ store }) {
|
||||
const serv = store.rootState.instance.server.replace('http', 'ws')
|
||||
const url = serv + getMastodonSocketURI({ credentials, stream: 'user' })
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
||||
|
||||
const startFetching = ({ credentials, store }) => {
|
||||
console.log('startFetching: Config')
|
||||
const boundFetchAndUpdate = () => store.dispatch('getSettingsProfile')
|
||||
boundFetchAndUpdate()
|
||||
return promiseInterval(boundFetchAndUpdate, 10 * 60000)
|
||||
|
|
|
@ -88,6 +88,9 @@ export const parseUser = (data) => {
|
|||
output.friends_count = data.following_count
|
||||
|
||||
output.bot = data.bot
|
||||
if (data.akkoma) {
|
||||
output.instance = data.akkoma.instance
|
||||
}
|
||||
|
||||
if (data.pleroma) {
|
||||
const relationship = data.pleroma.relationship
|
||||
|
@ -429,6 +432,24 @@ export const parseNotification = (data) => {
|
|||
return output
|
||||
}
|
||||
|
||||
export const parseReport = (data) => {
|
||||
const report = {}
|
||||
|
||||
report.account = parseUser(data.account)
|
||||
report.actor = parseUser(data.actor)
|
||||
report.statuses = data.statuses.map(parseStatus)
|
||||
report.notes = data.notes.map(note => {
|
||||
note.user = parseUser(note.user)
|
||||
return note
|
||||
})
|
||||
report.state = data.state
|
||||
report.content = data.content
|
||||
report.created_at = data.created_at
|
||||
report.id = data.id
|
||||
|
||||
return report
|
||||
}
|
||||
|
||||
const isNsfw = (status) => {
|
||||
const nsfwRegex = /#nsfw/i
|
||||
return (status.tags || []).includes('nsfw') || !!(status.text || '').match(nsfwRegex)
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import runtime from 'serviceworker-webpack-plugin/lib/runtime'
|
||||
|
||||
function urlBase64ToUint8Array (base64String) {
|
||||
const padding = '='.repeat((4 - base64String.length % 4) % 4)
|
||||
const base64 = (base64String + padding)
|
||||
|
@ -15,7 +13,7 @@ function isPushSupported () {
|
|||
}
|
||||
|
||||
function getOrCreateServiceWorker () {
|
||||
return runtime.register()
|
||||
return navigator.serviceWorker.register('/sw-pleroma.js')
|
||||
.catch((err) => console.error('Unable to get or create a service worker.', err))
|
||||
}
|
||||
|
||||
|
|
20
src/services/reports_fetcher/reports_fetcher.service.js
Normal file
20
src/services/reports_fetcher/reports_fetcher.service.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import apiService from '../api/api.service.js'
|
||||
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
||||
import { forEach } from 'lodash'
|
||||
|
||||
const fetchAndUpdate = ({ store, credentials, state, limit, page, pageSize }) => {
|
||||
return apiService.getReports({ credentials, state, limit, page, pageSize })
|
||||
.then(reports => forEach(reports, report => store.commit('setReport', { report })))
|
||||
}
|
||||
|
||||
const startFetching = ({ store, credentials, state, limit, page, pageSize }) => {
|
||||
const boundFetchAndUpdate = () => fetchAndUpdate({ store, credentials, state, limit, page, pageSize })
|
||||
boundFetchAndUpdate()
|
||||
return promiseInterval(boundFetchAndUpdate, 60000)
|
||||
}
|
||||
|
||||
const reportsFetcher = {
|
||||
startFetching
|
||||
}
|
||||
|
||||
export default reportsFetcher
|
|
@ -5,7 +5,9 @@ import { parseNotification } from './services/entity_normalizer/entity_normalize
|
|||
import { prepareNotificationObject } from './services/notification_utils/notification_utils.js'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import messages from './i18n/service_worker_messages.js'
|
||||
import { precacheAndRoute } from 'workbox-precaching/precacheAndRoute';
|
||||
|
||||
precacheAndRoute(self.__WB_MANIFEST);
|
||||
const i18n = createI18n({
|
||||
// By default, use the browser locale, we will update it if neccessary
|
||||
locale: 'en',
|
||||
|
|
|
@ -13,12 +13,10 @@
|
|||
"logoMargin": ".1em",
|
||||
"logoMask": true,
|
||||
"logoLeft": false,
|
||||
"minimalScopesMode": false,
|
||||
"nsfwCensorImage": "",
|
||||
"postContentType": "text/plain",
|
||||
"redirectRootLogin": "/main/friends",
|
||||
"redirectRootNoLogin": "/main/all",
|
||||
"scopeCopy": true,
|
||||
"showFeaturesPanel": true,
|
||||
"showInstanceSpecificPanel": false,
|
||||
"sidebarRight": false,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// https://github.com/webpack/karma-webpack
|
||||
|
||||
// var path = require('path')
|
||||
var merge = require('webpack-merge')
|
||||
var { merge } = require('webpack-merge')
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
var baseConfig = require('../../build/webpack.base.conf')
|
||||
var utils = require('../../build/utils')
|
||||
|
@ -16,7 +16,7 @@ var webpackConfig = merge(baseConfig, {
|
|||
module: {
|
||||
rules: utils.styleLoaders()
|
||||
},
|
||||
devtool: '#inline-source-map',
|
||||
devtool: 'inline-source-map',
|
||||
// vue: {
|
||||
// loaders: {
|
||||
// js: 'isparta'
|
||||
|
|
|
@ -194,8 +194,9 @@ describe('API Entities normalizer', () => {
|
|||
|
||||
expect(parsedPost).to.have.property('type', 'status')
|
||||
expect(parsedRepeat).to.have.property('type', 'retweet')
|
||||
console.log(parsedRepeat)
|
||||
expect(parsedRepeat).to.have.property('retweeted_status')
|
||||
expect(parsedRepeat).to.have.deep.property('retweeted_status.id', 'deadbeef')
|
||||
expect(parsedRepeat).to.have.nested.property('retweeted_status.id', 'deadbeef')
|
||||
})
|
||||
|
||||
it('sets nsfw for statuses with the #nsfw tag', () => {
|
||||
|
@ -229,7 +230,7 @@ describe('API Entities normalizer', () => {
|
|||
expect(parsedPost).to.have.property('type', 'status')
|
||||
expect(parsedRepeat).to.have.property('type', 'retweet')
|
||||
expect(parsedRepeat).to.have.property('retweeted_status')
|
||||
expect(parsedRepeat).to.have.deep.property('retweeted_status.id', 'deadbeef')
|
||||
expect(parsedRepeat).to.have.nested.property('retweeted_status.id', 'deadbeef')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -284,9 +285,9 @@ describe('API Entities normalizer', () => {
|
|||
})
|
||||
expect(parseNotification(notif)).to.have.property('id', 123)
|
||||
expect(parseNotification(notif)).to.have.property('seen', false)
|
||||
expect(parseNotification(notif)).to.have.deep.property('status.id', '444')
|
||||
expect(parseNotification(notif)).to.have.deep.property('action.id', '444')
|
||||
expect(parseNotification(notif)).to.have.deep.property('from_profile.id', 'spurdo')
|
||||
expect(parseNotification(notif)).to.have.nested.property('status.id', '444')
|
||||
expect(parseNotification(notif)).to.have.nested.property('action.id', '444')
|
||||
expect(parseNotification(notif)).to.have.nested.property('from_profile.id', 'spurdo')
|
||||
})
|
||||
|
||||
it('correctly normalizes favorite notifications', () => {
|
||||
|
@ -303,9 +304,9 @@ describe('API Entities normalizer', () => {
|
|||
expect(parseNotification(notif)).to.have.property('id', 123)
|
||||
expect(parseNotification(notif)).to.have.property('type', 'like')
|
||||
expect(parseNotification(notif)).to.have.property('seen', true)
|
||||
expect(parseNotification(notif)).to.have.deep.property('status.id', '4412')
|
||||
expect(parseNotification(notif)).to.have.deep.property('action.id', '444')
|
||||
expect(parseNotification(notif)).to.have.deep.property('from_profile.id', 'spurdo')
|
||||
expect(parseNotification(notif)).to.have.nested.property('status.id', '4412')
|
||||
expect(parseNotification(notif)).to.have.nested.property('action.id', '444')
|
||||
expect(parseNotification(notif)).to.have.nested.property('from_profile.id', 'spurdo')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in a new issue