diff options
89 files changed, 831 insertions, 173 deletions
diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 00000000..df2d4716 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,7 @@ +>0.2% +not op_mini all +Safari > 15 +Firefox >= 115 +Firefox ESR +Android > 4 +not dead diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 25e499c4..f4c5cf43 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -45,6 +45,7 @@ test: stage: test tags: - amd64 + - himem variables: APT_CACHE_DIR: apt-cache script: @@ -58,6 +59,7 @@ build: stage: build tags: - amd64 + - himem script: - yarn - npm run build diff --git a/CHANGELOG.md b/CHANGELOG.md index 444a863c..9844319e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,74 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## 2.7.1 +Bugfix release. Added small optimizations to emoji picker that should make it a bit more responsive, however it needs rather large change to make it more performant which might come in a major release. + +### Fixed +- Instance default theme not respected +- Nested panel header having wrong sticky position if navbar height != panel header height +- Toggled buttons having bad contrast (when using v2 theme) + +### Changed +- Simplify the OAuth client_name to 'PleromaFE' +- Small optimizations to emoji picker + + +## 2.7.0 + +### Known issues +We got some reports related to emoji picker performance, this hopefully will be fixed in 2.7.1. + +### Notes +This release overhauls how themes work, themes now need to be "compiled", which can cause some delay when loading for the first time and temporarily look "wrong" in some places (popups, menus, dialogs). Please do report any issues, especially if your theme looks wrong or breaks interface when loading. Also report issues if you're experiencing constant performance issues. + +To admins: remember that you can update PleromaFE to recent `master` or `develop` in admin dashboard in "Front-ends" tab, scroll down to find PleromaFE box and click "Reinstall `master`" or dropdown and then "Reinstall `develop`". Currently there is no mechanism to check if there is an update or not. + +### Changed +- Overhauled the way themes work, migrating to new Pleroma Interface Style Sheets system aka "Themes 3". +- Notifications are no longer sorted by "seen" status since interacting with them can change their read status and makes UI jumpy. Old behavior can be restored in settings. +- Notifications are now shown through a ServiceWorker (since mobile chrome does not allow them otherwise), it's always enabled, even if previously we only enabled it for WebPush notifications only. If you don't like websites "running" while closed, check how to disable them in your browser. Old way to show notifications will be used as a fallback but might not have all the new features. +- Reorganized Settings modal to move out visual stuff into Appearance tab + +### Added +- Emoji pack management to the admin panel +- Support `status` notification type (subscriptions/bell, fixes PleromaFE on newer PleromaBE versions) +- Poll end notifications. +- Added option to not mark all notifications when closing notifications drawer on mobile, this creates a new button to mark all as seen. +- Option to always "show" notifications when using web push for better compatibility with some browsers (chrome, edge, safari) +- Option to toggle what notification types appear in native notifications, by default less important ones (likes, repeats, etc) will no longer show up in native notifications. +- Option to treat non-interactive notifications (likes, repeats et all) as seen for visual purposes (no read mark, ignored in counters, still can show in native notifications) +- Ability to resize UI (and certain components) scale independent of browser/text scale +- Ability to override certain aspects of UI style independent of theme used (UI roundness, fonts, underlay) +- Theme selector with visual previews of the theme +- Display loading and error indicator for conversation page +- Option to only show scrobbles that are recent enough +- Interacting (opening reply box etc) or simply clicking on non-interactive notifications now marks them as read. Clicking on native notifications for non-interactive ones also marks them as seen. +- Support group actors +- Focusing into a tab clears all current desktop notifications +- Ability to change size of emoji +- Ability to view APNG (Animated PNG) attachments. +- Support showing extra notifications in the notifications column +- Create a link to the URL of the scrobble when it's present +- Allow hiding custom emojis in picker. +- Ability to mute sensitive posts (ported from eintei). +- Native notifications now also have "badge" property that matches instance's favicon (visible in Android Chromium at least) +- Display public favorites on user profiles +- Display quotes count on posts and add quotes list page +- Show a dedicated registration notice page when further action is required after registering + +### Fixed +- Synchronized requested notification types with backend, hopefully should fix missing notifications for polls and follow requests +- Error that appeared on mobile Chromium (and derivatives) when native notifications are allowed +- Being unable to set notification visibility for reports and follow requests +- Native notifications appearing as many times as there are open tabs. Clicking on notification will focus last focused tab. +- The expiry date indication won't be shown if the poll never expires +- Profile mentions causing a 422 error on newer PleromaBE versions. +- Color inputs are less ugly now +- Unread notifications should now properly catch up between sessions (eventually) in polling mode +- Video posters on Safari + + ## 2.6.1 ### Fixed - fix admin dashboard not having any feedback on frontend installation diff --git a/changelog.d/add-apng.add b/changelog.d/add-apng.add deleted file mode 100644 index cdec58af..00000000 --- a/changelog.d/add-apng.add +++ /dev/null @@ -1 +0,0 @@ -Make Pleroma FE to also view apng (Animated PNG) attachment. diff --git a/changelog.d/admin-emoji-packs.add b/changelog.d/admin-emoji-packs.add deleted file mode 100644 index 243163e8..00000000 --- a/changelog.d/admin-emoji-packs.add +++ /dev/null @@ -1 +0,0 @@ -Added emoji pack management to the admin panel diff --git a/changelog.d/appearance-tab.change b/changelog.d/appearance-tab.change deleted file mode 100644 index 7fe1b45e..00000000 --- a/changelog.d/appearance-tab.change +++ /dev/null @@ -1 +0,0 @@ -Reorganized Settings modal to move out visual stuff into Appearance tab diff --git a/changelog.d/browsers-support.change b/changelog.d/browsers-support.change new file mode 100644 index 00000000..a62e5024 --- /dev/null +++ b/changelog.d/browsers-support.change @@ -0,0 +1,9 @@ +Updated our build system to support browsers: + Safari >= 15 + Firefox >= 115 + Android > 4 + no Opera Mini support + no IE support + no "dead" (unmaintained) browsers support + +This does not guarantee that browsers will or will not work. diff --git a/changelog.d/ci-runner.skip b/changelog.d/ci-runner.skip deleted file mode 100644 index ad4b79d5..00000000 --- a/changelog.d/ci-runner.skip +++ /dev/null @@ -1 +0,0 @@ -stop using that one runner for intensive tasks
\ No newline at end of file diff --git a/changelog.d/create-link-when-url-present.add b/changelog.d/create-link-when-url-present.add deleted file mode 100644 index 11aa3758..00000000 --- a/changelog.d/create-link-when-url-present.add +++ /dev/null @@ -1 +0,0 @@ -Create a link to the URL of the scrobble when it's present diff --git a/changelog.d/date-absolute.add b/changelog.d/date-absolute.add new file mode 100644 index 00000000..d9365f46 --- /dev/null +++ b/changelog.d/date-absolute.add @@ -0,0 +1 @@ +Support displaying time in absolute format diff --git a/changelog.d/double-notifications.fix b/changelog.d/double-notifications.fix deleted file mode 100644 index 24e08c0f..00000000 --- a/changelog.d/double-notifications.fix +++ /dev/null @@ -1 +0,0 @@ -Fix native notifications appearing as many times as there are open tabs. Clicking on notification will focus last focused tab. diff --git a/changelog.d/emoji-scale.add b/changelog.d/emoji-scale.add deleted file mode 100644 index 791d80d9..00000000 --- a/changelog.d/emoji-scale.add +++ /dev/null @@ -1 +0,0 @@ -Ability to change size of emoji diff --git a/changelog.d/extra-notifications.add b/changelog.d/extra-notifications.add deleted file mode 100644 index 90f21f54..00000000 --- a/changelog.d/extra-notifications.add +++ /dev/null @@ -1 +0,0 @@ -Support showing extra notifications in the notifications column diff --git a/changelog.d/firefox-redmon.fix b/changelog.d/firefox-redmon.fix deleted file mode 100644 index 64ab9b14..00000000 --- a/changelog.d/firefox-redmon.fix +++ /dev/null @@ -1 +0,0 @@ -Bug with firefox and redmond themes diff --git a/changelog.d/fixes-themes.skip b/changelog.d/fixes-themes.skip deleted file mode 100644 index af691507..00000000 --- a/changelog.d/fixes-themes.skip +++ /dev/null @@ -1 +0,0 @@ -fixed themes for spw and kazvmoew diff --git a/changelog.d/fixes.skip b/changelog.d/fixes.skip deleted file mode 100644 index cc40793e..00000000 --- a/changelog.d/fixes.skip +++ /dev/null @@ -1 +0,0 @@ -fix post appearance tab bugs part I diff --git a/changelog.d/focus-clear.add b/changelog.d/focus-clear.add deleted file mode 100644 index 70f54ab6..00000000 --- a/changelog.d/focus-clear.add +++ /dev/null @@ -1 +0,0 @@ -Focusing into a tab clears all current desktop notifications diff --git a/changelog.d/group-actor.add b/changelog.d/group-actor.add deleted file mode 100644 index 7b62676a..00000000 --- a/changelog.d/group-actor.add +++ /dev/null @@ -1 +0,0 @@ -Support group actors diff --git a/changelog.d/hide-custom-emojis-in-picker.add b/changelog.d/hide-custom-emojis-in-picker.add deleted file mode 100644 index 4cfd2ca8..00000000 --- a/changelog.d/hide-custom-emojis-in-picker.add +++ /dev/null @@ -1 +0,0 @@ -Allow hiding custom emojis in picker. diff --git a/changelog.d/mobile-chrome-notifs.fix b/changelog.d/mobile-chrome-notifs.fix deleted file mode 100644 index 7db10c56..00000000 --- a/changelog.d/mobile-chrome-notifs.fix +++ /dev/null @@ -1 +0,0 @@ -Fixed error that appeared on mobile Chrome(ium) (and derivatives) when native notifications are allowed diff --git a/changelog.d/mobile-drawer-notifications.change b/changelog.d/mobile-drawer-notifications.change deleted file mode 100644 index 9353c709..00000000 --- a/changelog.d/mobile-drawer-notifications.change +++ /dev/null @@ -1 +0,0 @@ -Added option to not mark all notifications when closing notifications drawer on mobile, this creates a new button to mark all as seen. diff --git a/changelog.d/more-notification-types-setting.fix b/changelog.d/more-notification-types-setting.fix deleted file mode 100644 index 2d71b599..00000000 --- a/changelog.d/more-notification-types-setting.fix +++ /dev/null @@ -1 +0,0 @@ -Fixed being unable to set notification visibility for reports and follow requests diff --git a/changelog.d/mute-nsfw.add b/changelog.d/mute-nsfw.add deleted file mode 100644 index b1794a0c..00000000 --- a/changelog.d/mute-nsfw.add +++ /dev/null @@ -1 +0,0 @@ -Added ability to mute sensitive posts (ported from eintei) diff --git a/changelog.d/native-filtering.add b/changelog.d/native-filtering.add deleted file mode 100644 index 82ab9a23..00000000 --- a/changelog.d/native-filtering.add +++ /dev/null @@ -1 +0,0 @@ -Added option to toggle what notification types appear in native notifications, by default less important ones (likes, repeats, etc) will no longer show up in native notifications. diff --git a/changelog.d/native-notifications.add b/changelog.d/native-notifications.add deleted file mode 100644 index d896e7c0..00000000 --- a/changelog.d/native-notifications.add +++ /dev/null @@ -1 +0,0 @@ -Native notifications now also have "badge" property that matches instance's favicon (visible in Android Chromium at least) diff --git a/changelog.d/no-preserve-selection-color.fix b/changelog.d/no-preserve-selection-color.fix deleted file mode 100644 index 669e744c..00000000 --- a/changelog.d/no-preserve-selection-color.fix +++ /dev/null @@ -1 +0,0 @@ -Ensure selection text color has enough contrast diff --git a/changelog.d/non-anonymous-polls.add b/changelog.d/non-anonymous-polls.add new file mode 100644 index 00000000..9ff7f3ad --- /dev/null +++ b/changelog.d/non-anonymous-polls.add @@ -0,0 +1 @@ +Inform users that Smithereen public polls are public
\ No newline at end of file diff --git a/changelog.d/non-expiring-polls-indication.fix b/changelog.d/non-expiring-polls-indication.fix deleted file mode 100644 index 7979cc13..00000000 --- a/changelog.d/non-expiring-polls-indication.fix +++ /dev/null @@ -1 +0,0 @@ -The expiry date indication won't be shown if the poll never expires diff --git a/changelog.d/noninteractive-ignore-read.add b/changelog.d/noninteractive-ignore-read.add deleted file mode 100644 index 5e8710cf..00000000 --- a/changelog.d/noninteractive-ignore-read.add +++ /dev/null @@ -1 +0,0 @@ -Added option to treat non-interactive notifications (likes, repeats et all) as seen for visual purposes (no read mark, ignored in counters, still can show in native notifications) diff --git a/changelog.d/notif-types.fix b/changelog.d/notif-types.fix deleted file mode 100644 index fb0e5046..00000000 --- a/changelog.d/notif-types.fix +++ /dev/null @@ -1 +0,0 @@ -Synchronized requested notification types with backend, hopefully should fix missing notifications for polls and follow requests diff --git a/changelog.d/notification-read.add b/changelog.d/notification-read.add deleted file mode 100644 index e5027a95..00000000 --- a/changelog.d/notification-read.add +++ /dev/null @@ -1 +0,0 @@ -Interacting (opening reply box etc) or simply clicking on non-interactive notifications now marks them as read. Clicking on native notifications for non-interactive ones also marks them as seen. diff --git a/changelog.d/notifications-sorting.change b/changelog.d/notifications-sorting.change deleted file mode 100644 index 3a616244..00000000 --- a/changelog.d/notifications-sorting.change +++ /dev/null @@ -1 +0,0 @@ -Notifications are no longer sorted by "seen" status since interacting with them can change their read status and makes UI jumpy. Old behavior can be restored in settings. diff --git a/changelog.d/oauth-app-name.change b/changelog.d/oauth-app-name.change new file mode 100644 index 00000000..15d6f87e --- /dev/null +++ b/changelog.d/oauth-app-name.change @@ -0,0 +1 @@ +Simplify the OAuth client_name to 'PleromaFE' diff --git a/changelog.d/public-favorites.skip b/changelog.d/piss-serialization.skip index e69de29b..e69de29b 100644 --- a/changelog.d/public-favorites.skip +++ b/changelog.d/piss-serialization.skip diff --git a/changelog.d/poll-ended-notifications.fix b/changelog.d/poll-ended-notifications.fix deleted file mode 100644 index d04b8cb0..00000000 --- a/changelog.d/poll-ended-notifications.fix +++ /dev/null @@ -1 +0,0 @@ -Add poll end notifications to fetched types. diff --git a/changelog.d/preview-interference.skip b/changelog.d/preview-interference.skip deleted file mode 100644 index e32e76dd..00000000 --- a/changelog.d/preview-interference.skip +++ /dev/null @@ -1 +0,0 @@ -skip diff --git a/changelog.d/profile-mentions.fix b/changelog.d/profile-mentions.fix deleted file mode 100644 index 3f38ab0c..00000000 --- a/changelog.d/profile-mentions.fix +++ /dev/null @@ -1 +0,0 @@ -Fix profile mentions causing a 422 error diff --git a/changelog.d/public-favorites.add b/changelog.d/public-favorites.add deleted file mode 100644 index 183fcc85..00000000 --- a/changelog.d/public-favorites.add +++ /dev/null @@ -1 +0,0 @@ -Display public favorites on user profiles
\ No newline at end of file diff --git a/changelog.d/quotes-count.add b/changelog.d/quotes-count.add deleted file mode 100644 index 86779b96..00000000 --- a/changelog.d/quotes-count.add +++ /dev/null @@ -1 +0,0 @@ -Display quotes count on posts and add quotes list page
\ No newline at end of file diff --git a/changelog.d/registration-notice.add b/changelog.d/registration-notice.add deleted file mode 100644 index b2777ba2..00000000 --- a/changelog.d/registration-notice.add +++ /dev/null @@ -1 +0,0 @@ -Show a dedicated registration notice page when further action is required after registering diff --git a/changelog.d/scrobbles-age-filter.add b/changelog.d/scrobbles-age-filter.add deleted file mode 100644 index ecd3c7d8..00000000 --- a/changelog.d/scrobbles-age-filter.add +++ /dev/null @@ -1 +0,0 @@ -Option to only show scrobbles that are recent enough diff --git a/changelog.d/serviceworkers.change b/changelog.d/serviceworkers.change deleted file mode 100644 index b3b64f6d..00000000 --- a/changelog.d/serviceworkers.change +++ /dev/null @@ -1 +0,0 @@ -Notifications are now shown through a serviceworker (since mobile chrome does not allow them otherwise), it's always enabled, even if previously we only enabled it for WebPush notifications only. If you don't like websites "running" while closed, check how to disable them in your browser. Old way to show notifications will be used as a fallback but might not have all the new features. diff --git a/changelog.d/show-recent-scrobble.skip b/changelog.d/show-recent-scrobble.skip deleted file mode 100644 index 9227de06..00000000 --- a/changelog.d/show-recent-scrobble.skip +++ /dev/null @@ -1 +0,0 @@ -Shows the most recent scrobble under each post when available diff --git a/changelog.d/status-loading-indicator.add b/changelog.d/status-loading-indicator.add deleted file mode 100644 index d0725677..00000000 --- a/changelog.d/status-loading-indicator.add +++ /dev/null @@ -1 +0,0 @@ -Display loading and error indicator for conversation page diff --git a/changelog.d/status-notification-type.add b/changelog.d/status-notification-type.add deleted file mode 100644 index 0917dad4..00000000 --- a/changelog.d/status-notification-type.add +++ /dev/null @@ -1 +0,0 @@ -Support `status` notification type
\ No newline at end of file diff --git a/changelog.d/theme-selector.add b/changelog.d/theme-selector.add deleted file mode 100644 index c303f97c..00000000 --- a/changelog.d/theme-selector.add +++ /dev/null @@ -1 +0,0 @@ -Theme selector with visual previews of the theme diff --git a/changelog.d/themes3-cache.add b/changelog.d/themes3-cache.add deleted file mode 100644 index a275e44c..00000000 --- a/changelog.d/themes3-cache.add +++ /dev/null @@ -1 +0,0 @@ -Add caching system for themes3 diff --git a/changelog.d/themes3-fixes.fix b/changelog.d/themes3-fixes.fix deleted file mode 100644 index 15c31e82..00000000 --- a/changelog.d/themes3-fixes.fix +++ /dev/null @@ -1 +0,0 @@ -fix color inputs and some in-development themes3 issues diff --git a/changelog.d/themes3.change b/changelog.d/themes3.change deleted file mode 100644 index 5255f9b1..00000000 --- a/changelog.d/themes3.change +++ /dev/null @@ -1 +0,0 @@ -Overhauled the way themes work, migrating to new Pleroma Interface Style Sheets system. diff --git a/changelog.d/themesv3-on-safari.fix b/changelog.d/themesv3-on-safari.fix deleted file mode 100644 index af767b3a..00000000 --- a/changelog.d/themesv3-on-safari.fix +++ /dev/null @@ -1 +0,0 @@ -Fix Themes v3 on Safari not working diff --git a/changelog.d/ui-scale.add b/changelog.d/ui-scale.add deleted file mode 100644 index 594a9aa5..00000000 --- a/changelog.d/ui-scale.add +++ /dev/null @@ -1 +0,0 @@ -Ability to resize UI (and certain components) scale independent of browser/text scale diff --git a/changelog.d/unreads-sync.fix b/changelog.d/unreads-sync.fix deleted file mode 100644 index 1eac3364..00000000 --- a/changelog.d/unreads-sync.fix +++ /dev/null @@ -1 +0,0 @@ -unread notifications should now properly catch up (eventually) in polling mode diff --git a/changelog.d/user-overrides.add b/changelog.d/user-overrides.add deleted file mode 100644 index c0cb839a..00000000 --- a/changelog.d/user-overrides.add +++ /dev/null @@ -1 +0,0 @@ -Ability to override certain aspects of UI style independent of theme used (UI roundness, fonts, underlay) diff --git a/changelog.d/video-poster.fix b/changelog.d/video-poster.fix deleted file mode 100644 index 43e95f6e..00000000 --- a/changelog.d/video-poster.fix +++ /dev/null @@ -1 +0,0 @@ -Video posters on Safari diff --git a/changelog.d/video-poster.update.skip b/changelog.d/video-poster.update.skip deleted file mode 100644 index 9dafe9be..00000000 --- a/changelog.d/video-poster.update.skip +++ /dev/null @@ -1 +0,0 @@ -nothing diff --git a/changelog.d/web-push-always.add b/changelog.d/web-push-always.add deleted file mode 100644 index f8b8888a..00000000 --- a/changelog.d/web-push-always.add +++ /dev/null @@ -1 +0,0 @@ -Added option to always "show" notifications when using web push for better compatibility with some browsers (chrome, edge, safari) diff --git a/package.json b/package.json index cf2c73a9..93858457 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pleroma_fe", - "version": "2.6.1", + "version": "2.7.1", "description": "Pleroma frontend, the default frontend of Pleroma social network server", "author": "Pleroma contributors <https://git.pleroma.social/pleroma/pleroma-fe/-/blob/develop/CONTRIBUTORS.md>", "private": false, @@ -24,7 +24,7 @@ "@fortawesome/vue-fontawesome": "3.0.3", "@kazvmoe-infra/pinch-zoom-element": "1.2.0", "@kazvmoe-infra/unicode-emoji-json": "0.4.0", - "@ruffle-rs/ruffle": "0.1.0-nightly.2024.3.17", + "@ruffle-rs/ruffle": "0.1.0-nightly.2024.8.21", "@vuelidate/core": "2.0.3", "@vuelidate/validators": "2.0.4", "body-scroll-lock": "3.1.5", diff --git a/src/boot/after_store.js b/src/boot/after_store.js index a486bd4c..6cad05f6 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -122,6 +122,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => { store.dispatch('setInstanceOption', { name, value: config[name] }) } + copyInstanceOption('theme') copyInstanceOption('nsfwCensorImage') copyInstanceOption('background') copyInstanceOption('hidePostStats') diff --git a/src/components/alert.style.js b/src/components/alert.style.js index 19bd4bbb..abbeb5ba 100644 --- a/src/components/alert.style.js +++ b/src/components/alert.style.js @@ -27,7 +27,9 @@ export default { component: 'Alert' }, component: 'Border', - textColor: '--parent' + directives: { + textColor: '--parent' + } }, { variant: 'error', diff --git a/src/components/button.style.js b/src/components/button.style.js index 6fec67a0..1bee8f8e 100644 --- a/src/components/button.style.js +++ b/src/components/button.style.js @@ -34,8 +34,8 @@ export default { directives: { '--defaultButtonHoverGlow': 'shadow | 0 0 4 --text', '--defaultButtonShadow': 'shadow | 0 0 2 #000000', - '--defaultButtonBevel': 'shadow | $borderSide(#FFFFFF, top, 0.2) | $borderSide(#000000, bottom, 0.2)', - '--pressedButtonBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2)| $borderSide(#000000, top, 0.2)' + '--defaultButtonBevel': 'shadow | $borderSide(#FFFFFF, top, 0.2), $borderSide(#000000, bottom, 0.2)', + '--pressedButtonBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2), $borderSide(#000000, top, 0.2)' } }, { diff --git a/src/components/button_unstyled.style.js b/src/components/button_unstyled.style.js index 65b5c57b..435f9cc6 100644 --- a/src/components/button_unstyled.style.js +++ b/src/components/button_unstyled.style.js @@ -16,8 +16,7 @@ export default { { directives: { background: '#ffffff', - opacity: 0, - shadow: [] + opacity: 0 } }, { diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js index d71bc1bb..9ea5c877 100644 --- a/src/components/emoji_picker/emoji_picker.js +++ b/src/components/emoji_picker/emoji_picker.js @@ -150,7 +150,9 @@ const EmojiPicker = { }, showPicker () { this.$refs.popover.showPopover() - this.onShowing() + this.$nextTick(() => { + this.onShowing() + }) }, hidePicker () { this.$refs.popover.hidePopover() diff --git a/src/components/emoji_picker/emoji_picker.vue b/src/components/emoji_picker/emoji_picker.vue index 7c36deaa..a3dc8f9e 100644 --- a/src/components/emoji_picker/emoji_picker.vue +++ b/src/components/emoji_picker/emoji_picker.vue @@ -89,6 +89,7 @@ class="emoji-groups" :class="groupsScrolledClass" :min-item-size="minItemSize" + :buffer="minItemSize" :items="emojiItems" :emit-update="true" @update="onScroll" diff --git a/src/components/input.style.js b/src/components/input.style.js index 139a0034..7302cd6d 100644 --- a/src/components/input.style.js +++ b/src/components/input.style.js @@ -26,7 +26,7 @@ export default { { component: 'Root', directives: { - '--defaultInputBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2)| $borderSide(#000000, top, 0.2)' + '--defaultInputBevel': 'shadow | $borderSide(#FFFFFF, bottom, 0.2), $borderSide(#000000, top, 0.2)' } }, { diff --git a/src/components/panel_header.style.js b/src/components/panel_header.style.js index 32464bc5..010e42cd 100644 --- a/src/components/panel_header.style.js +++ b/src/components/panel_header.style.js @@ -16,8 +16,7 @@ export default { component: 'PanelHeader', directives: { backgroundNoCssColor: 'yes', - background: '--fg', - shadow: [] + background: '--fg' } } ] diff --git a/src/components/poll/poll.vue b/src/components/poll/poll.vue index 580e5377..e12f3e61 100644 --- a/src/components/poll/poll.vue +++ b/src/components/poll/poll.vue @@ -76,6 +76,13 @@ > {{ $t('polls.vote') }} </button> + <span + v-if="poll.pleroma?.non_anonymous" + :title="$t('polls.non_anonymous_title')" + > + {{ $t('polls.non_anonymous') }} + · + </span> <div class="total"> <template v-if="typeof poll.voters_count === 'number'"> {{ $tc("polls.people_voted_count", poll.voters_count, { count: poll.voters_count }) }} diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index 4ece6cf4..82d5da89 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -217,6 +217,29 @@ {{ $t('settings.no_rich_text_description') }} </BooleanSetting> </li> + <li> + <BooleanSetting + path="useAbsoluteTimeFormat" + expert="1" + > + {{ $t('settings.absolute_time_format') }} + </BooleanSetting> + </li> + <ul + class="setting-list suboptions" + v-if="mergedConfig.useAbsoluteTimeFormat" + > + <li> + <UnitSetting + path="absoluteTimeFormatMinAge" + unit-set="time" + :units="['s', 'm', 'h', 'd']" + :min="0" + > + {{ $t('settings.absolute_time_format_min_age') }} + </UnitSetting> + </li> + </ul> <h3>{{ $t('settings.attachments') }}</h3> <li> <BooleanSetting diff --git a/src/components/timeago/timeago.vue b/src/components/timeago/timeago.vue index b5f49515..bf918441 100644 --- a/src/components/timeago/timeago.vue +++ b/src/components/timeago/timeago.vue @@ -3,7 +3,7 @@ :datetime="time" :title="localeDateString" > - {{ relativeTimeString }} + {{ relativeOrAbsoluteTimeString }} </time> </template> @@ -16,16 +16,28 @@ export default { props: ['time', 'autoUpdate', 'longFormat', 'nowThreshold', 'templateKey'], data () { return { + relativeTimeMs: 0, relativeTime: { key: 'time.now', num: 0 }, interval: null } }, computed: { - localeDateString () { - const browserLocale = localeService.internalToBrowserLocale(this.$i18n.locale) + shouldUseAbsoluteTimeFormat () { + if (!this.$store.getters.mergedConfig.useAbsoluteTimeFormat) { + return false + } + return DateUtils.durationStrToMs(this.$store.getters.mergedConfig.absoluteTimeFormatMinAge) <= this.relativeTimeMs + }, + browserLocale () { + return localeService.internalToBrowserLocale(this.$i18n.locale) + }, + timeAsDate () { return typeof this.time === 'string' - ? new Date(Date.parse(this.time)).toLocaleString(browserLocale) - : this.time.toLocaleString(browserLocale) + ? new Date(Date.parse(this.time)) + : this.time + }, + localeDateString () { + return this.timeAsDate.toLocaleString(this.browserLocale) }, relativeTimeString () { const timeString = this.$i18n.tc(this.relativeTime.key, this.relativeTime.num, [this.relativeTime.num]) @@ -35,6 +47,40 @@ export default { } return timeString + }, + absoluteTimeString () { + if (this.longFormat) { + return this.localeDateString + } + const now = new Date() + const formatter = (() => { + if (DateUtils.isSameDay(this.timeAsDate, now)) { + return new Intl.DateTimeFormat(this.browserLocale, { + minute: 'numeric', + hour: 'numeric' + }) + } else if (DateUtils.isSameMonth(this.timeAsDate, now)) { + return new Intl.DateTimeFormat(this.browserLocale, { + hour: 'numeric', + day: 'numeric' + }) + } else if (DateUtils.isSameYear(this.timeAsDate, now)) { + return new Intl.DateTimeFormat(this.browserLocale, { + month: 'short', + day: 'numeric' + }) + } else { + return new Intl.DateTimeFormat(this.browserLocale, { + year: 'numeric', + month: 'short' + }) + } + })() + + return formatter.format(this.timeAsDate) + }, + relativeOrAbsoluteTimeString () { + return this.shouldUseAbsoluteTimeFormat ? this.absoluteTimeString : this.relativeTimeString } }, watch: { @@ -54,6 +100,7 @@ export default { methods: { refreshRelativeTimeObject () { const nowThreshold = typeof this.nowThreshold === 'number' ? this.nowThreshold : 1 + this.relativeTimeMs = DateUtils.relativeTimeMs(this.time) this.relativeTime = this.longFormat ? DateUtils.relativeTime(this.time, nowThreshold) : DateUtils.relativeTimeShort(this.time, nowThreshold) diff --git a/src/components/timeline/timeline.scss b/src/components/timeline/timeline.scss index 0fc0d979..2dd66328 100644 --- a/src/components/timeline/timeline.scss +++ b/src/components/timeline/timeline.scss @@ -26,7 +26,7 @@ } .conversation-heading { - top: calc(var(--__panel-heading-height) * var(--currentPanelStack, 2)); + top: calc(var(--__panel-heading-height) * var(--currentPanelStack, 1) + var(--navbar-height)); z-index: 2; } diff --git a/src/i18n/en.json b/src/i18n/en.json index 3f7ea282..423ce65e 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -229,7 +229,9 @@ "expiry": "Poll age", "expires_in": "Poll ends in {0}", "expired": "Poll ended {0} ago", - "not_enough_options": "Too few unique options in poll" + "not_enough_options": "Too few unique options in poll", + "non_anonymous": "Public poll", + "non_anonymous_title": "Other instances may display the options you voted for" }, "emoji": { "stickers": "Stickers", @@ -506,6 +508,8 @@ "autocomplete_select_first": "Automatically select the first candidate when autocomplete results are available", "emoji_reactions_on_timeline": "Show emoji reactions on timeline", "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", "export_theme": "Save preset", "filtering": "Filtering", "wordfilter": "Wordfilter", diff --git a/src/i18n/eo.json b/src/i18n/eo.json index 34a506b0..2912fc99 100644 --- a/src/i18n/eo.json +++ b/src/i18n/eo.json @@ -475,7 +475,8 @@ "interface": "Fasado", "input": "Enigaj kampoj", "post": "Teksto de afiŝo", - "postCode": "Egallarĝa teksto en afiŝo (riĉteksto)" + "postCode": "Egallarĝa teksto en afiŝo (riĉteksto)", + "monospace": "Egallarĝa teksto" }, "family": "Nomo de tiparo", "size": "Grando (en bilderoj)", @@ -495,6 +496,27 @@ "header_faint": "Tio estas en ordo", "checkbox": "Mi legetis la kondiĉojn de uzado", "link": "bela eta ligil’" + }, + "custom_theme_used": "(Propra haŭto)", + "themes3": { + "hacks": { + "underlay_override_mode_transparent": "Tute forigi (povus rompi iujn haŭtojn)", + "forced_roundness_mode_disabled": "Uzi implicitajn valorojn de haŭto", + "forced_roundness_mode_sharp": "Devigi akrajn randojn", + "forced_roundness_mode_nonsharp": "Devigi ne tiom akrajn randojn (rondigo je 1 bildero)", + "forced_roundness_mode_round": "Devigi rondajn randojn" + }, + "font": { + "builtin": { + "serif": "Kalkana", + "sans-serif": "Senkalkana", + "monospace": "Egallarĝa", + "inherit": "Senŝanĝe" + }, + "group-local": "Loke instalitaj signoformoj", + "local-unavailable1": "Listo de loke instalitaj signoformoj ne estas disponebla", + "font_list_unavailable": "Ne povis akiri loke instalitajn signoformojn: {error}" + } } }, "discoverable": "Permesi trovon de ĉi tiu konto en serĉrezultoj kaj aliaj servoj", @@ -744,7 +766,15 @@ "notification_visibility_reports": "Raportoj", "notification_setting_ignore_inactionable_seen": "Malatenti legitecon de nereageblaj sciigoj (ŝatoj, ripetoj, ktp.)", "notification_setting_ignore_inactionable_seen_tip": "Ĉi tio ne markos la sciigojn legitaj, kaj vi ankoraŭ ricevos labortablajn sciigojn pri ili, se vi elektis ricevi tiujn", - "notification_setting_unseen_at_top": "Montri nelegitajn sciigojn super aliaj" + "notification_setting_unseen_at_top": "Montri nelegitajn sciigojn super aliaj", + "appearance": "Aspekto", + "confirm_new_setting": "Ĉu konfirmi novan agordon?", + "confirm_new_question": "Ĉu tio ĉi aspektas ĝuste? La ŝanĝo malfariĝos post 10 sekundoj.", + "revert": "Malfari", + "confirm": "Konfirmi", + "text_size": "Grandeco de teksto kaj fasado", + "emoji_size": "Grandeco de bildosignoj", + "navbar_size": "Grandeco de supra breto" }, "timeline": { "collapse": "Maletendi", @@ -999,7 +1029,8 @@ "follows": "Novaj abonoj", "favs_repeats": "Ripetoj kaj ŝatoj", "emoji_reactions": "Bildosignaj reagoj", - "reports": "Raportoj" + "reports": "Raportoj", + "statuses": "Abonoj" }, "errors": { "storage_unavailable": "Pleroma ne povis aliri deponejon de la foliumilo. Via saluto kaj viaj lokaj agordoj ne estos konservitaj, kaj vi eble renkontos neatenditajn problemojn. Provu permesi kuketojn." @@ -1065,7 +1096,13 @@ "repeat_confirm_title": "Konfirmo de ripeto", "repeat_confirm_accept_button": "Ripeti", "repeat_confirm_cancel_button": "Ne ripeti", - "delete_confirm_cancel_button": "Ne forigi" + "delete_confirm_cancel_button": "Ne forigi", + "delete_error": "Eraris forigo de afiŝo: {0}", + "hide_quote": "Kaŝi la cititan afiŝon", + "display_quote": "Montri la cititan afiŝon", + "reaction_count_label": "{num} persono reagis | {num} personoj reagis", + "invisible_quote": "Citita afiŝo ne disponeblas: {link}", + "quotes": "Citaĵoj" }, "time": { "years_short": "{0}j", @@ -1267,7 +1304,9 @@ "save_meta": "Konservi pridatumojn", "description": "Priskribo", "homepage": "Hejmpaĝo", - "save": "Konservi" + "save": "Konservi", + "revert": "Malfari", + "share": "Kunhavigi" }, "tabs": { "emoji": "Bildosignoj", @@ -1283,7 +1322,8 @@ "header": "Limigi aliron por sennomaj vizitantoj", "timelines": "Aliro al historioj" }, - "access": "Aliro al nodo" + "access": "Aliro al nodo", + "kocaptcha": "Agordo de KoCaptcha" }, "limits": { "users": "Limoj de profiloj de uzantoj", @@ -1304,10 +1344,21 @@ ":instance": { ":public": { "label": "Nodo estas publika" + }, + ":background_image": { + "label": "Fonbildo", + "description": "Fonbildo (uzota ĉefe de PleromaFE)" + }, + ":description_limit": { + "description": "Limo de signoj por priskriboj de kunsendaĵoj", + "label": "Limo" } } } }, - "commit_all": "Konservi ĉion" + "commit_all": "Konservi ĉion", + "captcha": { + "kocaptcha": "KoCaptcha" + } } } diff --git a/src/i18n/fr.json b/src/i18n/fr.json index e2396937..4d421ff1 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -131,7 +131,8 @@ "mobile_notifications_close": "Fermer les notifications", "search_close": "Fermer la barre de recherche", "announcements": "Annonces", - "mobile_notifications_mark_as_seen": "Marquer tout comme vu" + "mobile_notifications_mark_as_seen": "Marquer tout comme vu", + "quotes": "Citations" }, "notifications": { "broken_favorite": "Message inconnu, recherche en cours…", diff --git a/src/i18n/ja_pedantic.json b/src/i18n/ja_pedantic.json index 45fd44e9..3d895fcd 100644 --- a/src/i18n/ja_pedantic.json +++ b/src/i18n/ja_pedantic.json @@ -565,7 +565,8 @@ "interface": "インターフェース", "input": "入力欄", "post": "投稿", - "postCode": "等幅 (投稿がリッチテキストであるとき)" + "postCode": "等幅 (投稿がリッチテキストであるとき)", + "monospace": "等幅テキスト" }, "family": "フォント名", "size": "大きさ (px)", @@ -585,7 +586,43 @@ "header_faint": "エラーではありません", "checkbox": "利用規約を読みました", "link": "ハイパーリンク" - } + }, + "themes2_outdated": "V2テーマのエディタは徐々に廃止され、最終的には新しいV3テーマのものに置き換えられる予定です。現状はまだ動作するはずですが、正しく動作する保証はありません。", + "themes3": { + "font": { + "group-local": "端末上にインストールされたフォント", + "local-unavailable2": "フォント名を直接指定してください", + "lookup_local_fonts": "端末上のフォントの一覧から選ぶ", + "group-builtin": "ブラウザのデフォルトフォント", + "builtin": { + "serif": "明朝体 (Serif)", + "sans-serif": "ゴシック体 (Sans-serif)", + "monospace": "等幅 (Monospace)", + "inherit": "変更しない" + }, + "local-unavailable1": "端末上のフォントの一覧が取得できません", + "font_list_unavailable": "端末上のフォントの一覧が取得できません: {error}", + "enter_manually": "フォント名を直接入力する", + "entry": "{fontFamily}を入力", + "select": "フォントを選択" + }, + "hacks": { + "underlay_overrides": "背景表示", + "underlay_override_mode_none": "テーマのデフォルトを使用する", + "underlay_override_mode_opaque": "単色に置き換える", + "underlay_override_mode_transparent": "非表示にする (テーマによっては表示が壊れる可能性があります)", + "force_interface_roundness": "インターフェースの角丸設定", + "forced_roundness_mode_disabled": "テーマのデフォルトを使用する", + "forced_roundness_mode_sharp": "角ばったデザインを強制する", + "forced_roundness_mode_nonsharp": "若干の角丸(1px分丸める)デザインを強制する", + "forced_roundness_mode_round": "角丸デザインを強制する" + }, + "define": "上書き" + }, + "custom_theme_used": "(カスタムテーマ)", + "appearance_tab_note": "以下の設定はテーマには反映されないため、エクスポートしたテーマの見た目は今見えているものと異なる可能性があります", + "update_preview": "プレビューを更新", + "interface_font_user_override": "フォント設定の上書き" }, "version": { "title": "バージョン", @@ -802,7 +839,19 @@ } }, "hide_scrobbles_after": "これより古いScrobbleを表示しない:", - "force_theme_recompilation_debug": "テーマのキャッシュを無効化し、起動の度にコンパイルし直す (デバッグ用)" + "force_theme_recompilation_debug": "テーマのキャッシュを無効化し、起動の度にコンパイルし直す (デバッグ用)", + "scale_and_layout": "インターフェースの表示サイズとレイアウト", + "appearance": "見た目", + "confirm_new_setting": "設定を適用しますか?", + "confirm_new_question": "これで問題ありませんか?10秒間操作がない場合、元の設定に戻ります。", + "revert": "元に戻す", + "confirm": "適用", + "text_size": "フォントサイズ", + "text_size_tip2": "{0}以外に設定すると見た目が壊れてしまう場合があります", + "emoji_size": "絵文字のサイズ", + "navbar_size": "トップバーのサイズ", + "panel_header_size": "パネルヘッダーのサイズ", + "notification_visibility_statuses": "購読" }, "time": { "day": "{0}日", @@ -941,7 +990,9 @@ "open_gallery": "メディアビューアで開く", "status_history": "編集履歴", "sensitive_muted": "閲覧注意な投稿のためミュートされています", - "load_error": "投稿の読み込みに失敗しました: {error}" + "load_error": "投稿の読み込みに失敗しました: {error}", + "loading": "読み込み中…", + "quotes": "引用" }, "user_card": { "approve": "承認", diff --git a/src/i18n/languages.js b/src/i18n/languages.js index 33a98f8e..475bf5ac 100644 --- a/src/i18n/languages.js +++ b/src/i18n/languages.js @@ -22,6 +22,7 @@ const languages = [ 'nl', 'oc', 'pl', + 'pdc', 'pt', 'ro', 'ru', diff --git a/src/i18n/pdc.json b/src/i18n/pdc.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/src/i18n/pdc.json @@ -0,0 +1 @@ +{} diff --git a/src/i18n/pl.json b/src/i18n/pl.json index efebcc83..9bfaa34d 100644 --- a/src/i18n/pl.json +++ b/src/i18n/pl.json @@ -24,7 +24,10 @@ "media_removal": "Usuwanie multimediów", "media_removal_desc": "Ta instancja usuwa multimedia z postów od wymienionych instancji:", "media_nsfw": "Multimedia ustawione jako wrażliwe", - "media_nsfw_desc": "Ta instancja wymusza, by multimedia z wymienionych instancji były ustawione jako wrażliwe:" + "media_nsfw_desc": "Ta instancja wymusza, by multimedia z wymienionych instancji były ustawione jako wrażliwe:", + "instance": "Instancja", + "reason": "Powód", + "not_applicable": "Nie dotyczy" } }, "staff": "Administracja" @@ -861,5 +864,13 @@ }, "errors": { "storage_unavailable": "Pleroma nie mogła uzyskać dostępu do pamięci masowej przeglądarki. Twój login lub lokalne ustawienia nie zostaną zapisane i możesz napotkać problemy. Spróbuj włączyć ciasteczka." + }, + "announcements": { + "page_header": "Ogłoszenia", + "title": "Ogłoszenie", + "mark_as_read_action": "Oznacz jako przeczytane", + "post_placeholder": "Wprowadź treść ogłoszenia…", + "close_error": "Zamknij", + "delete_action": "Usuń" } } diff --git a/src/i18n/service_worker_messages.js b/src/i18n/service_worker_messages.js index f691f1c4..a4e6d60a 100644 --- a/src/i18n/service_worker_messages.js +++ b/src/i18n/service_worker_messages.js @@ -25,6 +25,7 @@ const messages = { oc: require('../lib/notification-i18n-loader.js!./oc.json'), pl: require('../lib/notification-i18n-loader.js!./pl.json'), pt: require('../lib/notification-i18n-loader.js!./pt.json'), + pdc: require('../lib/notification-i18n-loader.js!./pdc.json'), ro: require('../lib/notification-i18n-loader.js!./ro.json'), ru: require('../lib/notification-i18n-loader.js!./ru.json'), sk: require('../lib/notification-i18n-loader.js!./sk.json'), diff --git a/src/i18n/zh.json b/src/i18n/zh.json index 2ade41f7..37ea03fc 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -130,20 +130,22 @@ "edit_nav_mobile": "自定义导航栏", "edit_pinned": "编辑固定的项目", "mobile_sidebar": "切换移动设备侧栏", - "search_close": "关闭搜索栏" + "search_close": "关闭搜索栏", + "mobile_notifications_mark_as_seen": "全部已阅", + "quotes": "引用" }, "notifications": { "broken_favorite": "未知的状态,正在搜索中…", - "favorited_you": "喜欢了你的状态", - "followed_you": "关注了你", + "favorited_you": "喜欢了您的状态", + "followed_you": "关注了您", "load_older": "加载更早的通知", "notifications": "通知", "read": "已阅!", - "repeated_you": "转发了你的状态", + "repeated_you": "转发了您的状态", "no_more_notifications": "没有更多的通知", - "reacted_with": "作出了 {0} 的反应", + "reacted_with": "作出了 {0} 的回应", "migrated_to": "迁移到了", - "follow_request": "想要关注你", + "follow_request": "想要关注您", "error": "取得通知时发生错误:{0}", "poll_ended": "投票结束了", "submitted_report": "提交举报", @@ -152,7 +154,8 @@ "unread_follow_requests": "{num} 个新关注请求", "configuration_tip": "可以在 {theSettings} 里定制什么会显示在这里。{dismiss}", "configuration_tip_settings": "设置", - "configuration_tip_dismiss": "不再显示" + "configuration_tip_dismiss": "不再显示", + "subscribed_status": "已发送" }, "polls": { "add_poll": "增加投票", @@ -179,11 +182,12 @@ "load_older": "加载更早的互动", "moves": "用户迁移", "reports": "举报", - "emoji_reactions": "表情回应" + "emoji_reactions": "表情回应", + "statuses": "订阅" }, "post_status": { "new_status": "发布新状态", - "account_not_locked_warning": "你的帐号没有 {0}。任何人都可以关注你并浏览你的上锁内容。", + "account_not_locked_warning": "您的帐号没有 {0}。任何人都可以关注您并浏览您的上锁内容。", "account_not_locked_warning_link": "上锁", "attachments_sensitive": "标记附件为敏感内容", "content_type": { @@ -199,12 +203,12 @@ "posting": "发送中", "scope_notice": { "public": "本条内容可以被所有人看到", - "private": "关注你的人才能看到本条内容", + "private": "关注您的人才能看到本条内容", "unlisted": "本条内容既不在公共时间线,也不会在所有已知网络上可见" }, "scope": { "direct": "私信 - 只发送给被提及的用户", - "private": "仅关注者 - 只有关注了你的人能看到", + "private": "仅关注者 - 只有关注了您的人能看到", "public": "公共 - 发送到公共时间轴", "unlisted": "不公开 - 不会发送到公共时间轴" }, @@ -212,7 +216,7 @@ "preview": "预览", "media_description": "媒体描述", "media_description_error": "更新媒体失败,请重试", - "empty_status_error": "不能发布没有内容、没有附件的发文", + "empty_status_error": "不能发布没有内容、没有附件的帖子", "post": "发送", "edit_remote_warning": "其它远程实例可能不支持编辑并且无法接收您的帖子的最新版本。", "edit_unsupported_warning": "Pleroma 不支持对提及或投票进行编辑。", @@ -233,7 +237,7 @@ "new_captcha": "点击图片获取新的验证码", "username_placeholder": "例如:lain", "fullname_placeholder": "例如:岩仓玲音", - "bio_placeholder": "例如:\n你好,我是玲音。\n我是一个住在日本郊区的动画少女。你可能在 Wired 见过我。", + "bio_placeholder": "例如:\n你好,我是玲音。\n我是一个住在日本郊区的动画少女。您可能在 Wired 见过我。", "validations": { "username_required": "不能留空", "fullname_required": "不能留空", @@ -247,7 +251,7 @@ "reason_placeholder": "此实例的注册需要手动批准。\n请让管理员知道您为什么想要注册。", "reason": "注册理由", "register": "注册", - "email_language": "你想从服务器收到什么语言的邮件?", + "email_language": "您想从服务器收到什么语言的邮件?", "bio_optional": "介绍(可选)", "email_optional": "电子邮件(可选)", "birthday": "生日:", @@ -289,7 +293,7 @@ "background": "背景", "bio": "简介", "block_export": "屏蔽名单导出", - "block_export_button": "导出你的屏蔽名单到一个 csv 文件", + "block_export_button": "导出您的屏蔽名单到一个 csv 文件", "block_import": "屏蔽名单导入", "block_import_error": "导入屏蔽名单出错", "blocks_imported": "屏蔽名单导入成功!需要一点时间来处理。", @@ -310,10 +314,10 @@ "current_profile_banner": "您当前的横幅图片", "data_import_export_tab": "数据导入/导出", "default_vis": "默认可见范围", - "delete_account": "删除账户", - "delete_account_description": "永久删除你的帐号和所有数据。", - "delete_account_error": "删除账户时发生错误,如果一直删除不了,请联系实例管理员。", - "delete_account_instructions": "在下面输入您的密码来确认删除账户。", + "delete_account": "删除账号", + "delete_account_description": "永久删除您的帐号和所有数据。", + "delete_account_error": "删除账号时发生错误。如果一直删除不了,请联系实例管理员。", + "delete_account_instructions": "在下面输入您的密码来确认删除账号。", "avatar_size_instruction": "推荐的头像图片最小尺寸为 150x150 像素。", "export_theme": "导出预置主题", "filtering": "过滤器", @@ -388,11 +392,11 @@ "autohide_floating_post_button": "自动隐藏新帖子的按钮(移动设备)", "saving_err": "保存设置时发生错误", "saving_ok": "设置已保存", - "search_user_to_block": "搜索你想屏蔽的用户", - "search_user_to_mute": "搜索你想要隐藏的用户", + "search_user_to_block": "搜索您想屏蔽的用户", + "search_user_to_mute": "搜索您想要隐藏的用户", "security_tab": "安全", "scope_copy": "回复时复制可见范围(私信中永远会复制)", - "minimal_scopes_mode": "使发文可见范围的选项最少化", + "minimal_scopes_mode": "使帖子可见范围的选项最小化", "set_new_avatar": "设置新头像", "set_new_profile_background": "设置新的个人资料背景", "set_new_profile_banner": "设置新的横幅图片", @@ -402,7 +406,7 @@ "subject_line_email": "类似电子邮件: \"re: 主题\"", "subject_line_mastodon": "类似 mastodon: 与原主题相同", "subject_line_noop": "不要复制", - "post_status_content_type": "发文状态内容类型", + "post_status_content_type": "帖子状态内容类型", "stop_gifs": "鼠标悬停时播放GIF", "streaming": "滚动到顶部时自动推送新内容", "text": "文本", @@ -546,7 +550,8 @@ "interface": "界面", "input": "输入框", "post": "发帖文字", - "postCode": "帖子中使用等间距文字(富文本)" + "postCode": "帖子中使用等间距文字(富文本)", + "monospace": "等宽文本" }, "family": "字体名称", "size": "大小 (in px)", @@ -566,7 +571,43 @@ "header_faint": "这很正常", "checkbox": "我已经浏览了条款及细则", "link": "一个棒棒的小小链接" - } + }, + "custom_theme_used": "(自定义主题)", + "themes2_outdated": "V2 主题的编辑器正在被淘汰并且最终会被新的利用 V3 主题引擎的编辑器取代。但是体验有可能会被降级并且不稳定。", + "appearance_tab_note": "在这个标签页的更改不会影响使用的主题,所以导出的主题会和界面显示的主题不同", + "update_preview": "更新预览", + "themes3": { + "define": "覆盖", + "hacks": { + "underlay_overrides": "更改底色", + "underlay_override_mode_none": "主题默认", + "underlay_override_mode_opaque": "使用单色更改", + "underlay_override_mode_transparent": "完全移除(有可能破外一些主题)", + "force_interface_roundness": "覆盖界面圆角/锐度", + "forced_roundness_mode_disabled": "使用主题默认", + "forced_roundness_mode_sharp": "强制使用锐利边角", + "forced_roundness_mode_nonsharp": "强制使用不太锋利(1px 圆角)的边角", + "forced_roundness_mode_round": "强制使用圆角" + }, + "font": { + "group-builtin": "浏览器默认字体", + "builtin": { + "serif": "衬线字体", + "sans-serif": "无衬线字体", + "monospace": "等宽字体", + "inherit": "未更改" + }, + "group-local": "本地字体", + "local-unavailable1": "不可用的本地字体列表", + "local-unavailable2": "使用手动输入来指定自定义字体", + "font_list_unavailable": "无法找到本地字体:{error}", + "lookup_local_fonts": "加载这台电脑的本地字体列表", + "enter_manually": "手动输入字体名称", + "entry": "输入 {fontFamily}", + "select": "选择字体" + } + }, + "interface_font_user_override": "覆盖使用的主题/浏览器字体" }, "version": { "title": "版本", @@ -582,12 +623,12 @@ "notification_setting_privacy_option": "在通知推送中隐藏发送者和内容", "notification_setting_privacy": "隐私", "hide_follows_count_description": "不显示关注数", - "notification_visibility_emoji_reactions": "互动", + "notification_visibility_emoji_reactions": "回应", "notification_visibility_moves": "用户迁移", "new_email": "新邮箱", - "emoji_reactions_on_timeline": "在时间线上显示表情符号互动", + "emoji_reactions_on_timeline": "在时间线上显示表情符号回应", "notification_setting_hide_notification_contents": "隐藏推送通知中的发送者与内容信息", - "notification_setting_block_from_strangers": "屏蔽来自你没有关注的用户的通知", + "notification_setting_block_from_strangers": "屏蔽来自您没有关注的用户的通知", "type_domains_to_mute": "搜索需要隐藏的域名", "useStreamingApi": "实时接收帖子和通知", "user_mutes": "用户", @@ -618,15 +659,15 @@ "mutes_imported": "隐藏名单导入成功!处理它们将需要一段时间。", "mute_import_error": "导入隐藏名单出错", "mute_import": "隐藏名单导入", - "mute_export_button": "导出你的隐藏名单到一个 csv 文件", + "mute_export_button": "导出您的隐藏名单到一个 csv 文件", "mute_export": "隐藏名单导出", "hide_wallpaper": "隐藏实例壁纸", "setting_changed": "与默认设置不同", "more_settings": "更多设置", - "sensitive_by_default": "默认标记发文为敏感内容", + "sensitive_by_default": "默认标记帖子为敏感内容", "reply_visibility_self_short": "只显示对我本人的回复", "reply_visibility_following_short": "显示对我关注的人的回复", - "hide_all_muted_posts": "不显示已隐藏的发文", + "hide_all_muted_posts": "不显示已隐藏的帖子", "hide_media_previews": "隐藏媒体预览", "word_filter": "词语过滤", "save": "保存更改", @@ -664,14 +705,14 @@ "move_account_target": "目标账号(例如 {example})", "moved_account": "账号移动好了。", "move_account_error": "移动账号时出错:{error}", - "setting_server_side": "这个设置是捆绑到你的个人资料的,能影响所有会话和客户端", + "setting_server_side": "这个设置是捆绑到您的个人资料的,能影响所有会话和客户端", "post_look_feel": "文章的样子跟感受", "email_language": "从服务器收邮件的语言", - "account_backup_description": "这个允许你下载一份账号信息和文章的存档,但是现在还不能导入到 Pleroma 账号里。", + "account_backup_description": "这个允许您下载一份账号信息和文章的存档,但是现在还不能导入到 Pleroma 账号里。", "backup_not_ready": "备份还没准备好。", "add_backup_error": "添加新备份时出错:{error}", "add_alias_error": "添加别名时出错:{error}", - "move_account_notes": "如果你想把账号移动到别的地方,你必须去目标账号,然后加一个指向这里的别名。", + "move_account_notes": "如果您想把账号移动到别的地方,您必须去目标账号,然后加一个指向这里的别名。", "wordfilter": "词语过滤器", "user_profiles": "用户资料", "third_column_mode_notifications": "通知栏", @@ -685,7 +726,7 @@ }, "hide_favorites_description": "不显示我的喜欢列表(人们仍然会收到通知)", "third_column_mode": "当有足够的空间时,显示第三栏包含", - "third_column_mode_postform": "主要的发文形式和导航", + "third_column_mode_postform": "主要的帖子形式和导航", "columns": "分栏", "user_popover_avatar_overlay": "在用户头像上显示用户弹出窗口", "navbar_column_stretch": "延伸导航栏至分栏宽度", @@ -705,12 +746,12 @@ "max_depth_in_thread": "默认显示同主题帖子中的最大层数", "hide_wordfiltered_statuses": "隐藏经过词语过滤的状态", "hide_muted_threads": "不显示已隐藏的同主题帖子", - "notification_visibility_polls": "你所投的投票的结束于", + "notification_visibility_polls": "您所投的投票的结束于", "tree_advanced": "允许在树状视图中进行更灵活的导航", "tree_fade_ancestors": "以模糊的文字显示当前状态的上级", "conversation_display_linear": "线性样式", "mention_link_fade_domain": "淡化域名(例如:{'@'}example.org 中的 {'@'}foo{'@'}example.org)", - "mention_link_bolden_you": "当你被提及时突出显示提及你", + "mention_link_bolden_you": "当您被提及时突出显示提及您", "user_popover_avatar_action": "弹出式头像点击动作", "user_popover_avatar_action_zoom": "缩放头像", "user_popover_avatar_action_close": "关闭弹出窗口", @@ -750,7 +791,7 @@ "url": "URL", "preview": "预览", "commit_value": "保存", - "commit_value_tooltip": "当前值未保存,请按此按钮以提交你的修改", + "commit_value_tooltip": "当前值未保存,请按此按钮以提交您的修改", "reset_value": "重置", "reset_value_tooltip": "重置草稿", "hard_reset_value": "硬重置", @@ -760,7 +801,52 @@ "notification_extra_chats": "显示未读聊天", "notification_extra_announcements": "显示未读公告", "notification_extra_follow_requests": "显示新的关注请求", - "notification_extra_tip": "显示额外通知的定制提示" + "notification_extra_tip": "显示额外通知的定制提示", + "notification_visibility_follow_requests": "关注请求", + "notification_visibility_reports": "举报", + "mute_sensitive_posts": "隐藏敏感帖子", + "notification_visibility_in_column": "在侧栏/菜单显示通知菜单", + "notification_visibility_native_notifications": "显示本地通知", + "units": { + "time": { + "m": "分钟", + "s": "秒", + "h": "小时", + "d": "天" + } + }, + "hide_scrobbles_after": "隐藏比这个时间更早的 scrobble", + "notification_setting_ignore_inactionable_seen": "忽略无法回复通知(喜欢,转发等)的已阅状态", + "notification_setting_unseen_at_top": "将未读通知置顶", + "notification_setting_ignore_inactionable_seen_tip": "如果您继续,这将不会标记这些通知为已读,并且您仍会接收到桌面推送通知", + "actor_type": "账号:", + "actor_type_description": "将您的账号标记为组会使其转发所有提及它的状态。", + "actor_type_Person": "正常用户", + "actor_type_Service": "机器人", + "actor_type_Group": "组", + "hide_actor_type_indication": "隐藏帖子中账号类型(机器人,组等)的表示", + "notification_setting_annoyance": "烦扰", + "notification_setting_drawer_marks_as_seen": "关闭菜单(移动端)来标记全部通知为已阅", + "enable_web_push_always_show_tip": "一些浏览器(Chromium,Chrome)需要推送信息才能显示通知,否则会显示“网页在背景发生了更改”的通知,勾选这个选项可以防止这种通知显示,因为 Chrome 在标签页激活时会隐藏网页推送通知。可能会在其他浏览器中显示双重通知。", + "enable_web_push_always_show": "总是显示网页推送通知", + "force_theme_recompilation_debug": "禁用主题缓存,强制在每次启动时重新编译(调试)", + "notification_setting_filters_chrome_push": "在一些浏览器中(Chrome),有可能无法完全按照类型过滤通过推送传递的通知", + "hide_scrobbles": "隐藏 scrobble", + "appearance": "外观", + "confirm_new_setting": "确认新的设置?", + "confirm_new_question": "是否保留这些设置?设置将在 10 秒后还原。", + "revert": "恢复", + "confirm": "确定", + "text_size": "文字与界面大小", + "text_size_tip": "用 {0} 作为绝对值,{1} 会根据浏览器默认文字大小进行缩放。", + "text_size_tip2": "{0} 之外的值可能会破坏一些功能和主题", + "emoji_size": "表情符号大小", + "navbar_size": "顶栏大小", + "panel_header_size": "面板标题大小", + "visual_tweaks": "细微外观调整", + "theme_debug": "显示当遇到透明背景时背景主题引擎的假设(调试)", + "scale_and_layout": "界面大小与布局", + "notification_visibility_statuses": "订阅" }, "time": { "day": "{0} 天", @@ -856,7 +942,7 @@ "nsfw": "NSFW", "external_source": "外部来源", "expand": "展开", - "you": "(你)", + "you": "(您)", "plus_more": "还有 {number} 个", "many_attachments": "文章有 {number} 个附件", "collapse_attachments": "折起附件", @@ -896,7 +982,12 @@ "reaction_count_label": "{num} 人作出了表情回应", "invisible_quote": "引用的状态不可用:{link}", "hide_quote": "隐藏引用的状态", - "display_quote": "显示引用的状态" + "display_quote": "显示引用的状态", + "quotes": "引用", + "sensitive_muted": "正在隐藏敏感内容", + "loading": "加载中...", + "load_error": "无法加载动态:{error}", + "more_actions": "状态的更多动作" }, "user_card": { "approve": "核准", @@ -911,14 +1002,14 @@ "followees": "正在关注", "followers": "关注者", "following": "正在关注!", - "follows_you": "关注了你!", - "its_you": "就是你!", + "follows_you": "关注了您!", + "its_you": "就是您!", "media": "媒体", "mute": "隐藏", "muted": "已隐藏", "per_day": "每天", "remote_follow": "跨站关注", - "report": "报告", + "report": "举报", "statuses": "状态", "subscribe": "订阅", "unsubscribe": "退订", @@ -945,7 +1036,7 @@ "disable_any_subscription": "完全禁止关注用户", "quarantine": "不许帖子传入别站", "delete_user": "删除用户", - "delete_user_data_and_deactivate_confirmation": "这将永久删除该账户的数据并停用该账户。你完全确定吗?" + "delete_user_data_and_deactivate_confirmation": "这将永久删除该账号的数据并停用该账号。您完全确定吗?" }, "hidden": "已隐藏", "show_repeats": "显示转发", @@ -993,7 +1084,8 @@ "note_blank": "(空)", "edit_note": "编辑备注", "edit_note_apply": "应用", - "edit_note_cancel": "取消" + "edit_note_cancel": "取消", + "group": "组" }, "user_profile": { "timeline_title": "用户时间线", @@ -1001,10 +1093,10 @@ "profile_loading_error": "抱歉,载入个人资料时出错。" }, "user_reporting": { - "title": "报告 {0}", - "add_comment_description": "此报告会发送给您的实例监察员。您可以在下面提供更多详细信息解释报告的缘由:", + "title": "举报 {0}", + "add_comment_description": "此举报会发送给您的实例监察员。您可以在下面提供更多详细信息解释举报的缘由:", "additional_comments": "其它信息", - "forward_description": "这个账号来自另一个服务器。是否同时发送一份报告副本到那里?", + "forward_description": "这个账号来自另一个服务器。是否同时发送一份举报副本到那里?", "forward_to": "转发 {0}", "submit": "提交", "generic_error": "当处理您的请求时,发生了一个错误。" @@ -1020,7 +1112,7 @@ "favorite": "喜欢", "user_settings": "用户设置", "reject_follow_request": "拒绝关注请求", - "add_reaction": "添加互动", + "add_reaction": "添加回应", "bookmark": "书签", "accept_follow_request": "接受关注请求", "toggle_expand": "展开或折叠通知以显示帖子全文", @@ -1090,7 +1182,8 @@ "smileys-and-emotion": "表情与情感" }, "regional_indicator": "地区指示符 {letter}", - "unpacked": "未分组的表情符号" + "unpacked": "未分组的表情符号", + "hide_custom_emoji": "隐藏自定义表情符号" }, "about": { "mrf": { @@ -1157,7 +1250,7 @@ "chats": "聊天", "delete": "删除", "message_user": "发消息给 {nickname}", - "you": "你:" + "you": "您:" }, "announcements": { "page_header": "公告", @@ -1198,8 +1291,8 @@ "update_changelog": "关于变化的更多细节,请参见 {theFullChangelog} 。", "update_changelog_here": "完整的更新日志", "big_update_title": "请忍耐一下", - "big_update_content": "我们已经有一段时间没有发布发行版,所以事情的外观和感觉可能与你习惯的不一样。", - "update_bugs": "请在 {pleromaGitlab} 上报告任何问题和bug,因为我们已经改变了很多,虽然我们进行了彻底的测试,并且自己使用了开发版本,但我们可能错过了一些东西。我们欢迎你对你可能遇到的问题或如何改进Pleroma和Pleroma-FE提出反馈和建议。", + "big_update_content": "我们已经有一段时间没有发布发行版,所以事情的外观和感觉可能与您习惯的不一样。", + "update_bugs": "请在 {pleromaGitlab} 上报告任何问题和 bug,因为我们改变了软件中的很多东西,虽然我们进行了彻底的测试,并且我们自己使用开发版本,但我们可能错过了一些东西。我们欢迎您对您可能遇到的问题或如何改进 Pleroma 和 Pleroma-FE 提出反馈和建议。", "art_by": "{linkToArtist} 的作品" }, "lists": { @@ -1232,13 +1325,14 @@ "nodb": "无数据库配置", "instance": "实例", "limits": "限制", - "frontends": "前端" + "frontends": "前端", + "emoji": "表情符号" }, "nodb": { "heading": "数据库配置已禁用", "documentation": "文档", "text2": "大多数配置选项将不可用。", - "text": "你需要修改后端配置文件,以便将 {property} 设置为 {value},更多内容请参见 {documentation}。" + "text": "您需要修改后端配置文件,以便将 {property} 设置为 {value},更多内容请参见 {documentation}。" }, "captcha": { "native": "本地", @@ -1281,8 +1375,11 @@ "set_default_version": "将版本 {version} 设为默认", "wip_notice": "请注意,此部分是一个WIP,缺乏某些功能,因为前端管理的后台实现并不完整。", "default_frontend": "默认前端", - "default_frontend_tip": "默认的前端将显示给所有用户。目前还没有办法让用户选择个人的前端。如果你不使用 PleromaFE,你很可能不得不使用旧的和有问题的 AdminFE 来进行实例配置,直到我们替换它。", - "available_frontends": "可供安装" + "default_frontend_tip": "默认的前端将显示给所有用户。目前还没有办法让用户选择自己的前端。如果您不使用 PleromaFE,您很可能不得不使用旧的和有问题的 AdminFE 来进行实例配置,直到我们替换它。", + "available_frontends": "可供安装", + "failure_installing_frontend": "无法安装前端 {version}:{reason}", + "success_installing_frontend": "前端 {version} 成功安装", + "default_frontend_unavail": "默认前端设置不可以,因为这需要数据库中的配置" }, "temp_overrides": { ":pleroma": { @@ -1306,6 +1403,50 @@ } } }, - "wip_notice": "此管理仪表板是实验性和 WIP 的,{adminFeLink}。" + "wip_notice": "此管理仪表板是实验性和 WIP 的,{adminFeLink}。", + "emoji": { + "remote_pack_instance": "远程表情包实例", + "fallback_src": "回退源", + "fallback_sha256": "回退 SHA256", + "delete_confirm": "您确定要删除 {0} 吗?", + "download_pack": "下载表情包", + "files": "文件", + "downloading_pack": "正在下载 {0}", + "download": "下载", + "download_as_name": "新名称", + "download_as_name_full": "新名称,留空来使用旧的名称", + "emoji_changed": "未保存的表情符号文件更改,检查突出显示的的表情符号", + "replace_warning": "这将替换本地同名的表情包", + "reload": "重新加载表情符号", + "create_pack": "创建表情包", + "emoji_pack": "表情包", + "save_meta": "保存元数据", + "delete": "删除", + "revert": "恢复", + "add_file": "添加文件", + "adding_new": "添加新的表情符号", + "shortcode": "简码", + "filename": "文件名", + "new_shortcode": "简码,留空来自动推断", + "emoji_packs": "表情包", + "remote_packs": "远程表情包", + "do_list": "列表", + "edit_pack": "编辑表情包", + "description": "描述", + "global_actions": "全局动作", + "importFS": "从文件系统导入表情符号", + "error": "错误:{0}", + "delete_pack": "删除表情包", + "new_pack_name": "新的表情包名称", + "create": "创建", + "homepage": "主页", + "share": "分享", + "save": "保存", + "revert_meta": "回复元数据", + "new_filename": "文件名,留空来自动推断", + "editing": "正在编辑 {0}", + "delete_title": "确定删除?", + "metadata_changed": "元数据和保存的不同" + } } } diff --git a/src/modules/config.js b/src/modules/config.js index cf84234a..835dcce4 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -180,7 +180,9 @@ export const defaultState = { autocompleteSelect: undefined, // instance default closingDrawerMarksAsSeen: undefined, // instance default unseenAtTop: undefined, // instance default - ignoreInactionableSeen: undefined // instance default + ignoreInactionableSeen: undefined, // instance default + useAbsoluteTimeFormat: undefined, // instance defualt + absoluteTimeFormatMinAge: undefined // instance default } // caching the instance default properties diff --git a/src/modules/instance.js b/src/modules/instance.js index 99b8b5d5..994f60a5 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -119,6 +119,8 @@ const defaultState = { closingDrawerMarksAsSeen: true, unseenAtTop: false, ignoreInactionableSeen: false, + useAbsoluteTimeFormat: false, + absoluteTimeFormatMinAge: '0d', // Nasty stuff customEmoji: [], diff --git a/src/services/date_utils/date_utils.js b/src/services/date_utils/date_utils.js index ed8e1417..69398c0c 100644 --- a/src/services/date_utils/date_utils.js +++ b/src/services/date_utils/date_utils.js @@ -6,10 +6,13 @@ export const WEEK = 7 * DAY export const MONTH = 30 * DAY export const YEAR = 365.25 * DAY -export const relativeTime = (date, nowThreshold = 1) => { +export const relativeTimeMs = (date) => { if (typeof date === 'string') date = Date.parse(date) + return Math.abs(Date.now() - date) +} +export const relativeTime = (date, nowThreshold = 1) => { const round = Date.now() > date ? Math.floor : Math.ceil - const d = Math.abs(Date.now() - date) + const d = relativeTimeMs(date) const r = { num: round(d / YEAR), key: 'time.unit.years' } if (d < nowThreshold * SECOND) { r.num = 0 @@ -57,3 +60,39 @@ export const secondsToUnit = (unit, amount) => { case 'days': return (1000 * amount) / DAY } } + +export const isSameYear = (a, b) => { + return a.getFullYear() === b.getFullYear() +} + +export const isSameMonth = (a, b) => { + return a.getFullYear() === b.getFullYear() && + a.getMonth() === b.getMonth() +} + +export const isSameDay = (a, b) => { + return a.getFullYear() === b.getFullYear() && + a.getMonth() === b.getMonth() && + a.getDate() === b.getDate() +} + +export const durationStrToMs = (str) => { + if (typeof str !== 'string') { + return 0 + } + + const unit = str.replace(/[0-9,.]+/, '') + const value = str.replace(/[^0-9,.]+/, '') + switch (unit) { + case 'd': + return value * DAY + case 'h': + return value * HOUR + case 'm': + return value * MINUTE + case 's': + return value * SECOND + default: + return 0 + } +} diff --git a/src/services/locale/locale.service.js b/src/services/locale/locale.service.js index 24ed3cdb..9a96cf01 100644 --- a/src/services/locale/locale.service.js +++ b/src/services/locale/locale.service.js @@ -3,6 +3,7 @@ import ISO6391 from 'iso-639-1' import _ from 'lodash' const specialLanguageCodes = { + pdc: 'en', ja_easy: 'ja', zh_Hant: 'zh-HANT', zh: 'zh-Hans' @@ -18,6 +19,7 @@ const internalToBackendLocaleMulti = codes => { const getLanguageName = (code) => { const specialLanguageNames = { + pdc: 'Pennsilfaanisch-Deitsch', ja_easy: 'やさしいにほんご', 'nan-TW': '臺語(閩南語)', zh: '简体中文', diff --git a/src/services/new_api/oauth.js b/src/services/new_api/oauth.js index 3c8e64bd..a4b7cbf0 100644 --- a/src/services/new_api/oauth.js +++ b/src/services/new_api/oauth.js @@ -10,7 +10,8 @@ export const getOrCreateApp = ({ clientId, clientSecret, instance, commit }) => const url = `${instance}/api/v1/apps` const form = new window.FormData() - form.append('client_name', `PleromaFE_${window.___pleromafe_commit_hash}_${(new Date()).toISOString()}`) + form.append('client_name', 'PleromaFE') + form.append('website', 'https://pleroma.social') form.append('redirect_uris', REDIRECT_URI) form.append('scopes', 'read write follow push admin') diff --git a/src/services/theme_data/iss_deserializer.js b/src/services/theme_data/iss_deserializer.js new file mode 100644 index 00000000..5d71f35f --- /dev/null +++ b/src/services/theme_data/iss_deserializer.js @@ -0,0 +1,153 @@ +import { flattenDeep } from 'lodash' + +const parseShadow = string => { + const modes = ['_full', 'inset', 'x', 'y', 'blur', 'spread', 'color', 'alpha'] + const regexPrep = [ + // inset keyword (optional) + '^(?:(inset)\\s+)?', + // x + '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)', + // y + '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)', + // blur (optional) + '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?', + // spread (optional) + '(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?', + // either hex, variable or function + '(#[0-9a-f]{6}|--[a-z\\-_]+|\\$[a-z\\-()_]+)', + // opacity (optional) + '(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?$' + ].join('') + const regex = new RegExp(regexPrep, 'gis') // global, (stable) indices, single-string + const result = regex.exec(string) + if (result == null) { + return string + } else { + const numeric = new Set(['x', 'y', 'blur', 'spread', 'alpha']) + const { x, y, blur, spread, alpha, inset, color } = Object.fromEntries(modes.map((mode, i) => { + if (numeric.has(mode)) { + return [mode, Number(result[i])] + } else if (mode === 'inset') { + return [mode, !!result[i]] + } else { + return [mode, result[i]] + } + }).filter(([k, v]) => v !== false).slice(1)) + + return { x, y, blur, spread, color, alpha, inset } + } +} +// this works nearly the same as HTML tree converter +const parseIss = (input) => { + const buffer = [{ selector: null, content: [] }] + let textBuffer = '' + + const getCurrentBuffer = () => { + let current = buffer[buffer.length - 1] + if (current == null) { + current = { selector: null, content: [] } + } + return current + } + + // Processes current line buffer, adds it to output buffer and clears line buffer + const flushText = (kind) => { + if (textBuffer === '') return + if (kind === 'content') { + getCurrentBuffer().content.push(textBuffer.trim()) + } else { + getCurrentBuffer().selector = textBuffer.trim() + } + textBuffer = '' + } + + for (let i = 0; i < input.length; i++) { + const char = input[i] + + if (char === ';') { + flushText('content') + } else if (char === '{') { + flushText('header') + } else if (char === '}') { + flushText('content') + buffer.push({ selector: null, content: [] }) + textBuffer = '' + } else { + textBuffer += char + } + } + + return buffer +} +export const deserialize = (input) => { + const ast = parseIss(input) + const finalResult = ast.filter(i => i.selector != null).map(item => { + const { selector, content } = item + let stateCount = 0 + const selectors = selector.split(/,/g) + const result = selectors.map(selector => { + const output = { component: '' } + let currentDepth = null + + selector.split(/ /g).reverse().forEach((fragment, index, arr) => { + const fragmentObject = { component: '' } + + let mode = 'component' + for (let i = 0; i < fragment.length; i++) { + const char = fragment[i] + switch (char) { + case '.': { + mode = 'variant' + fragmentObject.variant = '' + break + } + case ':': { + mode = 'state' + fragmentObject.state = fragmentObject.state || [] + stateCount++ + break + } + default: { + if (mode === 'state') { + const currentState = fragmentObject.state[stateCount - 1] + if (currentState == null) { + fragmentObject.state.push('') + } + fragmentObject.state[stateCount - 1] += char + } else { + fragmentObject[mode] += char + } + } + } + } + if (currentDepth !== null) { + currentDepth.parent = { ...fragmentObject } + currentDepth = currentDepth.parent + } else { + Object.keys(fragmentObject).forEach(key => { + output[key] = fragmentObject[key] + }) + if (index !== (arr.length - 1)) { + output.parent = { component: '' } + } + currentDepth = output + } + }) + + output.directives = Object.fromEntries(content.map(d => { + const [property, value] = d.split(':') + let realValue = value.trim() + if (property === 'shadow') { + realValue = value.split(',').map(v => parseShadow(v.trim())) + } if (!Number.isNaN(Number(value))) { + realValue = Number(value) + } + return [property, realValue] + })) + + return output + }) + return result + }) + return flattenDeep(finalResult) +} diff --git a/src/services/theme_data/iss_serializer.js b/src/services/theme_data/iss_serializer.js new file mode 100644 index 00000000..959852b7 --- /dev/null +++ b/src/services/theme_data/iss_serializer.js @@ -0,0 +1,44 @@ +import { unroll } from './iss_utils.js' + +const serializeShadow = s => { + if (typeof s === 'object') { + return `${s.inset ? 'inset ' : ''}${s.x} ${s.y} ${s.blur} ${s.spread} ${s.color} / ${s.alpha}` + } else { + return s + } +} + +export const serialize = (ruleset) => { + return ruleset.map((rule) => { + if (Object.keys(rule.directives || {}).length === 0) return false + + const header = unroll(rule).reverse().map(rule => { + const { component } = rule + const newVariant = (rule.variant == null || rule.variant === 'normal') ? '' : ('.' + rule.variant) + const newState = (rule.state || []).filter(st => st !== 'normal') + + return `${component}${newVariant}${newState.map(st => ':' + st).join('')}` + }).join(' ') + + const content = Object.entries(rule.directives).map(([directive, value]) => { + if (directive.startsWith('--')) { + const [valType, newValue] = value.split('|') // only first one! intentional! + switch (valType) { + case 'shadow': + return ` ${directive}: ${valType.trim()} | ${newValue.map(serializeShadow).map(s => s.trim()).join(', ')}` + default: + return ` ${directive}: ${valType.trim()} | ${newValue.trim()}` + } + } else { + switch (directive) { + case 'shadow': + return ` ${directive}: ${value.map(serializeShadow).join(', ')}` + default: + return ` ${directive}: ${value}` + } + } + }) + + return `${header} {\n${content.join(';\n')}\n}` + }).filter(x => x).join('\n\n') +} diff --git a/src/services/theme_data/theme2_to_theme3.js b/src/services/theme_data/theme2_to_theme3.js index 95eb03c1..bcc0c961 100644 --- a/src/services/theme_data/theme2_to_theme3.js +++ b/src/services/theme_data/theme2_to_theme3.js @@ -418,7 +418,6 @@ export const convertTheme2To3 = (data) => { case 'Border': newRule.parent = rule newRule.directives.textColor = data.colors[key] - newRule.directives.textAuto = 'no-auto' variantArray = parts.slice(0, -1) break default: diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index cf58da11..39c8b74f 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -504,9 +504,21 @@ export const init = ({ console.debug('Eager processing took ' + (t2 - t1) + ' ms') } + // optimization to traverse big-ass array only once instead of twice + const eager = [] + const lazy = [] + + result.forEach(x => { + if (typeof x === 'function') { + lazy.push(x) + } else { + eager.push(x) + } + }) + return { - lazy: result.filter(x => typeof x === 'function'), - eager: result.filter(x => typeof x !== 'function'), + lazy, + eager, staticVars, engineChecksum } diff --git a/test/unit/specs/services/theme_data/iss_deserializer.spec.js b/test/unit/specs/services/theme_data/iss_deserializer.spec.js new file mode 100644 index 00000000..01f8dacf --- /dev/null +++ b/test/unit/specs/services/theme_data/iss_deserializer.spec.js @@ -0,0 +1,40 @@ +import { deserialize } from 'src/services/theme_data/iss_deserializer.js' +import { serialize } from 'src/services/theme_data/iss_serializer.js' +const componentsContext = require.context('src', true, /\.style.js(on)?$/) + +describe('ISS (de)serialization', () => { + componentsContext.keys().forEach(key => { + const component = componentsContext(key).default + + it(`(De)serialization of component ${component.name} works`, () => { + const normalized = component.defaultRules.map(x => ({ component: component.name, ...x })) + const serialized = serialize(normalized) + const deserialized = deserialize(serialized) + + // for some reason comparing objects directly fails the assert + expect(JSON.stringify(deserialized, null, 2)).to.equal(JSON.stringify(normalized, null, 2)) + }) + }) + + /* + // Debug snippet + const onlyComponent = componentsContext('./components/panel_header.style.js').default + it(`(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 ==================') + const serialized = serialize(normalized) + console.log('BEGIN SERIAL ===============') + console.log(serialized) + console.log('END SERIAL =================') + const deserialized = deserialize(serialized) + console.log('BEGIN DESERIALIZED =========') + console.log(serialized) + console.log('END DESERIALIZED ===========') + + // for some reason comparing objects directly fails the assert + expect(JSON.stringify(deserialized, null, 2)).to.equal(JSON.stringify(normalized, null, 2)) + }) + */ +}) @@ -2014,10 +2014,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@ruffle-rs/ruffle@0.1.0-nightly.2024.3.17": - version "0.1.0-nightly.2024.3.17" - resolved "https://registry.yarnpkg.com/@ruffle-rs/ruffle/-/ruffle-0.1.0-nightly.2024.3.17.tgz#df3626f7277ed85742a602508c191d3186b0cabc" - integrity sha512-Wl/7CDZSmomOcfBeOYOO6xUUrN7upnGRDJEm3fpCtN3j5kU8dGF8xzaziAttjkD8byLYS09InE7PlUTyyAwCiQ== +"@ruffle-rs/ruffle@0.1.0-nightly.2024.8.21": + version "0.1.0-nightly.2024.8.21" + resolved "https://registry.yarnpkg.com/@ruffle-rs/ruffle/-/ruffle-0.1.0-nightly.2024.8.21.tgz#e4bdb6386b487dc12471681c7265f565d813e1cf" + integrity sha512-nfTPJEPJPo4MrUACuLoW19wNKgF1rrbxCO5if1MZfsWcUxZ6+pwlQWq1JxXalxEjYg8VwJtWzWEchWJQkMckwA== "@sinclair/typebox@^0.24.1": version "0.24.51" @@ -3139,30 +3139,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001370: - version "1.0.30001376" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001376.tgz#af2450833e5a06873fbb030a9556ca9461a2736d" - integrity sha512-I27WhtOQ3X3v3it9gNs/oTpoE5KpwmqKR5oKPA8M0G7uMXh9Ty81Q904HpKUrM30ei7zfcL5jE7AXefgbOfMig== - -caniuse-lite@^1.0.30001359: - version "1.0.30001366" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001366.tgz#c73352c83830a9eaf2dea0ff71fb4b9a4bbaa89c" - integrity sha512-yy7XLWCubDobokgzudpkKux8e0UOOnLHE6mlNJBzT3lZJz6s5atSEzjoL+fsCPkI0G8MP5uVdDx1ur/fXEWkZA== - -caniuse-lite@^1.0.30001400: - version "1.0.30001418" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz#5f459215192a024c99e3e3a53aac310fc7cf24e6" - integrity sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg== - -caniuse-lite@^1.0.30001587: - version "1.0.30001591" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz#16745e50263edc9f395895a7cd468b9f3767cf33" - integrity sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ== - -caniuse-lite@^1.0.30001599: - version "1.0.30001599" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz#571cf4f3f1506df9bf41fcbb6d10d5d017817bce" - integrity sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001359, caniuse-lite@^1.0.30001370, caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599: + version "1.0.30001662" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001662.tgz" + integrity sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA== chai-nightwatch@0.5.3: version "0.5.3" |
