Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/CallView/shared/EmptyCallView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'

import { CONVERSATION, PARTICIPANT } from '../../../constants.js'
import { copyConversationLinkToClipboard } from '../../../services/urlService.js'
import { copyConversationLinkToClipboard } from '../../../utils/handleUrl.ts'

export default {

Expand Down
2 changes: 1 addition & 1 deletion src/components/ConversationSettings/LinkShareSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadi
import NcPasswordField from '@nextcloud/vue/dist/Components/NcPasswordField.js'

import { CONVERSATION } from '../../constants.js'
import { copyConversationLinkToClipboard } from '../../services/urlService.js'
import { copyConversationLinkToClipboard } from '../../utils/handleUrl.ts'

export default {
name: 'LinkShareSettings',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ import ConversationIcon from './../../ConversationIcon.vue'

import { useConversationInfo } from '../../../composables/useConversationInfo.js'
import { PARTICIPANT } from '../../../constants.js'
import { copyConversationLinkToClipboard } from '../../../services/urlService.js'
import { copyConversationLinkToClipboard } from '../../../utils/handleUrl.ts'

export default {
name: 'Conversation',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ import Reactions from './MessagePart/Reactions.vue'
import { CONVERSATION, PARTICIPANT } from '../../../../constants.js'
import { EventBus } from '../../../../services/EventBus.js'
import { useChatExtrasStore } from '../../../../stores/chatExtras.js'
import { getItemTypeFromMessage } from '../../../../utils/getItemTypeFromMessage.js'
import { getItemTypeFromMessage } from '../../../../utils/getItemTypeFromMessage.ts'

const isTranslationAvailable = getCapabilities()?.spreed?.config?.chat?.['has-translation-providers']
// Fallback for the desktop client when connecting to Talk 17
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,9 @@ import NcEmojiPicker from '@nextcloud/vue/dist/Components/NcEmojiPicker.js'

import { PARTICIPANT, CONVERSATION, ATTENDEE } from '../../../../../constants.js'
import { getMessageReminder, removeMessageReminder, setMessageReminder } from '../../../../../services/remindersService.js'
import { copyConversationLinkToClipboard } from '../../../../../services/urlService.js'
import { useIntegrationsStore } from '../../../../../stores/integrations.js'
import { useReactionsStore } from '../../../../../stores/reactions.js'
import { copyConversationLinkToClipboard } from '../../../../../utils/handleUrl.ts'
import { parseMentions } from '../../../../../utils/textParse.ts'

const EmojiIndex = new EmojiIndexFactory(data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ import {
setConversationPassword,
} from '../../services/conversationsService.js'
import { addParticipant } from '../../services/participantsService.js'
import { copyConversationLinkToClipboard } from '../../services/urlService.js'
import { copyConversationLinkToClipboard } from '../../utils/handleUrl.ts'

const NEW_CONVERSATION = {
token: '',
Expand Down
2 changes: 1 addition & 1 deletion src/components/RightSidebar/Participants/Participant.vue
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ import {
callSIPSendDTMF,
} from '../../../services/callsService.js'
import { formattedTime } from '../../../utils/formattedTime.ts'
import { readableNumber } from '../../../utils/readableNumber.js'
import { readableNumber } from '../../../utils/readableNumber.ts'
import { getStatusMessage } from '../../../utils/userStatus.js'

export default {
Expand Down
2 changes: 1 addition & 1 deletion src/components/RightSidebar/SipSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<script>
import { loadState } from '@nextcloud/initial-state'

import { readableNumber } from '../../utils/readableNumber.js'
import { readableNumber } from '../../utils/readableNumber.ts'

export default {
name: 'SipSettings',
Expand Down
2 changes: 1 addition & 1 deletion src/components/TopBar/TopBarMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ import PromotedView from '../../assets/missingMaterialDesignIcons/PromotedView.v

import { useIsInCall } from '../../composables/useIsInCall.js'
import { CALL, CONVERSATION, PARTICIPANT } from '../../constants.js'
import { generateAbsoluteUrl } from '../../services/urlService.js'
import { generateAbsoluteUrl } from '../../utils/handleUrl.ts'
import { callParticipantCollection } from '../../utils/webrtc/index.js'

export default {
Expand Down
2 changes: 1 addition & 1 deletion src/stores/sharedItems.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { defineStore } from 'pinia'
import Vue from 'vue'

import { getSharedItemsOverview, getSharedItems } from '../services/sharedItemsService.js'
import { getItemTypeFromMessage } from '../utils/getItemTypeFromMessage.js'
import { getItemTypeFromMessage } from '../utils/getItemTypeFromMessage.ts'

/**
* @typedef {'media'|'file'|'voice'|'audio'|'location'|'deckcard'|'other'} Type
Expand Down
2 changes: 1 addition & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export type Mention = ParamObject & {
'call-type'?: string,
'icon-url'?: string,
}
type File = ParamObject & {
export type File = ParamObject & {
'size': number,
'path': string,
'link': string,
Expand Down
19 changes: 19 additions & 0 deletions src/utils/__tests__/formattedTime.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { formattedTime } from '../formattedTime.ts'

const TIME = (61 * 60 + 5) * 1000 // 1 hour, 1 minute, 5 seconds in ms

describe('formattedTime', () => {
it('should return the formatted time with optional spacing and padded minutes / seconds', () => {
const result = formattedTime(TIME)
expect(result).toBe('1 : 01 : 05')
const resultCondensed = formattedTime(TIME, true)
expect(resultCondensed).toBe('1:01:05')
})

it('should return fallback string when time value is falsy', () => {
const result = formattedTime(0)
expect(result).toBe('-- : --')
const resultCondensed = formattedTime(0, true)
expect(resultCondensed).toBe('--:--')
})
})
41 changes: 41 additions & 0 deletions src/utils/__tests__/getItemTypeFromMessage.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { SHARED_ITEM } from '../../constants.js'
import { getItemTypeFromMessage } from '../getItemTypeFromMessage.ts'

describe('getItemTypeFromMessage', () => {
it('should return the correct item type for a messages', () => {
const messages = {
1: { messageType: 'comment', messageParameters: { object: { type: 'geo-location' } } },
2: { messageType: 'comment', messageParameters: { object: { type: 'deck-card' } } },
3: { messageType: 'comment', messageParameters: { object: { type: 'talk-poll' } } },
4: { messageType: 'comment', messageParameters: { object: { type: 'some-type' } } },
5: { messageType: 'record-audio', messageParameters: { file: { mimetype: 'audio/mp3' } } },
6: { messageType: 'record-video', messageParameters: { file: { mimetype: 'video/mp4' } } },
7: { messageType: 'voice-message', messageParameters: { file: { mimetype: 'audio/mp3' } } },
8: { messageType: 'comment', messageParameters: { file: { mimetype: 'audio/mp3' } } },
9: { messageType: 'comment', messageParameters: { file: { mimetype: 'image/jpg' } } },
10: { messageType: 'comment', messageParameters: { file: { mimetype: 'video/mp4' } } },
11: { messageType: 'comment', messageParameters: { file: { mimetype: 'text/markdown' } } },
12: { messageType: 'comment', message: 'simple message' },
}

const outputTypes = {
1: SHARED_ITEM.TYPES.LOCATION,
2: SHARED_ITEM.TYPES.DECK_CARD,
3: SHARED_ITEM.TYPES.POLL,
4: SHARED_ITEM.TYPES.OTHER,
5: SHARED_ITEM.TYPES.RECORDING,
6: SHARED_ITEM.TYPES.RECORDING,
7: SHARED_ITEM.TYPES.VOICE,
8: SHARED_ITEM.TYPES.AUDIO,
9: SHARED_ITEM.TYPES.MEDIA,
10: SHARED_ITEM.TYPES.MEDIA,
11: SHARED_ITEM.TYPES.FILE,
12: SHARED_ITEM.TYPES.OTHER,
}

for (const i in messages) {
const type = i + ': ' + getItemTypeFromMessage(messages[i])
expect(type).toBe(i + ': ' + outputTypes[i])
}
})
})
63 changes: 63 additions & 0 deletions src/utils/__tests__/handleUrl.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { showError, showSuccess } from '@nextcloud/dialogs'

import {
generateAbsoluteUrl,
generateFullConversationLink,
copyConversationLinkToClipboard,
} from '../handleUrl.ts'

jest.mock('@nextcloud/dialogs', () => ({
showSuccess: jest.fn(),
showError: jest.fn(),
}))

describe('handleUrl', () => {
describe('generateAbsoluteUrl', () => {
it('should generate url with IS_DESKTOP=false correctly', () => {
const output = generateAbsoluteUrl('/path')
expect(output).toBe('http://localhost/nc-webroot/path')
})

it('should generate url with IS_DESKTOP=true correctly', () => {
const originalIsDesktop = global.IS_DESKTOP
global.IS_DESKTOP = true

const output = generateAbsoluteUrl('/path')
expect(output).toBe('/nc-webroot/path')

global.IS_DESKTOP = originalIsDesktop
})
})

describe('generateFullConversationLink', () => {
it('should generate links with given token', () => {
const link = generateFullConversationLink('TOKEN')
expect(link).toBe('http://localhost/nc-webroot/call/TOKEN')
})

it('should generate links with given token and message id', () => {
const link = generateFullConversationLink('TOKEN', '123')
expect(link).toBe('http://localhost/nc-webroot/call/TOKEN#message_123')
})
})

describe('copyConversationLinkToClipboard', () => {
it('should copy the conversation link and show success message', async () => {
Object.assign(navigator, { clipboard: { writeText: jest.fn().mockResolvedValueOnce() } })

await copyConversationLinkToClipboard('TOKEN', '123')

expect(navigator.clipboard.writeText).toHaveBeenCalledWith('http://localhost/nc-webroot/call/TOKEN#message_123')
expect(showSuccess).toHaveBeenCalled()
})

it('should show error message when copying fails', async () => {
Object.assign(navigator, { clipboard: { writeText: jest.fn().mockRejectedValueOnce() } })

await copyConversationLinkToClipboard('TOKEN', '123')

expect(navigator.clipboard.writeText).toHaveBeenCalledWith('http://localhost/nc-webroot/call/TOKEN#message_123')
expect(showError).toHaveBeenCalled()
})
})
})
61 changes: 61 additions & 0 deletions src/utils/__tests__/readableNumber.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { readableNumber, stringChop } from '../readableNumber.ts'

describe('readableNumber', () => {
describe('stringChop', () => {
it('should return the correct array of numbers', () => {
const numbers = {
1: { number: '123456789', size: 3 },
2: { number: '12345678', size: 3 },
3: { number: '1234567', size: 3 },
4: { number: '123456', size: 2 },
5: { number: '123456', size: 1 },
6: { number: '123456', size: 0 },
7: { number: '123456', size: 6 },
8: { number: '123456', size: 7 },
9: { number: '', size: 3 },
}

const outputTypes = {
1: ['123', '456', '789'],
2: ['123', '456', '78'],
3: ['123', '456', '7'],
4: ['12', '34', '56'],
5: ['1', '2', '3', '4', '5', '6'],
6: ['123456'],
7: ['123456'],
8: ['123456'],
9: [''],
}

for (const i in numbers) {
const output = i + ': ' + stringChop(numbers[i].number, numbers[i].size)
expect(output).toBe(i + ': ' + outputTypes[i])
}
})
})

describe('readableNumber', () => {
it('should return the correct readable number', () => {
const numbers = {
1: 123456789,
2: '123456789',
3: '12345678',
4: '1234567',
5: '',
}

const outputTypes = {
1: '123 456 789',
2: '123 456 789',
3: '123 456 78',
4: '123 4567',
5: '',
}

for (const i in numbers) {
const output = i + ': ' + readableNumber(numbers[i])
expect(output).toBe(i + ': ' + outputTypes[i])
}
})
})
})
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SHARED_ITEM } from '../constants.js'
import type { ChatMessage, File } from '../types'

export const getItemTypeFromMessage = function(message) {
export const getItemTypeFromMessage = function(message: ChatMessage): string {
if (message.messageParameters?.object) {
if (message.messageParameters.object.type === 'geo-location') {
return SHARED_ITEM.TYPES.LOCATION
Expand All @@ -13,7 +14,7 @@ export const getItemTypeFromMessage = function(message) {
}
} else if (message.messageParameters?.file) {
const messageType = message.messageType || ''
const mimetype = message.messageParameters.file?.mimetype || ''
const mimetype = (message.messageParameters.file as File)?.mimetype || ''
if (messageType === 'record-audio' || messageType === 'record-video') {
return SHARED_ITEM.TYPES.RECORDING
} else if (messageType === 'voice-message') {
Expand Down
34 changes: 13 additions & 21 deletions src/services/urlService.js → src/utils/handleUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,16 @@

import { showError, showSuccess } from '@nextcloud/dialogs'
import { generateUrl } from '@nextcloud/router'
import type { UrlOptions } from '@nextcloud/router'

/**
* Generate a full absolute link with @nextcloud/router.generateUrl
*
* @see @nextcloud/router.generateUrl
* @param {string} url - Path
* @param {object} [params] parameters to be replaced into the address
* @param {import('@nextcloud/router').UrlOptions} [options] options for the parameter replacement
* @param {boolean} options.noRewrite True if you want to force index.php being added
* @param {boolean} options.escape Set to false if parameters should not be URL encoded (default true)
* @return {string} Full absolute URL
* @param url - path
* @param [params] parameters to be replaced into the address
* @param [options] options for the parameter replacement
*/
export function generateAbsoluteUrl(url, params, options) {
export function generateAbsoluteUrl(url: string, params?: object, options?: UrlOptions): string {
// TODO: add this function to @nextcloud/router?
const fullPath = generateUrl(url, params, options)
if (!IS_DESKTOP) {
Expand All @@ -45,29 +42,24 @@ export function generateAbsoluteUrl(url, params, options) {
}

/**
* Generate full link to conversation
* Generate a full link to conversation
*
* @param {string} token - Conversation token
* @param {string} [messageId] - messageId for message in conversation link
* @return {string} - Absolute URL to conversation
* @param token - Conversation token
* @param [messageId] - messageId for message in conversation link
*/
export function generateFullConversationLink(token, messageId) {
export function generateFullConversationLink(token: string, messageId?: string): string {
return messageId !== undefined
? generateAbsoluteUrl('/call/{token}#message_{messageId}', {
token,
messageId,
})
? generateAbsoluteUrl('/call/{token}#message_{messageId}', { token, messageId })
: generateAbsoluteUrl('/call/{token}', { token })
}

/**
* Try to copy conversation link to a clipboard and display the result with dialogs
*
* @param {string} token - conversation token
* @param {string} [messageId] - messageId for message in conversation link
* @return {Promise<void>}
* @param token - conversation token
* @param [messageId] - messageId for message in conversation link
*/
export async function copyConversationLinkToClipboard(token, messageId) {
export async function copyConversationLinkToClipboard(token: string, messageId: string) {
try {
await navigator.clipboard.writeText(generateFullConversationLink(token, messageId))
showSuccess(t('spreed', 'Conversation link copied to clipboard'))
Expand Down
Loading