-
-
Notifications
You must be signed in to change notification settings - Fork 32
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
Gradual fetching of data by scrolling down in File Manger Application #166
Comments
BTW, What is your idea about adding this pagination feature to your file manager app? |
Hm. Relying on either the total number of files or the last one to figure out when to stop does not sound like a good idea. An easier way to do this is simply to set a "page size" and whenever you get a "page" back from the api that is less than this page size or zero, you know you've reached the end. |
Yes, seems a better idea, thanks :) but I still need to have the last file of each page, returned by server, as a token for getting the next page. |
It is possible just to use a page number 🤔 const pageSize = 10
let currentPage = 0
let reachedEnd = false
function changedDirectory() {
currentPage = 0
reachedEnd = false
}
async function scrollReachedBottom() {
if (reachedEnd) return
const nextPage = currentPage + 1
const result = await readdir('home:/', { page: nextPage, pageSize })
reachedEnd = result.length < pageSize
currentPage = nextPage
} Now you have a offset on the server: |
But it seems my storage pagination API does not support offset. |
I see. I'm not really sure how to solve this then without also adding sorting as an option (default true). |
So do you think you can add this feature? |
Feel free to have a look at it. This would also mean that the file manager would have to run the sort manually to make it behave the same. |
Great :) |
I was just thinking of adding it to the Line 84 in 385b38f
Lines 70 to 75 in 385b38f
|
About this part, when a new page is fetched, I think sorting the accumulative list (previous pages+ new page) leads to a bad UX. The files of previous pages and the new one might be mixed, and the user can not scroll just among files of new page. |
I was only thinking about applying sorting per result. Because in adapters that does not support this you'd only get one result. |
So by adding // Handles directory listing result(s)
const handleDirectoryList = (path, options) => result =>
Promise.resolve(result.map(stat => createFileIter(stat)))
.then(result => transformReaddir(pathToObject(path), result, {
showHiddenFiles: options.showHiddenFiles !== false,
filter: options.filter,
sortFiles: options.sortFiles
})); We just need to check The question is where should I config this boolean option? Maybe it is needed to add this config to fileManager:{
pageSize: 1000,
sortFiles: false
} |
Doesn't this just fit right into the FM readdir function ? |
So when we complete this feature, are you going to add it to your FM app? If so is it possible to set in readdir as below? const options = {
showHiddenFiles: proc.settings.showHiddenFiles,
sortFiles: false,
page:{
size:1000,
number: state.currentPage,
lastFetchedFile: state.lastFetchedFile
}
}; In this way when sorting (deafult is true) is going to happen? |
Yes, that will be possible. However, it would be nice to agree on a default option and properties for "paging" so that it can work for all adapters, not just the one that you're making. |
For sure we should agree on paging options. I've just only used some options for testing the functionality. Up to now, Three options seem necessary in my mind:
|
So what is your opinion ? |
I again find some time to work on paging project :) |
Sadly, I've been busy with a lot of other things. Those three options are fine, but I can also thing of another one: "pagination token". This is for examplle have in Google Drive. Maybe there are other options there to consider as well. |
Hm. Pretty sure that this can occur if you remove something from the VDOM while it's updating. Does it cause any issues ? |
It causes new page list items does not show on the file manager app. |
Can you share the code that causes this ? |
And there is also another question. If you agree, what is your suggestion for its implementation? |
I do not have access right now. I will share it tomorrow 🙏 |
This would require getting some additional information, which is now not really possible in the VFS 🤔 I'm starting to think maybe having a new scandir endpoint that is designed for doing things like this so that you get a proper server response:
Great! |
How about providing number of files and directories of each dir in So we should call |
Oh, yeah. I didn't think of that 🤦♂️ That could work! |
These are changes in filemanger application index file (till now): const PAGE_SIZE = 10;
/**
* Update some state properties when selected directory/file changed
*/
const updateState = (state) => {
state.currentList = [];
state.currentPage = 0;
state.fetchAllPages = false;
state.currentLastItem = '';
};
/**
* Detect pagination capability when selected mountpoint changed
*/
const isPagingActive = async (core, path) => {
const vfs = core.make('osjs/vfs');
const capabilities = await vfs.capabilities(path);
return capabilities.pagination;
};
/**
* Create files list by concating new page items by previous fetched pages items
*/
const createPagesList = async (proc, state, vfs, dir) => {
const options = {
showHiddenFiles: proc.settings.showHiddenFiles,
page:{
size:PAGE_SIZE,
number: state.currentPage,
marker: state.currentLastItem,
token: ''
}
};
let list = await vfs.readdir(dir, options);
// FIXME: the special file `..` should be handled in a better way (not add per page).
if(list.length === 0 || (list.length === 1 && list[0].filename === '..')) {
state.fetchAllPages = true;
}
if(list.length !== 0 && list !== state.currentList) {
// FIXME: the special file `..` should be handled in a better way (not add per page).
if(state.currentList.length !== 0 && list[0].filename === '..' && state.currentList[0].filename === '..') {
list = list.filter(item => item.filename !== '..');
}
state.currentLastItem = list.length !== 0 ? list[list.length - 1].filename : null;
state.currentList = state.currentList.concat(list);
list = state.currentList;
} else {
list = state.currentList;
}
return list;
};
/**
* Create total list of files when pagination is not supported
*/
const createTotalList = (proc, vfs, dir) => {
const options = {
showHiddenFiles: proc.settings.showHiddenFiles
};
return vfs.readdir(dir, options);
};
/**
* VFS action Factory
*/
const vfsActionFactory = (core, proc, win, dialog, state) => {
// ...
const readdir = async (dir, history, selectFile) => {
if (win.getState('loading')) {
return;
}
// if calling by scroll
if(dir === undefined) {
dir = state.currentPath;
state.currentPage += 1;
}
try {
const message = __('LBL_LOADING', dir.path);
win.setState('loading', true);
win.emit('filemanager:status', message);
let list;
if(state.pagination) {
list = await createPagesList(proc, state, vfs, dir);
}else {
list = await createTotalList(proc, vfs, dir);
}
// ...
};
/**
* Creates a new FileManager user interface
*/
const createApplication = (core, proc, state) => {
const createActions = (win) => ({
// ...
mountview: listView.actions({
select: async ({data}) => {
await updateState(state);
state.pagination = await isPagingActive(core, data.root);
win.emit('filemanager:navigate', {path: data.root});
}
}),
fileview: listView.actions({
// ...
activate: async ({data}) => {
await updateState(state);
win.emit(`filemanager:${data.isFile ? 'open' : 'navigate'}`, data);
},
scroll: (ev) => {
if (state.pagination) {
const el = ev.target;
if (state.fetchAllPages) {
return;
}
const hitBottom = (el.scrollTop + el.offsetHeight) >= el.scrollHeight;
if(hitBottom) {
win.emit('filemanager:navigate');
}
}
}
)}
// ...
}
/**
* Creates a new FileManager window
*/
const createWindow = async (core, proc) => {
let wired;
const state = {
currentFile: undefined,
currentPath: undefined,
currentList: [],
currentPage:0,
fetchAllPages: false,
currentLastItem:'',
pagination: false
};
const {homePath, initialPath} = createInitialPaths(core, proc);
state.pagination = await isPagingActive(core, initialPath);
// ...
} And I changed const sortedSpecial = createSpecials(path)
.sort(sorter(sortBy, sortDir))
.map(modify);
const sortFiles = files.filter(filterHidden)
.filter(filter)
.map(modify);
return [
...sortedSpecial,
...sortFiles
]; |
I think I'll have to actually run these changes myself to get a better understanding. Do you have this in a repo ? |
I worked more on that. This error happened whenever there is at least one duplicate in array list. So It can be solved by filtering the array as below: const filenameSet = new Set();
const filteredArr = list.filter((obj) => {
const isPresentInSet = filenameSet.has(obj.filename);
filenameSet.add(obj.filename);
return !isPresentInSet;
}); |
What is your suggestion for the page-size and number of pages can be shown on filemanager?
Option C seems better, but its implementation can be a bit complicated, since filesystems work differently (in my case it works by marker not offset) |
current statusbar: new statusbar (option 1): new statusbar (option 2): Which option do you think is better? if option 1 is needed, is it possible to return count of dir and files seperately in for option 2, I changed this code block as below: const createStat = async stat => {
let count = null;
if (stat.isDirectory()) {
const files = await fs.readdir(realPath);
count = files.length;
}
return ({
isDirectory: stat.isDirectory(),
isFile: stat.isFile(),
mime: stat.isFile() ? mime(realPath) : null,
size: stat.size,
path: file,
count: count,
filename,
stat
});
}; |
Hm. I think maybe it would be best if it simply said
Well, only if the filesystem that you're working with supports it. |
So by |
And what should I do for hidden files? Should I also filter them here server side to have total count without hidden files? |
With
Hidden files should probably only be counted if the option to show them is enabled. |
Actually my question was about the number of total bytes, which I guess you mean there is no need to show current bytes, just show total bytes.
So do you have any suggestion for this functionality? |
Yes, the total count.
Does this even need to be exposed ? If it says As for adjusting the page size, this is probably better as an adapter setting, and not something that you change in the UI. |
I did not understand this. I previously mentioned these three options.
So by considering |
None of the options. There is not any need if the user already sees |
I did not mean to show number of pages somewhere, Actually I am thinking about list of page items (files) itself. Consider situation when 4-5 pages with |
I don't really think there's a good way to solve this outside maybe doing some kind of categorization/grouping, which would not even be possible with pagination. Option B is very bad UX and option C will probably make it so that the user can't view all of their files, right ? |
So your opinion is that it is better to show as many pages as fetched, right?
yes, maybe previous files fetched again by scrolling up! |
Yes, show everything.
Hm. I'm not sure how this would be implemented, or how it would "feel" (UX) 🤔 |
Hi :) What is your suggestion for refreshing list to support actions like delete, paste and rename while pagination is active. Imagine we have deleted a file within one of our previous fetched pages. |
According to this issue, I am implementing gradual fetching by scrolling in FM app. I have two issues.
So I think I need to initially get total number of directories and files of a path (without calling readdir api and getting the list) and save it into some state.
I checked the stat api, but it does not provide this attribute.
The text was updated successfully, but these errors were encountered: