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
79 changes: 15 additions & 64 deletions apps/files_sharing/src/components/FileListFilterAccount.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,11 @@
</template>

<script setup lang="ts">
import type { IAccountData } from '../filters/AccountFilter.ts'
import type { IAccountData } from '../files_filters/AccountFilter.ts'

import { translate as t } from '@nextcloud/l10n'
import { ShareType } from '@nextcloud/sharing'
import { mdiAccountMultiple } from '@mdi/js'
import { useBrowserLocation } from '@vueuse/core'
import { computed, ref, watch } from 'vue'
import { useNavigation } from '../../../files/src/composables/useNavigation.ts'

import FileListFilter from '../../../files/src/components/FileListFilter/FileListFilter.vue'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
Expand All @@ -61,8 +58,6 @@ const emit = defineEmits<{
(event: 'update:accounts', value: IAccountData[]): void
}>()

const { currentView } = useNavigation()
const currentLocation = useBrowserLocation()
const accountFilter = ref('')
const availableAccounts = ref<IUserSelectData[]>([])
const selectedAccounts = ref<IUserSelectData[]>([])
Expand Down Expand Up @@ -105,72 +100,28 @@ watch(selectedAccounts, () => {
emit('update:accounts', accounts)
})

/**
* Update the accounts owning nodes or have nodes shared to them
* @param path The path inside the current view to load for accounts
*/
async function updateAvailableAccounts(path: string = '/') {
availableAccounts.value = []
if (!currentView.value) {
return
}

const { contents } = await currentView.value.getContents(path)
const available = new Map<string, IUserSelectData>()
for (const node of contents) {
const owner = node.owner
if (owner && !available.has(owner)) {
available.set(owner, {
id: owner,
user: owner,
displayName: node.attributes['owner-display-name'] ?? node.owner,
})
}

const sharees = node.attributes.sharees?.sharee
if (sharees) {
// ensure sharees is an array (if only one share then it is just an object)
for (const sharee of [sharees].flat()) {
// Skip link shares and other without user
if (sharee.id === '') {
continue
}
if (sharee.type !== ShareType.User && sharee.type !== ShareType.Remote) {
continue
}
// Add if not already added
if (!available.has(sharee.id)) {
available.set(sharee.id, {
id: sharee.id,
user: sharee.id,
displayName: sharee['display-name'],
})
}
}
}
}
availableAccounts.value = [...available.values()]
}

/**
* Reset this filter
*/
function resetFilter() {
selectedAccounts.value = []
accountFilter.value = ''
}
defineExpose({ resetFilter, toggleAccount })

// When the current view changes or the current directory,
// then we need to rebuild the available accounts
watch([currentView, currentLocation], () => {
if (currentView.value) {
// we have no access to the files router here...
const path = (currentLocation.value.search ?? '?dir=/').match(/(?<=&|\?)dir=([^&#]+)/)?.[1]
resetFilter()
updateAvailableAccounts(decodeURIComponent(path ?? '/'))
}
}, { immediate: true })
/**
* Update list of available accounts in current view.
*
* @param accounts - Accounts to use
*/
function setAvailableAccounts(accounts: IAccountData[]): void {
availableAccounts.value = accounts.map(({ uid, displayName }) => ({ displayName, id: uid, user: uid }))
}

defineExpose({
resetFilter,
setAvailableAccounts,
toggleAccount,
})
</script>

<style scoped lang="scss">
Expand Down
71 changes: 66 additions & 5 deletions apps/files_sharing/src/files_filters/AccountFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,40 @@
*/
import type { IFileListFilterChip, INode } from '@nextcloud/files'

import { subscribe } from '@nextcloud/event-bus'
import { FileListFilter, registerFileListFilter } from '@nextcloud/files'
import { ShareType } from '@nextcloud/sharing'
import Vue from 'vue'

import FileListFilterAccount from '../components/FileListFilterAccount.vue'

export interface IAccountData {
uid: string
displayName: string
}

type CurrentInstance = Vue & { resetFilter: () => void, toggleAccount: (account: string) => void }
type CurrentInstance = Vue & {
resetFilter: () => void
setAvailableAccounts: (accounts: IAccountData[]) => void
toggleAccount: (account: string) => void
}

/**
* File list filter to filter by owner / sharee
*/
class AccountFilter extends FileListFilter {

private availableAccounts: IAccountData[]
private currentInstance?: CurrentInstance
private filterAccounts?: IAccountData[]

constructor() {
super('files_sharing:account', 100)
this.availableAccounts = []

subscribe('files:list:updated', ({ contents }) => {
this.updateAvailableAccounts(contents)
})
}

public mount(el: HTMLElement) {
Expand All @@ -33,11 +46,11 @@ class AccountFilter extends FileListFilter {
}

const View = Vue.extend(FileListFilterAccount as never)
this.currentInstance = new View({
el,
})
.$on('update:accounts', this.setAccounts.bind(this))
this.currentInstance = new View({ el })
.$on('update:accounts', (accounts?: IAccountData[]) => this.setAccounts(accounts))
.$mount() as CurrentInstance
this.currentInstance
.setAvailableAccounts(this.availableAccounts)
}

public filter(nodes: INode[]): INode[] {
Expand Down Expand Up @@ -70,6 +83,11 @@ class AccountFilter extends FileListFilter {
this.currentInstance?.resetFilter()
}

/**
* Set accounts that should be filtered.
*
* @param accounts - Account to filter or undefined if inactive.
*/
public setAccounts(accounts?: IAccountData[]) {
this.filterAccounts = accounts
let chips: IFileListFilterChip[] = []
Expand All @@ -85,6 +103,49 @@ class AccountFilter extends FileListFilter {
this.filterUpdated()
}

/**
* Update the accounts owning nodes or have nodes shared to them.
*
* @param nodes - The current content of the file list.
*/
protected updateAvailableAccounts(nodes: INode[]): void {
const available = new Map<string, IAccountData>()

for (const node of nodes) {
const owner = node.owner
if (owner && !available.has(owner)) {
available.set(owner, {
uid: owner,
displayName: node.attributes['owner-display-name'] ?? node.owner,
})
}

// ensure sharees is an array (if only one share then it is just an object)
const sharees: { id: string, 'display-name': string, type: ShareType }[] = [node.attributes.sharees?.sharee].flat().filter(Boolean)
for (const sharee of [sharees].flat()) {
// Skip link shares and other without user
if (sharee.id === '') {
continue
}
if (sharee.type !== ShareType.User && sharee.type !== ShareType.Remote) {
continue
}
// Add if not already added
if (!available.has(sharee.id)) {
available.set(sharee.id, {
uid: sharee.id,
displayName: sharee['display-name'],
})
}
}
}

this.availableAccounts = [...available.values()]
if (this.currentInstance) {
this.currentInstance.setAvailableAccounts(this.availableAccounts)
}
}

}

/**
Expand Down
4 changes: 2 additions & 2 deletions dist/files_sharing-init.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/files_sharing-init.js.map

Large diffs are not rendered by default.

Loading