Browse Source

Merge pull request #11786 from nextcloud/chore/noid/migrate-utils-to-typescript

chore(typescript): migrate utils to TS
pull/11794/head
Maksim Sukharev 2 years ago
committed by GitHub
parent
commit
462a560501
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      src/components/CallView/shared/EmptyCallView.vue
  2. 2
      src/components/ConversationSettings/LinkShareSettings.vue
  3. 2
      src/components/LeftSidebar/ConversationsList/Conversation.vue
  4. 2
      src/components/MessagesList/MessagesGroup/Message/Message.vue
  5. 2
      src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue
  6. 2
      src/components/NewConversationDialog/NewConversationDialog.vue
  7. 2
      src/components/RightSidebar/Participants/Participant.vue
  8. 2
      src/components/RightSidebar/SipSettings.vue
  9. 2
      src/components/TopBar/TopBarMenu.vue
  10. 2
      src/stores/sharedItems.js
  11. 2
      src/types/index.ts
  12. 19
      src/utils/__tests__/formattedTime.spec.js
  13. 41
      src/utils/__tests__/getItemTypeFromMessage.spec.js
  14. 63
      src/utils/__tests__/handleUrl.spec.js
  15. 61
      src/utils/__tests__/readableNumber.spec.js
  16. 5
      src/utils/getItemTypeFromMessage.ts
  17. 34
      src/utils/handleUrl.ts
  18. 30
      src/utils/readableNumber.ts

2
src/components/CallView/shared/EmptyCallView.vue

@ -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 {

2
src/components/ConversationSettings/LinkShareSettings.vue

@ -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',

2
src/components/LeftSidebar/ConversationsList/Conversation.vue

@ -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',

2
src/components/MessagesList/MessagesGroup/Message/Message.vue

@ -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

2
src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue

@ -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)

2
src/components/NewConversationDialog/NewConversationDialog.vue

@ -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: '',

2
src/components/RightSidebar/Participants/Participant.vue

@ -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 {

2
src/components/RightSidebar/SipSettings.vue

@ -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',

2
src/components/TopBar/TopBarMenu.vue

@ -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 {

2
src/stores/sharedItems.js

@ -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

2
src/types/index.ts

@ -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,

19
src/utils/__tests__/formattedTime.spec.js

@ -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
src/utils/__tests__/getItemTypeFromMessage.spec.js

@ -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
src/utils/__tests__/handleUrl.spec.js

@ -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
src/utils/__tests__/readableNumber.spec.js

@ -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])
}
})
})
})

5
src/utils/getItemTypeFromMessage.js → src/utils/getItemTypeFromMessage.ts

@ -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
@ -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') {

34
src/services/urlService.js → src/utils/handleUrl.ts

@ -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) {
@ -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'))

30
src/utils/readableNumber.js → src/utils/readableNumber.ts

@ -21,14 +21,14 @@
/**
* Copied from https://www.w3resource.com/javascript-exercises/javascript-string-exercise-17.php
*
* @param {string} str The string to chop
* @param {number} size Size of the chunks
* @return {string[]}
* @param str The string to chop
* @param size Size of the chunks
*/
function stringChop(str, size) {
str = String(str)
size = ~~size
return size > 0 ? str.match(new RegExp('.{1,' + size + '}', 'g')) : [str]
function stringChop(str: string, size: number): string[] {
if (size <= 0) {
return [str]
}
return str.match(new RegExp('.{1,' + size + '}', 'g')) ?? [str]
}
/**
@ -39,18 +39,18 @@ function stringChop(str, size) {
* 943267028 => 943 267 028
* 9432670284 => 943 267 0284
*
* @param {string} number The number to make readable
* @return {string}
* @param number The number to make readable
*/
function readableNumber(number) {
const chunks = stringChop(number, 3)
function readableNumber(number: string | number): string {
const chunks = stringChop(number.toString(), 3)
const lastChunk = chunks.pop()
if (lastChunk.length === 1) {
return chunks.join(' ') + lastChunk
}
return chunks.join(' ') + ' ' + lastChunk
const shouldConcatLastChunk = !lastChunk?.length || lastChunk.length <= 1
return [chunks.join(' '), lastChunk].join(shouldConcatLastChunk ? '' : ' ')
}
export {
readableNumber,
stringChop,
}
Loading…
Cancel
Save