diff options
Diffstat (limited to 'src')
147 files changed, 1384 insertions, 1011 deletions
diff --git a/src/App.scss b/src/App.scss index 75b2667c..1c4c8941 100644 --- a/src/App.scss +++ b/src/App.scss @@ -1,5 +1,7 @@ // stylelint-disable rscss/class-format -@import './_variables.scss'; +/* stylelint-disable no-descending-specificity */ +@import "./variables"; +@import "./panel"; :root { --navbar-height: 3.5rem; @@ -123,7 +125,7 @@ h4 { font-weight: 1000; } -i[class*=icon-], +i[class*="icon-"], .svg-inline--fa, .iconLetter { color: $fallback--icon; @@ -132,7 +134,7 @@ i[class*=icon-], .button-unstyled:hover, a:hover { - > i[class*=icon-], + > i[class*="icon-"], > .svg-inline--fa, > .iconLetter { color: var(--text); @@ -141,12 +143,11 @@ a:hover { nav { z-index: var(--ZI_navbar); - color: var(--topBarText); background-color: $fallback--fg; background-color: var(--topBar, $fallback--fg); color: $fallback--faint; color: var(--faint, $fallback--faint); - box-shadow: 0 0 4px rgba(0, 0, 0, 0.6); + box-shadow: 0 0 4px rgb(0 0 0 / 60%); box-shadow: var(--topBarShadow); box-sizing: border-box; height: var(--navbar-height); @@ -191,13 +192,11 @@ nav { } .underlay { - grid-column-start: 1; - grid-column-end: span 3; - grid-row-start: 1; - grid-row-end: 1; + grid-column: 1 / span 3; + grid-row: 1 / 1; pointer-events: none; - background-color: rgba(0, 0, 0, 0.15); - background-color: var(--underlay, rgba(0, 0, 0, 0.15)); + background-color: rgb(0 0 0 / 15%); + background-color: var(--underlay, rgb(0 0 0 / 15%)); z-index: -1000; } @@ -231,8 +230,7 @@ nav { display: grid; grid-template-columns: 100%; box-sizing: border-box; - grid-row-start: 1; - grid-row-end: 1; + grid-row: 1 / 1; margin: 0 calc(var(--___columnMargin) / 2); padding: calc(var(--___columnMargin)) 0; row-gap: var(--___columnMargin); @@ -307,7 +305,7 @@ nav { align-content: start; } - &.-reverse:not(.-wide):not(.-mobile) { + &.-reverse:not(.-wide, .-mobile) { grid-template-columns: var(--effectiveContentColumnWidth) var(--effectiveSidebarColumnWidth); @@ -336,11 +334,8 @@ nav { padding: 0; .column { - margin-left: 0; - margin-right: 0; padding-top: 0; - margin-top: var(--navbar-height); - margin-bottom: 0; + margin: var(--navbar-height) 0 0 0; } .panel-heading, @@ -389,7 +384,7 @@ nav { background: transparent; } - i[class*=icon-], + i[class*="icon-"], .svg-inline--fa { color: $fallback--text; color: var(--btnText, $fallback--text); @@ -400,12 +395,15 @@ nav { } &:hover { - box-shadow: 0 0 4px rgba(255, 255, 255, 0.3); + box-shadow: 0 0 4px rgb(255 255 255 / 30%); box-shadow: var(--buttonHoverShadow); } &:active { - box-shadow: 0 0 4px 0 rgba(255, 255, 255, 0.3), 0 1px 0 0 rgba(0, 0, 0, 0.2) inset, 0 -1px 0 0 rgba(255, 255, 255, 0.2) inset; + 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); @@ -438,7 +436,10 @@ nav { color: var(--btnToggledText, $fallback--text); background-color: $fallback--fg; background-color: var(--btnToggled, $fallback--fg); - box-shadow: 0 0 4px 0 rgba(255, 255, 255, 0.3), 0 1px 0 0 rgba(0, 0, 0, 0.2) inset, 0 -1px 0 0 rgba(255, 255, 255, 0.2) inset; + 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, @@ -503,7 +504,10 @@ textarea, border: none; border-radius: $fallback--inputRadius; border-radius: var(--inputRadius, $fallback--inputRadius); - box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2) inset, 0 -1px 0 0 rgba(255, 255, 255, 0.2) inset, 0 0 2px 0 rgba(0, 0, 0, 1) inset; + box-shadow: + 0 1px 0 0 rgb(0 0 0 / 20%) inset, + 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); @@ -521,13 +525,13 @@ textarea, padding: 0 var(--_padding); &:disabled, - &[disabled=disabled], + &[disabled="disabled"], &.disabled { cursor: not-allowed; opacity: 0.5; } - &[type=range] { + &[type="range"] { background: none; border: none; margin: 0; @@ -535,7 +539,7 @@ textarea, flex: 1; } - &[type=radio] { + &[type="radio"] { display: none; &:checked + label::before { @@ -555,7 +559,7 @@ textarea, + label::before { flex-shrink: 0; display: inline-block; - content: ''; + content: ""; transition: box-shadow 200ms; width: 1.1em; height: 1.1em; @@ -575,7 +579,7 @@ textarea, } } - &[type=checkbox] { + &[type="checkbox"] { display: none; &:checked + label::before { @@ -594,7 +598,7 @@ textarea, + label::before { flex-shrink: 0; display: inline-block; - content: '✓'; + content: "✓"; transition: color 200ms; width: 1.1em; height: 1.1em; @@ -634,10 +638,10 @@ option { } .hide-number-spinner { - -moz-appearance: textfield; + appearance: textfield; - &[type=number]::-webkit-inner-spin-button, - &[type=number]::-webkit-outer-spin-button { + &[type="number"]::-webkit-inner-spin-button, + &[type="number"]::-webkit-outer-spin-button { opacity: 0; display: none; } @@ -669,8 +673,6 @@ option { } } -@import './panel.scss'; - .fa { color: grey; } @@ -686,7 +688,7 @@ option { max-width: 10em; min-width: 1.7em; height: 1.3em; - padding: 0.15em 0.15em; + padding: 0.15em; vertical-align: middle; font-weight: normal; font-style: normal; @@ -789,7 +791,8 @@ option { .fa-old-padding { &.iconLetter, - &.svg-inline--fa, &-layer { + &.svg-inline--fa, + &-layer { padding: 0 0.3em; } } @@ -883,3 +886,4 @@ option { .fade-leave-active { opacity: 0; } +/* stylelint-enable no-descending-specificity */ diff --git a/src/_mixins.scss b/src/_mixins.scss index 1fed16c3..e99fe26f 100644 --- a/src/_mixins.scss +++ b/src/_mixins.scss @@ -1,13 +1,14 @@ @mixin unfocused-style { @content; - &:focus:not(:focus-visible):not(:hover) { + &:focus:not(:focus-visible, :hover) { @content; } } @mixin focused-style { - &:hover, &:focus { + &:hover, + &:focus { @content; } diff --git a/src/_variables.scss b/src/_variables.scss index 099d3606..751fc9a4 100644 --- a/src/_variables.scss +++ b/src/_variables.scss @@ -4,20 +4,20 @@ $darkened-background: whitesmoke; $fallback--bg: #121a24; $fallback--fg: #182230; -$fallback--faint: rgba(185, 185, 186, .5); +$fallback--faint: rgb(185 185 186 / 50%); $fallback--text: #b9b9ba; $fallback--link: #d8a070; $fallback--icon: #666; -$fallback--lightBg: rgb(21, 30, 42); +$fallback--lightBg: rgb(21 30 42); $fallback--lightText: #b9b9ba; $fallback--border: #222; -$fallback--cRed: #ff0000; +$fallback--cRed: #f00; $fallback--cBlue: #0095ff; $fallback--cGreen: #0fa00f; $fallback--cOrange: orange; -$fallback--alertError: rgba(211,16,20,.5); -$fallback--alertWarning: rgba(111,111,20,.5); +$fallback--alertError: rgb(211 16 20 / 50%); +$fallback--alertWarning: rgb(111 111 20 / 50%); $fallback--panelRadius: 10px; $fallback--checkboxRadius: 2px; @@ -29,6 +29,8 @@ $fallback--avatarAltRadius: 10px; $fallback--attachmentRadius: 10px; $fallback--chatMessageRadius: 10px; -$fallback--buttonShadow: 0px 0px 2px 0px rgba(0, 0, 0, 1), 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset; +$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; diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 7a4672b6..1fa9dd2a 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -1,6 +1,8 @@ import { createApp } from 'vue' import { createRouter, createWebHistory } from 'vue-router' import vClickOutside from 'click-outside-vue3' +import VueVirtualScroller from 'vue-virtual-scroller' +import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome' @@ -397,6 +399,7 @@ const afterStoreSetup = async ({ store, i18n }) => { app.use(vClickOutside) app.use(VBodyScrollLock) + app.use(VueVirtualScroller) app.component('FAIcon', FontAwesomeIcon) app.component('FALayers', FontAwesomeLayers) diff --git a/src/components/about/about.vue b/src/components/about/about.vue index 33586c97..8a551485 100644 --- a/src/components/about/about.vue +++ b/src/components/about/about.vue @@ -9,6 +9,3 @@ </template> <script src="./about.js"></script> - -<style lang="scss"> -</style> diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue index 218aa6b3..973a5935 100644 --- a/src/components/account_actions/account_actions.vue +++ b/src/components/account_actions/account_actions.vue @@ -80,7 +80,8 @@ <script src="./account_actions.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; + .AccountActions { .ellipsis-button { width: 2.5em; diff --git a/src/components/announcement/announcement.js b/src/components/announcement/announcement.js index c10c7d90..30254926 100644 --- a/src/components/announcement/announcement.js +++ b/src/components/announcement/announcement.js @@ -27,6 +27,9 @@ const Announcement = { ...mapState({ currentUser: state => state.users.currentUser }), + canEditAnnouncement () { + return this.currentUser && this.currentUser.privileges.includes('announcements_manage_announcements') + }, content () { return this.announcement.content }, diff --git a/src/components/announcement/announcement.vue b/src/components/announcement/announcement.vue index 5f64232a..a1c5791e 100644 --- a/src/components/announcement/announcement.vue +++ b/src/components/announcement/announcement.vue @@ -45,14 +45,14 @@ {{ $t('announcements.mark_as_read_action') }} </button> <button - v-if="currentUser && currentUser.role === 'admin'" + v-if="canEditAnnouncement" class="btn button-default" @click="enterEditMode" > {{ $t('announcements.edit_action') }} </button> <button - v-if="currentUser && currentUser.role === 'admin'" + v-if="canEditAnnouncement" class="btn button-default" @click="deleteAnnouncement" > @@ -102,19 +102,19 @@ @import "../../variables"; .announcement { - border-bottom-width: 1px; - border-bottom-style: solid; - border-bottom-color: var(--border, $fallback--border); + border-bottom: 1px solid var(--border, $fallback--border); border-radius: 0; padding: var(--status-margin, $status-margin); - .heading, .body { + .heading, + .body { margin-bottom: var(--status-margin, $status-margin); } .footer { display: flex; flex-direction: column; + .times { display: flex; flex-direction: column; diff --git a/src/components/announcements_page/announcements_page.js b/src/components/announcements_page/announcements_page.js index 0bb4892e..8d1204d4 100644 --- a/src/components/announcements_page/announcements_page.js +++ b/src/components/announcements_page/announcements_page.js @@ -28,6 +28,9 @@ const AnnouncementsPage = { }), announcements () { return this.$store.state.announcements.announcements + }, + canPostAnnouncement () { + return this.currentUser && this.currentUser.privileges.includes('announcements_manage_announcements') } }, methods: { diff --git a/src/components/announcements_page/announcements_page.vue b/src/components/announcements_page/announcements_page.vue index b1489dec..78d3ecee 100644 --- a/src/components/announcements_page/announcements_page.vue +++ b/src/components/announcements_page/announcements_page.vue @@ -7,7 +7,7 @@ </div> <div class="panel-body"> <section - v-if="currentUser && currentUser.role === 'admin'" + v-if="canPostAnnouncement" > <div class="post-form"> <div class="heading"> @@ -67,7 +67,8 @@ .post-form { padding: var(--status-margin, $status-margin); - .heading, .body { + .heading, + .body { margin-bottom: var(--status-margin, $status-margin); } diff --git a/src/components/async_component_error/async_component_error.vue b/src/components/async_component_error/async_component_error.vue index 26ab5d21..2ff8974c 100644 --- a/src/components/async_component_error/async_component_error.vue +++ b/src/components/async_component_error/async_component_error.vue @@ -34,9 +34,10 @@ export default { height: 100%; align-items: center; justify-content: center; + .btn { - margin: .5em; - padding: .5em 2em; + margin: 0.5em; + padding: 0.5em 2em; } } </style> diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index 5dc50475..6e14b24d 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -36,6 +36,7 @@ library.add( const Attachment = { props: [ 'attachment', + 'compact', 'description', 'hideDescription', 'nsfw', @@ -71,7 +72,8 @@ const Attachment = { { '-loading': this.loading, '-nsfw-placeholder': this.hidden, - '-editable': this.edit !== undefined + '-editable': this.edit !== undefined, + '-compact': this.compact }, '-type-' + this.type, this.size && '-size-' + this.size, diff --git a/src/components/attachment/attachment.scss b/src/components/attachment/attachment.scss index b2dea98d..681bab29 100644 --- a/src/components/attachment/attachment.scss +++ b/src/components/attachment/attachment.scss @@ -1,4 +1,4 @@ -@import '../../_variables.scss'; +@import "../../variables"; .Attachment { display: inline-flex; @@ -102,14 +102,13 @@ padding-top: 0.5em; } - .play-icon { position: absolute; font-size: 64px; top: calc(50% - 32px); left: calc(50% - 32px); - color: rgba(255, 255, 255, 0.75); - text-shadow: 0 0 2px rgba(0, 0, 0, 0.4); + color: rgb(255 255 255 / 75%); + text-shadow: 0 0 2px rgb(0 0 0 / 40%); &::before { margin: 0; @@ -135,18 +134,32 @@ margin-left: 0.5em; font-size: 1.25em; // TODO: theming? hard to theme with unknown background image color - background: rgba(230, 230, 230, 0.7); + background: rgb(230 230 230 / 70%); .svg-inline--fa { - color: rgba(0, 0, 0, 0.6); + color: rgb(0 0 0 / 60%); } &:hover .svg-inline--fa { - color: rgba(0, 0, 0, 0.9); + color: rgb(0 0 0 / 90%); } } } + &.-contain-fit { + img, + canvas { + object-fit: contain; + } + } + + &.-cover-fit { + img, + canvas { + object-fit: cover; + } + } + .oembed-container { line-height: 1.2em; flex: 1 0 100%; @@ -160,8 +173,9 @@ .image { flex: 1; + img { - border: 0px; + border: 0; border-radius: 5px; height: 100%; object-fit: cover; @@ -172,9 +186,10 @@ flex: 2; margin: 8px; word-break: break-all; + h1 { font-size: 1rem; - margin: 0px; + margin: 0; } } } @@ -252,17 +267,9 @@ cursor: progress; } - &.-contain-fit { - img, - canvas { - object-fit: contain; - } - } - - &.-cover-fit { - img, - canvas { - object-fit: cover; + &.-compact { + .placeholder-container { + padding-bottom: 0.5em; } } } diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 2a89886d..79f62806 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -162,10 +162,11 @@ target="_blank" > <FAIcon - size="5x" + :size="compact ? '2x' : '5x'" :icon="placeholderIconClass" + :title="localDescription" /> - <p> + <p v-if="!compact"> {{ localDescription }} </p> </a> diff --git a/src/components/autosuggest/autosuggest.vue b/src/components/autosuggest/autosuggest.vue index f283ab82..7b7102fd 100644 --- a/src/components/autosuggest/autosuggest.vue +++ b/src/components/autosuggest/autosuggest.vue @@ -24,7 +24,7 @@ <script src="./autosuggest.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .autosuggest { position: relative; @@ -50,7 +50,7 @@ border-radius: var(--inputRadius, $fallback--inputRadius); border-top-left-radius: 0; border-top-right-radius: 0; - box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); + box-shadow: 1px 1px 4px rgb(0 0 0 / 60%); box-shadow: var(--panelShadow); overflow-y: auto; z-index: 1; diff --git a/src/components/avatar_list/avatar_list.vue b/src/components/avatar_list/avatar_list.vue index 9a6ca3f6..2d00cb7b 100644 --- a/src/components/avatar_list/avatar_list.vue +++ b/src/components/avatar_list/avatar_list.vue @@ -17,7 +17,7 @@ <script src="./avatar_list.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .avatars { display: flex; diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue index 418de926..705e20f5 100644 --- a/src/components/basic_user_card/basic_user_card.vue +++ b/src/components/basic_user_card/basic_user_card.vue @@ -49,7 +49,7 @@ margin: 0; padding: 0.6em 1em; - --emoji-size: 14px; + --emoji-size: 14px; &-collapsed-content { margin-left: 0.7em; diff --git a/src/components/block_card/block_card.vue b/src/components/block_card/block_card.vue index 2fe66d4c..b14ef844 100644 --- a/src/components/block_card/block_card.vue +++ b/src/components/block_card/block_card.vue @@ -37,6 +37,7 @@ .block-card-content-container { margin-top: 0.5em; text-align: right; + button { width: 10em; } diff --git a/src/components/chat/chat.scss b/src/components/chat/chat.scss index f2e154ab..43e7a5e4 100644 --- a/src/components/chat/chat.scss +++ b/src/components/chat/chat.scss @@ -17,7 +17,7 @@ width: 100%; overflow: visible; min-height: calc(100vh - var(--navbar-height)); - margin: 0 0 0 0; + margin: 0; border-radius: 10px 10px 0 0; border-radius: var(--panelRadius, 10px) var(--panelRadius, 10px) 0 0; @@ -66,7 +66,7 @@ display: flex; justify-content: center; align-items: center; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.3); + box-shadow: 0 1px 1px rgb(0 0 0 / 30%), 0 2px 4px rgb(0 0 0 / 30%); z-index: 10; transition: 0.35s all; transition-timing-function: cubic-bezier(0, 1, 0.5, 1); diff --git a/src/components/chat/chat.vue b/src/components/chat/chat.vue index 2e7df7bd..b1e5468c 100644 --- a/src/components/chat/chat.vue +++ b/src/components/chat/chat.vue @@ -95,6 +95,6 @@ <script src="./chat.js"></script> <style lang="scss"> -@import '../../_variables.scss'; -@import './chat.scss'; +@import "../../variables"; +@import "./chat"; </style> diff --git a/src/components/chat_list/chat_list.vue b/src/components/chat_list/chat_list.vue index 1248c4c8..27a475ed 100644 --- a/src/components/chat_list/chat_list.vue +++ b/src/components/chat_list/chat_list.vue @@ -45,7 +45,7 @@ <script src="./chat_list.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .chat-list { min-height: 25em; diff --git a/src/components/chat_list_item/chat_list_item.scss b/src/components/chat_list_item/chat_list_item.scss index c6b45c34..3a84672b 100644 --- a/src/components/chat_list_item/chat_list_item.scss +++ b/src/components/chat_list_item/chat_list_item.scss @@ -13,7 +13,7 @@ &:hover { background-color: var(--selectedPost, $fallback--lightBg); - box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.1); + box-shadow: 0 0 3px 1px rgb(0 0 0 / 10%); } .chat-list-item-left { @@ -67,6 +67,7 @@ canvas { display: none; } + img { visibility: visible; } @@ -79,13 +80,11 @@ .chat-preview-body { --emoji-size: 1.4em; + + padding-right: 1em; } .time-wrapper { line-height: var(--post-line-height); } - - .chat-preview-body { - padding-right: 1em; - } } diff --git a/src/components/chat_list_item/chat_list_item.vue b/src/components/chat_list_item/chat_list_item.vue index c7c0e878..69ad609b 100644 --- a/src/components/chat_list_item/chat_list_item.vue +++ b/src/components/chat_list_item/chat_list_item.vue @@ -48,6 +48,6 @@ <script src="./chat_list_item.js"></script> <style lang="scss"> -@import '../../_variables.scss'; -@import './chat_list_item.scss'; +@import "../../variables"; +@import "./chat_list_item"; </style> diff --git a/src/components/chat_message/chat_message.scss b/src/components/chat_message/chat_message.scss index 1913479f..fd5b7aa4 100644 --- a/src/components/chat_message/chat_message.scss +++ b/src/components/chat_message/chat_message.scss @@ -1,12 +1,12 @@ -@import '../../_variables.scss'; +@import "../../variables"; .chat-message-wrapper { - &.hovered-message-chain { .animated.Avatar { canvas { display: none; } + img { visibility: visible; } @@ -28,7 +28,8 @@ .menu-icon { cursor: pointer; - &:hover, .extra-button-popover.open & { + &:hover, + .extra-button-popover.open & { color: $fallback--text; color: var(--text, $fallback--text); } @@ -54,27 +55,11 @@ width: 32px; } - .link-preview, .attachments { + .link-preview, + .attachments { margin-bottom: 1em; } - .chat-message-inner { - display: flex; - flex-direction: column; - align-items: flex-start; - max-width: 80%; - min-width: 10em; - width: 100%; - - &.with-media { - width: 100%; - - .status { - width: 100%; - } - } - } - .status { border-radius: $fallback--chatMessageRadius; border-radius: var(--chatMessageRadius, $fallback--chatMessageRadius); @@ -86,7 +71,7 @@ position: relative; float: right; font-size: 0.8em; - margin: -1em 0 -0.5em 0; + margin: -1em 0 -0.5em; font-style: italic; opacity: 0.8; } @@ -103,18 +88,54 @@ } .pending { - .status-content.media-body, .created-at { + .status-content.media-body, + .created-at { color: var(--faint); } } .error { - .status-content.media-body, .created-at { + .status-content.media-body, + .created-at { color: $fallback--cRed; color: var(--badgeNotification, $fallback--cRed); } } + .chat-message-inner { + display: flex; + flex-direction: column; + align-items: flex-start; + max-width: 80%; + min-width: 10em; + width: 100%; + } + + .outgoing { + display: flex; + flex-flow: row wrap; + align-content: 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 { + align-items: flex-end; + } + + .chat-message-menu { + right: 0.4rem; + } + } + .incoming { a { color: var(--chatMessageIncomingLink, $fallback--link); @@ -137,36 +158,17 @@ } } - .outgoing { - display: flex; - flex-direction: row; - flex-wrap: wrap; - align-content: end; - justify-content: flex-end; - - a { - color: var(--chatMessageOutgoingLink, $fallback--link); - } + .chat-message-inner.with-media { + width: 100%; .status { - color: var(--chatMessageOutgoingText, $fallback--text); - background-color: var(--chatMessageOutgoingBg, $fallback--lightBg); - border: 1px solid var(--chatMessageOutgoingBorder, --lightBg); - } - - .chat-message-inner { - align-items: flex-end; - } - - .chat-message-menu { - right: 0.4rem; + width: 100%; } } .visible { opacity: 1; } - } .chat-message-date-separator { diff --git a/src/components/chat_message/chat_message.vue b/src/components/chat_message/chat_message.vue index d635c47e..381574c3 100644 --- a/src/components/chat_message/chat_message.vue +++ b/src/components/chat_message/chat_message.vue @@ -33,7 +33,7 @@ <div class="media status" :class="{ 'without-attachment': !hasAttachment, 'pending': chatViewItem.data.pending, 'error': chatViewItem.data.error }" - style="position: relative" + style="position: relative;" @mouseenter="hovered = true" @mouseleave="hovered = false" > @@ -98,6 +98,6 @@ <script src="./chat_message.js"></script> <style lang="scss"> -@import './chat_message.scss'; +@import "./chat_message"; </style> diff --git a/src/components/chat_new/chat_new.scss b/src/components/chat_new/chat_new.scss index 240e1a38..b145ecf9 100644 --- a/src/components/chat_new/chat_new.scss +++ b/src/components/chat_new/chat_new.scss @@ -1,7 +1,7 @@ .chat-new { .input-wrap { display: flex; - margin: 0.7em 0.5em 0.7em 0.5em; + margin: 0.7em 0.5em; input { width: 100%; diff --git a/src/components/chat_new/chat_new.vue b/src/components/chat_new/chat_new.vue index bf09a379..52306c1d 100644 --- a/src/components/chat_new/chat_new.vue +++ b/src/components/chat_new/chat_new.vue @@ -46,6 +46,6 @@ <script src="./chat_new.js"></script> <style lang="scss"> -@import '../../_variables.scss'; -@import './chat_new.scss'; +@import "../../variables"; +@import "./chat_new"; </style> diff --git a/src/components/chat_title/chat_title.vue b/src/components/chat_title/chat_title.vue index ab7491fa..93db4fa7 100644 --- a/src/components/chat_title/chat_title.vue +++ b/src/components/chat_title/chat_title.vue @@ -26,7 +26,7 @@ <script src="./chat_title.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .chat-title { display: flex; diff --git a/src/components/checkbox/checkbox.vue b/src/components/checkbox/checkbox.vue index b6768d67..7139d4fc 100644 --- a/src/components/checkbox/checkbox.vue +++ b/src/components/checkbox/checkbox.vue @@ -32,7 +32,7 @@ export default { </script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .checkbox { position: relative; @@ -49,13 +49,13 @@ export default { right: 0; top: 0; display: block; - content: '✓'; + content: "✓"; transition: color 200ms; width: 1.1em; height: 1.1em; border-radius: $fallback--checkboxRadius; border-radius: var(--checkboxRadius, $fallback--checkboxRadius); - box-shadow: 0px 0px 2px black inset; + box-shadow: 0 0 2px black inset; box-shadow: var(--inputShadow); background-color: $fallback--fg; background-color: var(--input, $fallback--fg); @@ -71,15 +71,16 @@ export default { &.disabled { .checkbox-indicator::before, .label { - opacity: .5; + opacity: 0.5; } + .label { color: $fallback--faint; color: var(--faint, $fallback--faint); } } - input[type=checkbox] { + input[type="checkbox"] { display: none; &:checked + .checkbox-indicator::before { @@ -88,15 +89,14 @@ export default { } &:indeterminate + .checkbox-indicator::before { - content: '–'; + content: "–"; color: $fallback--text; color: var(--inputText, $fallback--text); } - } & > span { - margin-left: .5em; + margin-left: 0.5em; } } </style> diff --git a/src/components/color_input/color_input.scss b/src/components/color_input/color_input.scss index 3de31fde..ca46199a 100644 --- a/src/components/color_input/color_input.scss +++ b/src/components/color_input/color_input.scss @@ -1,4 +1,4 @@ -@import '../../_variables.scss'; +@import "../../variables"; .color-input { display: inline-flex; @@ -8,7 +8,7 @@ flex: 0 0 0; max-width: 9em; align-items: stretch; - padding: .2em 8px; + padding: 0.2em 8px; input { background: none; @@ -31,6 +31,7 @@ min-height: 100%; } } + .computedIndicator, .transparentIndicator { flex: 0 0 2em; @@ -38,22 +39,27 @@ align-self: stretch; min-height: 100%; } + .transparentIndicator { // forgot to install counter-strike source, ooops - background-color: #FF00FF; + background-color: #f0f; position: relative; - &::before, &::after { + + &::before, + &::after { display: block; - content: ''; - background-color: #000000; + content: ""; + background-color: #000; position: absolute; height: 50%; width: 50%; } + &::after { top: 0; left: 0; } + &::before { bottom: 0; right: 0; @@ -64,5 +70,4 @@ .label { flex: 1 1 auto; } - } diff --git a/src/components/contrast_ratio/contrast_ratio.vue b/src/components/contrast_ratio/contrast_ratio.vue index 374cb9ba..bbd6fd4a 100644 --- a/src/components/contrast_ratio/contrast_ratio.vue +++ b/src/components/contrast_ratio/contrast_ratio.vue @@ -87,7 +87,6 @@ export default { .contrast-ratio { display: flex; justify-content: flex-end; - margin-top: -4px; margin-bottom: 5px; diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index afa04db0..7577129e 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -210,17 +210,16 @@ <script src="./conversation.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .Conversation { z-index: 1; .conversation-dive-to-top-level-box { padding: var(--status-margin, $status-margin); - border-bottom-width: 1px; - border-bottom-style: solid; - border-bottom-color: var(--border, $fallback--border); + border-bottom: 1px solid var(--border, $fallback--border); border-radius: 0; + /* Make the button stretch along the whole row */ display: flex; align-items: stretch; @@ -235,52 +234,48 @@ .thread-ancestor.-faded .StatusContent { --link: var(--faintLink); --text: var(--faint); + color: var(--text); } .thread-ancestor-dive-box { padding-left: var(--status-margin, $status-margin); - border-bottom-width: 1px; - border-bottom-style: solid; - border-bottom-color: var(--border, $fallback--border); + border-bottom: 1px solid var(--border, $fallback--border); border-radius: 0; + /* Make the button stretch along the whole row */ - &, &-inner { + &, + &-inner { display: flex; align-items: stretch; flex-direction: column; } } + .thread-ancestor-dive-box-inner { padding: var(--status-margin, $status-margin); } .conversation-status { - border-bottom-width: 1px; - border-bottom-style: solid; - border-bottom-color: var(--border, $fallback--border); + border-bottom: 1px solid var(--border, $fallback--border); border-radius: 0; } .thread-ancestor-has-other-replies .conversation-status, + &:last-child .conversation-status, .thread-ancestor:last-child .conversation-status, .thread-ancestor:last-child .thread-ancestor-dive-box, - &:last-child .conversation-status, &.-expanded .thread-tree .conversation-status { border-bottom: none; } .thread-ancestors + .thread-tree > .conversation-status { - border-top-width: 1px; - border-top-style: solid; - border-top-color: var(--border, $fallback--border); + border-top: 1px solid var(--border, $fallback--border); } /* expanded conversation in timeline */ &.status-fadein.-expanded .thread-body { - border-left-width: 4px; - border-left-style: solid; - border-left-color: $fallback--cRed; + border-left: 4px solid $fallback--cRed; border-left-color: var(--cRed, $fallback--cRed); border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); diff --git a/src/components/desktop_nav/desktop_nav.scss b/src/components/desktop_nav/desktop_nav.scss index 1ec25385..c7e02936 100644 --- a/src/components/desktop_nav/desktop_nav.scss +++ b/src/components/desktop_nav/desktop_nav.scss @@ -1,4 +1,4 @@ -@import '../../_variables.scss'; +@import "../../variables"; .DesktopNav { width: 100%; @@ -27,20 +27,13 @@ --miniColumn: 25rem; --maxiColumn: 45rem; --columnGap: 1em; - max-width: calc( - var(--sidebarColumnWidth, var(--miniColumn)) + - var(--contentColumnWidth, var(--maxiColumn)) + - var(--columnGap) - ); - } - &.-column-stretch.-wide .inner-nav { - max-width: calc( - var(--sidebarColumnWidth, var(--miniColumn)) + - var(--contentColumnWidth, var(--maxiColumn)) + - var(--notifsColumnWidth, var(--miniColumn)) + - var(--columnGap) - ); + max-width: + calc( + var(--sidebarColumnWidth, var(--miniColumn)) + + var(--contentColumnWidth, var(--maxiColumn)) + + var(--columnGap) + ); } &.-logoLeft .inner-nav { @@ -48,8 +41,19 @@ grid-template-areas: "logo sitename actions"; } + &.-column-stretch.-wide .inner-nav { + max-width: + calc( + var(--sidebarColumnWidth, var(--miniColumn)) + + var(--contentColumnWidth, var(--maxiColumn)) + + var(--notifsColumnWidth, var(--miniColumn)) + + var(--columnGap) + ); + } + .button-default { - &, svg { + &, + svg { color: $fallback--text; color: var(--btnTopBarText, $fallback--text); } @@ -70,7 +74,7 @@ color: $fallback--text; color: var(--btnToggledTopBarText, $fallback--text); background-color: $fallback--fg; - background-color: var(--btnToggledTopBar, $fallback--fg) + background-color: var(--btnToggledTopBar, $fallback--fg); } } @@ -82,6 +86,7 @@ transition-duration: 100ms; @media all and (min-width: 800px) { + /* stylelint-disable-next-line declaration-no-important */ opacity: 1 !important; } diff --git a/src/components/desktop_nav/desktop_nav.vue b/src/components/desktop_nav/desktop_nav.vue index 5db7fc79..07bf8005 100644 --- a/src/components/desktop_nav/desktop_nav.vue +++ b/src/components/desktop_nav/desktop_nav.vue @@ -38,7 +38,7 @@ /> <button class="button-unstyled nav-icon" - @click="openSettingsModal" + @click.stop="openSettingsModal" > <FAIcon fixed-width @@ -65,7 +65,7 @@ <button v-if="currentUser" class="button-unstyled nav-icon" - @click.prevent="logout" + @click.stop.prevent="logout" > <FAIcon fixed-width diff --git a/src/components/dialog_modal/dialog_modal.vue b/src/components/dialog_modal/dialog_modal.vue index 06b270c3..24d65142 100644 --- a/src/components/dialog_modal/dialog_modal.vue +++ b/src/components/dialog_modal/dialog_modal.vue @@ -25,7 +25,7 @@ <script src="./dialog_modal.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; // TODO: unify with other modals. .dark-overlay { @@ -38,7 +38,7 @@ position: fixed; right: 0; top: 0; - background: rgba(27,31,35,.5); + background: rgb(27 31 35 / 50%); z-index: 99; } } @@ -65,7 +65,7 @@ .dialog-modal-content { margin: 0; - padding: 1rem 1rem; + padding: 1rem; background-color: $fallback--bg; background-color: var(--bg, $fallback--bg); white-space: normal; @@ -73,7 +73,7 @@ .dialog-modal-footer { margin: 0; - padding: .5em .5em; + padding: 0.5em; background-color: $fallback--bg; background-color: var(--bg, $fallback--bg); border-top: 1px solid $fallback--border; @@ -83,7 +83,7 @@ button { width: auto; - margin-left: .5rem; + margin-left: 0.5rem; } } } diff --git a/src/components/edit_status_modal/edit_status_modal.vue b/src/components/edit_status_modal/edit_status_modal.vue index 1dbacaab..db62972d 100644 --- a/src/components/edit_status_modal/edit_status_modal.vue +++ b/src/components/edit_status_modal/edit_status_modal.vue @@ -26,6 +26,7 @@ .modal-view.edit-form-modal-view { align-items: flex-start; } + .edit-form-modal-panel { flex-shrink: 0; margin-top: 25%; diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue index c9bbc18f..ccba0393 100644 --- a/src/components/emoji_input/emoji_input.vue +++ b/src/components/emoji_input/emoji_input.vue @@ -91,22 +91,18 @@ <script src="./emoji_input.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .emoji-input { display: flex; flex-direction: column; position: relative; - &.with-picker input { - padding-right: 30px; - } - .emoji-picker-icon { position: absolute; top: 0; right: 0; - margin: .2em .25em; + margin: 0.2em 0.25em; font-size: 1.3em; cursor: pointer; line-height: 24px; @@ -123,14 +119,19 @@ margin-top: 2px; &.hide { - display: none + display: none; } } - input, textarea { + input, + textarea { flex: 1 0 auto; } + &.with-picker input { + padding-right: 30px; + } + .hidden-overlay { opacity: 0; pointer-events: none; @@ -140,8 +141,10 @@ right: 0; left: 0; overflow: hidden; + /* DEBUG STUFF */ color: red; + /* set opacity to non-zero to see the overlay */ .caret { @@ -151,6 +154,7 @@ } } } + .autocomplete { &-panel { position: absolute; @@ -160,7 +164,7 @@ display: flex; cursor: pointer; padding: 0.2em 0.4em; - border-bottom: 1px solid rgba(0, 0, 0, 0.4); + border-bottom: 1px solid rgb(0 0 0 / 40%); height: 32px; .image { @@ -169,7 +173,6 @@ line-height: 32px; text-align: center; font-size: 32px; - margin-right: 4px; img { @@ -199,6 +202,7 @@ 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); diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js index dd5e5217..0d7ca812 100644 --- a/src/components/emoji_picker/emoji_picker.js +++ b/src/components/emoji_picker/emoji_picker.js @@ -3,7 +3,6 @@ import Checkbox from '../checkbox/checkbox.vue' import Popover from 'src/components/popover/popover.vue' import StillImage from '../still-image/still-image.vue' import { ensureFinalFallback } from '../../i18n/languages.js' -import lozad from 'lozad' import { library } from '@fortawesome/fontawesome-svg-core' import { faBoxOpen, @@ -19,7 +18,7 @@ import { faCode, faFlag } from '@fortawesome/free-solid-svg-icons' -import { debounce, trim } from 'lodash' +import { debounce, trim, chunk } from 'lodash' library.add( faBoxOpen, @@ -82,6 +81,17 @@ const filterByKeyword = (list, keyword = '', languages, nameLocalizer) => { return orderedEmojiList.flat() } +const getOffset = (elem) => { + const style = elem.style.transform + const res = /translateY\((\d+)px\)/.exec(style) + if (!res) { return 0 } + return res[1] +} + +const toHeaderId = id => { + return id.replace(/^row-\d+-/, '') +} + const EmojiPicker = { props: { enableStickerPicker: { @@ -102,7 +112,8 @@ const EmojiPicker = { contentLoaded: false, groupRefs: {}, emojiRefs: {}, - filteredEmojiGroups: [] + filteredEmojiGroups: [], + width: 0 } }, components: { @@ -125,9 +136,6 @@ const EmojiPicker = { setGroupRef (name) { return el => { this.groupRefs[name] = el } }, - setEmojiRef (name) { - return el => { this.emojiRefs[name] = el } - }, onPopoverShown () { this.$emit('show') }, @@ -147,18 +155,21 @@ const EmojiPicker = { } this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen }) }, - onScroll (e) { - const target = (e && e.target) || this.$refs['emoji-groups'] - this.updateScrolledClass(target) - this.scrolledGroup(target) + onScroll (startIndex, endIndex, visibleStartIndex, visibleEndIndex) { + const target = this.$refs['emoji-groups'].$el + this.scrolledGroup(target, visibleStartIndex, visibleEndIndex) }, - scrolledGroup (target) { + scrolledGroup (target, start, end) { const top = target.scrollTop + 5 this.$nextTick(() => { - this.allEmojiGroups.forEach(group => { + this.emojiItems.slice(start, end + 1).forEach(group => { + const headerId = toHeaderId(group.id) const ref = this.groupRefs['group-' + group.id] - if (ref && ref.offsetTop <= top) { - this.activeGroup = group.id + if (!ref) { return } + const elem = ref.$el.parentElement + if (!elem) { return } + if (elem && getOffset(elem) <= top) { + this.activeGroup = headerId } }) this.scrollHeader() @@ -181,14 +192,10 @@ const EmojiPicker = { setScroll(right + margin - headerCont.clientWidth) } }, - highlight (key) { - const ref = this.groupRefs['group-' + key] - const top = ref.offsetTop + highlight (groupId) { this.setShowStickers(false) - this.activeGroup = key - this.$nextTick(() => { - this.$refs['emoji-groups'].scrollTop = top + 1 - }) + const indexInList = this.emojiItems.findIndex(k => k.id === groupId) + this.$refs['emoji-groups'].scrollToItem(indexInList) }, updateScrolledClass (target) { if (target.scrollTop <= 5) { @@ -208,43 +215,13 @@ const EmojiPicker = { filterByKeyword (list, keyword) { return filterByKeyword(list, keyword, this.languages, this.maybeLocalizedEmojiName) }, - initializeLazyLoad () { - this.destroyLazyLoad() - this.$nextTick(() => { - this.$lozad = lozad('.still-image.emoji-picker-emoji', { - load: el => { - const name = el.getAttribute('data-emoji-name') - const vn = this.emojiRefs[name] - if (!vn) { - return - } - - vn.loadLazy() - } - }) - this.$lozad.observe() - }) - }, - waitForDomAndInitializeLazyLoad () { - this.$nextTick(() => this.initializeLazyLoad()) - }, - destroyLazyLoad () { - if (this.$lozad) { - if (this.$lozad.observer) { - this.$lozad.observer.disconnect() - } - if (this.$lozad.mutationObserver) { - this.$lozad.mutationObserver.disconnect() - } - } - }, onShowing () { const oldContentLoaded = this.contentLoaded + this.recalculateItemPerRow() this.$nextTick(() => { this.$refs.search.focus() }) this.contentLoaded = true - this.waitForDomAndInitializeLazyLoad() this.filteredEmojiGroups = this.getFilteredEmojiGroups() if (!oldContentLoaded) { this.$nextTick(() => { @@ -261,6 +238,14 @@ const EmojiPicker = { emojis: this.filterByKeyword(group.emojis, trim(this.keyword)) })) .filter(group => group.emojis.length > 0) + }, + recalculateItemPerRow () { + this.$nextTick(() => { + if (!this.$refs['emoji-groups']) { + return + } + this.width = this.$refs['emoji-groups'].$el.clientWidth + }) } }, watch: { @@ -269,14 +254,22 @@ const EmojiPicker = { this.debouncedHandleKeywordChange() }, allCustomGroups () { - this.waitForDomAndInitializeLazyLoad() this.filteredEmojiGroups = this.getFilteredEmojiGroups() } }, - destroyed () { - this.destroyLazyLoad() - }, computed: { + minItemSize () { + return this.emojiHeight + }, + emojiHeight () { + return 32 + 4 + }, + emojiWidth () { + return 32 + 4 + }, + itemPerRow () { + return this.width ? Math.floor(this.width / this.emojiWidth - 1) : 6 + }, activeGroupView () { return this.showingStickers ? '' : this.activeGroup }, @@ -287,7 +280,11 @@ const EmojiPicker = { return 0 }, allCustomGroups () { - return this.$store.getters.groupedCustomEmojis + const emojis = this.$store.getters.groupedCustomEmojis + if (emojis.unpacked) { + emojis.unpacked.text = this.$t('emoji.unpacked') + } + return emojis }, defaultGroup () { return Object.keys(this.allCustomGroups)[0] @@ -310,10 +307,20 @@ const EmojiPicker = { }, debouncedHandleKeywordChange () { return debounce(() => { - this.waitForDomAndInitializeLazyLoad() this.filteredEmojiGroups = this.getFilteredEmojiGroups() }, 500) }, + emojiItems () { + return this.filteredEmojiGroups.map(group => + chunk(group.emojis, this.itemPerRow) + .map((items, index) => ({ + ...group, + id: index === 0 ? group.id : `row-${index}-${group.id}`, + emojis: items, + isFirstRow: index === 0 + }))) + .reduce((a, c) => a.concat(c), []) + }, languages () { return ensureFinalFallback(this.$store.getters.mergedConfig.interfaceLanguage) }, diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss index 53363ec1..5bcff33b 100644 --- a/src/components/emoji_picker/emoji_picker.scss +++ b/src/components/emoji_picker/emoji_picker.scss @@ -1,4 +1,4 @@ -@import '../../_variables.scss'; +@import "../../variables"; $emoji-picker-header-height: 36px; $emoji-picker-header-picture-width: 32px; @@ -7,14 +7,14 @@ $emoji-picker-emoji-size: 32px; .emoji-picker { width: 25em; - max-width: 100vw; + max-width: calc(100vw - 20px); // popover gives 10px margin from window edge display: flex; flex-direction: column; background-color: $fallback--bg; background-color: var(--popover, $fallback--bg); color: $fallback--link; color: var(--popoverText, $fallback--link); - --lightText: var(--popoverLightText, $fallback--faint); + --faint: var(--popoverFaintText, $fallback--faint); --faintLink: var(--popoverFaintLink, $fallback--faint); --lightText: var(--popoverLightText, $fallback--lightText); @@ -28,6 +28,7 @@ $emoji-picker-emoji-size: 32px; max-width: $emoji-picker-header-picture-width; height: $emoji-picker-header-picture-height; max-height: $emoji-picker-header-picture-height; + .still-image { max-width: 100%; max-height: 100%; @@ -62,24 +63,18 @@ $emoji-picker-emoji-size: 32px; display: flex; flex-direction: column; flex: 1 1 auto; - min-height: 0px; + min-height: 0; } .emoji-tabs { flex-grow: 1; display: flex; - flex-direction: row; - flex-wrap: nowrap; + flex-flow: row nowrap; overflow-x: auto; } - .emoji-groups { - min-height: 200px; - } - .additional-tabs { display: flex; - flex: 1; border-left: 1px solid; border-left-color: $fallback--icon; border-left-color: var(--icon, $fallback--icon); @@ -121,7 +116,7 @@ $emoji-picker-emoji-size: 32px; } .sticker-picker { - flex: 1 1 auto + flex: 1 1 auto; } .stickers, @@ -151,22 +146,27 @@ $emoji-picker-emoji-size: 32px; } &-groups { + height: 100%; + min-height: 200px; flex: 1 1 1px; position: relative; overflow: auto; user-select: none; - mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat, - linear-gradient(to bottom, white 0, transparent 100%) top no-repeat, - linear-gradient(to top, white, white); + mask: + linear-gradient(to top, white 0, transparent 100%) bottom no-repeat, + linear-gradient(to bottom, white 0, transparent 100%) top no-repeat, + linear-gradient(to top, white, white); transition: mask-size 150ms; mask-size: 100% 20px, 100% 20px, auto; // Autoprefixed seem to ignore this one, and also syntax is different - -webkit-mask-composite: xor; + mask-composite: xor; mask-composite: exclude; + &.scrolled { &-top { mask-size: 100% 20px, 100% 0, auto; } + &-bottom { mask-size: 100% 0, 100% 20px, auto; } @@ -200,7 +200,6 @@ $emoji-picker-emoji-size: 32px; align-items: center; justify-content: center; margin: 4px; - cursor: pointer; .emoji-picker-emoji.-custom { @@ -208,12 +207,11 @@ $emoji-picker-emoji-size: 32px; max-width: 100%; max-height: 100%; } + .emoji-picker-emoji.-unicode { font-size: 24px; overflow: hidden; } } - } - } diff --git a/src/components/emoji_picker/emoji_picker.vue b/src/components/emoji_picker/emoji_picker.vue index ff56d637..ca90bf26 100644 --- a/src/components/emoji_picker/emoji_picker.vue +++ b/src/components/emoji_picker/emoji_picker.vue @@ -74,45 +74,57 @@ @input="$event.target.composing = false" > </div> - <div + <DynamicScroller ref="emoji-groups" class="emoji-groups" :class="groupsScrolledClass" - @scroll="onScroll" + :min-item-size="minItemSize" + :items="emojiItems" + :emit-update="true" + @update="onScroll" + @visible="recalculateItemPerRow" + @resize="recalculateItemPerRow" > - <div - v-for="group in filteredEmojiGroups" - :key="group.id" - class="emoji-group" - > - <h6 + <template #default="{ item: group, index, active }"> + <DynamicScrollerItem :ref="setGroupRef('group-' + group.id)" - class="emoji-group-title" - > - {{ group.text }} - </h6> - <span - v-for="emoji in group.emojis" - :key="group.id + emoji.displayText" - :title="maybeLocalizedEmojiName(emoji)" - class="emoji-item" - @click.stop.prevent="onEmoji(emoji)" + :item="group" + :active="active" + :data-index="index" + :size-dependencies="[group.emojis.length]" > - <span - v-if="!emoji.imageUrl" - class="emoji-picker-emoji -unicode" - >{{ emoji.replacement }}</span> - <still-image - v-else - :ref="setEmojiRef(group.id + emoji.displayText)" - class="emoji-picker-emoji -custom" - :data-src="emoji.imageUrl" - :data-emoji-name="group.id + emoji.displayText" - /> - </span> - <span :ref="setGroupRef('group-end-' + group.id)" /> - </div> - </div> + <div + class="emoji-group" + > + <h6 + v-if="group.isFirstRow" + class="emoji-group-title" + > + {{ group.text }} + </h6> + <span + v-for="emoji in group.emojis" + :key="group.id + emoji.displayText" + :title="maybeLocalizedEmojiName(emoji)" + class="emoji-item" + @click.stop.prevent="onEmoji(emoji)" + > + <span + v-if="!emoji.imageUrl" + class="emoji-picker-emoji -unicode" + >{{ emoji.replacement }}</span> + <still-image + v-else + class="emoji-picker-emoji -custom" + loading="lazy" + :src="emoji.imageUrl" + :data-emoji-name="group.id + emoji.displayText" + /> + </span> + </div> + </DynamicScrollerItem> + </template> + </DynamicScroller> <div class="keep-open"> <Checkbox v-model="keepOpen"> {{ $t('emoji.keep_open') }} diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index 4eb22a65..a63daa97 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -28,7 +28,7 @@ <script src="./emoji_reactions.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .EmojiReactions { display: flex; @@ -55,6 +55,7 @@ &.not-clickable { cursor: default; + &:hover { box-shadow: $fallback--buttonShadow; box-shadow: var(--buttonShadow); @@ -75,10 +76,10 @@ display: flex; align-items: center; justify-content: center; + &:hover { text-decoration: underline; } } - } </style> diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue index b2fad1c9..a84d47f6 100644 --- a/src/components/extra_buttons/extra_buttons.vue +++ b/src/components/extra_buttons/extra_buttons.vue @@ -172,15 +172,10 @@ <script src="./extra_buttons.js"></script> <style lang="scss"> -@import '../../_variables.scss'; -@import '../../_mixins.scss'; +@import "../../variables"; +@import "../../mixins"; .ExtraButtons { - /* override of popover internal stuff */ - .popover-trigger-button { - width: auto; - } - .popover-trigger { position: static; padding: 10px; @@ -190,10 +185,12 @@ color: $fallback--text; color: var(--text, $fallback--text); } - } .popover-trigger-button { + /* override of popover internal stuff */ + width: auto; + @include unfocused-style { .focus-marker { visibility: hidden; diff --git a/src/components/favorite_button/favorite_button.vue b/src/components/favorite_button/favorite_button.vue index ea01720a..58d14945 100644 --- a/src/components/favorite_button/favorite_button.vue +++ b/src/components/favorite_button/favorite_button.vue @@ -58,8 +58,8 @@ <script src="./favorite_button.js"></script> <style lang="scss"> -@import '../../_variables.scss'; -@import '../../_mixins.scss'; +@import "../../variables"; +@import "../../mixins"; .FavoriteButton { display: flex; diff --git a/src/components/flash/flash.vue b/src/components/flash/flash.vue index 95f71950..9f58d314 100644 --- a/src/components/flash/flash.vue +++ b/src/components/flash/flash.vue @@ -42,7 +42,8 @@ <script src="./flash.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; + .Flash { display: inline-block; width: 100%; @@ -78,7 +79,7 @@ .hidden { display: none; - visibility: 'hidden'; + visibility: "hidden"; } } </style> diff --git a/src/components/follow_card/follow_card.vue b/src/components/follow_card/follow_card.vue index c919b11a..eff69fb2 100644 --- a/src/components/follow_card/follow_card.vue +++ b/src/components/follow_card/follow_card.vue @@ -39,9 +39,8 @@ &-content-container { flex-shrink: 0; display: flex; - flex-direction: row; + flex-flow: row wrap; justify-content: space-between; - flex-wrap: wrap; line-height: 1.5em; } diff --git a/src/components/follow_request_card/follow_request_card.vue b/src/components/follow_request_card/follow_request_card.vue index 1b12ba4b..eb222cc7 100644 --- a/src/components/follow_request_card/follow_request_card.vue +++ b/src/components/follow_request_card/follow_request_card.vue @@ -22,8 +22,8 @@ <style lang="scss"> .follow-request-card-content-container { display: flex; - flex-direction: row; - flex-wrap: wrap; + flex-flow: row wrap; + button { margin-top: 0.5em; margin-right: 0.5em; diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue index 83c1cef7..bb7e64bc 100644 --- a/src/components/font_control/font_control.vue +++ b/src/components/font_control/font_control.vue @@ -50,17 +50,20 @@ <script src="./font_control.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; + .font-control { input.custom-font { min-width: 10em; } + &.custom { /* TODO Should make proper joiners... */ .font-switcher { border-top-right-radius: 0; border-bottom-right-radius: 0; } + .custom-font { border-top-left-radius: 0; border-bottom-left-radius: 0; diff --git a/src/components/gallery/gallery.js b/src/components/gallery/gallery.js index 4e1bda55..e86a3eea 100644 --- a/src/components/gallery/gallery.js +++ b/src/components/gallery/gallery.js @@ -4,6 +4,7 @@ import { sumBy, set } from 'lodash' const Gallery = { props: [ 'attachments', + 'compact', 'limitRows', 'descriptions', 'limit', diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue index ccf6e3e2..96b554e3 100644 --- a/src/components/gallery/gallery.vue +++ b/src/components/gallery/gallery.vue @@ -20,6 +20,7 @@ v-for="(attachment, attachmentIndex) in row.items" :key="attachment.id" class="gallery-item" + :compact="compact" :nsfw="nsfw" :attachment="attachment" :size="size" @@ -86,7 +87,7 @@ <script src='./gallery.js'></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .Gallery { .gallery-rows { @@ -100,6 +101,53 @@ width: 100%; flex-grow: 1; + .gallery-row-inner { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + flex-flow: row wrap; + align-content: stretch; + + .gallery-item { + margin: 0 0.5em 0 0; + flex-grow: 1; + height: 100%; + box-sizing: border-box; + // to make failed images a bit more noticeable on chromium + min-width: 2em; + + &:last-child { + margin: 0; + } + } + + &.-grid { + width: 100%; + height: auto; + position: relative; + display: grid; + grid-gap: 0.5em; + grid-template-columns: repeat(auto-fill, minmax(15em, 1fr)); + + .gallery-item { + margin: 0; + height: 200px; + } + } + } + + &.-grid, + &.-minimal { + height: auto; + + .gallery-row-inner { + position: relative; + } + } + &:not(:first-child) { margin-top: 0.5em; } @@ -114,7 +162,7 @@ linear-gradient(to top, white, white); /* Autoprefixed seem to ignore this one, and also syntax is different */ - -webkit-mask-composite: xor; + mask-composite: xor; mask-composite: exclude; } } @@ -138,54 +186,5 @@ padding: 0 2em; } } - - .gallery-row { - &.-grid, - &.-minimal { - height: auto; - .gallery-row-inner { - position: relative; - } - } - } - - .gallery-row-inner { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-content: stretch; - - &.-grid { - width: 100%; - height: auto; - position: relative; - display: grid; - grid-column-gap: 0.5em; - grid-row-gap: 0.5em; - grid-template-columns: repeat(auto-fill, minmax(15em, 1fr)); - - .gallery-item { - margin: 0; - height: 200px; - } - } - } - - .gallery-item { - margin: 0 0.5em 0 0; - flex-grow: 1; - height: 100%; - box-sizing: border-box; - // to make failed images a bit more noticeable on chromium - min-width: 2em; - &:last-child { - margin: 0; - } - } } </style> diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue index d828b819..0e58476f 100644 --- a/src/components/global_notice_list/global_notice_list.vue +++ b/src/components/global_notice_list/global_notice_list.vue @@ -25,7 +25,7 @@ <script src="./global_notice_list.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .global-notice-list { position: fixed; @@ -73,6 +73,7 @@ .global-success { background-color: var(--alertPopupSuccess, $fallback--cGreen); color: var(--alertPopupSuccessText, $fallback--text); + .svg-inline--fa { color: var(--alertPopupSuccessText, $fallback--text); } @@ -81,6 +82,7 @@ .global-info { background-color: var(--alertPopupNeutral, $fallback--fg); color: var(--alertPopupNeutralText, $fallback--text); + .svg-inline--fa { color: var(--alertPopupNeutralText, $fallback--text); } @@ -88,6 +90,7 @@ .close-notice { padding-right: 0.2em; + .svg-inline--fa:hover { opacity: 0.6; } diff --git a/src/components/link-preview/link-preview.vue b/src/components/link-preview/link-preview.vue index 220527f2..09f341ac 100644 --- a/src/components/link-preview/link-preview.vue +++ b/src/components/link-preview/link-preview.vue @@ -33,7 +33,7 @@ <script src="./link-preview.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .link-preview-card { display: flex; @@ -46,6 +46,7 @@ flex-shrink: 0; width: 120px; max-width: 25%; + img { width: 100%; height: 100%; @@ -67,7 +68,7 @@ } .card-description { - margin: 0.5em 0 0 0; + margin: 0.5em 0 0; overflow: hidden; text-overflow: ellipsis; word-break: break-word; diff --git a/src/components/list/list.vue b/src/components/list/list.vue index a6223cce..f17766b4 100644 --- a/src/components/list/list.vue +++ b/src/components/list/list.vue @@ -35,7 +35,7 @@ export default { </script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .list { &-item:not(:last-child) { diff --git a/src/components/lists_card/lists_card.vue b/src/components/lists_card/lists_card.vue index 13866d8c..925da3a5 100644 --- a/src/components/lists_card/lists_card.vue +++ b/src/components/lists_card/lists_card.vue @@ -21,12 +21,16 @@ <script src="./lists_card.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .list-card { display: flex; } +.list-name { + flex-grow: 1; +} + .list-name, .button-list-edit { margin: 0; @@ -39,13 +43,10 @@ background-color: var(--selectedMenu, $fallback--lightBg); color: $fallback--link; color: var(--selectedMenuText, $fallback--link); + --faint: var(--selectedMenuFaintText, $fallback--faint); --faintLink: var(--selectedMenuFaintLink, $fallback--faint); --lightText: var(--selectedMenuLightText, $fallback--lightText); } } - -.list-name { - flex-grow: 1; -} </style> diff --git a/src/components/lists_edit/lists_edit.js b/src/components/lists_edit/lists_edit.js index c22d1323..c33659df 100644 --- a/src/components/lists_edit/lists_edit.js +++ b/src/components/lists_edit/lists_edit.js @@ -95,10 +95,10 @@ const ListsNew = { return this.addedUserIds.has(user.id) }, addUser (user) { - this.$store.dispatch('addListAccount', { accountId: this.user.id, listId: this.id }) + this.$store.dispatch('addListAccount', { accountId: user.id, listId: this.id }) }, removeUser (userId) { - this.$store.dispatch('removeListAccount', { accountId: this.user.id, listId: this.id }) + this.$store.dispatch('removeListAccount', { accountId: userId, listId: this.id }) }, onSearchLoading (results) { this.searchLoading = true diff --git a/src/components/lists_edit/lists_edit.vue b/src/components/lists_edit/lists_edit.vue index 6521aba6..eec0f978 100644 --- a/src/components/lists_edit/lists_edit.vue +++ b/src/components/lists_edit/lists_edit.vue @@ -164,7 +164,7 @@ <script src="./lists_edit.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .ListEdit { --panel-body-padding: 0.5em; diff --git a/src/components/lists_user_search/lists_user_search.vue b/src/components/lists_user_search/lists_user_search.vue index 8633170c..6ca107e6 100644 --- a/src/components/lists_user_search/lists_user_search.vue +++ b/src/components/lists_user_search/lists_user_search.vue @@ -27,12 +27,12 @@ <script src="./lists_user_search.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .ListsUserSearch { .input-wrap { display: flex; - margin: 0.7em 0.5em 0.7em 0.5em; + margin: 0.7em 0.5em; input { width: 100%; diff --git a/src/components/login_form/login_form.vue b/src/components/login_form/login_form.vue index 7a430c51..829e88ad 100644 --- a/src/components/login_form/login_form.vue +++ b/src/components/login_form/login_form.vue @@ -93,7 +93,7 @@ <script src="./login_form.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .login-form { display: flex; @@ -110,7 +110,7 @@ } .login-bottom { - margin-top: 1.0em; + margin-top: 1em; display: flex; flex-direction: row; align-items: center; @@ -121,7 +121,7 @@ display: flex; flex-direction: column; padding: 0.3em 0.5em 0.6em; - line-height:24px; + line-height: 24px; } .form-bottom { @@ -142,7 +142,6 @@ .error { text-align: center; - animation-name: shakeError; animation-duration: 0.4s; animation-timing-function: ease-in-out; diff --git a/src/components/media_modal/media_modal.js b/src/components/media_modal/media_modal.js index ff993664..05ef9fbe 100644 --- a/src/components/media_modal/media_modal.js +++ b/src/components/media_modal/media_modal.js @@ -63,6 +63,11 @@ const MediaModal = { }, type () { return this.currentMedia ? this.getType(this.currentMedia) : null + }, + swipeDisableClickThreshold () { + // If there is only one media, allow more mouse movements to close the modal + // because there is less chance that the user wants to switch to another image + return () => this.canNavigate ? 1 : 30 } }, methods: { diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue index d59055b3..eb28f8be 100644 --- a/src/components/media_modal/media_modal.vue +++ b/src/components/media_modal/media_modal.vue @@ -10,6 +10,7 @@ class="modal-image-container" :direction="swipeDirection" :threshold="swipeThreshold" + :disable-click-threshold="swipeDisableClickThreshold" @preview-requested="handleSwipePreview" @swipe-finished="handleSwipeEnd" @swipeless-clicked="hide" @@ -120,32 +121,12 @@ $modal-view-button-icon-half-height: calc(#{$modal-view-button-icon-height} / 2) $modal-view-button-icon-width: 3em; $modal-view-button-icon-margin: 0.5em; -.modal-view.media-modal-view { - z-index: var(--ZI_media_modal); - flex-direction: column; - - .modal-view-button-arrow, - .modal-view-button-hide { - opacity: 0.75; - - &:focus, - &:hover { - outline: none; - box-shadow: none; - } - - &:hover { - opacity: 1; - } - } - overflow: hidden; -} - .media-modal-view { @keyframes media-fadein { from { opacity: 0; } + to { opacity: 1; } @@ -226,7 +207,7 @@ $modal-view-button-icon-margin: 0.5em; appearance: none; overflow: visible; cursor: pointer; - transition: opacity 333ms cubic-bezier(.4,0,.22,1); + transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); height: $modal-view-button-icon-height; width: $modal-view-button-icon-width; @@ -236,9 +217,9 @@ $modal-view-button-icon-margin: 0.5em; width: $modal-view-button-icon-width; font-size: 1rem; line-height: $modal-view-button-icon-height; - color: #FFF; + color: #fff; text-align: center; - background-color: rgba(0,0,0,.3); + background-color: rgb(0 0 0 / 30%); } } @@ -254,13 +235,14 @@ $modal-view-button-icon-margin: 0.5em; position: absolute; top: 0; line-height: $modal-view-button-icon-height; - color: #FFF; + color: #fff; text-align: center; - background-color: rgba(0,0,0,.3); + background-color: rgb(0 0 0 / 30%); } &--prev { left: 0; + .arrow-icon { left: $modal-view-button-icon-margin; } @@ -268,6 +250,7 @@ $modal-view-button-icon-margin: 0.5em; &--next { right: 0; + .arrow-icon { right: $modal-view-button-icon-margin; } @@ -278,10 +261,33 @@ $modal-view-button-icon-margin: 0.5em; position: absolute; top: 0; right: 0; + .button-icon { top: $modal-view-button-icon-margin; right: $modal-view-button-icon-margin; } } } + +.modal-view.media-modal-view { + z-index: var(--ZI_media_modal); + flex-direction: column; + + .modal-view-button-arrow, + .modal-view-button-hide { + opacity: 0.75; + + &:focus, + &:hover { + outline: none; + box-shadow: none; + } + + &:hover { + opacity: 1; + } + } + + overflow: hidden; +} </style> diff --git a/src/components/media_upload/media_upload.vue b/src/components/media_upload/media_upload.vue index a538a5ed..2799495b 100644 --- a/src/components/media_upload/media_upload.vue +++ b/src/components/media_upload/media_upload.vue @@ -29,7 +29,7 @@ <script src="./media_upload.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .media-upload { cursor: pointer; // We use <label> for interactivity... i wonder if it's fine diff --git a/src/components/mention_link/mention_link.scss b/src/components/mention_link/mention_link.scss index 8b2af926..69e9fed1 100644 --- a/src/components/mention_link/mention_link.scss +++ b/src/components/mention_link/mention_link.scss @@ -1,4 +1,4 @@ -@import '../../_variables.scss'; +@import "../../variables"; .MentionLink { position: relative; @@ -59,6 +59,7 @@ font-weight: 600; } } + &.-has-selection { color: var(--alertNeutralText, $fallback--text); background-color: var(--alertNeutral, $fallback--fg); @@ -100,10 +101,6 @@ } } - .full { - pointer-events: none; - } - .serverName.-faded { color: var(--faintLink, $fallback--link); } diff --git a/src/components/mentions_line/mentions_line.scss b/src/components/mentions_line/mentions_line.scss index 9a622e75..b7dfbfb8 100644 --- a/src/components/mentions_line/mentions_line.scss +++ b/src/components/mentions_line/mentions_line.scss @@ -2,7 +2,7 @@ word-break: break-all; .mention-link:not(:first-child)::before { - content: ' '; + content: " "; } .showMoreLess { diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue index 0f1fe621..d6fe102c 100644 --- a/src/components/mobile_nav/mobile_nav.vue +++ b/src/components/mobile_nav/mobile_nav.vue @@ -94,7 +94,7 @@ <script src="./mobile_nav.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .MobileNav { z-index: var(--ZI_navbar); @@ -127,7 +127,7 @@ } .site-name { - padding: 0 .3em; + padding: 0 0.3em; display: inline-block; } @@ -156,7 +156,7 @@ position: fixed; top: 0; left: 0; - box-shadow: 1px 1px 4px rgba(0,0,0,.6); + box-shadow: 1px 1px 4px rgb(0 0 0 / 60%); box-shadow: var(--panelShadow); transition-property: transform; transition-duration: 0.25s; @@ -182,7 +182,7 @@ color: var(--topBarText); background-color: $fallback--fg; background-color: var(--topBar, $fallback--fg); - box-shadow: 0px 0px 4px rgba(0,0,0,.6); + box-shadow: 0 0 4px rgb(0 0 0 / 60%); box-shadow: var(--topBarShadow); .spacer { diff --git a/src/components/mobile_post_status_button/mobile_post_status_button.vue b/src/components/mobile_post_status_button/mobile_post_status_button.vue index 28a2c440..ef0f51fe 100644 --- a/src/components/mobile_post_status_button/mobile_post_status_button.vue +++ b/src/components/mobile_post_status_button/mobile_post_status_button.vue @@ -13,7 +13,7 @@ <script src="./mobile_post_status_button.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .MobilePostButton { &.button-default { @@ -30,9 +30,8 @@ display: flex; justify-content: center; align-items: center; - box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3), 0px 4px 6px rgba(0, 0, 0, 0.3); + box-shadow: 0 2px 2px rgb(0 0 0 / 30%), 0 4px 6px rgb(0 0 0 / 30%); z-index: 10; - transition: 0.35s transform; transition-timing-function: cubic-bezier(0, 1, 0.5, 1); } diff --git a/src/components/modal/modal.vue b/src/components/modal/modal.vue index 2187f392..9f767ede 100644 --- a/src/components/modal/modal.vue +++ b/src/components/modal/modal.vue @@ -59,7 +59,7 @@ export default { &.modal-background { pointer-events: initial; - background-color: rgba(0, 0, 0, 0.5); + background-color: rgb(0 0 0 / 50%); } &.open { @@ -69,10 +69,11 @@ export default { @keyframes modal-background-fadein { from { - background-color: rgba(0, 0, 0, 0); + background-color: rgb(0 0 0 / 0%); } + to { - background-color: rgba(0, 0, 0, 0.5); + background-color: rgb(0 0 0 / 50%); } } </style> diff --git a/src/components/moderation_tools/moderation_tools.vue b/src/components/moderation_tools/moderation_tools.vue index 8535ef27..b708cdc8 100644 --- a/src/components/moderation_tools/moderation_tools.vue +++ b/src/components/moderation_tools/moderation_tools.vue @@ -166,18 +166,21 @@ <script src="./moderation_tools.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .moderation-tools-popover { height: 100%; + .trigger { + /* stylelint-disable-next-line declaration-no-important */ display: flex !important; height: 100%; } } .moderation-tools-button { - svg,i { + svg, + i { font-size: 0.8em; } } diff --git a/src/components/mrf_transparency_panel/mrf_transparency_panel.scss b/src/components/mrf_transparency_panel/mrf_transparency_panel.scss index 80ea01d4..a262ed1c 100644 --- a/src/components/mrf_transparency_panel/mrf_transparency_panel.scss +++ b/src/components/mrf_transparency_panel/mrf_transparency_panel.scss @@ -2,19 +2,21 @@ margin: 1em; table { - width:100%; + width: 100%; text-align: left; - padding-left:10px; - padding-bottom:20px; + padding-left: 10px; + padding-bottom: 20px; - th, td { + th, + td { width: 180px; max-width: 360px; - overflow: hidden; + overflow: hidden; vertical-align: text-top; } - th+th, td+td { + th + th, + td + td { width: auto; } } diff --git a/src/components/mrf_transparency_panel/mrf_transparency_panel.vue b/src/components/mrf_transparency_panel/mrf_transparency_panel.vue index 1787fa07..97af4787 100644 --- a/src/components/mrf_transparency_panel/mrf_transparency_panel.vue +++ b/src/components/mrf_transparency_panel/mrf_transparency_panel.vue @@ -227,6 +227,6 @@ <script src="./mrf_transparency_panel.js"></script> <style lang="scss"> -@import '../../_variables.scss'; -@import './mrf_transparency_panel.scss'; +@import "../../variables"; +@import "./mrf_transparency_panel"; </style> diff --git a/src/components/mute_card/mute_card.vue b/src/components/mute_card/mute_card.vue index ca33c6c5..97e91cbc 100644 --- a/src/components/mute_card/mute_card.vue +++ b/src/components/mute_card/mute_card.vue @@ -37,6 +37,7 @@ .mute-card-content-container { margin-top: 0.5em; text-align: right; + button { width: 10em; } diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index d628c380..1a826cc4 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -102,7 +102,7 @@ <script src="./nav_panel.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .NavPanel { .panel { @@ -169,8 +169,9 @@ } .nav-panel-heading { - // breaks without a unit - --panel-heading-height-padding: 0em; + // breaks without a unit + // stylelint-disable-next-line length-zero-no-unit + --panel-heading-height-padding: 0px; } } </style> diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue index f4d53836..411ca536 100644 --- a/src/components/navigation/navigation_entry.vue +++ b/src/components/navigation/navigation_entry.vue @@ -63,7 +63,7 @@ <script src="./navigation_entry.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .NavigationEntry { display: flex; @@ -102,6 +102,7 @@ background-color: var(--selectedMenu, $fallback--lightBg); color: $fallback--link; color: var(--selectedMenuText, $fallback--link); + --faint: var(--selectedMenuFaintText, $fallback--faint); --faintLink: var(--selectedMenuFaintLink, $fallback--faint); --lightText: var(--selectedMenuLightText, $fallback--lightText); @@ -117,6 +118,7 @@ background-color: var(--selectedMenu, $fallback--lightBg); color: $fallback--text; color: var(--selectedMenuText, $fallback--text); + --faint: var(--selectedMenuFaintText, $fallback--faint); --faintLink: var(--selectedMenuFaintLink, $fallback--faint); --lightText: var(--selectedMenuLightText, $fallback--lightText); diff --git a/src/components/navigation/navigation_pins.vue b/src/components/navigation/navigation_pins.vue index 6a9ed6f5..4fbb4f95 100644 --- a/src/components/navigation/navigation_pins.vue +++ b/src/components/navigation/navigation_pins.vue @@ -27,7 +27,8 @@ <script src="./navigation_pins.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; + .NavigationPins { display: flex; flex-wrap: wrap; diff --git a/src/components/notification/notification.scss b/src/components/notification/notification.scss index 38978137..654aca3c 100644 --- a/src/components/notification/notification.scss +++ b/src/components/notification/notification.scss @@ -1,13 +1,14 @@ -@import '../../_variables.scss'; +@import "../../variables"; // TODO Copypaste from Status, should unify it somehow .Notification { - border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - word-wrap: break-word; - word-break: break-word; - --emoji-size: 14px; + border-bottom: 1px solid; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + word-wrap: break-word; + word-break: break-word; + + --emoji-size: 14px; &:hover { --_still-image-img-visibility: visible; @@ -54,7 +55,7 @@ margin-left: 0.2em; &::before { - content: ' '; + content: " "; } } diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 9b241565..41cfcef0 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -1,4 +1,4 @@ -@import '../../_variables.scss'; +@import "../../variables"; .Notifications { &:not(.minimal) { @@ -25,12 +25,13 @@ &.unseen { .notification-overlay { - background-image: linear-gradient(135deg, var(--badgeNotification, $fallback--cRed) 4px, transparent 10px) + background-image: linear-gradient(135deg, var(--badgeNotification, $fallback--cRed) 4px, transparent 10px); } } } } +/* stylelint-disable-next-line no-descending-specificity */ .notification { box-sizing: border-box; @@ -38,6 +39,7 @@ canvas { display: none; } + img { visibility: visible; } @@ -79,7 +81,8 @@ } } - .follow-text, .move-text { + .follow-text, + .move-text { padding: 0.5em 0; overflow-wrap: break-word; display: flex; diff --git a/src/components/panel_loading/panel_loading.vue b/src/components/panel_loading/panel_loading.vue index d916d8a6..17458e49 100644 --- a/src/components/panel_loading/panel_loading.vue +++ b/src/components/panel_loading/panel_loading.vue @@ -23,7 +23,7 @@ export default {} </script> <style lang="scss"> -@import 'src/_variables.scss'; +@import "src/variables"; .panel-loading { display: flex; @@ -33,6 +33,7 @@ export default {} font-size: 2em; color: $fallback--text; color: var(--text, $fallback--text); + .loading-text svg { line-height: 0; vertical-align: middle; diff --git a/src/components/password_reset/password_reset.vue b/src/components/password_reset/password_reset.vue index 90673f44..d6e10250 100644 --- a/src/components/password_reset/password_reset.vue +++ b/src/components/password_reset/password_reset.vue @@ -77,7 +77,7 @@ <script src="./password_reset.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .password-reset-form { display: flex; diff --git a/src/components/poll/poll.vue b/src/components/poll/poll.vue index f6b12a54..cacc3298 100644 --- a/src/components/poll/poll.vue +++ b/src/components/poll/poll.vue @@ -90,7 +90,7 @@ <script src="./poll.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .poll { .votes { @@ -98,9 +98,11 @@ flex-direction: column; margin: 0 0 0.5em; } + .poll-option { margin: 0.75em 0.5em; } + .option-result { height: 100%; display: flex; @@ -109,6 +111,7 @@ color: $fallback--lightText; color: var(--lightText, $fallback--lightText); } + .option-result-label { display: flex; align-items: center; @@ -116,10 +119,12 @@ z-index: 1; word-break: break-word; } + .result-percentage { width: 3.5em; flex-shrink: 0; } + .result-fill { height: 100%; position: absolute; @@ -133,20 +138,25 @@ left: 0; transition: width 0.5s; } + .option-vote { display: flex; align-items: center; } + input { width: 3.5em; } + .footer { display: flex; align-items: center; } + &.loading * { cursor: progress; } + .poll-vote-button { padding: 0 0.5em; margin-right: 0.5em; diff --git a/src/components/poll/poll_form.vue b/src/components/poll/poll_form.vue index 146754db..09d411ca 100644 --- a/src/components/poll/poll_form.vue +++ b/src/components/poll/poll_form.vue @@ -95,7 +95,7 @@ <script src="./poll_form.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .poll-form { display: flex; @@ -117,6 +117,7 @@ .input-container { width: 100%; + input { // Hack: dodge the floating X icon padding-right: 2.5em; diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue index 2869d736..fd0fd821 100644 --- a/src/components/popover/popover.vue +++ b/src/components/popover/popover.vue @@ -41,7 +41,7 @@ <script src="./popover.js" /> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .popover-trigger-button { display: inline-block; @@ -52,31 +52,31 @@ position: fixed; min-width: 0; max-width: calc(100vw - 20px); - box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5); + box-shadow: 2px 2px 3px rgb(0 0 0 / 50%); box-shadow: var(--popupShadow); } .popover-default { - &:after { - content: ''; + &::after { + content: ""; position: absolute; top: 0; bottom: 0; left: 0; right: 0; z-index: 3; - box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); + box-shadow: 1px 1px 4px rgb(0 0 0 / 60%); box-shadow: var(--panelShadow); pointer-events: none; } border-radius: $fallback--btnRadius; border-radius: var(--btnRadius, $fallback--btnRadius); - background-color: $fallback--bg; background-color: var(--popover, $fallback--bg); color: $fallback--text; color: var(--popoverText, $fallback--text); + --faint: var(--popoverFaintText, $fallback--faint); --faintLink: var(--popoverFaintLink, $fallback--faint); --lightText: var(--popoverLightText, $fallback--lightText); @@ -87,7 +87,7 @@ .dropdown-menu { display: block; - padding: .5rem 0; + padding: 0.5rem 0; font-size: 1em; text-align: left; list-style: none; @@ -97,7 +97,7 @@ .dropdown-divider { height: 0; - margin: .5rem 0; + margin: 0.5rem 0; overflow: hidden; border-top: 1px solid $fallback--border; border-top: 1px solid var(--border, $fallback--border); @@ -113,7 +113,7 @@ text-align: inherit; white-space: nowrap; border: none; - border-radius: 0px; + border-radius: 0; background-color: transparent; box-shadow: none; width: 100%; @@ -126,7 +126,7 @@ svg { width: 22px; margin-right: 0.75rem; - color: var(--menuPopoverIcon, $fallback--icon) + color: var(--menuPopoverIcon, $fallback--icon); } } @@ -137,17 +137,21 @@ } } - &:active, &:hover { + &:active, + &:hover { background-color: $fallback--lightBg; background-color: var(--selectedMenuPopover, $fallback--lightBg); box-shadow: none; + --btnText: var(--selectedMenuPopoverText, $fallback--link); --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); --icon: var(--selectedMenuPopoverIcon, $fallback--icon); + svg { color: var(--selectedMenuPopoverIcon, $fallback--icon); + --icon: var(--selectedMenuPopoverIcon, $fallback--icon); } } @@ -161,16 +165,16 @@ max-height: 22px; line-height: 22px; text-align: center; - border-radius: 0px; + border-radius: 0; background-color: $fallback--fg; background-color: var(--input, $fallback--fg); - box-shadow: 0px 0px 2px black inset; + box-shadow: 0 0 2px black inset; box-shadow: var(--inputShadow); margin-right: 0.75em; &.menu-checkbox-checked::after { font-size: 1.25em; - content: '✓'; + content: "✓"; } &.-radio { @@ -178,16 +182,15 @@ &.menu-checkbox-checked::after { font-size: 2em; - content: '•'; + content: "•"; } } } - } .button-default.dropdown-item { &, - i[class*=icon-] { + i[class*="icon-"] { color: $fallback--text; color: var(--btnText, $fallback--text); } diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index f65058f4..c51639db 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -331,7 +331,7 @@ <script src="./post_status_form.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .post-status-form { position: relative; @@ -378,7 +378,9 @@ &:hover { text-decoration: underline; } - svg, i { + + svg, + i { margin-left: 0.2em; font-size: 0.8em; transform: rotate(90deg); @@ -428,7 +430,25 @@ } } - .media-upload-icon, .poll-icon, .emoji-icon { + // Order is not necessary but a good indicator + .media-upload-icon { + order: 1; + justify-content: left; + } + + .emoji-icon { + order: 2; + justify-content: center; + } + + .poll-icon { + order: 3; + justify-content: right; + } + + .media-upload-icon, + .poll-icon, + .emoji-icon { font-size: 1.85em; line-height: 1.1; flex: 1; @@ -436,16 +456,20 @@ display: flex; align-items: center; - &.selected, &:hover { + &.selected, + &:hover { // needs to be specific to override icon default color - svg, i, label { + svg, + i, + label { color: $fallback--lightText; color: var(--lightText, $fallback--lightText); } } &.disabled { - svg, i { + svg, + i { cursor: not-allowed; color: $fallback--icon; color: var(--btnDisabledText, $fallback--icon); @@ -458,32 +482,17 @@ } } - // Order is not necessary but a good indicator - .media-upload-icon { - order: 1; - justify-content: left; - } - - .emoji-icon { - order: 2; - justify-content: center; - } - - .poll-icon { - order: 3; - justify-content: right; - } - .error { text-align: center; } .media-upload-wrapper { - margin-right: .2em; - margin-bottom: .5em; + margin-right: 0.2em; + margin-bottom: 0.5em; width: 18em; - img, video { + img, + video { object-fit: contain; max-height: 10em; } @@ -557,18 +566,14 @@ } } - .btn[disabled] { - cursor: not-allowed; - } - @keyframes fade-in { from { opacity: 0; } - to { opacity: 0.6; } + to { opacity: 0.6; } } @keyframes fade-out { from { opacity: 0.6; } - to { opacity: 0; } + to { opacity: 0; } } .drop-indicator { diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js index 2a0dac85..47a48623 100644 --- a/src/components/react_button/react_button.js +++ b/src/components/react_button/react_button.js @@ -41,7 +41,7 @@ const ReactButton = { }, focusInput () { this.$nextTick(() => { - const input = this.$el.querySelector('input') + const input = document.querySelector('.reaction-picker-filter > input') if (input) input.focus() }) }, diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue index 0c5fe321..a813b6fd 100644 --- a/src/components/react_button/react_button.vue +++ b/src/components/react_button/react_button.vue @@ -73,8 +73,8 @@ <script src="./react_button.js"></script> <style lang="scss"> -@import '../../_variables.scss'; -@import '../../_mixins.scss'; +@import "../../variables"; +@import "../../mixins"; .ReactButton { .reaction-picker-filter { @@ -104,20 +104,19 @@ text-align: center; align-content: flex-start; user-select: none; - - mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat, - linear-gradient(to bottom, white 0, transparent 100%) top no-repeat, - linear-gradient(to top, white, white); + mask: + linear-gradient(to top, white 0, transparent 100%) bottom no-repeat, + linear-gradient(to bottom, white 0, transparent 100%) top no-repeat, + linear-gradient(to top, white, white); transition: mask-size 150ms; mask-size: 100% 20px, 100% 20px, auto; /* Autoprefixed seem to ignore this one, and also syntax is different */ - -webkit-mask-composite: xor; + mask-composite: xor; mask-composite: exclude; .emoji-button { cursor: pointer; - flex-basis: 20%; line-height: 1.5; align-content: center; @@ -128,11 +127,6 @@ } } - /* override of popover internal stuff */ - .popover-trigger-button { - width: auto; - } - .popover-trigger { padding: 10px; margin: -10px; @@ -141,10 +135,12 @@ color: $fallback--text; color: var(--text, $fallback--text); } - } .popover-trigger-button { + /* override of popover internal stuff */ + width: auto; + @include unfocused-style { .focus-marker { visibility: hidden; diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue index 24d9b59b..a26162f0 100644 --- a/src/components/registration/registration.vue +++ b/src/components/registration/registration.vue @@ -277,7 +277,7 @@ <script src="./registration.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; $validations-cRed: #f04124; .registration-form { @@ -321,7 +321,7 @@ $validations-cRed: #f04124; .form-group--error { animation-name: shakeError; - animation-duration: .6s; + animation-duration: 0.6s; animation-timing-function: ease-in-out; } @@ -350,7 +350,7 @@ $validations-cRed: #f04124; } form textarea { - line-height:16px; + line-height: 16px; resize: vertical; } diff --git a/src/components/remote_user_resolver/remote_user_resolver.vue b/src/components/remote_user_resolver/remote_user_resolver.vue index f8945225..07aac169 100644 --- a/src/components/remote_user_resolver/remote_user_resolver.vue +++ b/src/components/remote_user_resolver/remote_user_resolver.vue @@ -15,6 +15,3 @@ </template> <script src="./remote_user_resolver.js"></script> - -<style lang="scss"> -</style> diff --git a/src/components/reply_button/reply_button.vue b/src/components/reply_button/reply_button.vue index dada511b..6e3964b7 100644 --- a/src/components/reply_button/reply_button.vue +++ b/src/components/reply_button/reply_button.vue @@ -51,8 +51,8 @@ <script src="./reply_button.js"></script> <style lang="scss"> -@import '../../_variables.scss'; -@import '../../_mixins.scss'; +@import "../../variables"; +@import "../../mixins"; .ReplyButton { display: flex; @@ -86,6 +86,5 @@ } } } - } </style> diff --git a/src/components/report/report.scss b/src/components/report/report.scss index 578b4eb1..9762400b 100644 --- a/src/components/report/report.scss +++ b/src/components/report/report.scss @@ -1,4 +1,4 @@ -@import '../../_variables.scss'; +@import "../../variables"; .Report { .report-content { diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue index 240828e3..7700ee0d 100644 --- a/src/components/retweet_button/retweet_button.vue +++ b/src/components/retweet_button/retweet_button.vue @@ -65,8 +65,8 @@ <script src="./retweet_button.js"></script> <style lang="scss"> -@import '../../_variables.scss'; -@import '../../_mixins.scss'; +@import "../../variables"; +@import "../../mixins"; .RetweetButton { display: flex; diff --git a/src/components/rich_content/rich_content.scss b/src/components/rich_content/rich_content.scss index db08ef1e..e5d353ac 100644 --- a/src/components/rich_content/rich_content.scss +++ b/src/components/rich_content/rich_content.scss @@ -1,7 +1,11 @@ +@import "../../variables"; + .RichContent { blockquote { - margin: 0.2em 0 0.2em 2em; + margin: 0.2em 0 0.2em 0.2em; font-style: italic; + border-left: 0.2em solid var(--faint, $fallback--faint); + padding-left: 1em; } pre { @@ -17,11 +21,11 @@ } p { - margin: 0 0 1em 0; + margin: 0 0 1em; } p:last-child { - margin: 0 0 0 0; + margin: 0; } h1 { diff --git a/src/components/scope_selector/scope_selector.vue b/src/components/scope_selector/scope_selector.vue index f3bee183..d6e7265b 100644 --- a/src/components/scope_selector/scope_selector.vue +++ b/src/components/scope_selector/scope_selector.vue @@ -64,10 +64,9 @@ <script src="./scope_selector.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .ScopeSelector { - .scope { display: inline-block; cursor: pointer; diff --git a/src/components/search/search.vue b/src/components/search/search.vue index 6fc6a0de..19b9c577 100644 --- a/src/components/search/search.vue +++ b/src/components/search/search.vue @@ -148,7 +148,7 @@ <script src="./search.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .search-result-heading { color: $fallback--faint; @@ -176,7 +176,7 @@ } .search-result-footer { - border-width: 1px 0 0 0; + border-width: 1px 0 0; border-style: solid; border-color: var(--border, $fallback--border); padding: 10px; @@ -229,11 +229,11 @@ color: $fallback--text; color: var(--text, $fallback--text); } - } +} - .more-statuses-button { - height: 3.5em; - line-height: 3.5em; - } +.more-statuses-button { + height: 3.5em; + line-height: 3.5em; +} </style> diff --git a/src/components/search_bar/search_bar.vue b/src/components/search_bar/search_bar.vue index 199a7500..3969d8de 100644 --- a/src/components/search_bar/search_bar.vue +++ b/src/components/search_bar/search_bar.vue @@ -56,7 +56,7 @@ <script src="./search_bar.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .SearchBar { display: inline-flex; diff --git a/src/components/select/select.vue b/src/components/select/select.vue index 92493b0b..0ce4ea82 100644 --- a/src/components/select/select.vue +++ b/src/components/select/select.vue @@ -21,22 +21,20 @@ <script src="./select.js"> </script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; /* TODO fix order of styles */ label.Select { padding: 0; select { - -webkit-appearance: none; - -moz-appearance: none; appearance: none; background: transparent; border: none; color: $fallback--text; color: var(--inputText, --text, $fallback--text); margin: 0; - padding: 0 2em 0 .2em; + padding: 0 2em 0 0.2em; font-family: sans-serif; font-family: var(--inputFont, sans-serif); font-size: 1em; @@ -59,6 +57,5 @@ label.Select { z-index: 0; pointer-events: none; } - } </style> diff --git a/src/components/selectable_list/selectable_list.vue b/src/components/selectable_list/selectable_list.vue index 1f7683ab..14910fc5 100644 --- a/src/components/selectable_list/selectable_list.vue +++ b/src/components/selectable_list/selectable_list.vue @@ -51,7 +51,7 @@ <script src="./selectable_list.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .selectable-list { &-item-inner { @@ -67,6 +67,7 @@ background-color: $fallback--lightBg; background-color: var(--selectedMenu, $fallback--lightBg); color: var(--selectedMenuText, $fallback--text); + --faint: var(--selectedMenuFaintText, $fallback--faint); --faintLink: var(--selectedMenuFaintLink, $fallback--faint); --lightText: var(--selectedMenuLightText, $fallback--lightText); diff --git a/src/components/settings_modal/helpers/choice_setting.vue b/src/components/settings_modal/helpers/choice_setting.vue index d141a0d6..8fdbb5d3 100644 --- a/src/components/settings_modal/helpers/choice_setting.vue +++ b/src/components/settings_modal/helpers/choice_setting.vue @@ -28,8 +28,3 @@ </template> <script src="./choice_setting.js"></script> - -<style lang="scss"> -.ChoiceSetting { -} -</style> diff --git a/src/components/settings_modal/helpers/size_setting.vue b/src/components/settings_modal/helpers/size_setting.vue index 90c9f538..5a78f100 100644 --- a/src/components/settings_modal/helpers/size_setting.vue +++ b/src/components/settings_modal/helpers/size_setting.vue @@ -45,10 +45,11 @@ <script src="./size_setting.js"></script> <style lang="scss"> -.css-unit-input, .css-unit-input select { +.css-unit-input, +.css-unit-input select { margin-left: 0.5em; - width: 4em !important; - max-width: 4em !important; - min-width: 4em !important; + width: 4em; + max-width: 4em; + min-width: 4em; } </style> diff --git a/src/components/settings_modal/settings_modal.scss b/src/components/settings_modal/settings_modal.scss index 13cb0e65..f5861229 100644 --- a/src/components/settings_modal/settings_modal.scss +++ b/src/components/settings_modal/settings_modal.scss @@ -1,4 +1,5 @@ -@import 'src/_variables.scss'; +@import "src/variables"; + .settings-modal { overflow: hidden; @@ -6,32 +7,13 @@ .option-list { list-style-type: none; padding-left: 2em; + li { margin-bottom: 0.5em; } - .suboptions { - margin-top: 0.3em - } - } - - &.peek { - .settings-modal-panel { - /* Explanation: - * Modal is positioned vertically centered. - * 100vh - 100% = Distance between modal's top+bottom boundaries and screen - * (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen - * + 100% - we move modal completely off-screen, it's top boundary touches - * bottom of the screen - * - 50px - leaving tiny amount of space so that titlebar + tiny amount of modal is visible - */ - transform: translateY(calc(((100vh - 100%) / 2 + 100%) - 50px)); - @media all and (max-width: 800px) { - /* For mobile, the modal takes 100% of the available screen. - This ensures the minimized modal is always 50px above the browser bottom bar regardless of whether or not it is visible. - */ - transform: translateY(calc(100% - 50px)); - } + .suboptions { + margin-top: 0.3em; } } @@ -63,6 +45,7 @@ .settings-footer { display: flex; + >* { margin-right: 0.5em; } @@ -72,4 +55,26 @@ flex-grow: 1; } } + + &.peek { + .settings-modal-panel { + /* Explanation: + * Modal is positioned vertically centered. + * 100vh - 100% = Distance between modal's top+bottom boundaries and screen + * (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen + * + 100% - we move modal completely off-screen, it's top boundary touches + * bottom of the screen + * - 50px - leaving tiny amount of space so that titlebar + tiny amount of modal is visible + */ + transform: translateY(calc(((100vh - 100%) / 2 + 100%) - 50px)); + + @media all and (max-width: 800px) { + /* For mobile, the modal takes 100% of the available screen. + This ensures the minimized modal is always 50px above the browser bottom + bar regardless of whether or not it is visible. + */ + transform: translateY(calc(100% - 50px)); + } + } + } } diff --git a/src/components/settings_modal/settings_modal_content.scss b/src/components/settings_modal/settings_modal_content.scss index 81ab434b..87df7982 100644 --- a/src/components/settings_modal/settings_modal_content.scss +++ b/src/components/settings_modal/settings_modal_content.scss @@ -1,4 +1,5 @@ -@import 'src/_variables.scss'; +@import "src/variables"; + .settings_tab-switcher { height: 100%; @@ -10,7 +11,8 @@ > div, > label { display: block; - margin-bottom: .5em; + margin-bottom: 0.5em; + &:last-child { margin-bottom: 0; } @@ -21,7 +23,7 @@ .option-list { margin: 0; - padding-left: .5em; + padding-left: 0.5em; } } diff --git a/src/components/settings_modal/tabs/data_import_export_tab.vue b/src/components/settings_modal/tabs/data_import_export_tab.vue index e3b7f407..48356c9b 100644 --- a/src/components/settings_modal/tabs/data_import_export_tab.vue +++ b/src/components/settings_modal/tabs/data_import_export_tab.vue @@ -78,6 +78,16 @@ {{ $t('settings.download_backup') }} </a> <span + v-else-if="backup.state === 'running'" + > + {{ $tc('settings.backup_running', backup.processed_number, { number: backup.processed_number }) }} + </span> + <span + v-else-if="backup.state === 'failed'" + > + {{ $t('settings.backup_failed') }} + </span> + <span v-else > {{ $t('settings.backup_not_ready') }} diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js index 5354e5db..7c37f0bc 100644 --- a/src/components/settings_modal/tabs/filtering_tab.js +++ b/src/components/settings_modal/tabs/filtering_tab.js @@ -1,4 +1,4 @@ -import { filter, trim } from 'lodash' +import { filter, trim, debounce } from 'lodash' import BooleanSetting from '../helpers/boolean_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue' import IntegerSetting from '../helpers/integer_setting.vue' @@ -29,11 +29,16 @@ const FilteringTab = { }, set (value) { this.muteWordsStringLocal = value + this.debouncedSetMuteWords(value) + } + }, + debouncedSetMuteWords () { + return debounce((value) => { this.$store.dispatch('setOption', { name: 'muteWords', value: filter(value.split('\n'), (word) => trim(word).length > 0) }) - } + }, 1000) } }, // Updating nested properties diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index 8561647b..582cb288 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -464,6 +464,7 @@ justify-content: space-evenly; flex-wrap: wrap; } + .column-settings .size-label { display: block; margin-bottom: 0.5em; diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.scss b/src/components/settings_modal/tabs/mutes_and_blocks_tab.scss index 2adff847..5fa3a27b 100644 --- a/src/components/settings_modal/tabs/mutes_and_blocks_tab.scss +++ b/src/components/settings_modal/tabs/mutes_and_blocks_tab.scss @@ -1,29 +1,29 @@ .mutes-and-blocks-tab { - height: 100%; + height: 100%; - .usersearch-wrapper { - padding: 1em; - } + .usersearch-wrapper { + padding: 1em; + } - .bulk-actions { - text-align: right; - padding: 0 1em; - min-height: 2em; - } + .bulk-actions { + text-align: right; + padding: 0 1em; + min-height: 2em; + } - .bulk-action-button { - width: 10em - } + .bulk-action-button { + width: 10em; + } - .domain-mute-form { - padding: 1em; - display: flex; - flex-direction: column - } + .domain-mute-form { + padding: 1em; + display: flex; + flex-direction: column; + } - .domain-mute-button { - align-self: flex-end; - margin-top: 1em; - width: 10em - } + .domain-mute-button { + align-self: flex-end; + margin-top: 1em; + width: 10em; + } } diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js index d5977461..b2d8fed2 100644 --- a/src/components/settings_modal/tabs/profile_tab.js +++ b/src/components/settings_modal/tabs/profile_tab.js @@ -153,7 +153,7 @@ const ProfileTab = { return false }, deleteField (index, event) { - this.$delete(this.newFields, index) + this.newFields.splice(index, 1) }, uploadFile (slot, e) { const file = e.target.files[0] diff --git a/src/components/settings_modal/tabs/profile_tab.scss b/src/components/settings_modal/tabs/profile_tab.scss index 201f1a76..73879192 100644 --- a/src/components/settings_modal/tabs/profile_tab.scss +++ b/src/components/settings_modal/tabs/profile_tab.scss @@ -1,4 +1,5 @@ -@import '../../../_variables.scss'; +@import "../../../variables"; + .profile-tab { .bio { margin: 0; @@ -8,7 +9,7 @@ padding-top: 5px; } - input[type=file] { + input[type="file"] { padding: 5px; height: auto; } @@ -52,7 +53,7 @@ right: 0.2em; border-radius: $fallback--tooltipRadius; border-radius: var(--tooltipRadius, $fallback--tooltipRadius); - background-color: rgba(0, 0, 0, 0.6); + background-color: rgb(0 0 0 / 60%); opacity: 0.7; width: 1.5em; height: 1.5em; diff --git a/src/components/settings_modal/tabs/security_tab/mfa.vue b/src/components/settings_modal/tabs/security_tab/mfa.vue index 455d17b6..ee5b6b13 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa.vue +++ b/src/components/settings_modal/tabs/security_tab/mfa.vue @@ -137,9 +137,11 @@ <script src="./mfa.js"></script> <style lang="scss"> -@import '../../../../_variables.scss'; +@import "../../../../variables"; + .mfa-settings { - .mfa-heading, .method-item { + .mfa-heading, + .method-item { display: flex; flex-wrap: wrap; justify-content: space-between; @@ -155,18 +157,19 @@ display: flex; justify-content: center; flex-wrap: wrap; + .qr-code { flex: 1; padding-right: 10px; } .verify { flex: 1; } - .error { margin: 4px 0 0 0; } + .error { margin: 4px 0 0; } + .confirm-otp-actions { button { width: 15em; margin-top: 5px; } - } } } diff --git a/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue b/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue index d7e98b3c..923161b2 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue +++ b/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue @@ -21,13 +21,14 @@ </template> <script src="./mfa_backup_codes.js"></script> <style lang="scss"> -@import '../../../../_variables.scss'; +@import "../../../../variables"; .mfa-backup-codes { .warning { color: $fallback--cOrange; color: var(--cOrange, $fallback--cOrange); } + .backup-codes { font-family: var(--postCodeFont, monospace); } diff --git a/src/components/settings_modal/tabs/theme_tab/preview.vue b/src/components/settings_modal/tabs/theme_tab/preview.vue index ba6bd529..d755279a 100644 --- a/src/components/settings_modal/tabs/theme_tab/preview.vue +++ b/src/components/settings_modal/tabs/theme_tab/preview.vue @@ -33,10 +33,10 @@ scope="global" keypath="settings.style.preview.text" > - <code style="font-family: var(--postCodeFont)"> + <code style="font-family: var(--postCodeFont);"> {{ $t('settings.style.preview.mono') }} </code> - <a style="color: var(--link)"> + <a style="color: var(--link);"> {{ $t('settings.style.preview.link') }} </a> </i18n-t> @@ -44,25 +44,25 @@ <div class="icons"> <FAIcon fixed-width - style="color: var(--cBlue)" + style="color: var(--cBlue);" class="fa-scale-110 fa-old-padding" icon="reply" /> <FAIcon fixed-width - style="color: var(--cGreen)" + style="color: var(--cGreen);" class="fa-scale-110 fa-old-padding" icon="retweet" /> <FAIcon fixed-width - style="color: var(--cOrange)" + style="color: var(--cOrange);" class="fa-scale-110 fa-old-padding" icon="star" /> <FAIcon fixed-width - style="color: var(--cRed)" + style="color: var(--cRed);" class="fa-scale-110 fa-old-padding" icon="times" /> @@ -81,7 +81,7 @@ class="faint" scope="global" > - <a style="color: var(--faintLink)"> + <a style="color: var(--faintLink);"> {{ $t('settings.style.preview.faint_link') }} </a> </i18n-t> @@ -138,6 +138,7 @@ export default {} .preview-container { position: relative; } + .underlay-preview { position: absolute; top: 0; diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss index bad6f51b..9935c2e7 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss @@ -1,20 +1,17 @@ -@import 'src/_variables.scss'; +@import "src/variables"; + .theme-tab { padding-bottom: 2em; - .theme-warning { - display: flex; - align-items: baseline; - margin-bottom: .5em; - .buttons { - .btn { - margin-bottom: .5em; - } - } - } + .preset-switcher { margin-right: 1em; } + .btn { + margin-left: 0.25em; + margin-right: 0.25em; + } + .style-control { display: flex; align-items: baseline; @@ -24,35 +21,37 @@ flex: 1; } - &.disabled { - input, select { - opacity: .5 - } - } - .opt { - margin: .5em; + margin: 0.5em; } .color-input { flex: 0 0 0; } - input, select { + input, + select { min-width: 3em; margin: 0; flex: 0; - &[type=number] { + &[type="number"] { min-width: 5em; } - &[type=range] { + &[type="range"] { flex: 1; min-width: 3em; align-self: flex-start; } } + + &.disabled { + input, + select { + opacity: 0.5; + } + } } .reset-container { @@ -63,8 +62,7 @@ .reset-container, .apply-container, .radius-container, - .color-container, - { + .color-container, { display: flex; } @@ -73,10 +71,11 @@ flex-direction: column; } - .color-container{ + .color-container { > h4 { width: 99%; } + flex-wrap: wrap; justify-content: space-between; } @@ -100,7 +99,7 @@ p { flex: 1; margin: 0; - margin-right: .5em; + margin-right: 0.5em; } } @@ -112,15 +111,16 @@ min-width: 1px; flex: 0 auto; padding: 0 1em; - margin-bottom: .5em; + margin-bottom: 0.5em; } } .shadow-selector { .override { flex: 1; - margin-left: .5em; + margin-left: 0.5em; } + .select-container { margin-top: -4px; margin-bottom: -3px; @@ -136,7 +136,7 @@ .presets, .import-export { - margin-bottom: .5em; + margin-bottom: 0.5em; } .import-export { @@ -144,16 +144,17 @@ } .override { - margin-left: .5em; + margin-left: 0.5em; } } .save-load-options { flex-wrap: wrap; - margin-top: .5em; + margin-top: 0.5em; justify-content: center; + .keep-option { - margin: 0 .5em .5em; + margin: 0 0.5em 0.5em; min-width: 25%; } } @@ -179,11 +180,11 @@ flex: 1; h4 { - margin-bottom: .25em; + margin-bottom: 0.25em; } .icons { - margin-top: .5em; + margin-top: 0.5em; display: flex; i { @@ -199,8 +200,20 @@ align-items: center; } - .avatar, .avatar-alt{ - background: linear-gradient(135deg, #b8e1fc 0%,#a9d2f3 10%,#90bae4 25%,#90bcea 37%,#90bff0 50%,#6ba8e5 51%,#a2daf5 83%,#bdf3fd 100%); + .avatar, + .avatar-alt { + background: + linear-gradient( + 135deg, + #b8e1fc 0%, + #a9d2f3 10%, + #90bae4 25%, + #90bcea 37%, + #90bff0 50%, + #6ba8e5 51%, + #a2daf5 83%, + #bdf3fd 100% + ); color: black; font-family: sans-serif; text-align: center; @@ -251,33 +264,33 @@ } } + .radius-item { + flex-basis: auto; + } + .radius-item, .color-item { min-width: 20em; margin: 5px 6px 0 0; - display:flex; + display: flex; flex-direction: column; flex: 1 1 0; &.wide { - min-width: 60% + min-width: 60%; } &:not(.wide):nth-child(2n+1) { margin-right: 7px; - } - .color, .opacity { - display:flex; + .color, + .opacity { + display: flex; align-items: baseline; } } - .radius-item { - flex-basis: auto; - } - .theme-radius-rn, .theme-color-cl { border: 0; @@ -295,14 +308,11 @@ .theme-radius-in { min-width: 1em; - } - - .theme-radius-in { max-width: 7em; flex: 1; } - .theme-radius-lb{ + .theme-radius-lb { max-width: 50em; } @@ -310,9 +320,16 @@ padding: 20px; } - .btn { - margin-left: .25em; - margin-right: .25em; + .theme-warning { + display: flex; + align-items: baseline; + margin-bottom: 0.5em; + + .buttons { + .btn { + margin-bottom: 0.5em; + } + } } } @@ -323,6 +340,7 @@ justify-content: space-around; flex-grow: 1; + /* stylelint-disable-next-line no-descending-specificity */ .btn { flex-grow: 1; min-height: 2em; diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index 669cac71..7546535d 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -218,7 +218,8 @@ <script src="./shadow_control.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; + .shadow-control { display: flex; flex-wrap: wrap; @@ -229,6 +230,7 @@ .shadow-tweak { margin: 5px 6px 0 0; } + .shadow-preview-container { flex: 0; display: flex; @@ -236,19 +238,19 @@ $side: 15em; - input[type=number] { + input[type="number"] { width: 5em; min-width: 2em; } + .x-shift-control, .y-shift-control { display: flex; flex: 0; - &[disabled=disabled] *{ - opacity: .5 + &[disabled="disabled"] * { + opacity: 0.5; } - } .x-shift-control { @@ -256,37 +258,40 @@ } .x-shift-control .wrap, - input[type=range] { + input[type="range"] { margin: 0; width: $side; height: 2em; } + .y-shift-control { flex-direction: column; align-items: flex-end; + .wrap { width: 2em; height: $side; } - input[type=range] { + + input[type="range"] { transform-origin: 1em 1em; transform: rotate(90deg); } } + .preview-window { flex: 1; - background-color: #999999; + background-color: #999; display: flex; align-items: center; justify-content: center; background-image: - linear-gradient(45deg, #666666 25%, transparent 25%), - linear-gradient(-45deg, #666666 25%, transparent 25%), - linear-gradient(45deg, transparent 75%, #666666 75%), - linear-gradient(-45deg, transparent 75%, #666666 75%); + linear-gradient(45deg, #666 25%, transparent 25%), + linear-gradient(-45deg, #666 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, #666 75%), + linear-gradient(-45deg, transparent 75%, #666 75%); background-size: 20px 20px; - background-position:0 0, 0 10px, 10px -10px, -10px 0; - + background-position: 0 0, 0 10px, 10px -10px, -10px 0; border-radius: $fallback--inputRadius; border-radius: var(--inputRadius, $fallback--inputRadius); @@ -312,14 +317,15 @@ flex: 1; } - .shadow-switcher, .btn { + .shadow-switcher, + .btn { min-width: 1px; margin-right: 5px; } .btn { - padding: 0 .4em; - margin: 0 .1em; + padding: 0 0.4em; + margin: 0 0.1em; } } } diff --git a/src/components/shout_panel/shout_panel.vue b/src/components/shout_panel/shout_panel.vue index 688c2d61..a7013469 100644 --- a/src/components/shout_panel/shout_panel.vue +++ b/src/components/shout_panel/shout_panel.vue @@ -75,7 +75,7 @@ <script src="./shout_panel.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .floating-shout { position: fixed; diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index 887596f8..994ac953 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -73,7 +73,7 @@ > <router-link :to="{ name: 'chats', params: { username: currentUser.screen_name } }" - style="position: relative" + style="position: relative;" > <FAIcon fixed-width @@ -251,7 +251,7 @@ <script src="./side_drawer.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .side-drawer-container { position: fixed; @@ -284,11 +284,11 @@ z-index: -1; transition: 0.35s; transition-property: background-color; - background-color: rgba(0, 0, 0, 0.5); + background-color: rgb(0 0 0 / 50%); } .side-drawer-darken-closed { - background-color: rgba(0, 0, 0, 0); + background-color: rgb(0 0 0 / 0%); } .side-drawer-click-outside { @@ -297,20 +297,21 @@ .side-drawer { overflow-x: hidden; - transition-timing-function: cubic-bezier(0, 1, 0.5, 1); transition: 0.35s; + transition-timing-function: cubic-bezier(0, 1, 0.5, 1); transition-property: transform; margin: 0 0 0 -100px; padding: 0 0 1em 100px; width: 80%; max-width: 20em; flex: 0 0 80%; - box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); + box-shadow: 1px 1px 4px rgb(0 0 0 / 60%); box-shadow: var(--panelShadow); 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); @@ -360,7 +361,6 @@ list-style: none; margin: 0; padding: 0; - border-bottom: 1px solid; border-color: $fallback--border; border-color: var(--border, $fallback--border); @@ -373,7 +373,8 @@ .side-drawer li { padding: 0; - a, button { + a, + button { box-sizing: border-box; display: block; height: 3em; @@ -385,6 +386,7 @@ background-color: var(--selectedMenuPopover, $fallback--lightBg); color: $fallback--text; color: var(--selectedMenuPopoverText, $fallback--text); + --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); diff --git a/src/components/staff_panel/staff_panel.vue b/src/components/staff_panel/staff_panel.vue index 6b9e61f2..77155e8b 100644 --- a/src/components/staff_panel/staff_panel.vue +++ b/src/components/staff_panel/staff_panel.vue @@ -27,7 +27,6 @@ <script src="./staff_panel.js"></script> <style lang="scss"> - .staff-group { padding-left: 1em; padding-top: 1em; diff --git a/src/components/status/status.scss b/src/components/status/status.scss index ada9841e..44812867 100644 --- a/src/components/status/status.scss +++ b/src/components/status/status.scss @@ -1,4 +1,4 @@ -@import '../../_variables.scss'; +@import "../../variables"; .Status { min-width: 0; @@ -181,7 +181,7 @@ .reply-to-popover { .reply-to:hover::before { - content: ''; + content: ""; display: block; position: absolute; bottom: 0; @@ -197,7 +197,7 @@ &.-strikethrough { .reply-to::after { - content: ''; + content: ""; display: block; position: absolute; top: 50%; @@ -336,7 +336,7 @@ margin-left: 0.2em; &::before { - content: ' '; + content: " "; } } @@ -374,7 +374,7 @@ align-items: center; &::before { - content: ''; + content: ""; position: absolute; height: 100%; width: 1px; diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 82eb7ac6..35b15362 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -84,7 +84,7 @@ :user="statusoid.user" /> <div class="right-side faint"> - <span + <bdi class="status-username repeater-name" :title="retweeter" > @@ -101,7 +101,7 @@ v-else :to="retweeterProfileLink" >{{ retweeter }}</router-link> - </span> + </bdi> {{ ' ' }} <FAIcon icon="retweet" @@ -261,7 +261,7 @@ v-if="!isPreview" :status-id="status.parent_visible && status.in_reply_to_status_id" class="reply-to-popover" - style="min-width: 0" + style="min-width: 0;" :class="{ '-strikethrough': !status.parent_visible }" > <button diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss index 039d4c7f..8fd60afc 100644 --- a/src/components/status_body/status_body.scss +++ b/src/components/status_body/status_body.scss @@ -1,4 +1,4 @@ -@import '../../_variables.scss'; +@import "../../variables"; .StatusBody { display: flex; @@ -40,7 +40,7 @@ .summary-wrapper { margin-bottom: 0.5em; border-style: solid; - border-width: 0 0 1px 0; + border-width: 0 0 1px; border-color: var(--border, $fallback--border); flex-grow: 0; @@ -58,8 +58,7 @@ .text-wrapper { display: flex; - flex-direction: column; - flex-wrap: nowrap; + flex-flow: column nowrap; &.-tall-status { position: relative; @@ -75,7 +74,7 @@ linear-gradient(to top, white, white); /* Autoprefixed seem to ignore this one, and also syntax is different */ - -webkit-mask-composite: xor; + mask-composite: xor; mask-composite: exclude; } } @@ -144,7 +143,7 @@ mask-image: linear-gradient(to bottom, white 2em, transparent 3em); /* Autoprefixed seem to ignore this one, and also syntax is different */ - -webkit-mask-composite: xor; + mask-composite: xor; mask-composite: exclude; } @@ -158,7 +157,7 @@ .summary-wrapper { .summary::after { - content: ': '; + content: ": "; } line-height: inherit; diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue index e2120f7a..c0e5c0b9 100644 --- a/src/components/status_content/status_content.vue +++ b/src/components/status_content/status_content.vue @@ -33,6 +33,7 @@ <gallery v-if="status.attachments.length !== 0" class="attachments media-body" + :compact="compact" :nsfw="nsfwClickthrough" :attachments="status.attachments" :limit="compact ? 1 : 0" diff --git a/src/components/status_history_modal/status_history_modal.vue b/src/components/status_history_modal/status_history_modal.vue index 990be35b..79642e9c 100644 --- a/src/components/status_history_modal/status_history_modal.vue +++ b/src/components/status_history_modal/status_history_modal.vue @@ -32,6 +32,7 @@ .modal-view.status-history-modal-view { align-items: flex-start; } + .status-history-modal-panel { flex-shrink: 0; margin-top: 25%; diff --git a/src/components/status_popover/status_popover.vue b/src/components/status_popover/status_popover.vue index f4ab357b..311ca099 100644 --- a/src/components/status_popover/status_popover.vue +++ b/src/components/status_popover/status_popover.vue @@ -40,14 +40,13 @@ <script src="./status_popover.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; /* popover styles load on-demand, so we need to override */ .status-popover.popover { font-size: 1rem; min-width: 15em; max-width: 95%; - border-color: $fallback--border; border-color: var(--border, $fallback--border); border-style: solid; diff --git a/src/components/sticker_picker/sticker_picker.vue b/src/components/sticker_picker/sticker_picker.vue index dc449ccb..904853c0 100644 --- a/src/components/sticker_picker/sticker_picker.vue +++ b/src/components/sticker_picker/sticker_picker.vue @@ -32,24 +32,29 @@ <script src="./sticker_picker.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .sticker-picker { width: 100%; + .contents { min-height: 250px; + .sticker-picker-content { display: flex; flex-wrap: wrap; padding: 0 4px; + .sticker { display: flex; flex: 1 1 auto; margin: 4px; width: 56px; height: 56px; + img { height: 100%; + &:hover { filter: drop-shadow(0 0 5px var(--accent, $fallback--link)); } diff --git a/src/components/still-image/still-image.js b/src/components/still-image/still-image.js index 200ef147..56fd2fd9 100644 --- a/src/components/still-image/still-image.js +++ b/src/components/still-image/still-image.js @@ -8,7 +8,8 @@ const StillImage = { 'alt', 'height', 'width', - 'dataSrc' + 'dataSrc', + 'loading' ], data () { return { diff --git a/src/components/still-image/still-image.vue b/src/components/still-image/still-image.vue index 633fb229..fc46fbe6 100644 --- a/src/components/still-image/still-image.vue +++ b/src/components/still-image/still-image.vue @@ -17,6 +17,7 @@ :data-src="dataSrc" :src="realSrc" :referrerpolicy="referrerpolicy" + :loading="loading" @load="onLoad" @error="onError" > @@ -27,7 +28,7 @@ <script src="./still-image.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .still-image { position: relative; @@ -57,13 +58,13 @@ &.animated { &::before { zoom: var(--_still_image-label-scale, 1); - content: 'gif'; + content: "gif"; position: absolute; line-height: 1; font-size: 0.7em; top: 0.5em; left: 0.5em; - background: rgba(127, 127, 127, 0.5); + background: rgb(127 127 127 / 50%); color: #fff; display: block; padding: 2px 4px; diff --git a/src/components/swipe_click/swipe_click.js b/src/components/swipe_click/swipe_click.js index 238e6df8..2a423901 100644 --- a/src/components/swipe_click/swipe_click.js +++ b/src/components/swipe_click/swipe_click.js @@ -5,6 +5,8 @@ import GestureService from '../../services/gesture_service/gesture_service' * direction: a vector that indicates the direction of the intended swipe * threshold: the minimum distance in pixels the swipe has moved on `direction' * for swipe-finished() to have a non-zero sign + * disableClickThreshold: the minimum distance in pixels for the swipe to + * not trigger a click * perpendicularTolerance: see gesture_service * * Events: @@ -34,6 +36,10 @@ const SwipeClick = { type: Function, default: () => 30 }, + disableClickThreshold: { + type: Function, + default: () => 1 + }, perpendicularTolerance: { type: Number, default: 1.0 @@ -72,6 +78,7 @@ const SwipeClick = { this.$gesture = new GestureService.SwipeAndClickGesture({ direction: this.direction, threshold: this.threshold, + disableClickThreshold: this.disableClickThreshold, perpendicularTolerance: this.perpendicularTolerance, swipePreviewCallback: this.preview, swipeEndCallback: this.end, diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index d930368c..705925c8 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -1,5 +1,6 @@ -@import '../../_variables.scss'; +@import "../../variables"; +/* stylelint-disable no-descending-specificity */ .tab-switcher { display: flex; @@ -19,8 +20,9 @@ flex-direction: row; flex: 0 0 auto; - &::after, &::before { - content: ''; + &::after, + &::before { + content: ""; flex: 1 1 auto; border-bottom: 1px solid; border-bottom-color: $fallback--border; @@ -39,6 +41,7 @@ border-bottom-color: var(--border, $fallback--border); } } + .tab { width: 100%; min-width: 1px; @@ -48,6 +51,7 @@ margin-bottom: 6px - 99px; } } + .contents.scrollable-tabs { flex-basis: 0; } @@ -70,10 +74,11 @@ overflow-x: hidden; flex-direction: column; - &::after, &::before { + &::after, + &::before { flex-shrink: 0; - flex-basis: .5em; - content: ''; + flex-basis: 0.5em; + content: ""; border-right: 1px solid; border-right-color: $fallback--border; border-right-color: var(--border, $fallback--border); @@ -107,7 +112,7 @@ &::before { flex: 0 0 6px; - content: ''; + content: ""; border-right: 1px solid; border-right-color: $fallback--border; border-right-color: var(--border, $fallback--border); @@ -131,12 +136,13 @@ margin-left: 1em; @media all and (max-width: 800px) { - padding-left: .25em; - padding-right: calc(.25em + 200px); - margin-right: calc(.25em - 200px); - margin-left: .25em; + padding-left: 0.25em; + padding-right: calc(0.25em + 200px); + margin-right: calc(0.25em - 200px); + margin-left: 0.25em; + .text { - display: none + display: none; } } } @@ -145,15 +151,17 @@ .contents { flex: 1 0 auto; - min-height: 0px; + min-height: 0; .hidden { display: none; } + .full-height:not(.hidden) { height: 100%; display: flex; flex-direction: column; + > *:not(.mobile-label) { flex: 1; } @@ -196,7 +204,8 @@ position: relative; box-sizing: border-box; - &::after, &::before { + &::after, + &::before { display: block; flex: 1 1 auto; } @@ -209,7 +218,7 @@ &:not(.active) { &::after { - content: ''; + content: ""; position: absolute; z-index: 7; } @@ -217,11 +226,11 @@ } .mobile-label { - padding-left: .3em; - padding-bottom: .25em; - margin-top: .5em; - margin-left: .2em; - margin-bottom: .25em; + padding-left: 0.3em; + padding-bottom: 0.25em; + margin-top: 0.5em; + margin-left: 0.2em; + margin-bottom: 0.25em; border-bottom: 1px solid var(--border, $fallback--border); @media all and (min-width: 800px) { @@ -229,3 +238,4 @@ } } } +/* stylelint-enable no-descending-specificity */ diff --git a/src/components/terms_of_service_panel/terms_of_service_panel.vue b/src/components/terms_of_service_panel/terms_of_service_panel.vue index 1df41d70..bff0ae74 100644 --- a/src/components/terms_of_service_panel/terms_of_service_panel.vue +++ b/src/components/terms_of_service_panel/terms_of_service_panel.vue @@ -17,6 +17,6 @@ <style lang="scss"> .tos-content { - margin: 1em + margin: 1em; } </style> diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index c6fffc71..04727278 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -119,7 +119,8 @@ <script src="./thread_tree.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; + .thread-tree-replies { margin-left: var(--status-margin, $status-margin); border-left: 2px solid var(--border, $fallback--border); @@ -127,6 +128,7 @@ .thread-tree-replies-hidden { padding: var(--status-margin, $status-margin); + /* Make the button stretch along the whole row */ display: flex; align-items: stretch; diff --git a/src/components/timeline/timeline.scss b/src/components/timeline/timeline.scss index c6fb1ca7..4371947d 100644 --- a/src/components/timeline/timeline.scss +++ b/src/components/timeline/timeline.scss @@ -1,4 +1,4 @@ -@import '../../_variables.scss'; +@import "../../variables"; .Timeline { .alert-dot { @@ -46,10 +46,9 @@ text-align: center; line-height: 2.75em; padding: 0 0.5em; - } - .timeline-heading { - .button-default, .alert { + .button-default, + .alert { line-height: 2em; width: 100%; } diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue index e7250282..5f1da1f7 100644 --- a/src/components/timeline_menu/timeline_menu.vue +++ b/src/components/timeline_menu/timeline_menu.vue @@ -45,56 +45,7 @@ <script src="./timeline_menu.js"></script> <style lang="scss"> -@import '../../_variables.scss'; - -.TimelineMenu { - margin-right: auto; - min-width: 0; - - .popover-trigger-button { - vertical-align: bottom; - } - - .panel::after { - border-top-right-radius: 0; - border-top-left-radius: 0; - } - - .timeline-menu-title { - margin: 0; - cursor: pointer; - user-select: none; - width: 100%; - display: flex; - - .timeline-menu-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - svg { - margin-left: 0.6em; - transition: transform 100ms; - } - - .click-blocker { - cursor: default; - flex-grow: 1; - } - } - - &.open .timeline-menu-title svg { - color: $fallback--text; - color: var(--panelText, $fallback--text); - transform: rotate(180deg); - } - - .panel { - box-shadow: var(--popoverShadow); - } - -} +@import "../../variables"; .timeline-menu-popover { min-width: 24rem; @@ -110,24 +61,6 @@ padding: 0; } - li { - border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - padding: 0; - - &:last-child a { - border-bottom-right-radius: $fallback--panelRadius; - border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius); - border-bottom-left-radius: $fallback--panelRadius; - border-bottom-left-radius: var(--panelRadius, $fallback--panelRadius); - } - - &:last-child { - border: none; - } - } - a { display: block; padding: 0 0.65em; @@ -139,6 +72,7 @@ background-color: var(--selectedMenu, $fallback--lightBg); color: $fallback--link; color: var(--selectedMenuText, $fallback--link); + --faint: var(--selectedMenuFaintText, $fallback--faint); --faintLink: var(--selectedMenuFaintLink, $fallback--faint); --lightText: var(--selectedMenuLightText, $fallback--lightText); @@ -150,7 +84,9 @@ background-color: $fallback--lightBg; background-color: var(--selectedMenu, $fallback--lightBg); color: $fallback--text; - color: var(--selectedMenuText, $fallback--text); --faint: var(--selectedMenuFaintText, $fallback--faint); + color: var(--selectedMenuText, $fallback--text); + + --faint: var(--selectedMenuFaintText, $fallback--faint); --faintLink: var(--selectedMenuFaintLink, $fallback--faint); --lightText: var(--selectedMenuLightText, $fallback--lightText); --icon: var(--selectedMenuIcon, $fallback--icon); @@ -165,6 +101,71 @@ margin-left: -0.2em; } } + + li { + border-bottom: 1px solid; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + padding: 0; + + &:last-child a { + border-bottom-right-radius: $fallback--panelRadius; + border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius); + border-bottom-left-radius: $fallback--panelRadius; + border-bottom-left-radius: var(--panelRadius, $fallback--panelRadius); + } + + &:last-child { + border: none; + } + } } +.TimelineMenu { + margin-right: auto; + min-width: 0; + + .popover-trigger-button { + vertical-align: bottom; + } + + .panel::after { + border-top-right-radius: 0; + border-top-left-radius: 0; + } + + .timeline-menu-title { + margin: 0; + cursor: pointer; + user-select: none; + width: 100%; + display: flex; + + .timeline-menu-name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + svg { + margin-left: 0.6em; + transition: transform 100ms; + } + + .click-blocker { + cursor: default; + flex-grow: 1; + } + } + + &.open .timeline-menu-title svg { + color: $fallback--text; + color: var(--panelText, $fallback--text); + transform: rotate(180deg); + } + + .panel { + box-shadow: var(--popoverShadow); + } +} </style> diff --git a/src/components/update_notification/update_notification.scss b/src/components/update_notification/update_notification.scss index ce8129d0..4337acc4 100644 --- a/src/components/update_notification/update_notification.scss +++ b/src/components/update_notification/update_notification.scss @@ -1,4 +1,5 @@ -@import 'src/_variables.scss'; +@import "src/variables"; + .UpdateNotification { overflow: hidden; } @@ -21,7 +22,8 @@ @media all and (max-width: 800px) { /* For mobile, the modal takes 100% of the available screen. - This ensures the minimized modal is always 50px above the browser bottom bar regardless of whether or not it is visible. + This ensures the minimized modal is always 50px above the browser + bottom bar regardless of whether or not it is visible. */ width: 100vw; } @@ -44,7 +46,7 @@ } .panel-body { - border-width: 0 0 1px 0; + border-width: 0 0 1px; border-style: solid; border-color: var(--border, $fallback--border); } @@ -67,7 +69,7 @@ z-index: 20; position: relative; shape-margin: 0.5em; - filter: drop-shadow(5px 5px 10px rgba(0,0,0,0.5)); + filter: drop-shadow(5px 5px 10px rgb(0 0 0 / 50%)); pointer-events: none; } @@ -94,7 +96,7 @@ } &.-peek { - /* Explanation: + /* Explanation: * 100vh - 100% = Distance between modal's top+bottom boundaries and screen * (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen */ @@ -103,7 +105,7 @@ .pleroma-tan { float: right; z-index: 10; - shape-image-threshold: 0.7; + shape-image-threshold: 70%; } .extra-info-group { diff --git a/src/components/user_avatar/user_avatar.vue b/src/components/user_avatar/user_avatar.vue index f4d294df..91c17611 100644 --- a/src/components/user_avatar/user_avatar.vue +++ b/src/components/user_avatar/user_avatar.vue @@ -27,7 +27,7 @@ <script src="./user_avatar.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .Avatar { --_avatarShadowBox: var(--avatarStatusShadow); @@ -85,10 +85,9 @@ right: 0; margin: -0.2em; padding: 0.2em; - background: rgba(127, 127, 127, 0.5); + background: rgb(127 127 127 / 50%); color: #fff; border-radius: var(--tooltipRadius); } - } </style> diff --git a/src/components/user_card/user_card.scss b/src/components/user_card/user_card.scss index cdb8cb57..d56b6672 100644 --- a/src/components/user_card/user_card.scss +++ b/src/components/user_card/user_card.scss @@ -1,4 +1,4 @@ -@import '../../_variables.scss'; +@import "../../variables"; .user-card { position: relative; @@ -11,7 +11,7 @@ } .panel-heading { - padding: .5em 0; + padding: 0.5em 0; text-align: center; box-shadow: none; background: transparent; @@ -35,10 +35,11 @@ left: 0; right: 0; bottom: 0; - mask: linear-gradient(to top, white, transparent) bottom no-repeat, - linear-gradient(to top, white, white); + mask: + linear-gradient(to top, white, transparent) bottom no-repeat, + linear-gradient(to top, white, white); // Autoprefixer seem to ignore this one, and also syntax is different - -webkit-mask-composite: xor; + mask-composite: xor; mask-composite: exclude; background-size: cover; mask-size: 100% 60%; @@ -159,17 +160,17 @@ top: 0; right: 0; bottom: 0; - background-color: rgba(0, 0, 0, 0.3); + background-color: rgb(0 0 0 / 30%); display: flex; justify-content: center; align-items: center; border-radius: $fallback--avatarRadius; border-radius: var(--avatarRadius, $fallback--avatarRadius); opacity: 0; - transition: opacity .2s ease; + transition: opacity 0.2s ease; svg { - color: #FFF; + color: #fff; } } @@ -178,7 +179,8 @@ } } - .external-link-button, .edit-profile-button { + .external-link-button, + .edit-profile-button { cursor: pointer; width: 2.5em; text-align: center; @@ -191,34 +193,6 @@ } } - .user-summary { - display: block; - margin-left: 0.6em; - text-align: left; - text-overflow: ellipsis; - white-space: nowrap; - flex: 1 1 0; - // This is so that text doesn't get overlapped by avatar's shadow if it has - // big one - z-index: 1; - line-height: 2em; - - --emoji-size: 1.7em; - - .top-line, - .bottom-line { - display: flex; - } - } - - .user-name { - text-overflow: ellipsis; - overflow: hidden; - flex: 1 1 auto; - margin-right: 1em; - font-size: 1.1em; - } - .bottom-line { font-weight: light; font-size: 1.1em; @@ -253,8 +227,36 @@ } } + .user-summary { + display: block; + margin-left: 0.6em; + text-align: left; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1 1 0; + // This is so that text doesn't get overlapped by avatar's shadow if it has + // big one + z-index: 1; + line-height: 2em; + + --emoji-size: 1.7em; + + .top-line, + .bottom-line { + display: flex; + } + } + + .user-name { + text-overflow: ellipsis; + overflow: hidden; + flex: 1 1 auto; + margin-right: 1em; + font-size: 1.1em; + } + .user-meta { - margin-bottom: .15em; + margin-bottom: 0.15em; display: flex; align-items: baseline; line-height: 22px; @@ -263,7 +265,7 @@ .following { flex: 1 0 auto; margin: 0; - margin-bottom: .25em; + margin-bottom: 0.25em; text-align: left; } @@ -271,7 +273,7 @@ flex: 0 1 auto; display: flex; flex-wrap: wrap; - margin-right: -.5em; + margin-right: -0.5em; align-self: start; .userHighlightCl { @@ -294,19 +296,20 @@ .userHighlightText, .userHighlightSel { vertical-align: top; - margin-right: .5em; - margin-bottom: .25em; + margin-right: 0.5em; + margin-bottom: 0.25em; } } } + .user-interactions { position: relative; display: flex; flex-flow: row wrap; - margin-right: -.75em; + margin-right: -0.75em; > * { - margin: 0 .75em .6em 0; + margin: 0 0.75em 0.6em 0; white-space: nowrap; min-width: 95px; } @@ -317,7 +320,7 @@ } .user-note { - margin: 0 .75em .6em 0; + margin: 0 0.75em 0.6em 0; } } @@ -327,8 +330,8 @@ .user-counts { display: flex; - line-height:16px; - padding: .5em 1.5em 0em 1.5em; + line-height: 16px; + padding: 0.5em 1.5em 0; text-align: center; justify-content: space-between; color: $fallback--lightText; @@ -338,14 +341,16 @@ .user-count { flex: 1 0 auto; - padding: .5em 0 .5em 0; - margin: 0 .5em; + padding: 0.5em 0; + margin: 0 0.5em; h5 { - font-size:1em; + font-size: 1em; font-weight: bolder; margin: 0 0 0.25em; } + + /* stylelint-disable-next-line no-descending-specificity */ a { text-decoration: none; } diff --git a/src/components/user_list_popover/user_list_popover.vue b/src/components/user_list_popover/user_list_popover.vue index 635dc7f6..8307cc8a 100644 --- a/src/components/user_list_popover/user_list_popover.vue +++ b/src/components/user_list_popover/user_list_popover.vue @@ -48,7 +48,7 @@ <script src="./user_list_popover.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .user-list-popover { padding: 0.5em; diff --git a/src/components/user_note/user_note.vue b/src/components/user_note/user_note.vue index 4286e017..4e05951f 100644 --- a/src/components/user_note/user_note.vue +++ b/src/components/user_note/user_note.vue @@ -48,7 +48,7 @@ <script src="./user_note.js"></script> <style lang="scss"> -@import '../../variables'; +@import "../../variables"; .user-note { display: flex; diff --git a/src/components/user_popover/user_popover.vue b/src/components/user_popover/user_popover.vue index 53d51fc4..3b2bbc45 100644 --- a/src/components/user_popover/user_popover.vue +++ b/src/components/user_popover/user_popover.vue @@ -24,10 +24,12 @@ <script src="./user_popover.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; /* popover styles load on-demand, so we need to override */ +/* stylelint-disable block-no-empty */ .user-popover.popover { } +/* stylelint-enable block-no-empty */ </style> diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index d5e8d230..2720f052 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -140,7 +140,7 @@ <script src="./user_profile.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .user-profile { flex: 2; @@ -186,7 +186,8 @@ margin: 0 0 0 0.25em; } - .user-profile-field-name, .user-profile-field-value { + .user-profile-field-name, + .user-profile-field-value { line-height: 1.3; text-overflow: ellipsis; white-space: nowrap; @@ -204,6 +205,7 @@ padding: 2em; } } + .user-profile-placeholder { .panel-body { display: flex; diff --git a/src/components/user_reporting_modal/user_reporting_modal.vue b/src/components/user_reporting_modal/user_reporting_modal.vue index 8c42ab7b..092c514e 100644 --- a/src/components/user_reporting_modal/user_reporting_modal.vue +++ b/src/components/user_reporting_modal/user_reporting_modal.vue @@ -72,7 +72,7 @@ <script src="./user_reporting_modal.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import "../../variables"; .user-reporting-panel { width: 90vw; @@ -121,7 +121,7 @@ } .alert { - margin: 1em 0 0 0; + margin: 1em 0 0; line-height: 1.3em; } } diff --git a/src/components/who_to_follow/who_to_follow.vue b/src/components/who_to_follow/who_to_follow.vue index 3a17d0e2..56543d91 100644 --- a/src/components/who_to_follow/who_to_follow.vue +++ b/src/components/who_to_follow/who_to_follow.vue @@ -15,6 +15,3 @@ </template> <script src="./who_to_follow.js"></script> - -<style lang="scss"> -</style> diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.vue b/src/components/who_to_follow_panel/who_to_follow_panel.vue index c1ba6fb1..0fecec0b 100644 --- a/src/components/who_to_follow_panel/who_to_follow_panel.vue +++ b/src/components/who_to_follow_panel/who_to_follow_panel.vue @@ -33,24 +33,28 @@ .who-to-follow * { vertical-align: middle; } + .who-to-follow img { width: 32px; height: 32px; } + .who-to-follow { - padding: 0em 1em; - margin: 0px; + padding: 0 1em; + margin: 0; } + .who-to-follow-items { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - padding: 0px; - margin: 1em 0em; + padding: 0; + margin: 1em 0; } + .who-to-follow-more { - padding: 0px; - margin: 1em 0em; + padding: 0; + margin: 1em 0; text-align: center; } </style> diff --git a/src/hocs/with_load_more/with_load_more.scss b/src/hocs/with_load_more/with_load_more.scss index de86ed4a..bad852f4 100644 --- a/src/hocs/with_load_more/with_load_more.scss +++ b/src/hocs/with_load_more/with_load_more.scss @@ -1,5 +1,4 @@ - -@import '../../_variables.scss'; +@import "../../variables"; .with-load-more { &-footer { diff --git a/src/hocs/with_subscription/with_subscription.scss b/src/hocs/with_subscription/with_subscription.scss index 7fd83802..996045b4 100644 --- a/src/hocs/with_subscription/with_subscription.scss +++ b/src/hocs/with_subscription/with_subscription.scss @@ -7,4 +7,4 @@ font-size: 1rem; } } -}
\ No newline at end of file +} diff --git a/src/i18n/en.json b/src/i18n/en.json index 7df42d92..e780cea3 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -225,6 +225,7 @@ "search_emoji": "Search for an emoji", "add_emoji": "Insert emoji", "custom": "Custom emoji", + "unpacked": "Unpacked emoji", "unicode": "Unicode emoji", "unicode_groups": { "activities": "Activities", @@ -394,6 +395,8 @@ "account_backup_table_head": "Backup", "download_backup": "Download", "backup_not_ready": "This backup is not ready yet.", + "backup_running": "This backup is in progress, processed {number} record. | This backup is in progress, processed {number} records.", + "backup_failed": "This backup has failed.", "remove_backup": "Remove", "list_backups_error": "Error fetching backup list: {error}", "add_backup": "Create a new backup", diff --git a/src/i18n/eo.json b/src/i18n/eo.json index 5a2c8afb..e013edee 100644 --- a/src/i18n/eo.json +++ b/src/i18n/eo.json @@ -96,7 +96,7 @@ "friend_requests": "Petoj pri abono", "mentions": "Mencioj", "dms": "Rektaj mesaĝoj", - "public_tl": "Loka historio", + "public_tl": "Noda historio", "timeline": "Historio", "twkn": "Federa historio", "user_search": "Serĉi uzantojn", @@ -112,16 +112,19 @@ "edit_pinned": "Redakti fiksitajn erojn", "lists": "Listoj", "edit_nav_mobile": "Adapti navigan breton", - "edit_finish": "Fini redakton" + "edit_finish": "Fini redakton", + "mobile_notifications": "Malfermi sciigojn (estas nelegitaj)", + "mobile_notifications_close": "Fermi sciigojn", + "announcements": "Anoncoj" }, "notifications": { - "broken_favorite": "Nekonata stato, serĉante ĝin…", - "favorited_you": "ŝatis vian staton", + "broken_favorite": "Nekonata afiŝo, serĉante ĝin…", + "favorited_you": "ŝatis vian afiŝon", "followed_you": "ekabonis vin", "load_older": "Enlegi pli malnovajn sciigojn", "notifications": "Sciigoj", "read": "Legite!", - "repeated_you": "ripetis vian staton", + "repeated_you": "ripetis vian afiŝon", "no_more_notifications": "Neniuj pliaj sciigoj", "reacted_with": "reagis per {0}", "migrated_to": "migris al", @@ -131,7 +134,7 @@ "poll_ended": "enketo finiĝis" }, "post_status": { - "new_status": "Afiŝi novan staton", + "new_status": "Afiŝi", "account_not_locked_warning": "Via konto ne estas {0}. Iu ajn povas vin aboni por vidi eĉ viajn afiŝoj nur por abonantoj.", "account_not_locked_warning_link": "ŝlosita", "attachments_sensitive": "Marki kunsendaĵojn konsternaj", @@ -152,12 +155,12 @@ "unlisted": "Nelistigita – ne afiŝi al publikaj historioj" }, "scope_notice": { - "unlisted": "Ĉi tiu afiŝo ne estos videbla en la Loka historio kaj la Federa historio", + "unlisted": "Ĉi tiu afiŝo ne estos videbla en la Noda kaj la Federa historioj", "private": "Ĉi tiu afiŝo estos videbla nur al viaj abonantoj", "public": "Ĉi tiu afiŝo estos videbla al ĉiuj" }, "media_description_error": "Malsukcesis afiŝo de vidaŭdaĵoj; reprovu", - "empty_status_error": "Ne povas afiŝi malplenan staton sen dosieroj", + "empty_status_error": "Ne povas fari malplenan afiŝon sen dosieroj", "preview_empty": "Malplena", "preview": "Antaŭrigardo", "direct_warning_to_first_only": "Ĉi tiu afiŝo estas nur videbla al uzantoj menciitaj je la komenco de la mesaĝo.", @@ -166,7 +169,7 @@ "post": "Afiŝo", "edit_remote_warning": "Aliaj foraj nodoj eble ne subtenas redaktadon, kaj ne povos ricevi pli novan version de via afiŝo.", "edit_unsupported_warning": "Pleroma ne subtenas redaktadon de mencioj aŭ enketoj.", - "edit_status": "Stato de redakto" + "edit_status": "Redakti afiŝon" }, "registration": { "bio": "Priskribo", @@ -228,7 +231,7 @@ "avatar_size_instruction": "La rekomendata minimuma grando de profilbildoj estas 150×150 bilderoj.", "export_theme": "Konservi antaŭagordon", "filtering": "Filtrado", - "filtering_explanation": "Ĉiuj statoj kun tiuj ĉi vortoj silentiĝos; skribu po unu linie", + "filtering_explanation": "Ĉiuj afiŝoj kun tiuj ĉi vortoj silentiĝos; skribu po unu linie", "follow_export": "Elporto de abonoj", "follow_export_button": "Elporti viajn abonojn al CSV-dosiero", "follow_export_processing": "Traktante; baldaŭ vi ricevos peton elŝuti la dosieron", @@ -245,7 +248,7 @@ "use_one_click_nsfw": "Malfermi konsternajn kunsendaĵojn per nur unu klako", "hide_post_stats": "Kaŝi statistikon de afiŝoj (ekz. nombron de ŝatoj)", "hide_user_stats": "Kaŝi statistikon de uzantoj (ekz. nombron de abonantoj)", - "hide_filtered_statuses": "Kaŝi filtritajn statojn", + "hide_filtered_statuses": "Kaŝi ĉiujn filtritajn afiŝojn", "import_followers_from_a_csv_file": "Enporti abonojn el CSV-dosiero", "import_theme": "Enlegi antaŭagordojn", "inputRadius": "Enigaj kampoj", @@ -278,7 +281,7 @@ "hide_followers_description": "Ne montri kiu min sekvas", "show_admin_badge": "Montri la insignon de administranto en mia profilo", "show_moderator_badge": "Montri la insignon de reguligisto en mia profilo", - "nsfw_clickthrough": "Ŝalti traklakan kaŝadon de kunsendaĵoj kaj antaŭmontroj de ligiloj por konsternaj statoj", + "nsfw_clickthrough": "Ŝalti traklakan kaŝadon de kunsendaĵoj kaj antaŭmontroj de ligiloj por konsternaj afiŝoj", "oauth_tokens": "Pecoj de OAuth", "token": "Peco", "refresh_token": "Aktualiga peco", @@ -600,7 +603,7 @@ "use_websockets": "Uzi teĥnikaron «websockets» (tuja ĝisdatigo)", "mention_link_display_full_for_remote": "plene nur je uzantoj foraj (ekz. {'@'}zozo{'@'}ekzemplo.org)", "expert_mode": "Montri altnivelajn", - "setting_server_side": "Ĉi tiu agordo estas ligita al via profilo, kaj efektiviĝon en ĉiuj viaj salutoj kaj klientoj", + "setting_server_side": "Ĉi tiu agordo estas ligita al via profilo, kaj efektiviĝos en ĉiuj viaj salutoj kaj klientoj", "post_look_feel": "Aspekto de afiŝoj", "mention_links": "Menciaj ligiloj", "email_language": "Lingvo de leteroj ricevotaj de la servilo", @@ -627,14 +630,14 @@ "word_filter_and_more": "Vortofiltrado kaj pli…", "mute_bot_posts": "Silentigi afiŝojn de robotoj", "hide_bot_indication": "Kaŝi markon de roboteco en afiŝoj", - "hide_wordfiltered_statuses": "Kaŝi vorte filtritajn statojn", + "hide_wordfiltered_statuses": "Kaŝi vorte filtritajn afiŝojn", "hide_muted_threads": "Kaŝi silentigitajn fadenojn", "account_privacy": "Privateco", "user_profiles": "Profiloj de uzantoj", "hide_favorites_description": "Ne montri liston de miaj ŝatatoj (oni tamen sciiĝas)", "conversation_display_tree": "Arba stilo", "conversation_display_tree_quick": "Arba vido", - "show_scrollbars": "Montri rulumajn bretojn de flankaj kolumnoj", + "show_scrollbars": "Montri rulumskalojn de flankaj kolumnoj", "third_column_mode_none": "Neniam montri trian kolumnon", "third_column_mode_notifications": "Kolumno de sciigoj", "columns": "Kolumnoj", @@ -644,9 +647,9 @@ "column_sizes_notifs": "Sciigoj", "tree_advanced": "Permesi pli flekseblan navigadon en arba vido", "conversation_display_linear": "Linia stilo", - "conversation_other_replies_button": "Montri la butonon «aliaj respondoj»", - "conversation_other_replies_button_below": "Sub statoj", - "conversation_other_replies_button_inside": "En statoj", + "conversation_other_replies_button": "Montri la butonon pri «aliaj respondoj»", + "conversation_other_replies_button_below": "Sub afiŝoj", + "conversation_other_replies_button_inside": "En afiŝoj", "max_depth_in_thread": "Maksimuma nombro de niveloj implicite montrataj en fadeno", "auto_update": "Montri novajn afiŝojn memage", "use_at_icon": "Montri simbolon {'@'} kiel bildon anstataŭ teksto", @@ -662,19 +665,20 @@ "user_popover_avatar_action_open": "Malfermi la profilon", "user_popover_avatar_overlay": "Aperigi ŝprucaĵon pri uzanto sur profilbildo", "show_yous": "Montri la markon «(Vi)»", - "user_popover_avatar_action_zoom": "Zomi la profilbildon" + "user_popover_avatar_action_zoom": "Zomi la profilbildon", + "third_column_mode": "Kun sufiĉo da spaco, montri trian kolumnon kun" }, "timeline": { "collapse": "Maletendi", "conversation": "Interparolo", "error_fetching": "Eraris ĝisdatigo", - "load_older": "Montri pli malnovajn statojn", + "load_older": "Montri pli malnovajn afiŝojn", "no_retweet_hint": "Afiŝo estas markita kiel rekta aŭ nur por abonantoj, kaj ne eblas ĝin ripeti", "repeated": "ripetis", "show_new": "Montri novajn", "up_to_date": "Ĝisdata", - "no_more_statuses": "Neniuj pliaj statoj", - "no_statuses": "Neniuj statoj", + "no_more_statuses": "Neniuj pliaj afiŝoj", + "no_statuses": "Neniuj afiŝoj", "reload": "Enlegi ree", "error": "Eraris akirado de historio: {0}", "socket_reconnected": "Realtempa konekto fariĝis", @@ -700,7 +704,7 @@ "muted": "Silentigita", "per_day": "tage", "remote_follow": "Fore aboni", - "statuses": "Statoj", + "statuses": "Afiŝoj", "unblock": "Malbloki", "unblock_progress": "Malblokante…", "block_progress": "Blokante…", @@ -744,7 +748,12 @@ "edit_profile": "Redakti profilon", "deactivated": "Malaktiva", "follow_cancel": "Nuligi peton", - "remove_follower": "Forigi abonanton" + "remove_follower": "Forigi abonanton", + "note": "Noto", + "note_blank": "(Neniu)", + "edit_note_apply": "Apliki", + "edit_note_cancel": "Nuligi", + "edit_note": "Redakti noton" }, "user_profile": { "timeline_title": "Historio de uzanto", @@ -764,7 +773,9 @@ "bookmark": "Legosigno", "reject_follow_request": "Rifuzi abonpeton", "accept_follow_request": "Akcepti abonpeton", - "add_reaction": "Aldoni reagon" + "add_reaction": "Aldoni reagon", + "toggle_expand": "Etendi aŭ maletendi sciigon por montri plenan afiŝon", + "toggle_mute": "Etendi aŭ maletendi afiŝon por montri silentigitan enhavon" }, "upload": { "error": { @@ -893,19 +904,19 @@ "show_full_subject": "Montri plenan temon", "thread_muted_and_words": ", enhavas vortojn:", "thread_muted": "Fadeno silentigita", - "copy_link": "Kopii ligilon al stato", - "status_unavailable": "Stato ne estas disponebla", + "copy_link": "Kopii ligilon al afiŝo", + "status_unavailable": "Afiŝo ne estas disponebla", "unmute_conversation": "Malsilentigi interparolon", "mute_conversation": "Silentigi interparolon", "replies_list": "Respondoj:", "reply_to": "Responde al", - "delete_confirm": "Ĉu vi certe volas forigi ĉi tiun staton?", + "delete_confirm": "Ĉu vi certe volas forigi ĉi tiun afiŝon?", "unbookmark": "Senlegosigni", "bookmark": "Legosigni", "pinned": "Fiksita", "unpin": "Malfiksi de profilo", "pin": "Fiksi al profilo", - "delete": "Forigi staton", + "delete": "Forigi afiŝon", "repeats": "Ripetoj", "favorites": "Ŝatoj", "status_deleted": "Ĉi tiu afiŝo foriĝis", @@ -939,7 +950,8 @@ "ancestor_follow_with_icon": "{icon} {text}", "show_all_conversation_with_icon": "{icon} {text}", "show_only_conversation_under_this": "Montri nur respondojn al ĉi tiu afiŝo", - "status_history": "Historio de afiŝo" + "status_history": "Historio de afiŝo", + "open_gallery": "Malfermi galerion" }, "time": { "years_short": "{0}j", @@ -996,7 +1008,9 @@ "no_results": "Neniuj rezultoj", "people_talking": "{count} personoj parolas", "person_talking": "{count} persono parolas", - "hashtags": "Kradvortoj" + "hashtags": "Kradvortoj", + "no_more_results": "Neniuj pliaj rezultoj", + "load_more": "Enlegi pliajn rezultojn" }, "display_date": { "today": "Hodiaŭ" @@ -1047,9 +1061,9 @@ "report": { "reporter": "Raportinto:", "reported_user": "Raportito:", - "reported_statuses": "Raportitaj statoj:", + "reported_statuses": "Raportitaj afiŝoj:", "notes": "Notoj:", - "state": "Stato:", + "state": "Afiŝo:", "state_open": "Malfermita", "state_closed": "Fermita", "state_resolved": "Solvita" diff --git a/src/i18n/uk.json b/src/i18n/uk.json index c75ed197..c781b1f6 100644 --- a/src/i18n/uk.json +++ b/src/i18n/uk.json @@ -32,7 +32,13 @@ "private": "Лише читачі", "public": "Публічне", "unlisted": "Непублічне" - } + }, + "undo": "Відмінити", + "yes": "Так", + "no": "Ні", + "unpin": "Відкріпити", + "scroll_to_top": "Вгору", + "pin": "Прикріпити" }, "finder": { "error_fetching_user": "Користувача не знайдено", @@ -48,7 +54,7 @@ "media_proxy": "Посередник медіа-даних", "text_limit": "Ліміт символів", "upload_limit": "Обмеження завантажень", - "shout": "Оголошення" + "shout": "Гучномовець" }, "exporter": { "processing": "Опрацьовую, скоро ви зможете завантажити файл", @@ -61,7 +67,7 @@ "mute": "Ігнорувати" }, "shoutbox": { - "title": "Оголошення" + "title": "Гучномовець" }, "about": { "staff": "Адміністрація", @@ -81,7 +87,8 @@ "accept_desc": "Поточний інстанс приймає повідомлення тільки з перелічених інстансів:", "simple_policies": "Правила поточного інстансу", "reason": "Причина", - "not_applicable": "н/в" + "not_applicable": "н/в", + "instance": "Інстанс" }, "mrf_policies_desc": "Правила MRF розповсюджуються на даний інстанс. Наступні правила активні:", "mrf_policies": "Активувати правила MRF (модуль переписування повідомлень)", @@ -153,7 +160,8 @@ "favorited_you": "вподобав(-ла) ваш допис", "broken_favorite": "Невідомий допис, шукаю його…", "error": "Помилка при оновленні сповіщень: {0}", - "poll_ended": "опитування закінчено" + "poll_ended": "опитування закінчено", + "submitted_report": "подав скаргу" }, "nav": { "chats": "Чати", @@ -174,7 +182,14 @@ "back": "Назад", "administration": "Адміністрування", "home_timeline": "Домашня стрічка", - "lists": "Списки" + "lists": "Списки", + "edit_pinned": "Редагувати прикріплене", + "edit_finish": "Завершити редагування", + "mobile_sidebar": "Ввімкнути бокову панель", + "mobile_notifications": "Відкрити сповіщення (є непрочитані)", + "mobile_notifications_close": "Закрити сповіщення", + "edit_nav_mobile": "Редагувати панель навігації", + "announcements": "Анонси" }, "media_modal": { "next": "Наступна", @@ -221,7 +236,8 @@ "follows": "Нові підписки", "favs_repeats": "Поширення та вподобайки", "moves": "Міграції користувачів", - "emoji_reactions": "Емоджі реакції" + "emoji_reactions": "Емоджі реакції", + "reports": "Скарги" }, "errors": { "storage_unavailable": "Pleroma не змогла отримати доступ до сховища браузеру. Ваша сесія та налаштування не будуть збережені, це може спричинити непередбачувані проблеми. Спробуйте увімкнути cookie." @@ -235,7 +251,19 @@ "emoji": "Емодзі", "load_all": "Всі {emojiAmount} эмодзі завантажуються", "load_all_hint": "Завантажені перші {saneAmount} емодзі, завантаження всіх емодзі може призвести до проблем з продуктивністю.", - "unicode": "Стандартні емодзі" + "unicode": "Стандартні емодзі", + "regional_indicator": "Регіональний індикатор {letter}", + "unicode_groups": { + "animals-and-nature": "Тварини і Рослини", + "flags": "Прапори", + "food-and-drink": "Їжа та Напої", + "objects": "Об'єкти", + "people-and-body": "Люди та Тіло", + "smileys-and-emotion": "Смайлики та Емотікони", + "activities": "Активності", + "symbols": "Символи", + "travel-and-places": "Подорожі та Місця" + } }, "post_status": { "content_type": { @@ -269,7 +297,10 @@ "preview_empty": "Пустий", "media_description_error": "Не вдалось оновити медіа, спробуйте ще раз", "media_description": "Опис медіа", - "post": "Опублікувати" + "post": "Опублікувати", + "edit_unsupported_warning": "Pleroma не підтримує редагування згадувань чи голосувань.", + "edit_status": "Редагувати допис", + "edit_remote_warning": "Інші віддалені інстанси можуть не підтримувати редагування та вони можуть не отримати актуальну версію допису." }, "settings": { "blocks_imported": "Блокування імпортовані! Їх обробка триватиме певний час.", @@ -654,7 +685,7 @@ "backup_restore": "Резервне копіювання налаштувань" }, "right_sidebar": "Показувати бокову панель справа", - "hide_shoutbox": "Приховати оголошення інстансу", + "hide_shoutbox": "Приховати гучномовець", "setting_server_side": "Цей параметр прив’язаний до вашого профілю та впливає на всі сеанси та клієнти", "lists_navigation": "Показувати списки в навігації", "account_backup": "Резервне копіювання облікового запису", @@ -682,7 +713,33 @@ "move_account": "Перемістити обліковий запис", "move_account_target": "Цільовий обліковий запис (напр. {example})", "moved_account": "Обліковий запис переміщено.", - "move_account_error": "Помилка під час переміщення облікового запису: {error}" + "move_account_error": "Помилка під час переміщення облікового запису: {error}", + "word_filter_and_more": "Фільтр слів та більше...", + "hide_bot_indication": "Сховати позначку бот у дописах", + "navbar_column_stretch": "Розтягнути панель навігації на ширину колонок", + "hide_wordfiltered_statuses": "Ховати фільтровані статуси", + "hide_muted_threads": "Ховати приглушені треди", + "posts": "Дописи", + "account_privacy": "Безпека", + "conversation_display": "Стиль відображення розмови", + "conversation_display_tree": "Деревоподібне", + "conversation_display_tree_quick": "Вигляд дерева", + "disable_sticky_headers": "Не закріплювати заголовок колонки зверху на сторінці", + "third_column_mode_none": "Не показувати третю колонку взагалі", + "third_column_mode_notifications": "Колонка сповіщень", + "columns": "Колонки", + "auto_update": "Автоматично показувати нові дописи", + "use_websockets": "Використовувати вебсокети (Оновлення в реальному часі)", + "use_at_icon": "Показувати {'@'} символ як іконку замість тексту", + "mute_bot_posts": "Приховати дописи ботів", + "always_show_post_button": "Завжди показувати плаваючу кнопку «Новий Допис»", + "hide_favorites_description": "Не показувати список моїх вподобань (люди все одно отримують сповіщення)", + "third_column_mode": "Коли достатньо місця, показувати третю колонку, що містить", + "user_popover_avatar_action_open": "Відкрити профіль", + "wordfilter": "Фільтр слів", + "mention_links": "Посилання для згадування", + "user_profiles": "Профілі користувачів", + "notification_visibility_polls": "Закінчення опитувань, в яких ви проголосували" }, "selectable_list": { "select_all": "Вибрати все" @@ -781,7 +838,23 @@ "day": "{0} день", "seconds_short": "{0}с", "seconds": "{0} секунди", - "in_future": "через {0}" + "in_future": "через {0}", + "unit": { + "months": "{0} місяць | {0} місяців", + "minutes": "{0} хвилина | {0} хвилин", + "hours_short": "{0}год", + "minutes_short": "{0}хв", + "months_short": "{0}міс", + "seconds": "{0} секунда | {0} секунд", + "seconds_short": "{0}с", + "weeks_short": "{0}тижд", + "years": "{0} рік | {0} років", + "years_short": "{0}р.", + "days": "{0} день | {0} днів", + "days_short": "{0}д", + "hours": "{0} година | {0} годин", + "weeks": "{0} тиждень | {0} тижнів" + } }, "search": { "no_results": "Немає результатів", @@ -850,7 +923,9 @@ "disabled": "Не виділяти" }, "bot": "Бот", - "edit_profile": "Редагувати профіль" + "edit_profile": "Редагувати профіль", + "deactivated": "Деактивований", + "follow_cancel": "Скасувати запит" }, "status": { "copy_link": "Скопіювати посилання на допис", @@ -877,7 +952,16 @@ "thread_muted": "Нитка заглушена", "unmute_conversation": "Припинити глушити розмову", "external_source": "Зовнішнє джерело", - "expand": "Розгорнути" + "expand": "Розгорнути", + "edit": "Редагувати допис", + "edited_at": "(змінено: {time})", + "thread_follow_with_icon": "{icon} {text}", + "ancestor_follow_with_icon": "{icon} {text}", + "show_all_conversation_with_icon": "{icon} {text}", + "plus_more": "+{number} більше", + "thread_show_full_with_icon": "{icon} {text}", + "show_only_conversation_under_this": "Показати всі відповіді на цей допис", + "status_history": "Історія змін" }, "timeline": { "no_more_statuses": "Більше немає дописів", @@ -913,6 +997,30 @@ "state": "Статус:", "state_open": "відкритий", "state_closed": "закритий", - "state_resolved": "вирішений" + "state_resolved": "вирішений", + "reported_statuses": "Дописи, на які подано скаргу:", + "reporter": "Позивач:", + "reported_user": "Відповідач:" + }, + "announcements": { + "delete_action": "Видалити", + "page_header": "Анонси", + "title": "Анонси", + "mark_as_read_action": "Позначити як прочитане", + "post_form_header": "Розмістити оголошення", + "post_placeholder": "Введіть текст вашого оголошення тут...", + "post_action": "Пост", + "post_error": "Помилка: {error}", + "close_error": "Закрити", + "start_time_prompt": "Початок: ", + "end_time_prompt": "Кінець: ", + "all_day_prompt": "Це захід на цілий день", + "published_time_display": "Опубліковано в {time}", + "start_time_display": "Початок о {time}", + "end_time_display": "Кінець о {time}", + "edit_action": "Редагувати", + "submit_edit_action": "Надіслати", + "cancel_edit_action": "Скасувати", + "inactive_message": "Це оголошення неактивне" } } diff --git a/src/i18n/zh.json b/src/i18n/zh.json index cf5f384c..8f0b04bd 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -53,7 +53,13 @@ "direct": "私讯", "private": "仅关注者", "unlisted": "列外" - } + }, + "scroll_to_top": "滚动至顶", + "generic_error_message": "发生一个错误:{0}", + "never_show_again": "不再显示", + "undo": "撤销", + "yes": "是", + "no": "否" }, "image_cropper": { "crop_picture": "裁剪图片", @@ -109,7 +115,12 @@ "chats": "聊天", "timelines": "时间线", "bookmarks": "书签", - "home_timeline": "主页时间线" + "home_timeline": "主页时间线", + "lists": "列表", + "edit_finish": "完成编辑", + "mobile_notifications": "打开通知(有未读的)", + "mobile_notifications_close": "关闭通知", + "announcements": "公告" }, "notifications": { "broken_favorite": "未知的状态,正在搜索中…", @@ -890,7 +901,18 @@ "search_emoji": "搜索表情符号", "emoji": "表情符号", "load_all": "加载所有表情符号(共 {emojiAmount} 个)", - "load_all_hint": "最先加载的 {saneAmount} 表情符号,加载全部表情符号可能会带来性能问题。" + "load_all_hint": "最先加载的 {saneAmount} 表情符号,加载全部表情符号可能会带来性能问题。", + "unicode_groups": { + "flags": "旗帜", + "food-and-drink": "饮食", + "objects": "物件", + "people-and-body": "人和身体", + "symbols": "符号", + "travel-and-places": "旅行和地点", + "activities": "活动", + "animals-and-nature": "动物和自然" + }, + "regional_indicator": "地区指示符 {letter}" }, "about": { "mrf": { @@ -958,5 +980,26 @@ "delete": "删除", "message_user": "发消息给 {nickname}", "you": "你:" + }, + "announcements": { + "page_header": "公告", + "title": "公告", + "mark_as_read_action": "标为已读", + "post_form_header": "发布公告", + "post_placeholder": "在这里输入公告内容...", + "post_action": "发布", + "post_error": "错误:{error}", + "close_error": "关闭", + "delete_action": "删除", + "start_time_prompt": "起始时间: ", + "end_time_prompt": "终止时间: ", + "all_day_prompt": "这是全天的事件", + "published_time_display": "发表于 {time}", + "start_time_display": "开始于 {time}", + "end_time_display": "结束于 {time}", + "edit_action": "编辑", + "submit_edit_action": "提交", + "cancel_edit_action": "取消", + "inactive_message": "这个公告不活跃" } } diff --git a/src/modules/announcements.js b/src/modules/announcements.js index e4d2d2b0..4504263b 100644 --- a/src/modules/announcements.js +++ b/src/modules/announcements.js @@ -49,7 +49,7 @@ const announcements = { } const currentUser = store.rootState.users.currentUser - const isAdmin = currentUser && currentUser.role === 'admin' + const isAdmin = currentUser && currentUser.privileges.includes('announcements_manage_announcements') const getAnnouncements = async () => { if (!isAdmin) { diff --git a/src/modules/instance.js b/src/modules/instance.js index bff26151..9e47a13a 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -181,15 +181,28 @@ const instance = { }, groupedCustomEmojis (state) { const packsOf = emoji => { - return emoji.tags + const packs = emoji.tags .filter(k => k.startsWith('pack:')) - .map(k => k.slice(5)) // remove 'pack:' prefix + .map(k => { + const packName = k.slice(5) // remove 'pack:' prefix + return { + id: `custom-${packName}`, + text: packName + } + }) + + if (!packs.length) { + return [{ + id: 'unpacked' + }] + } else { + return packs + } } return state.customEmoji .reduce((res, emoji) => { - packsOf(emoji).forEach(packName => { - const packId = `custom-${packName}` + packsOf(emoji).forEach(({ id: packId, text: packName }) => { if (!res[packId]) { res[packId] = ({ id: packId, @@ -295,9 +308,22 @@ const instance = { const lb = b.toLowerCase() return la > lb ? 1 : (la < lb ? -1 : 0) } + const noPackLast = (a, b) => { + const aNull = a === '' + const bNull = b === '' + if (aNull === bNull) { + return 0 + } else if (aNull && !bNull) { + return 1 + } else { + return -1 + } + } const byPackThenByName = (a, b) => { const packOf = emoji => (emoji.tags.filter(k => k.startsWith('pack:'))[0] || '').slice(5) - return caseInsensitiveStrCmp(packOf(a), packOf(b)) || caseInsensitiveStrCmp(a.displayText, b.displayText) + const packOfA = packOf(a) + const packOfB = packOf(b) + return noPackLast(packOfA, packOfB) || caseInsensitiveStrCmp(packOfA, packOfB) || caseInsensitiveStrCmp(a.displayText, b.displayText) } const emoji = Object.entries(values).map(([key, value]) => { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 5a5c7b1b..77dd7e1c 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -765,6 +765,7 @@ const statuses = { return store.rootState.api.backendInteractor.search2({ q, resolve, limit, offset, following, type }) .then((data) => { store.commit('addNewUsers', data.accounts) + store.commit('addNewUsers', data.statuses.map(s => s.user).filter(u => u)) store.commit('addNewStatuses', { statuses: data.statuses }) return data }) diff --git a/src/panel.scss b/src/panel.scss index a53e47c6..9471da11 100644 --- a/src/panel.scss +++ b/src/panel.scss @@ -1,3 +1,4 @@ +/* stylelint-disable no-descending-specificity */ .panel { position: relative; display: flex; @@ -12,14 +13,14 @@ } &::after { - content: ''; + content: ""; position: absolute; top: 0; bottom: 0; left: 0; right: 0; z-index: 5; - box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); + box-shadow: 1px 1px 4px rgb(0 0 0 / 60%); box-shadow: var(--panelShadow); pointer-events: none; } @@ -87,7 +88,7 @@ &::after, &::before { - content: ''; + content: ""; position: absolute; top: 0; bottom: 0; @@ -126,7 +127,7 @@ .panel-heading { border-radius: $fallback--panelRadius $fallback--panelRadius 0 0; border-radius: var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius) 0 0; - border-width: 0 0 1px 0; + border-width: 0 0 1px; align-items: start; // panel theme color: var(--panelText); @@ -150,7 +151,7 @@ .button-unstyled:hover, a:hover { - i[class*=icon-], + i[class*="icon-"], .svg-inline--fa, .iconLetter { color: var(--panelText); @@ -173,7 +174,7 @@ flex-shrink: 0; &, - i[class*=icon-] { + i[class*="icon-"] { color: $fallback--text; color: var(--btnPanelText, $fallback--text); } @@ -234,7 +235,8 @@ border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); align-items: center; - border-width: 1px 0 0 0; + border-width: 1px 0 0; border-style: solid; border-color: var(--border, $fallback--border); } +/* stylelint-enable no-descending-specificity */ diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 7174cc5d..af12265e 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -164,7 +164,7 @@ const updateNotificationSettings = ({ credentials, settings }) => { form.append(key, value) }) - return fetch(NOTIFICATION_SETTINGS_URL, { + return fetch(`${NOTIFICATION_SETTINGS_URL}?${new URLSearchParams(settings)}`, { headers: authHeaders(credentials), method: 'PUT', body: form @@ -734,26 +734,22 @@ const fetchTimeline = ({ const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') url += `?${queryString}` - let status = '' - let statusText = '' - - let pagination = {} return fetch(url, { headers: authHeaders(credentials) }) - .then((data) => { - status = data.status - statusText = data.statusText - pagination = parseLinkHeaderPagination(data.headers.get('Link'), { - flakeId: timeline !== 'bookmarks' && timeline !== 'notifications' - }) - return data - }) - .then((data) => data.json()) - .then((data) => { - if (!data.errors) { + .then(async (response) => { + const success = response.ok + + const data = await response.json() + + if (success && !data.errors) { + const pagination = parseLinkHeaderPagination(response.headers.get('Link'), { + flakeId: timeline !== 'bookmarks' && timeline !== 'notifications' + }) + return { data: data.map(isNotifications ? parseNotification : parseStatus), pagination } } else { - data.status = status - data.statusText = statusText + data.errors ||= [] + data.status = response.status + data.statusText = response.statusText return data } }) |
