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
39 changes: 17 additions & 22 deletions core/src/OC/mimeType.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { generateUrl } from '@nextcloud/router'
import { generateUrl, getRootUrl } from '@nextcloud/router'

const iconCache = new Map()

Expand All @@ -23,36 +23,24 @@ export function getIconUrl(mimeType) {
}

if (!iconCache.has(mimeType)) {
// First try to get the correct icon from the current theme
let gotIcon = null
let gotIcon = false
let path = ''
// First try to get the correct icon from the current legacy-theme
if (OC.theme.folder !== '' && Array.isArray(OC.MimeTypeList.themes[OC.theme.folder])) {
path = generateUrl('/themes/' + window.OC.theme.folder + '/core/img/filetypes/')
path = getRootUrl() + '/themes/' + window.OC.theme.folder + '/core/img/filetypes/'
const icon = getMimeTypeIcon(mimeType, window.OC.MimeTypeList.themes[OC.theme.folder])

if (icon !== null) {
if (icon) {
gotIcon = true
path += icon
path += icon + '.svg'
}
}
if (window.OCA.Theming && gotIcon === null) {
path = generateUrl('/apps/theming/img/core/filetypes/')
path += getMimeTypeIcon(mimeType, window.OC.MimeTypeList.files)
gotIcon = true
}

// If we do not yet have an icon fall back to the default
if (gotIcon === null) {
path = generateUrl('/core/img/filetypes/')
path += getMimeTypeIcon(mimeType, window.OC.MimeTypeList.files)
}

path += '.svg'

if (window.OCA.Theming) {
path += '?v=' + window.OCA.Theming.cacheBuster
// theming is always enabled since Nextcloud 20 so we get it from that
if (!gotIcon) {
path = generateUrl('/apps/theming/img/core/filetypes/' + getMimeTypeIcon(mimeType, window.OC.MimeTypeList.files) + '.svg')
}

path += '?v=' + window.OCA.Theming.cacheBuster
// Cache the result
iconCache.set(mimeType, path)
}
Expand Down Expand Up @@ -92,3 +80,10 @@ function getMimeTypeIcon(mimeType, files) {

return null
}

/**
* Clear the icon cache
*/
export function clearIconCache() {
iconCache.clear()
}
112 changes: 42 additions & 70 deletions core/src/tests/OC/mimeType.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,54 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { join } from 'node:path'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { beforeEach, describe, expect, it, test } from 'vitest'
import { clearIconCache, getIconUrl } from '../../OC/mimeType.js'

const generateUrl = vi.hoisted(() => vi.fn((url) => join('/ROOT', url)))

vi.mock('@nextcloud/router', () => ({
generateUrl,
}))

beforeEach(() => {
vi.resetModules()
vi.resetAllMocks()
})

describe('OC.MimeType tests', async () => {
beforeEach(async () => {
describe('OC.MimeType tests', () => {
beforeEach(() => {
window.OC.MimeTypeList = {
aliases: { 'app/foobar': 'foo/bar' },
files: ['folder', 'folder-shared', 'folder-external', 'foo-bar', 'foo', 'file'],
themes: {
abc: ['folder'],
},
}
// @ts-expect-error - mocking global variable
window._oc_webroot = '/ROOT'
// setup for legacy theme
window.OC.theme ??= {}
window.OC.theme.folder = ''
// the theming app is always enabled since Nextcloud 20
window.OCA.Theming ??= {}
window.OCA.Theming.cacheBuster = '1cacheBuster2'
clearIconCache()
})

test('uses icon cache if availble', async () => {
window.OC.theme.folder = 'abc'
expect(getIconUrl('dir')).toEqual('/ROOT/themes/abc/core/img/filetypes/folder.svg?v=1cacheBuster2')
window.OC.theme.folder = ''
expect(getIconUrl('dir')).toEqual('/ROOT/themes/abc/core/img/filetypes/folder.svg?v=1cacheBuster2')
clearIconCache()
expect(getIconUrl('dir')).toEqual('/ROOT/index.php/apps/theming/img/core/filetypes/folder.svg?v=1cacheBuster2')
})

describe('no theme', async () => {
beforeEach(async () => {
window.OC.theme ??= {}
describe('with legacy themes', async () => {
beforeEach(() => {
window.OC.theme.folder = 'abc'
})

it('uses theme path if a theme icon is availble', async () => {
expect(getIconUrl('dir')).toEqual('/ROOT/themes/abc/core/img/filetypes/folder.svg?v=1cacheBuster2')
})

it('fallbacks to the default theme if no icon is available in the theme', async () => {
expect(getIconUrl('dir-shared')).toEqual('/ROOT/index.php/apps/theming/img/core/filetypes/folder-shared.svg?v=1cacheBuster2')
})
})

describe('no legacy theme', async () => {
beforeEach(() => {
window.OC.theme.folder = ''
})

Expand All @@ -47,72 +67,24 @@ describe('OC.MimeType tests', async () => {
// return the file mimetype if we have no matching icon but do have a file icon
{ mimeType: 'foobar', icon: 'file' },
])('returns correct icon', async ({ icon, mimeType }) => {
const { getIconUrl } = await getMethod()
expect(getIconUrl(mimeType)).toEqual(`/ROOT/core/img/filetypes/${icon}.svg`)
expect(getIconUrl(mimeType)).toEqual(`/ROOT/index.php/apps/theming/img/core/filetypes/${icon}.svg?v=1cacheBuster2`)
})

it('returns undefined if the an icon for undefined is requested', async () => {
const { getIconUrl } = await getMethod()
// @ts-expect-error - testing invalid input
expect(getIconUrl(undefined)).toEqual(undefined)
})

it('uses the cache if available', async () => {
const { getIconUrl } = await getMethod()
expect(generateUrl).not.toHaveBeenCalled()

expect(getIconUrl('dir')).toEqual('/ROOT/core/img/filetypes/folder.svg')
expect(generateUrl).toHaveBeenCalledTimes(1)

expect(getIconUrl('dir')).toEqual('/ROOT/core/img/filetypes/folder.svg')
expect(generateUrl).toHaveBeenCalledTimes(1)

expect(getIconUrl('dir-shared')).toEqual('/ROOT/core/img/filetypes/folder-shared.svg')
expect(generateUrl).toHaveBeenCalledTimes(2)
})

it('converts aliases correctly', async () => {
const { getIconUrl } = await getMethod()
expect(getIconUrl('app/foobar')).toEqual('/ROOT/core/img/filetypes/foo-bar.svg')
})
})

describe('with legacy themes', async () => {
beforeEach(async () => {
window.OC.theme ??= {}
window.OC.theme.folder = 'abc'
})

it('uses theme path if a theme icon is availble', async () => {
const { getIconUrl } = await getMethod()
expect(getIconUrl('dir')).toEqual('/ROOT/themes/abc/core/img/filetypes/folder.svg')
})

it('fallbacks to the default theme if no icon is available in the theme', async () => {
const { getIconUrl } = await getMethod()
expect(getIconUrl('dir-shared')).toEqual('/ROOT/core/img/filetypes/folder-shared.svg')
})
})

describe('with theming app', async () => {
beforeEach(async () => {
window.OC.theme ??= {}
window.OC.theme.folder = ''
window.OCA.Theming ??= {}
window.OCA.Theming.cacheBuster = '1cacheBuster2'
expect(getIconUrl('app/foobar')).toEqual('/ROOT/index.php/apps/theming/img/core/filetypes/foo-bar.svg?v=1cacheBuster2')
})

it('uses the correct theming URL', async () => {
const { getIconUrl } = await getMethod()
expect(getIconUrl('dir')).toMatch('/apps/theming/img/core/filetypes/folder.svg')
expect(getIconUrl('dir')).toMatch('/ROOT/index.php/apps/theming/img/core/filetypes/folder.svg?v=1cacheBuster2')
})

it('uses the cache buster', async () => {
const { getIconUrl } = await getMethod()
expect(getIconUrl('file')).toMatch(/\?v=1cacheBuster2$/)
})
})
})

async function getMethod() {
return await import('../../OC/mimeType.js')
}
4 changes: 2 additions & 2 deletions dist/core-login.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/core-login.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/core-main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/core-main.js.map

Large diffs are not rendered by default.

Loading