logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://anongit.hacktivis.me/git/pleroma-fe.git/
commit: 753f5fa65d5a5a163a9f7992a1bfaff45879dfc5
parent c16d71eb94db628ad4b6dda07f4023d6029db241
Author: HJ <30-hj@users.noreply.git.pleroma.social>
Date:   Tue, 28 Jan 2025 14:54:05 +0000

Merge branch 'fixes-roundup3' into 'develop'

Fixes Roundup 3

See merge request pleroma/pleroma-fe!2013

Diffstat:

Mbuild/build.js2+-
Mbuild/check-versions.js8+++-----
Mbuild/dev-server.js4++--
Mbuild/webpack.dev.conf.js3++-
Mbuild/webpack.prod.conf.js3++-
Achangelog.d/roundup3.skip0
Mconfig/index.js6+++---
Msrc/boot/after_store.js9+++------
Msrc/components/bookmark_folders_menu/bookmark_folders_menu_content.js3+++
Msrc/components/bookmark_folders_menu/bookmark_folders_menu_content.vue2++
Msrc/components/confirm_modal/mute_confirm.js4+---
Msrc/components/emoji_picker/emoji_picker.js2--
Msrc/components/mrf_transparency_panel/mrf_transparency_panel.vue204++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/components/nav_panel/nav_panel.vue1+
Msrc/components/navigation/navigation_pins.js9++++++++-
Msrc/components/navigation/navigation_pins.vue14+++++++++++++-
Msrc/components/quick_view_settings/quick_view_settings.vue1-
Msrc/components/rich_content/rich_content.scss4++++
Msrc/components/settings_modal/tabs/general_tab.js5+++++
Msrc/components/settings_modal/tabs/general_tab.vue28+++++++++++-----------------
Msrc/components/shadow_control/shadow_control.js2+-
Msrc/components/status/status.js26+++++++++++++++-----------
Msrc/components/status_action_buttons/action_button.js3+++
Msrc/components/status_action_buttons/action_button.scss51+++++++++++++++++++++++++++------------------------
Msrc/components/status_action_buttons/action_button.vue17+++++++++--------
Msrc/components/status_action_buttons/action_button_container.js4++--
Msrc/components/status_action_buttons/action_button_container.vue15+++++++++------
Msrc/components/status_action_buttons/status_action_buttons.js4----
Msrc/components/status_action_buttons/status_action_buttons.scss3+--
Msrc/components/timeago/timeago.vue15+++++++++++----
Msrc/components/user_card/user_card.vue2+-
Msrc/i18n/compare38+++++++++++++++++++-------------------
Msrc/i18n/en.json5++++-
Msrc/modules/config.js1+
Msrc/modules/instance.js10++++------
Msrc/modules/serverSideStorage.js15+++++----------
Msrc/modules/statuses.js8++++----
Msrc/services/color_convert/color_convert.js6+++---
Msrc/services/export_import/export_import.js2+-
Msrc/services/style_setter/style_setter.js5+++--
Mtest/unit/specs/components/rich_content.spec.js13++++++-------
Mtest/unit/specs/services/theme_data/iss_deserializer.spec.js18+++++++++---------
Mtools/emoji_merger.js2+-
43 files changed, 311 insertions(+), 266 deletions(-)

diff --git a/build/build.js b/build/build.js @@ -9,7 +9,7 @@ var ora = require('ora') var webpack = require('webpack') var webpackConfig = require('./webpack.prod.conf') -console.log( +console.info( ' Tip:\n' + ' Built files are meant to be served over an HTTP server.\n' + ' Opening index.html over file:// won\'t work.\n' diff --git a/build/check-versions.js b/build/check-versions.js @@ -27,14 +27,12 @@ module.exports = function () { } if (warnings.length) { - console.log('') - console.log(chalk.yellow('To use this template, you must update following to modules:')) - console.log() + console.warn(chalk.yellow('\nTo use this template, you must update following to modules:\n')) for (var i = 0; i < warnings.length; i++) { var warning = warnings[i] - console.log(' ' + warning) + console.warn(' ' + warning) } - console.log() + console.warn() process.exit(1) } } diff --git a/build/dev-server.js b/build/dev-server.js @@ -72,10 +72,10 @@ app.use(staticPath, express.static('./static')) module.exports = app.listen(port, function (err) { if (err) { - console.log(err) + console.error(err) return } var uri = 'http://localhost:' + port - console.log('Listening at ' + uri + '\n') + console.info('Listening at ' + uri + '\n') // opn(uri) }) diff --git a/build/webpack.dev.conf.js b/build/webpack.dev.conf.js @@ -23,7 +23,8 @@ module.exports = merge(baseWebpackConfig, { 'COMMIT_HASH': JSON.stringify('DEV'), 'DEV_OVERRIDES': JSON.stringify(config.dev.settings), '__VUE_OPTIONS_API__': true, - '__VUE_PROD_DEVTOOLS__': false + '__VUE_PROD_DEVTOOLS__': false, + '__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': false }), // https://github.com/glenjamin/webpack-hot-middleware#installation--usage new webpack.HotModuleReplacementPlugin(), diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js @@ -50,7 +50,8 @@ var webpackConfig = merge(baseWebpackConfig, { 'COMMIT_HASH': JSON.stringify(commitHash), 'DEV_OVERRIDES': JSON.stringify(undefined), '__VUE_OPTIONS_API__': true, - '__VUE_PROD_DEVTOOLS__': false + '__VUE_PROD_DEVTOOLS__': false, + '__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': false }), // extract css into its own file new MiniCssExtractPlugin({ diff --git a/changelog.d/roundup3.skip b/changelog.d/roundup3.skip diff --git a/config/index.js b/config/index.js @@ -8,10 +8,10 @@ try { // and that's how actual BE reports its url settings.target = settings.target.replace(/\/$/, '') } - console.log('Using local dev server settings (/config/local.json):') - console.log(JSON.stringify(settings, null, 2)) + console.info('Using local dev server settings (/config/local.json):') + console.info(JSON.stringify(settings, null, 2)) } catch (e) { - console.log('Local dev server settings not found (/config/local.json)') + console.info('Local dev server settings not found (/config/local.json)') } const target = settings.target || 'http://localhost:4000/' diff --git a/src/boot/after_store.js b/src/boot/after_store.js @@ -174,8 +174,7 @@ const getTOS = async ({ store }) => { throw (res) } } catch (e) { - console.warn("Can't load TOS") - console.warn(e) + console.warn("Can't load TOS\n", e) } } @@ -189,8 +188,7 @@ const getInstancePanel = async ({ store }) => { throw (res) } } catch (e) { - console.warn("Can't load instance panel") - console.warn(e) + console.warn("Can't load instance panel\n", e) } } @@ -220,8 +218,7 @@ const getStickers = async ({ store }) => { throw (res) } } catch (e) { - console.warn("Can't load stickers") - console.warn(e) + console.warn("Can't load stickers\n", e) } } diff --git a/src/components/bookmark_folders_menu/bookmark_folders_menu_content.js b/src/components/bookmark_folders_menu/bookmark_folders_menu_content.js @@ -3,6 +3,9 @@ import NavigationEntry from 'src/components/navigation/navigation_entry.vue' import { getBookmarkFolderEntries } from 'src/components/navigation/filter.js' export const BookmarkFoldersMenuContent = { + props: [ + 'showPin' + ], components: { NavigationEntry }, diff --git a/src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue b/src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue @@ -7,10 +7,12 @@ label: 'nav.all_bookmarks', icon: 'bookmark' }" + :show-pin="showPin" /> <NavigationEntry v-for="item in folders" :key="item.id" + :show-pin="showPin" :item="item" /> </ul> diff --git a/src/components/confirm_modal/mute_confirm.js b/src/components/confirm_modal/mute_confirm.js @@ -5,7 +5,7 @@ import ConfirmModal from './confirm_modal.vue' import Select from 'src/components/select/select.vue' export default { - props: ['type', 'user'], + props: ['type', 'user', 'status'], emits: ['hide', 'show', 'muted'], data: () => ({ showing: false, @@ -61,9 +61,7 @@ export default { }, methods: { optionallyPrompt () { - console.log('Triggered') if (this.shouldConfirm) { - console.log('SHAWN!!') this.show() } else { this.doMute() diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js @@ -162,7 +162,6 @@ const EmojiPicker = { } else { emojiSizeReal = emojiSizeValue } - console.log(emojiSizeReal) const fullEmojiSize = emojiSizeReal + (2 * 0.2 * fontSizeMultiplier * 14) this.emojiSize = fullEmojiSize @@ -319,7 +318,6 @@ const EmojiPicker = { return this.emojiSize }, itemPerRow () { - console.log('CALC', this.emojiSize, this.width) return this.width ? Math.floor(this.width / this.emojiSize) : 6 }, activeGroupView () { diff --git a/src/components/mrf_transparency_panel/mrf_transparency_panel.vue b/src/components/mrf_transparency_panel/mrf_transparency_panel.vue @@ -32,22 +32,24 @@ <p>{{ $t("about.mrf.simple.accept_desc") }}</p> <table> - <tr> - <th>{{ $t("about.mrf.simple.instance") }}</th> - <th>{{ $t("about.mrf.simple.reason") }}</th> - </tr> - <tr - v-for="entry in acceptInstances" - :key="entry.instance + '_accept'" - > - <td>{{ entry.instance }}</td> - <td v-if="entry.reason === ''"> - {{ $t("about.mrf.simple.not_applicable") }} - </td> - <td v-else> - {{ entry.reason }} - </td> - </tr> + <tbody> + <tr> + <th>{{ $t("about.mrf.simple.instance") }}</th> + <th>{{ $t("about.mrf.simple.reason") }}</th> + </tr> + <tr + v-for="entry in acceptInstances" + :key="entry.instance + '_accept'" + > + <td>{{ entry.instance }}</td> + <td v-if="entry.reason === ''"> + {{ $t("about.mrf.simple.not_applicable") }} + </td> + <td v-else> + {{ entry.reason }} + </td> + </tr> + </tbody> </table> </div> @@ -57,22 +59,24 @@ <p>{{ $t("about.mrf.simple.reject_desc") }}</p> <table> - <tr> - <th>{{ $t("about.mrf.simple.instance") }}</th> - <th>{{ $t("about.mrf.simple.reason") }}</th> - </tr> - <tr - v-for="entry in rejectInstances" - :key="entry.instance + '_reject'" - > - <td>{{ entry.instance }}</td> - <td v-if="entry.reason === ''"> - {{ $t("about.mrf.simple.not_applicable") }} - </td> - <td v-else> - {{ entry.reason }} - </td> - </tr> + <tbody> + <tr> + <th>{{ $t("about.mrf.simple.instance") }}</th> + <th>{{ $t("about.mrf.simple.reason") }}</th> + </tr> + <tr + v-for="entry in rejectInstances" + :key="entry.instance + '_reject'" + > + <td>{{ entry.instance }}</td> + <td v-if="entry.reason === ''"> + {{ $t("about.mrf.simple.not_applicable") }} + </td> + <td v-else> + {{ entry.reason }} + </td> + </tr> + </tbody> </table> </div> @@ -82,22 +86,24 @@ <p>{{ $t("about.mrf.simple.quarantine_desc") }}</p> <table> - <tr> - <th>{{ $t("about.mrf.simple.instance") }}</th> - <th>{{ $t("about.mrf.simple.reason") }}</th> - </tr> - <tr - v-for="entry in quarantineInstances" - :key="entry.instance + '_quarantine'" - > - <td>{{ entry.instance }}</td> - <td v-if="entry.reason === ''"> - {{ $t("about.mrf.simple.not_applicable") }} - </td> - <td v-else> - {{ entry.reason }} - </td> - </tr> + <tbody> + <tr> + <th>{{ $t("about.mrf.simple.instance") }}</th> + <th>{{ $t("about.mrf.simple.reason") }}</th> + </tr> + <tr + v-for="entry in quarantineInstances" + :key="entry.instance + '_quarantine'" + > + <td>{{ entry.instance }}</td> + <td v-if="entry.reason === ''"> + {{ $t("about.mrf.simple.not_applicable") }} + </td> + <td v-else> + {{ entry.reason }} + </td> + </tr> + </tbody> </table> </div> @@ -107,22 +113,24 @@ <p>{{ $t("about.mrf.simple.ftl_removal_desc") }}</p> <table> - <tr> - <th>{{ $t("about.mrf.simple.instance") }}</th> - <th>{{ $t("about.mrf.simple.reason") }}</th> - </tr> - <tr - v-for="entry in ftlRemovalInstances" - :key="entry.instance + '_ftl_removal'" - > - <td>{{ entry.instance }}</td> - <td v-if="entry.reason === ''"> - {{ $t("about.mrf.simple.not_applicable") }} - </td> - <td v-else> - {{ entry.reason }} - </td> - </tr> + <tbody> + <tr> + <th>{{ $t("about.mrf.simple.instance") }}</th> + <th>{{ $t("about.mrf.simple.reason") }}</th> + </tr> + <tr + v-for="entry in ftlRemovalInstances" + :key="entry.instance + '_ftl_removal'" + > + <td>{{ entry.instance }}</td> + <td v-if="entry.reason === ''"> + {{ $t("about.mrf.simple.not_applicable") }} + </td> + <td v-else> + {{ entry.reason }} + </td> + </tr> + </tbody> </table> </div> @@ -132,22 +140,24 @@ <p>{{ $t("about.mrf.simple.media_nsfw_desc") }}</p> <table> - <tr> - <th>{{ $t("about.mrf.simple.instance") }}</th> - <th>{{ $t("about.mrf.simple.reason") }}</th> - </tr> - <tr - v-for="entry in mediaNsfwInstances" - :key="entry.instance + '_media_nsfw'" - > - <td>{{ entry.instance }}</td> - <td v-if="entry.reason === ''"> - {{ $t("about.mrf.simple.not_applicable") }} - </td> - <td v-else> - {{ entry.reason }} - </td> - </tr> + <tbody> + <tr> + <th>{{ $t("about.mrf.simple.instance") }}</th> + <th>{{ $t("about.mrf.simple.reason") }}</th> + </tr> + <tr + v-for="entry in mediaNsfwInstances" + :key="entry.instance + '_media_nsfw'" + > + <td>{{ entry.instance }}</td> + <td v-if="entry.reason === ''"> + {{ $t("about.mrf.simple.not_applicable") }} + </td> + <td v-else> + {{ entry.reason }} + </td> + </tr> + </tbody> </table> </div> @@ -157,22 +167,24 @@ <p>{{ $t("about.mrf.simple.media_removal_desc") }}</p> <table> - <tr> - <th>{{ $t("about.mrf.simple.instance") }}</th> - <th>{{ $t("about.mrf.simple.reason") }}</th> - </tr> - <tr - v-for="entry in mediaRemovalInstances" - :key="entry.instance + '_media_removal'" - > - <td>{{ entry.instance }}</td> - <td v-if="entry.reason === ''"> - {{ $t("about.mrf.simple.not_applicable") }} - </td> - <td v-else> - {{ entry.reason }} - </td> - </tr> + <tbody> + <tr> + <th>{{ $t("about.mrf.simple.instance") }}</th> + <th>{{ $t("about.mrf.simple.reason") }}</th> + </tr> + <tr + v-for="entry in mediaRemovalInstances" + :key="entry.instance + '_media_removal'" + > + <td>{{ entry.instance }}</td> + <td v-if="entry.reason === ''"> + {{ $t("about.mrf.simple.not_applicable") }} + </td> + <td v-else> + {{ entry.reason }} + </td> + </tr> + </tbody> </table> </div> diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue @@ -113,6 +113,7 @@ :class="{ '-expanded': showBookmarkFolders }" > <BookmarkFoldersMenuContent + :show-pin="editMode || forceEditMode" class="timelines" /> </div> diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js @@ -1,6 +1,8 @@ import { mapState } from 'vuex' import { TIMELINES, ROOT_ITEMS, routeTo } from 'src/components/navigation/navigation.js' -import { getListEntries, filterNavigation } from 'src/components/navigation/filter.js' +import { getBookmarkFolderEntries, getListEntries, filterNavigation } from 'src/components/navigation/filter.js' + +import StillImage from 'src/components/still-image/still-image.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -34,12 +36,16 @@ const NavPanel = { return routeTo(item, this.currentUser) } }, + components: { + StillImage + }, computed: { getters () { return this.$store.getters }, ...mapState({ lists: getListEntries, + bookmarks: getBookmarkFolderEntries, currentUser: state => state.users.currentUser, followRequestCount: state => state.api.followRequests.length, privateMode: state => state.instance.private, @@ -70,6 +76,7 @@ const NavPanel = { .filter(([k]) => this.pinnedItems.has(k)) .map(([k, v]) => ({ ...v, name: k })), ...this.lists.filter((k) => this.pinnedItems.has(k.name)), + ...this.bookmarks.filter((k) => this.pinnedItems.has(k.name)), ...Object .entries({ ...ROOT_ITEMS }) .filter(([k]) => this.pinnedItems.has(k)) diff --git a/src/components/navigation/navigation_pins.vue b/src/components/navigation/navigation_pins.vue @@ -14,9 +14,14 @@ :icon="item.icon" /> <span - v-if="item.iconLetter" + v-if="item.iconLetter && !item.iconEmoji" class="iconLetter fa-scale-110 fa-old-padding" >{{ item.iconLetter }}</span> + <StillImage + v-if="item.iconEmoji" + class="bookmark-emoji" + :src="item.iconEmojiUrl" + /> <div v-if="item.badgeGetter && getters[item.badgeGetter]" class="badge -dot" @@ -52,6 +57,13 @@ box-sizing: border-box; height: 100%; + .bookmark-emoji { + height: 100%; + box-sizing: border-box; + padding: 0.5em; + } + + & .bookmark-emoji, & .svg-inline--fa, & .iconLetter { margin: 0; diff --git a/src/components/quick_view_settings/quick_view_settings.vue b/src/components/quick_view_settings/quick_view_settings.vue @@ -3,7 +3,6 @@ trigger="click" class="QuickViewSettings" :bound-to="{ x: 'container' }" - :trigger-attrs="triggerAttrs" > <template #content> <div diff --git a/src/components/rich_content/rich_content.scss b/src/components/rich_content/rich_content.scss @@ -8,6 +8,10 @@ --funtextGreentext: var(--funtextGreentextFaint) !important; --funtextCyantext: var(--funtextCyantextFaint) !important; /* stylelint-enable declaration-no-important */ + + a { + color: var(--linkFaint); + } } blockquote { diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js @@ -30,6 +30,11 @@ const GeneralTab = { value: mode, label: this.$t(`settings.conversation_display_${mode}`) })), + absoluteTime12hOptions: ['24h', '12h'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`settings.absolute_time_format_12h_${mode}`) + })), conversationOtherRepliesButtonOptions: ['below', 'inside'].map(mode => ({ key: mode, value: mode, diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue @@ -249,6 +249,16 @@ {{ $t('settings.absolute_time_format_min_age') }} </UnitSetting> </li> + <li> + <ChoiceSetting + id="absoluteTime12h" + path="absoluteTime12h" + :options="absoluteTime12hOptions" + :expert="1" + > + {{ $t('settings.absolute_time_format_12h') }} + </ChoiceSetting> + </li> </ul> <h3>{{ $t('settings.attachments') }}</h3> <li> @@ -464,22 +474,6 @@ </li> <li> <BooleanSetting - path="alwaysShowNewPostButton" - expert="1" - > - {{ $t('settings.always_show_post_button') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting - path="autohideFloatingPostButton" - expert="1" - > - {{ $t('settings.autohide_floating_post_button') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="padEmoji" expert="1" > @@ -501,7 +495,7 @@ {{ $t('settings.auto_save_draft') }} </BooleanSetting> </li> - <li v-if="!autoSaveDraft"> + <li v-if="!mergedConfig.autoSaveDraft"> <ChoiceSetting id="unsavedPostAction" path="unsavedPostAction" diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js @@ -130,7 +130,7 @@ export default { const serialized = this.cValue.map(x => serializeShadow(x)).join(',') serialized.split(/,/).map(deserializeShadow) // validate const expandedShadow = flattenDeep(findShadow(this.cValue, { dynamicVars: {}, staticVars: this.staticVars })) - const fixedShadows = expandedShadow.map(x => ({ ...x, color: console.log(x) || rgb2hex(x.color) })) + const fixedShadows = expandedShadow.map(x => ({ ...x, color: rgb2hex(x.color) })) if (this.separateInset) { result = { diff --git a/src/components/status/status.js b/src/components/status/status.js @@ -255,7 +255,7 @@ const Status = { muteReasons () { return [ this.userIsMuted ? 'user' : null, - status.thread_muted ? 'thread' : null, + this.status.thread_muted ? 'thread' : null, (this.muteWordHits.length > 0) ? 'wordfilter' : null, (this.muteBotStatuses && this.botStatus) ? 'bot' : null, (this.muteSensitiveStatuses && this.sensitiveStatus) ? 'nsfw' : null @@ -280,14 +280,18 @@ const Status = { case 'nsfw': return this.$t('status.sensitive_muted') } } - return this.$t( - 'status.multi_reason_mute', - { - main: mainReason(), - numReasonsMore: this.muteReasons.length - 1 - }, - this.muteReasons.length - 1 - ) + if (this.muteReasons.length > 1) { + return this.$t( + 'status.multi_reason_mute', + { + main: mainReason(), + numReasonsMore: this.muteReasons.length - 1 + }, + this.muteReasons.length - 1 + ) + } else { + return mainReason() + } }, muted () { if (this.statusoid.user.id === this.currentUser.id) return false @@ -299,9 +303,9 @@ const Status = { const { reblog } = status const relationship = this.$store.getters.relationship(status.user.id) const relationshipReblog = reblog && this.$store.getters.relationship(reblog.user.id) - return status.muted || + return (status.muted && !status.thread_muted) || // Reprööt of a muted post according to BE - (reblog && reblog.muted) || + (reblog && reblog.muted && !reblog.thread_muted) || // Muted user relationship.muting || // Muted user of a reprööt diff --git a/src/components/status_action_buttons/action_button.js b/src/components/status_action_buttons/action_button.js @@ -92,6 +92,9 @@ export default { threadIsMuted () { return this.status.thread_muted }, + hideCustomEmoji () { + return !this.$store.state.instance.pleromaCustomEmojiReactionsAvailable + }, buttonInnerClass () { return [ this.button.name + '-button', diff --git a/src/components/status_action_buttons/action_button.scss b/src/components/status_action_buttons/action_button.scss @@ -2,9 +2,9 @@ /* stylelint-disable declaration-no-important */ .quick-action { - display: grid; - grid-template-columns: minmax(max-content, 1fr); - grid-gap: 0.25em; + justify-content: space-between; + display: flex; + align-items: baseline; align-items: center; height: 1.5em; @@ -12,6 +12,7 @@ overflow-x: hidden; text-overflow: ellipsis; white-space: nowrap; + margin-left: 1em; } .action-button-inner, @@ -25,6 +26,7 @@ align-self: stretch; width: 1px; background-color: var(--icon); + margin-left: 1em; margin-right: 0.5em; } @@ -35,6 +37,7 @@ border-radius: var(--roundness); grid-template-columns: minmax(max-content, 1fr) auto; + .chevron-icon, .extra-button, .separator { display: none; @@ -44,10 +47,30 @@ .action-button-inner { display: grid; grid-gap: 1em; - grid-template-columns: max-content 1fr; + grid-template-columns: max-content; grid-auto-flow: column; grid-auto-columns: max-content; align-items: center; + + @include unfocused-style { + .focus-marker { + visibility: hidden; + } + + .active-marker { + visibility: visible; + } + } + + @include focused-style { + .focus-marker { + visibility: visible; + } + + .active-marker { + visibility: hidden; + } + } } } @@ -79,24 +102,4 @@ } } } - - @include unfocused-style { - .focus-marker { - visibility: hidden; - } - - .active-marker { - visibility: visible; - } - } - - @include focused-style { - .focus-marker { - visibility: visible; - } - - .active-marker { - visibility: hidden; - } - } } diff --git a/src/components/status_action_buttons/action_button.vue b/src/components/status_action_buttons/action_button.vue @@ -9,6 +9,7 @@ :class="buttonInnerClass" role="menuitem" type="button" + :title="$t(button.label(funcArg))" target="_blank" :tabindex="0" :disabled="buttonClass.disabled" @@ -50,12 +51,6 @@ > {{ $t(button.label(funcArg)) }} </span> - <span - v-if="!extra && button.counter?.(funcArg) > 0" - class="action-counter" - > - {{ button.counter?.(funcArg) }} - </span> <FAIcon v-if="button.dropdown?.()" class="chevron-icon" @@ -65,14 +60,20 @@ /> </component> <span + v-if="!extra && button.counter?.(funcArg) > 0" + class="action-counter" + > + {{ button.counter?.(funcArg) }} + </span> + <span v-if="!extra && button.name === 'bookmark'" class="separator" /> <Popover v-if="button.name === 'bookmark'" - trigger="hover" + :trigger="extra ? 'hover' : 'click'" :placement="extra ? 'right' : 'top'" - :offset="{ y: 5 }" + :offset="extra ? { x: 10 } : { y: 10 }" :trigger-attrs="{ class: 'extra-button' }" > <template #trigger> diff --git a/src/components/status_action_buttons/action_button_container.js b/src/components/status_action_buttons/action_button_container.js @@ -58,8 +58,8 @@ export default { unmuteUser () { return this.$store.dispatch('unmuteUser', this.user.id) }, - unmuteThread () { - return this.$store.dispatch('unmuteConversation', this.user.id) + unmuteConversation () { + return this.$store.dispatch('unmuteConversation', { id: this.status.id }) }, unmuteDomain () { return this.$store.dispatch('unmuteDomain', this.user.id) diff --git a/src/components/status_action_buttons/action_button_container.vue b/src/components/status_action_buttons/action_button_container.vue @@ -2,7 +2,7 @@ <div> <Popover v-if="button.dropdown?.()" - trigger="hover" + :trigger="$attrs.extra ? 'hover' : 'click'" :offset="{ y: 5 }" :placement="$attrs.extra ? 'right' : 'top'" > @@ -40,13 +40,13 @@ <div class="menu-item dropdown-item extra-action -icon"> <button class="main-button" - @click="toggleUserMute" + @click="toggleConversationMute" > <FAIcon icon="folder-tree" fixed-width /> - <template v-if="threadIsMuted"> + <template v-if="conversationIsMuted"> {{ $t('status.unmute_conversation') }} </template> <template v-else> @@ -81,19 +81,22 @@ v-bind="$attrs" /> <teleport to="#modal"> - <mute-confirm + <MuteConfirm ref="confirmConversation" type="conversation" :status="status" + :user="user" /> - <mute-confirm + <MuteConfirm ref="confirmDomain" type="domain" + :status="status" :user="user" /> - <mute-confirm + <MuteConfirm ref="confirmUser" type="user" + :status="status" :user="user" /> </teleport> diff --git a/src/components/status_action_buttons/status_action_buttons.js b/src/components/status_action_buttons/status_action_buttons.js @@ -51,9 +51,6 @@ const StatusActionButtons = { currentUser () { return this.$store.state.users.currentUser }, - hideCustomEmoji () { - return !this.$store.state.instance.pleromaCustomEmojiReactionsAvailable - }, funcArg () { return { status: this.status, @@ -71,7 +68,6 @@ const StatusActionButtons = { return { title: this.$t('status.more_actions'), 'aria-controls': `popup-menu-${this.randomSeed}`, - 'aria-expanded': this.expanded, 'aria-haspopup': 'menu' } } diff --git a/src/components/status_action_buttons/status_action_buttons.scss b/src/components/status_action_buttons/status_action_buttons.scss @@ -3,12 +3,11 @@ .StatusActionButtons { .quick-action-buttons { display: grid; - grid-template-columns: repeat(auto-fill, 5em); + grid-template-columns: repeat(auto-fill, 4em); grid-auto-flow: row dense; grid-auto-rows: 1fr; grid-gap: 1.25em 1em; margin-top: var(--status-margin); - align-items: baseline; } .pin-action-button { diff --git a/src/components/timeago/timeago.vue b/src/components/timeago/timeago.vue @@ -28,6 +28,9 @@ export default { } return DateUtils.durationStrToMs(this.$store.getters.mergedConfig.absoluteTimeFormatMinAge) <= this.relativeTimeMs }, + time12hFormat () { + return this.$store.getters.mergedConfig.absoluteTimeFormat12h === '12h' + }, browserLocale () { return localeService.internalToBrowserLocale(this.$i18n.locale) }, @@ -57,22 +60,26 @@ export default { if (DateUtils.isSameDay(this.timeAsDate, now)) { return new Intl.DateTimeFormat(this.browserLocale, { minute: 'numeric', - hour: 'numeric' + hour: 'numeric', + hour12: this.time12hFormat }) } else if (DateUtils.isSameMonth(this.timeAsDate, now)) { return new Intl.DateTimeFormat(this.browserLocale, { month: 'short', - day: 'numeric' + day: 'numeric', + hour12: this.time12hFormat }) } else if (DateUtils.isSameYear(this.timeAsDate, now)) { return new Intl.DateTimeFormat(this.browserLocale, { month: 'short', - day: 'numeric' + day: 'numeric', + hour12: this.time12hFormat }) } else { return new Intl.DateTimeFormat(this.browserLocale, { year: 'numeric', - month: 'short' + month: 'short', + hour12: this.time12hFormat }) } })() diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue @@ -311,7 +311,7 @@ /> </div> <teleport to="#modal"> - <mute-confirm + <MuteConfirm ref="confirmation" type="user" :user="user" diff --git a/src/i18n/compare b/src/i18n/compare @@ -2,23 +2,23 @@ const arg = process.argv[2] if (typeof arg === 'undefined') { - console.log('This is a very simple and tiny tool that checks en.json with any other language and') - console.log('outputs all the things present in english but missing in foreign language.') - console.log('') - console.log('Usage: ./compare.js <lang> ') - console.log(' or') - console.log(' node ./compare.js <lang>') - console.log('') - console.log('Where <lang> is name of .json file containing language. For ./fi.json it should be:') - console.log(' ./compare.js fi ') - console.log('') - console.log('Limitations: ') - console.log('* This program does not work with languages left over in messages.js') - console.log('* This program does not check for extra strings present in foreign language but missing') - console.log(' in english.js (for now)') - console.log('') - console.log('There are no other arguments or options. Make an issue if you encounter a bug or want') - console.log('some feature to be implemented. Merge requests are welcome as well.') + console.info('This is a very simple and tiny tool that checks en.json with any other language and') + console.info('outputs all the things present in english but missing in foreign language.') + console.info('') + console.info('Usage: ./compare.js <lang> ') + console.info(' or') + console.info(' node ./compare.js <lang>') + console.info('') + console.info('Where <lang> is name of .json file containing language. For ./fi.json it should be:') + console.info(' ./compare.js fi ') + console.info('') + console.info('Limitations: ') + console.info('* This program does not work with languages left over in messages.js') + console.info('* This program does not check for extra strings present in foreign language but missing') + console.info(' in english.js (for now)') + console.info('') + console.info('There are no other arguments or options. Make an issue if you encounter a bug or want') + console.info('some feature to be implemented. Merge requests are welcome as well.') process.exit() } @@ -35,10 +35,10 @@ function walker (a, b, path = []) { const article = aType[0] === 'o' ? 'an' : 'a' if (bType === 'undefined') { - console.log(`Foreign language is missing ${article} ${aType} at path ${currentPath.join('.')}`) + console.warn(`Foreign language is missing ${article} ${aType} at path ${currentPath.join('.')}`) } else if (aType === 'object') { if (bType !== 'object') { - console.log(`Type mismatch! English has ${aType} while foreign has ${bType} at path ${currentPath.join['.']}`) + console.warn(`Type mismatch! English has ${aType} while foreign has ${bType} at path ${currentPath.join['.']}`) } else { walker(aVal, bVal, currentPath) } diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -532,6 +532,9 @@ "emoji_reactions_scale": "Reactions scale factor", "absolute_time_format": "Use absolute time format", "absolute_time_format_min_age": "Only use for time older than this amount of time", + "absolute_time_format_12h": "Time format", + "absolute_time_format_12h_12h": "12 hour format (i.e. 10:00 PM)", + "absolute_time_format_12h_24h": "24 hour format (i.e. 22:00)", "export_theme": "Save preset", "filtering": "Filtering", "wordfilter": "Wordfilter", @@ -1256,7 +1259,7 @@ "copy_link": "Copy link to status", "external_source": "External source", "muted_words": "Wordfiltered: {word} | Wordfiltered: {word} and {numWordsMore} more words", - "multi_reason_mute": "{main} | {main} + one more reason | {main} + {numReasonsMore} more reasons", + "multi_reason_mute": "{main} + one more reason | {main} + {numReasonsMore} more reasons", "muted_user": "User muted", "thread_muted": "Thread muted", "thread_muted_and_words": ", has words:", diff --git a/src/modules/config.js b/src/modules/config.js @@ -193,6 +193,7 @@ export const defaultState = { autoSaveDraft: undefined, // instance default useAbsoluteTimeFormat: undefined, // instance default absoluteTimeFormatMinAge: undefined, // instance default + absoluteTime12h: undefined, // instance default imageCompression: true } diff --git a/src/modules/instance.js b/src/modules/instance.js @@ -131,6 +131,7 @@ const defaultState = { autoSaveDraft: false, useAbsoluteTimeFormat: false, absoluteTimeFormatMinAge: '0d', + absoluteTime12h: '24h', // Nasty stuff customEmoji: [], @@ -314,8 +315,7 @@ const instance = { }, {}) commit('setInstanceOption', { name: 'emoji', value: injectRegionalIndicators(emoji) }) } catch (e) { - console.warn("Can't load static emoji") - console.warn(e) + console.warn("Can't load static emoji\n", e) } }, @@ -382,8 +382,7 @@ const instance = { throw (res) } } catch (e) { - console.warn("Can't load custom emojis") - console.warn(e) + console.warn("Can't load custom emojis\n", e) } }, fetchEmoji ({ dispatch, state }) { @@ -404,8 +403,7 @@ const instance = { }) commit('setKnownDomains', result) } catch (e) { - console.warn("Can't load known domains") - console.warn(e) + console.warn("Can't load known domains\n", e) } } } diff --git a/src/modules/serverSideStorage.js b/src/modules/serverSideStorage.js @@ -9,7 +9,8 @@ import { groupBy, findLastIndex, takeRight, - uniqWith + uniqWith, + merge as _merge } from 'lodash' import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js' @@ -127,14 +128,8 @@ export const _getRecentData = (cache, live, isTest) => { _version: a._version ?? b._version, _timestamp: a._timestamp ?? b._timestamp, needUpload: b.needUpload ?? a.needUpload, - prefsStorage: { - ...a.prefsStorage, - ...b.prefsStorage - }, - flagStorage: { - ...a.flagStorage, - ...b.flagStorage - } + prefsStorage: _merge(a.prefsStorage, b.prefsStorage), + flagStorage: _merge(a.flagStorage, b.flagStorage) }) result.recent = isTest ? result.recent : (result.recent && merge(defaultState, result.recent)) result.stale = isTest ? result.stale : (result.stale && merge(defaultState, result.stale)) @@ -315,7 +310,7 @@ export const mutations = { state.raw = live let cache = state.cache if (cache && cache._user !== userData.fqn) { - console.warn('cache belongs to another user! reinitializing local cache!') + console.warn('Cache belongs to another user! reinitializing local cache!') cache = null } diff --git a/src/modules/statuses.js b/src/modules/statuses.js @@ -276,8 +276,8 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us // NOOP, it is known status but we don't do anything about it for now }, default: (unknown) => { - console.log('unknown status type') - console.log(unknown) + console.warn('unknown status type') + console.warn(unknown) } } @@ -549,11 +549,11 @@ const statuses = { rootState.api.backendInteractor.unpinOwnStatus({ id: statusId }) .then((status) => dispatch('addNewStatuses', { statuses: [status] })) }, - muteConversation ({ rootState, commit }, statusId) { + muteConversation ({ rootState, commit }, { id: statusId }) { return rootState.api.backendInteractor.muteConversation({ id: statusId }) .then((status) => commit('setMutedStatus', status)) }, - unmuteConversation ({ rootState, commit }, statusId) { + unmuteConversation ({ rootState, commit }, { id: statusId }) { return rootState.api.backendInteractor.unmuteConversation({ id: statusId }) .then((status) => commit('setMutedStatus', status)) }, diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js @@ -1,7 +1,7 @@ import { invertLightness, contrastRatio, convert } from 'chromatism' // useful for visualizing color when debugging -export const consoleColor = (color) => console.log('%c##########', 'background: ' + color + '; color: ' + color) +// const consoleColor = (color) => console.debug('%c##########', 'background: ' + color + '; color: ' + color) /** * Convert r, g, b values into hex notation. All components are [0-255] @@ -236,8 +236,8 @@ export const getTextColor = function (bg, text, preserve) { let contrast = getContrastRatio(bg, text) const result = convert(rgb2hex(workColor)).hsl - const delta = result.l > 50 ? 1 : -1 - const multiplier = 10 + const delta = result.l >= 50 ? 1 : -1 + const multiplier = 1 while (contrast < 4.5 && result.l > 20 && result.l < 80) { result.l += delta * multiplier result.l = Math.min(100, Math.max(0, result.l)) diff --git a/src/services/export_import/export_import.js b/src/services/export_import/export_import.js @@ -3,7 +3,7 @@ import utf8 from 'utf8' export const newExporter = ({ filename = 'data', mime = 'application/json', - extension = '.json', + extension = 'json', getExportedObject }) => ({ exportData () { diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js @@ -95,9 +95,10 @@ export const tryLoadCache = async () => { if (!data) return null let cache try { - const decoded = new TextDecoder().decode(pako.inflate(data)) + const inflated = pako.inflate(data) + const decoded = new TextDecoder().decode(inflated) cache = JSON.parse(decoded) - console.info(`Loaded theme from cache, size=${cache}`) + console.info(`Loaded theme from cache, compressed=${Math.ceil(data.length / 1024)}kiB size=${Math.ceil(inflated.length / 1024)}kiB`) } catch (e) { console.error('Failed to decode theme cache:', e) return false diff --git a/test/unit/specs/components/rich_content.spec.js b/test/unit/specs/components/rich_content.spec.js @@ -539,7 +539,6 @@ describe('RichContent', () => { `, props: ['handleLinks', 'attentions', 'vhtml'] } - console.log(1) const ptest = (handleLinks, vhtml) => { const t0 = performance.now() @@ -562,11 +561,11 @@ describe('RichContent', () => { return `Mount: ${t1 - t0}ms, destroy: ${t2 - t1}ms, avg ${(t1 - t0) / amount}ms - ${(t2 - t1) / amount}ms per item` } - console.log(`${amount} items with links handling:`) - console.log(ptest(true)) - console.log(`${amount} items without links handling:`) - console.log(ptest(false)) - console.log(`${amount} items plain v-html:`) - console.log(ptest(false, true)) + console.debug(`${amount} items with links handling:`) + console.debug(ptest(true)) + console.debug(`${amount} items without links handling:`) + console.debug(ptest(false)) + console.debug(`${amount} items plain v-html:`) + console.debug(ptest(false, true)) }) }) diff --git a/test/unit/specs/services/theme_data/iss_deserializer.spec.js b/test/unit/specs/services/theme_data/iss_deserializer.spec.js @@ -21,17 +21,17 @@ describe('ISS (de)serialization', () => { const onlyComponent = componentsContext('./components/panel_header.style.js').default it.only(`(De)serialization of component ${onlyComponent.name} works`, () => { const normalized = onlyComponent.defaultRules.map(x => ({ component: onlyComponent.name, ...x })) - console.log('BEGIN INPUT ================') - console.log(normalized) - console.log('END INPUT ==================') + console.debug('BEGIN INPUT ================') + console.debug(normalized) + console.debug('END INPUT ==================') const serialized = serialize(normalized) - console.log('BEGIN SERIAL ===============') - console.log(serialized) - console.log('END SERIAL =================') + console.debug('BEGIN SERIAL ===============') + console.debug(serialized) + console.debug('END SERIAL =================') const deserialized = deserialize(serialized) - console.log('BEGIN DESERIALIZED =========') - console.log(serialized) - console.log('END DESERIALIZED ===========') + console.debug('BEGIN DESERIALIZED =========') + console.debug(serialized) + console.debug('END DESERIALIZED ===========') // for some reason comparing objects directly fails the assert expect(JSON.stringify(deserialized, null, 2)).to.equal(JSON.stringify(normalized, null, 2)) diff --git a/tools/emoji_merger.js b/tools/emoji_merger.js @@ -60,7 +60,7 @@ const run = () => { }, {}) fs.writeFile(outputFilename, JSON.stringify(sorted, null, 2), 'utf8', (err) => { - if (err) console.log('Error writing file', err) + if (err) console.error('Error writing file', err) }) }