From 0d6a9e8a647be860b10506aecaafb4ff0f10150f Mon Sep 17 00:00:00 2001 From: tusooa Date: Sun, 13 Aug 2023 23:57:34 -0400 Subject: Display extra notifications on notifications column --- .../extra_notifications/extra_notifications.js | 9 +++++ .../extra_notifications/extra_notifications.vue | 42 ++++++++++++++++++++++ src/components/notifications/notifications.js | 7 +++- src/components/notifications/notifications.vue | 7 ++++ 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/components/extra_notifications/extra_notifications.js create mode 100644 src/components/extra_notifications/extra_notifications.vue (limited to 'src/components') diff --git a/src/components/extra_notifications/extra_notifications.js b/src/components/extra_notifications/extra_notifications.js new file mode 100644 index 00000000..0bf904ba --- /dev/null +++ b/src/components/extra_notifications/extra_notifications.js @@ -0,0 +1,9 @@ +import { mapGetters } from 'vuex' + +const ExtraNotifications = { + computed: { + ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']) + } +} + +export default ExtraNotifications diff --git a/src/components/extra_notifications/extra_notifications.vue b/src/components/extra_notifications/extra_notifications.vue new file mode 100644 index 00000000..11eeb937 --- /dev/null +++ b/src/components/extra_notifications/extra_notifications.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/src/components/settings_modal/settings_modal_admin_content.js b/src/components/settings_modal/settings_modal_admin_content.js index f94721ec..ce835bf2 100644 --- a/src/components/settings_modal/settings_modal_admin_content.js +++ b/src/components/settings_modal/settings_modal_admin_content.js @@ -3,6 +3,7 @@ import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import InstanceTab from './admin_tabs/instance_tab.vue' import LimitsTab from './admin_tabs/limits_tab.vue' import FrontendsTab from './admin_tabs/frontends_tab.vue' +import EmojiTab from './admin_tabs/emoji_tab.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -33,7 +34,8 @@ const SettingsModalAdminContent = { InstanceTab, LimitsTab, - FrontendsTab + FrontendsTab, + EmojiTab }, computed: { user () { diff --git a/src/components/settings_modal/settings_modal_admin_content.vue b/src/components/settings_modal/settings_modal_admin_content.vue index a7a2ac9a..ba2d0a30 100644 --- a/src/components/settings_modal/settings_modal_admin_content.vue +++ b/src/components/settings_modal/settings_modal_admin_content.vue @@ -60,6 +60,14 @@ > + +
+ +
diff --git a/src/i18n/en.json b/src/i18n/en.json index f4c9de18..09ba7025 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -877,7 +877,8 @@ "nodb": "No DB Config", "instance": "Instance", "limits": "Limits", - "frontends": "Front-ends" + "frontends": "Front-ends", + "emoji": "Emoji" }, "nodb": { "heading": "Database config is disabled", @@ -931,6 +932,10 @@ "failure_installing_frontend": "Failed to install frontend {version}: {reason}", "success_installing_frontend": "Frontend {version} successfully installed" }, + "emoji": { + "reload": "Reload emoji", + "importFS": "Import emoji from filesystem" + }, "temp_overrides": { ":pleroma": { ":instance": { diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index bde2e163..4d8fdae2 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -114,6 +114,15 @@ const PLEROMA_ADMIN_DESCRIPTIONS_URL = '/api/pleroma/admin/config/descriptions' const PLEROMA_ADMIN_FRONTENDS_URL = '/api/pleroma/admin/frontends' const PLEROMA_ADMIN_FRONTENDS_INSTALL_URL = '/api/pleroma/admin/frontends/install' +const PLEROMA_EMOJI_RELOAD_URL = '/api/pleroma/admin/reload_emoji' +const PLEROMA_EMOJI_IMPORT_FS_URL = '/api/pleroma/emoji/packs/import' +const PLEROMA_EMOJI_PACKS_URL = (page, pageSize) => `/api/pleroma/emoji/packs?page=${page}&page_size=${pageSize}` +const PLEROMA_EMOJI_PACK_URL = (name) => `/api/pleroma/emoji/pack?name=${name}` +const PLEROMA_EMOJI_PACKS_DL_REMOTE_URL = '/api/pleroma/emoji/packs/download' +const PLEROMA_EMOJI_PACKS_LS_REMOTE_URL = + (url, page, pageSize) => `/api/pleroma/emoji/packs/remote?url=${url}&page=${page}&page_size=${pageSize}` +const PLEROMA_EMOJI_UPDATE_FILE_URL = (name) => `/api/pleroma/emoji/packs/files?name=${name}` + const oldfetch = window.fetch const fetch = (url, options) => { @@ -1787,6 +1796,92 @@ const fetchScrobbles = ({ accountId, limit = 1 }) => { }) } +const deleteEmojiPack = ({ name }) => { + return fetch(PLEROMA_EMOJI_PACK_URL(name), { method: 'DELETE' }) +} + +const reloadEmoji = () => { + return fetch(PLEROMA_EMOJI_RELOAD_URL, { method: 'POST' }) +} + +const importEmojiFromFS = () => { + return fetch(PLEROMA_EMOJI_IMPORT_FS_URL) +} + +const createEmojiPack = ({ name }) => { + return fetch(PLEROMA_EMOJI_PACK_URL(name), { method: 'PUT' }) +} + +const listEmojiPacks = () => { + return fetch(PLEROMA_EMOJI_PACKS_URL(1, 25)) +} + +const listRemoteEmojiPacks = ({ instance }) => { + return fetch( + PLEROMA_EMOJI_PACKS_LS_REMOTE_URL, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ instance_address: instance }) + } + ) +} + +const downloadRemoteEmojiPack = ({ instance, packName, as }) => { + if (as.trim() === '') { + as = null + } + + return fetch( + PLEROMA_EMOJI_PACKS_DL_REMOTE_URL, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + instance_address: instance, pack_name: packName, as + }) + } + ) +} + +const saveEmojiPackMetadata = ({ name, newData }) => { + return fetch( + PLEROMA_EMOJI_PACK_URL(name), + { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name, new_data: newData }) + } + ) +} + +const addNewEmojiFile = ({ packName, file, shortcode, filename }) => { + const data = new FormData() + if (filename.trim() !== '') { data.set('filename', filename) } + if (shortcode.trim() !== '') { data.set('shortcode', shortcode) } + data.set('file', file) + + return fetch( + PLEROMA_EMOJI_UPDATE_FILE_URL(packName), + { method: 'POST', data } + ) +} + +const updateEmojiFile = ({ packName, shortcode, newShortcode, newFilename, force }) => { + return fetch( + PLEROMA_EMOJI_UPDATE_FILE_URL(packName), + { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ shortcode, new_shortcode: newShortcode, new_filename: newFilename, force }) + } + ) +} + +const deleteEmojiFile = ({ packName, shortcode }) => { + return fetch(`${PLEROMA_EMOJI_UPDATE_FILE_URL(packName)}&shortcode=${shortcode}`, { method: 'DELETE' }) +} + const apiService = { verifyCredentials, fetchTimeline, @@ -1906,7 +2001,18 @@ const apiService = { fetchInstanceConfigDescriptions, fetchAvailableFrontends, pushInstanceDBConfig, - installFrontend + installFrontend, + importEmojiFromFS, + reloadEmoji, + listEmojiPacks, + createEmojiPack, + deleteEmojiPack, + saveEmojiPackMetadata, + addNewEmojiFile, + updateEmojiFile, + deleteEmojiFile, + listRemoteEmojiPacks, + downloadRemoteEmojiPack } export default apiService -- cgit v1.2.3-70-g09d2 From bfdad56b0d4b4f8ed89295661ff8abba22dca7f0 Mon Sep 17 00:00:00 2001 From: Ekaterina Vaartis Date: Thu, 21 Dec 2023 00:48:14 +0300 Subject: Make the frontend config work somewhat even without DB config --- src/components/settings_modal/admin_tabs/frontends_tab.js | 10 +++++++--- src/components/settings_modal/admin_tabs/frontends_tab.vue | 14 +++++++++----- src/components/settings_modal/helpers/setting.js | 3 ++- src/i18n/en.json | 1 + 4 files changed, 19 insertions(+), 9 deletions(-) (limited to 'src/components') diff --git a/src/components/settings_modal/admin_tabs/frontends_tab.js b/src/components/settings_modal/admin_tabs/frontends_tab.js index 8163af59..f57310ee 100644 --- a/src/components/settings_modal/admin_tabs/frontends_tab.js +++ b/src/components/settings_modal/admin_tabs/frontends_tab.js @@ -55,9 +55,13 @@ const FrontendsTab = { return fe.refs.includes(frontend.ref) }, getSuggestedRef (frontend) { - const defaultFe = this.adminDraft[':pleroma'][':frontends'][':primary'] - if (defaultFe?.name === frontend.name && this.canInstall(defaultFe)) { - return defaultFe.ref + if (this.adminDraft) { + const defaultFe = this.adminDraft[':pleroma'][':frontends'][':primary'] + if (defaultFe?.name === frontend.name && this.canInstall(defaultFe)) { + return defaultFe.ref + } else { + return frontend.refs[0] + } } else { return frontend.refs[0] } diff --git a/src/components/settings_modal/admin_tabs/frontends_tab.vue b/src/components/settings_modal/admin_tabs/frontends_tab.vue index dd4c9790..097877bc 100644 --- a/src/components/settings_modal/admin_tabs/frontends_tab.vue +++ b/src/components/settings_modal/admin_tabs/frontends_tab.vue @@ -6,7 +6,7 @@

{{ $t('admin_dash.tabs.frontends') }}

{{ $t('admin_dash.frontend.wip_notice') }}

-
    +
    • {{ $t('admin_dash.frontend.default_frontend') }}

      {{ $t('admin_dash.frontend.default_frontend_tip') }}

      @@ -23,6 +23,10 @@
+
+ {{ $t('admin_dash.frontend.default_frontend_unavail') }} +
+

{{ $t('admin_dash.frontend.available_frontends') }}

@@ -33,9 +37,9 @@ > {{ frontend.name }} {{ ' ' }} - + @@ -134,7 +138,7 @@ class="button button-default btn" type="button" :disabled=" - adminDraft[':pleroma'][':frontends'][':primary']?.name === frontend.name && + !adminDraft || adminDraft[':pleroma'][':frontends'][':primary']?.name === frontend.name && adminDraft[':pleroma'][':frontends'][':primary']?.ref === frontend.refs[0] " @click="setDefault(frontend)" diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index b3add346..abf9cfdf 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -195,7 +195,8 @@ export default { } }, canHardReset () { - return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths.has(this.canonPath.join(' -> ')) + return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths && + this.$store.state.adminSettings.modifiedPaths.has(this.canonPath.join(' -> ')) }, matchesExpertLevel () { return (this.expert || 0) <= this.$store.state.config.expertLevel > 0 diff --git a/src/i18n/en.json b/src/i18n/en.json index 09ba7025..62316a3f 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -928,6 +928,7 @@ "wip_notice": "Please note that this section is a WIP and lacks certain features as backend implementation of front-end management is incomplete.", "default_frontend": "Default front-end", "default_frontend_tip": "Default front-end will be shown to all users. Currently there's no way to for a user to select personal front-end. If you switch away from PleromaFE you'll most likely have to use old and buggy AdminFE to do instance configuration until we replace it.", + "default_frontend_unavail": "Default frontend settings are not available, as this requires configuration in the database", "available_frontends": "Available for install", "failure_installing_frontend": "Failed to install frontend {version}: {reason}", "success_installing_frontend": "Frontend {version} successfully installed" -- cgit v1.2.3-70-g09d2 From 0110fd86c2f166de5be8d675ffa34ab815463b2d Mon Sep 17 00:00:00 2001 From: tusooa Date: Wed, 27 Dec 2023 22:30:19 -0500 Subject: Allow user to mark account as group --- src/boot/after_store.js | 1 + src/components/settings_modal/tabs/profile_tab.js | 10 ++++++++-- src/components/settings_modal/tabs/profile_tab.vue | 15 ++++++++++++--- src/i18n/en.json | 5 ++++- src/modules/instance.js | 1 + .../entity_normalizer/entity_normalizer.service.js | 1 + 6 files changed, 27 insertions(+), 6 deletions(-) (limited to 'src/components') diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 84fea954..7039f85a 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -261,6 +261,7 @@ const getNodeInfo = async ({ store }) => { store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits }) store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled }) store.dispatch('setInstanceOption', { name: 'quotingAvailable', value: features.includes('quote_posting') }) + store.dispatch('setInstanceOption', { name: 'groupActorAvailable', value: features.includes('pleroma:group_actors') }) const uploadLimits = metadata.uploadLimits store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) }) diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js index eeacad48..3cb3ae45 100644 --- a/src/components/settings_modal/tabs/profile_tab.js +++ b/src/components/settings_modal/tabs/profile_tab.js @@ -9,6 +9,7 @@ import suggestor from 'src/components/emoji_input/suggestor.js' import Autosuggest from 'src/components/autosuggest/autosuggest.vue' import Checkbox from 'src/components/checkbox/checkbox.vue' import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue' +import Select from 'src/components/select/select.vue' import BooleanSetting from '../helpers/boolean_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' import localeService from 'src/services/locale/locale.service.js' @@ -39,6 +40,7 @@ const ProfileTab = { showRole: this.$store.state.users.currentUser.show_role, role: this.$store.state.users.currentUser.role, bot: this.$store.state.users.currentUser.bot, + actorType: this.$store.state.users.currentUser.actor_type, pickAvatarBtnVisible: true, bannerUploading: false, backgroundUploading: false, @@ -57,7 +59,8 @@ const ProfileTab = { ProgressButton, Checkbox, BooleanSetting, - InterfaceLanguageSwitcher + InterfaceLanguageSwitcher, + Select }, computed: { user () { @@ -116,6 +119,9 @@ const ProfileTab = { bannerImgSrc () { const src = this.$store.state.users.currentUser.cover_photo return (!src) ? this.defaultBanner : src + }, + availableActorTypes () { + return this.$store.state.instance.groupActorAvailable ? ['Person', 'Service', 'Group'] : ['Person', 'Service'] } }, methods: { @@ -127,7 +133,7 @@ const ProfileTab = { /* eslint-disable camelcase */ display_name: this.newName, fields_attributes: this.newFields.filter(el => el != null), - bot: this.bot, + actor_type: this.actorType, show_role: this.showRole, birthday: this.newBirthday || '', show_birthday: this.showBirthday diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue index 1cc850cb..e6dc5987 100644 --- a/src/components/settings_modal/tabs/profile_tab.vue +++ b/src/components/settings_modal/tabs/profile_tab.vue @@ -109,9 +109,18 @@

- - {{ $t('settings.bot') }} - +

{ output.show_role = data.source.pleroma.show_role output.discoverable = data.source.pleroma.discoverable output.show_birthday = data.pleroma.show_birthday + output.actor_type = data.source.pleroma.actor_type } } -- cgit v1.2.3-70-g09d2 From 4777bec85fd5488c5c50d5452efe26a51f90f53e Mon Sep 17 00:00:00 2001 From: tusooa Date: Wed, 27 Dec 2023 22:36:13 -0500 Subject: Add a description on what groups do --- src/components/settings_modal/tabs/profile_tab.js | 5 ++++- src/components/settings_modal/tabs/profile_tab.vue | 5 +++++ src/i18n/en.json | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src/components') diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js index 3cb3ae45..dee17450 100644 --- a/src/components/settings_modal/tabs/profile_tab.js +++ b/src/components/settings_modal/tabs/profile_tab.js @@ -120,8 +120,11 @@ const ProfileTab = { const src = this.$store.state.users.currentUser.cover_photo return (!src) ? this.defaultBanner : src }, + groupActorAvailable () { + return this.$store.state.instance.groupActorAvailable + }, availableActorTypes () { - return this.$store.state.instance.groupActorAvailable ? ['Person', 'Service', 'Group'] : ['Person', 'Service'] + return this.groupActorAvailable ? ['Person', 'Service', 'Group'] : ['Person', 'Service'] } }, methods: { diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue index e6dc5987..de5219a7 100644 --- a/src/components/settings_modal/tabs/profile_tab.vue +++ b/src/components/settings_modal/tabs/profile_tab.vue @@ -122,6 +122,11 @@

+
+ + {{ $t('settings.actor_type_description') }} + +

Date: Wed, 27 Dec 2023 22:40:07 -0500 Subject: Make user card group-aware --- src/components/user_card/user_card.vue | 8 +++++++- src/i18n/en.json | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src/components') diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 2de14063..2c76a220 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -124,11 +124,17 @@ {{ $t(`general.role.${visibleRole}`) }} {{ $t('user_card.bot') }} + + {{ $t('user_card.group') }} + Date: Wed, 27 Dec 2023 22:54:44 -0500 Subject: Implement indicator for groups --- src/components/settings_modal/tabs/filtering_tab.vue | 2 +- src/components/status/status.js | 10 ++-------- src/components/status/status.vue | 6 +++--- src/components/user_avatar/user_avatar.js | 8 +++++--- src/components/user_avatar/user_avatar.vue | 11 ++++++++--- src/i18n/en.json | 2 +- 6 files changed, 20 insertions(+), 19 deletions(-) (limited to 'src/components') diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue index 89fdef1a..9e82fcfd 100644 --- a/src/components/settings_modal/tabs/filtering_tab.vue +++ b/src/components/settings_modal/tabs/filtering_tab.vue @@ -51,7 +51,7 @@

  • - {{ $t('settings.hide_bot_indication') }} + {{ $t('settings.hide_actor_type_indication') }}
  • @@ -133,7 +133,7 @@ >
    diff --git a/src/components/user_avatar/user_avatar.js b/src/components/user_avatar/user_avatar.js index 33d9a258..ffd81f87 100644 --- a/src/components/user_avatar/user_avatar.js +++ b/src/components/user_avatar/user_avatar.js @@ -3,11 +3,13 @@ import StillImage from '../still-image/still-image.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { - faRobot + faRobot, + faPeopleGroup } from '@fortawesome/free-solid-svg-icons' library.add( - faRobot + faRobot, + faPeopleGroup ) const UserAvatar = { @@ -15,7 +17,7 @@ const UserAvatar = { 'user', 'betterShadow', 'compact', - 'bot' + 'showActorTypeIndicator' ], data () { return { diff --git a/src/components/user_avatar/user_avatar.vue b/src/components/user_avatar/user_avatar.vue index 91c17611..3cbccec3 100644 --- a/src/components/user_avatar/user_avatar.vue +++ b/src/components/user_avatar/user_avatar.vue @@ -18,9 +18,14 @@ :class="{ '-compact': compact }" /> + @@ -79,7 +84,7 @@ height: 100%; } - .bot-indicator { + .actor-type-indicator { position: absolute; bottom: 0; right: 0; diff --git a/src/i18n/en.json b/src/i18n/en.json index bb93f26f..71098822 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -499,7 +499,7 @@ "hide_media_previews": "Hide media previews", "hide_muted_posts": "Hide posts of muted users", "mute_bot_posts": "Mute bot posts", - "hide_bot_indication": "Hide bot indication in posts", + "hide_actor_type_indication": "Hide actor type (bots, groups, etc.) indication in posts", "hide_scrobbles": "Hide scrobbles", "hide_all_muted_posts": "Hide muted posts", "max_thumbnails": "Maximum amount of thumbnails per post (empty = no limit)", -- cgit v1.2.3-70-g09d2 From a709127a3c9b20d9d6cca6d9d4f00754a4726428 Mon Sep 17 00:00:00 2001 From: tusooa Date: Wed, 27 Dec 2023 22:55:58 -0500 Subject: Use actor type to determine whether a user is a bot --- src/components/status/status.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/components') diff --git a/src/components/status/status.js b/src/components/status/status.js index 458c8554..8f22b708 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -233,7 +233,7 @@ const Status = { return muteWordHits(this.status, this.muteWords) }, botStatus () { - return this.status.user.bot + return this.status.user.actor_type === 'Service' }, showActorTypeIndicator () { return !this.hideBotIndication -- cgit v1.2.3-70-g09d2 From 6f452d672fe740035cf1d29d03bcda0d39438753 Mon Sep 17 00:00:00 2001 From: marcin mikołajczak Date: Thu, 28 Dec 2023 10:43:06 +0100 Subject: Display public favorites on user profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- changelog.d/public-favorites.add | 1 + src/components/user_profile/user_profile.js | 5 +++++ src/components/user_profile/user_profile.vue | 3 ++- src/services/api/api.service.js | 6 ++++++ src/services/entity_normalizer/entity_normalizer.service.js | 1 + 5 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 changelog.d/public-favorites.add (limited to 'src/components') diff --git a/changelog.d/public-favorites.add b/changelog.d/public-favorites.add new file mode 100644 index 00000000..183fcc85 --- /dev/null +++ b/changelog.d/public-favorites.add @@ -0,0 +1 @@ +Display public favorites on user profiles \ No newline at end of file diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index acb612ed..751bfd5a 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -80,6 +80,9 @@ const UserProfile = { followersTabVisible () { return this.isUs || !this.user.hide_followers }, + favoritesTabVisible () { + return this.isUs || !this.user.hide_favorites + }, formattedBirthday () { const browserLocale = localeService.internalToBrowserLocale(this.$i18n.locale) return this.user.birthday && new Date(Date.parse(this.user.birthday)).toLocaleDateString(browserLocale, { timeZone: 'UTC', day: 'numeric', month: 'long', year: 'numeric' }) @@ -103,6 +106,8 @@ const UserProfile = { startFetchingTimeline('user', userId) startFetchingTimeline('media', userId) if (this.isUs) { + startFetchingTimeline('favorites') + } else if (!this.user.hide_favorites) { startFetchingTimeline('favorites', userId) } // Fetch all pinned statuses immediately diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index c63a303c..d0618dbb 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -109,7 +109,7 @@ :footer-slipgate="footerRef" /> diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index bde2e163..19de07f7 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -108,6 +108,7 @@ const PLEROMA_POST_ANNOUNCEMENT_URL = '/api/v1/pleroma/admin/announcements' const PLEROMA_EDIT_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}` const PLEROMA_DELETE_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}` const PLEROMA_SCROBBLES_URL = id => `/api/v1/pleroma/accounts/${id}/scrobbles` +const PLEROMA_USER_FAVORITES_TIMELINE_URL = id => `/api/v1/pleroma/accounts/${id}/favourites` const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config' const PLEROMA_ADMIN_DESCRIPTIONS_URL = '/api/pleroma/admin/config/descriptions' @@ -690,6 +691,7 @@ const fetchTimeline = ({ media: MASTODON_USER_TIMELINE_URL, list: MASTODON_LIST_TIMELINE_URL, favorites: MASTODON_USER_FAVORITES_TIMELINE_URL, + publicFavorites: PLEROMA_USER_FAVORITES_TIMELINE_URL, tag: MASTODON_TAG_TIMELINE_URL, bookmarks: MASTODON_BOOKMARK_TIMELINE_URL } @@ -698,6 +700,10 @@ const fetchTimeline = ({ let url = timelineUrls[timeline] + if (timeline === 'favorites' && userId) { + url = timelineUrls.publicFavorites(userId) + } + if (timeline === 'user' || timeline === 'media') { url = url(userId) } diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 85da5223..97b5beb5 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -107,6 +107,7 @@ export const parseUser = (data) => { output.allow_following_move = data.pleroma.allow_following_move + output.hide_favorites = data.pleroma.hide_favorites output.hide_follows = data.pleroma.hide_follows output.hide_followers = data.pleroma.hide_followers output.hide_follows_count = data.pleroma.hide_follows_count -- cgit v1.2.3-70-g09d2 From 6c4c8fe51f0e2c4ce55f5915a2bc5434aef5e5e7 Mon Sep 17 00:00:00 2001 From: marcin mikołajczak Date: Thu, 4 Jan 2024 22:46:04 +0100 Subject: Display quotes count on posts and add quotes list page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- changelog.d/quotes-count.add | 1 + src/boot/routes.js | 2 ++ src/components/quotes_timeline/quotes_timeline.js | 26 ++++++++++++++++++++++ src/components/quotes_timeline/quotes_timeline.vue | 10 +++++++++ src/components/status/status.scss | 1 + src/components/status/status.vue | 13 +++++++++++ src/components/timeline/timeline.js | 3 +++ src/components/timeline_menu/timeline_menu.js | 3 ++- src/i18n/en.json | 4 +++- src/modules/api.js | 5 +++-- src/services/api/api.service.js | 9 +++++++- .../backend_interactor_service.js | 4 ++-- .../entity_normalizer/entity_normalizer.service.js | 1 + .../timeline_fetcher/timeline_fetcher.service.js | 8 ++++--- 14 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 changelog.d/quotes-count.add create mode 100644 src/components/quotes_timeline/quotes_timeline.js create mode 100644 src/components/quotes_timeline/quotes_timeline.vue (limited to 'src/components') diff --git a/changelog.d/quotes-count.add b/changelog.d/quotes-count.add new file mode 100644 index 00000000..86779b96 --- /dev/null +++ b/changelog.d/quotes-count.add @@ -0,0 +1 @@ +Display quotes count on posts and add quotes list page \ No newline at end of file diff --git a/src/boot/routes.js b/src/boot/routes.js index 2dc900e7..31e3dbb0 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -25,6 +25,7 @@ import ListsTimeline from 'components/lists_timeline/lists_timeline.vue' import ListsEdit from 'components/lists_edit/lists_edit.vue' import NavPanel from 'src/components/nav_panel/nav_panel.vue' import AnnouncementsPage from 'components/announcements_page/announcements_page.vue' +import QuotesTimeline from '../components/quotes_timeline/quotes_timeline.vue' export default (store) => { const validateAuthenticatedRoute = (to, from, next) => { @@ -51,6 +52,7 @@ export default (store) => { { name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline }, { name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline }, { name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } }, + { name: 'quotes', path: '/notice/:id/quotes', component: QuotesTimeline }, { name: 'remote-user-profile-acct', path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)', diff --git a/src/components/quotes_timeline/quotes_timeline.js b/src/components/quotes_timeline/quotes_timeline.js new file mode 100644 index 00000000..a5f42da5 --- /dev/null +++ b/src/components/quotes_timeline/quotes_timeline.js @@ -0,0 +1,26 @@ +import Timeline from '../timeline/timeline.vue' + +const QuotesTimeline = { + created () { + this.$store.commit('clearTimeline', { timeline: 'quotes' }) + this.$store.dispatch('startFetchingTimeline', { timeline: 'quotes', statusId: this.statusId }) + }, + components: { + Timeline + }, + computed: { + statusId () { return this.$route.params.id }, + timeline () { return this.$store.state.statuses.timelines.quotes } + }, + watch: { + statusId () { + this.$store.commit('clearTimeline', { timeline: 'quotes' }) + this.$store.dispatch('startFetchingTimeline', { timeline: 'quotes', statusId: this.statusId }) + } + }, + unmounted () { + this.$store.dispatch('stopFetchingTimeline', 'quotes') + } +} + +export default QuotesTimeline diff --git a/src/components/quotes_timeline/quotes_timeline.vue b/src/components/quotes_timeline/quotes_timeline.vue new file mode 100644 index 00000000..835abd12 --- /dev/null +++ b/src/components/quotes_timeline/quotes_timeline.vue @@ -0,0 +1,10 @@ + + + diff --git a/src/components/status/status.scss b/src/components/status/status.scss index 760c6ac1..54c4b4ff 100644 --- a/src/components/status/status.scss +++ b/src/components/status/status.scss @@ -398,6 +398,7 @@ font-weight: bolder; font-size: 1.1em; line-height: 1em; + color: var(--selectedPostText, $fallback--text); } &:hover .stat-title { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index fa9d6f0b..11dc1e2e 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -506,6 +506,19 @@
    + +
    + {{ $t('status.quotes') }} +
    + {{ statusFromGlobalRepository.quotes_count }} +
    +
    +
    diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 1050b87a..6cb9d1f1 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -25,6 +25,7 @@ const Timeline = { 'title', 'userId', 'listId', + 'statusId', 'tag', 'embedded', 'count', @@ -121,6 +122,7 @@ const Timeline = { showImmediately, userId: this.userId, listId: this.listId, + statusId: this.statusId, tag: this.tag }) }, @@ -183,6 +185,7 @@ const Timeline = { showImmediately: true, userId: this.userId, listId: this.listId, + statusId: this.statusId, tag: this.tag }).then(({ statuses }) => { if (statuses && statuses.length === 0) { diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js index 5a2a86c2..c4586b32 100644 --- a/src/components/timeline_menu/timeline_menu.js +++ b/src/components/timeline_menu/timeline_menu.js @@ -19,7 +19,8 @@ export const timelineNames = () => { bookmarks: 'nav.bookmarks', dms: 'nav.dms', 'public-timeline': 'nav.public_tl', - 'public-external-timeline': 'nav.twkn' + 'public-external-timeline': 'nav.twkn', + quotes: 'nav.quotes' } } diff --git a/src/i18n/en.json b/src/i18n/en.json index f4c9de18..96b84c90 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -190,7 +190,8 @@ "mobile_notifications": "Open notifications (there are unread ones)", "mobile_notifications_close": "Close notifications", "mobile_notifications_mark_as_seen": "Mark all as seen", - "announcements": "Announcements" + "announcements": "Announcements", + "quotes": "Quotes" }, "notifications": { "broken_favorite": "Unknown status, searching for it…", @@ -996,6 +997,7 @@ "status": { "favorites": "Favorites", "repeats": "Repeats", + "quotes": "Quotes", "repeat_confirm": "Do you really want to repeat this status?", "repeat_confirm_title": "Repeat confirmation", "repeat_confirm_accept_button": "Repeat", diff --git a/src/modules/api.js b/src/modules/api.js index fee584e8..3dbead5b 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -202,12 +202,13 @@ const api = { timeline = 'friends', tag = false, userId = false, - listId = false + listId = false, + statusId = false }) { if (store.state.fetchers[timeline]) return const fetcher = store.state.backendInteractor.startFetchingTimeline({ - timeline, store, userId, listId, tag + timeline, store, userId, listId, statusId, tag }) store.commit('addFetcher', { fetcherName: timeline, fetcher }) }, diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index bde2e163..004698e7 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -108,6 +108,7 @@ const PLEROMA_POST_ANNOUNCEMENT_URL = '/api/v1/pleroma/admin/announcements' const PLEROMA_EDIT_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}` const PLEROMA_DELETE_ANNOUNCEMENT_URL = id => `/api/v1/pleroma/admin/announcements/${id}` const PLEROMA_SCROBBLES_URL = id => `/api/v1/pleroma/accounts/${id}/scrobbles` +const PLEROMA_STATUS_QUOTES_URL = id => `/api/v1/pleroma/statuses/${id}/quotes` const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config' const PLEROMA_ADMIN_DESCRIPTIONS_URL = '/api/pleroma/admin/config/descriptions' @@ -675,6 +676,7 @@ const fetchTimeline = ({ until = false, userId = false, listId = false, + statusId = false, tag = false, withMuted = false, replyVisibility = 'all', @@ -691,7 +693,8 @@ const fetchTimeline = ({ list: MASTODON_LIST_TIMELINE_URL, favorites: MASTODON_USER_FAVORITES_TIMELINE_URL, tag: MASTODON_TAG_TIMELINE_URL, - bookmarks: MASTODON_BOOKMARK_TIMELINE_URL + bookmarks: MASTODON_BOOKMARK_TIMELINE_URL, + quotes: PLEROMA_STATUS_QUOTES_URL } const isNotifications = timeline === 'notifications' const params = [] @@ -706,6 +709,10 @@ const fetchTimeline = ({ url = url(listId) } + if (timeline === 'quotes') { + url = url(statusId) + } + if (minId) { params.push(['min_id', minId]) } diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index 62ee8549..8ceb897d 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -5,8 +5,8 @@ import followRequestFetcher from '../../services/follow_request_fetcher/follow_r import listsFetcher from '../../services/lists_fetcher/lists_fetcher.service.js' const backendInteractorService = credentials => ({ - startFetchingTimeline ({ timeline, store, userId = false, listId = false, tag }) { - return timelineFetcher.startFetching({ timeline, store, credentials, userId, listId, tag }) + startFetchingTimeline ({ timeline, store, userId = false, listId = false, statusId = false, tag }) { + return timelineFetcher.startFetching({ timeline, store, credentials, userId, listId, statusId, tag }) }, fetchTimeline (args) { diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 85da5223..4d671c5d 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -329,6 +329,7 @@ export const parseStatus = (data) => { output.quote_id = pleroma.quote_id ? pleroma.quote_id : (output.quote ? output.quote.id : undefined) output.quote_url = pleroma.quote_url output.quote_visible = pleroma.quote_visible + output.quotes_count = pleroma.quotes_count } else { output.text = data.content output.summary = data.spoiler_text diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 8501907e..2fed14bc 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -24,6 +24,7 @@ const fetchAndUpdate = ({ showImmediately = false, userId = false, listId = false, + statusId = false, tag = false, until, since @@ -47,6 +48,7 @@ const fetchAndUpdate = ({ args.userId = userId args.listId = listId + args.statusId = statusId args.tag = tag args.withMuted = !hideMutedPosts if (loggedIn && ['friends', 'public', 'publicAndExternal'].includes(timeline)) { @@ -78,15 +80,15 @@ const fetchAndUpdate = ({ }) } -const startFetching = ({ timeline = 'friends', credentials, store, userId = false, listId = false, tag = false }) => { +const startFetching = ({ timeline = 'friends', credentials, store, userId = false, listId = false, statusId = false, tag = false }) => { const rootState = store.rootState || store.state const timelineData = rootState.statuses.timelines[camelCase(timeline)] const showImmediately = timelineData.visibleStatuses.length === 0 timelineData.userId = userId timelineData.listId = listId - fetchAndUpdate({ timeline, credentials, store, showImmediately, userId, listId, tag }) + fetchAndUpdate({ timeline, credentials, store, showImmediately, userId, listId, statusId, tag }) const boundFetchAndUpdate = () => - fetchAndUpdate({ timeline, credentials, store, userId, listId, tag }) + fetchAndUpdate({ timeline, credentials, store, userId, listId, statusId, tag }) return promiseInterval(boundFetchAndUpdate, 10000) } const timelineFetcher = { -- cgit v1.2.3-70-g09d2 From 091532d577ae9a23f7a43f681bfd344f040c7738 Mon Sep 17 00:00:00 2001 From: Ekaterina Vaartis Date: Sun, 7 Jan 2024 02:45:49 +0300 Subject: Editing emojis in popover, pack creation/deletion Also fixed some API calls since they weren't working apparently --- .../settings_modal/admin_tabs/emoji_tab.js | 117 ++++++++++-- .../settings_modal/admin_tabs/emoji_tab.scss | 22 +++ .../settings_modal/admin_tabs/emoji_tab.vue | 206 ++++++++++++++------- .../settings_modal_admin_content.vue | 2 +- src/services/api/api.service.js | 14 +- 5 files changed, 272 insertions(+), 89 deletions(-) (limited to 'src/components') diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.js b/src/components/settings_modal/admin_tabs/emoji_tab.js index f9d3b24e..874d1c3b 100644 --- a/src/components/settings_modal/admin_tabs/emoji_tab.js +++ b/src/components/settings_modal/admin_tabs/emoji_tab.js @@ -1,20 +1,46 @@ +import { clone } from 'lodash' import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import StringSetting from '../helpers/string_setting.vue' import Checkbox from 'components/checkbox/checkbox.vue' import StillImage from 'components/still-image/still-image.vue' +import Select from 'components/select/select.vue' +import Popover from 'components/popover/popover.vue' +import ConfirmModal from 'components/confirm_modal/confirm_modal.vue' +import ModifiedIndicator from '../helpers/modified_indicator.vue' const EmojiTab = { components: { TabSwitcher, StringSetting, Checkbox, - StillImage + StillImage, + Select, + Popover, + ConfirmModal, + ModifiedIndicator }, data () { return { knownPacks: { }, - editedParts: { } + editedParts: { }, + editedMetadata: { }, + packName: '', + newPackName: '', + deleteModalVisible: false + } + }, + + computed: { + pack () { + return this.packName !== '' ? this.knownPacks[this.packName] : undefined + }, + packMeta () { + if (this.editedMetadata[this.packName] === undefined) { + this.editedMetadata[this.packName] = clone(this.knownPacks[this.packName].pack) + } + + return this.editedMetadata[this.packName] } }, @@ -25,37 +51,90 @@ const EmojiTab = { importFromFS () { this.$store.state.api.backendInteractor.importEmojiFromFS() }, - emojiAddr (packName, name) { - return `${this.$store.state.instance.server}/emoji/${encodeURIComponent(packName)}/${name}` + emojiAddr (name) { + return `${this.$store.state.instance.server}/emoji/${encodeURIComponent(this.packName)}/${name}` }, - editEmoji (packName, shortcode) { - if (this.editedParts[packName] === undefined) { this.editedParts[packName] = {} } - this.editedParts[packName][shortcode] = { - shortcode, file: this.knownPacks[packName].files[shortcode] + createEmojiPack () { + this.$store.state.api.backendInteractor.createEmojiPack( + { name: this.newPackName } + ).then(resp => resp.json()).then(resp => { + if (resp === 'ok') { + return this.refreshPackList() + } else { + return Promise.reject(resp) + } + }).then(done => { + this.$refs.createPackPopover.hidePopover() + + this.packName = this.newPackName + this.newPackName = '' + }) + }, + deleteEmojiPack () { + this.$store.state.api.backendInteractor.deleteEmojiPack( + { name: this.packName } + ).then(resp => resp.json()).then(resp => { + if (resp === 'ok') { + return this.refreshPackList() + } else { + return Promise.reject(resp) + } + }).then(done => { + delete this.editedMetadata[this.packName] + delete this.editedParts[this.packName] + + this.deleteModalVisible = false + this.packName = '' + }) + }, + + metaEdited (prop) { + return this.pack && this.packMeta[prop] !== this.pack.pack[prop] + }, + savePackMetadata () { + this.$store.state.api.backendInteractor.saveEmojiPackMetadata({ name: this.packName, newData: this.packMeta }).then( + resp => resp.json() + ).then(resp => { + // Update actual pack data + this.pack.pack = resp + // Delete edited pack data, should auto-update itself + delete this.editedMetadata[this.packName] + }) + }, + + editEmoji (shortcode) { + if (this.editedParts[this.packName] === undefined) { this.editedParts[this.packName] = {} } + + if (this.editedParts[this.packName][shortcode] === undefined) { + this.editedParts[this.packName][shortcode] = { + shortcode, file: this.knownPacks[this.packName].files[shortcode] + } } }, - saveEditedEmoji (packName, shortcode) { - const edited = this.editedParts[packName][shortcode] + saveEditedEmoji (shortcode) { + const edited = this.editedParts[this.packName][shortcode] this.$store.state.api.backendInteractor.updateEmojiFile( - { packName, shortcode, newShortcode: edited.shortcode, newFilename: edited.file, force: false } + { packName: this.packName, shortcode, newShortcode: edited.shortcode, newFilename: edited.file, force: false } ).then(resp => resp.ok ? resp.json() : resp.text().then(respText => Promise.reject(respText)) ).then(resp => { - this.knownPacks[packName].files = resp - delete this.editedParts[packName][shortcode] + this.knownPacks[this.packName].files = resp + delete this.editedParts[this.packName][shortcode] }) + }, + refreshPackList () { + return this.$store.state.api.backendInteractor.listEmojiPacks() + .then(data => data.json()) + .then(packData => { + this.knownPacks = packData.packs + }) } }, mounted () { - this.$store.state.api.backendInteractor.listEmojiPacks() - .then(data => data.json()) - .then(packData => { - this.knownPacks = packData.packs - console.log(this.knownPacks) - }) + this.refreshPackList() } } diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.scss b/src/components/settings_modal/admin_tabs/emoji_tab.scss index 397580af..89bf2f7f 100644 --- a/src/components/settings_modal/admin_tabs/emoji_tab.scss +++ b/src/components/settings_modal/admin_tabs/emoji_tab.scss @@ -1,3 +1,5 @@ +@import "src/variables"; + .emoji-tab { .btn-group .btn { margin-left: 0.5em; @@ -21,4 +23,24 @@ width: 32px; height: 32px; } + + .emoji-unsaved { + box-shadow: 2px 3px 5px var(--cBlue, $fallback--cBlue); + } + + .emoji-list { + display: flex; + flex-wrap: wrap; + gap: 1em 1em; + } +} + +.emoji-tab-edit-popover { + padding-left: 0.5em; + padding-right: 0.5em; + padding-bottom: 0.5em; +} + +.emoji-tab-popover-button { + margin-left: 0.5em; } diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.vue b/src/components/settings_modal/admin_tabs/emoji_tab.vue index 699e4afe..534f881a 100644 --- a/src/components/settings_modal/admin_tabs/emoji_tab.vue +++ b/src/components/settings_modal/admin_tabs/emoji_tab.vue @@ -6,84 +6,166 @@

    {{ $t('admin_dash.tabs.emoji') }}

    - - - - +
      +
    • + + +
    • - -
      -
      -
        -
      • -
        Description
        -