diff --git a/changelog.d/akkoma-sharkey-net-support.add b/changelog.d/akkoma-sharkey-net-support.add new file mode 100644 index 000000000..0e39a4145 --- /dev/null +++ b/changelog.d/akkoma-sharkey-net-support.add @@ -0,0 +1 @@ +Added support for Akkoma and Sharkey.NET backend (tested on Sharkey) diff --git a/index.html b/index.html index a2f928361..f279ed01a 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,7 @@ + diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 1b133a089..39fc6f6f0 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -63,10 +63,11 @@ const getInstanceConfig = async ({ store }) => { const textlimit = data.max_toot_chars const vapidPublicKey = data.pleroma.vapid_public_key + store.dispatch('setInstanceOption', { name: 'pleromaExtensionsAvailable', value: data.pleroma }) store.dispatch('setInstanceOption', { name: 'textlimit', value: textlimit }) store.dispatch('setInstanceOption', { name: 'accountApprovalRequired', value: data.approval_required }) - store.dispatch('setInstanceOption', { name: 'birthdayRequired', value: !!data.pleroma.metadata.birthday_required }) - store.dispatch('setInstanceOption', { name: 'birthdayMinAge', value: data.pleroma.metadata.birthday_min_age || 0 }) + store.dispatch('setInstanceOption', { name: 'birthdayRequired', value: !!data.pleroma?.metadata.birthday_required }) + store.dispatch('setInstanceOption', { name: 'birthdayMinAge', value: data.pleroma?.metadata.birthday_min_age || 0 }) if (vapidPublicKey) { store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey }) @@ -78,6 +79,8 @@ const getInstanceConfig = async ({ store }) => { console.error('Could not load instance config, potentially fatal') console.error(error) } + // We should check for scrobbles support here but it requires userId + // so instead we check for it where it's fetched (statuses.js) } const getBackendProvidedConfig = async () => { @@ -242,7 +245,8 @@ const resolveStaffAccounts = ({ store, accounts }) => { const getNodeInfo = async ({ store }) => { try { - const res = await preloadFetch('/nodeinfo/2.1.json') + let res = await preloadFetch('/nodeinfo/2.1.json') + if (!res.ok) res = await preloadFetch('/nodeinfo/2.0.json') if (res.ok) { const data = await res.json() const metadata = data.metadata @@ -262,6 +266,7 @@ const getNodeInfo = async ({ store }) => { 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') }) + store.dispatch('setInstanceOption', { name: 'localBubbleInstances', value: metadata.localBubbleInstances ?? [] }) const uploadLimits = metadata.uploadLimits store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) }) @@ -280,7 +285,6 @@ const getNodeInfo = async ({ store }) => { const software = data.software store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version }) store.dispatch('setInstanceOption', { name: 'backendRepository', value: software.repository }) - store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: software.name === 'pleroma' }) const priv = metadata.private store.dispatch('setInstanceOption', { name: 'private', value: priv }) diff --git a/src/boot/routes.js b/src/boot/routes.js index da87c6c61..02abf8ce6 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -1,4 +1,5 @@ import PublicTimeline from 'components/public_timeline/public_timeline.vue' +import BubbleTimeline from 'components/bubble_timeline/bubble_timeline.vue' import PublicAndExternalTimeline from 'components/public_and_external_timeline/public_and_external_timeline.vue' import FriendsTimeline from 'components/friends_timeline/friends_timeline.vue' import TagTimeline from 'components/tag_timeline/tag_timeline.vue' @@ -54,6 +55,7 @@ export default (store) => { { name: 'friends', path: '/main/friends', component: FriendsTimeline, beforeEnter: validateAuthenticatedRoute }, { name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline }, { name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline }, + { name: 'bubble', path: '/bubble', component: BubbleTimeline }, { name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } }, { name: 'quotes', path: '/notice/:id/quotes', component: QuotesTimeline }, { diff --git a/src/components/bubble_timeline/bubble_timeline.js b/src/components/bubble_timeline/bubble_timeline.js new file mode 100644 index 000000000..6f73dd2b8 --- /dev/null +++ b/src/components/bubble_timeline/bubble_timeline.js @@ -0,0 +1,18 @@ +import Timeline from '../timeline/timeline.vue' +const BubbleTimeline = { + components: { + Timeline + }, + computed: { + timeline () { return this.$store.state.statuses.timelines.bubble } + }, + created () { + this.$store.dispatch('startFetchingTimeline', { timeline: 'bubble' }) + }, + unmounted () { + this.$store.dispatch('stopFetchingTimeline', 'bubble') + } + +} + +export default BubbleTimeline diff --git a/src/components/bubble_timeline/bubble_timeline.vue b/src/components/bubble_timeline/bubble_timeline.vue new file mode 100644 index 000000000..4aefa2729 --- /dev/null +++ b/src/components/bubble_timeline/bubble_timeline.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index 681aaf05b..a155abe0c 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -15,6 +15,7 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faUsers, faGlobe, + faCity, faBookmark, faEnvelope, faChevronDown, @@ -31,6 +32,7 @@ import { library.add( faUsers, faGlobe, + faCity, faBookmark, faEnvelope, faChevronDown, @@ -108,12 +110,15 @@ const NavPanel = { privateMode: state => state.instance.private, federating: state => state.instance.federating, pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable, - bookmarkFolders: state => state.instance.pleromaBookmarkFoldersAvailable + bookmarkFolders: state => state.instance.pleromaBookmarkFoldersAvailable, + bubbleTimeline: state => state.instance.localBubbleInstances.length > 0 }), timelinesItems () { return filterNavigation( Object .entries({ ...TIMELINES }) + // do not show in timeliens list since it's in a better place now + .filter(([key]) => key !== 'bookmarks') .map(([k, v]) => ({ ...v, name: k })), { hasChats: this.pleromaChatMessagesAvailable, @@ -121,6 +126,7 @@ const NavPanel = { isFederating: this.federating, isPrivate: this.privateMode, currentUser: this.currentUser, + supportsBubbleTimeline: this.bubbleTimeline, supportsBookmarkFolders: this.bookmarkFolders } ) @@ -136,6 +142,7 @@ const NavPanel = { isFederating: this.federating, isPrivate: this.privateMode, currentUser: this.currentUser, + supportsBubbleTimeline: this.bubbleTimeline, supportsBookmarkFolders: this.bookmarkFolders } ) diff --git a/src/components/navigation/filter.js b/src/components/navigation/filter.js index 12ab9266e..54abb67b4 100644 --- a/src/components/navigation/filter.js +++ b/src/components/navigation/filter.js @@ -1,4 +1,12 @@ -export const filterNavigation = (list = [], { hasChats, hasAnnouncements, isFederating, isPrivate, currentUser, supportsBookmarkFolders }) => { +export const filterNavigation = (list = [], { + hasChats, + hasAnnouncements, + isFederating, + isPrivate, + currentUser, + supportsBookmarkFolders, + supportsBubbleTimeline +}) => { return list.filter(({ criteria, anon, anonRoute }) => { const set = new Set(criteria || []) if (!isFederating && set.has('federating')) return false @@ -7,6 +15,8 @@ export const filterNavigation = (list = [], { hasChats, hasAnnouncements, isFede if ((!currentUser || !currentUser.locked) && set.has('lockedUser')) return false if (!hasChats && set.has('chats')) return false if (!hasAnnouncements && set.has('announcements')) return false + if (!supportsBubbleTimeline && set.has('supportsBubbleTimeline')) return false + if (!supportsBookmarkFolders && set.has('supportsBookmarkFolders')) return false if (supportsBookmarkFolders && set.has('!supportsBookmarkFolders')) return false return true }) @@ -19,11 +29,11 @@ export const getListEntries = store => store.allLists.map(list => ({ iconLetter: list.title[0] })) -export const getBookmarkFolderEntries = store => store.allFolders.map(folder => ({ +export const getBookmarkFolderEntries = store => store.allFolders ? store.allFolders.map(folder => ({ name: 'bookmark-folder-' + folder.id, routeObject: { name: 'bookmark-folder', params: { id: folder.id } }, labelRaw: folder.name, iconEmoji: folder.emoji, iconEmojiUrl: folder.emoji_url, iconLetter: folder.name[0] -})) +})) : [] diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js index a8e1fc966..d1c2b6763 100644 --- a/src/components/navigation/navigation.js +++ b/src/components/navigation/navigation.js @@ -27,6 +27,13 @@ export const TIMELINES = { label: 'nav.public_tl', criteria: ['!private'] }, + bubble: { + route: 'bubble', + anon: true, + icon: 'city', + label: 'nav.bubble', + criteria: ['!private', 'federating', 'supportsBubbleTimeline'] + }, twkn: { route: 'public-external-timeline', anon: true, @@ -34,11 +41,11 @@ export const TIMELINES = { label: 'nav.twkn', criteria: ['!private', 'federating'] }, + // bookmarks are still technically a timeline so we should show it in the dropdown bookmarks: { route: 'bookmarks', icon: 'bookmark', label: 'nav.bookmarks', - criteria: ['!supportsBookmarkFolders'] }, favorites: { routeObject: { name: 'user-profile', query: { tab: 'favorites' } }, @@ -53,6 +60,15 @@ export const TIMELINES = { } export const ROOT_ITEMS = { + bookmarks: { + route: 'bookmarks', + icon: 'bookmark', + label: 'nav.bookmarks', + // shows bookmarks entry in a better suited location + // hides it when bookmark folders are supported since + // we show custom component instead of it + criteria: ['!supportsBookmarkFolders'] + }, interactions: { route: 'interactions', icon: 'bell', diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js index 50acbbaf1..f9a900fc6 100644 --- a/src/components/navigation/navigation_pins.js +++ b/src/components/navigation/navigation_pins.js @@ -9,6 +9,7 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faUsers, faGlobe, + faCity, faBookmark, faEnvelope, faComments, @@ -25,6 +26,7 @@ import { useServerSideStorageStore } from 'src/stores/serverSideStorage' library.add( faUsers, faGlobe, + faCity, faBookmark, faEnvelope, faComments, @@ -65,7 +67,8 @@ const NavPanel = { followRequestCount: state => state.api.followRequests.length, privateMode: state => state.instance.private, federating: state => state.instance.federating, - pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable + pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable, + bubbleTimeline: state => state.instance.localBubbleInstances.length > 0 }), pinnedList () { if (!this.currentUser) { @@ -79,7 +82,9 @@ const NavPanel = { hasAnnouncements: this.supportsAnnouncements, isFederating: this.federating, isPrivate: this.privateMode, - currentUser: this.currentUser + currentUser: this.currentUser, + supportsBubbleTimeline: this.bubbleTimeline, + supportsBookmarkFolders: this.bookmarks }) } return filterNavigation( @@ -98,6 +103,8 @@ const NavPanel = { { hasChats: this.pleromaChatMessagesAvailable, hasAnnouncements: this.supportsAnnouncements, + supportsBubbleTimeline: this.bubbleTimeline, + supportsBookmarkFolders: this.bookmarks, isFederating: this.federating, isPrivate: this.privateMode, currentUser: this.currentUser diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.js b/src/components/settings_modal/tabs/security_tab/security_tab.js index 3cbc0c927..04e0328cd 100644 --- a/src/components/settings_modal/tabs/security_tab/security_tab.js +++ b/src/components/settings_modal/tabs/security_tab/security_tab.js @@ -41,8 +41,8 @@ const SecurityTab = { user () { return this.$store.state.users.currentUser }, - pleromaBackend () { - return this.$store.state.instance.pleromaBackend + pleromaExtensionsAvailable () { + return this.$store.state.instance.pleromaExtensionsAvailable }, oauthTokens () { return useOAuthTokensStore().tokens.map(oauthToken => { diff --git a/src/components/status/status.js b/src/components/status/status.js index 7ee2632bd..ba6fe1b68 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -24,6 +24,7 @@ import { faLock, faLockOpen, faGlobe, + faIgloo, faTimes, faRetweet, faReply, @@ -43,6 +44,7 @@ import { library.add( faEnvelope, faGlobe, + faIgloo, faLock, faLockOpen, faTimes, @@ -484,6 +486,8 @@ const Status = { return 'lock-open' case 'direct': return 'envelope' + case 'local': + return 'igloo' default: return 'globe' } diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js index 38e6bdf4f..4c8f7b76e 100644 --- a/src/components/timeline_menu/timeline_menu.js +++ b/src/components/timeline_menu/timeline_menu.js @@ -24,7 +24,8 @@ export const timelineNames = (supportsBookmarkFolders) => { dms: 'nav.dms', 'public-timeline': 'nav.public_tl', 'public-external-timeline': 'nav.twkn', - quotes: 'nav.quotes' + quotes: 'nav.quotes', + bubble: 'nav.bubble' } } @@ -58,7 +59,8 @@ const TimelineMenu = { currentUser: state => state.users.currentUser, privateMode: state => state.instance.private, federating: state => state.instance.federating, - bookmarkFolders: state => state.instance.pleromaBookmarkFoldersAvailable + bookmarkFolders: state => state.instance.pleromaBookmarkFoldersAvailable, + bubbleTimeline: state => state.instance.localBubbleInstances.length > 0 }), timelinesList () { return filterNavigation( @@ -68,7 +70,8 @@ const TimelineMenu = { isFederating: this.federating, isPrivate: this.privateMode, currentUser: this.currentUser, - supportsBookmarkFolders: this.bookmarkFolders + supportsBookmarkFolders: this.bookmarkFolders, + supportsBubbleTimeline: this.bubbleTimeline } ) } diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index be81b8ad5..55c76225c 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -134,7 +134,10 @@ export default { }, showModerationMenu () { const privileges = this.loggedIn.privileges - return this.loggedIn.role === 'admin' || privileges.includes('users_manage_activation_state') || privileges.includes('users_delete') || privileges.includes('users_manage_tags') + return this.loggedIn.role === 'admin' || + privileges.includes('users_manage_activation_state') || + privileges.includes('users_delete') || + privileges.includes('users_manage_tags') }, hasNote () { return this.relationship.note diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 751bfd5a0..49d6fd60a 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -81,7 +81,7 @@ const UserProfile = { return this.isUs || !this.user.hide_followers }, favoritesTabVisible () { - return this.isUs || !this.user.hide_favorites + return this.isUs || (this.$store.state.instance.pleromaPublicFavouritesAvailable && !this.user.hide_favorites) }, formattedBirthday () { const browserLocale = localeService.internalToBrowserLocale(this.$i18n.locale) diff --git a/src/i18n/en.json b/src/i18n/en.json index 019beba1c..14f36844b 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -117,6 +117,7 @@ "flash_security": "Note that this can be potentially dangerous since Flash content is still arbitrary code.", "flash_fail": "Failed to load flash content, see console for details.", "scope_in_timeline": { + "local": "Non-federated", "direct": "Direct", "private": "Followers-only", "public": "Public", @@ -171,6 +172,7 @@ "interactions": "Interactions", "dms": "Direct messages", "public_tl": "Public timeline", + "bubble": "Bubble timeline", "timeline": "Timeline", "home_timeline": "Home timeline", "twkn": "Known Network", @@ -289,7 +291,8 @@ "text/plain": "Plain text", "text/html": "HTML", "text/markdown": "Markdown", - "text/bbcode": "BBCode" + "text/bbcode": "BBCode", + "text/x.misskeymarkdown": "MFM" }, "content_type_selection": "Post format", "content_warning": "Subject (optional)", diff --git a/src/modules/api.js b/src/modules/api.js index 69c8282b8..ab8d7162f 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -211,6 +211,7 @@ const api = { statusId = false, bookmarkFolderId = false }) { + if (timeline === 'favourites' && !store.rootState.instance.pleromaPublicFavouritesAvailable) return if (store.state.fetchers[timeline]) return const fetcher = store.state.backendInteractor.startFetchingTimeline({ @@ -281,6 +282,7 @@ const api = { // Bookmark folders startFetchingBookmarkFolders (store) { if (store.state.fetchers.bookmarkFolders) return + if (!store.rootState.instance.pleromaBookmarkFoldersAvailable) return const fetcher = store.state.backendInteractor.startFetchingBookmarkFolders({ store }) store.commit('addFetcher', { fetcherName: 'bookmarkFolders', fetcher }) }, diff --git a/src/modules/instance.js b/src/modules/instance.js index 83671a881..39d1fc662 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -143,7 +143,7 @@ const defaultState = { emoji: {}, emojiFetched: false, unicodeEmojiAnnotations: {}, - pleromaBackend: true, + pleromaExtensionsAvailable: true, postFormats: [], restrictedNicknames: [], safeDM: true, @@ -156,12 +156,14 @@ const defaultState = { pleromaChatMessagesAvailable: false, pleromaCustomEmojiReactionsAvailable: false, pleromaBookmarkFoldersAvailable: false, + pleromaPublicFavouritesAvailable: true, gopherAvailable: false, mediaProxyAvailable: false, suggestionsEnabled: false, suggestionsWeb: '', quotingAvailable: false, groupActorAvailable: false, + localBubbleInstances: [], // Akkoma // Html stuff instanceSpecificPanelContent: '', @@ -340,7 +342,10 @@ const instance = { async getCustomEmoji ({ commit, state }) { try { - const res = await window.fetch('/api/pleroma/emoji.json') + let res = await window.fetch('/api/v1/pleroma/emoji') + if (!res.ok) { + res = await window.fetch('/api/pleroma/emoji.json') + } if (res.ok) { const result = await res.json() const values = Array.isArray(result) ? Object.assign({}, ...result) : result diff --git a/src/modules/statuses.js b/src/modules/statuses.js index d08b3334c..efdfc5894 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -39,6 +39,7 @@ export const defaultState = () => ({ conversationsObject: {}, maxId: 0, favorites: new Set(), + pleromaScrobblesAvailable: true, // not reported in nodeinfo timelines: { mentions: emptyTl(), public: emptyTl(), @@ -50,7 +51,8 @@ export const defaultState = () => ({ tag: emptyTl(), dms: emptyTl(), bookmarks: emptyTl(), - list: emptyTl() + list: emptyTl(), + bubble: emptyTl() } }) @@ -108,12 +110,21 @@ const sortTimeline = (timeline) => { } const getLatestScrobble = (state, user) => { + const scrobbles = state.pleromaScrobblesAvailable + if (!scrobbles) return + if (state.scrobblesNextFetch[user.id] && state.scrobblesNextFetch[user.id] > Date.now()) { return } state.scrobblesNextFetch[user.id] = Date.now() + 24 * 60 * 60 * 1000 + if (!scrobbles) return apiService.fetchScrobbles({ accountId: user.id }).then((scrobbles) => { + if (scrobbles?.error?.status === 501) { + state.pleromaScrobblesAvailable = false + return + } + if (scrobbles.length > 0) { user.latestScrobble = scrobbles[0] diff --git a/src/modules/users.js b/src/modules/users.js index 01936c716..c59c04dff 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -599,6 +599,7 @@ const users = { return new Promise((resolve, reject) => { const commit = store.commit const dispatch = store.dispatch + const rootState = store.rootState commit('beginLogin') store.rootState.api.backendInteractor.verifyCredentials(accessToken) .then((data) => { @@ -665,8 +666,10 @@ const users = { // Start fetching notifications dispatch('startFetchingNotifications') - // Start fetching chats - dispatch('startFetchingChats') + if (rootState.instance.pleromaChatMessagesAvailable) { + // Start fetching chats + dispatch('startFetchingChats') + } } dispatch('startFetchingLists') diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 117c621d9..6de94278e 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -15,7 +15,7 @@ const TAG_USER_URL = '/api/pleroma/admin/users/tag' const PERMISSION_GROUP_URL = (screenName, right) => `/api/pleroma/admin/users/${screenName}/permission_group/${right}` const ACTIVATE_USER_URL = '/api/pleroma/admin/users/activate' const DEACTIVATE_USER_URL = '/api/pleroma/admin/users/deactivate' -const ADMIN_USERS_URL = '/api/pleroma/admin/users' +const ADMIN_USERS_URL = '/api/v1/pleroma/admin/users' const SUGGESTIONS_URL = '/api/v1/suggestions' const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings' const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read' @@ -61,6 +61,7 @@ const MASTODON_LIST_TIMELINE_URL = id => `/api/v1/timelines/list/${id}` const MASTODON_LIST_ACCOUNTS_URL = id => `/api/v1/lists/${id}/accounts` const MASTODON_TAG_TIMELINE_URL = tag => `/api/v1/timelines/tag/${tag}` const MASTODON_BOOKMARK_TIMELINE_URL = '/api/v1/bookmarks' +const AKKOMA_BUBBLE_TIMELINE_URL = '/api/v1/timelines/bubble' const MASTODON_USER_BLOCKS_URL = '/api/v1/blocks/' const MASTODON_USER_MUTES_URL = '/api/v1/mutes/' const MASTODON_BLOCK_USER_URL = id => `/api/v1/accounts/${id}/block` @@ -99,7 +100,7 @@ const PLEROMA_CHAT_URL = id => `/api/v1/pleroma/chats/by-account-id/${id}` const PLEROMA_CHAT_MESSAGES_URL = id => `/api/v1/pleroma/chats/${id}/messages` const PLEROMA_CHAT_READ_URL = id => `/api/v1/pleroma/chats/${id}/read` const PLEROMA_DELETE_CHAT_MESSAGE_URL = (chatId, messageId) => `/api/v1/pleroma/chats/${chatId}/messages/${messageId}` -const PLEROMA_ADMIN_REPORTS = '/api/pleroma/admin/reports' +const PLEROMA_ADMIN_REPORTS = '/api/v1/pleroma/admin/reports' const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups' const PLEROMA_ANNOUNCEMENTS_URL = '/api/v1/pleroma/admin/announcements' const PLEROMA_POST_ANNOUNCEMENT_URL = '/api/v1/pleroma/admin/announcements' @@ -111,10 +112,10 @@ const PLEROMA_USER_FAVORITES_TIMELINE_URL = id => `/api/v1/pleroma/accounts/${id const PLEROMA_BOOKMARK_FOLDERS_URL = '/api/v1/pleroma/bookmark_folders' const PLEROMA_BOOKMARK_FOLDER_URL = id => `/api/v1/pleroma/bookmark_folders/${id}` -const PLEROMA_ADMIN_CONFIG_URL = '/api/pleroma/admin/config' -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_ADMIN_CONFIG_URL = '/api/v1/pleroma/admin/config' +const PLEROMA_ADMIN_DESCRIPTIONS_URL = '/api/v1/pleroma/admin/config/descriptions' +const PLEROMA_ADMIN_FRONTENDS_URL = '/api/v1/pleroma/admin/frontends' +const PLEROMA_ADMIN_FRONTENDS_INSTALL_URL = '/api/v1/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' @@ -707,7 +708,8 @@ const fetchTimeline = ({ publicFavorites: PLEROMA_USER_FAVORITES_TIMELINE_URL, tag: MASTODON_TAG_TIMELINE_URL, bookmarks: MASTODON_BOOKMARK_TIMELINE_URL, - quotes: PLEROMA_STATUS_QUOTES_URL + quotes: PLEROMA_STATUS_QUOTES_URL, + bubble: AKKOMA_BUBBLE_TIMELINE_URL } const isNotifications = timeline === 'notifications' const params = [] diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index d9e3d427a..61eb5313f 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -91,6 +91,8 @@ export const parseUser = (data) => { output.bot = data.bot + output.privileges = [] + if (data.pleroma) { if (data.pleroma.settings_store) { output.storage = data.pleroma.settings_store['pleroma-fe'] @@ -317,20 +319,18 @@ export const parseStatus = (data) => { output.edited_at = data.edited_at + const { pleroma } = data + if (data.pleroma) { - const { pleroma } = data output.text = pleroma.content ? data.pleroma.content['text/plain'] : data.content output.summary = pleroma.spoiler_text ? data.pleroma.spoiler_text['text/plain'] : data.spoiler_text output.statusnet_conversation_id = data.pleroma.conversation_id output.is_local = pleroma.local - output.in_reply_to_screen_name = data.pleroma.in_reply_to_account_acct + output.in_reply_to_screen_name = pleroma.in_reply_to_account_acct output.thread_muted = pleroma.thread_muted output.emoji_reactions = pleroma.emoji_reactions output.parent_visible = pleroma.parent_visible === undefined ? true : pleroma.parent_visible - output.quote = pleroma.quote ? parseStatus(pleroma.quote) : undefined - 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.quote_visible = pleroma.quote_visible || true output.quotes_count = pleroma.quotes_count output.bookmark_folder_id = pleroma.bookmark_folder } else { @@ -338,6 +338,12 @@ export const parseStatus = (data) => { output.summary = data.spoiler_text } + const quoteRaw = pleroma?.quote || data.quote + const quoteData = quoteRaw ? parseStatus(quoteRaw) : undefined + output.quote = quoteData + output.quote_id = data.quote?.id ?? data.quote_id ?? quoteData?.id ?? pleroma.quote_id + output.quote_url = data.quote?.url ?? quoteData?.url ?? pleroma.quote_url + output.in_reply_to_status_id = data.in_reply_to_id output.in_reply_to_user_id = data.in_reply_to_account_id output.replies_count = data.replies_count diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index d85fc7305..f64646593 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -54,7 +54,7 @@ const fetchAndUpdate = ({ args.bookmarkFolderId = bookmarkFolderId args.tag = tag args.withMuted = !hideMutedPosts - if (loggedIn && ['friends', 'public', 'publicAndExternal'].includes(timeline)) { + if (loggedIn && ['friends', 'public', 'publicAndExternal', 'bubble'].includes(timeline)) { args.replyVisibility = replyVisibility } @@ -63,6 +63,10 @@ const fetchAndUpdate = ({ return apiService.fetchTimeline(args) .then(response => { if (response.errors) { + if (timeline === 'favorites') { + rootState.instance.pleromaPublicFavouritesAvailable = false + return + } throw new Error(`${response.status} ${response.statusText}`) }