Merge remote-tracking branch 'origin/develop' into navigation-update

* origin/develop: (49 commits)
  Fix react & extra buttons not styled on tab-focus
  Fix popover not popping up
  Fix styling on Safari
  Use :focus-visible instead of :focus for focus markers
  Optimize Reply badge position
  Add badges to status interacting buttons
  Update dependency nightwatch to v2
  Update dependency eslint-plugin-n to v15.2.5
  Update dependency mocha to v10
  Update dependency karma-coverage to v2
  Update dependency sass to v1.54.5
  Update dependency karma-firefox-launcher to v2
  Update dependency vue-template-compiler to v2.7.9
  Pin dependencies
  Refresh yarn.lock
  Allow column width configuration: allow stretching navbar with columns
  Remove legacy code for chunksSortMode
  Add FIXME comment about html-webpack-plugin-after-emit
  Use exact webpack version in package.json
  Reintroduce css minimizer
  ...
This commit is contained in:
Henry Jameson 2022-08-23 00:35:25 +03:00
commit 7b6745bb84
39 changed files with 2409 additions and 3714 deletions

View File

@ -29,18 +29,20 @@ var devMiddleware = require('webpack-dev-middleware')(compiler, {
}) })
var hotMiddleware = require('webpack-hot-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' }) // FIXME: The statement below gives error about hooks being required in webpack 5.
// cb() // 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 // proxy api requests
Object.keys(proxyTable).forEach(function (context) { Object.keys(proxyTable).forEach(function (context) {
@ -48,7 +50,7 @@ Object.keys(proxyTable).forEach(function (context) {
if (typeof options === 'string') { if (typeof options === 'string') {
options = { target: options } options = { target: options }
} }
app.use(proxyMiddleware(context, options)) app.use(proxyMiddleware.createProxyMiddleware(context, options))
}) })
// handle fallback for HTML5 history API // handle fallback for HTML5 history API

View File

@ -2,7 +2,7 @@ var path = require('path')
var config = require('../config') var config = require('../config')
var utils = require('./utils') var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../') var projectRoot = path.resolve(__dirname, '../')
var ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin') var ServiceWorkerWebpackPlugin = require('serviceworker-webpack5-plugin')
var CopyPlugin = require('copy-webpack-plugin'); var CopyPlugin = require('copy-webpack-plugin');
var { VueLoaderPlugin } = require('vue-loader') var { VueLoaderPlugin } = require('vue-loader')
var ESLintPlugin = require('eslint-webpack-plugin'); var ESLintPlugin = require('eslint-webpack-plugin');
@ -42,6 +42,10 @@ module.exports = {
'assets': path.resolve(__dirname, '../src/assets'), 'assets': path.resolve(__dirname, '../src/assets'),
'components': path.resolve(__dirname, '../src/components'), 'components': path.resolve(__dirname, '../src/components'),
'vue-i18n': 'vue-i18n/dist/vue-i18n.runtime.esm-bundler.js' 'vue-i18n': 'vue-i18n/dist/vue-i18n.runtime.esm-bundler.js'
},
fallback: {
'querystring': require.resolve('querystring-es3'),
'url': require.resolve('url/')
} }
}, },
module: { module: {
@ -78,22 +82,16 @@ module.exports = {
}, },
{ {
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: { type: 'asset',
loader: 'url-loader', generator: {
options: { filename: utils.assetsPath('img/[name].[hash:7][ext]')
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
} }
}, },
{ {
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: { type: 'asset',
loader: 'url-loader', generator: {
options: { filename: utils.assetsPath('fonts/[name].[hash:7][ext]')
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
} }
}, },
{ {
@ -117,9 +115,8 @@ module.exports = {
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
{ {
from: "node_modules/@ruffle-rs/ruffle/*", from: "node_modules/@ruffle-rs/ruffle/**/*",
to: "static/ruffle", to: "static/ruffle/[name][ext]"
flatten: true
}, },
], ],
options: { options: {

View File

@ -16,7 +16,7 @@ module.exports = merge(baseWebpackConfig, {
}, },
mode: 'development', mode: 'development',
// eval-source-map is faster for development // eval-source-map is faster for development
devtool: '#eval-source-map', devtool: 'eval-source-map',
plugins: [ plugins: [
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env': config.dev.env, 'process.env': config.dev.env,

View File

@ -5,6 +5,7 @@ var webpack = require('webpack')
var merge = require('webpack-merge') var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf') var baseWebpackConfig = require('./webpack.base.conf')
var MiniCssExtractPlugin = require('mini-css-extract-plugin') var MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
var HtmlWebpackPlugin = require('html-webpack-plugin') var HtmlWebpackPlugin = require('html-webpack-plugin')
var env = process.env.NODE_ENV === 'testing' var env = process.env.NODE_ENV === 'testing'
? require('../config/test.env') ? require('../config/test.env')
@ -19,12 +20,16 @@ var webpackConfig = merge(baseWebpackConfig, {
module: { module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, extract: true }) rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, extract: true })
}, },
devtool: config.build.productionSourceMap ? '#source-map' : false, devtool: config.build.productionSourceMap ? 'source-map' : false,
optimization: { optimization: {
minimize: true, minimize: true,
splitChunks: { splitChunks: {
chunks: 'all' chunks: 'all'
} },
minimizer: [
`...`,
new CssMinimizerPlugin()
]
}, },
output: { output: {
path: config.build.assetsRoot, path: config.build.assetsRoot,
@ -60,9 +65,7 @@ var webpackConfig = merge(baseWebpackConfig, {
ignoreCustomComments: [/server-generated-meta/] ignoreCustomComments: [/server-generated-meta/]
// more options: // more options:
// https://github.com/kangax/html-minifier#options-quick-reference // https://github.com/kangax/html-minifier#options-quick-reference
}, }
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}), }),
// split vendor js into its own file // split vendor js into its own file
// extract webpack runtime and module manifest to its own file in order to // extract webpack runtime and module manifest to its own file in order to

View File

@ -23,8 +23,8 @@
"@fortawesome/free-solid-svg-icons": "6.1.2", "@fortawesome/free-solid-svg-icons": "6.1.2",
"@fortawesome/vue-fontawesome": "3.0.1", "@fortawesome/vue-fontawesome": "3.0.1",
"@kazvmoe-infra/pinch-zoom-element": "1.2.0", "@kazvmoe-infra/pinch-zoom-element": "1.2.0",
"@ruffle-rs/ruffle": "^0.1.0-nightly.2022.7.12", "@ruffle-rs/ruffle": "0.1.0-nightly.2022.7.12",
"@vuelidate/core": "2.0.0-alpha.43", "@vuelidate/core": "2.0.0-alpha.44",
"@vuelidate/validators": "2.0.0-alpha.31", "@vuelidate/validators": "2.0.0-alpha.31",
"body-scroll-lock": "3.1.5", "body-scroll-lock": "3.1.5",
"chromatism": "3.0.0", "chromatism": "3.0.0",
@ -32,95 +32,95 @@
"cropperjs": "1.5.12", "cropperjs": "1.5.12",
"diff": "3.5.0", "diff": "3.5.0",
"escape-html": "1.0.3", "escape-html": "1.0.3",
"js-cookie": "^3.0.1", "js-cookie": "3.0.1",
"localforage": "1.10.0", "localforage": "1.10.0",
"parse-link-header": "1.0.1", "parse-link-header": "2.0.0",
"phoenix": "1.6.2", "phoenix": "1.6.2",
"punycode.js": "2.1.0", "punycode.js": "2.1.0",
"qrcode": "1", "qrcode": "1.5.0",
"utf8": "^3.0.0", "querystring-es3": "0.2.1",
"url": "0.11.0",
"utf8": "3.0.0",
"vue": "3.2.37", "vue": "3.2.37",
"vue-i18n": "9.2.0", "vue-i18n": "9.2.2",
"vue-router": "4.1.3", "vue-router": "4.1.3",
"vue-template-compiler": "2.7.8", "vue-template-compiler": "2.7.9",
"vuex": "4.0.2" "vuex": "4.0.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.18.10", "@babel/core": "7.18.10",
"@babel/eslint-parser": "7.18.9",
"@babel/plugin-transform-runtime": "7.18.10", "@babel/plugin-transform-runtime": "7.18.10",
"@babel/preset-env": "7.18.10", "@babel/preset-env": "7.18.10",
"@babel/register": "7.18.9", "@babel/register": "7.18.9",
"@babel/eslint-parser": "7.18.9", "@intlify/vue-i18n-loader": "5.0.0",
"@intlify/vue-i18n-loader": "^5.0.0",
"@ungap/event-target": "0.2.3", "@ungap/event-target": "0.2.3",
"@vue/babel-helper-vue-jsx-merge-props": "1.2.1", "@vue/babel-helper-vue-jsx-merge-props": "1.2.1",
"@vue/babel-plugin-jsx": "1.1.1", "@vue/babel-plugin-jsx": "1.1.1",
"@vue/compiler-sfc": "3.2.37", "@vue/compiler-sfc": "3.2.37",
"@vue/test-utils": "2.0.2", "@vue/test-utils": "2.0.2",
"autoprefixer": "6.7.7", "autoprefixer": "10.4.8",
"babel-loader": "8.2.5", "babel-loader": "8.2.5",
"babel-plugin-lodash": "3.3.4", "babel-plugin-lodash": "3.3.4",
"chai": "3.5.0", "chai": "4.3.6",
"chalk": "1.1.3", "chalk": "1.1.3",
"chromedriver": "103.0.0", "chromedriver": "104.0.0",
"connect-history-api-fallback": "1.6.0", "connect-history-api-fallback": "2.0.0",
"copy-webpack-plugin": "6.4.1", "copy-webpack-plugin": "11.0.0",
"cross-spawn": "4.0.2", "cross-spawn": "7.0.3",
"css-loader": "0.28.11", "css-loader": "6.7.1",
"css-minimizer-webpack-plugin": "4.0.0",
"custom-event-polyfill": "1.0.7", "custom-event-polyfill": "1.0.7",
"eslint": "8.20.0", "eslint": "8.22.0",
"eslint-config-standard": "17.0.0", "eslint-config-standard": "17.0.0",
"eslint-formatter-friendly": "7.0.0", "eslint-formatter-friendly": "7.0.0",
"eslint-webpack-plugin": "2.7.0",
"eslint-plugin-import": "2.26.0", "eslint-plugin-import": "2.26.0",
"eslint-plugin-n": "15.2.4", "eslint-plugin-n": "15.2.5",
"eslint-plugin-promise": "6.0.0", "eslint-plugin-promise": "6.0.0",
"eslint-plugin-vue": "9.3.0", "eslint-plugin-vue": "9.3.0",
"eslint-webpack-plugin": "3.2.0",
"eventsource-polyfill": "0.9.6", "eventsource-polyfill": "0.9.6",
"express": "4.18.1", "express": "4.18.1",
"file-loader": "3.0.1",
"function-bind": "1.1.1", "function-bind": "1.1.1",
"html-webpack-plugin": "3.2.0", "html-webpack-plugin": "5.5.0",
"http-proxy-middleware": "0.21.0", "http-proxy-middleware": "2.0.6",
"inject-loader": "2.0.1",
"iso-639-1": "2.1.15", "iso-639-1": "2.1.15",
"isparta-loader": "2.0.0", "isparta-loader": "2.0.0",
"json-loader": "0.5.7", "json-loader": "0.5.7",
"karma": "6.4.0", "karma": "6.4.0",
"karma-coverage": "1.1.2", "karma-coverage": "2.2.0",
"karma-firefox-launcher": "1.3.0", "karma-firefox-launcher": "2.1.2",
"karma-mocha": "2.0.1", "karma-mocha": "2.0.1",
"karma-mocha-reporter": "2.2.5", "karma-mocha-reporter": "2.2.5",
"karma-sinon-chai": "2.0.2", "karma-sinon-chai": "2.0.2",
"karma-sourcemap-loader": "0.3.8", "karma-sourcemap-loader": "0.3.8",
"karma-spec-reporter": "0.0.34", "karma-spec-reporter": "0.0.34",
"karma-webpack": "4.0.2", "karma-webpack": "5.0.0",
"lodash": "4.17.21", "lodash": "4.17.21",
"lolex": "1.6.0", "lolex": "1.6.0",
"mini-css-extract-plugin": "0.12.0", "mini-css-extract-plugin": "2.6.1",
"mocha": "3.5.3", "mocha": "10.0.0",
"nightwatch": "0.9.21", "nightwatch": "2.3.3",
"opn": "4.0.2", "opn": "4.0.2",
"ora": "0.4.1", "ora": "0.4.1",
"postcss-loader": "3.0.0", "postcss": "8.4.16",
"raw-loader": "0.5.1", "postcss-loader": "7.0.1",
"sass": "1.54.0", "sass": "1.54.5",
"sass-loader": "7.3.1", "sass-loader": "13.0.2",
"selenium-server": "2.53.1", "selenium-server": "2.53.1",
"semver": "5.7.1", "semver": "5.7.1",
"serviceworker-webpack-plugin": "1.0.1", "serviceworker-webpack5-plugin": "2.0.0",
"shelljs": "0.8.5", "shelljs": "0.8.5",
"sinon": "2.4.1", "sinon": "2.4.1",
"sinon-chai": "2.14.0", "sinon-chai": "2.14.0",
"stylelint": "13.13.1", "stylelint": "13.13.1",
"stylelint-config-standard": "20.0.0", "stylelint-config-standard": "20.0.0",
"stylelint-rscss": "0.4.0", "stylelint-rscss": "0.4.0",
"url-loader": "1.1.2", "vue-loader": "17.0.0",
"vue-loader": "^16.0.0",
"vue-style-loader": "4.1.3", "vue-style-loader": "4.1.3",
"webpack": "4.46.0", "webpack": "5.74.0",
"webpack-dev-middleware": "3.7.3", "webpack-dev-middleware": "3.7.3",
"webpack-hot-middleware": "2.25.1", "webpack-hot-middleware": "2.25.2",
"webpack-merge": "0.20.0" "webpack-merge": "0.20.0"
}, },
"engines": { "engines": {

View File

@ -60,6 +60,13 @@ export default {
'-' + this.layoutType '-' + this.layoutType
] ]
}, },
navClasses () {
const { navbarColumnStretch } = this.$store.getters.mergedConfig
return [
'-' + this.layoutType,
...(navbarColumnStretch ? ['-column-stretch'] : [])
]
},
currentUser () { return this.$store.state.users.currentUser }, currentUser () { return this.$store.state.users.currentUser },
userBackground () { return this.currentUser.background_image }, userBackground () { return this.currentUser.background_image },
instanceBackground () { instanceBackground () {

View File

@ -189,13 +189,18 @@ nav {
.app-layout { .app-layout {
--miniColumn: 25rem; --miniColumn: 25rem;
--maxiColumn: minmax(var(--miniColumn), 45rem); --maxiColumn: 45rem;
--columnGap: 1em; --columnGap: 1em;
--status-margin: 0.75em; --status-margin: 0.75em;
--effectiveSidebarColumnWidth: minmax(var(--miniColumn), var(--sidebarColumnWidth, var(--miniColumn)));
--effectiveNotifsColumnWidth: minmax(var(--miniColumn), var(--notifsColumnWidth, var(--miniColumn)));
--effectiveContentColumnWidth: minmax(var(--miniColumn), var(--contentColumnWidth, var(--maxiColumn)));
position: relative; position: relative;
display: grid; display: grid;
grid-template-columns: var(--miniColumn) var(--maxiColumn); grid-template-columns:
var(--effectiveSidebarColumnWidth)
var(--effectiveContentColumnWidth);
grid-template-areas: "sidebar content"; grid-template-areas: "sidebar content";
grid-template-rows: 1fr; grid-template-rows: 1fr;
box-sizing: border-box; box-sizing: border-box;
@ -289,15 +294,24 @@ nav {
} }
&.-reverse:not(.-wide):not(.-mobile) { &.-reverse:not(.-wide):not(.-mobile) {
grid-template-columns: var(--maxiColumn) var(--miniColumn); grid-template-columns:
var(--effectiveContentColumnWidth)
var(--effectiveSidebarColumnWidth);
grid-template-areas: "content sidebar"; grid-template-areas: "content sidebar";
} }
&.-wide { &.-wide {
grid-template-columns: var(--miniColumn) var(--maxiColumn) var(--miniColumn); grid-template-columns:
var(--effectiveSidebarColumnWidth)
var(--effectiveContentColumnWidth)
var(--effectiveNotifsColumnWidth);
grid-template-areas: "sidebar content notifs"; grid-template-areas: "sidebar content notifs";
&.-reverse { &.-reverse {
grid-template-columns:
var(--effectiveNotifsColumnWidth)
var(--effectiveContentColumnWidth)
var(--effectiveSidebarColumnWidth);
grid-template-areas: "notifs content sidebar"; grid-template-areas: "notifs content sidebar";
} }
} }
@ -760,11 +774,12 @@ option {
} }
.fa-old-padding { .fa-old-padding {
&.svg-inline--fa, &.iconLetter,
&.iconLetter { &.svg-inline--fa, &-layer {
padding: 0 0.3em; padding: 0 0.3em;
} }
} }
.veryfaint { .veryfaint {
opacity: 0.25; opacity: 0.25;
} }

View File

@ -8,7 +8,10 @@
class="app-bg-wrapper" class="app-bg-wrapper"
/> />
<MobileNav v-if="layoutType === 'mobile'" /> <MobileNav v-if="layoutType === 'mobile'" />
<DesktopNav v-else /> <DesktopNav
v-else
:class="navClasses"
/>
<Notifications v-if="currentUser" /> <Notifications v-if="currentUser" />
<div <div
id="content" id="content"

17
src/_mixins.scss Normal file
View File

@ -0,0 +1,17 @@
@mixin unfocused-style {
@content;
&:focus:not(:focus-visible):not(:hover) {
@content;
}
}
@mixin focused-style {
&:hover, &:focus {
@content;
}
&:focus-visible {
@content;
}
}

View File

@ -12,7 +12,7 @@ import { windowWidth, windowHeight } from '../services/window_utils/window_utils
import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js' import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js'
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js' import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
import { applyTheme } from '../services/style_setter/style_setter.js' import { applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
import FaviconService from '../services/favicon_service/favicon_service.js' import FaviconService from '../services/favicon_service/favicon_service.js'
let staticInitialResults = null let staticInitialResults = null
@ -360,6 +360,8 @@ const afterStoreSetup = async ({ store, i18n }) => {
console.error('Failed to load any theme!') console.error('Failed to load any theme!')
} }
applyConfig(store.state.config)
// Now we can try getting the server settings and logging in // Now we can try getting the server settings and logging in
// Most of these are preloaded into the index.html so blocking is minimized // Most of these are preloaded into the index.html so blocking is minimized
await Promise.all([ await Promise.all([

View File

@ -23,6 +23,26 @@
max-width: 980px; max-width: 980px;
} }
&.-column-stretch .inner-nav {
--miniColumn: 25rem;
--maxiColumn: 45rem;
--columnGap: 1em;
max-width: calc(
var(--sidebarColumnWidth, var(--miniColumn)) +
var(--contentColumnWidth, var(--maxiColumn)) +
var(--columnGap)
);
}
&.-column-stretch.-wide .inner-nav {
max-width: calc(
var(--sidebarColumnWidth, var(--miniColumn)) +
var(--contentColumnWidth, var(--maxiColumn)) +
var(--notifsColumnWidth, var(--miniColumn)) +
var(--columnGap)
);
}
&.-logoLeft .inner-nav { &.-logoLeft .inner-nav {
grid-template-columns: auto 2fr 2fr; grid-template-columns: auto 2fr 2fr;
grid-template-areas: "logo sitename actions"; grid-template-areas: "logo sitename actions";

View File

@ -6,7 +6,9 @@ import {
faEyeSlash, faEyeSlash,
faThumbtack, faThumbtack,
faShareAlt, faShareAlt,
faExternalLinkAlt faExternalLinkAlt,
faPlus,
faTimes
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { import {
faBookmark as faBookmarkReg, faBookmark as faBookmarkReg,
@ -21,13 +23,26 @@ library.add(
faThumbtack, faThumbtack,
faShareAlt, faShareAlt,
faExternalLinkAlt, faExternalLinkAlt,
faFlag faFlag,
faPlus,
faTimes
) )
const ExtraButtons = { const ExtraButtons = {
props: ['status'], props: ['status'],
components: { Popover }, components: { Popover },
data () {
return {
expanded: false
}
},
methods: { methods: {
onShow () {
this.expanded = true
},
onClose () {
this.expanded = false
},
deleteStatus () { deleteStatus () {
const confirmed = window.confirm(this.$t('status.delete_confirm')) const confirmed = window.confirm(this.$t('status.delete_confirm'))
if (confirmed) { if (confirmed) {

View File

@ -6,6 +6,8 @@
:offset="{ y: 5 }" :offset="{ y: 5 }"
:bound-to="{ x: 'container' }" :bound-to="{ x: 'container' }"
remove-padding remove-padding
@show="onShow"
@close="onClose"
> >
<template #content="{close}"> <template #content="{close}">
<div class="dropdown-menu"> <div class="dropdown-menu">
@ -122,10 +124,24 @@
</template> </template>
<template #trigger> <template #trigger>
<span class="button-unstyled popover-trigger"> <span class="button-unstyled popover-trigger">
<FALayers class="fa-old-padding-layer">
<FAIcon <FAIcon
class="fa-scale-110 fa-old-padding" class="fa-scale-110 "
icon="ellipsis-h" icon="ellipsis-h"
/> />
<FAIcon
v-show="!expanded"
class="focus-marker"
transform="shrink-6 up-8 right-16"
icon="plus"
/>
<FAIcon
v-show="expanded"
class="focus-marker"
transform="shrink-6 up-8 right-16"
icon="times"
/>
</FALayers>
</span> </span>
</template> </template>
</Popover> </Popover>
@ -135,6 +151,7 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_mixins.scss';
.ExtraButtons { .ExtraButtons {
/* override of popover internal stuff */ /* override of popover internal stuff */
@ -151,6 +168,21 @@
color: $fallback--text; color: $fallback--text;
color: var(--text, $fallback--text); color: var(--text, $fallback--text);
} }
}
.popover-trigger-button {
@include unfocused-style {
.focus-marker {
visibility: hidden;
}
}
@include focused-style {
.focus-marker {
visibility: visible;
}
}
} }
} }
</style> </style>

View File

@ -1,13 +1,21 @@
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faStar } from '@fortawesome/free-solid-svg-icons' import {
faStar,
faPlus,
faMinus,
faCheck
} from '@fortawesome/free-solid-svg-icons'
import { import {
faStar as faStarRegular faStar as faStarRegular
} from '@fortawesome/free-regular-svg-icons' } from '@fortawesome/free-regular-svg-icons'
library.add( library.add(
faStar, faStar,
faStarRegular faStarRegular,
faPlus,
faMinus,
faCheck
) )
const FavoriteButton = { const FavoriteButton = {

View File

@ -7,11 +7,31 @@
:title="$t('tool_tip.favorite')" :title="$t('tool_tip.favorite')"
@click.prevent="favorite()" @click.prevent="favorite()"
> >
<FALayers class="fa-scale-110 fa-old-padding-layer">
<FAIcon <FAIcon
class="fa-scale-110 fa-old-padding" class="fa-scale-110"
:icon="[status.favorited ? 'fas' : 'far', 'star']" :icon="[status.favorited ? 'fas' : 'far', 'star']"
:spin="animated" :spin="animated"
/> />
<FAIcon
v-if="status.favorited"
class="active-marker"
transform="shrink-6 up-9 right-12"
icon="check"
/>
<FAIcon
v-if="!status.favorited"
class="focus-marker"
transform="shrink-6 up-9 right-12"
icon="plus"
/>
<FAIcon
v-else
class="focus-marker"
transform="shrink-6 up-9 right-12"
icon="minus"
/>
</FALayers>
</button> </button>
<span v-else> <span v-else>
<FAIcon <FAIcon
@ -33,6 +53,7 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_mixins.scss';
.FavoriteButton { .FavoriteButton {
display: flex; display: flex;
@ -57,6 +78,26 @@
color: $fallback--cOrange; color: $fallback--cOrange;
color: var(--cOrange, $fallback--cOrange); color: var(--cOrange, $fallback--cOrange);
} }
@include unfocused-style {
.focus-marker {
visibility: hidden;
}
.active-marker {
visibility: visible;
}
}
@include focused-style {
.focus-marker {
visibility: visible;
}
.active-marker {
visibility: hidden;
}
}
} }
} }
</style> </style>

View File

@ -1,15 +1,21 @@
import Popover from '../popover/popover.vue' import Popover from '../popover/popover.vue'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faPlus, faTimes } from '@fortawesome/free-solid-svg-icons'
import { faSmileBeam } from '@fortawesome/free-regular-svg-icons' import { faSmileBeam } from '@fortawesome/free-regular-svg-icons'
import { trim } from 'lodash' import { trim } from 'lodash'
library.add(faSmileBeam) library.add(
faPlus,
faTimes,
faSmileBeam
)
const ReactButton = { const ReactButton = {
props: ['status'], props: ['status'],
data () { data () {
return { return {
filterWord: '' filterWord: '',
expanded: false
} }
}, },
components: { components: {
@ -25,6 +31,13 @@ const ReactButton = {
} }
close() close()
}, },
onShow () {
this.expanded = true
this.focusInput()
},
onClose () {
this.expanded = false
},
focusInput () { focusInput () {
this.$nextTick(() => { this.$nextTick(() => {
const input = this.$el.querySelector('input') const input = this.$el.querySelector('input')

View File

@ -7,7 +7,8 @@
:bound-to="{ x: 'container' }" :bound-to="{ x: 'container' }"
remove-padding remove-padding
popover-class="ReactButton popover-default" popover-class="ReactButton popover-default"
@show="focusInput" @show="onShow"
@close="onClose"
> >
<template #content="{close}"> <template #content="{close}">
<div class="reaction-picker-filter"> <div class="reaction-picker-filter">
@ -46,10 +47,24 @@
class="button-unstyled popover-trigger" class="button-unstyled popover-trigger"
:title="$t('tool_tip.add_reaction')" :title="$t('tool_tip.add_reaction')"
> >
<FALayers>
<FAIcon <FAIcon
class="fa-scale-110 fa-old-padding" class="fa-scale-110 fa-old-padding"
:icon="['far', 'smile-beam']" :icon="['far', 'smile-beam']"
/> />
<FAIcon
v-show="!expanded"
class="focus-marker"
transform="shrink-6 up-9 right-17"
icon="plus"
/>
<FAIcon
v-show="expanded"
class="focus-marker"
transform="shrink-6 up-9 right-17"
icon="times"
/>
</FALayers>
</span> </span>
</template> </template>
</Popover> </Popover>
@ -59,6 +74,7 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_mixins.scss';
.ReactButton { .ReactButton {
.reaction-picker-filter { .reaction-picker-filter {
@ -125,6 +141,21 @@
color: $fallback--text; color: $fallback--text;
color: var(--text, $fallback--text); color: var(--text, $fallback--text);
} }
}
.popover-trigger-button {
@include unfocused-style {
.focus-marker {
visibility: hidden;
}
}
@include focused-style {
.focus-marker {
visibility: visible;
}
}
} }
} }

View File

@ -1,7 +1,15 @@
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faReply } from '@fortawesome/free-solid-svg-icons' import {
faReply,
faPlus,
faTimes
} from '@fortawesome/free-solid-svg-icons'
library.add(faReply) library.add(
faReply,
faPlus,
faTimes
)
const ReplyButton = { const ReplyButton = {
name: 'ReplyButton', name: 'ReplyButton',

View File

@ -7,10 +7,24 @@
:title="$t('tool_tip.reply')" :title="$t('tool_tip.reply')"
@click.prevent="$emit('toggle')" @click.prevent="$emit('toggle')"
> >
<FALayers class="fa-old-padding-layer">
<FAIcon <FAIcon
class="fa-scale-110 fa-old-padding" class="fa-scale-110"
icon="reply" icon="reply"
/> />
<FAIcon
v-if="!replying"
class="focus-marker"
transform="shrink-6 up-8 right-11"
icon="plus"
/>
<FAIcon
v-else
class="focus-marker"
transform="shrink-6 up-8 right-11"
icon="times"
/>
</FALayers>
</button> </button>
<span v-else> <span v-else>
<FAIcon <FAIcon
@ -32,6 +46,7 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_mixins.scss';
.ReplyButton { .ReplyButton {
display: flex; display: flex;
@ -52,6 +67,18 @@
color: $fallback--cBlue; color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue); color: var(--cBlue, $fallback--cBlue);
} }
@include unfocused-style {
.focus-marker {
visibility: hidden;
}
}
@include focused-style {
.focus-marker {
visibility: visible;
}
}
} }
} }

View File

@ -1,7 +1,17 @@
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faRetweet } from '@fortawesome/free-solid-svg-icons' import {
faRetweet,
faPlus,
faMinus,
faCheck
} from '@fortawesome/free-solid-svg-icons'
library.add(faRetweet) library.add(
faRetweet,
faPlus,
faMinus,
faCheck
)
const RetweetButton = { const RetweetButton = {
props: ['status', 'loggedIn', 'visibility'], props: ['status', 'loggedIn', 'visibility'],

View File

@ -7,11 +7,31 @@
:title="$t('tool_tip.repeat')" :title="$t('tool_tip.repeat')"
@click.prevent="retweet()" @click.prevent="retweet()"
> >
<FALayers class="fa-old-padding-layer">
<FAIcon <FAIcon
class="fa-scale-110 fa-old-padding" class="fa-scale-110"
icon="retweet" icon="retweet"
:spin="animated" :spin="animated"
/> />
<FAIcon
v-if="status.repeated"
class="active-marker"
transform="shrink-6 up-9 right-12"
icon="check"
/>
<FAIcon
v-if="!status.repeated"
class="focus-marker"
transform="shrink-6 up-9 right-12"
icon="plus"
/>
<FAIcon
v-else
class="focus-marker"
transform="shrink-6 up-9 right-12"
icon="minus"
/>
</FALayers>
</button> </button>
<span v-else-if="loggedIn"> <span v-else-if="loggedIn">
<FAIcon <FAIcon
@ -40,6 +60,7 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../../_mixins.scss';
.RetweetButton { .RetweetButton {
display: flex; display: flex;
@ -64,6 +85,26 @@
color: $fallback--cGreen; color: $fallback--cGreen;
color: var(--cGreen, $fallback--cGreen); color: var(--cGreen, $fallback--cGreen);
} }
@include unfocused-style {
.focus-marker {
visibility: hidden;
}
.active-marker {
visibility: visible;
}
}
@include focused-style {
.focus-marker {
visibility: visible;
}
.active-marker {
visibility: hidden;
}
}
} }
} }
</style> </style>

View File

@ -42,6 +42,9 @@ export default {
methods: { methods: {
update (e) { update (e) {
set(this.$parent, this.path, e) set(this.$parent, this.path, e)
},
reset () {
set(this.$parent, this.path, this.defaultState)
} }
} }
} }

View File

@ -15,7 +15,12 @@
<slot /> <slot />
</span> </span>
{{ ' ' }} {{ ' ' }}
<ModifiedIndicator :changed="isChanged" /><ServerSideIndicator :server-side="isServerSide" /> </Checkbox> <ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ServerSideIndicator :server-side="isServerSide" />
</Checkbox>
</label> </label>
</template> </template>

View File

@ -43,6 +43,9 @@ export default {
methods: { methods: {
update (e) { update (e) {
set(this.$parent, this.path, e) set(this.$parent, this.path, e)
},
reset () {
set(this.$parent, this.path, this.defaultState)
} }
} }
} }

View File

@ -19,7 +19,10 @@
{{ option.value === defaultState ? $t('settings.instance_default_simple') : '' }} {{ option.value === defaultState ? $t('settings.instance_default_simple') : '' }}
</option> </option>
</Select> </Select>
<ModifiedIndicator :changed="isChanged" /> <ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ServerSideIndicator :server-side="isServerSide" /> <ServerSideIndicator :server-side="isServerSide" />
</label> </label>
</template> </template>

View File

@ -36,6 +36,9 @@ export default {
methods: { methods: {
update (e) { update (e) {
set(this.$parent, this.path, parseInt(e.target.value)) set(this.$parent, this.path, parseInt(e.target.value))
},
reset () {
set(this.$parent, this.path, this.defaultState)
} }
} }
} }

View File

@ -17,7 +17,10 @@
@change="update" @change="update"
> >
{{ ' ' }} {{ ' ' }}
<ModifiedIndicator :changed="isChanged" /> <ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
</span> </span>
</template> </template>

View File

@ -0,0 +1,67 @@
import { get, set } from 'lodash'
import ModifiedIndicator from './modified_indicator.vue'
import Select from 'src/components/select/select.vue'
export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%']
export const defaultHorizontalUnits = ['px', 'rem', 'vw']
export const defaultVerticalUnits = ['px', 'rem', 'vh']
export default {
components: {
ModifiedIndicator,
Select
},
props: {
path: String,
disabled: Boolean,
min: Number,
units: {
type: [String],
default: () => allCssUnits
},
expert: [Number, String]
},
computed: {
pathDefault () {
const [firstSegment, ...rest] = this.path.split('.')
return [firstSegment + 'DefaultValue', ...rest].join('.')
},
stateUnit () {
return (this.state || '').replace(/\d+/, '')
},
stateValue () {
return (this.state || '').replace(/\D+/, '')
},
state () {
const value = get(this.$parent, this.path)
if (value === undefined) {
return this.defaultState
} else {
return value
}
},
defaultState () {
return get(this.$parent, this.pathDefault)
},
isChanged () {
return this.state !== this.defaultState
},
matchesExpertLevel () {
return (this.expert || 0) <= this.$parent.expertLevel
}
},
methods: {
update (e) {
set(this.$parent, this.path, e)
},
reset () {
set(this.$parent, this.path, this.defaultState)
},
updateValue (e) {
set(this.$parent, this.path, parseInt(e.target.value) + this.stateUnit)
},
updateUnit (e) {
set(this.$parent, this.path, this.stateValue + e.target.value)
}
}
}

View File

@ -0,0 +1,54 @@
<template>
<span
v-if="matchesExpertLevel"
class="SizeSetting"
>
<label
:for="path"
class="size-label"
>
<slot />
</label>
<input
:id="path"
class="number-input"
type="number"
step="1"
:disabled="disabled"
:min="min || 0"
:value="stateValue"
@change="updateValue"
>
<Select
:id="path"
:model-value="stateUnit"
:disabled="disabled"
class="css-unit-input"
@change="updateUnit"
>
<option
v-for="option in units"
:key="option"
:value="option"
>
{{ option }}
</option>
</Select>
{{ ' ' }}
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
</span>
</template>
<script src="./size_setting.js"></script>
<style lang="scss">
.css-unit-input, .css-unit-input select {
margin-left: 0.5em;
width: 4em !important;
max-width: 4em !important;
min-width: 4em !important;
}
</style>

View File

@ -2,6 +2,7 @@ import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue'
import ScopeSelector from 'src/components/scope_selector/scope_selector.vue' import ScopeSelector from 'src/components/scope_selector/scope_selector.vue'
import IntegerSetting from '../helpers/integer_setting.vue' import IntegerSetting from '../helpers/integer_setting.vue'
import SizeSetting, { defaultHorizontalUnits } from '../helpers/size_setting.vue'
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue' import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js' import SharedComputedObject from '../helpers/shared_computed_object.js'
@ -56,11 +57,15 @@ const GeneralTab = {
BooleanSetting, BooleanSetting,
ChoiceSetting, ChoiceSetting,
IntegerSetting, IntegerSetting,
SizeSetting,
InterfaceLanguageSwitcher, InterfaceLanguageSwitcher,
ScopeSelector, ScopeSelector,
ServerSideIndicator ServerSideIndicator
}, },
computed: { computed: {
horizontalUnits () {
return defaultHorizontalUnits
},
postFormats () { postFormats () {
return this.$store.state.instance.postFormats || [] return this.$store.state.instance.postFormats || []
}, },
@ -71,6 +76,17 @@ const GeneralTab = {
label: this.$t(`post_status.content_type["${format}"]`) label: this.$t(`post_status.content_type["${format}"]`)
})) }))
}, },
columns () {
const mode = this.$store.getters.mergedConfig.thirdColumnMode
const notif = mode === 'none' ? [] : ['notifs']
if (this.$store.getters.mergedConfig.sidebarRight || mode === 'postform') {
return [...notif, 'content', 'sidebar']
} else {
return ['sidebar', 'content', ...notif]
}
},
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
instanceWallpaperUsed () { instanceWallpaperUsed () {
return this.$store.state.instance.background && return this.$store.state.instance.background &&

View File

@ -15,11 +15,6 @@
{{ $t('settings.hide_isp') }} {{ $t('settings.hide_isp') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li>
<BooleanSetting path="sidebarRight">
{{ $t('settings.right_sidebar') }}
</BooleanSetting>
</li>
<li v-if="instanceWallpaperUsed"> <li v-if="instanceWallpaperUsed">
<BooleanSetting path="hideInstanceWallpaper"> <BooleanSetting path="hideInstanceWallpaper">
{{ $t('settings.hide_wallpaper') }} {{ $t('settings.hide_wallpaper') }}
@ -64,16 +59,6 @@
{{ $t('settings.virtual_scrolling') }} {{ $t('settings.virtual_scrolling') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li>
<BooleanSetting path="disableStickyHeaders">
{{ $t('settings.disable_sticky_headers') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="showScrollbars">
{{ $t('settings.show_scrollbars') }}
</BooleanSetting>
</li>
<li> <li>
<BooleanSetting <BooleanSetting
path="userPopoverZoom" path="userPopoverZoom"
@ -90,16 +75,6 @@
{{ $t('settings.user_popover_avatar_overlay') }} {{ $t('settings.user_popover_avatar_overlay') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li>
<ChoiceSetting
v-if="user"
id="thirdColumnMode"
path="thirdColumnMode"
:options="thirdColumnModeOptions"
>
{{ $t('settings.third_column_mode') }}
</ChoiceSetting>
</li>
<li> <li>
<BooleanSetting <BooleanSetting
path="alwaysShowNewPostButton" path="alwaysShowNewPostButton"
@ -142,6 +117,11 @@
{{ $t('settings.right_sidebar') }} {{ $t('settings.right_sidebar') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li>
<BooleanSetting path="navbarColumnStretch">
{{ $t('settings.navbar_column_stretch') }}
</BooleanSetting>
</li>
<li> <li>
<ChoiceSetting <ChoiceSetting
v-if="user" v-if="user"
@ -475,3 +455,16 @@
</template> </template>
<script src="./general_tab.js"></script> <script src="./general_tab.js"></script>
<style lang="scss">
.column-settings {
display: flex;
justify-content: space-evenly;
flex-wrap: wrap;
}
.column-settings .size-label {
display: block;
margin-bottom: 0.5em;
margin-top: 0.5em;
}
</style>

View File

@ -417,6 +417,7 @@
"hide_isp": "Hide instance-specific panel", "hide_isp": "Hide instance-specific panel",
"hide_shoutbox": "Hide instance shoutbox", "hide_shoutbox": "Hide instance shoutbox",
"right_sidebar": "Reverse order of columns", "right_sidebar": "Reverse order of columns",
"navbar_column_stretch": "Stretch navbar to columns width",
"always_show_post_button": "Always show floating New Post button", "always_show_post_button": "Always show floating New Post button",
"hide_wallpaper": "Hide instance wallpaper", "hide_wallpaper": "Hide instance wallpaper",
"preload_images": "Preload images", "preload_images": "Preload images",
@ -538,6 +539,11 @@
"third_column_mode_none": "Don't show third column at all", "third_column_mode_none": "Don't show third column at all",
"third_column_mode_notifications": "Notifications column", "third_column_mode_notifications": "Notifications column",
"third_column_mode_postform": "Main post form and navigation", "third_column_mode_postform": "Main post form and navigation",
"columns": "Columns",
"column_sizes": "Column sizes",
"column_sizes_sidebar": "Sidebar",
"column_sizes_content": "Content",
"column_sizes_notifs": "Notifications",
"tree_advanced": "Allow more flexible navigation in tree view", "tree_advanced": "Allow more flexible navigation in tree view",
"tree_fade_ancestors": "Display ancestors of the current status in faint text", "tree_fade_ancestors": "Display ancestors of the current status in faint text",
"conversation_display_linear": "Linear-style", "conversation_display_linear": "Linear-style",

View File

@ -456,6 +456,15 @@
"subject_line_mastodon": "Как в Mastodon: скопировать как есть", "subject_line_mastodon": "Как в Mastodon: скопировать как есть",
"subject_line_email": "Как в электронной почте: \"re: тема\"", "subject_line_email": "Как в электронной почте: \"re: тема\"",
"subject_line_behavior": "Копировать тему в ответах", "subject_line_behavior": "Копировать тему в ответах",
"third_column_mode": "Когда недостаточно места, показывать третью колонку содержащую",
"third_column_mode_none": "Не показывать третью колонку совсем",
"third_column_mode_notifications": "Колонку уведомлений",
"third_column_mode_postform": "Форму отправки сообщения и навигацию",
"columns": "Колонки",
"column_sizes": "Размеры колонок",
"column_sizes_sidebar": "Боковой",
"column_sizes_content": "Содержимого",
"column_sizes_notifs": "Уведомлений",
"no_mutes": "Нет игнорируемых", "no_mutes": "Нет игнорируемых",
"no_blocks": "Нет блокировок", "no_blocks": "Нет блокировок",
"notification_visibility_emoji_reactions": "Реакции", "notification_visibility_emoji_reactions": "Реакции",

View File

@ -1,5 +1,5 @@
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import { setPreset, applyTheme } from '../services/style_setter/style_setter.js' import { setPreset, applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
import messages from '../i18n/messages' import messages from '../i18n/messages'
import localeService from '../services/locale/locale.service.js' import localeService from '../services/locale/locale.service.js'
@ -87,6 +87,7 @@ export const defaultState = {
sidebarColumnWidth: '25rem', sidebarColumnWidth: '25rem',
contentColumnWidth: '45rem', contentColumnWidth: '45rem',
notifsColumnWidth: '25rem', notifsColumnWidth: '25rem',
navbarColumnStretch: false,
greentext: undefined, // instance default greentext: undefined, // instance default
useAtIcon: undefined, // instance default useAtIcon: undefined, // instance default
mentionLinkDisplay: undefined, // instance default mentionLinkDisplay: undefined, // instance default
@ -164,12 +165,17 @@ const config = {
setHighlight ({ commit, dispatch }, { user, color, type }) { setHighlight ({ commit, dispatch }, { user, color, type }) {
commit('setHighlight', { user, color, type }) commit('setHighlight', { user, color, type })
}, },
setOption ({ commit, dispatch }, { name, value }) { setOption ({ commit, dispatch, state }, { name, value }) {
commit('setOption', { name, value }) commit('setOption', { name, value })
switch (name) { switch (name) {
case 'theme': case 'theme':
setPreset(value) setPreset(value)
break break
case 'sidebarColumnWidth':
case 'contentColumnWidth':
case 'notifsColumnWidth':
applyConfig(state)
break
case 'customTheme': case 'customTheme':
case 'customThemeSource': case 'customThemeSource':
applyTheme(value) applyTheme(value)

View File

@ -1,4 +1,4 @@
import runtime from 'serviceworker-webpack-plugin/lib/runtime' import runtime from 'serviceworker-webpack5-plugin/lib/runtime'
function urlBase64ToUint8Array (base64String) { function urlBase64ToUint8Array (base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4) const padding = '='.repeat((4 - base64String.length % 4) % 4)

View File

@ -1,6 +1,7 @@
import { convert } from 'chromatism' import { convert } from 'chromatism'
import { rgb2hex, hex2rgb, rgba2css, getCssColor, relativeLuminance } from '../color_convert/color_convert.js' import { rgb2hex, hex2rgb, rgba2css, getCssColor, relativeLuminance } from '../color_convert/color_convert.js'
import { getColors, computeDynamicColor, getOpacitySlot } from '../theme_data/theme_data.service.js' import { getColors, computeDynamicColor, getOpacitySlot } from '../theme_data/theme_data.service.js'
import { defaultState } from '../../modules/config.js'
export const applyTheme = (input) => { export const applyTheme = (input) => {
const { rules } = generatePreset(input) const { rules } = generatePreset(input)
@ -20,6 +21,36 @@ export const applyTheme = (input) => {
body.classList.remove('hidden') body.classList.remove('hidden')
} }
const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth }) =>
({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth })
const defaultConfigColumns = configColumns(defaultState)
export const applyConfig = (config) => {
const columns = configColumns(config)
if (columns === defaultConfigColumns) {
return
}
const head = document.head
const body = document.body
body.classList.add('hidden')
const rules = Object
.entries(columns)
.filter(([k, v]) => v)
.map(([k, v]) => `--${k}: ${v}`).join(';')
const styleEl = document.createElement('style')
head.appendChild(styleEl)
const styleSheet = styleEl.sheet
styleSheet.toString()
styleSheet.insertRule(`:root { ${rules} }`, 'index-max')
body.classList.remove('hidden')
}
export const getCssShadow = (input, usesDropShadow) => { export const getCssShadow = (input, usesDropShadow) => {
if (input.length === 0) { if (input.length === 0) {
return 'none' return 'none'

View File

@ -16,7 +16,7 @@ const webpackConfig = merge(baseConfig, {
module: { module: {
rules: utils.styleLoaders() rules: utils.styleLoaders()
}, },
devtool: '#inline-source-map', devtool: 'inline-source-map',
// vue: { // vue: {
// loaders: { // loaders: {
// js: 'isparta' // js: 'isparta'

View File

@ -195,7 +195,7 @@ describe('API Entities normalizer', () => {
expect(parsedPost).to.have.property('type', 'status') expect(parsedPost).to.have.property('type', 'status')
expect(parsedRepeat).to.have.property('type', 'retweet') expect(parsedRepeat).to.have.property('type', 'retweet')
expect(parsedRepeat).to.have.property('retweeted_status') 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', () => { it('sets nsfw for statuses with the #nsfw tag', () => {
@ -229,7 +229,7 @@ describe('API Entities normalizer', () => {
expect(parsedPost).to.have.property('type', 'status') expect(parsedPost).to.have.property('type', 'status')
expect(parsedRepeat).to.have.property('type', 'retweet') expect(parsedRepeat).to.have.property('type', 'retweet')
expect(parsedRepeat).to.have.property('retweeted_status') 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 +284,9 @@ describe('API Entities normalizer', () => {
}) })
expect(parseNotification(notif)).to.have.property('id', 123) expect(parseNotification(notif)).to.have.property('id', 123)
expect(parseNotification(notif)).to.have.property('seen', false) expect(parseNotification(notif)).to.have.property('seen', false)
expect(parseNotification(notif)).to.have.deep.property('status.id', '444') expect(parseNotification(notif)).to.have.nested.property('status.id', '444')
expect(parseNotification(notif)).to.have.deep.property('action.id', '444') expect(parseNotification(notif)).to.have.nested.property('action.id', '444')
expect(parseNotification(notif)).to.have.deep.property('from_profile.id', 'spurdo') expect(parseNotification(notif)).to.have.nested.property('from_profile.id', 'spurdo')
}) })
it('correctly normalizes favorite notifications', () => { it('correctly normalizes favorite notifications', () => {
@ -303,9 +303,9 @@ describe('API Entities normalizer', () => {
expect(parseNotification(notif)).to.have.property('id', 123) expect(parseNotification(notif)).to.have.property('id', 123)
expect(parseNotification(notif)).to.have.property('type', 'like') expect(parseNotification(notif)).to.have.property('type', 'like')
expect(parseNotification(notif)).to.have.property('seen', true) expect(parseNotification(notif)).to.have.property('seen', true)
expect(parseNotification(notif)).to.have.deep.property('status.id', '4412') expect(parseNotification(notif)).to.have.nested.property('status.id', '4412')
expect(parseNotification(notif)).to.have.deep.property('action.id', '444') expect(parseNotification(notif)).to.have.nested.property('action.id', '444')
expect(parseNotification(notif)).to.have.deep.property('from_profile.id', 'spurdo') expect(parseNotification(notif)).to.have.nested.property('from_profile.id', 'spurdo')
}) })
}) })

5321
yarn.lock

File diff suppressed because it is too large Load Diff