-
Notifications
You must be signed in to change notification settings - Fork 157
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
335 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
export * from './archiver' | ||
export * from './cache' | ||
export * from './client' | ||
export * from './listing' | ||
export { default as Registry } from './registry' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './listing' | ||
export * from './loader/resourceLoader' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { Store } from 'vuex' | ||
import { View } from '../../types/view' | ||
import { DavProperties, DavProperty } from 'web-pkg/src/constants' | ||
import resource, { MutationType as ResourceMutationType } from '../../store/modules/resource' | ||
import { | ||
buildStoreModuleIdentifier, | ||
MutationType as ListingMutationType | ||
} from '../../store/modules/listing' | ||
import OwnCloud from 'owncloud-sdk' | ||
import { ResourceLoader } from './loader' | ||
import { RuntimeError } from 'web-runtime/src/container/error' | ||
|
||
export class ListingService { | ||
store: Store<any> | ||
sdk: OwnCloud | ||
loaders: Map<View, ResourceLoader> | ||
|
||
public initialize(store: Store<any>, sdk: OwnCloud): void { | ||
this.store = store | ||
this.sdk = sdk | ||
this.loaders = new Map<View, ResourceLoader>() | ||
} | ||
|
||
public registerResourceLoader(view: View, loader: ResourceLoader) { | ||
this.loaders.set(view, loader) | ||
} | ||
|
||
public async loadResources( | ||
view: View, | ||
path: string, | ||
davProperties: DavProperty[] = DavProperties.Default | ||
): Promise<void> { | ||
if (!this.loaders.has(view)) { | ||
throw new RuntimeError(`unknown view ${view} in listingService`) | ||
} | ||
|
||
// init store module for the view & path | ||
const identifier = buildStoreModuleIdentifier(view, path) | ||
store.registerModule(identifier, resource) | ||
store.mutations[`Files/${identifier}/${ResourceMutationType.SET_LOADING}`](true) | ||
store.mutations[`Files/${identifier}/${ResourceMutationType.SET_PATH}`](path) | ||
|
||
// announce new view & path in `listing` store module | ||
store.mutations[`Files/listing/${ListingMutationType.SET_ACTIVE_VIEW}`]({ view, path }) | ||
|
||
// load resources | ||
const resourceNode = await this.loaders | ||
.get(view) | ||
.loadResources(this.sdk, path, davProperties) | ||
.catch(e => { | ||
console.error(e) | ||
// TODO: set error state in store? | ||
store.mutations[`Files/${identifier}/${ResourceMutationType.SET_LOADING}`](false) | ||
}) | ||
store.mutations[`Files/${identifier}/${ResourceMutationType.SET_PARENT}`](resourceNode.parent) | ||
store.mutations[`Files/${identifier}/${ResourceMutationType.SET_CHILDREN}`]( | ||
resourceNode.children | ||
) | ||
|
||
// exit `loading` state | ||
store.mutations[`Files/${identifier}/${ResourceMutationType.SET_LOADING}`](false) | ||
} | ||
} | ||
|
||
export const listingService = new ListingService() |
17 changes: 17 additions & 0 deletions
17
packages/web-app-files/src/services/listing/loader/favoritesLoader.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { ResourceLoader, ResourceNode } from './resourceLoader' | ||
import { buildResource } from '../../../helpers/resources' | ||
|
||
export class FavoritesLoader implements ResourceLoader { | ||
async loadResources( | ||
sdk: OwnCloud, | ||
path: string, | ||
davProperties: DavProperty[] | ||
): Promise<ResourceNode> { | ||
let resources = await this.sdk.files.getFavoriteFiles(davProperties) | ||
resources = resources.map(buildResource) | ||
return { | ||
parent: null, | ||
children: resources | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './resourceLoader' | ||
export * from './personalLoader' | ||
export * from './favoritesLoader' |
18 changes: 18 additions & 0 deletions
18
packages/web-app-files/src/services/listing/loader/personalLoader.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { ResourceLoader, ResourceNode } from './resourceLoader' | ||
import { buildResource } from '../../../helpers/resources' | ||
|
||
export class PersonalLoader implements ResourceLoader { | ||
async loadResources( | ||
sdk: OwnCloud, | ||
path: string, | ||
davProperties: DavProperty[] | ||
): Promise<ResourceNode> { | ||
let resources = await this.sdk.files.list(path, 1, davProperties) | ||
resources = resources.map(buildResource) | ||
const currentFolder = resources.shift() | ||
return { | ||
parent: currentFolder, | ||
children: resources | ||
} | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
packages/web-app-files/src/services/listing/loader/resourceLoader.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Resource } from '../../../types/resource' | ||
import { DavProperty } from 'web-pkg/src/constants' | ||
import OwnCloud from 'owncloud-sdk' | ||
|
||
export type ResourceNode = { | ||
parent?: Resource | ||
children: Resource[] | ||
} | ||
|
||
export interface ResourceLoader { | ||
loadResources(sdk: OwnCloud, path: string, davProperties: DavProperty[]): Promise<ResourceNode> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/** | ||
* problem statement: | ||
* our `files` array in the vuex store of the files app is only capable of representing one state. | ||
* multiple PROPFINDs for different folders, fired at similar times, will override each other in that state. | ||
* the longest running propfind wins. Cancelling the requests is not solving all edge cases, as the | ||
* PROPFINDs might finish at similar times so that cancellation is not happening anymore. | ||
* | ||
* idea for a solution: | ||
* loading resources in the different views should be converted into a two step process, each: | ||
* 1) set the activeView and activePath immediately after each navigation (SET_ACTIVE_VIEW, SET_ACTIVE_PATH) | ||
* 2) do the PROPFIND, convert the response into resources, and set them in a dedicated folder store module. | ||
* That store module needs to get registered dynamically. The PROPFIND and conversion might be long running. | ||
* It's really important that the activePath is set as early as possible! | ||
* | ||
* the views then render the files of the store module that matches the current `activePath`. | ||
* as a bonus, requests could be cancelled so that we avoid unnecessary store module creation. | ||
* | ||
* reasons for dynamically registered store modules: we need the reactivity of the individual files. | ||
* Which comes built in with the store modules. Another approach would be to build a cache of recent | ||
* propfind results (= response converted into a file listing). but that's not reactive by default. | ||
* | ||
* TODO: think about when to unregister the folder store modules. they might be big so removal should be happening as soon as not needed anymore. | ||
* TODO: further think about different views: propfind, fetch incoming shares, fetch outgoing shares, trashbin | ||
* TODO: the different `loadResources` methods from the views should live in a service, which then also takes care of creating the respective store module. | ||
*/ | ||
import { GetterTree, MutationTree } from 'vuex' | ||
import crypto from 'crypto' | ||
import { Resource } from '../../types/resource' | ||
import { RuntimeError } from 'web-runtime/src/container/error' | ||
|
||
export const state = { | ||
activeView: null, | ||
activePath: null | ||
} | ||
export type State = typeof state | ||
|
||
export enum MutationType { | ||
SET_ACTIVE_VIEW_AND_PATH = 'SET_ACTIVE_VIEW_AND_PATH' | ||
} | ||
export type Mutations<State> = { | ||
[MutationType.SET_ACTIVE_VIEW_AND_PATH]( | ||
state: State, | ||
params: { | ||
activeView: string | ||
activePath: string | ||
} | ||
): void | ||
} | ||
export const mutations: MutationTree<State> & Mutations<State> = { | ||
[MutationType.SET_ACTIVE_VIEW_AND_PATH]( | ||
state: State, | ||
params: { | ||
activeView: string | ||
activePath: string | ||
} | ||
) { | ||
state.activeView = params.activeView | ||
state.activePath = params.activePath | ||
} | ||
} | ||
|
||
export type Getters = { | ||
isLoading(state: State, getters: Getters, rootState: any): boolean | ||
getCurrentFolder(state: State, getters: Getters, rootState: any, rootGetters: any): Resource | ||
getFiles(state: State, getters: Getters, rootState: any, rootGetters: any): Resource[] | ||
} | ||
|
||
export const getters: GetterTree<State, State> & Getters = { | ||
isLoading(state, getters, rootState): boolean { | ||
const identifier = buildStoreModuleIdentifier(state.activeView, state.activePath) | ||
if (!hasStoreModule(state.activeView, state.activePath, rootState)) { | ||
return false | ||
} | ||
return rootState[`Files/${identifier}/loading`] | ||
}, | ||
getCurrentFolder(state, getters, rootState, rootGetters): Resource { | ||
const identifier = buildStoreModuleIdentifier(state.activeView, state.activePath) | ||
if (!hasStoreModule(state.activeView, state.activePath, rootState)) { | ||
throw new RuntimeError('unknown resource') | ||
} | ||
return rootGetters[`Files/${identifier}/parent`] | ||
}, | ||
getFiles(state, getters, rootState, rootGetters): Resource[] { | ||
const identifier = buildStoreModuleIdentifier(state.activeView, state.activePath) | ||
if (!hasStoreModule(state.activeView, state.activePath, rootState)) { | ||
throw new RuntimeError('unknown resource') | ||
} | ||
return rootGetters[`Files/${identifier}/children`] | ||
} | ||
} | ||
|
||
export const buildStoreModuleIdentifier = (activeView: string, activePath: string): string => { | ||
const hash = crypto.createHash('sha256') | ||
hash.update(activePath) | ||
return `${activeView}_${hash.digest().toString('hex')}` | ||
} | ||
|
||
const hasStoreModule = (activeView: string, activePath: string, rootState: any): boolean => { | ||
const identifier = buildStoreModuleIdentifier(activeView, activePath) | ||
return !!rootState[`Files/${identifier}`] | ||
} | ||
|
||
export default { | ||
namespaced: true, | ||
state: () => state, | ||
getters, | ||
mutations | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { MutationTree } from 'vuex' | ||
import { Resource } from '../../types/resource' | ||
|
||
export const state = { | ||
loading: true, | ||
path: '', | ||
parent: null, | ||
children: [] | ||
} | ||
export type State = typeof state | ||
|
||
export enum MutationType { | ||
SET_LOADING = 'SET_LOADING', | ||
SET_PATH = 'SET_PATH', | ||
SET_PARENT = 'SET_PARENT', | ||
SET_CHILDREN = 'SET_CHILDREN' | ||
} | ||
|
||
export type Mutations<State> = { | ||
[MutationType.SET_LOADING](state: State, loading: boolean): void | ||
[MutationType.SET_PATH](state: State, path: string): void | ||
[MutationType.SET_PARENT](state: State, parent: Resource): void | ||
[MutationType.SET_CHILDREN](state: State, children: Resource[]): void | ||
} | ||
|
||
export const mutations: MutationTree<State> & Mutations = { | ||
[MutationType.SET_LOADING](state: State, loading: boolean): void { | ||
state.loading = loading | ||
}, | ||
[MutationType.SET_PATH](state: State, path: string): void { | ||
state.path = path | ||
}, | ||
[MutationType.SET_PARENT](state: State, parent: Resource): void { | ||
state.parent = parent | ||
}, | ||
[MutationType.SET_CHILDREN](state: State, children: Resource[]): void { | ||
state.children = children | ||
} | ||
} | ||
|
||
export default { | ||
namespaced: true, | ||
state: () => state, | ||
mutations | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export abstract class View { | ||
static readonly Personal: string = 'personal' | ||
static readonly Favorites: string = 'favorites' | ||
static readonly SharedWithMe: string = 'shared-with-me' | ||
static readonly SharedWithOthers: string = 'shared-with-others' | ||
static readonly SharedViaLink: string = 'shared-via-link' | ||
static readonly PublicFiles: string = 'public-files' | ||
static readonly TrashBin: string = 'trash-bin' | ||
} |
Oops, something went wrong.