Compare commits

..

No commits in common. "shigusegubu-themes3" and "develop" have entirely different histories.

425 changed files with 11103 additions and 16528 deletions

2
.eslintignore Normal file
View file

@ -0,0 +1,2 @@
build/*.js
config/*.js

27
.eslintrc.js Normal file
View file

@ -0,0 +1,27 @@
module.exports = {
root: true,
parserOptions: {
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'
],
// add your custom rules here
rules: {
// allow paren-less arrow functions
'arrow-parens': 0,
// allow async-await
'generator-star-spacing': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'vue/require-prop-types': 0,
'vue/multi-word-component-names': 0
}
}

View file

@ -1,7 +1,7 @@
# This file is a template, and might need editing before it works on your project.
# Official framework image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/node/tags/
image: node:18
image: node:16
stages:
- check-changelog
@ -38,8 +38,8 @@ lint:
stage: lint
script:
- yarn
- yarn lint
- yarn stylelint
- npm run lint
- npm run stylelint
test:
stage: test
@ -62,7 +62,7 @@ build:
- himem
script:
- yarn
- yarn build
- npm run build
artifacts:
paths:
- dist/

View file

@ -1 +1 @@
18.20.6
16.18.1

View file

@ -1,5 +1,5 @@
// https://github.com/shelljs/shelljs
import('./check-versions.mjs').then(m => m.default())
require('./check-versions')()
require('shelljs/global')
env.NODE_ENV = 'production'
@ -9,7 +9,7 @@ var ora = require('ora')
var webpack = require('webpack')
var webpackConfig = require('./webpack.prod.conf')
console.info(
console.log(
' Tip:\n' +
' Built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'

45
build/check-versions.js Normal file
View file

@ -0,0 +1,45 @@
var semver = require('semver')
var chalk = require('chalk')
var packageConfig = require('../package.json')
var exec = function (cmd) {
return require('child_process')
.execSync(cmd).toString().trim()
}
var versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
},
{
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
}
]
module.exports = function () {
var warnings = []
for (var i = 0; i < versionRequirements.length; i++) {
var mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (var i = 0; i < warnings.length; i++) {
var warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}

View file

@ -1,35 +0,0 @@
import semver from 'semver'
import chalk from 'chalk'
import packageConfig from '../package.json' with { type: 'json' }
var versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
export default function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.warn(chalk.yellow('\nTo use this template, you must update following to modules:\n'))
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.warn(' ' + warning)
}
console.warn()
process.exit(1)
}
}

View file

@ -1,4 +1,4 @@
import('./check-versions.mjs').then(m => m.default())
require('./check-versions')()
var config = require('../config')
if (!process.env.NODE_ENV) process.env.NODE_ENV = config.dev.env
var path = require('path')
@ -53,8 +53,7 @@ Object.keys(proxyTable).forEach(function (context) {
if (typeof options === 'string') {
options = { target: options }
}
options.pathFilter = context
app.use(proxyMiddleware.createProxyMiddleware(options))
app.use(proxyMiddleware.createProxyMiddleware(context, options))
})
// handle fallback for HTML5 history API
@ -73,10 +72,10 @@ app.use(staticPath, express.static('./static'))
module.exports = app.listen(port, function (err) {
if (err) {
console.error(err)
console.log(err)
return
}
var uri = 'http://localhost:' + port
console.info('Listening at ' + uri + '\n')
console.log('Listening at ' + uri + '\n')
// opn(uri)
})

View file

@ -34,18 +34,26 @@ exports.cssLoaders = function (options) {
use: generateLoaders(['css-loader', 'postcss-loader', 'less-loader']),
},
{
test: /\.scss$/,
test: /\.sass$/,
use: generateLoaders([
'css-loader',
'postcss-loader',
{
loader: 'sass-loader',
options: {
api: 'modern'
indentedSyntax: true
}
}
])
}
},
{
test: /\.scss$/,
use: generateLoaders(['css-loader', 'postcss-loader', 'sass-loader'])
},
{
test: /\.styl(us)?$/,
use: generateLoaders(['css-loader', 'postcss-loader', 'stylus-loader']),
},
]
}

View file

@ -108,9 +108,8 @@ module.exports = {
filename: 'sw-pleroma.js'
}),
new ESLintPlugin({
formatter: require('eslint-formatter-friendly'),
overrideConfigFile: path.resolve(__dirname, '..', 'eslint.config.mjs'),
configType: 'flat'
extensions: ['js', 'vue'],
formatter: require('eslint-formatter-friendly')
}),
new StylelintPlugin({}),
new VueLoaderPlugin(),

View file

@ -23,8 +23,7 @@ module.exports = merge(baseWebpackConfig, {
'COMMIT_HASH': JSON.stringify('DEV'),
'DEV_OVERRIDES': JSON.stringify(config.dev.settings),
'__VUE_OPTIONS_API__': true,
'__VUE_PROD_DEVTOOLS__': false,
'__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': false
'__VUE_PROD_DEVTOOLS__': false
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.HotModuleReplacementPlugin(),

View file

@ -50,8 +50,7 @@ var webpackConfig = merge(baseWebpackConfig, {
'COMMIT_HASH': JSON.stringify(commitHash),
'DEV_OVERRIDES': JSON.stringify(undefined),
'__VUE_OPTIONS_API__': true,
'__VUE_PROD_DEVTOOLS__': false,
'__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': false
'__VUE_PROD_DEVTOOLS__': false
}),
// extract css into its own file
new MiniCssExtractPlugin({

View file

@ -1 +0,0 @@
Added option to always convert images to JPEG format instead of using WebP when compressing images.

View file

@ -1 +0,0 @@
checkbox vertical alignment has been fixed

View file

@ -1 +0,0 @@
Some new default color schemes

View file

@ -1 +0,0 @@
Fix some of the color manipulation functions

View file

@ -1 +0,0 @@
Added support for fetching /{resource}.custom.ext to allow adding instance-specific themes without altering sourcetree

View file

@ -1 +0,0 @@
Post actions can be customized

View file

@ -1 +0,0 @@
Use /api/v1/accounts/:id/follow for account subscriptions instead of the deprecated routes

View file

@ -1 +0,0 @@
Add draft management system

View file

@ -1 +0,0 @@
fixed occasional overflows in emoji picker and made header scrollable

View file

@ -1 +0,0 @@
fix emoji inconsistencies in notifications, fix some emoji not scaling with interface

View file

@ -1 +0,0 @@
Added configurable image compression option in general settings, allowing users to control whether images are compressed before upload.

View file

@ -1 +0,0 @@
Fix few markup panel inconsistencies; better ToS and registration

View file

@ -1 +0,0 @@
Fix small markup inconsistencies

View file

@ -1 +0,0 @@
modal layout for mobile has new layout to make it easy to use

View file

@ -1 +0,0 @@
fixed modals buttons overflow

View file

@ -1 +0,0 @@
Added missing EN translation key for status.muted_user

View file

@ -1 +0,0 @@
better display of mute reason on posts

View file

@ -1 +0,0 @@
proper sticky header for conversations on user page

View file

@ -1 +0,0 @@
Partially migrated from vuex to pinia

View file

@ -1 +0,0 @@
reply-or-quote buttons now take less space

View file

@ -1 +0,0 @@
Bookmarks visible again on mobile

View file

@ -1 +0,0 @@
Authenticate and subscribe to streaming after connection

View file

@ -1 +0,0 @@
Tabs now have indentation for better visibility of which tab is currently active

View file

@ -1 +0,0 @@
UI for making v3 themes and palettes, support for bundling v3 themes

View file

@ -1 +0,0 @@
Resize most kinds of images on upload.

View file

@ -1 +0,0 @@
Make UserLink wrappable

View file

@ -1 +0,0 @@
Upgraded Vue to version 3.5

View file

@ -1 +0,0 @@
Show only month and day instead of weird "day, hour" format. While at it, fixed typo "defualt" in a comment.

View file

@ -8,10 +8,10 @@ try {
// and that's how actual BE reports its url
settings.target = settings.target.replace(/\/$/, '')
}
console.info('Using local dev server settings (/config/local.json):')
console.info(JSON.stringify(settings, null, 2))
console.log('Using local dev server settings (/config/local.json):')
console.log(JSON.stringify(settings, null, 2))
} catch (e) {
console.info('Local dev server settings not found (/config/local.json)')
console.log('Local dev server settings not found (/config/local.json)')
}
const target = settings.target || 'http://localhost:4000/'

View file

@ -1,37 +0,0 @@
import vue from "eslint-plugin-vue";
import js from "@eslint/js";
import globals from "globals";
export default [
...vue.configs['flat/recommended'],
js.configs.recommended,
{
files: ["**/*.js", "**/*.mjs", "**/*.vue"],
ignores: ["build/*.js", "config/*.js"],
languageOptions: {
ecmaVersion: 2024,
sourceType: "module",
parserOptions: {
parser: "@babel/eslint-parser",
},
globals: {
...globals.browser,
...globals.mocha,
...globals.chai,
...globals.commonjs,
...globals.serviceworker
}
},
rules: {
'arrow-parens': 0,
'generator-star-spacing': 0,
'no-debugger': 0,
'vue/require-prop-types': 0,
'vue/multi-word-component-names': 0,
}
}
]

View file

@ -3,6 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
<link rel="icon" type="image/png" href="/favicon.png">
<!-- putting styles here to avoid having to wait for styles to load up -->
<style id="splashscreen">
#splash {
@ -69,8 +70,6 @@
"P P . . ."
"P P . E E"
"P P . E E";
--logoChunkSize: calc(2em * 0.5 * var(--scale))
}
.chunk {
@ -100,27 +99,6 @@
text-align: center;
}
#statusError {
display: none;
margin-top: 1em;
font-size: calc(1vw + 1vh + 1vmin);
line-height: 2;
width: 100%;
text-align: center;
}
#statusStack {
display: none;
margin-top: 1em;
font-size: calc((1vw + 1vh + 1vmin) / 2.5);
width: calc(100vw - 5em);
padding: 1em;
text-overflow: ellipsis;
overflow-x: hidden;
text-align: left;
line-height: 2;
}
@media (prefers-reduced-motion) {
#throbber {
animation: none !important;
@ -155,8 +133,6 @@
<!-- it's a pseudographic, don't want screenreader read out nonsense -->
<span aria-hidden="true" class="initial-text">(。&gt;&lt;)</span>
</div>
<code id="statusError"></code>
<pre id="statusStack"></pre>
</div>
</div>
<div id="app" class="hidden"></div>

View file

@ -10,111 +10,110 @@
"unit": "karma start test/unit/karma.conf.js --single-run",
"unit:watch": "karma start test/unit/karma.conf.js --single-run=false",
"e2e": "node test/e2e/runner.js",
"test": "yarn run unit && yarn run e2e",
"stylelint": "yarn exec stylelint '**/*.scss' '**/*.vue'",
"lint": "eslint src test/unit/specs test/e2e/specs",
"lint-fix": "eslint --fix src test/unit/specs test/e2e/specs"
"test": "npm run unit && npm run e2e",
"stylelint": "npx stylelint '**/*.scss' '**/*.vue'",
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs",
"lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs"
},
"dependencies": {
"@babel/runtime": "7.26.9",
"@babel/runtime": "7.21.5",
"@chenfengyuan/vue-qrcode": "2.0.0",
"@fortawesome/fontawesome-svg-core": "6.7.2",
"@fortawesome/free-regular-svg-icons": "6.7.2",
"@fortawesome/free-solid-svg-icons": "6.7.2",
"@fortawesome/vue-fontawesome": "3.0.8",
"@fortawesome/fontawesome-svg-core": "6.4.0",
"@fortawesome/free-regular-svg-icons": "6.4.0",
"@fortawesome/free-solid-svg-icons": "6.4.0",
"@fortawesome/vue-fontawesome": "3.0.3",
"@kazvmoe-infra/pinch-zoom-element": "1.2.0",
"@kazvmoe-infra/unicode-emoji-json": "0.4.0",
"@ruffle-rs/ruffle": "0.1.0-nightly.2025.1.13",
"@ruffle-rs/ruffle": "0.1.0-nightly.2024.8.21",
"@vuelidate/core": "2.0.3",
"@vuelidate/validators": "2.0.4",
"body-scroll-lock": "3.1.5",
"chromatism": "3.0.0",
"click-outside-vue3": "4.0.1",
"cropperjs": "1.6.2",
"cropperjs": "1.5.13",
"escape-html": "1.0.3",
"globals": "^15.14.0",
"hash-sum": "^2.0.0",
"js-cookie": "3.0.5",
"localforage": "1.10.0",
"parse-link-header": "2.0.0",
"phoenix": "1.7.19",
"pinia": "^2.0.33",
"punycode.js": "2.3.1",
"qrcode": "1.5.4",
"phoenix": "1.7.7",
"punycode.js": "2.3.0",
"qrcode": "1.5.3",
"querystring-es3": "0.2.1",
"url": "0.11.4",
"url": "0.11.0",
"utf8": "3.0.0",
"vue": "3.5.13",
"vue-i18n": "10",
"vue-router": "4.5.0",
"vue": "3.2.45",
"vue-i18n": "9.2.2",
"vue-router": "4.1.6",
"vue-template-compiler": "2.7.14",
"vue-virtual-scroller": "^2.0.0-beta.7",
"vuex": "4.1.0"
},
"devDependencies": {
"@babel/core": "7.26.9",
"@babel/eslint-parser": "7.26.8",
"@babel/plugin-transform-runtime": "7.26.9",
"@babel/preset-env": "7.26.9",
"@babel/register": "7.25.9",
"@babel/core": "7.21.8",
"@babel/eslint-parser": "7.21.8",
"@babel/plugin-transform-runtime": "7.21.4",
"@babel/preset-env": "7.21.5",
"@babel/register": "7.21.0",
"@intlify/vue-i18n-loader": "5.0.1",
"@ungap/event-target": "0.2.4",
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
"@vue/babel-plugin-jsx": "1.2.5",
"@vue/compiler-sfc": "3.5.13",
"@vue/test-utils": "2.4.6",
"autoprefixer": "10.4.20",
"babel-loader": "9.2.1",
"@vue/babel-plugin-jsx": "1.2.2",
"@vue/compiler-sfc": "3.2.45",
"@vue/test-utils": "2.2.8",
"autoprefixer": "10.4.19",
"babel-loader": "9.1.3",
"babel-plugin-lodash": "3.3.4",
"chai": "4.5.0",
"chalk": "5.4.1",
"chromedriver": "133.0.2",
"chai": "4.3.7",
"chalk": "1.1.3",
"chromedriver": "108.0.0",
"connect-history-api-fallback": "2.0.0",
"copy-webpack-plugin": "12.0.2",
"cross-spawn": "7.0.6",
"css-loader": "7.1.2",
"css-minimizer-webpack-plugin": "7.0.0",
"copy-webpack-plugin": "11.0.0",
"cross-spawn": "7.0.3",
"css-loader": "6.10.0",
"css-minimizer-webpack-plugin": "4.2.2",
"custom-event-polyfill": "1.0.7",
"eslint": "9.20.1",
"eslint-config-standard": "17.1.0",
"eslint": "8.33.0",
"eslint-config-standard": "17.0.0",
"eslint-formatter-friendly": "7.0.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-n": "17.15.1",
"eslint-plugin-promise": "7.2.1",
"eslint-plugin-vue": "9.32.0",
"eslint-webpack-plugin": "4.2.0",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-n": "15.6.1",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-vue": "9.9.0",
"eslint-webpack-plugin": "3.2.0",
"eventsource-polyfill": "0.9.6",
"express": "4.21.2",
"function-bind": "1.1.2",
"html-webpack-plugin": "5.6.3",
"http-proxy-middleware": "3.0.3",
"iso-639-1": "3.1.5",
"express": "4.18.2",
"function-bind": "1.1.1",
"html-webpack-plugin": "5.5.1",
"http-proxy-middleware": "2.0.6",
"iso-639-1": "2.1.15",
"json-loader": "0.5.7",
"karma": "6.4.4",
"karma-coverage": "2.2.1",
"karma-coverage": "2.2.0",
"karma-firefox-launcher": "2.1.3",
"karma-mocha": "2.0.1",
"karma-mocha-reporter": "2.2.5",
"karma-sinon-chai": "2.0.2",
"karma-sourcemap-loader": "0.4.0",
"karma-sourcemap-loader": "0.3.8",
"karma-spec-reporter": "0.0.36",
"karma-webpack": "5.0.1",
"karma-webpack": "5.0.0",
"lodash": "4.17.21",
"mini-css-extract-plugin": "2.9.2",
"mocha": "11.1.0",
"mini-css-extract-plugin": "2.7.6",
"mocha": "10.2.0",
"nightwatch": "2.6.25",
"opn": "5.5.0",
"ora": "0.4.1",
"postcss": "8.5.2",
"postcss": "8.4.23",
"postcss-html": "^1.5.0",
"postcss-loader": "7.3.4",
"postcss-loader": "7.0.2",
"postcss-scss": "^4.0.6",
"sass": "1.85.0",
"sass-loader": "13.3.3",
"selenium-server": "3.141.59",
"semver": "7.7.1",
"sass": "1.60.0",
"sass-loader": "13.2.2",
"selenium-server": "2.53.1",
"semver": "7.3.8",
"serviceworker-webpack5-plugin": "2.0.0",
"shelljs": "0.8.5",
"sinon": "15.2.0",
"sinon": "15.0.4",
"sinon-chai": "3.7.0",
"stylelint": "14.16.1",
"stylelint-config-html": "^1.1.0",
@ -123,15 +122,16 @@
"stylelint-config-standard": "29.0.0",
"stylelint-rscss": "0.4.0",
"stylelint-webpack-plugin": "^3.3.0",
"vue-loader": "17.4.2",
"vue-loader": "17.0.1",
"vue-style-loader": "4.1.3",
"webpack": "5.97.1",
"webpack": "5.75.0",
"webpack-dev-middleware": "3.7.3",
"webpack-hot-middleware": "2.26.1",
"webpack-hot-middleware": "2.25.3",
"webpack-merge": "0.20.0"
},
"engines": {
"node": ">= 16.0.0"
"node": ">= 16.0.0",
"npm": ">= 3.0.0"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}

View file

@ -17,8 +17,6 @@ import GlobalNoticeList from './components/global_notice_list/global_notice_list
import { windowWidth, windowHeight } from './services/window_utils/window_utils'
import { mapGetters } from 'vuex'
import { defineAsyncComponent } from 'vue'
import { useShoutStore } from './stores/shout'
import { useInterfaceStore } from './stores/interface'
export default {
name: 'app',
@ -47,11 +45,8 @@ export default {
mobileActivePanel: 'timeline'
}),
watch: {
themeApplied () {
themeApplied (value) {
this.removeSplash()
},
layoutType () {
document.getElementById('modal').classList = ['-' + this.layoutType]
}
},
created () {
@ -59,10 +54,9 @@ export default {
const val = this.$store.getters.mergedConfig.interfaceLanguage
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
window.addEventListener('resize', this.updateMobileState)
document.getElementById('modal').classList = ['-' + this.layoutType]
},
mounted () {
if (useInterfaceStore().themeApplied) {
if (this.$store.state.interface.themeApplied) {
this.removeSplash()
}
},
@ -71,10 +65,7 @@ export default {
},
computed: {
themeApplied () {
return useInterfaceStore().themeApplied
},
layoutModalClass () {
return '-' + this.layoutType
return this.$store.state.interface.themeApplied
},
classes () {
return [
@ -108,7 +99,7 @@ export default {
}
}
},
shout () { return useShoutStore().joined },
shout () { return this.$store.state.shout.joined },
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
showInstanceSpecificPanel () {
return this.$store.state.instance.showInstanceSpecificPanel &&
@ -134,7 +125,7 @@ export default {
hideShoutbox () {
return this.$store.getters.mergedConfig.hideShoutbox
},
layoutType () { return useInterfaceStore().layoutType },
layoutType () { return this.$store.state.interface.layoutType },
privateMode () { return this.$store.state.instance.private },
reverseLayout () {
const { thirdColumnMode, sidebarRight: reverseSetting } = this.$store.getters.mergedConfig
@ -150,8 +141,8 @@ export default {
},
methods: {
updateMobileState () {
useInterfaceStore().setLayoutWidth(windowWidth())
useInterfaceStore().setLayoutHeight(windowHeight())
this.$store.dispatch('setLayoutWidth', windowWidth())
this.$store.dispatch('setLayoutHeight', windowHeight())
},
removeSplash () {
document.querySelector('#status').textContent = this.$t('splash.fun_' + Math.ceil(Math.random() * 4))

View file

@ -1,6 +1,6 @@
// stylelint-disable rscss/class-format
/* stylelint-disable no-descending-specificity */
@use "panel";
@import "./panel";
:root {
--status-margin: 0.75em;
@ -390,29 +390,37 @@ nav {
}
}
.menu-item {
line-height: var(--__line-height);
.menu-item,
.list-item {
display: block;
box-sizing: border-box;
border: none;
outline: none;
text-align: initial;
font-size: inherit;
font-family: inherit;
font-weight: 400;
font-size: 100%;
cursor: pointer;
a,
button:not(.button-default) {
color: var(--text);
font-size: 100%;
}
&.disabled {
cursor: not-allowed;
}
}
.list-item {
color: inherit;
clear: both;
position: relative;
white-space: nowrap;
border-color: var(--border);
border-style: solid;
border-width: 0;
border-top-width: 1px;
width: 100%;
line-height: var(--__line-height);
padding: var(--__vertical-gap) var(--__horizontal-gap);
background: transparent;
--__line-height: 1.5em;
--__horizontal-gap: 0.75em;
--__vertical-gap: 0.5em;
&.-non-interactive {
cursor: auto;
}
&.-active,
&:hover {
@ -434,6 +442,20 @@ nav {
border-bottom-width: 1px;
}
a,
button:not(.button-default) {
text-align: initial;
padding: 0;
background: none;
border: none;
outline: none;
display: inline;
font-size: 100%;
font-family: inherit;
line-height: unset;
color: var(--text);
}
&:first-child {
border-top-right-radius: var(--roundness);
border-top-left-radius: var(--roundness);
@ -447,42 +469,6 @@ nav {
}
}
.menu-item,
.list-item {
display: block;
box-sizing: border-box;
border: none;
outline: none;
text-align: initial;
color: inherit;
clear: both;
position: relative;
white-space: nowrap;
width: 100%;
padding: var(--__vertical-gap) var(--__horizontal-gap);
background: transparent;
--__line-height: 1.5em;
--__horizontal-gap: 0.75em;
--__vertical-gap: 0.5em;
&.-non-interactive {
cursor: auto;
}
a,
button:not(.button-default) {
text-align: initial;
padding: 0;
background: none;
border: none;
outline: none;
display: inline;
font-family: inherit;
line-height: unset;
}
}
.button-unstyled {
border: none;
outline: none;
@ -1094,8 +1080,3 @@ option {
}
}
}
@property --shadow {
syntax: "*";
inherits: false;
}

View file

@ -1,6 +1,6 @@
<template>
<div
v-show="themeApplied"
v-show="$store.state.interface.themeApplied"
id="app-loaded"
:style="bgStyle"
>
@ -70,7 +70,7 @@
<PostStatusModal />
<EditStatusModal v-if="editingAvailable" />
<StatusHistoryModal v-if="editingAvailable" />
<SettingsModal :class="layoutModalClass" />
<SettingsModal />
<UpdateNotification />
<GlobalNoticeList />
</div>

View file

@ -17,10 +17,6 @@ import { applyConfig } from '../services/style_setter/style_setter.js'
import FaviconService from '../services/favicon_service/favicon_service.js'
import { initServiceWorker, updateFocus } from '../services/sw/sw.js'
import { useI18nStore } from 'src/stores/i18n'
import { useInterfaceStore } from 'src/stores/interface'
import { useAnnouncementsStore } from 'src/stores/announcements'
let staticInitialResults = null
const parsedInitialResults = () => {
@ -79,7 +75,7 @@ const getInstanceConfig = async ({ store }) => {
}
}
const getBackendProvidedConfig = async () => {
const getBackendProvidedConfig = async ({ store }) => {
try {
const res = await window.fetch('/api/pleroma/frontend_configurations')
if (res.ok) {
@ -127,9 +123,6 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
}
copyInstanceOption('theme')
copyInstanceOption('style')
copyInstanceOption('palette')
copyInstanceOption('embeddedToS')
copyInstanceOption('nsfwCensorImage')
copyInstanceOption('background')
copyInstanceOption('hidePostStats')
@ -179,7 +172,8 @@ const getTOS = async ({ store }) => {
throw (res)
}
} catch (e) {
console.warn("Can't load TOS\n", e)
console.warn("Can't load TOS")
console.warn(e)
}
}
@ -193,7 +187,8 @@ const getInstancePanel = async ({ store }) => {
throw (res)
}
} catch (e) {
console.warn("Can't load instance panel\n", e)
console.warn("Can't load instance panel")
console.warn(e)
}
}
@ -223,7 +218,8 @@ const getStickers = async ({ store }) => {
throw (res)
}
} catch (e) {
console.warn("Can't load stickers\n", e)
console.warn("Can't load stickers")
console.warn(e)
}
}
@ -245,7 +241,7 @@ const resolveStaffAccounts = ({ store, accounts }) => {
const getNodeInfo = async ({ store }) => {
try {
const res = await preloadFetch('/nodeinfo/2.1.json')
const res = await preloadFetch('/nodeinfo/2.0.json')
if (res.ok) {
const data = await res.json()
const metadata = data.metadata
@ -282,7 +278,6 @@ const getNodeInfo = async ({ store }) => {
const software = data.software
store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version })
store.dispatch('setInstanceOption', { name: 'backendRepository', value: software.repository })
store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: software.name === 'pleroma' })
const priv = metadata.private
@ -338,16 +333,9 @@ const checkOAuthToken = async ({ store }) => {
return Promise.resolve()
}
const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
const app = createApp(App)
app.use(pinia)
if (storageError) {
useInterfaceStore().pushGlobalNotice({ messageKey: 'errors.storage_unavailable', level: 'error' })
}
useInterfaceStore().setLayoutWidth(windowWidth())
useInterfaceStore().setLayoutHeight(windowHeight())
const afterStoreSetup = async ({ store, i18n }) => {
store.dispatch('setLayoutWidth', windowWidth())
store.dispatch('setLayoutHeight', windowHeight())
FaviconService.initFaviconService()
initServiceWorker(store)
@ -358,11 +346,12 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
store.dispatch('setInstanceOption', { name: 'server', value: server })
document.querySelector('#status').textContent = i18n.global.t('splash.settings')
await setConfig({ store })
document.querySelector('#status').textContent = i18n.global.t('splash.theme')
try {
await useInterfaceStore().applyTheme().catch((e) => { console.error('Error setting theme', e) })
await store.dispatch('setTheme').catch((e) => { console.error('Error setting theme', e) })
} catch (e) {
window.splashError(e)
return Promise.reject(e)
}
@ -370,6 +359,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
// Now we can try getting the server settings and logging in
// Most of these are preloaded into the index.html so blocking is minimized
document.querySelector('#status').textContent = i18n.global.t('splash.instance')
await Promise.all([
checkOAuthToken({ store }),
getInstancePanel({ store }),
@ -377,11 +367,9 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
getInstanceConfig({ store })
]).catch(e => Promise.reject(e))
await store.dispatch('loadDrafts')
// Start fetching things that don't need to block the UI
store.dispatch('fetchMutes')
useAnnouncementsStore().startFetchingAnnouncements()
store.dispatch('startFetchingAnnouncements')
getTOS({ store })
getStickers({ store })
@ -396,19 +384,12 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
}
})
useI18nStore().setI18n(i18n)
const app = createApp(App)
app.use(router)
app.use(store)
app.use(i18n)
// Little thing to get out of invalid theme state
window.resetThemes = () => {
useInterfaceStore().resetThemeV3()
useInterfaceStore().resetThemeV3Palette()
useInterfaceStore().resetThemeV2()
}
app.use(vClickOutside)
app.use(VBodyScrollLock)
app.use(VueVirtualScroller)
@ -418,6 +399,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
// remove after vue 3.3
app.config.unwrapInjectedRef = true
document.querySelector('#status').textContent = i18n.global.t('splash.almost')
app.mount('#app')
return app

View file

@ -26,7 +26,6 @@ import ListsEdit from 'components/lists_edit/lists_edit.vue'
import NavPanel from 'src/components/nav_panel/nav_panel.vue'
import AnnouncementsPage from 'components/announcements_page/announcements_page.vue'
import QuotesTimeline from '../components/quotes_timeline/quotes_timeline.vue'
import Drafts from 'components/drafts/drafts.vue'
import BookmarkFolders from '../components/bookmark_folders/bookmark_folders.vue'
import BookmarkFolderEdit from '../components/bookmark_folder_edit/bookmark_folder_edit.vue'
@ -43,7 +42,7 @@ export default (store) => {
{
name: 'root',
path: '/',
redirect: () => {
redirect: _to => {
return (store.state.users.currentUser
? store.state.instance.redirectRootLogin
: store.state.instance.redirectRootNoLogin) || '/main/all'
@ -83,7 +82,6 @@ export default (store) => {
{ name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute },
{ name: 'about', path: '/about', component: About },
{ name: 'announcements', path: '/announcements', component: AnnouncementsPage },
{ name: 'drafts', path: '/drafts', component: Drafts },
{ name: 'user-profile', path: '/users/:name', component: UserProfile },
{ name: 'legacy-user-profile', path: '/:name', component: UserProfile },
{ name: 'lists', path: '/lists', component: Lists },

View file

@ -7,7 +7,6 @@ import { library } from '@fortawesome/fontawesome-svg-core'
import {
faEllipsisV
} from '@fortawesome/free-solid-svg-icons'
import { useReportsStore } from 'src/stores/reports'
library.add(
faEllipsisV
@ -74,7 +73,7 @@ const AccountActions = {
this.hideConfirmRemoveUserFromFollowers()
},
reportUser () {
useReportsStore().openUserReportingModal({ userId: this.user.id })
this.$store.dispatch('openUserReportingModal', { userId: this.user.id })
},
openChat () {
this.$router.push({

View file

@ -9,80 +9,60 @@
<template #content>
<div class="dropdown-menu">
<template v-if="relationship.following">
<div
<button
v-if="relationship.showing_reblogs"
class="menu-item dropdown-item"
class="dropdown-item menu-item"
@click="hideRepeats"
>
<button
class="main-button"
@click="hideRepeats"
>
{{ $t('user_card.hide_repeats') }}
</button>
</div>
<div
{{ $t('user_card.hide_repeats') }}
</button>
<button
v-if="!relationship.showing_reblogs"
class="menu-item dropdown-item"
class="dropdown-item menu-item"
@click="showRepeats"
>
<button
class="main-button"
@click="showRepeats"
>
{{ $t('user_card.show_repeats') }}
</button>
</div>
{{ $t('user_card.show_repeats') }}
</button>
<div
role="separator"
class="dropdown-divider"
/>
</template>
<UserListMenu :user="user" />
<div
<button
v-if="relationship.followed_by"
class="menu-item dropdown-item"
class="dropdown-item menu-item"
@click="removeUserFromFollowers"
>
<button
class="main-button"
@click="removeUserFromFollowers"
>
{{ $t('user_card.remove_follower') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
v-if="relationship.blocking"
class="main-button"
@click="unblockUser"
>
{{ $t('user_card.unblock') }}
</button>
<button
v-else
class="main-button"
@click="blockUser"
>
{{ $t('user_card.block') }}
</button>
</div>
<div class="menu-item dropdown-item">
<button
class="main-button"
@click="reportUser"
>
{{ $t('user_card.report') }}
</button>
</div>
<div
{{ $t('user_card.remove_follower') }}
</button>
<button
v-if="relationship.blocking"
class="dropdown-item menu-item"
@click="unblockUser"
>
{{ $t('user_card.unblock') }}
</button>
<button
v-else
class="dropdown-item menu-item"
@click="blockUser"
>
{{ $t('user_card.block') }}
</button>
<button
class="dropdown-item menu-item"
@click="reportUser"
>
{{ $t('user_card.report') }}
</button>
<button
v-if="pleromaChatMessagesAvailable"
class="menu-item dropdown-item"
class="dropdown-item menu-item"
@click="openChat"
>
<button
class="main-button"
@click="openChat"
>
{{ $t('user_card.message') }}
</button>
</div>
{{ $t('user_card.message') }}
</button>
</div>
</template>
<template #trigger>
@ -106,7 +86,6 @@
<i18n-t
keypath="user_card.block_confirm"
tag="span"
scope="global"
>
<template #user>
<span
@ -128,7 +107,6 @@
<i18n-t
keypath="user_card.remove_follower_confirm"
tag="span"
scope="global"
>
<template #user>
<span

View file

@ -14,10 +14,6 @@ export default {
warning: '.warning',
success: '.success'
},
editor: {
border: 1,
aspect: '3 / 1'
},
defaultRules: [
{
directives: {

View file

@ -2,7 +2,6 @@ import { mapState } from 'vuex'
import AnnouncementEditor from '../announcement_editor/announcement_editor.vue'
import RichContent from '../rich_content/rich_content.jsx'
import localeService from '../../services/locale/locale.service.js'
import { useAnnouncementsStore } from 'src/stores/announcements'
const Announcement = {
components: {
@ -68,11 +67,11 @@ const Announcement = {
methods: {
markAsRead () {
if (!this.isRead) {
return useAnnouncementsStore().markAnnouncementAsRead(this.announcement.id)
return this.$store.dispatch('markAnnouncementAsRead', this.announcement.id)
}
},
deleteAnnouncement () {
return useAnnouncementsStore().deleteAnnouncement(this.announcement.id)
return this.$store.dispatch('deleteAnnouncement', this.announcement.id)
},
formatTimeOrDate (time, locale) {
const d = new Date(time)
@ -86,7 +85,7 @@ const Announcement = {
this.editing = true
},
submitEdit () {
useAnnouncementsStore().editAnnouncement({
this.$store.dispatch('editAnnouncement', {
id: this.announcement.id,
...this.editedAnnouncement
})

View file

@ -34,9 +34,8 @@
id="announcement-all-day"
v-model="announcement.allDay"
:disabled="disabled"
>
{{ $t('announcements.all_day_prompt') }}
</Checkbox>
/>
<label for="announcement-all-day">{{ $t('announcements.all_day_prompt') }}</label>
</span>
</div>
</template>

View file

@ -1,7 +1,6 @@
import { mapState } from 'vuex'
import Announcement from '../announcement/announcement.vue'
import AnnouncementEditor from '../announcement_editor/announcement_editor.vue'
import { useAnnouncementsStore } from 'src/stores/announcements'
const AnnouncementsPage = {
components: {
@ -21,14 +20,14 @@ const AnnouncementsPage = {
}
},
mounted () {
useAnnouncementsStore().fetchAnnouncements()
this.$store.dispatch('fetchAnnouncements')
},
computed: {
...mapState({
currentUser: state => state.users.currentUser
}),
announcements () {
return useAnnouncementsStore().announcements
return this.$store.state.announcements.announcements
},
canPostAnnouncement () {
return this.currentUser && this.currentUser.privileges.includes('announcements_manage_announcements')
@ -37,7 +36,7 @@ const AnnouncementsPage = {
methods: {
postAnnouncement () {
this.posting = true
useAnnouncementsStore().postAnnouncement(this.newAnnouncement)
this.$store.dispatch('postAnnouncement', this.newAnnouncement)
.then(() => {
this.newAnnouncement.content = ''
this.startsAt = undefined

View file

@ -1,9 +1,9 @@
<template>
<div class="panel panel-default announcements-page">
<div class="panel-heading">
<h1 class="title">
<span>
{{ $t('announcements.page_header') }}
</h1>
</span>
</div>
<div class="panel-body">
<section

View file

@ -18,7 +18,6 @@ import {
faPencilAlt,
faAlignRight
} from '@fortawesome/free-solid-svg-icons'
import { useMediaViewerStore } from 'src/stores/media_viewer'
library.add(
faFile,
@ -145,17 +144,17 @@ const Attachment = {
window.open(target.href, '_blank')
}
},
openModal () {
openModal (event) {
if (this.useModal) {
this.$emit('setMedia')
useMediaViewerStore().setCurrentMedia(this.attachment)
this.$store.dispatch('setCurrentMedia', this.attachment)
} else if (this.type === 'unknown') {
window.open(this.attachment.url)
}
},
openModalForce () {
openModalForce (event) {
this.$emit('setMedia')
useMediaViewerStore().setCurrentMedia(this.attachment)
this.$store.dispatch('setCurrentMedia', this.attachment)
},
onEdit (event) {
this.edit && this.edit(this.attachment, event)

View file

@ -1,10 +1,9 @@
export default {
name: 'Attachment',
selector: '.Attachment',
notEditable: true,
validInnerComponents: [
'Border',
'Button',
'ButtonUnstyled',
'Input'
],
defaultRules: [
@ -14,7 +13,7 @@ export default {
}
},
{
component: 'Button',
component: 'ButtonUnstyled',
parent: { component: 'Attachment' },
directives: {
background: '#FFFFFF',

View file

@ -1,7 +1,7 @@
<template>
<button
v-if="usePlaceholder"
class="Attachment -placeholder button-default"
class="Attachment -placeholder button-unstyled"
:class="classNames"
@click="openModal"
>
@ -23,7 +23,7 @@
>
<button
v-if="remove"
class="button-default attachment-button"
class="button-unstyled attachment-button"
@click.prevent="onRemove"
>
<FAIcon icon="trash-alt" />
@ -81,7 +81,7 @@
>
<button
v-if="type === 'flash' && flashLoaded"
class="button-default attachment-button"
class="button-unstyled attachment-button"
:title="$t('status.attachment_stop_flash')"
@click.prevent="stopFlash"
>
@ -89,7 +89,7 @@
</button>
<button
v-if="attachment.description && size !== 'small' && !edit && type !== 'unknown'"
class="button-default attachment-button"
class="button-unstyled attachment-button"
:title="$t('status.show_attachment_description')"
@click.prevent="toggleDescription"
>
@ -97,7 +97,7 @@
</button>
<button
v-if="!useModal && type !== 'unknown'"
class="button-default attachment-button"
class="button-unstyled attachment-button"
:title="$t('status.show_attachment_in_modal')"
@click.prevent="openModalForce"
>
@ -105,7 +105,7 @@
</button>
<button
v-if="nsfw && hideNsfwLocal"
class="button-default attachment-button"
class="button-unstyled attachment-button"
:title="$t('status.hide_attachment')"
@click.prevent="toggleHidden"
>
@ -113,7 +113,7 @@
</button>
<button
v-if="shiftUp"
class="button-default attachment-button"
class="button-unstyled attachment-button"
:title="$t('status.move_up')"
@click.prevent="onShiftUp"
>
@ -121,7 +121,7 @@
</button>
<button
v-if="shiftDn"
class="button-default attachment-button"
class="button-unstyled attachment-button"
:title="$t('status.move_down')"
@click.prevent="onShiftDn"
>
@ -129,7 +129,7 @@
</button>
<button
v-if="remove"
class="button-default attachment-button"
class="button-unstyled attachment-button"
:title="$t('status.remove_attachment')"
@click.prevent="onRemove"
>
@ -238,8 +238,8 @@
ref="flash"
class="flash"
:src="attachment.large_thumb_url || attachment.url"
@player-opened="setFlashLoaded(true)"
@player-closed="setFlashLoaded(false)"
@playerOpened="setFlashLoaded(true)"
@playerClosed="setFlashLoaded(false)"
/>
</span>
</div>

View file

@ -48,7 +48,7 @@
flex: 1 0;
margin: 0;
--emoji-size: 1em;
--emoji-size: 14px;
&-collapsed-content {
margin-left: 0.7em;

View file

@ -63,7 +63,7 @@ const BookmarkFolderEdit = {
this.$router.push({ name: 'bookmark-folders' })
})
.catch((e) => {
this.$store.useInterfaceStore().pushGlobalNotice({
this.$store.dispatch('pushGlobalNotice', {
messageKey: 'bookmark_folders.error',
messageArgs: [e.message],
level: 'error'

View file

@ -13,11 +13,10 @@
icon="chevron-left"
/>
</button>
<h1 class="title">
<div class="title">
<i18n-t
v-if="id"
keypath="bookmark_folders.editing_folder"
scope="global"
>
<template #folderName>
{{ name }}
@ -26,9 +25,8 @@
<i18n-t
v-else
keypath="bookmark_folders.creating_folder"
scope="global"
/>
</h1>
</div>
</div>
<div class="panel-body">
<div class="input-wrap">

View file

@ -1,9 +1,9 @@
<template>
<div class="Bookmark-folders panel panel-default">
<div class="panel-heading">
<h1 class="title">
<div class="title">
{{ $t('nav.bookmark_folders') }}
</h1>
</div>
<router-link
:to="{ name: 'bookmark-folder-new' }"
class="button-default btn new-folder-button"

View file

@ -3,9 +3,6 @@ import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
import { getBookmarkFolderEntries } from 'src/components/navigation/filter.js'
export const BookmarkFoldersMenuContent = {
props: [
'showPin'
],
components: {
NavigationEntry
},

View file

@ -7,12 +7,10 @@
label: 'nav.all_bookmarks',
icon: 'bookmark'
}"
:show-pin="showPin"
/>
<NavigationEntry
v-for="item in folders"
:key="item.id"
:show-pin="showPin"
:item="item"
/>
</ul>

View file

@ -5,7 +5,7 @@ export default {
defaultRules: [
{
directives: {
textColor: '$mod(--parent 10)',
textColor: '$mod(--parent, 10)',
textAuto: 'no-auto'
}
}

View file

@ -9,9 +9,9 @@ export default {
// However, cascading still works, so resulting state will be result of merging of all relevant states/variants
// normal: '' // normal state is implicitly added, it is always included
toggled: '.toggled',
focused: ':focus-visible',
pressed: ':focus:active',
pressed: ':active',
hover: ':hover:not(:disabled)',
focused: ':focus-within',
disabled: ':disabled'
},
// Variants are mutually exclusive, each component implicitly has "normal" variant, and all other variants inherit from it.
@ -22,9 +22,6 @@ export default {
// Overall the compuation difficulty is N*((1/6)M^3+M) where M is number of distinct states and N is number of variants.
// This (currently) is further multipled by number of places where component can exist.
},
editor: {
aspect: '2 / 1'
},
// This lists all other components that can possibly exist within one. Recursion is currently not supported (and probably won't be supported ever).
validInnerComponents: [
'Text',
@ -35,11 +32,10 @@ export default {
{
component: 'Root',
directives: {
'--buttonDefaultHoverGlow': 'shadow | 0 0 1 2 --text / 0.4',
'--buttonDefaultFocusGlow': 'shadow | 0 0 1 2 --link / 0.5',
'--buttonDefaultShadow': 'shadow | 0 0 2 #000000',
'--buttonDefaultBevel': 'shadow | $borderSide(#FFFFFF top 0.2 1), $borderSide(#000000 bottom 0.2 1)',
'--buttonPressedBevel': 'shadow | inset 0 0 4 #000000, $borderSide(#FFFFFF bottom 0.2 1), $borderSide(#000000 top 0.2 1)'
'--defaultButtonHoverGlow': 'shadow | 0 0 4 --text',
'--defaultButtonShadow': 'shadow | 0 0 2 #000000',
'--defaultButtonBevel': 'shadow | $borderSide(#FFFFFF, top, 0.2), $borderSide(#000000, bottom, 0.2)',
'--pressedButtonBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2), $borderSide(#000000, top, 0.2)'
}
},
{
@ -47,60 +43,47 @@ export default {
// like within it
directives: {
background: '--fg',
shadow: ['--buttonDefaultShadow', '--buttonDefaultBevel'],
shadow: ['--defaultButtonShadow', '--defaultButtonBevel'],
roundness: 3
}
},
{
state: ['hover'],
directives: {
shadow: ['--buttonDefaultHoverGlow', '--buttonDefaultBevel']
}
},
{
state: ['focused'],
directives: {
shadow: ['--buttonDefaultFocusGlow', '--buttonDefaultBevel']
shadow: ['--defaultButtonHoverGlow', '--defaultButtonBevel']
}
},
{
state: ['pressed'],
directives: {
shadow: ['--buttonDefaultShadow', '--buttonPressedBevel']
shadow: ['--defaultButtonShadow', '--pressedButtonBevel']
}
},
{
state: ['pressed', 'hover'],
state: ['hover', 'pressed'],
directives: {
shadow: ['--buttonPressedBevel', '--buttonDefaultHoverGlow']
shadow: ['--defaultButtonHoverGlow', '--pressedButtonBevel']
}
},
{
state: ['toggled'],
directives: {
background: '--accent,-24.2',
shadow: ['--buttonDefaultShadow', '--buttonPressedBevel']
background: '--inheritedBackground,-14.2',
shadow: ['--defaultButtonShadow', '--pressedButtonBevel']
}
},
{
state: ['toggled', 'hover'],
directives: {
background: '--accent,-24.2',
shadow: ['--buttonDefaultHoverGlow', '--buttonPressedBevel']
}
},
{
state: ['toggled', 'disabled'],
directives: {
background: '$blend(--accent 0.25 --parent)',
shadow: ['--buttonPressedBevel']
background: '--inheritedBackground,-14.2',
shadow: ['--defaultButtonHoverGlow', '--pressedButtonBevel']
}
},
{
state: ['disabled'],
directives: {
background: '$blend(--accent 0.25 --parent)',
shadow: ['--buttonDefaultBevel']
background: '$blend(--inheritedBackground, 0.25, --parent)',
shadow: ['--defaultButtonBevel']
}
},
{

View file

@ -1,8 +1,6 @@
export default {
name: 'ButtonUnstyled',
selector: '.button-unstyled',
notEditable: true,
transparent: true,
states: {
toggled: '.toggled',
disabled: ':disabled',
@ -11,13 +9,14 @@ export default {
},
validInnerComponents: [
'Text',
'Link',
'Icon',
'Badge'
],
defaultRules: [
{
directives: {
background: '#ffffff',
opacity: 0,
shadow: []
}
},

View file

@ -1,7 +1,6 @@
import _ from 'lodash'
import { WSConnectionStatus } from '../../services/api/api.service.js'
import { mapGetters, mapState } from 'vuex'
import { mapState as mapPiniaState } from 'pinia'
import ChatMessage from '../chat_message/chat_message.vue'
import PostStatusForm from '../post_status_form/post_status_form.vue'
import ChatTitle from '../chat_title/chat_title.vue'
@ -14,7 +13,6 @@ import {
faChevronLeft
} from '@fortawesome/free-solid-svg-icons'
import { buildFakeMessage } from '../../services/chat_utils/chat_utils.js'
import { useInterfaceStore } from 'src/stores/interface.js'
library.add(
faChevronDown,
@ -92,12 +90,10 @@ const Chat = {
'findOpenedChatByRecipientId',
'mergedConfig'
]),
...mapPiniaState(useInterfaceStore, {
mobileLayout: store => store.layoutType === 'mobile'
}),
...mapState({
backendInteractor: state => state.api.backendInteractor,
mastoUserSocketStatus: state => state.api.mastoUserSocketStatus,
mobileLayout: state => state.interface.layoutType === 'mobile',
currentUser: state => state.users.currentUser
})
},

View file

@ -76,7 +76,6 @@
:disable-sensitivity-checkbox="true"
:disable-submit="errorLoadingChat || !currentChat"
:disable-preview="true"
:disable-draft="true"
:optimistic-posting="true"
:post-handler="sendMessage"
:submit-on-enter="!mobileLayout"
@ -95,4 +94,6 @@
</template>
<script src="./chat.js"></script>
<style src="./chat.scss" lang="scss" />
<style lang="scss">
@import "./chat";
</style>

View file

@ -7,9 +7,9 @@
class="chat-list panel panel-default"
>
<div class="panel-heading -sticky">
<h1 class="title">
<span class="title">
{{ $t("chats.chats") }}
</h1>
</span>
<button
class="button-default"
@click="newChat"
@ -34,7 +34,7 @@
</div>
<div
v-else
class="empty-chat-list-alert"
class="emtpy-chat-list-alert"
>
<span>{{ $t('chats.empty_chat_list_placeholder') }}</span>
</div>
@ -50,7 +50,7 @@
margin-bottom: 0;
}
.empty-chat-list-alert {
.emtpy-chat-list-alert {
padding: 3em;
font-size: 1.2em;
display: flex;

View file

@ -52,7 +52,7 @@ const ChatListItem = {
}
},
methods: {
openChat () {
openChat (_e) {
if (this.chat.id) {
this.$router.push({
name: 'chat',

View file

@ -47,4 +47,6 @@
<script src="./chat_list_item.js"></script>
<style src="./chat_list_item.scss" lang="scss" />
<style lang="scss">
@import "./chat_list_item";
</style>

View file

@ -1,5 +1,4 @@
import { mapState, mapGetters } from 'vuex'
import { mapState as mapPiniaState } from 'pinia'
import Popover from '../popover/popover.vue'
import Attachment from '../attachment/attachment.vue'
import UserAvatar from '../user_avatar/user_avatar.vue'
@ -13,7 +12,6 @@ import {
faTimes,
faEllipsisH
} from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from 'src/stores/interface'
library.add(
faTimes,
@ -67,10 +65,8 @@ const ChatMessage = {
hasAttachment () {
return this.message.attachments.length > 0
},
...mapPiniaState(useInterfaceStore, {
betterShadow: store => store.browserSupport.cssFilter
}),
...mapState({
betterShadow: state => state.interface.browserSupport.cssFilter,
currentUser: state => state.users.currentUser,
restrictedNicknames: state => state.instance.restrictedNicknames
}),

View file

@ -20,6 +20,7 @@
>
<UserAvatar
:compact="true"
:better-shadow="betterShadow"
:user="author"
/>
</UserPopover>
@ -51,14 +52,12 @@
>
<template #content>
<div class="dropdown-menu">
<div class="menu-item dropdown-item -icon">
<button
class="main-button"
@click="deleteMessage"
>
<FAIcon icon="times" /> {{ $t("chats.delete") }}
</button>
</div>
<button
class="menu-item dropdown-item dropdown-item-icon"
@click="deleteMessage"
>
<FAIcon icon="times" /> {{ $t("chats.delete") }}
</button>
</div>
</template>
<template #trigger>
@ -98,5 +97,7 @@
</template>
<script src="./chat_message.js"></script>
<style lang="scss">
@import "./chat_message";
<style src="./chat_message.scss" lang="scss" />
</style>

View file

@ -47,5 +47,6 @@
</template>
<script src="./chat_new.js"></script>
<style src="./chat_new.scss" lang="scss" />
<style lang="scss">
@import "./chat_new";
</style>

View file

@ -32,7 +32,7 @@
text-overflow: ellipsis;
white-space: nowrap;
--emoji-size: 1em;
--emoji-size: 14px;
.username {
max-width: 100%;

View file

@ -53,7 +53,7 @@ export default {
}
},
methods: {
onTransitionEnd () {
onTransitionEnd (e) {
if (!this.indeterminate) {
this.indeterminateTransitionFix = false
}
@ -63,40 +63,35 @@ export default {
</script>
<style lang="scss">
@import "../../mixins";
.checkbox {
position: relative;
display: inline-block;
min-height: 1.2em;
&-indicator,
& .label {
vertical-align: middle;
}
& > &-indicator {
/* Reset .input stuff */
padding: 0;
margin: 0;
position: relative;
line-height: inherit;
display: inline-block;
width: 1.2em;
height: 1.2em;
display: inline;
padding-left: 1.2em;
box-shadow: none;
--_shadow: var(--shadow);
}
&-indicator::before {
position: absolute;
inset: 0;
right: 0;
top: 0;
display: block;
content: "✓";
transition: color 200ms;
width: 1.1em;
height: 1.1em;
border-radius: var(--roundness);
box-shadow: var(--_shadow);
box-shadow: var(--shadow);
background-color: var(--background);
vertical-align: top;
text-align: center;

View file

@ -5,10 +5,6 @@
flex: 1 1 auto;
}
.opt {
margin-right: 0.5em;
}
&-field.input {
display: inline-flex;
flex: 0 0 0;

View file

@ -11,11 +11,11 @@
{{ label }}
</label>
<Checkbox
v-if="typeof fallback !== 'undefined' && showOptionalCheckbox && !hideOptionalCheckbox"
v-if="typeof fallback !== 'undefined' && showOptionalTickbox"
:model-value="present"
:disabled="disabled"
class="opt"
@update:model-value="updateValue(typeof modelValue === 'undefined' ? fallback : undefined)"
@update:modelValue="update(typeof modelValue === 'undefined' ? fallback : undefined)"
/>
<div
class="input color-input-field"
@ -112,16 +112,10 @@ export default {
default: false
},
// Show "optional" tickbox, for when value might become mandatory
showOptionalCheckbox: {
showOptionalTickbox: {
required: false,
type: Boolean,
default: true
},
// Force "optional" tickbox to hide
hideOptionalCheckbox: {
required: false,
type: Boolean,
default: false
}
},
emits: ['update:modelValue'],
@ -136,7 +130,7 @@ export default {
return this.modelValue === 'transparent'
},
computedColor () {
return this.modelValue && (this.modelValue.startsWith('--') || this.modelValue.startsWith('$'))
return this.modelValue && this.modelValue.startsWith('--')
}
},
methods: {

View file

@ -1,193 +1,88 @@
<template>
<div
class="ComponentPreview"
:class="{ '-shadow-controls': shadowControl }"
<div
class="ComponentPreview"
:class="{ '-shadow-controls': shadowControl }"
>
<label
class="header"
v-show="shadowControl"
:class="{ faint: disabled }"
>
{{ $t('settings.style.shadows.offset') }}
</label>
<input
v-show="shadowControl"
:value="shadow?.y"
:disabled="disabled"
:class="{ disabled }"
class="input input-number y-shift-number"
type="number"
@input="e => updateProperty('y', e.target.value)"
>
<input
v-show="shadowControl"
:value="shadow?.y"
:disabled="disabled"
:class="{ disabled }"
class="input input-range y-shift-slider"
type="range"
max="20"
min="-20"
@input="e => updateProperty('y', e.target.value)"
>
<div
class="preview-window"
:class="{ '-light-grid': lightGrid }"
>
<!-- eslint-disable vue/no-v-html vue/no-v-text-v-html-on-component -->
<component
:is="'style'"
v-html="previewCss"
/>
<!-- eslint-enable vue/no-v-html vue/no-v-text-v-html-on-component -->
<label
v-show="shadowControl"
role="heading"
class="header"
:class="{ faint: disabled }"
>
{{ $t('settings.style.shadows.offset') }}
</label>
<label
v-show="shadowControl && !hideControls"
class="x-shift-number"
>
{{ $t('settings.style.shadows.offset-x') }}
<input
:value="shadow?.x"
:disabled="disabled"
:class="{ disabled }"
class="input input-number"
type="number"
@input="e => updateProperty('x', e.target.value)"
>
</label>
<label
v-show="shadowControl && !hideControls"
class="y-shift-number"
>
{{ $t('settings.style.shadows.offset-y') }}
<input
:value="shadow?.y"
:disabled="disabled"
:class="{ disabled }"
class="input input-number"
type="number"
@input="e => updateProperty('y', e.target.value)"
>
</label>
<input
v-show="shadowControl && !hideControls"
:value="shadow?.x"
:disabled="disabled"
:class="{ disabled }"
class="input input-range x-shift-slider"
type="range"
max="20"
min="-20"
@input="e => updateProperty('x', e.target.value)"
>
<input
v-show="shadowControl && !hideControls"
:value="shadow?.y"
:disabled="disabled"
:class="{ disabled }"
class="input input-range y-shift-slider"
type="range"
max="20"
min="-20"
@input="e => updateProperty('y', e.target.value)"
>
<div
class="preview-window"
:class="{ '-light-grid': lightGrid }"
>
<div
class="preview-block"
:class="previewClass"
:style="style"
>
{{ $t('settings.style.themes3.editor.test_string') }}
</div>
<div
v-if="invalid"
class="invalid-container"
>
<div class="alert error invalid-label">
{{ $t('settings.style.themes3.editor.invalid') }}
</div>
</div>
</div>
<div class="assists">
<Checkbox
v-model="lightGrid"
name="lightGrid"
class="input-light-grid"
>
{{ $t('settings.style.shadows.light_grid') }}
</Checkbox>
<div class="style-control">
<label class="label">
{{ $t('settings.style.shadows.zoom') }}
</label>
<input
v-model="zoom"
class="input input-number y-shift-number"
type="number"
>
</div>
<ColorInput
v-if="!noColorControl"
v-model="colorOverride"
class="input-color-input"
fallback="#606060"
:label="$t('settings.style.shadows.color_override')"
/>
</div>
class="preview-block"
:style="previewStyle"
/>
</div>
<input
v-show="shadowControl"
:value="shadow?.x"
:disabled="disabled"
:class="{ disabled }"
class="input input-number x-shift-number"
type="number"
@input="e => updateProperty('x', e.target.value)"
>
<input
v-show="shadowControl"
:value="shadow?.x"
:disabled="disabled"
:class="{ disabled }"
class="input input-range x-shift-slider"
type="range"
max="20"
min="-20"
@input="e => updateProperty('x', e.target.value)"
>
<Checkbox
id="lightGrid"
v-model="lightGrid"
:disabled="shadow == null"
name="lightGrid"
class="input-light-grid"
>
{{ $t('settings.style.shadows.light_grid') }}
</Checkbox>
</div>
</template>
<script>
import Checkbox from 'src/components/checkbox/checkbox.vue'
import ColorInput from 'src/components/color_input/color_input.vue'
export default {
components: {
Checkbox,
ColorInput
},
props: [
'shadow',
'shadowControl',
'previewClass',
'previewStyle',
'previewCss',
'disabled',
'invalid',
'noColorControl'
],
emits: ['update:shadow'],
data () {
return {
colorOverride: undefined,
lightGrid: false,
zoom: 100
}
},
computed: {
style () {
const result = [
this.previewStyle,
`zoom: ${this.zoom / 100}`
]
if (this.colorOverride) result.push(`--background: ${this.colorOverride}`)
return result
},
hideControls () {
return typeof this.shadow === 'string'
}
},
methods: {
updateProperty (axis, value) {
this.$emit('update:shadow', { axis, value: Number(value) })
}
}
}
</script>
<style lang="scss">
.ComponentPreview {
display: grid;
grid-template-columns: 1em 1fr 1fr 1em;
grid-template-rows: 2em 1fr 1fr 1fr 1em 2em max-content;
grid-template-columns: 3em 1fr 3em;
grid-template-rows: 2em 1fr 2em;
grid-template-areas:
"header header header header "
"preview preview preview y-slide"
"preview preview preview y-slide"
"preview preview preview y-slide"
"x-slide x-slide x-slide . "
"x-num x-num y-num y-num "
"assists assists assists assists";
". header y-num "
". preview y-slide"
"x-num x-slide . "
"options options options";
grid-gap: 0.5em;
&:not(.-shadow-controls) {
grid-template-areas:
"header header header header "
"preview preview preview y-slide"
"preview preview preview y-slide"
"preview preview preview y-slide"
"assists assists assists assists";
grid-template-rows: 2em 1fr 1fr 1fr max-content;
}
.header {
grid-area: header;
justify-self: center;
@ -195,31 +90,8 @@ export default {
line-height: 2;
}
.invalid-container {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: grid;
align-items: center;
justify-items: center;
background-color: rgba(100 0 0 / 50%);
.alert {
padding: 0.5em 1em;
}
}
.assists {
grid-area: assists;
display: grid;
grid-auto-flow: rows;
grid-auto-rows: 2em;
grid-gap: 0.5em;
}
.input-light-grid {
grid-area: options;
justify-self: center;
}
@ -229,19 +101,6 @@ export default {
.x-shift-number {
grid-area: x-num;
justify-self: right;
}
.y-shift-number {
grid-area: y-num;
justify-self: left;
}
.x-shift-number,
.y-shift-number {
input {
max-width: 4em;
}
}
.x-shift-slider {
@ -251,6 +110,10 @@ export default {
min-width: 10em;
}
.y-shift-number {
grid-area: y-num;
}
.y-shift-slider {
grid-area: y-slide;
writing-mode: vertical-lr;
@ -276,7 +139,6 @@ export default {
--__grid-color2-disabled: rgba(255 255 255 / 20%);
}
position: relative;
grid-area: preview;
aspect-ratio: 1;
display: flex;
@ -321,3 +183,30 @@ export default {
}
}
</style>
<script>
import Checkbox from 'src/components/checkbox/checkbox.vue'
export default {
props: [
'shadow',
'shadowControl',
'previewClass',
'previewStyle',
'disabled'
],
data () {
return {
lightGrid: false
}
},
emits: ['update:shadow'],
components: {
Checkbox
},
methods: {
updateProperty (axis, value) {
this.$emit('update:shadow', { axis, value })
}
}
}
</script>

View file

@ -22,7 +22,6 @@ const ConfirmModal = {
type: String
}
},
emits: ['cancelled', 'accepted'],
computed: {
},
methods: {

View file

@ -1,8 +1,8 @@
<template>
<DialogModal
<dialog-modal
v-body-scroll-lock="true"
class="confirm-modal"
@cancel="onCancel"
:on-cancel="onCancel"
>
<template #header>
<span v-text="title" />
@ -23,7 +23,7 @@
v-text="cancelText"
/>
</template>
</DialogModal>
</dialog-modal>
</template>
<script src="./confirm_modal.js"></script>

View file

@ -1,109 +0,0 @@
import { unitToSeconds } from 'src/services/date_utils/date_utils.js'
import { mapGetters } from 'vuex'
import ConfirmModal from './confirm_modal.vue'
import Select from 'src/components/select/select.vue'
export default {
props: ['type', 'user', 'status'],
emits: ['hide', 'show', 'muted'],
data: () => ({
showing: false,
muteExpiryAmount: 2,
muteExpiryUnit: 'hours'
}),
components: {
ConfirmModal,
Select
},
computed: {
muteExpiryValue () {
unitToSeconds(this.muteExpiryUnit, this.muteExpiryAmount)
},
muteExpiryUnits () {
return ['minutes', 'hours', 'days']
},
domain () {
return this.user.fqn.split('@')[1]
},
keypath () {
if (this.type === 'domain') {
return 'status.mute_domain_confirm'
} else if (this.type === 'conversation') {
return 'status.mute_conversation_confirm'
} else {
return 'user_card.mute_confirm'
}
},
userIsMuted () {
return this.$store.getters.relationship(this.user.id).muting
},
conversationIsMuted () {
return this.status.conversation_muted
},
domainIsMuted () {
return new Set(this.$store.state.users.currentUser.domainMutes).has(this.domain)
},
shouldConfirm () {
switch (this.type) {
case 'domain': {
return this.mergedConfig.modalOnMuteDomain
}
case 'conversation': {
return this.mergedConfig.modalOnMuteConversation
}
default: {
return this.mergedConfig.modalOnMute
}
}
},
...mapGetters(['mergedConfig'])
},
methods: {
optionallyPrompt () {
if (this.shouldConfirm) {
this.show()
} else {
this.doMute()
}
},
show () {
this.showing = true
this.$emit('show')
},
hide () {
this.showing = false
this.$emit('hide')
},
doMute () {
switch (this.type) {
case 'domain': {
if (!this.domainIsMuted) {
this.$store.dispatch('muteDomain', { id: this.domain, expiresIn: this.muteExpiryValue })
} else {
this.$store.dispatch('unmuteDomain', { id: this.domain })
}
break
}
case 'conversation': {
if (!this.conversationIsMuted) {
this.$store.dispatch('muteConversation', { id: this.status.id, expiresIn: this.muteExpiryValue })
} else {
this.$store.dispatch('unmuteConversation', { id: this.status.id })
}
break
}
default: {
if (!this.userIsMuted) {
this.$store.dispatch('muteUser', { id: this.user.id, expiresIn: this.muteExpiryValue })
} else {
this.$store.dispatch('unmuteUser', { id: this.user.id })
}
break
}
}
this.$emit('muted')
this.hide()
}
}
}

View file

@ -1,61 +0,0 @@
<template>
<confirm-modal
v-if="showing"
:title="$t('user_card.mute_confirm_title')"
:confirm-text="$t('user_card.mute_confirm_accept_button')"
:cancel-text="$t('user_card.mute_confirm_cancel_button')"
@accepted="doMute"
@cancelled="hide"
>
<i18n-t
:keypath="keypath"
tag="div"
>
<template #domain>
<span v-text="domain" />
</template>
<template #user>
<span v-text="user.screen_name_ui" />
</template>
</i18n-t>
<div
v-if="type !== 'domain'"
class="mute-expiry"
>
<p>
<label>
{{ $t('user_card.mute_duration_prompt') }}
</label>
<input
v-model="muteExpiryAmount"
type="number"
class="input expiry-amount hide-number-spinner"
:min="0"
>
{{ ' ' }}
<Select
v-model="muteExpiryUnit"
unstyled="true"
class="expiry-unit"
>
<option
v-for="unit in muteExpiryUnits"
:key="unit"
:value="unit"
>
{{ $t(`time.unit.${unit}_short`, ['']) }}
</option>
</Select>
</p>
</div>
</confirm-modal>
</template>
<script src="./mute_confirm.js" />
<style lang="scss">
.expiry-amount {
width: 4em;
text-align: right;
}
</style>

View file

@ -3,62 +3,39 @@
v-if="contrast"
class="contrast-ratio"
>
<span v-if="showRatio">
{{ contrast.text }}
</span>
<Tooltip
:text="hint"
<span
:title="hint"
class="rating"
>
<span v-if="contrast.aaa">
<FAIcon
icon="thumbs-up"
:size="showRatio ? 'lg' : ''"
/>
<FAIcon icon="thumbs-up" />
</span>
<span v-if="!contrast.aaa && contrast.aa">
<FAIcon
icon="adjust"
:size="showRatio ? 'lg' : ''"
/>
<FAIcon icon="adjust" />
</span>
<span v-if="!contrast.aaa && !contrast.aa">
<FAIcon
icon="exclamation-triangle"
:size="showRatio ? 'lg' : ''"
/>
<FAIcon icon="exclamation-triangle" />
</span>
</Tooltip>
<Tooltip
</span>
<span
v-if="contrast && large"
:text="hint_18pt"
class="rating"
:title="hint_18pt"
>
<span v-if="contrast.laaa">
<FAIcon
icon="thumbs-up"
:size="showRatio ? 'large' : ''"
/>
<FAIcon icon="thumbs-up" />
</span>
<span v-if="!contrast.laaa && contrast.laa">
<FAIcon
icon="adjust"
:size="showRatio ? 'lg' : ''"
/>
<FAIcon icon="adjust" />
</span>
<span v-if="!contrast.laaa && !contrast.laa">
<FAIcon
icon="exclamation-triangle"
:size="showRatio ? 'lg' : ''"
/>
<FAIcon icon="exclamation-triangle" />
</span>
</Tooltip>
</span>
</span>
</template>
<script>
import Tooltip from 'src/components/tooltip/tooltip.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faAdjust,
@ -73,9 +50,6 @@ library.add(
)
export default {
components: {
Tooltip
},
props: {
large: {
required: false,
@ -88,11 +62,6 @@ export default {
required: false,
type: Object,
default: () => ({})
},
showRatio: {
required: false,
type: Boolean,
default: false
}
},
computed: {
@ -118,7 +87,8 @@ export default {
.contrast-ratio {
display: flex;
justify-content: flex-end;
align-items: baseline;
margin-top: -4px;
margin-bottom: 5px;
.label {
margin-right: 1em;
@ -126,6 +96,7 @@ export default {
.rating {
display: inline-block;
text-align: center;
margin-left: 0.5em;
}
}

View file

@ -3,10 +3,8 @@ import Status from '../status/status.vue'
import ThreadTree from '../thread_tree/thread_tree.vue'
import { WSConnectionStatus } from '../../services/api/api.service.js'
import { mapGetters, mapState } from 'vuex'
import { mapState as mapPiniaState } from 'pinia'
import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings.vue'
import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue'
import { useInterfaceStore } from 'src/stores/interface'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
@ -114,7 +112,7 @@ const conversation = {
suspendable () {
if (this.isTreeView) {
return Object.entries(this.statusContentProperties)
.every(([, prop]) => !prop.replying && prop.mediaPlaying.length === 0)
.every(([k, prop]) => !prop.replying && prop.mediaPlaying.length === 0)
}
if (this.$refs.statusComponent && this.$refs.statusComponent[0]) {
return this.$refs.statusComponent.every(s => s.suspendable)
@ -272,8 +270,11 @@ const conversation = {
},
replies () {
let i = 1
return reduce(this.conversation, (result, { id, in_reply_to_status_id: irid }) => {
// eslint-disable-next-line camelcase
return reduce(this.conversation, (result, { id, in_reply_to_status_id }) => {
/* eslint-disable camelcase */
const irid = in_reply_to_status_id
/* eslint-enable camelcase */
if (irid) {
result[irid] = result[irid] || []
result[irid].push({
@ -350,9 +351,6 @@ const conversation = {
...mapGetters(['mergedConfig']),
...mapState({
mastoUserSocketStatus: state => state.api.mastoUserSocketStatus
}),
...mapPiniaState(useInterfaceStore, {
mobileLayout: store => store.layoutType === 'mobile'
})
},
components: {
@ -378,7 +376,7 @@ const conversation = {
this.resetDisplayState()
}
},
virtualHidden () {
virtualHidden (value) {
this.$store.dispatch(
'setVirtualHeight',
{ statusId: this.statusId, height: `${this.$el.clientHeight}px` }
@ -474,7 +472,7 @@ const conversation = {
// nothing found, fall back to toplevel
return this.topLevel[0] ? this.topLevel[0].id : undefined
},
diveIntoStatus (id) {
diveIntoStatus (id, preventScroll) {
this.tryScrollTo(id)
},
diveToTopLevel () {

View file

@ -9,9 +9,7 @@
v-if="isExpanded"
class="panel-heading conversation-heading -sticky"
>
<h1 class="title">
{{ $t('timeline.conversation') }}
</h1>
<span class="title"> {{ $t('timeline.conversation') }} </span>
<button
v-if="collapsable"
class="button-unstyled -link"
@ -20,7 +18,7 @@
{{ $t('timeline.collapse') }}
</button>
<QuickFilterSettings
v-if="!collapsable && mobileLayout"
v-if="!collapsable"
:conversation="true"
class="rightside-button"
/>
@ -73,7 +71,7 @@
</template>
<template #text>
<span>
{{ $t('status.show_all_conversation', { numStatus: otherTopLevelCount }, otherTopLevelCount) }}
{{ $tc('status.show_all_conversation', otherTopLevelCount, { numStatus: otherTopLevelCount }) }}
</span>
</template>
</i18n-t>
@ -123,7 +121,7 @@
:controlled-set-media-playing="(newVal) => toggleStatusContentProperty(status.id, 'mediaPlaying', newVal)"
@goto="setHighlight"
@toggle-expanded="toggleExpanded"
@toggleExpanded="toggleExpanded"
/>
<div
v-if="showOtherRepliesButtonBelowStatus && getReplies(status.id).length > 1"
@ -146,7 +144,7 @@
</template>
<template #text>
<span>
{{ $t('status.ancestor_follow', { numReplies: getReplies(status.id, getReplies(status.id).length - 1).length - 1 }) }}
{{ $tc('status.ancestor_follow', getReplies(status.id).length - 1, { numReplies: getReplies(status.id).length - 1 }) }}
</span>
</template>
</i18n-t>
@ -217,7 +215,7 @@
:toggle-status-content-property="toggleStatusContentProperty"
@goto="setHighlight"
@toggle-expanded="toggleExpanded"
@toggleExpanded="toggleExpanded"
/>
</article>
</div>

View file

@ -14,7 +14,6 @@ import {
faCog,
faInfoCircle
} from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from 'src/stores/interface'
library.add(
faSignInAlt,
@ -108,10 +107,10 @@ export default {
this.searchBarHidden = hidden
},
openSettingsModal () {
useInterfaceStore().openSettingsModal('user')
this.$store.dispatch('openSettingsModal', 'user')
},
openAdminModal () {
useInterfaceStore().openSettingsModal('admin')
this.$store.dispatch('openSettingsModal', 'admin')
}
}
}

View file

@ -6,6 +6,10 @@
color: var(--inputTopbarText, var(--inputText));
}
a {
color: var(--link);
}
.inner-nav {
display: grid;
grid-template-rows: var(--navbar-height);
@ -87,6 +91,10 @@
width: 2em;
height: 100%;
text-align: center;
.svg-inline--fa {
color: var(--link);
}
}
.sitename {

Some files were not shown because too many files have changed in this diff Show more