Compare commits

..

25 Commits

Author SHA1 Message Date
tusooa e5382781a4
Fix save draft button 2023-05-02 08:08:33 -04:00
tusooa 81cfcae433
Fix adding poll options not working 2023-05-02 08:06:55 -04:00
tusooa a73f9771ee
Do not popup dialog if autosave is on 2023-05-01 22:05:38 -04:00
tusooa 7ec3f49c3e
Give the option to auto save drafts 2023-05-01 22:05:38 -04:00
tusooa c9cacc32a4
Allow confirmation on closing edit form 2023-05-01 22:05:38 -04:00
tusooa f2bdc1c563
Allow confirmation on closing reply form 2023-05-01 22:05:38 -04:00
tusooa aa59adc81f
Use neutral badge for drafts 2023-05-01 22:05:38 -04:00
tusooa b3236e707c
Prevent main post form from being associated with draft on load 2023-05-01 22:05:38 -04:00
tusooa bae81f8d39
Fix poll duration unit 2023-05-01 22:05:38 -04:00
tusooa d884082761
Fix adding/removing poll options 2023-05-01 22:05:38 -04:00
tusooa 3c39269be8
Save only if status has something to save 2023-05-01 22:05:38 -04:00
tusooa 5e08c4537b
Clean up debug statements 2023-05-01 22:05:38 -04:00
tusooa 0256ff5f63
Save drafts permanently in local storage 2023-05-01 22:05:38 -04:00
tusooa 39b42da724
Add drafts to side drawer 2023-05-01 22:05:37 -04:00
tusooa 19b5bb06a0
Add draft count to nav panel 2023-05-01 22:05:37 -04:00
tusooa 3c828f3575
Use the word compose instead of edit in drafts 2023-05-01 22:05:37 -04:00
tusooa 5cddf18a4e
Handle polls in drafts 2023-05-01 22:05:37 -04:00
tusooa 4a044d067a
Handle situations where draft should be saved 2023-05-01 22:05:37 -04:00
tusooa 050266ee39
Abandon draft after all postings 2023-05-01 22:05:37 -04:00
tusooa 8d27c68d5f
Support editing 2023-05-01 22:05:37 -04:00
tusooa a9f374b18f
Display information about replied-to/edited status 2023-05-01 22:05:37 -04:00
tusooa f13444a3c8
Make it possible to abandon draft 2023-05-01 22:05:37 -04:00
tusooa dcd4587525
Add minimal draft management tool 2023-05-01 22:05:37 -04:00
tusooa d4444cd0b1
Save draft immediately before unmount 2023-05-01 22:05:37 -04:00
tusooa 4daa272fdf
Add basic draft saving 2023-05-01 22:05:14 -04:00
363 changed files with 4843 additions and 15828 deletions

1
.gitattributes vendored
View File

@ -1 +0,0 @@
/build/webpack.prod.conf.js export-subst

View File

@ -4,36 +4,11 @@
image: node:16 image: node:16
stages: stages:
- check-changelog
- lint - lint
- build - build
- test - test
- deploy - deploy
# https://git.pleroma.social/help/ci/yaml/workflow.md#switch-between-branch-pipelines-and-merge-request-pipelines
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
check-changelog:
stage: check-changelog
image: alpine
rules:
- if: $CI_MERGE_REQUEST_SOURCE_PROJECT_PATH == 'pleroma/pleroma-fe' && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^renovate/
when: never
- if: $CI_MERGE_REQUEST_SOURCE_PROJECT_PATH == 'pleroma/pleroma-fe' && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == 'weblate'
when: never
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"
before_script: ''
after_script: ''
cache: {}
script:
- apk add git
- sh ./tools/check-changelog
lint: lint:
stage: lint stage: lint
script: script:
@ -43,8 +18,6 @@ lint:
test: test:
stage: test stage: test
tags:
- amd64
variables: variables:
APT_CACHE_DIR: apt-cache APT_CACHE_DIR: apt-cache
script: script:
@ -56,8 +29,6 @@ test:
build: build:
stage: build stage: build
tags:
- amd64
script: script:
- yarn - yarn
- npm run build - npm run build

View File

@ -3,37 +3,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.6.1
### Fixed
- fix admin dashboard not having any feedback on frontend installation
- Fix frontend admin tab crashing when no primary frontend is set
- Add aria attributes to react and extra buttons
## 2.6.0
### Added
- add the initial i18n translation file for Taiwanese (Hokkien), and modify some related files.
- Implemented a very basic instance administration screen
- Implement quoting
### Fixed
- Keep aspect ratio of custom emoji reaction in notification
- Fix openSettingsModalTab so that it correctly opens Settings modal instead of Admin modal
- Add alt text to emoji picker buttons
- Use export-subst gitattribute to allow tarball builds
- fix reports now showing reason/content
- Fix HTML attribute parsing, discard attributes not strating with a letter
- Make MentionsLine aware of line breaking by non-br elements
- Fix a bug where mentioning a user twice will not fill the mention into the textarea
- Fix parsing non-ascii tags
- Fix OAuth2 token lingering after revocation
- fix regex issue in HTML parser/renderer
- don't display quoted status twice
- fix typo in code that prevented cards from showing at all
- Fix react button not working if reaction accounts are not loaded
- Fix react button misalignment on safari ios
- Fix pinned statuses gone when reloading user timeline
- Fix scrolling emoji selector in modal in safari ios
## 2.5.1 ## 2.5.1
### Fixed ### Fixed
- Checkboxes in settings can now work with screenreaders - Checkboxes in settings can now work with screenreaders

View File

@ -11,16 +11,9 @@ var env = process.env.NODE_ENV === 'testing'
? require('../config/test.env') ? require('../config/test.env')
: config.build.env : config.build.env
let commitHash = (() => { let commitHash = require('child_process')
const subst = "$Format:%h$"; .execSync('git rev-parse --short HEAD')
if(!subst.match(/Format:/)) { .toString();
return subst;
} else {
return require('child_process')
.execSync('git rev-parse --short HEAD')
.toString();
}
})();
var webpackConfig = merge(baseWebpackConfig, { var webpackConfig = merge(baseWebpackConfig, {
mode: 'production', mode: 'production',

View File

@ -1 +0,0 @@
Make Pleroma FE to also view apng (Animated PNG) attachment.

View File

@ -1 +0,0 @@
Added emoji pack management to the admin panel

View File

@ -1 +0,0 @@
stop using that one runner for intensive tasks

View File

@ -1 +0,0 @@
Create a link to the URL of the scrobble when it's present

View File

@ -1 +0,0 @@
Fix native notifications appearing as many times as there are open tabs. Clicking on notification will focus last focused tab.

View File

@ -1 +0,0 @@
Support showing extra notifications in the notifications column

View File

@ -1 +0,0 @@
Focusing into a tab clears all current desktop notifications

View File

@ -1 +0,0 @@
Support group actors

View File

@ -1 +0,0 @@
Allow hiding custom emojis in picker.

View File

@ -1 +0,0 @@
Fixed error that appeared on mobile Chrome(ium) (and derivatives) when native notifications are allowed

View File

@ -1 +0,0 @@
Added option to not mark all notifications when closing notifications drawer on mobile, this creates a new button to mark all as seen.

View File

@ -1 +0,0 @@
Fixed being unable to set notification visibility for reports and follow requests

View File

@ -1 +0,0 @@
Added ability to mute sensitive posts (ported from eintei)

View File

@ -1 +0,0 @@
Added option to toggle what notification types appear in native notifications, by default less important ones (likes, repeats, etc) will no longer show up in native notifications.

View File

@ -1 +0,0 @@
Native notifications now also have "badge" property that matches instance's favicon (visible in Android Chromium at least)

View File

@ -1 +0,0 @@
The expiry date indication won't be shown if the poll never expires

View File

@ -1 +0,0 @@
Added option to treat non-interactive notifications (likes, repeats et all) as seen for visual purposes (no read mark, ignored in counters, still can show in native notifications)

View File

@ -1 +0,0 @@
Synchronized requested notification types with backend, hopefully should fix missing notifications for polls and follow requests

View File

@ -1 +0,0 @@
Interacting (opening reply box etc) or simply clicking on non-interactive notifications now marks them as read. Clicking on native notifications for non-interactive ones also marks them as seen.

View File

@ -1 +0,0 @@
Notifications are no longer sorted by "seen" status since interacting with them can change their read status and makes UI jumpy. Old behavior can be restored in settings.

View File

@ -1 +0,0 @@
Add poll end notifications to fetched types.

View File

@ -1 +0,0 @@
Display public favorites on user profiles

View File

@ -1 +0,0 @@
Display quotes count on posts and add quotes list page

View File

@ -1 +0,0 @@
Show a dedicated registration notice page when further action is required after registering

View File

@ -1 +0,0 @@
Option to only show scrobbles that are recent enough

View File

@ -1 +0,0 @@
Notifications are now shown through a serviceworker (since mobile chrome does not allow them otherwise), it's always enabled, even if previously we only enabled it for WebPush notifications only. If you don't like websites "running" while closed, check how to disable them in your browser. Old way to show notifications will be used as a fallback but might not have all the new features.

View File

@ -1 +0,0 @@
Shows the most recent scrobble under each post when available

View File

@ -1 +0,0 @@
Display loading and error indicator for conversation page

View File

@ -1 +0,0 @@
Add caching system for themes3

View File

@ -1 +0,0 @@
fix color inputs and some in-development themes3 issues

View File

@ -1 +0,0 @@
Overhauled the way themes work, migrating to new Pleroma Interface Style Sheets system.

View File

@ -1 +0,0 @@
unread notifications should now properly catch up (eventually) in polling mode

View File

@ -1 +0,0 @@
Video posters on Safari

View File

@ -1 +0,0 @@
nothing

View File

@ -1 +0,0 @@
Added option to always "show" notifications when using web push for better compatibility with some browsers (chrome, edge, safari)

View File

@ -3,8 +3,8 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
<link rel="icon" type="image/png" href="/favicon.png">
<!--server-generated-meta--> <!--server-generated-meta-->
<link rel="icon" type="image/png" href="/favicon.png">
</head> </head>
<body class="hidden"> <body class="hidden">
<noscript>To use Pleroma, please enable JavaScript.</noscript> <noscript>To use Pleroma, please enable JavaScript.</noscript>

View File

@ -1,6 +1,6 @@
{ {
"name": "pleroma_fe", "name": "pleroma_fe",
"version": "2.6.1", "version": "2.5.0",
"description": "Pleroma frontend, the default frontend of Pleroma social network server", "description": "Pleroma frontend, the default frontend of Pleroma social network server",
"author": "Pleroma contributors <https://git.pleroma.social/pleroma/pleroma-fe/-/blob/develop/CONTRIBUTORS.md>", "author": "Pleroma contributors <https://git.pleroma.social/pleroma/pleroma-fe/-/blob/develop/CONTRIBUTORS.md>",
"private": false, "private": false,
@ -16,29 +16,28 @@
"lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs" "lint-fix": "eslint --fix --ext .js,.vue src test/unit/specs test/e2e/specs"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "7.21.5", "@babel/runtime": "7.21.0",
"@chenfengyuan/vue-qrcode": "2.0.0", "@chenfengyuan/vue-qrcode": "2.0.0",
"@fortawesome/fontawesome-svg-core": "6.4.0", "@fortawesome/fontawesome-svg-core": "6.3.0",
"@fortawesome/free-regular-svg-icons": "6.4.0", "@fortawesome/free-regular-svg-icons": "6.3.0",
"@fortawesome/free-solid-svg-icons": "6.4.0", "@fortawesome/free-solid-svg-icons": "6.3.0",
"@fortawesome/vue-fontawesome": "3.0.3", "@fortawesome/vue-fontawesome": "3.0.3",
"@kazvmoe-infra/pinch-zoom-element": "1.2.0", "@kazvmoe-infra/pinch-zoom-element": "1.2.0",
"@kazvmoe-infra/unicode-emoji-json": "0.4.0", "@kazvmoe-infra/unicode-emoji-json": "0.4.0",
"@ruffle-rs/ruffle": "0.1.0-nightly.2024.3.17", "@ruffle-rs/ruffle": "0.1.0-nightly.2022.7.12",
"@vuelidate/core": "2.0.3", "@vuelidate/core": "2.0.0",
"@vuelidate/validators": "2.0.4", "@vuelidate/validators": "2.0.0",
"body-scroll-lock": "3.1.5", "body-scroll-lock": "3.1.5",
"chromatism": "3.0.0", "chromatism": "3.0.0",
"click-outside-vue3": "4.0.1", "click-outside-vue3": "4.0.1",
"cropperjs": "1.5.13", "cropperjs": "1.5.13",
"escape-html": "1.0.3", "escape-html": "1.0.3",
"hash-sum": "^2.0.0", "js-cookie": "3.0.1",
"js-cookie": "3.0.5",
"localforage": "1.10.0", "localforage": "1.10.0",
"parse-link-header": "2.0.0", "parse-link-header": "2.0.0",
"phoenix": "1.7.7", "phoenix": "1.6.2",
"punycode.js": "2.3.0", "punycode.js": "2.3.0",
"qrcode": "1.5.3", "qrcode": "1.5.1",
"querystring-es3": "0.2.1", "querystring-es3": "0.2.1",
"url": "0.11.0", "url": "0.11.0",
"utf8": "3.0.0", "utf8": "3.0.0",
@ -50,19 +49,19 @@
"vuex": "4.1.0" "vuex": "4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.21.8", "@babel/core": "7.21.4",
"@babel/eslint-parser": "7.21.8", "@babel/eslint-parser": "7.21.3",
"@babel/plugin-transform-runtime": "7.21.4", "@babel/plugin-transform-runtime": "7.21.4",
"@babel/preset-env": "7.21.5", "@babel/preset-env": "7.21.4",
"@babel/register": "7.21.0", "@babel/register": "7.21.0",
"@intlify/vue-i18n-loader": "5.0.1", "@intlify/vue-i18n-loader": "5.0.1",
"@ungap/event-target": "0.2.4", "@ungap/event-target": "0.2.3",
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0", "@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
"@vue/babel-plugin-jsx": "1.2.1", "@vue/babel-plugin-jsx": "1.1.1",
"@vue/compiler-sfc": "3.2.45", "@vue/compiler-sfc": "3.2.45",
"@vue/test-utils": "2.2.8", "@vue/test-utils": "2.2.8",
"autoprefixer": "10.4.19", "autoprefixer": "10.4.14",
"babel-loader": "9.1.3", "babel-loader": "9.1.2",
"babel-plugin-lodash": "3.3.4", "babel-plugin-lodash": "3.3.4",
"chai": "4.3.7", "chai": "4.3.7",
"chalk": "1.1.3", "chalk": "1.1.3",
@ -70,7 +69,7 @@
"connect-history-api-fallback": "2.0.0", "connect-history-api-fallback": "2.0.0",
"copy-webpack-plugin": "11.0.0", "copy-webpack-plugin": "11.0.0",
"cross-spawn": "7.0.3", "cross-spawn": "7.0.3",
"css-loader": "6.10.0", "css-loader": "6.7.3",
"css-minimizer-webpack-plugin": "4.2.2", "css-minimizer-webpack-plugin": "4.2.2",
"custom-event-polyfill": "1.0.7", "custom-event-polyfill": "1.0.7",
"eslint": "8.33.0", "eslint": "8.33.0",
@ -84,11 +83,11 @@
"eventsource-polyfill": "0.9.6", "eventsource-polyfill": "0.9.6",
"express": "4.18.2", "express": "4.18.2",
"function-bind": "1.1.1", "function-bind": "1.1.1",
"html-webpack-plugin": "5.5.1", "html-webpack-plugin": "5.5.0",
"http-proxy-middleware": "2.0.6", "http-proxy-middleware": "2.0.6",
"iso-639-1": "2.1.15", "iso-639-1": "2.1.15",
"json-loader": "0.5.7", "json-loader": "0.5.7",
"karma": "6.4.2", "karma": "6.4.1",
"karma-coverage": "2.2.0", "karma-coverage": "2.2.0",
"karma-firefox-launcher": "2.1.2", "karma-firefox-launcher": "2.1.2",
"karma-mocha": "2.0.1", "karma-mocha": "2.0.1",
@ -98,22 +97,22 @@
"karma-spec-reporter": "0.0.36", "karma-spec-reporter": "0.0.36",
"karma-webpack": "5.0.0", "karma-webpack": "5.0.0",
"lodash": "4.17.21", "lodash": "4.17.21",
"mini-css-extract-plugin": "2.7.6", "mini-css-extract-plugin": "2.7.2",
"mocha": "10.2.0", "mocha": "10.2.0",
"nightwatch": "2.6.25", "nightwatch": "2.6.19",
"opn": "5.5.0", "opn": "5.5.0",
"ora": "0.4.1", "ora": "0.4.1",
"postcss": "8.4.23", "postcss": "8.4.20",
"postcss-html": "^1.5.0", "postcss-html": "^1.5.0",
"postcss-loader": "7.0.2", "postcss-loader": "7.0.2",
"postcss-scss": "^4.0.6", "postcss-scss": "^4.0.6",
"sass": "1.60.0", "sass": "1.60.0",
"sass-loader": "13.2.2", "sass-loader": "13.2.0",
"selenium-server": "2.53.1", "selenium-server": "2.53.1",
"semver": "7.3.8", "semver": "7.3.8",
"serviceworker-webpack5-plugin": "2.0.0", "serviceworker-webpack5-plugin": "2.0.0",
"shelljs": "0.8.5", "shelljs": "0.8.5",
"sinon": "15.0.4", "sinon": "15.0.1",
"sinon-chai": "3.7.0", "sinon-chai": "3.7.0",
"stylelint": "14.16.1", "stylelint": "14.16.1",
"stylelint-config-html": "^1.1.0", "stylelint-config-html": "^1.1.0",

View File

@ -1,10 +1,9 @@
// stylelint-disable rscss/class-format // stylelint-disable rscss/class-format
/* stylelint-disable no-descending-specificity */ /* stylelint-disable no-descending-specificity */
@import "./variables";
@import "./panel"; @import "./panel";
:root { :root {
--font-size: 14px;
--status-margin: 0.75em;
--navbar-height: 3.5rem; --navbar-height: 3.5rem;
--post-line-height: 1.4; --post-line-height: 1.4;
// Z-Index stuff // Z-Index stuff
@ -14,21 +13,19 @@
--ZI_navbar_popovers: 7500; --ZI_navbar_popovers: 7500;
--ZI_navbar: 7000; --ZI_navbar: 7000;
--ZI_popovers: 6000; --ZI_popovers: 6000;
// Fallback for when stuff is loading
--background: var(--bg);
} }
html { html {
font-size: var(--font-size); font-size: 14px;
// overflow-x: clip causes my browser's tab to crash with SIGILL lul // overflow-x: clip causes my browser's tab to crash with SIGILL lul
} }
body { body {
font-family: sans-serif; font-family: sans-serif;
font-family: var(--font); font-family: var(--interfaceFont, sans-serif);
margin: 0; margin: 0;
color: var(--text); color: $fallback--text;
color: var(--text, $fallback--text);
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
overscroll-behavior-y: none; overscroll-behavior-y: none;
@ -45,35 +42,17 @@ body {
// have a cursor/pointer to operate them // have a cursor/pointer to operate them
@media (any-pointer: fine) { @media (any-pointer: fine) {
* { * {
scrollbar-color: var(--fg) transparent; scrollbar-color: var(--btn) transparent;
&::-webkit-scrollbar { &::-webkit-scrollbar {
background: transparent; background: transparent;
} }
&::-webkit-scrollbar-corner {
background: transparent;
}
&::-webkit-resizer {
/* stylelint-disable-next-line declaration-no-important */
background-color: transparent !important;
background-image:
linear-gradient(
135deg,
transparent calc(50% - 1px),
var(--textFaint) 50%,
transparent calc(50% + 1px),
transparent calc(75% - 1px),
var(--textFaint) 75%,
transparent calc(75% + 1px),
);
}
&::-webkit-scrollbar-button, &::-webkit-scrollbar-button,
&::-webkit-scrollbar-thumb { &::-webkit-scrollbar-thumb {
box-shadow: var(--shadow); background-color: var(--btn);
border-radius: var(--roundness); box-shadow: var(--buttonShadow);
border-radius: var(--btnRadius);
} }
// horizontal/vertical/increment/decrement are webkit-specific stuff // horizontal/vertical/increment/decrement are webkit-specific stuff
@ -82,7 +61,7 @@ body {
&::-webkit-scrollbar-button { &::-webkit-scrollbar-button {
--___bgPadding: 2px; --___bgPadding: 2px;
color: var(--text); color: var(--btnText);
background-repeat: no-repeat, no-repeat; background-repeat: no-repeat, no-repeat;
&:horizontal { &:horizontal {
@ -90,15 +69,15 @@ body {
&:increment { &:increment {
background-image: background-image:
linear-gradient(45deg, var(--text) 50%, transparent 51%), linear-gradient(45deg, var(--btnText) 50%, transparent 51%),
linear-gradient(-45deg, transparent 50%, var(--text) 51%); linear-gradient(-45deg, transparent 50%, var(--btnText) 51%);
background-position: top var(--___bgPadding) left 50%, right 50% bottom var(--___bgPadding); background-position: top var(--___bgPadding) left 50%, right 50% bottom var(--___bgPadding);
} }
&:decrement { &:decrement {
background-image: background-image:
linear-gradient(45deg, transparent 50%, var(--text) calc(50% + 1px)), linear-gradient(45deg, transparent 50%, var(--btnText) 51%),
linear-gradient(-45deg, var(--text) 50%, transparent 51%); linear-gradient(-45deg, var(--btnText) 50%, transparent 51%);
background-position: bottom var(--___bgPadding) right 50%, left 50% top var(--___bgPadding); background-position: bottom var(--___bgPadding) right 50%, left 50% top var(--___bgPadding);
} }
} }
@ -108,15 +87,15 @@ body {
&:increment { &:increment {
background-image: background-image:
linear-gradient(-45deg, transparent 50%, var(--text) 51%), linear-gradient(-45deg, transparent 50%, var(--btnText) 51%),
linear-gradient(45deg, transparent 50%, var(--text) 51%); linear-gradient(45deg, transparent 50%, var(--btnText) 51%);
background-position: right var(--___bgPadding) top 50%, left var(--___bgPadding) top 50%; background-position: right var(--___bgPadding) top 50%, left var(--___bgPadding) top 50%;
} }
&:decrement { &:decrement {
background-image: background-image:
linear-gradient(-45deg, var(--text) 50%, transparent 51%), linear-gradient(-45deg, var(--btnText) 50%, transparent 51%),
linear-gradient(45deg, var(--text) 50%, transparent 51%); linear-gradient(45deg, var(--btnText) 50%, transparent 51%);
background-position: left var(--___bgPadding) top 50%, right var(--___bgPadding) top 50%; background-position: left var(--___bgPadding) top 50%, right var(--___bgPadding) top 50%;
} }
} }
@ -125,14 +104,15 @@ body {
} }
// Body should have background to scrollbar otherwise it will use white (body color?) // Body should have background to scrollbar otherwise it will use white (body color?)
html { html {
scrollbar-color: var(--fg) var(--wallpaper); scrollbar-color: var(--selectedMenu) var(--wallpaper);
background: var(--wallpaper); background: var(--wallpaper);
} }
} }
a { a {
text-decoration: none; text-decoration: none;
color: var(--link); color: $fallback--link;
color: var(--link, $fallback--link);
} }
h4 { h4 {
@ -148,12 +128,27 @@ h4 {
i[class*="icon-"], i[class*="icon-"],
.svg-inline--fa, .svg-inline--fa,
.iconLetter { .iconLetter {
color: var(--icon); color: $fallback--icon;
color: var(--icon, $fallback--icon);
}
.button-unstyled:hover,
a:hover {
> i[class*="icon-"],
> .svg-inline--fa,
> .iconLetter {
color: var(--text);
}
} }
nav { nav {
z-index: var(--ZI_navbar); z-index: var(--ZI_navbar);
box-shadow: var(--shadow); background-color: $fallback--fg;
background-color: var(--topBar, $fallback--fg);
color: $fallback--faint;
color: var(--faint, $fallback--faint);
box-shadow: 0 0 4px rgb(0 0 0 / 60%);
box-shadow: var(--topBarShadow);
box-sizing: border-box; box-sizing: border-box;
height: var(--navbar-height); height: var(--navbar-height);
position: fixed; position: fixed;
@ -200,7 +195,8 @@ nav {
grid-column: 1 / span 3; grid-column: 1 / span 3;
grid-row: 1 / 1; grid-row: 1 / 1;
pointer-events: none; pointer-events: none;
background-color: var(--underlay); background-color: rgb(0 0 0 / 15%);
background-color: var(--underlay, rgb(0 0 0 / 15%));
z-index: -1000; z-index: -1000;
} }
@ -208,6 +204,7 @@ nav {
--miniColumn: 25rem; --miniColumn: 25rem;
--maxiColumn: 45rem; --maxiColumn: 45rem;
--columnGap: 1em; --columnGap: 1em;
--status-margin: 0.75em;
--effectiveSidebarColumnWidth: minmax(var(--miniColumn), var(--sidebarColumnWidth, var(--miniColumn))); --effectiveSidebarColumnWidth: minmax(var(--miniColumn), var(--sidebarColumnWidth, var(--miniColumn)));
--effectiveNotifsColumnWidth: minmax(var(--miniColumn), var(--notifsColumnWidth, var(--miniColumn))); --effectiveNotifsColumnWidth: minmax(var(--miniColumn), var(--notifsColumnWidth, var(--miniColumn)));
--effectiveContentColumnWidth: minmax(var(--miniColumn), var(--contentColumnWidth, var(--maxiColumn))); --effectiveContentColumnWidth: minmax(var(--miniColumn), var(--contentColumnWidth, var(--maxiColumn)));
@ -369,113 +366,106 @@ nav {
.button-default { .button-default {
user-select: none; user-select: none;
color: var(--text); color: $fallback--text;
color: var(--btnText, $fallback--text);
background-color: $fallback--fg;
background-color: var(--btn, $fallback--fg);
border: none; border: none;
border-radius: var(--roundness); border-radius: $fallback--btnRadius;
border-radius: var(--btnRadius, $fallback--btnRadius);
cursor: pointer; cursor: pointer;
background-color: var(--background); box-shadow: $fallback--buttonShadow;
box-shadow: var(--shadow); box-shadow: var(--buttonShadow);
font-size: 1em; font-size: 1em;
font-family: sans-serif; font-family: sans-serif;
font-family: var(--font); font-family: var(--interfaceFont, sans-serif);
&.-sublime {
background: transparent;
}
i[class*="icon-"],
.svg-inline--fa {
color: $fallback--text;
color: var(--btnText, $fallback--text);
}
&::-moz-focus-inner { &::-moz-focus-inner {
border: none; border: none;
} }
&:hover {
box-shadow: 0 0 4px rgb(255 255 255 / 30%);
box-shadow: var(--buttonHoverShadow);
}
&:active {
box-shadow:
0 0 4px 0 rgb(255 255 255 / 30%),
0 1px 0 0 rgb(0 0 0 / 20%) inset,
0 -1px 0 0 rgb(255 255 255 / 20%) inset;
box-shadow: var(--buttonPressedShadow);
color: $fallback--text;
color: var(--btnPressedText, $fallback--text);
background-color: $fallback--fg;
background-color: var(--btnPressed, $fallback--fg);
svg,
i {
color: $fallback--text;
color: var(--btnPressedText, $fallback--text);
}
}
&:disabled { &:disabled {
cursor: not-allowed; cursor: not-allowed;
} color: $fallback--text;
} color: var(--btnDisabledText, $fallback--text);
background-color: $fallback--fg;
background-color: var(--btnDisabled, $fallback--fg);
.menu-item, svg,
.list-item { i {
display: block; color: $fallback--text;
box-sizing: border-box; color: var(--btnDisabledText, $fallback--text);
border: none; }
outline: none;
text-align: initial;
font-size: inherit;
font-family: inherit;
font-weight: 400;
cursor: pointer;
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, &.toggled {
&:hover { color: $fallback--text;
border-top-width: 1px; color: var(--btnToggledText, $fallback--text);
border-bottom-width: 1px; background-color: $fallback--fg;
background-color: var(--btnToggled, $fallback--fg);
box-shadow:
0 0 4px 0 rgb(255 255 255 / 30%),
0 1px 0 0 rgb(0 0 0 / 20%) inset,
0 -1px 0 0 rgb(255 255 255 / 20%) inset;
box-shadow: var(--buttonPressedShadow);
svg,
i {
color: $fallback--text;
color: var(--btnToggledText, $fallback--text);
}
} }
&.-active + &, &.danger {
&:hover + & { // TODO: add better color variable
border-top-width: 0; color: $fallback--text;
} color: var(--alertErrorPanelText, $fallback--text);
background-color: $fallback--alertError;
&:hover + .menu-item-collapsible:not(.-expanded) + &, background-color: var(--alertError, $fallback--alertError);
&.-active + .menu-item-collapsible:not(.-expanded) + & {
border-top-width: 0;
}
&[aria-expanded="true"] {
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);
border-top-width: 0;
}
&:last-child {
border-bottom-right-radius: var(--roundness);
border-bottom-left-radius: var(--roundness);
border-bottom-width: 0;
} }
} }
.button-unstyled { .button-unstyled {
background: none;
border: none; border: none;
outline: none; outline: none;
display: inline; display: inline;
text-align: initial; text-align: initial;
font-size: 100%; font-size: 100%;
font-family: inherit; font-family: inherit;
box-shadow: var(--shadow);
background-color: transparent;
padding: 0; padding: 0;
line-height: unset; line-height: unset;
cursor: pointer; cursor: pointer;
@ -483,23 +473,28 @@ nav {
color: inherit; color: inherit;
&.-link { &.-link {
/* stylelint-disable-next-line declaration-no-important */ color: $fallback--link;
color: var(--link) !important; color: var(--link, $fallback--link);
}
&.-fullwidth {
width: 100%;
}
&.-hover-highlight {
&:hover svg {
color: $fallback--lightText;
color: var(--lightText, $fallback--lightText);
}
} }
} }
input, input,
textarea { textarea,
border: none;
display: inline-block;
outline: none;
}
.input { .input {
&.unstyled { &.unstyled {
border-radius: 0; border-radius: 0;
/* stylelint-disable-next-line declaration-no-important */ background: none;
background: none !important;
box-shadow: none; box-shadow: none;
height: unset; height: unset;
} }
@ -507,11 +502,19 @@ textarea {
--_padding: 0.5em; --_padding: 0.5em;
border: none; border: none;
border-radius: var(--roundness); border-radius: $fallback--inputRadius;
background-color: var(--background); border-radius: var(--inputRadius, $fallback--inputRadius);
color: var(--text); box-shadow:
box-shadow: var(--shadow); 0 1px 0 0 rgb(0 0 0 / 20%) inset,
font-family: var(--font); 0 -1px 0 0 rgb(255 255 255 / 20%) inset,
0 0 2px 0 rgb(0 0 0 / 100%) inset;
box-shadow: var(--inputShadow);
background-color: $fallback--fg;
background-color: var(--input, $fallback--fg);
color: $fallback--lightText;
color: var(--inputText, $fallback--lightText);
font-family: sans-serif;
font-family: var(--inputFont, sans-serif);
font-size: 1em; font-size: 1em;
margin: 0; margin: 0;
box-sizing: border-box; box-sizing: border-box;
@ -525,6 +528,7 @@ textarea {
&[disabled="disabled"], &[disabled="disabled"],
&.disabled { &.disabled {
cursor: not-allowed; cursor: not-allowed;
opacity: 0.5;
} }
&[type="range"] { &[type="range"] {
@ -539,9 +543,9 @@ textarea {
display: none; display: none;
&:checked + label::before { &:checked + label::before {
box-shadow: var(--shadow); box-shadow: 0 0 2px black inset, 0 0 0 4px $fallback--fg inset;
background-color: var(--background); box-shadow: var(--inputShadow), 0 0 0 4px var(--fg, $fallback--fg) inset;
color: var(--text); background-color: var(--accent, $fallback--link);
} }
&:disabled { &:disabled {
@ -555,14 +559,16 @@ textarea {
+ label::before { + label::before {
flex-shrink: 0; flex-shrink: 0;
display: inline-block; display: inline-block;
content: ""; content: "";
transition: box-shadow 200ms; transition: box-shadow 200ms;
width: 1.1em; width: 1.1em;
height: 1.1em; height: 1.1em;
border-radius: 100%; // Radio buttons should always be circle border-radius: 100%; // Radio buttons should always be circle
background-color: var(--background); box-shadow: 0 0 2px black inset;
box-shadow: var(--shadow); box-shadow: var(--inputShadow);
margin-right: 0.5em; margin-right: 0.5em;
background-color: $fallback--fg;
background-color: var(--input, $fallback--fg);
vertical-align: top; vertical-align: top;
text-align: center; text-align: center;
line-height: 1.1; line-height: 1.1;
@ -575,9 +581,8 @@ textarea {
&[type="checkbox"] { &[type="checkbox"] {
&:checked + label::before { &:checked + label::before {
color: var(--text); color: $fallback--text;
background-color: var(--background); color: var(--inputText, $fallback--text);
box-shadow: var(--shadow);
} }
&:disabled { &:disabled {
@ -595,9 +600,13 @@ textarea {
transition: color 200ms; transition: color 200ms;
width: 1.1em; width: 1.1em;
height: 1.1em; height: 1.1em;
border-radius: var(--roundness); border-radius: $fallback--checkboxRadius;
box-shadow: var(--shadow); border-radius: var(--checkboxRadius, $fallback--checkboxRadius);
box-shadow: 0 0 2px black inset;
box-shadow: var(--inputShadow);
margin-right: 0.5em; margin-right: 0.5em;
background-color: $fallback--fg;
background-color: var(--input, $fallback--fg);
vertical-align: top; vertical-align: top;
text-align: center; text-align: center;
line-height: 1.1; line-height: 1.1;
@ -614,14 +623,16 @@ textarea {
} }
// Textareas should have stock line-height + vertical padding instead of huge line-height // Textareas should have stock line-height + vertical padding instead of huge line-height
textarea.input { textarea {
padding: var(--_padding); padding: var(--_padding);
line-height: var(--post-line-height); line-height: var(--post-line-height);
} }
option { option {
color: var(--text); color: $fallback--text;
background-color: var(--background); color: var(--text, $fallback--text);
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
} }
.hide-number-spinner { .hide-number-spinner {
@ -634,20 +645,6 @@ option {
} }
} }
.cards-list {
list-style: none;
display: grid;
grid-auto-flow: row dense;
grid-template-columns: 1fr 1fr;
li {
border: 1px solid var(--border);
border-radius: var(--roundness);
padding: 0.5em;
margin: 0.25em;
}
}
.btn-block { .btn-block {
display: block; display: block;
width: 100%; width: 100%;
@ -658,19 +655,16 @@ option {
display: inline-flex; display: inline-flex;
vertical-align: middle; vertical-align: middle;
button, button {
.button-dropdown {
position: relative; position: relative;
flex: 1 1 auto; flex: 1 1 auto;
&:not(:last-child), &:not(:last-child) {
&:not(:last-child) .button-default {
border-top-right-radius: 0; border-top-right-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
} }
&:not(:first-child), &:not(:first-child) {
&:not(:first-child) .button-default {
border-top-left-radius: 0; border-top-left-radius: 0;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
@ -703,58 +697,81 @@ option {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
&.-dot, &.badge-notification {
&.-counter { background-color: $fallback--cRed;
margin: 0; background-color: var(--badgeNotification, $fallback--cRed);
position: absolute; color: white;
color: var(--badgeNotificationText, white);
} }
&.-dot { &.badge-neutral {
min-height: 8px; background-color: $fallback--cGreen;
max-height: 8px; background-color: var(--badgeNeutral, $fallback--cGreen);
min-width: 8px; color: white;
max-width: 8px; color: var(--badgeNeutralText, white);
padding: 0;
line-height: 0;
font-size: 0;
left: calc(50% - 4px);
top: calc(50% - 4px);
margin-left: 6px;
margin-top: -6px;
}
&.-counter {
border-radius: var(--roundness);
font-size: 0.75em;
line-height: 1;
text-align: right;
padding: 0.2em;
min-width: 0;
left: calc(50% - 0.5em);
top: calc(50% - 0.4em);
margin-left: 0.7em;
margin-top: -1em;
} }
} }
.alert { .alert {
margin: 0 0.35em; margin: 0 0.35em;
padding: 0 0.25em; padding: 0 0.25em;
border-radius: var(--roundness); border-radius: $fallback--tooltipRadius;
border: 1px solid var(--border); border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
&.error {
background-color: $fallback--alertError;
background-color: var(--alertError, $fallback--alertError);
color: $fallback--text;
color: var(--alertErrorText, $fallback--text);
.panel-heading & {
color: $fallback--text;
color: var(--alertErrorPanelText, $fallback--text);
}
}
&.warning {
background-color: $fallback--alertWarning;
background-color: var(--alertWarning, $fallback--alertWarning);
color: $fallback--text;
color: var(--alertWarningText, $fallback--text);
.panel-heading & {
color: $fallback--text;
color: var(--alertWarningPanelText, $fallback--text);
}
}
&.success {
background-color: var(--alertSuccess, $fallback--alertWarning);
color: var(--alertSuccessText, $fallback--text);
.panel-heading & {
color: var(--alertSuccessPanelText, $fallback--text);
}
}
} }
.faint { .faint {
--text: var(--textFaint); color: $fallback--faint;
--link: var(--linkFaint); color: var(--faint, $fallback--faint);
}
color: var(--text); .faint-link {
color: $fallback--faint;
color: var(--faint, $fallback--faint);
&:hover {
text-decoration: underline;
}
} }
.visibility-notice { .visibility-notice {
padding: 0.5em; padding: 0.5em;
border: 1px solid var(--textFaint); border: 1px solid $fallback--faint;
border-radius: var(--roundness); border: 1px solid var(--faint, $fallback--faint);
border-radius: $fallback--inputRadius;
border-radius: var(--inputRadius, $fallback--inputRadius);
} }
.notice-dismissible { .notice-dismissible {
@ -775,10 +792,6 @@ option {
&.iconLetter { &.iconLetter {
font-size: 1.1em; font-size: 1.1em;
} }
&.svg-inline--fa {
vertical-align: -0.15em;
}
} }
.fa-old-padding { .fa-old-padding {
@ -793,11 +806,6 @@ option {
opacity: 0.25; opacity: 0.25;
} }
.timeago {
--link: var(--text);
--linkFaint: var(--textFaint);
}
.login-hint { .login-hint {
text-align: center; text-align: center;
@ -896,8 +904,3 @@ option {
padding: 0; padding: 0;
position: absolute; position: absolute;
} }
*::selection {
color: var(--selectionText);
background-color: var(--selectionBackground);
}

View File

@ -1,6 +1,5 @@
<template> <template>
<div <div
v-show="$store.state.interface.themeApplied"
id="app-loaded" id="app-loaded"
:style="bgStyle" :style="bgStyle"
> >

36
src/_variables.scss Normal file
View File

@ -0,0 +1,36 @@
$main-color: #f58d2c;
$main-background: white;
$darkened-background: whitesmoke;
$fallback--bg: #121a24;
$fallback--fg: #182230;
$fallback--faint: rgb(185 185 186 / 50%);
$fallback--text: #b9b9ba;
$fallback--link: #d8a070;
$fallback--icon: #666;
$fallback--lightBg: rgb(21 30 42);
$fallback--lightText: #b9b9ba;
$fallback--border: #222;
$fallback--cRed: #f00;
$fallback--cBlue: #0095ff;
$fallback--cGreen: #0fa00f;
$fallback--cOrange: orange;
$fallback--alertError: rgb(211 16 20 / 50%);
$fallback--alertWarning: rgb(111 111 20 / 50%);
$fallback--panelRadius: 10px;
$fallback--checkboxRadius: 2px;
$fallback--btnRadius: 4px;
$fallback--inputRadius: 4px;
$fallback--tooltipRadius: 5px;
$fallback--avatarRadius: 4px;
$fallback--avatarAltRadius: 10px;
$fallback--attachmentRadius: 10px;
$fallback--chatMessageRadius: 10px;
$fallback--buttonShadow: 0 0 2px 0 rgb(0 0 0 / 100%),
0 1px 0 0 rgb(255 255 255 / 20%) inset,
0 -1px 0 0 rgb(0 0 0 / 20%) inset;
$status-margin: 0.75em;

View File

@ -14,9 +14,8 @@ 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, applyConfig, tryLoadCache } 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'
import { initServiceWorker, updateFocus } from '../services/sw/sw.js'
let staticInitialResults = null let staticInitialResults = null
@ -260,8 +259,6 @@ const getNodeInfo = async ({ store }) => {
store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') }) store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') })
store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits }) store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits })
store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled }) store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })
store.dispatch('setInstanceOption', { name: 'quotingAvailable', value: features.includes('quote_posting') })
store.dispatch('setInstanceOption', { name: 'groupActorAvailable', value: features.includes('pleroma:group_actors') })
const uploadLimits = metadata.uploadLimits const uploadLimits = metadata.uploadLimits
store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) }) store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) })
@ -328,14 +325,17 @@ const setConfig = async ({ store }) => {
} }
const checkOAuthToken = async ({ store }) => { const checkOAuthToken = async ({ store }) => {
if (store.getters.getUserToken()) { // eslint-disable-next-line no-async-promise-executor
try { return new Promise(async (resolve, reject) => {
await store.dispatch('loginUser', store.getters.getUserToken()) if (store.getters.getUserToken()) {
} catch (e) { try {
console.error(e) await store.dispatch('loginUser', store.getters.getUserToken())
} catch (e) {
console.error(e)
}
} }
} resolve()
return Promise.resolve() })
} }
const afterStoreSetup = async ({ store, i18n }) => { const afterStoreSetup = async ({ store, i18n }) => {
@ -343,9 +343,6 @@ const afterStoreSetup = async ({ store, i18n }) => {
store.dispatch('setLayoutHeight', windowHeight()) store.dispatch('setLayoutHeight', windowHeight())
FaviconService.initFaviconService() FaviconService.initFaviconService()
initServiceWorker(store)
window.addEventListener('focus', () => updateFocus())
const overrides = window.___pleromafe_dev_overrides || {} const overrides = window.___pleromafe_dev_overrides || {}
const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
@ -353,25 +350,20 @@ const afterStoreSetup = async ({ store, i18n }) => {
await setConfig({ store }) await setConfig({ store })
const { customTheme, customThemeSource, forceThemeRecompilation } = store.state.config const { customTheme, customThemeSource } = store.state.config
const { theme } = store.state.instance const { theme } = store.state.instance
const customThemePresent = customThemeSource || customTheme const customThemePresent = customThemeSource || customTheme
if (!forceThemeRecompilation && tryLoadCache()) { if (customThemePresent) {
store.commit('setThemeApplied') if (customThemeSource && customThemeSource.themeEngineVersion === CURRENT_VERSION) {
} else { applyTheme(customThemeSource)
if (customThemePresent) {
if (customThemeSource && customThemeSource.themeEngineVersion === CURRENT_VERSION) {
applyTheme(customThemeSource)
} else {
applyTheme(customTheme)
}
store.commit('setThemeApplied')
} else if (theme) {
// do nothing, it will load asynchronously
} else { } else {
console.error('Failed to load any theme!') applyTheme(customTheme)
} }
} else if (theme) {
// do nothing, it will load asynchronously
} else {
console.error('Failed to load any theme!')
} }
applyConfig(store.state.config) applyConfig(store.state.config)
@ -385,6 +377,8 @@ const afterStoreSetup = async ({ store, i18n }) => {
getInstanceConfig({ store }) getInstanceConfig({ store })
]) ])
await store.dispatch('loadDrafts')
// Start fetching things that don't need to block the UI // Start fetching things that don't need to block the UI
store.dispatch('fetchMutes') store.dispatch('fetchMutes')
store.dispatch('startFetchingAnnouncements') store.dispatch('startFetchingAnnouncements')

View File

@ -25,7 +25,7 @@ import ListsTimeline from 'components/lists_timeline/lists_timeline.vue'
import ListsEdit from 'components/lists_edit/lists_edit.vue' import ListsEdit from 'components/lists_edit/lists_edit.vue'
import NavPanel from 'src/components/nav_panel/nav_panel.vue' import NavPanel from 'src/components/nav_panel/nav_panel.vue'
import AnnouncementsPage from 'components/announcements_page/announcements_page.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'
export default (store) => { export default (store) => {
const validateAuthenticatedRoute = (to, from, next) => { const validateAuthenticatedRoute = (to, from, next) => {
@ -52,7 +52,6 @@ export default (store) => {
{ name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline }, { name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline },
{ name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline }, { name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline },
{ name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } }, { name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
{ name: 'quotes', path: '/notice/:id/quotes', component: QuotesTimeline },
{ {
name: 'remote-user-profile-acct', name: 'remote-user-profile-acct',
path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)', path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)',
@ -80,6 +79,7 @@ export default (store) => {
{ name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute }, { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute },
{ name: 'about', path: '/about', component: About }, { name: 'about', path: '/about', component: About },
{ name: 'announcements', path: '/announcements', component: AnnouncementsPage }, { name: 'announcements', path: '/announcements', component: AnnouncementsPage },
{ name: 'drafts', path: '/drafts', component: Drafts },
{ name: 'user-profile', path: '/users/:name', component: UserProfile }, { name: 'user-profile', path: '/users/:name', component: UserProfile },
{ name: 'legacy-user-profile', path: '/:name', component: UserProfile }, { name: 'legacy-user-profile', path: '/:name', component: UserProfile },
{ name: 'lists', path: '/lists', component: Lists }, { name: 'lists', path: '/lists', component: Lists },

View File

@ -11,14 +11,14 @@
<template v-if="relationship.following"> <template v-if="relationship.following">
<button <button
v-if="relationship.showing_reblogs" v-if="relationship.showing_reblogs"
class="dropdown-item menu-item" class="btn button-default dropdown-item"
@click="hideRepeats" @click="hideRepeats"
> >
{{ $t('user_card.hide_repeats') }} {{ $t('user_card.hide_repeats') }}
</button> </button>
<button <button
v-if="!relationship.showing_reblogs" v-if="!relationship.showing_reblogs"
class="dropdown-item menu-item" class="btn button-default dropdown-item"
@click="showRepeats" @click="showRepeats"
> >
{{ $t('user_card.show_repeats') }} {{ $t('user_card.show_repeats') }}
@ -31,34 +31,34 @@
<UserListMenu :user="user" /> <UserListMenu :user="user" />
<button <button
v-if="relationship.followed_by" v-if="relationship.followed_by"
class="dropdown-item menu-item" class="btn button-default btn-block dropdown-item"
@click="removeUserFromFollowers" @click="removeUserFromFollowers"
> >
{{ $t('user_card.remove_follower') }} {{ $t('user_card.remove_follower') }}
</button> </button>
<button <button
v-if="relationship.blocking" v-if="relationship.blocking"
class="dropdown-item menu-item" class="btn button-default btn-block dropdown-item"
@click="unblockUser" @click="unblockUser"
> >
{{ $t('user_card.unblock') }} {{ $t('user_card.unblock') }}
</button> </button>
<button <button
v-else v-else
class="dropdown-item menu-item" class="btn button-default btn-block dropdown-item"
@click="blockUser" @click="blockUser"
> >
{{ $t('user_card.block') }} {{ $t('user_card.block') }}
</button> </button>
<button <button
class="dropdown-item menu-item" class="btn button-default btn-block dropdown-item"
@click="reportUser" @click="reportUser"
> >
{{ $t('user_card.report') }} {{ $t('user_card.report') }}
</button> </button>
<button <button
v-if="pleromaChatMessagesAvailable" v-if="pleromaChatMessagesAvailable"
class="dropdown-item menu-item" class="btn button-default btn-block dropdown-item"
@click="openChat" @click="openChat"
> >
{{ $t('user_card.message') }} {{ $t('user_card.message') }}
@ -122,12 +122,19 @@
<script src="./account_actions.js"></script> <script src="./account_actions.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
.AccountActions { .AccountActions {
.ellipsis-button { .ellipsis-button {
width: 2.5em; width: 2.5em;
margin: -0.5em 0; margin: -0.5em 0;
padding: 0.5em 0; padding: 0.5em 0;
text-align: center; text-align: center;
&:not(:hover) .icon {
color: $fallback--lightText;
color: var(--lightText, $fallback--lightText);
}
} }
} }
</style> </style>

View File

@ -1,51 +0,0 @@
export default {
name: 'Alert',
selector: '.alert',
validInnerComponents: [
'Text',
'Icon',
'Link',
'Border',
'ButtonUnstyled'
],
variants: {
normal: '.neutral',
error: '.error',
warning: '.warning',
success: '.success'
},
defaultRules: [
{
directives: {
background: '--text',
opacity: 0.5,
blur: '9px'
}
},
{
parent: {
component: 'Alert'
},
component: 'Border',
textColor: '--parent'
},
{
variant: 'error',
directives: {
background: '--cRed'
}
},
{
variant: 'warning',
directives: {
background: '--cOrange'
}
},
{
variant: 'success',
directives: {
background: '--cGreen'
}
}
]
}

View File

@ -99,14 +99,16 @@
<script src="./announcement.js"></script> <script src="./announcement.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
.announcement { .announcement {
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border, $fallback--border);
border-radius: 0; border-radius: 0;
padding: var(--status-margin); padding: var(--status-margin, $status-margin);
.heading, .heading,
.body { .body {
margin-bottom: var(--status-margin); margin-bottom: var(--status-margin, $status-margin);
} }
.footer { .footer {

View File

@ -3,7 +3,7 @@
<textarea <textarea
ref="textarea" ref="textarea"
v-model="announcement.content" v-model="announcement.content"
class="input post-textarea" class="post-textarea"
rows="1" rows="1"
cols="1" cols="1"
:placeholder="$t('announcements.post_placeholder')" :placeholder="$t('announcements.post_placeholder')"
@ -14,7 +14,6 @@
<input <input
id="announcement-start-time" id="announcement-start-time"
v-model="announcement.startsAt" v-model="announcement.startsAt"
class="input"
:type="announcement.allDay ? 'date' : 'datetime-local'" :type="announcement.allDay ? 'date' : 'datetime-local'"
:disabled="disabled" :disabled="disabled"
> >
@ -24,7 +23,6 @@
<input <input
id="announcement-end-time" id="announcement-end-time"
v-model="announcement.endsAt" v-model="announcement.endsAt"
class="input"
:type="announcement.allDay ? 'date' : 'datetime-local'" :type="announcement.allDay ? 'date' : 'datetime-local'"
:disabled="disabled" :disabled="disabled"
> >

View File

@ -61,13 +61,15 @@
<script src="./announcements_page.js"></script> <script src="./announcements_page.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
.announcements-page { .announcements-page {
.post-form { .post-form {
padding: var(--status-margin); padding: var(--status-margin, $status-margin);
.heading, .heading,
.body { .body {
margin-bottom: var(--status-margin); margin-bottom: var(--status-margin, $status-margin);
} }
.post-button { .post-button {

View File

@ -1,3 +1,5 @@
@import "../../variables";
.Attachment { .Attachment {
display: inline-flex; display: inline-flex;
flex-direction: column; flex-direction: column;
@ -7,8 +9,10 @@
height: 100%; height: 100%;
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-radius: var(--roundness); border-radius: $fallback--attachmentRadius;
border-color: var(--border); border-radius: var(--attachmentRadius, $fallback--attachmentRadius);
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
.attachment-wrapper { .attachment-wrapper {
flex: 1 1 auto; flex: 1 1 auto;
@ -80,13 +84,6 @@
} }
} }
.video-container {
border: none;
outline: none;
color: inherit;
background: transparent;
}
.audio-container { .audio-container {
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
@ -129,12 +126,23 @@
.attachment-button { .attachment-button {
padding: 0; padding: 0;
border-radius: var(--roundness); border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
text-align: center; text-align: center;
width: 2em; width: 2em;
height: 2em; height: 2em;
margin-left: 0.5em; margin-left: 0.5em;
font-size: 1.25em; font-size: 1.25em;
// TODO: theming? hard to theme with unknown background image color
background: rgb(230 230 230 / 70%);
.svg-inline--fa {
color: rgb(0 0 0 / 60%);
}
&:hover .svg-inline--fa {
color: rgb(0 0 0 / 90%);
}
} }
} }
@ -209,7 +217,8 @@
&.-placeholder { &.-placeholder {
display: inline-block; display: inline-block;
color: var(--link); color: $fallback--link;
color: var(--postLink, $fallback--link);
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
height: auto; height: auto;

View File

@ -1,24 +0,0 @@
export default {
name: 'Attachment',
selector: '.Attachment',
validInnerComponents: [
'Border',
'ButtonUnstyled',
'Input'
],
defaultRules: [
{
directives: {
roundness: 3
}
},
{
component: 'ButtonUnstyled',
parent: { component: 'Attachment' },
directives: {
background: '#FFFFFF',
opacity: 0.5
}
}
]
}

View File

@ -38,7 +38,7 @@
v-if="edit" v-if="edit"
v-model="localDescription" v-model="localDescription"
type="text" type="text"
class="input description-field" class="description-field"
:placeholder="$t('post_status.media_description')" :placeholder="$t('post_status.media_description')"
@keydown.enter.prevent="" @keydown.enter.prevent=""
> >
@ -175,6 +175,7 @@
:is="videoTag" :is="videoTag"
v-if="type === 'video' && !hidden" v-if="type === 'video' && !hidden"
class="video-container" class="video-container"
:class="{ 'button-unstyled': 'isModal' }"
:href="attachment.url" :href="attachment.url"
@click.stop.prevent="openModal" @click.stop.prevent="openModal"
> >
@ -252,7 +253,7 @@
v-if="edit" v-if="edit"
v-model="localDescription" v-model="localDescription"
type="text" type="text"
class="input description-field" class="description-field"
:placeholder="$t('post_status.media_description')" :placeholder="$t('post_status.media_description')"
@keydown.enter.prevent="" @keydown.enter.prevent=""
> >

View File

@ -1,4 +1,3 @@
<!-- FIXME THIS NEEDS TO BE REFACTORED TO USE POPOVER -->
<template> <template>
<div <div
v-click-outside="onClickOutside" v-click-outside="onClickOutside"
@ -7,12 +6,12 @@
<input <input
v-model="term" v-model="term"
:placeholder="placeholder" :placeholder="placeholder"
class="input autosuggest-input" class="autosuggest-input"
@click="onInputClick" @click="onInputClick"
> >
<div <div
v-if="resultsVisible && filtered.length > 0" v-if="resultsVisible && filtered.length > 0"
class="panel autosuggest-results" class="autosuggest-results"
> >
<slot <slot
v-for="item in filtered" v-for="item in filtered"
@ -25,6 +24,8 @@
<script src="./autosuggest.js"></script> <script src="./autosuggest.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
.autosuggest { .autosuggest {
position: relative; position: relative;
@ -39,15 +40,18 @@
top: 100%; top: 100%;
right: 0; right: 0;
max-height: 400px; max-height: 400px;
background-color: var(--bg); background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-color: var(--border); border-color: $fallback--border;
border-radius: var(--roundness); border-color: var(--border, $fallback--border);
border-radius: $fallback--inputRadius;
border-radius: var(--inputRadius, $fallback--inputRadius);
border-top-left-radius: 0; border-top-left-radius: 0;
border-top-right-radius: 0; border-top-right-radius: 0;
box-shadow: 1px 1px 4px rgb(0 0 0 / 60%); box-shadow: 1px 1px 4px rgb(0 0 0 / 60%);
box-shadow: var(--shadow); box-shadow: var(--panelShadow);
overflow-y: auto; overflow-y: auto;
z-index: 1; z-index: 1;
} }

View File

@ -17,6 +17,8 @@
<script src="./avatar_list.js"></script> <script src="./avatar_list.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
.avatars { .avatars {
display: flex; display: flex;
margin: 0; margin: 0;
@ -34,7 +36,8 @@
} }
.avatar-small { .avatar-small {
border-radius: var(--roundness); border-radius: $fallback--avatarAltRadius;
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
height: 24px; height: 24px;
width: 24px; width: 24px;
} }

View File

@ -1,30 +0,0 @@
export default {
name: 'Badge',
selector: '.badge',
validInnerComponents: [
'Text',
'Icon'
],
variants: {
notification: '.-notification'
},
defaultRules: [
{
component: 'Root',
directives: {
'--badgeNotification': 'color | --cRed'
}
},
{
directives: {
background: '--cGreen'
}
},
{
variant: 'notification',
directives: {
background: '--cRed'
}
}
]
}

View File

@ -47,6 +47,7 @@
display: flex; display: flex;
flex: 1 0; flex: 1 0;
margin: 0; margin: 0;
padding: 0.6em 1em;
--emoji-size: 14px; --emoji-size: 14px;

View File

@ -1,13 +0,0 @@
export default {
name: 'Border',
selector: '/*border*/',
virtual: true,
defaultRules: [
{
directives: {
textColor: '$mod(--parent, 10)',
textAuto: 'no-auto'
}
}
]
}

View File

@ -1,101 +0,0 @@
export default {
name: 'Button', // Name of the component
selector: '.button-default', // CSS selector/prefix
// outOfTreeSelector: '' // out-of-tree selector is used when other components are laid over it but it's not part of the tree, see Underlay component
// States, system witll calculate ALL possible combinations of those and prepend "normal" to them + standalone "normal" state
states: {
// States are a bit expensive - the amount of combinations generated is about (1/6)n^3+n, so adding more state increased number of combination by an order of magnitude!
// All states inherit from "normal" state, there is no other inheirtance, i.e. hover+disabled only inherits from "normal", not from hover nor disabled.
// 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',
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.
variants: {
// Variants save on computation time since adding new variant just adds one more "set".
// normal: '', // you can override normal variant, it will be appenended to the main class
danger: '.danger'
// 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.
},
// 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',
'Icon'
],
// Default rules, used as "default theme", essentially.
defaultRules: [
{
component: 'Root',
directives: {
'--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)'
}
},
{
// component: 'Button', // no need to specify components every time unless you're specifying how other component should look
// like within it
directives: {
background: '--fg',
shadow: ['--defaultButtonShadow', '--defaultButtonBevel'],
roundness: 3
}
},
{
state: ['hover'],
directives: {
shadow: ['--defaultButtonHoverGlow', '--defaultButtonBevel']
}
},
{
state: ['pressed'],
directives: {
shadow: ['--defaultButtonShadow', '--pressedButtonBevel']
}
},
{
state: ['hover', 'pressed'],
directives: {
shadow: ['--defaultButtonHoverGlow', '--pressedButtonBevel']
}
},
{
state: ['toggled'],
directives: {
background: '--inheritedBackground,-14.2',
shadow: ['--defaultButtonShadow', '--pressedButtonBevel']
}
},
{
state: ['toggled', 'hover'],
directives: {
background: '--inheritedBackground,-14.2',
shadow: ['--defaultButtonHoverGlow', '--pressedButtonBevel']
}
},
{
state: ['disabled'],
directives: {
background: '$blend(--inheritedBackground, 0.25, --parent)',
shadow: ['--defaultButtonBevel']
}
},
{
component: 'Text',
parent: {
component: 'Button',
state: ['disabled']
},
directives: {
textOpacity: 0.25,
textOpacityMode: 'blend'
}
}
]
}

View File

@ -1,96 +0,0 @@
export default {
name: 'ButtonUnstyled',
selector: '.button-unstyled',
states: {
toggled: '.toggled',
disabled: ':disabled',
hover: ':hover:not(:disabled)',
focused: ':focus-within'
},
validInnerComponents: [
'Text',
'Icon',
'Badge'
],
defaultRules: [
{
directives: {
background: '#ffffff',
opacity: 0,
shadow: []
}
},
{
component: 'Icon',
parent: {
component: 'ButtonUnstyled',
state: ['hover']
},
directives: {
textColor: '--parent--text'
}
},
{
component: 'Icon',
parent: {
component: 'ButtonUnstyled',
state: ['toggled']
},
directives: {
textColor: '--parent--text'
}
},
{
component: 'Icon',
parent: {
component: 'ButtonUnstyled',
state: ['toggled', 'hover']
},
directives: {
textColor: '--parent--text'
}
},
{
component: 'Icon',
parent: {
component: 'ButtonUnstyled',
state: ['toggled', 'focused']
},
directives: {
textColor: '--parent--text'
}
},
{
component: 'Icon',
parent: {
component: 'ButtonUnstyled',
state: ['toggled', 'focused', 'hover']
},
directives: {
textColor: '--parent--text'
}
},
{
component: 'Text',
parent: {
component: 'ButtonUnstyled',
state: ['disabled']
},
directives: {
textOpacity: 0.25,
textOpacityMode: 'blend'
}
},
{
component: 'Icon',
parent: {
component: 'ButtonUnstyled',
state: ['disabled']
},
directives: {
textOpacity: 0.25,
textOpacityMode: 'blend'
}
}
]
}

View File

@ -11,15 +11,15 @@
.chat-view-body { .chat-view-body {
box-sizing: border-box; box-sizing: border-box;
background-color: var(--chatBg, $fallback--bg);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
overflow: visible; overflow: visible;
min-height: calc(100vh - var(--navbar-height)); min-height: calc(100vh - var(--navbar-height));
margin: 0; margin: 0;
border-radius: var(--roundness); border-radius: 10px 10px 0 0;
border-bottom-left-radius: 0; border-radius: var(--panelRadius, 10px) var(--panelRadius, 10px) 0 0;
border-bottom-right-radius: 0;
&::after { &::after {
border-radius: 0; border-radius: 0;
@ -37,6 +37,8 @@
.footer { .footer {
position: sticky; position: sticky;
bottom: 0; bottom: 0;
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
z-index: 1; z-index: 1;
} }
@ -59,6 +61,8 @@
position: absolute; position: absolute;
right: 1.3em; right: 1.3em;
top: -3.2em; top: -3.2em;
background-color: $fallback--fg;
background-color: var(--btn, $fallback--fg);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@ -75,6 +79,12 @@
visibility: visible; visibility: visible;
} }
i {
font-size: 1em;
color: $fallback--text;
color: var(--text, $fallback--text);
}
.unread-message-count { .unread-message-count {
font-size: 0.8em; font-size: 0.8em;
left: 50%; left: 50%;

View File

@ -1,19 +0,0 @@
export default {
name: 'Chat',
selector: '.chat-message-list',
validInnerComponents: [
'Text',
'Link',
'Icon',
'Avatar',
'ChatMessage'
],
defaultRules: [
{
directives: {
background: '--bg',
blur: '5px'
}
}
]
}

View File

@ -26,7 +26,7 @@
</div> </div>
</div> </div>
<div <div
class="chat-message-list message-list" class="message-list"
:style="{ height: scrollableContainerHeight }" :style="{ height: scrollableContainerHeight }"
> >
<template v-if="!errorLoadingChat"> <template v-if="!errorLoadingChat">
@ -61,7 +61,7 @@
<FAIcon icon="chevron-down" /> <FAIcon icon="chevron-down" />
<div <div
v-if="newMessageCount" v-if="newMessageCount"
class="badge -notification unread-chat-count unread-message-count" class="badge badge-notification unread-chat-count unread-message-count"
> >
{{ newMessageCount }} {{ newMessageCount }}
</div> </div>
@ -95,5 +95,6 @@
<script src="./chat.js"></script> <script src="./chat.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
@import "./chat"; @import "./chat";
</style> </style>

View File

@ -45,6 +45,8 @@
<script src="./chat_list.js"></script> <script src="./chat_list.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
.chat-list { .chat-list {
min-height: 25em; min-height: 25em;
margin-bottom: 0; margin-bottom: 0;
@ -55,7 +57,8 @@
font-size: 1.2em; font-size: 1.2em;
display: flex; display: flex;
justify-content: center; justify-content: center;
color: var(--textFaint); color: $fallback--text;
color: var(--faint, $fallback--text);
} }
</style> </style>

View File

@ -1,6 +1,8 @@
.chat-list-item { .chat-list-item {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
padding: 0.75em;
height: 5em;
overflow: hidden; overflow: hidden;
box-sizing: border-box; box-sizing: border-box;
cursor: pointer; cursor: pointer;
@ -9,6 +11,11 @@
outline: none; outline: none;
} }
&:hover {
background-color: var(--selectedPost, $fallback--lightBg);
box-shadow: 0 0 3px 1px rgb(0 0 0 / 10%);
}
.chat-list-item-left { .chat-list-item-left {
margin-right: 1em; margin-right: 1em;
} }
@ -22,7 +29,7 @@
.heading { .heading {
width: 100%; width: 100%;
display: flex; display: inline-flex;
justify-content: space-between; justify-content: space-between;
line-height: 1em; line-height: 1em;
} }
@ -40,17 +47,18 @@
} }
.chat-preview { .chat-preview {
display: flex; display: inline-flex;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
margin: 0.35em 0; margin: 0.35em 0;
color: var(--textFaint); color: $fallback--text;
color: var(--faint, $fallback--text);
width: 100%; width: 100%;
} }
a { a {
color: var(--linkFaint); color: var(--faintLink, $fallback--link);
text-decoration: none; text-decoration: none;
pointer-events: none; pointer-events: none;
} }
@ -65,6 +73,11 @@
} }
} }
.Avatar {
border-radius: $fallback--avatarAltRadius;
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
}
.chat-preview-body { .chat-preview-body {
--emoji-size: 1.4em; --emoji-size: 1.4em;

View File

@ -36,7 +36,7 @@
/> />
<div <div
v-if="chat.unread > 0" v-if="chat.unread > 0"
class="badge -notification unread-chat-count" class="badge badge-notification unread-chat-count"
> >
{{ chat.unread }} {{ chat.unread }}
</div> </div>
@ -48,5 +48,6 @@
<script src="./chat_list_item.js"></script> <script src="./chat_list_item.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
@import "./chat_list_item"; @import "./chat_list_item";
</style> </style>

View File

@ -1,3 +1,5 @@
@import "../../variables";
.chat-message-wrapper { .chat-message-wrapper {
&.hovered-message-chain { &.hovered-message-chain {
.animated.Avatar { .animated.Avatar {
@ -25,6 +27,12 @@
.menu-icon { .menu-icon {
cursor: pointer; cursor: pointer;
&:hover,
.extra-button-popover.open & {
color: $fallback--text;
color: var(--text, $fallback--text);
}
} }
.popover { .popover {
@ -53,12 +61,10 @@
} }
.status { .status {
background-color: var(--background); border-radius: $fallback--chatMessageRadius;
color: var(--text); border-radius: var(--chatMessageRadius, $fallback--chatMessageRadius);
border-radius: var(--roundness);
display: flex; display: flex;
padding: 0.75em; padding: 0.75em;
border: 1px solid var(--border);
} }
.created-at { .created-at {
@ -91,7 +97,8 @@
.error { .error {
.status-content.media-body, .status-content.media-body,
.created-at { .created-at {
color: var(--badgeNotification); color: $fallback--cRed;
color: var(--badgeNotification, $fallback--cRed);
} }
} }
@ -110,6 +117,16 @@
align-content: end; align-content: end;
justify-content: flex-end; justify-content: flex-end;
a {
color: var(--chatMessageOutgoingLink, $fallback--link);
}
.status {
color: var(--chatMessageOutgoingText, $fallback--text);
background-color: var(--chatMessageOutgoingBg, $fallback--lightBg);
border: 1px solid var(--chatMessageOutgoingBorder, --lightBg);
}
.chat-message-inner { .chat-message-inner {
align-items: flex-end; align-items: flex-end;
} }
@ -120,6 +137,22 @@
} }
.incoming { .incoming {
a {
color: var(--chatMessageIncomingLink, $fallback--link);
}
.status {
color: var(--chatMessageIncomingText, $fallback--text);
background-color: var(--chatMessageIncomingBg, $fallback--bg);
border: 1px solid var(--chatMessageIncomingBorder, --border);
}
.created-at {
a {
color: var(--chatMessageIncomingText, $fallback--text);
}
}
.chat-message-menu { .chat-message-menu {
left: 0.4rem; left: 0.4rem;
} }
@ -143,5 +176,6 @@
margin: 1.4em 0; margin: 1.4em 0;
font-size: 0.9em; font-size: 0.9em;
user-select: none; user-select: none;
color: var(--textFaint); color: $fallback--text;
color: var(--faintedText, $fallback--text);
} }

View File

@ -1,30 +0,0 @@
export default {
name: 'ChatMessage',
selector: '.chat-message',
variants: {
outgoing: '.outgoing'
},
validInnerComponents: [
'Text',
'Icon',
'Border',
'Button',
'RichContent',
'Attachment',
'PollGraph'
],
defaultRules: [
{
directives: {
background: '--bg, 2',
backgroundNoCssColor: 'yes'
}
},
{
variant: 'outgoing',
directives: {
background: '--bg, 5'
}
}
]
}

View File

@ -53,7 +53,7 @@
<template #content> <template #content>
<div class="dropdown-menu"> <div class="dropdown-menu">
<button <button
class="menu-item dropdown-item dropdown-item-icon" class="button-default dropdown-item dropdown-item-icon"
@click="deleteMessage" @click="deleteMessage"
> >
<FAIcon icon="times" /> {{ $t("chats.delete") }} <FAIcon icon="times" /> {{ $t("chats.delete") }}

View File

@ -16,6 +16,11 @@
padding-bottom: 0.7rem; padding-bottom: 0.7rem;
} }
.basic-user-card:hover {
cursor: pointer;
background-color: var(--selectedPost, $fallback--lightBg);
}
.go-back-button { .go-back-button {
text-align: center; text-align: center;
line-height: 1; line-height: 1;

View File

@ -16,29 +16,27 @@
/> />
</button> </button>
</div> </div>
<div class="panel-body"> <div class="input-wrap">
<div class="input-wrap"> <div class="input-search">
<div class="input-search"> <FAIcon
<FAIcon class="search-icon fa-scale-110 fa-old-padding"
class="search-icon fa-scale-110 fa-old-padding" icon="search"
icon="search" />
/>
</div>
<input
ref="search"
v-model="query"
class="input"
placeholder="Search people"
@input="onInput"
>
</div> </div>
<div class="member-list"> <input
<div ref="search"
v-for="user in availableUsers" v-model="query"
:key="user.id" placeholder="Search people"
class="list-item" @input="onInput"
@click.capture.prevent="goToChat(user)" >
> </div>
<div class="member-list">
<div
v-for="user in availableUsers"
:key="user.id"
class="member"
>
<div @click.capture.prevent="goToChat(user)">
<BasicUserCard :user="user" /> <BasicUserCard :user="user" />
</div> </div>
</div> </div>
@ -48,5 +46,6 @@
<script src="./chat_new.js"></script> <script src="./chat_new.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
@import "./chat_new"; @import "./chat_new";
</style> </style>

View File

@ -26,6 +26,8 @@
<script src="./chat_title.js"></script> <script src="./chat_title.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
.chat-title { .chat-title {
display: flex; display: flex;
overflow: hidden; overflow: hidden;
@ -52,7 +54,8 @@
margin-right: 0.5em; margin-right: 0.5em;
height: 1.5em; height: 1.5em;
width: 1.5em; width: 1.5em;
border-radius: var(--roundness); border-radius: $fallback--avatarAltRadius;
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
&.animated::before { &.animated::before {
display: none; display: none;

View File

@ -1,7 +1,7 @@
<template> <template>
<label <label
class="checkbox" class="checkbox"
:class="{ disabled, indeterminate, 'indeterminate-fix': indeterminateTransitionFix }" :class="{ disabled, indeterminate }"
> >
<input <input
type="checkbox" type="checkbox"
@ -12,9 +12,8 @@
@change="$emit('update:modelValue', $event.target.checked)" @change="$emit('update:modelValue', $event.target.checked)"
> >
<i <i
class="input -checkbox checkbox-indicator" class="checkbox-indicator"
:aria-hidden="true" :aria-hidden="true"
@transitionend.capture="onTransitionEnd"
/> />
<span <span
v-if="!!$slots.default" v-if="!!$slots.default"
@ -32,28 +31,12 @@ export default {
'indeterminate', 'indeterminate',
'disabled' 'disabled'
], ],
emits: ['update:modelValue'], emits: ['update:modelValue']
data: (vm) => ({
indeterminateTransitionFix: vm.indeterminate
}),
watch: {
indeterminate (e) {
if (e) {
this.indeterminateTransitionFix = true
}
}
},
methods: {
onTransitionEnd (e) {
if (!this.indeterminate) {
this.indeterminateTransitionFix = false
}
}
}
} }
</script> </script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
@import "../../mixins"; @import "../../mixins";
.checkbox { .checkbox {
@ -61,15 +44,9 @@ export default {
display: inline-block; display: inline-block;
min-height: 1.2em; min-height: 1.2em;
& > &-indicator { &-indicator {
/* Reset .input stuff */
padding: 0;
margin: 0;
position: relative; position: relative;
line-height: inherit;
display: inline;
padding-left: 1.2em; padding-left: 1.2em;
box-shadow: none;
} }
&-indicator::before { &-indicator::before {
@ -81,9 +58,12 @@ export default {
transition: color 200ms; transition: color 200ms;
width: 1.1em; width: 1.1em;
height: 1.1em; height: 1.1em;
border-radius: var(--roundness); border-radius: $fallback--checkboxRadius;
box-shadow: var(--shadow); border-radius: var(--checkboxRadius, $fallback--checkboxRadius);
background-color: var(--background); box-shadow: 0 0 2px black inset;
box-shadow: var(--inputShadow);
background-color: $fallback--fg;
background-color: var(--input, $fallback--fg);
vertical-align: top; vertical-align: top;
text-align: center; text-align: center;
line-height: 1.1em; line-height: 1.1em;
@ -100,24 +80,21 @@ export default {
} }
.label { .label {
color: var(--text); color: $fallback--faint;
color: var(--faint, $fallback--faint);
} }
} }
input[type="checkbox"] { input[type="checkbox"] {
&:checked + .checkbox-indicator::before { &:checked + .checkbox-indicator::before {
color: var(--text); color: $fallback--text;
color: var(--inputText, $fallback--text);
} }
&:indeterminate + .checkbox-indicator::before { &:indeterminate + .checkbox-indicator::before {
content: ""; content: "";
color: var(--text); color: $fallback--text;
} color: var(--inputText, $fallback--text);
}
&.indeterminate-fix {
input[type="checkbox"] + .checkbox-indicator::before {
content: "";
} }
} }

View File

@ -1,3 +1,5 @@
@import "../../variables";
.color-input { .color-input {
display: inline-flex; display: inline-flex;
@ -9,8 +11,9 @@
padding: 0.2em 8px; padding: 0.2em 8px;
input { input {
color: var(--text);
background: none; background: none;
color: $fallback--lightText;
color: var(--inputText, $fallback--lightText);
border: none; border: none;
padding: 0; padding: 0;
margin: 0; margin: 0;
@ -20,38 +23,21 @@
min-width: 3em; min-width: 3em;
padding: 0; padding: 0;
} }
}
.nativeColor { &.nativeColor {
cursor: pointer; flex: 0 0 2em;
flex: 0 0 auto; min-width: 2em;
align-self: stretch;
input { min-height: 100%;
appearance: none;
max-width: 0;
min-width: 0;
max-height: 0;
/* stylelint-disable-next-line declaration-no-important */
opacity: 0 !important;
} }
} }
.computedIndicator, .computedIndicator,
.validIndicator,
.invalidIndicator,
.transparentIndicator { .transparentIndicator {
flex: 0 0 2em; flex: 0 0 2em;
margin: 0 0.5em;
min-width: 2em; min-width: 2em;
align-self: stretch; align-self: stretch;
min-height: 1.5em; min-height: 100%;
border-radius: var(--roundness);
}
.invalidIndicator {
background: transparent;
box-sizing: border-box;
border: 2px solid var(--cRed);
} }
.transparentIndicator { .transparentIndicator {
@ -72,13 +58,11 @@
&::after { &::after {
top: 0; top: 0;
left: 0; left: 0;
border-top-left-radius: var(--roundness);
} }
&::before { &::before {
bottom: 0; bottom: 0;
right: 0; right: 0;
border-bottom-right-radius: var(--roundness);
} }
} }
} }

View File

@ -25,51 +25,30 @@
:disabled="!present || disabled" :disabled="!present || disabled"
@input="$emit('update:modelValue', $event.target.value)" @input="$emit('update:modelValue', $event.target.value)"
> >
<div <input
v-if="validColor" v-if="validColor"
class="validIndicator" :id="name"
:style="{backgroundColor: modelValue || fallback}" class="nativeColor unstyled"
/> type="color"
:value="modelValue || fallback"
:disabled="!present || disabled"
@input="$emit('update:modelValue', $event.target.value)"
>
<div <div
v-else-if="transparentColor" v-if="transparentColor"
class="transparentIndicator" class="transparentIndicator"
/> />
<div <div
v-else-if="computedColor" v-if="computedColor"
class="computedIndicator" class="computedIndicator"
:style="{backgroundColor: fallback}" :style="{backgroundColor: fallback}"
/> />
<div
v-else
class="invalidIndicator"
/>
<label class="nativeColor">
<FAIcon icon="eye-dropper" />
<input
:id="name"
class="unstyled"
type="color"
:value="modelValue || fallback"
:disabled="!present || disabled"
@input="$emit('update:modelValue', $event.target.value)"
>
</label>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import Checkbox from '../checkbox/checkbox.vue' import Checkbox from '../checkbox/checkbox.vue'
import { hex2rgb } from '../../services/color_convert/color_convert.js' import { hex2rgb } from '../../services/color_convert/color_convert.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faEyeDropper
} from '@fortawesome/free-solid-svg-icons'
library.add(
faEyeDropper
)
export default { export default {
components: { components: {
Checkbox Checkbox
@ -129,3 +108,12 @@ export default {
} }
</script> </script>
<style lang="scss" src="./color_input.scss"></style> <style lang="scss" src="./color_input.scss"></style>
<style lang="scss">
.color-control {
input.text-input {
max-width: 7em;
flex: 1;
}
}
</style>

View File

@ -56,8 +56,7 @@ const conversation = {
expanded: false, expanded: false,
threadDisplayStatusObject: {}, // id => 'showing' | 'hidden' threadDisplayStatusObject: {}, // id => 'showing' | 'hidden'
statusContentPropertiesObject: {}, statusContentPropertiesObject: {},
inlineDivePosition: null, inlineDivePosition: null
loadStatusError: null
} }
}, },
props: [ props: [
@ -393,15 +392,11 @@ const conversation = {
this.setHighlight(this.originalStatusId) this.setHighlight(this.originalStatusId)
}) })
} else { } else {
this.loadStatusError = null
this.$store.state.api.backendInteractor.fetchStatus({ id: this.statusId }) this.$store.state.api.backendInteractor.fetchStatus({ id: this.statusId })
.then((status) => { .then((status) => {
this.$store.dispatch('addNewStatuses', { statuses: [status] }) this.$store.dispatch('addNewStatuses', { statuses: [status] })
this.fetchConversation() this.fetchConversation()
}) })
.catch((error) => {
this.loadStatusError = error
})
} }
}, },
getReplies (id) { getReplies (id) {

View File

@ -28,27 +28,7 @@
class="rightside-button" class="rightside-button"
/> />
</div> </div>
<div <div class="conversation-body panel-body">
v-if="isPage && !status"
class="conversation-body"
:class="{ 'panel-body': isExpanded }"
>
<p v-if="!loadStatusError">
<FAIcon
spin
icon="circle-notch"
/>
{{ $t('status.loading') }}
</p>
<p v-else>
{{ $t('status.load_error', { error: loadStatusError }) }}
</p>
</div>
<div
v-else
class="conversation-body"
:class="{ 'panel-body': isExpanded }"
>
<div <div
v-if="isTreeView" v-if="isTreeView"
class="thread-body" class="thread-body"
@ -223,7 +203,6 @@
</div> </div>
<div <div
v-else v-else
class="Conversation -hidden"
:style="hiddenStyle" :style="hiddenStyle"
/> />
</template> </template>
@ -231,17 +210,14 @@
<script src="./conversation.js"></script> <script src="./conversation.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
.Conversation { .Conversation {
z-index: 1; z-index: 1;
&.-hidden {
background: var(--__panel-background);
backdrop-filter: var(--__panel-backdrop-filter);
}
.conversation-dive-to-top-level-box { .conversation-dive-to-top-level-box {
padding: var(--status-margin); padding: var(--status-margin, $status-margin);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border, $fallback--border);
border-radius: 0; border-radius: 0;
/* Make the button stretch along the whole row */ /* Make the button stretch along the whole row */
@ -251,22 +227,20 @@
} }
.thread-ancestors { .thread-ancestors {
margin-left: var(--status-margin); margin-left: var(--status-margin, $status-margin);
border-left: 2px solid var(--border); border-left: 2px solid var(--border, $fallback--border);
} }
.thread-ancestor.-faded .RichContent { .thread-ancestor.-faded .StatusContent {
/* stylelint-disable declaration-no-important */ --link: var(--faintLink);
--text: var(--textFaint) !important; --text: var(--faint);
--link: var(--linkFaint) !important;
--funtextGreentext: var(--funtextGreentextFaint) !important; color: var(--text);
--funtextCyantext: var(--funtextCyantextFaint) !important;
/* stylelint-enable declaration-no-important */
} }
.thread-ancestor-dive-box { .thread-ancestor-dive-box {
padding-left: var(--status-margin); padding-left: var(--status-margin, $status-margin);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border, $fallback--border);
border-radius: 0; border-radius: 0;
/* Make the button stretch along the whole row */ /* Make the button stretch along the whole row */
@ -279,17 +253,16 @@
} }
.thread-ancestor-dive-box-inner { .thread-ancestor-dive-box-inner {
padding: var(--status-margin); padding: var(--status-margin, $status-margin);
} }
.conversation-status { .conversation-status {
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border, $fallback--border);
border-radius: 0; border-radius: 0;
} }
.thread-ancestor-has-other-replies .conversation-status, .thread-ancestor-has-other-replies .conversation-status,
&:last-child:not(.-expanded) .conversation-status, &:last-child .conversation-status,
&.-expanded .conversation-status:last-child,
.thread-ancestor:last-child .conversation-status, .thread-ancestor:last-child .conversation-status,
.thread-ancestor:last-child .thread-ancestor-dive-box, .thread-ancestor:last-child .thread-ancestor-dive-box,
&.-expanded .thread-tree .conversation-status { &.-expanded .thread-tree .conversation-status {
@ -297,36 +270,20 @@
} }
.thread-ancestors + .thread-tree > .conversation-status { .thread-ancestors + .thread-tree > .conversation-status {
border-top: 1px solid var(--border); border-top: 1px solid var(--border, $fallback--border);
} }
/* expanded conversation in timeline */ /* expanded conversation in timeline */
&.status-fadein.-expanded .thread-body { &.status-fadein.-expanded .thread-body {
border-left: 4px solid var(--cRed); border-left: 4px solid $fallback--cRed;
border-radius: var(--roundness); border-left-color: var(--cRed, $fallback--cRed);
border-top-left-radius: 0; border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;
border-top-right-radius: 0; border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border, $fallback--border);
} }
&.-expanded.status-fadein { &.-expanded.status-fadein {
--___margin: calc(var(--status-margin) / 2); margin: calc(var(--status-margin, $status-margin) / 2);
background: var(--background);
margin: var(--___margin);
&::before {
z-index: -1;
content: "";
display: block;
position: absolute;
top: calc(var(--___margin) * -1);
bottom: calc(var(--___margin) * -1);
left: calc(var(--___margin) * -1);
right: calc(var(--___margin) * -1);
background: var(--background);
backdrop-filter: var(--__panel-backdrop-filter);
}
} }
} }
</style> </style>

View File

@ -107,10 +107,7 @@ export default {
this.searchBarHidden = hidden this.searchBarHidden = hidden
}, },
openSettingsModal () { openSettingsModal () {
this.$store.dispatch('openSettingsModal', 'user') this.$store.dispatch('openSettingsModal')
},
openAdminModal () {
this.$store.dispatch('openSettingsModal', 'admin')
} }
} }
} }

View File

@ -1,3 +1,5 @@
@import "../../variables";
.DesktopNav { .DesktopNav {
width: 100%; width: 100%;
z-index: var(--ZI_navbar); z-index: var(--ZI_navbar);
@ -7,7 +9,7 @@
} }
a { a {
color: var(--link); color: var(--topBarLink, $fallback--link);
} }
.inner-nav { .inner-nav {
@ -52,7 +54,27 @@
.button-default { .button-default {
&, &,
svg { svg {
color: var(--text); color: $fallback--text;
color: var(--btnTopBarText, $fallback--text);
}
&:active {
background-color: $fallback--fg;
background-color: var(--btnPressedTopBar, $fallback--fg);
color: $fallback--text;
color: var(--btnPressedTopBarText, $fallback--text);
}
&:disabled {
color: $fallback--text;
color: var(--btnDisabledTopBarText, $fallback--text);
}
&.toggled {
color: $fallback--text;
color: var(--btnToggledTopBarText, $fallback--text);
background-color: $fallback--fg;
background-color: var(--btnToggledTopBar, $fallback--fg);
} }
} }
@ -72,7 +94,8 @@
mask-repeat: no-repeat; mask-repeat: no-repeat;
mask-position: center; mask-position: center;
mask-size: contain; mask-size: contain;
background-color: var(--text); background-color: $fallback--fg;
background-color: var(--topBarText, $fallback--fg);
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
@ -93,7 +116,8 @@
text-align: center; text-align: center;
.svg-inline--fa { .svg-inline--fa {
color: var(--link); color: $fallback--link;
color: var(--topBarLink, $fallback--link);
} }
} }

View File

@ -48,19 +48,20 @@
icon="cog" icon="cog"
/> />
</button> </button>
<button <a
v-if="currentUser && currentUser.role === 'admin'" v-if="currentUser && currentUser.role === 'admin'"
class="button-unstyled nav-icon" href="/pleroma/admin/#/login-pleroma"
class="nav-icon"
target="_blank" target="_blank"
:title="$t('nav.administration')" :title="$t('nav.administration')"
@click.stop="openAdminModal" @click.stop
> >
<FAIcon <FAIcon
fixed-width fixed-width
class="fa-scale-110 fa-old-padding" class="fa-scale-110 fa-old-padding"
icon="tachometer-alt" icon="tachometer-alt"
/> />
</button> </a>
<span class="spacer" /> <span class="spacer" />
<button <button
v-if="currentUser" v-if="currentUser"

View File

@ -12,7 +12,7 @@
<slot name="header" /> <slot name="header" />
</div> </div>
</div> </div>
<div class="panel-body dialog-modal-content"> <div class="dialog-modal-content">
<slot name="default" /> <slot name="default" />
</div> </div>
<div class="dialog-modal-footer user-interactions panel-footer"> <div class="dialog-modal-footer user-interactions panel-footer">
@ -25,6 +25,8 @@
<script src="./dialog_modal.js"></script> <script src="./dialog_modal.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../variables";
// TODO: unify with other modals. // TODO: unify with other modals.
.dark-overlay { .dark-overlay {
&::before { &::before {
@ -52,6 +54,8 @@
z-index: 2001; z-index: 2001;
cursor: default; cursor: default;
display: block; display: block;
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
.dialog-modal-heading { .dialog-modal-heading {
.title { .title {
@ -62,13 +66,18 @@
.dialog-modal-content { .dialog-modal-content {
margin: 0; margin: 0;
padding: 1rem; padding: 1rem;
background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
white-space: normal; white-space: normal;
} }
.dialog-modal-footer { .dialog-modal-footer {
margin: 0; margin: 0;
padding: 0.5em; padding: 0.5em;
border-top: 1px solid var(--border); background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg);
border-top: 1px solid $fallback--border;
border-top: 1px solid var(--border, $fallback--border);
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;

View File

@ -0,0 +1,64 @@
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
import EditStatusForm from 'src/components/edit_status_form/edit_status_form.vue'
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
import StatusContent from 'src/components/status_content/status_content.vue'
const Draft = {
components: {
PostStatusForm,
EditStatusForm,
ConfirmModal,
StatusContent
},
props: {
draft: {
type: Object,
required: true
}
},
data () {
return {
editing: false,
showingConfirmDialog: false
}
},
computed: {
relAttrs () {
if (this.draft.type === 'edit') {
return { statusId: this.draft.refId }
} else if (this.draft.type === 'reply') {
return { replyTo: this.draft.refId }
} else {
return {}
}
},
postStatusFormProps () {
return {
draftId: this.draft.id,
...this.relAttrs
}
},
refStatus () {
return this.draft.refId ? this.$store.state.statuses.allStatusesObject[this.draft.refId] : undefined
}
},
methods: {
toggleEditing () {
this.editing = !this.editing
},
abandon () {
this.showingConfirmDialog = true
},
doAbandon () {
this.$store.dispatch('abandonDraft', { id: this.draft.id })
.then(() => {
this.hideConfirmDialog()
})
},
hideConfirmDialog () {
this.showingConfirmDialog = false
}
}
}
export default Draft

View File

@ -0,0 +1,104 @@
<template>
<article class="Draft">
<div class="actions">
<button
class="btn button-default"
:class="{ toggled: editing }"
:aria-expanded="editing"
@click.prevent.stop="toggleEditing"
>
{{ $t('drafts.continue') }}
</button>
<button
class="btn button-default"
@click.prevent.stop="abandon"
>
{{ $t('drafts.abandon') }}
</button>
</div>
<div
v-if="!editing"
class="status-content"
>
<div>
<i18n-t
v-if="draft.type === 'reply' || draft.type === 'edit'"
tag="span"
:keypath="draft.type === 'reply' ? 'drafts.replying' : 'drafts.editing'"
>
<template #statusLink>
<router-link
class="faint-link"
:to="{ name: 'conversation', params: { id: draft.refId } }"
>
{{ refStatus ? refStatus.external_url : $t('drafts.unavailable') }}
</router-link>
</template>
</i18n-t>
<StatusContent
v-if="draft.refId && refStatus"
class="status-content"
:status="refStatus"
:compact="true"
/>
</div>
<p>{{ draft.status }}</p>
</div>
<div v-if="editing">
<PostStatusForm
v-if="draft.type !== 'edit'"
v-bind="postStatusFormProps"
/>
<EditStatusForm
v-else
:params="postStatusFormProps"
/>
</div>
<teleport to="#modal">
<confirm-modal
v-if="showingConfirmDialog"
:title="$t('drafts.abandon_confirm_title')"
:confirm-text="$t('drafts.abandon_confirm_accept_button')"
:cancel-text="$t('drafts.abandon_confirm_cancel_button')"
@accepted="doAbandon"
@cancelled="hideConfirmDialog"
>
{{ $t('drafts.abandon_confirm') }}
</confirm-modal>
</teleport>
</article>
</template>
<script src="./draft.js"></script>
<style lang="scss">
@import "src/variables";
.Draft {
margin: 1em;
.status-content {
border: 1px solid $fallback--faint;
border-color: var(--faint, $fallback--faint);
border-radius: $fallback--inputRadius;
border-radius: var(--inputRadius, $fallback--inputRadius);
color: $fallback--text;
color: var(--text, $fallback--text);
padding: 0.5em;
margin: 0.5em 0;
}
.actions {
display: flex;
flex-direction: row;
justify-content: space-evenly;
.btn {
flex: 1;
margin-left: 1em;
margin-right: 1em;
max-width: 10em;
}
}
}
</style>

View File

@ -0,0 +1,52 @@
import DialogModal from 'src/components/dialog_modal/dialog_modal.vue'
const DraftCloser = {
data () {
return {
showing: false
}
},
components: {
DialogModal
},
emits: [
'save',
'discard'
],
computed: {
action () {
if (this.$store.getters.mergedConfig.autoSaveDraft) {
return 'save'
} else {
return this.$store.getters.mergedConfig.unsavedPostAction
}
},
shouldConfirm () {
return this.action === 'confirm'
}
},
methods: {
requestClose () {
if (this.shouldConfirm) {
this.showing = true
} else if (this.action === 'save') {
this.save()
} else {
this.discard()
}
},
save () {
this.$emit('save')
this.showing = false
},
discard () {
this.$emit('discard')
this.showing = false
},
cancel () {
this.showing = false
}
}
}
export default DraftCloser

View File

@ -0,0 +1,43 @@
<template>
<teleport to="#modal">
<dialog-modal
v-if="showing"
v-body-scroll-lock="true"
class="confirm-modal"
:on-cancel="cancel"
>
<template #header>
<span>
{{ $t('post_status.close_confirm_title') }}
</span>
</template>
{{ $t('post_status.close_confirm') }}
<template #footer>
<button
class="btn button-default"
@click.prevent="save"
>
{{ $t('post_status.close_confirm_save_button') }}
</button>
<button
class="btn button-default"
@click.prevent="discard"
>
{{ $t('post_status.close_confirm_discard_button') }}
</button>
<button
class="btn button-default"
@click.prevent="cancel"
>
{{ $t('post_status.close_confirm_continue_composing_button') }}
</button>
</template>
</dialog-modal>
</teleport>
</template>
<script src="./draft_closer.js"></script>

View File

@ -0,0 +1,16 @@
import Draft from 'src/components/draft/draft.vue'
import List from 'src/components/list/list.vue'
const Drafts = {
components: {
Draft,
List
},
computed: {
drafts () {
return this.$store.getters.draftsArray
}
}
}
export default Drafts

View File

@ -0,0 +1,24 @@
<template>
<div class="Drafts">
<div class="panel panel-default">
<div class="panel-heading">
<div class="title">
{{ $t('drafts.drafts') }}
</div>
</div>
<div class="panel-body">
<List
:items="drafts"
>
<template #item="{ item: draft }">
<Draft
:draft="draft"
/>
</template>
</List>
</div>
</div>
</div>
</template>
<script src="./drafts.js"></script>

View File

@ -0,0 +1,44 @@
import PostStatusForm from '../post_status_form/post_status_form.vue'
import statusPosterService from '../../services/status_poster/status_poster.service.js'
const EditStatusForm = {
components: {
PostStatusForm
},
props: {
params: {
type: Object,
required: true
}
},
methods: {
requestClose () {
this.$refs.postStatusForm.requestClose()
},
doEditStatus ({ status, spoilerText, sensitive, media, contentType, poll }) {
const params = {
store: this.$store,
statusId: this.params.statusId,
status,
spoilerText,
sensitive,
poll,
media,
contentType
}
return statusPosterService.editStatus(params)
.then((data) => {
return data
})
.catch((err) => {
console.error('Error editing status', err)
return {
error: err.message
}
})
}
}
}
export default EditStatusForm

View File

@ -0,0 +1,11 @@
<template>
<PostStatusForm
ref="postStatusForm"
v-bind="params"
:post-handler="doEditStatus"
:disable-polls="true"
:disable-visibility-selector="true"
/>
</template>
<script src="./edit_status_form.js"></script>

View File

@ -1,11 +1,10 @@
import PostStatusForm from '../post_status_form/post_status_form.vue' import EditStatusForm from '../edit_status_form/edit_status_form.vue'
import Modal from '../modal/modal.vue' import Modal from '../modal/modal.vue'
import statusPosterService from '../../services/status_poster/status_poster.service.js'
import get from 'lodash/get' import get from 'lodash/get'
const EditStatusModal = { const EditStatusModal = {
components: { components: {
PostStatusForm, EditStatusForm,
Modal Modal
}, },
data () { data () {
@ -43,30 +42,10 @@ const EditStatusModal = {
} }
}, },
methods: { methods: {
doEditStatus ({ status, spoilerText, sensitive, media, contentType, poll }) {
const params = {
store: this.$store,
statusId: this.$store.state.editStatus.params.statusId,
status,
spoilerText,
sensitive,
poll,
media,
contentType
}
return statusPosterService.editStatus(params)
.then((data) => {
return data
})
.catch((err) => {
console.error('Error editing status', err)
return {
error: err.message
}
})
},
closeModal () { closeModal () {
this.$refs.editStatusForm.requestClose()
},
doCloseModal () {
this.$store.dispatch('closeEditStatusModal') this.$store.dispatch('closeEditStatusModal')
} }
} }

View File

@ -8,13 +8,12 @@
<div class="panel-heading"> <div class="panel-heading">
{{ $t('post_status.edit_status') }} {{ $t('post_status.edit_status') }}
</div> </div>
<PostStatusForm <EditStatusForm
ref="editStatusForm"
class="panel-body" class="panel-body"
v-bind="params" :params="params"
:post-handler="doEditStatus" @posted="doCloseModal"
:disable-polls="true" @can-close="doCloseModal"
:disable-visibility-selector="true"
@posted="closeModal"
/> />
</div> </div>
</Modal> </Modal>

View File

@ -1,5 +1,4 @@
import Completion from '../../services/completion/completion.js' import Completion from '../../services/completion/completion.js'
import genRandomSeed from '../../services/random_seed/random_seed.service.js'
import EmojiPicker from '../emoji_picker/emoji_picker.vue' import EmojiPicker from '../emoji_picker/emoji_picker.vue'
import Popover from 'src/components/popover/popover.vue' import Popover from 'src/components/popover/popover.vue'
import ScreenReaderNotice from 'src/components/screen_reader_notice/screen_reader_notice.vue' import ScreenReaderNotice from 'src/components/screen_reader_notice/screen_reader_notice.vue'
@ -111,7 +110,7 @@ const EmojiInput = {
}, },
data () { data () {
return { return {
randomSeed: genRandomSeed(), randomSeed: `${Math.random()}`.replace('.', '-'),
input: undefined, input: undefined,
caretEl: undefined, caretEl: undefined,
highlighted: -1, highlighted: -1,

View File

@ -1,7 +1,7 @@
<template> <template>
<div <div
ref="root" ref="root"
class="input emoji-input" class="emoji-input"
:class="{ 'with-picker': !hideEmojiButton }" :class="{ 'with-picker': !hideEmojiButton }"
> >
<slot <slot
@ -68,9 +68,9 @@
v-for="(suggestion, index) in suggestions" v-for="(suggestion, index) in suggestions"
:id="suggestionItemId(index)" :id="suggestionItemId(index)"
:key="index" :key="index"
class="menu-item autocomplete-item" class="autocomplete-item"
role="option" role="option"
:class="{ '-active': index === highlighted }" :class="{ highlighted: index === highlighted }"
:aria-label="autoCompleteItemLabel(suggestion)" :aria-label="autoCompleteItemLabel(suggestion)"
:aria-selected="index === highlighted" :aria-selected="index === highlighted"
@click.stop.prevent="onClick($event, suggestion)" @click.stop.prevent="onClick($event, suggestion)"
@ -110,8 +110,9 @@
<script src="./emoji_input.js"></script> <script src="./emoji_input.js"></script>
<style lang="scss"> <style lang="scss">
.input.emoji-input { @import "../../variables";
padding: 0;
.emoji-input {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative; position: relative;
@ -126,7 +127,8 @@
line-height: 24px; line-height: 24px;
&:hover i { &:hover i {
color: var(--text); color: $fallback--text;
color: var(--text, $fallback--text);
} }
} }
@ -143,12 +145,6 @@
input, input,
textarea { textarea {
flex: 1 0 auto; flex: 1 0 auto;
color: inherit;
/* stylelint-disable-next-line declaration-no-important */
background: none !important;
box-shadow: none;
border: none;
outline: none;
} }
&.with-picker input { &.with-picker input {
@ -183,28 +179,26 @@
position: absolute; position: absolute;
} }
&-item.menu-item { &-item {
display: flex; display: flex;
padding-top: 0; cursor: pointer;
padding-bottom: 0; padding: 0.2em 0.4em;
border-bottom: 1px solid rgb(0 0 0 / 40%);
height: 32px;
.image { .image {
width: calc(var(--__line-height) + var(--__vertical-gap) * 2); width: 32px;
height: calc(var(--__line-height) + var(--__vertical-gap) * 2); height: 32px;
line-height: var(--__line-height); line-height: 32px;
text-align: center; text-align: center;
margin-right: var(--__horizontal-gap); font-size: 32px;
margin-right: 4px;
img { img {
width: calc(var(--__line-height) + var(--__vertical-gap) * 2); width: 32px;
height: calc(var(--__line-height) + var(--__vertical-gap) * 2); height: 32px;
object-fit: contain; object-fit: contain;
} }
span {
font-size: var(--__line-height);
line-height: var(--__line-height);
}
} }
.label { .label {
@ -222,6 +216,17 @@
line-height: 9px; line-height: 9px;
} }
} }
&.highlighted {
background-color: $fallback--fg;
background-color: var(--selectedMenuPopover, $fallback--fg);
color: var(--selectedMenuPopoverText, $fallback--text);
--faint: var(--selectedMenuPopoverFaintText, $fallback--faint);
--faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint);
--lightText: var(--selectedMenuPopoverLightText, $fallback--lightText);
--icon: var(--selectedMenuPopoverIcon, $fallback--icon);
}
} }
} }
</style> </style>

View File

@ -105,7 +105,6 @@ const EmojiPicker = {
default: false default: false
} }
}, },
inject: ['popoversZLayer'],
data () { data () {
return { return {
keyword: '', keyword: '',
@ -114,7 +113,6 @@ const EmojiPicker = {
groupsScrolledClass: 'scrolled-top', groupsScrolledClass: 'scrolled-top',
keepOpen: false, keepOpen: false,
customEmojiTimeout: null, customEmojiTimeout: null,
hideCustomEmojiInPicker: false,
// Lazy-load only after the first time `showing` becomes true. // Lazy-load only after the first time `showing` becomes true.
contentLoaded: false, contentLoaded: false,
groupRefs: {}, groupRefs: {},
@ -287,7 +285,7 @@ const EmojiPicker = {
return 0 return 0
}, },
allCustomGroups () { allCustomGroups () {
if (this.hideCustomEmoji || this.hideCustomEmojiInPicker) { if (this.hideCustomEmoji) {
return {} return {}
} }
const emojis = this.$store.getters.groupedCustomEmojis const emojis = this.$store.getters.groupedCustomEmojis
@ -352,9 +350,6 @@ const EmojiPicker = {
return emoji.displayText return emoji.displayText
} }
},
isInModal () {
return this.popoversZLayer === 'modals'
} }
} }
} }

View File

@ -1,3 +1,5 @@
@import "../../variables";
$emoji-picker-header-height: 36px; $emoji-picker-header-height: 36px;
$emoji-picker-header-picture-width: 32px; $emoji-picker-header-picture-width: 32px;
$emoji-picker-header-picture-height: 32px; $emoji-picker-header-picture-height: 32px;
@ -8,6 +10,15 @@ $emoji-picker-emoji-size: 32px;
max-width: calc(100vw - 20px); // popover gives 10px margin from window edge max-width: calc(100vw - 20px); // popover gives 10px margin from window edge
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background-color: $fallback--bg;
background-color: var(--popover, $fallback--bg);
color: $fallback--link;
color: var(--popoverText, $fallback--link);
--faint: var(--popoverFaintText, $fallback--faint);
--faintLink: var(--popoverFaintLink, $fallback--faint);
--lightText: var(--popoverLightText, $fallback--lightText);
--icon: var(--popoverIcon, $fallback--icon);
&-header-image { &-header-image {
display: inline-flex; display: inline-flex;
@ -28,16 +39,11 @@ $emoji-picker-emoji-size: 32px;
} }
.keep-open, .keep-open,
.too-many-emoji, .too-many-emoji {
.hide-custom-emoji {
padding: 7px; padding: 7px;
line-height: normal; line-height: normal;
} }
.hide-custom-emoji {
padding-top: 0;
}
.too-many-emoji { .too-many-emoji {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -70,7 +76,8 @@ $emoji-picker-emoji-size: 32px;
.additional-tabs { .additional-tabs {
display: flex; display: flex;
border-left: 1px solid; border-left: 1px solid;
border-left-color: var(--border); border-left-color: $fallback--icon;
border-left-color: var(--icon, $fallback--icon);
padding-left: 7px; padding-left: 7px;
flex: 0 0 auto; flex: 0 0 auto;
} }
@ -97,8 +104,13 @@ $emoji-picker-emoji-size: 32px;
pointer-events: none; pointer-events: none;
} }
&.toggled { &.active {
border-bottom: 4px solid; border-bottom: 4px solid;
svg {
color: $fallback--lightText;
color: var(--lightText, $fallback--lightText);
}
} }
} }
} }

View File

@ -3,32 +3,25 @@
ref="popover" ref="popover"
trigger="click" trigger="click"
popover-class="emoji-picker popover-default" popover-class="emoji-picker popover-default"
:trigger-attrs="{ 'aria-hidden': true, tabindex: -1 }" :trigger-attrs="{ 'aria-hidden': true }"
@show="onPopoverShown" @show="onPopoverShown"
@close="onPopoverClosed" @close="onPopoverClosed"
> >
<template #content> <template #content>
<div class="heading"> <div class="heading">
<!--
Body scroll lock needs to be on every scrollable element on safari iOS.
Here we tell it to enable scrolling for this element.
See https://github.com/willmcpo/body-scroll-lock#vanilla-js
-->
<span <span
ref="header" ref="header"
v-body-scroll-lock="isInModal"
class="emoji-tabs" class="emoji-tabs"
> >
<span <span
v-for="group in filteredEmojiGroups" v-for="group in filteredEmojiGroups"
:ref="setGroupRef('group-header-' + group.id)" :ref="setGroupRef('group-header-' + group.id)"
:key="group.id" :key="group.id"
class="button-unstyled emoji-tabs-item" class="emoji-tabs-item"
:class="{ :class="{
toggled: activeGroupView === group.id active: activeGroupView === group.id
}" }"
:title="group.text" :title="group.text"
role="button"
@click.prevent="highlight(group.id)" @click.prevent="highlight(group.id)"
> >
<span <span
@ -52,8 +45,8 @@
class="additional-tabs" class="additional-tabs"
> >
<span <span
class="button-unstyled stickers-tab-icon additional-tabs-item" class="stickers-tab-icon additional-tabs-item"
:class="{toggled: showingStickers}" :class="{active: showingStickers}"
:title="$t('emoji.stickers')" :title="$t('emoji.stickers')"
@click.prevent="toggleStickers" @click.prevent="toggleStickers"
> >
@ -77,15 +70,13 @@
ref="search" ref="search"
v-model="keyword" v-model="keyword"
type="text" type="text"
class="input form-control" class="form-control"
:placeholder="$t('emoji.search_emoji')" :placeholder="$t('emoji.search_emoji')"
@input="$event.target.composing = false" @input="$event.target.composing = false"
> >
</div> </div>
<!-- Enables scrolling for this element on safari iOS. See comments for header. -->
<DynamicScroller <DynamicScroller
ref="emoji-groups" ref="emoji-groups"
v-body-scroll-lock="isInModal"
class="emoji-groups" class="emoji-groups"
:class="groupsScrolledClass" :class="groupsScrolledClass"
:min-item-size="minItemSize" :min-item-size="minItemSize"
@ -117,7 +108,6 @@
:key="group.id + emoji.displayText" :key="group.id + emoji.displayText"
:title="maybeLocalizedEmojiName(emoji)" :title="maybeLocalizedEmojiName(emoji)"
class="emoji-item" class="emoji-item"
role="button"
@click.stop.prevent="onEmoji(emoji)" @click.stop.prevent="onEmoji(emoji)"
> >
<span <span
@ -128,7 +118,6 @@
v-else v-else
class="emoji-picker-emoji -custom" class="emoji-picker-emoji -custom"
loading="lazy" loading="lazy"
:alt="maybeLocalizedEmojiName(emoji)"
:src="emoji.imageUrl" :src="emoji.imageUrl"
:data-emoji-name="group.id + emoji.displayText" :data-emoji-name="group.id + emoji.displayText"
/> />
@ -142,17 +131,6 @@
{{ $t('emoji.keep_open') }} {{ $t('emoji.keep_open') }}
</Checkbox> </Checkbox>
</div> </div>
<div
v-if="!hideCustomEmoji"
class="hide-custom-emoji"
>
<Checkbox
v-model="hideCustomEmojiInPicker"
@change="onShowing"
>
{{ $t('emoji.hide_custom_emoji') }}
</Checkbox>
</div>
</div> </div>
<div <div
v-if="showingStickers" v-if="showingStickers"

View File

@ -1,17 +1,5 @@
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
import UserListPopover from '../user_list_popover/user_list_popover.vue' import UserListPopover from '../user_list_popover/user_list_popover.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faPlus,
faMinus,
faCheck
} from '@fortawesome/free-solid-svg-icons'
library.add(
faPlus,
faMinus,
faCheck
)
const EMOJI_REACTION_COUNT_CUTOFF = 12 const EMOJI_REACTION_COUNT_CUTOFF = 12
@ -45,9 +33,6 @@ const EmojiReactions = {
}, },
loggedIn () { loggedIn () {
return !!this.$store.state.users.currentUser return !!this.$store.state.users.currentUser
},
remoteInteractionLink () {
return this.$store.getters.remoteInteractionLink({ statusId: this.status.id })
} }
}, },
methods: { methods: {
@ -57,10 +42,10 @@ const EmojiReactions = {
reactedWith (emoji) { reactedWith (emoji) {
return this.status.emoji_reactions.find(r => r.name === emoji).me return this.status.emoji_reactions.find(r => r.name === emoji).me
}, },
async fetchEmojiReactionsByIfMissing () { fetchEmojiReactionsByIfMissing () {
const hasNoAccounts = this.status.emoji_reactions.find(r => !r.accounts) const hasNoAccounts = this.status.emoji_reactions.find(r => !r.accounts)
if (hasNoAccounts) { if (hasNoAccounts) {
return await this.$store.dispatch('fetchEmojiReactionsBy', this.status.id) this.$store.dispatch('fetchEmojiReactionsBy', this.status.id)
} }
}, },
reactWith (emoji) { reactWith (emoji) {
@ -69,26 +54,14 @@ const EmojiReactions = {
unreact (emoji) { unreact (emoji) {
this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
}, },
async emojiOnClick (emoji, event) { emojiOnClick (emoji, event) {
if (!this.loggedIn) return if (!this.loggedIn) return
await this.fetchEmojiReactionsByIfMissing()
if (this.reactedWith(emoji)) { if (this.reactedWith(emoji)) {
this.unreact(emoji) this.unreact(emoji)
} else { } else {
this.reactWith(emoji) this.reactWith(emoji)
} }
},
counterTriggerAttrs (reaction) {
return {
class: [
'btn',
'button-default',
'emoji-reaction-count-button',
{ '-picked-reaction': this.reactedWith(reaction.name) }
],
'aria-label': this.$tc('status.reaction_count_label', reaction.count, { num: reaction.count })
}
} }
} }
} }

View File

@ -1,19 +1,15 @@
<template> <template>
<div class="EmojiReactions"> <div class="EmojiReactions">
<span <UserListPopover
v-for="(reaction) in emojiReactions" v-for="(reaction) in emojiReactions"
:key="reaction.url || reaction.name" :key="reaction.url || reaction.name"
class="emoji-reaction-container btn-group" :users="accountsForEmoji[reaction.name]"
> >
<component <button
:is="loggedIn ? 'button' : 'a'"
v-bind="!loggedIn ? { href: remoteInteractionLink } : {}"
role="button"
class="emoji-reaction btn button-default" class="emoji-reaction btn button-default"
:class="{ '-picked-reaction': reactedWith(reaction.name) }" :class="{ '-picked-reaction': reactedWith(reaction.name), 'not-clickable': !loggedIn }"
:title="reaction.url ? reaction.name : undefined"
:aria-pressed="reactedWith(reaction.name)"
@click="emojiOnClick(reaction.name, $event)" @click="emojiOnClick(reaction.name, $event)"
@mouseenter="fetchEmojiReactionsByIfMissing()"
> >
<span <span
class="reaction-emoji" class="reaction-emoji"
@ -21,6 +17,7 @@
<img <img
v-if="reaction.url" v-if="reaction.url"
:src="reaction.url" :src="reaction.url"
:title="reaction.name"
class="reaction-emoji-content" class="reaction-emoji-content"
width="1em" width="1em"
> >
@ -29,36 +26,9 @@
class="reaction-emoji reaction-emoji-content" class="reaction-emoji reaction-emoji-content"
>{{ reaction.name }}</span> >{{ reaction.name }}</span>
</span> </span>
<FALayers> <span>{{ reaction.count }}</span>
<FAIcon </button>
v-if="reactedWith(reaction.name)" </UserListPopover>
class="active-marker"
transform="shrink-6 up-9"
icon="check"
/>
<FAIcon
v-if="!reactedWith(reaction.name)"
class="focus-marker"
transform="shrink-6 up-9"
icon="plus"
/>
<FAIcon
v-else
class="focus-marker"
transform="shrink-6 up-9"
icon="minus"
/>
</FALayers>
</component>
<UserListPopover
:users="accountsForEmoji[reaction.name]"
class="emoji-reaction-popover"
:trigger-attrs="counterTriggerAttrs(reaction)"
@show="fetchEmojiReactionsByIfMissing()"
>
<span class="emoji-reaction-counts">{{ reaction.count }}</span>
</UserListPopover>
</span>
<a <a
v-if="tooManyReactions" v-if="tooManyReactions"
class="emoji-reaction-expand faint" class="emoji-reaction-expand faint"
@ -72,7 +42,7 @@
<script src="./emoji_reactions.js"></script> <script src="./emoji_reactions.js"></script>
<style lang="scss"> <style lang="scss">
@import "../../mixins"; @import "../../variables";
.EmojiReactions { .EmojiReactions {
display: flex; display: flex;
@ -81,43 +51,14 @@
--emoji-size: calc(1.25em * var(--emojiReactionsScale, 1)); --emoji-size: calc(1.25em * var(--emojiReactionsScale, 1));
.emoji-reaction-container {
display: flex;
align-items: stretch;
margin-top: 0.5em;
margin-right: 0.5em;
.emoji-reaction-popover {
padding: 0;
.emoji-reaction-count-button {
margin: 0;
height: 100%;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
box-sizing: border-box;
min-width: 2em;
display: inline-flex;
justify-content: center;
align-items: center;
&.-picked-reaction {
border: 1px solid var(--accent);
margin-right: -1px;
}
}
}
}
.emoji-reaction { .emoji-reaction {
padding-left: 0.5em; padding: 0 0.5em;
margin-right: 0.5em;
margin-top: 0.5em;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-sizing: border-box; box-sizing: border-box;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
margin: 0;
.reaction-emoji { .reaction-emoji {
width: var(--emoji-size); width: var(--emoji-size);
@ -144,42 +85,19 @@
outline: none; outline: none;
} }
.svg-inline--fa { &.not-clickable {
color: var(--text); cursor: default;
&:hover {
box-shadow: $fallback--buttonShadow;
box-shadow: var(--buttonShadow);
}
} }
&.-picked-reaction { &.-picked-reaction {
border: 1px solid var(--accent); border: 1px solid var(--accent, $fallback--link);
margin-left: -1px; // offset the border, can't use inset shadows either margin-left: -1px; // offset the border, can't use inset shadows either
margin-right: -1px; margin-right: calc(0.5em - 1px);
.svg-inline--fa {
color: var(--accent);
}
}
@include unfocused-style {
.focus-marker {
visibility: hidden;
}
.active-marker {
visibility: visible;
}
}
@include focused-style {
.svg-inline--fa {
color: var(--accent);
}
.focus-marker {
visibility: visible;
}
.active-marker {
visibility: hidden;
}
} }
} }

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