Skip to content

Commit

Permalink
fix: preview loading on shares and favorites pages
Browse files Browse the repository at this point in the history
Fixes the preview app loading on the shares and the favorites pages so only the images of the current file list are being loaded.
  • Loading branch information
JammingBen committed Nov 28, 2024
1 parent af742df commit ca29fc6
Show file tree
Hide file tree
Showing 12 changed files with 219 additions and 93 deletions.
60 changes: 23 additions & 37 deletions packages/web-app-files/src/components/Search/List.vue
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ import {
SearchResult,
useCapabilityStore,
useConfigStore,
useResourcesStore
useResourcesStore,
useSearch
} from '@ownclouders/web-pkg'
import { NoContentMessage } from '@ownclouders/web-pkg'
import { ResourceTable } from '@ownclouders/web-pkg'
Expand Down Expand Up @@ -247,6 +248,7 @@ export default defineComponent({
const { y: fileListHeaderY } = useFileListHeaderPosition()
const clientService = useClientService()
const { getMatchingSpace } = useGetMatchingSpace()
const { buildSearchTerm } = useSearch()
const resourcesStore = useResourcesStore()
const { initResourceList, clearResourceList, setAncestorMetaData } = resourcesStore
Expand Down Expand Up @@ -342,28 +344,21 @@ export default defineComponent({
return { type: 'file', extension: item.icon, isFolder: item.icon == 'folder' } as Resource
}
const buildSearchTerm = (manuallyUpdateFilterChip = false) => {
const query: string[] = []
const humanSearchTerm = unref(searchTerm)
const doSearch = (manuallyUpdateFilterChip = false) => {
const isTitleOnlySearch = queryItemAsString(unref(titleOnlyParam)) == 'true'
const useFullTextSearch = unref(fullTextSearchEnabled) && !isTitleOnlySearch
if (!!humanSearchTerm) {
let nameQuery = `name:"*${humanSearchTerm}*"`
if (useFullTextSearch) {
nameQuery = `(name:"*${humanSearchTerm}*" OR content:"${humanSearchTerm}")`
}
query.push(nameQuery)
}
const humanScopeQuery = unref(scopeQuery)
const isScopedSearch = unref(doUseScope) === 'true'
if (isScopedSearch && humanScopeQuery) {
query.push(`scope:${humanScopeQuery}`)
}
const tags = queryItemAsString(unref(tagParam))
const lastModified = queryItemAsString(unref(lastModifiedParam))
const mediaType = queryItemAsString(unref(mediaTypeParam))
const query = buildSearchTerm({
term: unref(searchTerm),
isTitleOnlySearch,
tags,
lastModified,
mediaType,
scope: queryItemAsString(unref(scopeQuery)),
useScope: unref(doUseScope) === 'true'
})
const updateFilter = (v: Ref<InstanceType<typeof ItemFilter>>) => {
if (manuallyUpdateFilterChip && unref(v)) {
Expand All @@ -377,28 +372,19 @@ export default defineComponent({
}
}
const humanTagsParams = queryItemAsString(unref(tagParam))
if (humanTagsParams) {
const tags = humanTagsParams.split('+').map((t) => `"${t}"`)
query.push(`tag:(${tags.join(' OR ')})`)
if (tags) {
updateFilter(tagFilter)
}
const lastModifiedParams = queryItemAsString(unref(lastModifiedParam))
if (lastModifiedParams) {
query.push(`mtime:${lastModifiedParams}`)
if (lastModified) {
updateFilter(lastModifiedFilter)
}
const mediaTypeParams = queryItemAsString(unref(mediaTypeParam))
if (mediaTypeParams) {
const mediatypes = mediaTypeParams.split('+').map((t) => `"${t}"`)
query.push(`mediatype:(${mediatypes.join(' OR ')})`)
if (mediaType) {
updateFilter(mediaTypeFilter)
}
return query
.sort((a, b) => Number(a.startsWith('scope:')) - Number(b.startsWith('scope:')))
.join(' AND ')
}
const breadcrumbs = computed(() => {
Expand Down Expand Up @@ -429,7 +415,7 @@ export default defineComponent({
if (capabilityStore.filesTags) {
await loadAvailableTagsTask.perform()
}
emit('search', buildSearchTerm())
emit('search', doSearch())
})
watch(
Expand Down Expand Up @@ -458,7 +444,7 @@ export default defineComponent({
}
}
emit('search', buildSearchTerm(true))
emit('search', doSearch(true))
},
{ deep: true }
)
Expand Down
49 changes: 23 additions & 26 deletions packages/web-app-preview/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ import {
Ref
} from 'vue'
import { RouteLocationRaw } from 'vue-router'
import { isShareSpaceResource, Resource } from '@ownclouders/web-client'
import { Resource } from '@ownclouders/web-client'
import {
AppFileHandlingResult,
AppFolderHandlingResult,
Expand All @@ -94,7 +94,9 @@ import {
useRoute,
useRouteQuery,
useRouter,
usePreviewService
usePreviewService,
useGetMatchingSpace,
isLocationSharesActive
} from '@ownclouders/web-pkg'
import MediaControls from './components/MediaControls.vue'
import MediaAudio from './components/Sources/MediaAudio.vue'
Expand Down Expand Up @@ -145,13 +147,18 @@ export default defineComponent({
const { isFileTypeAudio, isFileTypeImage, isFileTypeVideo } = useFileTypes()
const previewService = usePreviewService()
const { dimensions } = usePreviewDimensions()
const { getMatchingSpace } = useGetMatchingSpace()
const activeIndex = ref<number>()
const cachedFiles = ref<Record<string, CachedFile>>({})
const folderLoaded = ref(false)
const isAutoPlayEnabled = ref(true)
const preview = ref<HTMLElement>()
const space = computed(() => {
return getMatchingSpace(unref(activeFilteredFile))
})
const sortBy = computed(() => {
if (!unref(contextRouteQuery)) {
return 'name'
Expand Down Expand Up @@ -209,7 +216,7 @@ export default defineComponent({
if (cachedFile.isImage) {
cachedFile.url = await previewService.loadPreview(
{
space: unref(props.currentFileContext.space),
space: unref(space),
resource: file,
dimensions: unref(dimensions),
processor: ProcessorType.enum.fit
Expand All @@ -219,7 +226,7 @@ export default defineComponent({
)
return
}
cachedFile.url = await props.getUrlForResource(unref(props.currentFileContext.space), file)
cachedFile.url = await props.getUrlForResource(unref(space), file)
} catch (e) {
console.error(e)
cachedFile.isError.value = true
Expand All @@ -235,10 +242,7 @@ export default defineComponent({
return
}
const { params, query } = createFileRouteOptions(
unref(props.currentFileContext.space),
unref(activeFilteredFile)
)
const { params, query } = createFileRouteOptions(unref(space), unref(activeFilteredFile))
router.replace({
...unref(route),
params: { ...unref(route).params, ...params },
Expand All @@ -259,7 +263,7 @@ export default defineComponent({
folderLoaded.value = true
}
;(instance.proxy as any).setActiveFile(unref(props.currentFileContext.driveAliasAndItem))
;(instance.proxy as any).setActiveFile()
},
{ immediate: true }
)
Expand Down Expand Up @@ -302,7 +306,8 @@ export default defineComponent({
isAutoPlayEnabled,
preview,
isFileTypeImage,
loadFileIntoCache
loadFileIntoCache,
space
}
},
Expand Down Expand Up @@ -336,32 +341,24 @@ export default defineComponent({
},
methods: {
setActiveFile(driveAliasAndItem: string) {
setActiveFile() {
for (let i = 0; i < this.filteredFiles.length; i++) {
if (isShareSpaceResource(unref(this.currentFileContext.space))) {
// with share space resources, we don't have an underlying space, so match the file id
if (this.filteredFiles[i].remoteItemId === this.fileId) {
this.activeIndex = i
return
}
this.activeIndex = 0
continue
}
const filterAttr = isLocationSharesActive(this.$router, 'files-shares-with-me')
? 'remoteItemId'
: 'fileId'
if (
unref(this.currentFileContext.space)?.getDriveAliasAndItem(this.filteredFiles[i]) ===
driveAliasAndItem
) {
if (this.filteredFiles[i][filterAttr] === this.fileId) {
this.activeIndex = i
return
}
this.activeIndex = 0
}
},
// react to PopStateEvent ()
handleLocalHistoryEvent() {
const result = this.$router.resolve(document.location as unknown as RouteLocationRaw)
this.setActiveFile(queryItemAsString(result.params.driveAliasAndItem))
this.setActiveFile()
},
goToNext() {
if (this.activeIndex + 1 >= this.filteredFiles.length) {
Expand Down
28 changes: 20 additions & 8 deletions packages/web-app-preview/tests/unit/app.spec.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,83 @@
import App from '../../src/App.vue'
import { nextTick } from 'vue'
import { defaultComponentMocks, defaultPlugins, shallowMount } from '@ownclouders/web-test-helpers'
import { FileContext } from '@ownclouders/web-pkg'
import { FileContext, queryItemAsString } from '@ownclouders/web-pkg'
import { mock } from 'vitest-mock-extended'

vi.mock('@ownclouders/web-pkg', async (importOriginal) => ({
...(await importOriginal<any>()),
queryItemAsString: vi.fn(),
createFileRouteOptions: vi.fn(() => ({ params: {}, query: {} }))
}))

const activeFiles = [
{
id: '1',
fileId: '1',
name: 'bear.png',
mimeType: 'image/png',
path: 'personal/admin/bear.png',
canDownload: () => true
},
{
id: '2',
fileId: '2',
name: 'elephant.png',
mimeType: 'image/png',
path: 'personal/admin/elephant.png',
canDownload: () => true
},
{
id: '3',
fileId: '3',
name: 'wale_sounds.flac',
mimeType: 'audio/flac',
path: 'personal/admin/wale_sounds.flac',
canDownload: () => true
},
{
id: '4',
fileId: '4',
name: 'lonely_sloth_very_sad.gif',
mimeType: 'image/gif',
path: 'personal/admin/lonely_sloth_very_sad.gif',
canDownload: () => true
},
{
id: '5',
fileId: '5',
name: 'tiger_eats_plants.mp4',
mimeType: 'video/mp4',
path: 'personal/admin/tiger_eats_plants.mp4',
canDownload: () => true
},
{
id: '6',
fileId: '6',
name: 'happy_hippo.gif',
mimeType: 'image/gif',
path: 'personal/admin/happy_hippo.gif',
canDownload: () => true
},
{
id: '7',
fileId: '7',
name: 'sleeping_dog.gif',
mimeType: 'image/gif',
path: 'personal/admin/sleeping_dog.gif',
canDownload: () => true
},
{
id: '8',
fileId: '8',
name: 'cat_murr_murr.gif',
mimeType: 'image/gif',
path: 'personal/admin/cat_murr_murr.gif',
canDownload: () => true
},
{
id: '9',
fileId: '9',
name: 'labrador.gif',
mimeType: 'image/gif',
path: 'personal/admin/labrador.gif',
Expand All @@ -77,7 +92,7 @@ describe('Preview app', () => {
await nextTick()

wrapper.vm.cachedFiles = {}
wrapper.vm.setActiveFile('personal/admin/sleeping_dog.gif')
wrapper.vm.goToNext()

await nextTick()

Expand All @@ -94,16 +109,13 @@ describe('Preview app', () => {
function createShallowMountWrapper() {
const mocks = defaultComponentMocks()
mocks.$previewService.loadPreview.mockResolvedValue('')
vi.mocked(queryItemAsString).mockImplementationOnce(() => '1')

return {
wrapper: shallowMount(App, {
props: {
currentFileContext: mock<FileContext>({
path: 'personal/admin/bear.png',
space: {
getDriveAliasAndItem: vi.fn().mockImplementation((file) => {
return activeFiles.find((filteredFile) => filteredFile.id == file.id)?.path
})
}
path: 'personal/admin/bear.png'
}),
activeFiles,
isFolderLoading: true,
Expand Down
1 change: 1 addition & 0 deletions packages/web-app-search/src/portals/SearchBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
class="preview-component"
:provider="provider"
:search-result="providerSearchResultValue"
:term="term"
/>
</li>
</oc-list>
Expand Down
12 changes: 11 additions & 1 deletion packages/web-client/src/helpers/resource/functions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import path, { basename, dirname } from 'path'
import { urlJoin } from '../../utils'
import { DavPermission, DavProperty } from '../../webdav/constants'
import { Resource, ResourceIndicator, TrashResource, WebDavResponseResource } from './types'
import {
Resource,
ResourceIndicator,
SearchResource,
TrashResource,
WebDavResponseResource
} from './types'
import { camelCase } from 'lodash-es'

const fileExtensions = {
Expand All @@ -12,6 +18,10 @@ export const isTrashResource = (resource: Resource): resource is TrashResource =
return Object.hasOwn(resource, 'ddate')
}

export const isSearchResource = (resource: Resource): resource is SearchResource => {
return Object.hasOwn(resource, 'highlights')
}

export const extractDomSelector = (str: string): string => {
return str.replace(/[^A-Za-z0-9\-_]/g, '')
}
Expand Down
Loading

0 comments on commit ca29fc6

Please sign in to comment.