diff options
Diffstat (limited to 'apps/settings/src/utils')
-rw-r--r-- | apps/settings/src/utils/appDiscoverParser.spec.ts | 79 | ||||
-rw-r--r-- | apps/settings/src/utils/appDiscoverParser.ts | 48 | ||||
-rw-r--r-- | apps/settings/src/utils/handlers.ts | 33 | ||||
-rw-r--r-- | apps/settings/src/utils/sorting.ts | 14 | ||||
-rw-r--r-- | apps/settings/src/utils/userUtils.ts | 27 | ||||
-rw-r--r-- | apps/settings/src/utils/validate.js | 63 |
6 files changed, 232 insertions, 32 deletions
diff --git a/apps/settings/src/utils/appDiscoverParser.spec.ts b/apps/settings/src/utils/appDiscoverParser.spec.ts new file mode 100644 index 00000000000..2a631014679 --- /dev/null +++ b/apps/settings/src/utils/appDiscoverParser.spec.ts @@ -0,0 +1,79 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { IAppDiscoverElement } from '../constants/AppDiscoverTypes' + +import { describe, expect, it } from 'vitest' +import { filterElements, parseApiResponse } from './appDiscoverParser' + +describe('App Discover API parser', () => { + describe('filterElements', () => { + it('can filter expired elements', () => { + const result = filterElements({ id: 'test', type: 'post', expiryDate: 100 }) + expect(result).toBe(false) + }) + + it('can filter upcoming elements', () => { + const result = filterElements({ id: 'test', type: 'post', date: Date.now() + 10000 }) + expect(result).toBe(false) + }) + + it('ignores element without dates', () => { + const result = filterElements({ id: 'test', type: 'post' }) + expect(result).toBe(true) + }) + + it('allows not yet expired elements', () => { + const result = filterElements({ id: 'test', type: 'post', expiryDate: Date.now() + 10000 }) + expect(result).toBe(true) + }) + + it('allows yet included elements', () => { + const result = filterElements({ id: 'test', type: 'post', date: 100 }) + expect(result).toBe(true) + }) + + it('allows elements included and not expired', () => { + const result = filterElements({ id: 'test', type: 'post', date: 100, expiryDate: Date.now() + 10000 }) + expect(result).toBe(true) + }) + + it('can handle null values', () => { + const result = filterElements({ id: 'test', type: 'post', date: null, expiryDate: null } as unknown as IAppDiscoverElement) + expect(result).toBe(true) + }) + }) + + describe('parseApiResponse', () => { + it('can handle basic post', () => { + const result = parseApiResponse({ id: 'test', type: 'post' }) + expect(result).toEqual({ id: 'test', type: 'post' }) + }) + + it('can handle carousel', () => { + const result = parseApiResponse({ id: 'test', type: 'carousel' }) + expect(result).toEqual({ id: 'test', type: 'carousel' }) + }) + + it('can handle showcase', () => { + const result = parseApiResponse({ id: 'test', type: 'showcase' }) + expect(result).toEqual({ id: 'test', type: 'showcase' }) + }) + + it('throws on unknown type', () => { + expect(() => parseApiResponse({ id: 'test', type: 'foo-bar' })).toThrow() + }) + + it('parses the date', () => { + const result = parseApiResponse({ id: 'test', type: 'showcase', date: '2024-03-19T17:28:19+0000' }) + expect(result).toEqual({ id: 'test', type: 'showcase', date: 1710869299000 }) + }) + + it('parses the expiryDate', () => { + const result = parseApiResponse({ id: 'test', type: 'showcase', expiryDate: '2024-03-19T17:28:19Z' }) + expect(result).toEqual({ id: 'test', type: 'showcase', expiryDate: 1710869299000 }) + }) + }) +}) diff --git a/apps/settings/src/utils/appDiscoverParser.ts b/apps/settings/src/utils/appDiscoverParser.ts new file mode 100644 index 00000000000..1be44f01068 --- /dev/null +++ b/apps/settings/src/utils/appDiscoverParser.ts @@ -0,0 +1,48 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { IAppDiscoverCarousel, IAppDiscoverElement, IAppDiscoverElements, IAppDiscoverPost, IAppDiscoverShowcase } from '../constants/AppDiscoverTypes.ts' + +/** + * Helper to transform the JSON API results to proper frontend objects (app discover section elements) + * + * @param element The JSON API element to transform + */ +export const parseApiResponse = (element: Record<string, unknown>): IAppDiscoverElements => { + const appElement = { ...element } + if (appElement.date) { + appElement.date = Date.parse(appElement.date as string) + } + if (appElement.expiryDate) { + appElement.expiryDate = Date.parse(appElement.expiryDate as string) + } + + if (appElement.type === 'post') { + return appElement as unknown as IAppDiscoverPost + } else if (appElement.type === 'showcase') { + return appElement as unknown as IAppDiscoverShowcase + } else if (appElement.type === 'carousel') { + return appElement as unknown as IAppDiscoverCarousel + } + throw new Error(`Invalid argument, app discover element with type ${element.type ?? 'unknown'} is unknown`) +} + +/** + * Filter outdated or upcoming elements + * @param element Element to check + */ +export const filterElements = (element: IAppDiscoverElement) => { + const now = Date.now() + // Element not yet published + if (element.date && element.date > now) { + return false + } + + // Element expired + if (element.expiryDate && element.expiryDate < now) { + return false + } + return true +} diff --git a/apps/settings/src/utils/handlers.ts b/apps/settings/src/utils/handlers.ts new file mode 100644 index 00000000000..edd9a6c0cff --- /dev/null +++ b/apps/settings/src/utils/handlers.ts @@ -0,0 +1,33 @@ +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { AxiosError } from '@nextcloud/axios' +import { showError } from '@nextcloud/dialogs' +import { translate as t } from '@nextcloud/l10n' + +import logger from '../logger.ts' + +/** + * @param error the error + * @param message the message to display + */ +export function handleError(error: AxiosError, message: string) { + let fullMessage = '' + + if (message) { + fullMessage += message + } + + if (error.response?.status === 429) { + if (fullMessage) { + fullMessage += '\n' + } + fullMessage += t('settings', 'There were too many requests from your network. Retry later or contact your administrator if this is an error.') + } + + fullMessage = fullMessage || t('settings', 'Error') + showError(fullMessage) + logger.error(fullMessage, { error }) +} diff --git a/apps/settings/src/utils/sorting.ts b/apps/settings/src/utils/sorting.ts new file mode 100644 index 00000000000..88f877733cc --- /dev/null +++ b/apps/settings/src/utils/sorting.ts @@ -0,0 +1,14 @@ +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { getCanonicalLocale, getLanguage } from '@nextcloud/l10n' + +export const naturalCollator = Intl.Collator( + [getLanguage(), getCanonicalLocale()], + { + numeric: true, + usage: 'sort', + }, +) diff --git a/apps/settings/src/utils/userUtils.ts b/apps/settings/src/utils/userUtils.ts new file mode 100644 index 00000000000..7d9a516a542 --- /dev/null +++ b/apps/settings/src/utils/userUtils.ts @@ -0,0 +1,27 @@ +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { translate as t } from '@nextcloud/l10n' + +export const unlimitedQuota = { + id: 'none', + label: t('settings', 'Unlimited'), +} + +export const defaultQuota = { + id: 'default', + label: t('settings', 'Default quota'), +} + +/** + * Return `true` if the logged in user does not have permissions to view the + * data of `user` + * @param user The user to check + * @param user.id Id of the user + */ +export const isObfuscated = (user: { id: string, [key: string]: unknown }) => { + const keys = Object.keys(user) + return keys.length === 1 && keys.at(0) === 'id' +} diff --git a/apps/settings/src/utils/validate.js b/apps/settings/src/utils/validate.js index 58d42863f4e..0f76f4e6dc5 100644 --- a/apps/settings/src/utils/validate.js +++ b/apps/settings/src/utils/validate.js @@ -1,23 +1,6 @@ /** - * @copyright 2021, Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ /* @@ -26,19 +9,7 @@ * TODO add nice validation errors for Profile page settings modal */ -import { VALIDATE_EMAIL_REGEX } from '../constants/AccountPropertyConstants' - -/** - * Validate the string input - * - * Generic validator just to check that input is not an empty string* - * - * @param {string} input the input - * @return {boolean} - */ -export function validateStringInput(input) { - return input !== '' -} +import { VALIDATE_EMAIL_REGEX } from '../constants/AccountPropertyConstants.ts' /** * Validate the email input @@ -59,6 +30,22 @@ export function validateEmail(input) { } /** + * Validate the URL input + * + * @param {string} input the input + * @return {boolean} + */ +export function validateUrl(input) { + try { + // eslint-disable-next-line no-new + new URL(input) + return true + } catch (e) { + return false + } +} + +/** * Validate the language input * * @param {object} input the input @@ -71,6 +58,18 @@ export function validateLanguage(input) { } /** + * Validate the locale input + * + * @param {object} input the input + * @return {boolean} + */ +export function validateLocale(input) { + return input.code !== '' + && input.name !== '' + && input.name !== undefined +} + +/** * Validate boolean input * * @param {boolean} input the input |