Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move sharing indicators into own component and add extension point #2928

Merged
merged 1 commit into from
Jan 30, 2020
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
75 changes: 74 additions & 1 deletion apps/files/docs/extensionpoints.adoc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Extensionpoints
## Extension points

#### file-details-panel

Expand All @@ -17,3 +17,76 @@

OC.$extend.request('files', 'file-details-panel', payload).then( response => {});
----

### Files list status indicators

An extension can extend status indicators in files list with custom component, properties and methods.
Extending this list can be done by creating a new object inside of appInfo called `filesListIndicators`. This object can contain keys `name` and `component`.

#### Props

Please see known issues before using props in custom indicators.

- `item`: resource to which are indicators assigned
- `parentPath`: parent folder path of the item

#### Example:

##### App entry point
[source,js]
----
import ExampleIndicators from './components/ExampleIndicators.vue'

const appInfo = {
name: 'ExampleIndicators',
id: 'ExampleIndicators',
icon: 'info',
isFileEditor: false,
extensions: [],
filesListIndicators: [{
name: 'CustomIndicators',
component: ExampleIndicators
}]
}
----

##### ExampleIndicators component
[source,vue]
LukasHirt marked this conversation as resolved.
Show resolved Hide resolved
----
<template>
<oc-button
v-if="isFolder"
class="file-row-share-indicator uk-text-middle"
aria-label="Custom indicator"
@click="triggerAlert"
variation="raw"
>
<oc-icon
name="folder"
class="uk-text-middle"
size="small"
variation="active"
/>
</oc-button>
</template>

<script>
export default {
name: 'ExampleIndicators',
computed: {
isFolder () {
return this.$parent.item.type === 'folder'
}
},
methods: {
triggerAlert () {
alert('Hello world')
}
}
}
</script>
----

#### Known issues:

Passing resource as a prop into the dynamic component causes Vuex mutation error. To avoid this, access resource direcly via parent component - `this.$parent.item`
84 changes: 5 additions & 79 deletions apps/files/src/components/AllFilesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,7 @@
class="uk-margin-small-left"
/>
</div>
<div>
<oc-button v-if="$_isUserShare(item)" class="file-row-share-indicator uk-text-middle" :aria-label="$_shareUserIconLabel(item)" @click="$_openSideBar(item, 'files-sharing')" variation="raw">
<oc-icon name="group" class="uk-text-middle" size="small" :variation="$_shareUserIconVariation(item)"/>
</oc-button>
<oc-button v-if="$_isLinkShare(item)" class="file-row-share-indicator uk-text-middle" :aria-label="$_shareLinkIconLabel(item)" @click="$_openSideBar(item, 'file-link')" variation="raw">
<oc-icon name="link" class="uk-text-middle" size="small" :variation="$_shareLinkIconVariation(item)"/>
</oc-button>
</div>
<StatusIndicators :item="item" :parentPath="currentFolder.path" @click="$_openSideBar" />
<div class="uk-text-meta uk-text-nowrap uk-width-small" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-hidden' : _sidebarOpen }">
{{ item.size | fileSize }}
</div>
Expand Down Expand Up @@ -75,18 +68,16 @@
<script>
import FileList from './FileList.vue'
import { mapGetters, mapActions, mapState } from 'vuex'
import { shareTypes } from '../helpers/shareTypes'
import { getParentPaths } from '../helpers/path'

import Mixins from '../mixins'
import FileActions from '../fileactions'
import intersection from 'lodash/intersection'

const userShareTypes = [shareTypes.user, shareTypes.group, shareTypes.guest, shareTypes.remote]
const StatusIndicators = () => import('./FilesLists/StatusIndicators/StatusIndicators.vue')

export default {
components: {
FileList
FileList,
StatusIndicators
},
mixins: [
Mixins,
Expand All @@ -110,46 +101,6 @@ export default {
this.$emit('sideBarOpen', item, sideBarName)
},

$_isDirectUserShare (item) {
return (intersection(userShareTypes, item.shareTypes).length > 0)
},

$_isIndirectUserShare (item) {
return (item.isReceivedShare() || intersection(userShareTypes, this.$_shareTypesIndirect).length > 0)
},

$_isDirectLinkShare (item) {
return (item.shareTypes.indexOf(shareTypes.link) >= 0)
},

$_isIndirectLinkShare (item) {
return (this.$_shareTypesIndirect.indexOf(shareTypes.link) >= 0)
},

$_isUserShare (item) {
return this.$_isDirectUserShare(item) || this.$_isIndirectUserShare(item)
},

$_isLinkShare (item) {
return this.$_isDirectLinkShare(item) || this.$_isIndirectLinkShare(item)
},

$_shareUserIconVariation (item) {
return this.$_isDirectUserShare(item) ? 'active' : 'passive'
},

$_shareLinkIconVariation (item) {
return this.$_isDirectLinkShare(item) ? 'active' : 'passive'
},

$_shareUserIconLabel (item) {
return this.$_isDirectUserShare(item) ? this.$gettext('Directly shared with collaborators') : this.$gettext('Shared with collaborators through one of the parent folders')
},

$_shareLinkIconLabel (item) {
return this.$_isDirectLinkShare(item) ? this.$gettext('Directly shared with links') : this.$gettext('Shared with links through one of the parent folders')
},

$_ocFilesFolder_getFolder () {
this.setFilterTerm('')
let absolutePath
Expand Down Expand Up @@ -216,38 +167,13 @@ export default {
},
computed: {
...mapState(['route']),
...mapGetters('Files', ['loadingFolder', 'activeFiles', 'quota', 'filesTotalSize', 'activeFilesCount', 'currentFolder', 'sharesTree']),
...mapGetters('Files', ['loadingFolder', 'activeFiles', 'quota', 'filesTotalSize', 'activeFilesCount', 'currentFolder']),
...mapGetters(['configuration']),

item () {
return this.$route.params.item
},

$_shareTypesIndirect () {
const parentPaths = getParentPaths(this.currentFolder.path, true)
if (parentPaths.length === 0) {
return []
}

// remove root entry
parentPaths.pop()

const shareTypes = {}
parentPaths.forEach((parentPath) => {
// TODO: optimize for performance by skipping once we got all known types
const shares = this.sharesTree[parentPath]
if (shares) {
shares.forEach((share) => {
// note: no distinction between incoming and outgoing shares as we display the same
// indirect indicator for them
shareTypes[share.info.share_type] = true
})
}
})

return Object.keys(shareTypes).map(shareType => parseInt(shareType, 10))
},

quotaVisible () {
return (
!this.publicPage() &&
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<template>
<div>
<oc-button
v-for="(indicator, index) in indicators"
:key="index"
class="file-row-share-indicator uk-text-middle"
:class="{ 'uk-margin-xsmall-left' : index > 0 }"
PVince81 marked this conversation as resolved.
Show resolved Hide resolved
:aria-label="indicator.label"
@click="indicator.handler(item, indicator.id)"
variation="raw"
>
<oc-icon
:name="indicator.icon"
class="uk-text-middle"
size="small"
:variation="indicator.status"
/>
</oc-button>
</div>
</template>

<script>
import intersection from 'lodash/intersection'
import { mapGetters } from 'vuex'
import { shareTypes } from '../../../helpers/shareTypes'
import { getParentPaths } from '../../../helpers/path'

const userShareTypes = [shareTypes.user, shareTypes.group, shareTypes.guest, shareTypes.remote]

export default {
name: 'StatusIndicators',

props: {
item: {
type: Object,
required: true
},
parentPath: {
type: String,
required: true
}
},

computed: {
...mapGetters('Files', ['sharesTree']),

indicators () {
const indicators = []

if (this.isUserShare(this.item)) {
indicators.push({
id: 'files-sharing',
label: this.shareUserIconLabel(this.item),
icon: 'group',
status: this.shareUserIconVariation(this.item),
handler: this.indicatorHandler
})
}

if (this.isLinkShare(this.item)) {
indicators.push({
id: 'file-link',
label: this.shareLinkIconLabel(this.item),
icon: 'link',
status: this.shareLinkIconVariation(this.item),
handler: this.indicatorHandler
})
}

return indicators
},

shareTypesIndirect () {
PVince81 marked this conversation as resolved.
Show resolved Hide resolved
const parentPaths = getParentPaths(this.parentPath, true)
if (parentPaths.length === 0) {
return []
}

// remove root entry
parentPaths.pop()

const shareTypes = {}
parentPaths.forEach((parentPath) => {
// TODO: optimize for performance by skipping once we got all known types
const shares = this.sharesTree[parentPath]
if (shares) {
shares.forEach((share) => {
// note: no distinction between incoming and outgoing shares as we display the same
// indirect indicator for them
shareTypes[share.info.share_type] = true
})
}
})

return Object.keys(shareTypes).map(shareType => parseInt(shareType, 10))
}
},

methods: {
isDirectUserShare (item) {
return (intersection(userShareTypes, item.shareTypes).length > 0)
},

isIndirectUserShare (item) {
return (item.isReceivedShare() || intersection(userShareTypes, this.shareTypesIndirect).length > 0)
},

isDirectLinkShare (item) {
return (item.shareTypes.indexOf(shareTypes.link) >= 0)
},

isIndirectLinkShare () {
return (this.shareTypesIndirect.indexOf(shareTypes.link) >= 0)
},

isUserShare (item) {
return this.isDirectUserShare(item) || this.isIndirectUserShare(item)
},

isLinkShare (item) {
return this.isDirectLinkShare(item) || this.isIndirectLinkShare(item)
},

shareUserIconVariation (item) {
return this.isDirectUserShare(item) ? 'active' : 'passive'
},

shareLinkIconVariation (item) {
return this.isDirectLinkShare(item) ? 'active' : 'passive'
},

shareUserIconLabel (item) {
return this.isDirectUserShare(item) ? this.$gettext('Directly shared with collaborators') : this.$gettext('Shared with collaborators through one of the parent folders')
},

shareLinkIconLabel (item) {
return this.isDirectLinkShare(item) ? this.$gettext('Directly shared with links') : this.$gettext('Shared with links through one of the parent folders')
},

indicatorHandler (item, sideBarName) {
this.$emit('click', item, sideBarName)
}
}
}
</script>
Loading