diff --git a/src/services/CapabilitiesManager.ts b/src/services/CapabilitiesManager.ts index 4484b5d59f5..67dcd8cb832 100644 --- a/src/services/CapabilitiesManager.ts +++ b/src/services/CapabilitiesManager.ts @@ -10,7 +10,7 @@ import { getCapabilities as _getCapabilities } from '@nextcloud/capabilities' import { getRemoteCapabilities } from './federationService.ts' import BrowserStorage from '../services/BrowserStorage.js' import { useTalkHashStore } from '../stores/talkHash.js' -import type { Capabilities, Conversation, JoinRoomFullResponse } from '../types/index.ts' +import type { acceptShareResponse, Capabilities, Conversation, JoinRoomFullResponse } from '../types/index.ts' type Config = Capabilities['spreed']['config'] type RemoteCapability = Capabilities & { hash?: string } @@ -149,6 +149,31 @@ export async function setRemoteCapabilities(joinRoomResponse: JoinRoomFullRespon } } +/** + * Fetch new capabilities if remote server is not yet known + * @param acceptShareResponse server response + */ +export async function setRemoteCapabilitiesIfEmpty(acceptShareResponse: Awaited): Promise { + const token = acceptShareResponse.data.ocs.data.token + const remoteServer = acceptShareResponse.data.ocs.data.remoteServer! + + // Check if remote capabilities already exists + if (remoteCapabilities[remoteServer]) { + return + } + + const response = await getRemoteCapabilities(token) + const newRemoteCapabilities = response.data.ocs.data as Capabilities['spreed'] + if (!Object.keys(newRemoteCapabilities).length) { + // data: {} received from server, nothing to update with + return + } + + remoteCapabilities[remoteServer] = { spreed: newRemoteCapabilities } + BrowserStorage.setItem('remoteCapabilities', JSON.stringify(remoteCapabilities)) + patchTokenMap(acceptShareResponse.data.ocs.data) +} + /** * Deep comparison of remote capabilities, whether there are actual changes that require reload * @param newObject new remote capabilities diff --git a/src/services/__tests__/CapabilitiesManager.spec.js b/src/services/__tests__/CapabilitiesManager.spec.js index 7cc136fce09..8375f9e8db0 100644 --- a/src/services/__tests__/CapabilitiesManager.spec.js +++ b/src/services/__tests__/CapabilitiesManager.spec.js @@ -12,6 +12,7 @@ import { hasTalkFeature, getTalkConfig, setRemoteCapabilities, + setRemoteCapabilitiesIfEmpty, } from '../CapabilitiesManager.ts' import { getRemoteCapabilities } from '../federationService.ts' @@ -209,4 +210,39 @@ describe('CapabilitiesManager', () => { expect(BrowserStorage.setItem).toHaveBeenCalledTimes(2) }) }) + + describe('setRemoteCapabilitiesIfEmpty', () => { + const token = 'TOKEN8FED4' + const remoteServer = 'https://nextcloud4.local' + + it('should early return if already has capabilities', async () => { + const [remoteServer, remoteCapabilities] = Object.entries(mockedRemotes)[0] + const token = remoteCapabilities.tokens[0] + const acceptShareResponseMock = generateOCSResponse({ + payload: { token, remoteServer }, + }) + await setRemoteCapabilitiesIfEmpty(acceptShareResponseMock) + expect(BrowserStorage.setItem).toHaveBeenCalledTimes(0) + }) + + it('should early return if no capabilities received from server', async () => { + const acceptShareResponseMock = generateOCSResponse({ + payload: { token, remoteServer }, + }) + const responseMock = generateOCSResponse({ payload: {} }) + getRemoteCapabilities.mockReturnValue(responseMock) + await setRemoteCapabilitiesIfEmpty(acceptShareResponseMock) + expect(BrowserStorage.setItem).toHaveBeenCalledTimes(0) + }) + + it('should set capabilities for new server', async () => { + const acceptShareResponseMock = generateOCSResponse({ + payload: { token, remoteServer }, + }) + const responseMock = generateOCSResponse({ payload: mockedCapabilities.spreed }) + getRemoteCapabilities.mockReturnValue(responseMock) + await setRemoteCapabilitiesIfEmpty(acceptShareResponseMock) + expect(BrowserStorage.setItem).toHaveBeenCalledTimes(1) + }) + }) }) diff --git a/src/stores/__tests__/federation.spec.js b/src/stores/__tests__/federation.spec.js index 3aae72463e6..22ebf6cf5cd 100644 --- a/src/stores/__tests__/federation.spec.js +++ b/src/stores/__tests__/federation.spec.js @@ -4,7 +4,8 @@ */ import { setActivePinia, createPinia } from 'pinia' -import { getShares, acceptShare, rejectShare } from '../../services/federationService.ts' +import { mockedCapabilities } from '../../__mocks__/capabilities.ts' +import { getShares, acceptShare, rejectShare, getRemoteCapabilities } from '../../services/federationService.ts' import { generateOCSErrorResponse, generateOCSResponse } from '../../test-helpers.js' import { useFederationStore } from '../federation.ts' @@ -12,6 +13,7 @@ jest.mock('../../services/federationService', () => ({ getShares: jest.fn(), acceptShare: jest.fn(), rejectShare: jest.fn(), + getRemoteCapabilities: jest.fn(), })) describe('federationStore', () => { @@ -187,6 +189,9 @@ describe('federationStore', () => { const acceptResponse = generateOCSResponse({ payload: room }) acceptShare.mockResolvedValueOnce(acceptResponse) + const responseMock = generateOCSResponse({ payload: mockedCapabilities.spreed }) + getRemoteCapabilities.mockReturnValue(responseMock) + // Act: accept invite const conversation = await federationStore.acceptShare(invites[0].id) diff --git a/src/stores/federation.ts b/src/stores/federation.ts index 9baac76ef67..b1f031dea44 100644 --- a/src/stores/federation.ts +++ b/src/stores/federation.ts @@ -11,6 +11,7 @@ import { t } from '@nextcloud/l10n' import { getBaseUrl } from '@nextcloud/router' import { FEDERATION } from '../constants.ts' +import { setRemoteCapabilitiesIfEmpty } from '../services/CapabilitiesManager.ts' import { getShares, acceptShare, rejectShare } from '../services/federationService.ts' import type { Conversation, FederationInvite, NotificationInvite } from '../types/index.ts' @@ -109,6 +110,8 @@ export const useFederationStore = defineStore('federation', { try { Vue.set(this.pendingShares[id], 'loading', 'accept') const response = await acceptShare(id) + // Fetch remote capabilities for unknown federation server + await setRemoteCapabilitiesIfEmpty(response) this.markInvitationAccepted(id, response.data.ocs.data) this.updatePendingSharesCount(Object.keys(this.pendingShares).length) return response.data.ocs.data