Skip to content

Commit d650265

Browse files
authored
Merge pull request #14007 from nextcloud/fix/noid/groupware-store
fix(integrations): move groupware-related API to dedicated store
2 parents ee6b4ec + 0c60eb1 commit d650265

11 files changed

+275
-163
lines changed

src/components/NewMessage/NewMessage.vue

+5-3
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ import { getTalkConfig, hasTalkFeature } from '../../services/CapabilitiesManage
218218
import { EventBus } from '../../services/EventBus.ts'
219219
import { shareFile } from '../../services/filesSharingServices.js'
220220
import { useChatExtrasStore } from '../../stores/chatExtras.js'
221+
import { useGroupwareStore } from '../../stores/groupware.ts'
221222
import { useSettingsStore } from '../../stores/settings.js'
222223
import { fetchClipboardContent } from '../../utils/clipboard.js'
223224
import { parseSpecialSymbols } from '../../utils/textParse.ts'
@@ -312,6 +313,7 @@ export default {
312313
const { createTemporaryMessage } = useTemporaryMessage()
313314
return {
314315
chatExtrasStore: useChatExtrasStore(),
316+
groupwareStore: useGroupwareStore(),
315317
settingsStore: useSettingsStore(),
316318
supportTypingStatus,
317319
autoComplete,
@@ -474,7 +476,7 @@ export default {
474476
},
475477

476478
userAbsence() {
477-
return this.chatExtrasStore.absence[this.token]
479+
return this.groupwareStore.absence[this.token]
478480
},
479481

480482
showChatSummary() {
@@ -988,13 +990,13 @@ export default {
988990
// TODO replace with status message id 'vacationing'
989991
if (this.conversation.status === 'dnd') {
990992
// Fetch actual absence status from server
991-
await this.chatExtrasStore.getUserAbsence({
993+
await this.groupwareStore.getUserAbsence({
992994
token: this.token,
993995
userId: this.conversation.name,
994996
})
995997
} else {
996998
// Remove stored absence status
997-
this.chatExtrasStore.removeUserAbsence(this.token)
999+
this.groupwareStore.removeUserAbsence(this.token)
9981000
}
9991001
},
10001002

src/components/TopBar/TopBar.vue

+4-4
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ import ConversationIcon from '../ConversationIcon.vue'
144144
import { useGetParticipants } from '../../composables/useGetParticipants.js'
145145
import { AVATAR, CONVERSATION } from '../../constants.js'
146146
import { getTalkConfig } from '../../services/CapabilitiesManager.ts'
147-
import { useChatExtrasStore } from '../../stores/chatExtras.js'
147+
import { useGroupwareStore } from '../../stores/groupware.ts'
148148
import { useSidebarStore } from '../../stores/sidebar.js'
149149
import { getStatusMessage } from '../../utils/userStatus.ts'
150150
import { localCallParticipantModel, localMediaModel } from '../../utils/webrtc/index.js'
@@ -193,7 +193,7 @@ export default {
193193
AVATAR,
194194
localCallParticipantModel,
195195
localMediaModel,
196-
chatExtrasStore: useChatExtrasStore(),
196+
groupwareStore: useGroupwareStore(),
197197
sidebarStore: useSidebarStore(),
198198
isMobile: useIsMobile(),
199199
CONVERSATION,
@@ -284,7 +284,7 @@ export default {
284284
},
285285

286286
nextEvent() {
287-
return this.chatExtrasStore.getNextEvent(this.token)
287+
return this.groupwareStore.getNextEvent(this.token)
288288
},
289289

290290
eventInfo() {
@@ -318,7 +318,7 @@ export default {
318318
// Do not fetch upcoming events for guests (401 unauthorzied) or in sidebar
319319
return
320320
}
321-
this.chatExtrasStore.getUpcomingEvents(value)
321+
this.groupwareStore.getUpcomingEvents(value)
322322
}
323323
},
324324
},

src/services/conversationsService.js

-14
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,6 @@ const fetchConversations = async function(options) {
2121
return axios.get(generateOcsUrl('apps/spreed/api/v4/room'), options)
2222
}
2323

24-
/**
25-
* fetch future events for a given conversation within the next 31 days.
26-
*
27-
* @param {string} location room's absolute url
28-
*/
29-
const getUpcomingEvents = async (location) => {
30-
return axios.get(generateOcsUrl('/apps/dav/api/v1/events/upcoming'), {
31-
params: {
32-
location,
33-
},
34-
})
35-
}
36-
3724
/**
3825
* Fetches a conversation from the server.
3926
*
@@ -356,7 +343,6 @@ export {
356343
fetchConversations,
357344
fetchConversation,
358345
fetchNoteToSelfConversation,
359-
getUpcomingEvents,
360346
searchListedConversations,
361347
createOneToOneConversation,
362348
createGroupConversation,

src/services/coreService.ts

-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { generateOcsUrl } from '@nextcloud/router'
99
import { getTalkConfig, hasTalkFeature } from './CapabilitiesManager.ts'
1010
import { SHARE } from '../constants.js'
1111
import type {
12-
OutOfOfficeResponse,
1312
TaskProcessingResponse,
1413
} from '../types/index.ts'
1514

@@ -64,18 +63,8 @@ const deleteTaskById = async function(id: number, options?: object): Promise<nul
6463
return axios.delete(generateOcsUrl('taskprocessing/task/{id}', { id }), options)
6564
}
6665

67-
/**
68-
* Get absence information for a user (in a given 1-1 conversation).
69-
*
70-
* @param userId user id
71-
*/
72-
const getUserAbsence = async (userId: string): OutOfOfficeResponse => {
73-
return axios.get(generateOcsUrl('/apps/dav/api/v1/outOfOffice/{userId}/now', { userId }))
74-
}
75-
7666
export {
7767
autocompleteQuery,
7868
getTaskById,
7969
deleteTaskById,
80-
getUserAbsence,
8170
}

src/services/groupwareService.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import axios from '@nextcloud/axios'
7+
import { generateOcsUrl } from '@nextcloud/router'
8+
9+
import type {
10+
OutOfOfficeResponse,
11+
UpcomingEventsResponse,
12+
} from '../types/index.ts'
13+
14+
/**
15+
* Get upcoming events for a given conversation within the next 31 days.
16+
* @param location conversation's absolute URL
17+
*/
18+
const getUpcomingEvents = async (location: string): UpcomingEventsResponse => {
19+
return axios.get(generateOcsUrl('/apps/dav/api/v1/events/upcoming'), {
20+
params: {
21+
location,
22+
},
23+
})
24+
}
25+
26+
/**
27+
* Get absence information for a user (in a given 1-1 conversation).
28+
* @param userId user id
29+
*/
30+
const getUserAbsence = async (userId: string): OutOfOfficeResponse => {
31+
return axios.get(generateOcsUrl('/apps/dav/api/v1/outOfOffice/{userId}/now', { userId }))
32+
}
33+
34+
export {
35+
getUpcomingEvents,
36+
getUserAbsence,
37+
}

src/store/conversationsStore.js

+3
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import { talkBroadcastChannel } from '../services/talkBroadcastChannel.js'
6363
import { useBreakoutRoomsStore } from '../stores/breakoutRooms.ts'
6464
import { useChatExtrasStore } from '../stores/chatExtras.js'
6565
import { useFederationStore } from '../stores/federation.ts'
66+
import { useGroupwareStore } from '../stores/groupware.ts'
6667
import { useReactionsStore } from '../stores/reactions.js'
6768
import { useTalkHashStore } from '../stores/talkHash.js'
6869

@@ -370,6 +371,8 @@ const actions = {
370371
// FIXME: rename to deleteConversationsFromStore or a better name
371372
const chatExtrasStore = useChatExtrasStore()
372373
chatExtrasStore.purgeChatExtras(token)
374+
const groupwareStore = useGroupwareStore()
375+
groupwareStore.purgeGroupwareStore(token)
373376
const reactionsStore = useReactionsStore()
374377
reactionsStore.purgeReactionsStore(token)
375378
context.dispatch('purgeMessagesStore', token)

src/stores/__tests__/chatExtras.spec.js

-64
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,11 @@
55
import { setActivePinia, createPinia } from 'pinia'
66

77
import BrowserStorage from '../../services/BrowserStorage.js'
8-
import { getUserAbsence } from '../../services/coreService.ts'
98
import { EventBus } from '../../services/EventBus.ts'
10-
import { generateOCSErrorResponse, generateOCSResponse } from '../../test-helpers.js'
119
import { useChatExtrasStore } from '../chatExtras.js'
1210

13-
jest.mock('../../services/coreService', () => ({
14-
getUserAbsence: jest.fn(),
15-
}))
16-
1711
describe('chatExtrasStore', () => {
1812
const token = 'TOKEN'
19-
const userId = 'alice'
20-
const payload = { id: 1, userId: 'alice', firstDay: '2023-11-15', lastDay: '2023-11-17', status: 'absence status', message: 'absence message' }
2113
let chatExtrasStore
2214

2315
beforeEach(async () => {
@@ -29,57 +21,6 @@ describe('chatExtrasStore', () => {
2921
jest.clearAllMocks()
3022
})
3123

32-
describe('absence status', () => {
33-
it('processes a response from server and stores absence status', async () => {
34-
// Arrange
35-
const response = generateOCSResponse({ payload })
36-
getUserAbsence.mockResolvedValueOnce(response)
37-
38-
// Act
39-
await chatExtrasStore.getUserAbsence({ token, userId })
40-
41-
// Assert
42-
expect(getUserAbsence).toHaveBeenCalledWith(userId)
43-
expect(chatExtrasStore.absence[token]).toEqual(payload)
44-
})
45-
46-
it('does not show error if absence status is not found', async () => {
47-
// Arrange
48-
const errorNotFound = generateOCSErrorResponse({ payload: null, status: 404 })
49-
const errorOther = generateOCSErrorResponse({ payload: null, status: 500 })
50-
getUserAbsence
51-
.mockRejectedValueOnce(errorNotFound)
52-
.mockRejectedValueOnce(errorOther)
53-
console.error = jest.fn()
54-
55-
// Act
56-
await chatExtrasStore.getUserAbsence({ token, userId })
57-
await chatExtrasStore.getUserAbsence({ token, userId })
58-
59-
// Assert
60-
expect(getUserAbsence).toHaveBeenCalledTimes(2)
61-
expect(console.error).toHaveBeenCalledTimes(1)
62-
expect(chatExtrasStore.absence[token]).toEqual(null)
63-
})
64-
65-
it('removes absence status from the store', async () => {
66-
// Arrange
67-
const response = generateOCSResponse({ payload })
68-
getUserAbsence.mockResolvedValueOnce(response)
69-
const token2 = 'TOKEN_2'
70-
71-
// Act
72-
await chatExtrasStore.getUserAbsence({ token, userId })
73-
chatExtrasStore.removeUserAbsence(token)
74-
chatExtrasStore.removeUserAbsence(token2)
75-
76-
// Assert
77-
expect(chatExtrasStore.absence[token]).not.toBeDefined()
78-
expect(chatExtrasStore.absence[token2]).not.toBeDefined()
79-
})
80-
81-
})
82-
8324
describe('reply message', () => {
8425
it('adds reply message id to the store', () => {
8526
// Act
@@ -171,18 +112,13 @@ describe('chatExtrasStore', () => {
171112
describe('purge store', () => {
172113
it('clears store for provided token', async () => {
173114
// Arrange
174-
const response = generateOCSResponse({ payload })
175-
getUserAbsence.mockResolvedValueOnce(response)
176-
177-
await chatExtrasStore.getUserAbsence({ token: 'token-1', userId })
178115
chatExtrasStore.setParentIdToReply({ token: 'token-1', id: 101 })
179116
chatExtrasStore.setChatInput({ token: 'token-1', text: 'message-1' })
180117

181118
// Act
182119
chatExtrasStore.purgeChatExtras('token-1')
183120

184121
// Assert
185-
expect(chatExtrasStore.absence['token-1']).not.toBeDefined()
186122
expect(chatExtrasStore.parentToReply['token-1']).not.toBeDefined()
187123
expect(chatExtrasStore.chatInput['token-1']).not.toBeDefined()
188124
})
+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
import { setActivePinia, createPinia } from 'pinia'
6+
7+
import { getUserAbsence } from '../../services/groupwareService.ts'
8+
import { generateOCSErrorResponse, generateOCSResponse } from '../../test-helpers.js'
9+
import { useGroupwareStore } from '../groupware.ts'
10+
11+
jest.mock('../../services/groupwareService', () => ({
12+
getUserAbsence: jest.fn(),
13+
}))
14+
15+
describe('groupwareStore', () => {
16+
const token = 'TOKEN'
17+
const userId = 'alice'
18+
const payload = { id: 1, userId: 'alice', firstDay: '2023-11-15', lastDay: '2023-11-17', status: 'absence status', message: 'absence message' }
19+
let groupwareStore
20+
21+
beforeEach(async () => {
22+
setActivePinia(createPinia())
23+
groupwareStore = useGroupwareStore()
24+
})
25+
26+
afterEach(async () => {
27+
jest.clearAllMocks()
28+
})
29+
30+
describe('absence status', () => {
31+
it('processes a response from server and stores absence status', async () => {
32+
// Arrange
33+
const response = generateOCSResponse({ payload })
34+
getUserAbsence.mockResolvedValueOnce(response)
35+
36+
// Act
37+
await groupwareStore.getUserAbsence({ token, userId })
38+
39+
// Assert
40+
expect(getUserAbsence).toHaveBeenCalledWith(userId)
41+
expect(groupwareStore.absence[token]).toEqual(payload)
42+
})
43+
44+
it('does not show error if absence status is not found', async () => {
45+
// Arrange
46+
const errorNotFound = generateOCSErrorResponse({ payload: null, status: 404 })
47+
const errorOther = generateOCSErrorResponse({ payload: null, status: 500 })
48+
getUserAbsence
49+
.mockRejectedValueOnce(errorNotFound)
50+
.mockRejectedValueOnce(errorOther)
51+
console.error = jest.fn()
52+
53+
// Act
54+
await groupwareStore.getUserAbsence({ token, userId })
55+
await groupwareStore.getUserAbsence({ token, userId })
56+
57+
// Assert
58+
expect(getUserAbsence).toHaveBeenCalledTimes(2)
59+
expect(console.error).toHaveBeenCalledTimes(1)
60+
expect(groupwareStore.absence[token]).toEqual(null)
61+
})
62+
63+
it('removes absence status from the store', async () => {
64+
// Arrange
65+
const response = generateOCSResponse({ payload })
66+
getUserAbsence.mockResolvedValueOnce(response)
67+
const token2 = 'TOKEN_2'
68+
69+
// Act
70+
await groupwareStore.getUserAbsence({ token, userId })
71+
groupwareStore.removeUserAbsence(token)
72+
groupwareStore.removeUserAbsence(token2)
73+
74+
// Assert
75+
expect(groupwareStore.absence[token]).not.toBeDefined()
76+
expect(groupwareStore.absence[token2]).not.toBeDefined()
77+
})
78+
})
79+
80+
describe('purge store', () => {
81+
it('clears store for provided token', async () => {
82+
// Arrange
83+
const response = generateOCSResponse({ payload })
84+
getUserAbsence.mockResolvedValueOnce(response)
85+
86+
await groupwareStore.getUserAbsence({ token: 'token-1', userId })
87+
88+
// Act
89+
groupwareStore.purgeGroupwareStore('token-1')
90+
91+
// Assert
92+
expect(groupwareStore.absence['token-1']).not.toBeDefined()
93+
})
94+
})
95+
})

0 commit comments

Comments
 (0)