🔢 Shared with me filters (#79)
✨ feat: shared with me filters 🐛 Fix try to access the service with init 🌐 feat: shared with me i18n 🌐russian translation --------- Co-authored-by: Anton SHEPILOV <ashepilov@linagora.com>
This commit is contained in:
@@ -1,88 +0,0 @@
|
||||
import yargs from "yargs";
|
||||
import ora from "ora";
|
||||
import tdrive from "../../../tdrive";
|
||||
import Table from "cli-table";
|
||||
import { exit } from "process";
|
||||
import gr from "../../../services/global-resolver";
|
||||
|
||||
/**
|
||||
* Merge command parameters. Check the builder definition below for more details.
|
||||
*/
|
||||
type CLIArgs = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const services = [
|
||||
"storage",
|
||||
"counter",
|
||||
"message-queue",
|
||||
"platform-services",
|
||||
"user",
|
||||
"search",
|
||||
"database",
|
||||
"webserver",
|
||||
"statistics",
|
||||
"applications",
|
||||
"auth",
|
||||
"realtime",
|
||||
"websocket",
|
||||
];
|
||||
|
||||
const command: yargs.CommandModule<unknown, CLIArgs> = {
|
||||
command: "remove",
|
||||
describe: "command that allow you to remove one user",
|
||||
builder: {
|
||||
id: {
|
||||
default: "",
|
||||
type: "string",
|
||||
description: "User ID",
|
||||
},
|
||||
},
|
||||
handler: async argv => {
|
||||
const tableBefore = new Table({
|
||||
head: ["User ID", "Username", "Deleted"],
|
||||
colWidths: [40, 40, 10],
|
||||
});
|
||||
const tableAfter = new Table({
|
||||
head: ["User ID", "Username", "Deleted"],
|
||||
colWidths: [40, 40, 10],
|
||||
});
|
||||
const spinner = ora({ text: "Retrieving user" }).start();
|
||||
|
||||
const platform = await tdrive.run(services);
|
||||
await gr.doInit(platform);
|
||||
|
||||
const user = await gr.services.users.get({ id: argv.id });
|
||||
|
||||
if (!user) {
|
||||
console.error("Error: You need to provide User ID");
|
||||
spinner.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
// Table before
|
||||
tableBefore.push([user.id, user.username_canonical, user.deleted]);
|
||||
|
||||
await gr.services.users.anonymizeAndDelete(
|
||||
{ id: user.id },
|
||||
{
|
||||
user: { id: user.id, server_request: true },
|
||||
},
|
||||
);
|
||||
|
||||
const finalUser = await gr.services.users.get({ id: argv.id });
|
||||
|
||||
// Table after
|
||||
tableAfter.push([finalUser.id, finalUser.username_canonical, finalUser.deleted]);
|
||||
|
||||
spinner.stop();
|
||||
}
|
||||
|
||||
exit();
|
||||
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
||||
@@ -85,7 +85,7 @@ export type AccessInformation = {
|
||||
entities: AuthEntity[];
|
||||
};
|
||||
|
||||
type AuthEntity = {
|
||||
export type AuthEntity = {
|
||||
type: "user" | "channel" | "company" | "folder";
|
||||
id: string | "parent";
|
||||
level: publicAccessLevel | DriveFileAccessLevel;
|
||||
|
||||
@@ -3,7 +3,7 @@ import _ from "lodash";
|
||||
import { logger } from "../../../core/platform/framework";
|
||||
import Repository from "../../../core/platform/services/database/services/orm/repository/repository";
|
||||
import globalResolver from "../../global-resolver";
|
||||
import { AccessInformation, DriveFile } from "../entities/drive-file";
|
||||
import { AccessInformation, AuthEntity, DriveFile } from "../entities/drive-file";
|
||||
import { CompanyExecutionContext, DriveFileAccessLevel } from "../types";
|
||||
|
||||
/**
|
||||
@@ -341,11 +341,20 @@ export const getSharedByUser = (
|
||||
for (const idx in accessInfo?.entities) {
|
||||
const entity = accessInfo.entities[idx];
|
||||
if (entity.type === "user" && entity.id === context.user?.id) {
|
||||
return entity.grantor;
|
||||
return getGrantorAndThrowIfEmpty(entity);
|
||||
}
|
||||
if (entity.type === "company" && entity.id === context.company.id) {
|
||||
return entity.grantor;
|
||||
if (entity.type === "company" && entity.id === context.company.id && entity.level != "none") {
|
||||
console.log("company", entity);
|
||||
return getGrantorAndThrowIfEmpty(entity);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const getGrantorAndThrowIfEmpty = (entity: AuthEntity) => {
|
||||
if (!entity.grantor) {
|
||||
logger.warn(`For the file permissions ${entity.id} grantor is not defined`);
|
||||
// throw CrudException.badGateway(`For the file permissions ${entity.id} grantor is not defined`);
|
||||
}
|
||||
return entity.grantor;
|
||||
};
|
||||
|
||||
+13
-13
@@ -1,17 +1,19 @@
|
||||
import { DriveFile } from "../../entities/drive-file";
|
||||
import { DriveFileDTO } from "./drive-file-dto";
|
||||
import { DriveFile } from "../entities/drive-file";
|
||||
import { DriveFileDTO } from "../web/dto/drive-file-dto";
|
||||
import {
|
||||
ListResult,
|
||||
Paginable,
|
||||
Pagination,
|
||||
} from "../../../../core/platform/framework/api/crud-service";
|
||||
} from "../../../core/platform/framework/api/crud-service";
|
||||
import _ from "lodash";
|
||||
import { CompanyExecutionContext } from "../../types";
|
||||
import globalResolver from "../../../../services/global-resolver";
|
||||
import User from "../../../../services/user/entities/user";
|
||||
import { getSharedByUser } from "../../services/access-check";
|
||||
import { CompanyExecutionContext } from "../types";
|
||||
import User from "../../user/entities/user";
|
||||
import { getSharedByUser } from "./access-check";
|
||||
import globalResolver from "../../../services/global-resolver";
|
||||
|
||||
export class DriveFileDTOBuilder {
|
||||
static VIEW_SHARED_WITH_ME = "shared_with_me";
|
||||
|
||||
private views: Map<string, string[]> = new Map([
|
||||
[
|
||||
"default",
|
||||
@@ -35,7 +37,7 @@ export class DriveFileDTOBuilder {
|
||||
],
|
||||
],
|
||||
[
|
||||
"shared_with_me",
|
||||
DriveFileDTOBuilder.VIEW_SHARED_WITH_ME,
|
||||
[
|
||||
"id",
|
||||
"name",
|
||||
@@ -51,7 +53,6 @@ export class DriveFileDTOBuilder {
|
||||
],
|
||||
],
|
||||
]);
|
||||
usersService = globalResolver.services?.users;
|
||||
|
||||
public async build(
|
||||
files: ListResult<DriveFile>,
|
||||
@@ -59,8 +60,6 @@ export class DriveFileDTOBuilder {
|
||||
fields?: string[],
|
||||
view?: string,
|
||||
): Promise<ListResult<DriveFileDTO>> {
|
||||
const file = new DriveFile();
|
||||
file.id = "1";
|
||||
if (view) {
|
||||
fields = this.views.get(view);
|
||||
}
|
||||
@@ -110,9 +109,10 @@ export class DriveFileDTOBuilder {
|
||||
});
|
||||
}
|
||||
|
||||
private async fetchUsers(ids: string[]) {
|
||||
async fetchUsers(ids: string[]) {
|
||||
ids.filter(id => id != null);
|
||||
return (
|
||||
await this.usersService.list(
|
||||
await globalResolver.services.users.list(
|
||||
{
|
||||
limitStr: ids.length.toString(),
|
||||
} as Pagination,
|
||||
@@ -783,9 +783,7 @@ export class DocumentsService {
|
||||
limitStr: "100",
|
||||
},
|
||||
$in: [
|
||||
...(options.onlyDirectlyShared
|
||||
? [["access_entities", [context.user.id, context.company.id]] as inType]
|
||||
: []),
|
||||
...(options.onlyDirectlyShared ? [["access_entities", [context.user.id]] as inType] : []),
|
||||
...(options.company_id ? [["company_id", [options.company_id]] as inType] : []),
|
||||
...(options.creator ? [["creator", [options.creator]] as inType] : []),
|
||||
...(options.mime_type
|
||||
@@ -834,9 +832,17 @@ export class DocumentsService {
|
||||
});
|
||||
|
||||
return new ListResult(result.type, filteredResult, result.nextPage);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (options.onlyUploadedNotByMe) {
|
||||
const filteredResult = await this.filter(result.getEntities(), async item => {
|
||||
return item.creator != context.user.id;
|
||||
});
|
||||
|
||||
return new ListResult(result.type, filteredResult, result.nextPage);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
private async filter(arr, callback) {
|
||||
|
||||
@@ -48,6 +48,7 @@ export type SearchDocumentsOptions = {
|
||||
view?: string;
|
||||
fields?: string[];
|
||||
onlyDirectlyShared?: boolean;
|
||||
onlyUploadedNotByMe?: boolean;
|
||||
};
|
||||
|
||||
export type SearchDocumentsBody = {
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
SearchDocumentsOptions,
|
||||
} from "../../types";
|
||||
import { DriveFileDTO } from "../dto/drive-file-dto";
|
||||
import { DriveFileDTOBuilder } from "../dto/drive-file-dto-builder";
|
||||
import { DriveFileDTOBuilder } from "../../services/drive-file-dto-builder";
|
||||
|
||||
export class DocumentsController {
|
||||
private driveFileDTOBuilder = new DriveFileDTOBuilder();
|
||||
@@ -154,7 +154,9 @@ export class DocumentsController {
|
||||
const options: SearchDocumentsOptions = {
|
||||
...request.body,
|
||||
company_id: request.body.company_id || context.company.id,
|
||||
view: DriveFileDTOBuilder.VIEW_SHARED_WITH_ME,
|
||||
onlyDirectlyShared: true,
|
||||
onlyUploadedNotByMe: true,
|
||||
};
|
||||
|
||||
if (!Object.keys(options).length) {
|
||||
|
||||
@@ -171,7 +171,7 @@ describe("the Drive feature", () => {
|
||||
});
|
||||
|
||||
it("did search for an item and check that all the fields for 'shared_with_me' view", async () => {
|
||||
jest.setTimeout(10000);
|
||||
jest.setTimeout(20000);
|
||||
//given:: user uploaded one doc and give permission to another user
|
||||
const oneUser = await TestHelpers.getInstance(platform, true);
|
||||
const anotherUser = await TestHelpers.getInstance(platform, true);
|
||||
@@ -187,7 +187,7 @@ describe("the Drive feature", () => {
|
||||
});
|
||||
await oneUser.updateDocument(doc.id, doc);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
//when:: user search for a doc
|
||||
const searchResponse = await anotherUser.searchDocument({ view: "shared_with_me" });
|
||||
@@ -211,6 +211,34 @@ describe("the Drive feature", () => {
|
||||
expect(actual.shared_by?.first_name).toEqual(oneUser.user.first_name);
|
||||
});
|
||||
|
||||
it("'shared_with_me' shouldn't return files uploaded by me", async () => {
|
||||
jest.setTimeout(20000);
|
||||
//given:: user uploaded one doc and give permission to another user
|
||||
const oneUser = await TestHelpers.getInstance(platform, true);
|
||||
const anotherUser = await TestHelpers.getInstance(platform, true);
|
||||
const doc = await oneUser.uploadRandomFileAndCreateDocument();
|
||||
await new Promise(r => setTimeout(r, 3000));
|
||||
//give permissions to the file
|
||||
doc.access_info.entities.push({
|
||||
type: "user",
|
||||
id: anotherUser.user.id,
|
||||
level: "read",
|
||||
grantor: null,
|
||||
});
|
||||
await oneUser.updateDocument(doc.id, doc);
|
||||
//another user also uploaded several files
|
||||
await anotherUser.uploadRandomFileAndCreateDocument();
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
//when:: user search for a doc
|
||||
const searchResponse = await anotherUser.sharedWithMeDocuments({});
|
||||
|
||||
//then::
|
||||
expect(searchResponse.entities?.length).toEqual(1);
|
||||
const actual = searchResponse.entities[0];
|
||||
})
|
||||
|
||||
it("did search for an item that doesn't exist", async () => {
|
||||
await createItem();
|
||||
|
||||
|
||||
+12
-10
@@ -1,7 +1,7 @@
|
||||
import "reflect-metadata";
|
||||
import { describe } from "@jest/globals";
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { DriveFileDTOBuilder } from "../../../../../../../src/services/documents/web/dto/drive-file-dto-builder";
|
||||
import { DriveFileDTOBuilder } from "../../../../../../../src/services/documents/services/drive-file-dto-builder";
|
||||
import { ListResult } from "../../../../../../../src/core/platform/framework/api/crud-service";
|
||||
import { DriveFile } from "../../../../../../../src/services/documents/entities/drive-file";
|
||||
import { CompanyExecutionContext } from "../../../../../../../src/services/applications/web/types";
|
||||
@@ -9,13 +9,11 @@ import { UserServiceImpl } from "../../../../../../../src/services/user/services
|
||||
|
||||
describe("Drive File DTO Builder Test", () => {
|
||||
|
||||
const subj = new DriveFileDTOBuilder();
|
||||
|
||||
|
||||
it("The dto object will contain selected fields with the data ", async () => {
|
||||
//given
|
||||
const fields = ["id", "name"];
|
||||
const file = newFile("file_id", "file_name");
|
||||
const subj = new DriveFileDTOBuilder();
|
||||
|
||||
//when
|
||||
|
||||
@@ -37,9 +35,9 @@ describe("Drive File DTO Builder Test", () => {
|
||||
//given
|
||||
const fields = ["id", "name"];
|
||||
const file = newFile("file_id", "file_name", "file_parent_id");
|
||||
const subj = new DriveFileDTOBuilder();
|
||||
|
||||
//when
|
||||
|
||||
let transformedFiles = await subj.build(
|
||||
new ListResult<DriveFile>("drive_files", [file]),
|
||||
context,
|
||||
@@ -56,9 +54,10 @@ describe("Drive File DTO Builder Test", () => {
|
||||
it("When there is no fields set then whole object should be copied", async () => {
|
||||
//given
|
||||
const file = newFile("file_id", "file_name", "file_parent_id");
|
||||
const subj = new DriveFileDTOBuilder();
|
||||
|
||||
|
||||
//when
|
||||
|
||||
let transformedFiles = await subj.build(
|
||||
new ListResult<DriveFile>("drive_files", [file]),
|
||||
context
|
||||
@@ -77,6 +76,8 @@ describe("Drive File DTO Builder Test", () => {
|
||||
//given
|
||||
const file = newFile("file_id", "file_name");
|
||||
let fields = ["id", "name", "unknown_property"];
|
||||
const subj = new DriveFileDTOBuilder();
|
||||
|
||||
|
||||
//when
|
||||
let transformedFiles = await subj.build(
|
||||
@@ -98,8 +99,9 @@ describe("Drive File DTO Builder Test", () => {
|
||||
let file_creator = newUser();
|
||||
const file = newFile("file_id", "file_name", "parent_id", file_creator.id);
|
||||
let fields = ["id", "name", "created_by"];
|
||||
subj.usersService = mock<UserServiceImpl>();
|
||||
subj.usersService.list = jest.fn().mockReturnValue(new ListResult("users", [file_creator]));
|
||||
const usersService = mock<UserServiceImpl>() as UserServiceImpl;
|
||||
const subj = new DriveFileDTOBuilder()
|
||||
subj.fetchUsers = jest.fn().mockReturnValue([file_creator]);
|
||||
|
||||
//when
|
||||
let transformedFiles = await subj.build(
|
||||
@@ -122,8 +124,8 @@ describe("Drive File DTO Builder Test", () => {
|
||||
let file_shared_by = newUser();
|
||||
const file = newFile("file_id", "file_name", "parent_id", null, file_shared_by.id);
|
||||
let fields = ["id", "name", "shared_by"];
|
||||
subj.usersService = mock<UserServiceImpl>();
|
||||
subj.usersService.list = jest.fn().mockReturnValue(new ListResult("users", [file_shared_by]));
|
||||
const subj = new DriveFileDTOBuilder();
|
||||
subj.fetchUsers = jest.fn().mockReturnValue([file_shared_by]);
|
||||
|
||||
//when
|
||||
let transformedFiles = await subj.build(
|
||||
|
||||
@@ -257,5 +257,18 @@
|
||||
"components.item_context_menu.trash.empty": "Empty trash",
|
||||
"components.item_context_menu.add_documents": "Add document or folder",
|
||||
"components.item_context_menu.download_folder": "Download folder",
|
||||
"components.item_context_menu.go_to_trash": "Go to trash"
|
||||
"components.item_context_menu.go_to_trash": "Go to trash",
|
||||
"components.item_context_menu.all": "All",
|
||||
"components.item_context_menu.today": "Today",
|
||||
"components.item_context_menu.last_week": "Last week",
|
||||
"components.item_context_menu.last_month": "Last month",
|
||||
"scenes.app.shared_with_me.shared_with_me": "Shared with me",
|
||||
"scenes.app.shared_with_me.file_type": "File type",
|
||||
"scenes.app.shared_with_me.people": "People",
|
||||
"scenes.app.shared_with_me.last_modified": "Last modified",
|
||||
"scenes.app.shared_with_me.documents": "Documents",
|
||||
"scenes.app.shared_with_me.name": "Name",
|
||||
"scenes.app.shared_with_me.shared_by": "Shared by",
|
||||
"scenes.app.shared_with_me.shared_date": "Shared date",
|
||||
"scenes.app.shared_with_me.edit": "Edit"
|
||||
}
|
||||
@@ -211,54 +211,64 @@
|
||||
"components.side_menu.trash": "Poubelle",
|
||||
"components.side_menu.home": "Disque partagé",
|
||||
"components.side_menu.my_drive": "Mon disque",
|
||||
|
||||
|
||||
|
||||
"components.searchpopup.soon": "This feature is coming soon \uD83D\uDE80",
|
||||
"components.side_menu.buttons.upload": "Upload",
|
||||
"components.side_menu.buttons.create": "Create",
|
||||
"components.side_menu.buttons.empty_trash": "Empty trash",
|
||||
"scenes.app.drive.nothing": "Nothing here.",
|
||||
"scenes.app.drive.drag_and_drop": "Drag and drop files to upload them or click on the 'Add document' button.",
|
||||
"scenes.app.drive.add_doc": "Add document or folder",
|
||||
"scenes.app.drive.folders": "Folders",
|
||||
"scenes.app.drive.used": "used in this folder",
|
||||
"components.searchpopup.soon": "Cette fonctionnalité arrive bientôt \uD83D\uDE80",
|
||||
"components.side_menu.buttons.upload": "Télécharger",
|
||||
"components.side_menu.buttons.create": "Créer",
|
||||
"components.side_menu.buttons.empty_trash": "Vider la corbeille",
|
||||
"scenes.app.drive.nothing": "Rien ici.",
|
||||
"scenes.app.drive.drag_and_drop": "Faites glisser et déposez des fichiers pour les télécharger ou cliquez sur le bouton 'Ajouter un document'.",
|
||||
"scenes.app.drive.add_doc": "Ajouter un document ou un dossier",
|
||||
"scenes.app.drive.folders": "Dossiers",
|
||||
"scenes.app.drive.used": "utilisé dans ce dossier",
|
||||
"scenes.app.drive.documents": "Documents",
|
||||
"scenes.app.drive.context_menu": "More",
|
||||
"components.disk_usage.used": "used,",
|
||||
"components.disk_usage.in_trash": "in trash",
|
||||
"components.create_modal.create_folder_or_doc": "Create document or folder",
|
||||
"components.create_modal.create_folder": "Create a folder",
|
||||
"components.create_modal.upload_files": "Upload files from device",
|
||||
"components.create_modal.upload_folders": "Upload folders from device",
|
||||
"components.create_modal.create_link": "Create a link file",
|
||||
"components.create_folder_modal.hint": "Choose a name for the new folder.",
|
||||
"components.create_folder_modal.placeholder": "Folder name",
|
||||
"components.create_link_modal.hint": "Link name",
|
||||
"components.create_link_modal.button": "Create link",
|
||||
"components.pending_file_list.estimation.end": "Will end",
|
||||
"components.pending_file_list.estimation.approximations": "Waiting for time approximations...",
|
||||
"components.item_context_menu.preview": "Preview",
|
||||
"components.item_context_menu.download": "Download",
|
||||
"components.item_context_menu.rename": "Rename",
|
||||
"components.item_context_menu.manage_access": "Manage access",
|
||||
"components.item_context_menu.share": "Share",
|
||||
"components.item_context_menu.copy_link": "Copy public link",
|
||||
"components.item_context_menu.copy_link.success": "Public link copied to clipboard",
|
||||
"scenes.app.drive.context_menu": "Plus",
|
||||
"components.disk_usage.used": "utilisé,",
|
||||
"components.disk_usage.in_trash": "dans la corbeille",
|
||||
"components.create_modal.create_folder_or_doc": "Créer un document ou un dossier",
|
||||
"components.create_modal.create_folder": "Créer un dossier",
|
||||
"components.create_modal.upload_files": "Télécharger des fichiers depuis l'appareil",
|
||||
"components.create_modal.upload_folders": "Télécharger des dossiers depuis l'appareil",
|
||||
"components.create_modal.create_link": "Créer un lien",
|
||||
"components.create_folder_modal.hint": "Choisissez un nom pour le nouveau dossier.",
|
||||
"components.create_folder_modal.placeholder": "Nom du dossier",
|
||||
"components.create_link_modal.hint": "Nom du lien",
|
||||
"components.create_link_modal.button": "Créer le lien",
|
||||
"components.pending_file_list.estimation.end": "Se terminera",
|
||||
"components.pending_file_list.estimation.approximations": "En attente d'estimations de temps...",
|
||||
"components.item_context_menu.preview": "Aperçu",
|
||||
"components.item_context_menu.download": "Télécharger",
|
||||
"components.item_context_menu.rename": "Renommer",
|
||||
"components.item_context_menu.manage_access": "Gérer l'accès",
|
||||
"components.item_context_menu.share": "Partager",
|
||||
"components.item_context_menu.copy_link": "Copier le lien public",
|
||||
"components.item_context_menu.copy_link.success": "Lien public copié dans le presse-papiers",
|
||||
"components.item_context_menu.versions": "Versions",
|
||||
"components.item_context_menu.move": "Move",
|
||||
"components.item_context_menu.move.modal_header": "Move",
|
||||
"components.item_context_menu.move_to_trash": "Move to trash",
|
||||
"components.item_context_menu.delete": "Delete",
|
||||
"components.item_context_menu.move_multiple": "Move selected items",
|
||||
"components.item_context_menu.move_multiple.modal_header": "Move selected items",
|
||||
"components.item_context_menu.download_multiple": "Download selected items",
|
||||
"components.item_context_menu.clear_selection": "Clear selection",
|
||||
"components.item_context_menu.delete_multiple": "Delete",
|
||||
"components.item_context_menu.to_trash_multiple": "Move selected items to trash",
|
||||
"components.item_context_menu.trash.exit": "Exit trash",
|
||||
"components.item_context_menu.trash.empty": "Empty trash",
|
||||
"components.item_context_menu.add_documents": "Add document or folder",
|
||||
"components.item_context_menu.download_folder": "Download folder",
|
||||
"components.item_context_menu.go_to_trash": "Go to trash"
|
||||
"components.item_context_menu.move": "Déplacer",
|
||||
"components.item_context_menu.move.modal_header": "Déplacer",
|
||||
"components.item_context_menu.move_to_trash": "Déplacer vers la corbeille",
|
||||
"components.item_context_menu.delete": "Supprimer",
|
||||
"components.item_context_menu.move_multiple": "Déplacer les éléments sélectionnés",
|
||||
"components.item_context_menu.move_multiple.modal_header": "Déplacer les éléments sélectionnés",
|
||||
"components.item_context_menu.download_multiple": "Télécharger les éléments sélectionnés",
|
||||
"components.item_context_menu.clear_selection": "Effacer la sélection",
|
||||
"components.item_context_menu.delete_multiple": "Supprimer",
|
||||
"components.item_context_menu.to_trash_multiple": "Déplacer les éléments sélectionnés vers la corbeille",
|
||||
"components.item_context_menu.trash.exit": "Quitter la corbeille",
|
||||
"components.item_context_menu.trash.empty": "Vider la corbeille",
|
||||
"components.item_context_menu.add_documents": "Ajouter un document ou un dossier",
|
||||
"components.item_context_menu.download_folder": "Télécharger le dossier",
|
||||
"components.item_context_menu.go_to_trash": "Aller à la corbeille",
|
||||
"components.item_context_menu.all": "Tous",
|
||||
"components.item_context_menu.today": "Aujourd'hui",
|
||||
"components.item_context_menu.last_week": "La semaine dernière",
|
||||
"components.item_context_menu.last_month": "Le mois dernier",
|
||||
"scenes.app.shared_with_me.shared_with_me": "Partagé avec moi",
|
||||
"scenes.app.shared_with_me.file_type": "Type de fichier",
|
||||
"scenes.app.shared_with_me.people": "Personnes",
|
||||
"scenes.app.shared_with_me.last_modified": "Dernière modification",
|
||||
"scenes.app.shared_with_me.documents": "Documents",
|
||||
"scenes.app.shared_with_me.name": "Nom",
|
||||
"scenes.app.shared_with_me.shared_by": "Partagé par",
|
||||
"scenes.app.shared_with_me.shared_date": "Date de partage",
|
||||
"scenes.app.shared_with_me.edit": "Modifier"
|
||||
}
|
||||
@@ -257,5 +257,18 @@
|
||||
"components.item_context_menu.trash.empty": "Очистить корзину",
|
||||
"components.item_context_menu.add_documents": "Добавить документ в папку",
|
||||
"components.item_context_menu.download_folder": "Скачать папку",
|
||||
"components.item_context_menu.go_to_trash": "Перейти в корзину"
|
||||
"components.item_context_menu.go_to_trash": "Перейти в корзину",
|
||||
"components.item_context_menu.all": "Все",
|
||||
"components.item_context_menu.today": "Сегодня",
|
||||
"components.item_context_menu.last_week": "За неделю",
|
||||
"components.item_context_menu.last_month": "За месяц",
|
||||
"scenes.app.shared_with_me.shared_with_me": "Доступные мне",
|
||||
"scenes.app.shared_with_me.file_type": "Тип файла",
|
||||
"scenes.app.shared_with_me.people": "Люди",
|
||||
"scenes.app.shared_with_me.last_modified": "Последнее изменение",
|
||||
"scenes.app.shared_with_me.documents": "Файлы",
|
||||
"scenes.app.shared_with_me.name": "Название",
|
||||
"scenes.app.shared_with_me.shared_by": "Кто открыл доступ",
|
||||
"scenes.app.shared_with_me.shared_date": "Дата предоставления доступа",
|
||||
"scenes.app.shared_with_me.edit": "Редактировать"
|
||||
}
|
||||
@@ -17,6 +17,10 @@ export type SearchDocumentsBody = {
|
||||
creator?: string;
|
||||
added?: string;
|
||||
};
|
||||
export type sharedWithMeFilterBody = {
|
||||
mime_type?: string;
|
||||
creator?: string;
|
||||
};
|
||||
|
||||
let publicLinkToken: null | string = null;
|
||||
let tdriveTabToken: null | string = null;
|
||||
@@ -132,12 +136,12 @@ export class DriveApiClient {
|
||||
);
|
||||
}
|
||||
|
||||
static async search(searchString: string, view?: string,options?: BaseSearchOptions) {
|
||||
static async search(searchString: string, view?: string, options?: BaseSearchOptions) {
|
||||
const companyId = options?.company_id ? options.company_id : Workspace.currentGroupId;
|
||||
const query = `/internal/services/documents/v1/companies/${companyId}/search`;
|
||||
const searchData = {
|
||||
search: searchString,
|
||||
view: view
|
||||
view: view,
|
||||
};
|
||||
const res = await Api.post<SearchDocumentsBody, { entities: DriveItem[] }>(query, searchData);
|
||||
this.logger.debug(
|
||||
@@ -149,11 +153,14 @@ export class DriveApiClient {
|
||||
return res;
|
||||
}
|
||||
|
||||
static async sharedWithMe(options?: BaseSearchOptions) {
|
||||
static async sharedWithMe(filter?: any, options?: BaseSearchOptions) {
|
||||
const companyId = options?.company_id ? options.company_id : Workspace.currentGroupId;
|
||||
const query = `/internal/services/documents/v1/companies/${companyId}/shared-with-me`;
|
||||
const filterData = {};
|
||||
const res = await Api.post<SearchDocumentsBody, { entities: DriveItem[] }>(query, filterData);
|
||||
const filterData = { mime_type: filter.mimeType, creator: filter.creator, view: "shared_with_me" };
|
||||
const res = await Api.post<sharedWithMeFilterBody, { entities: DriveItem[] }>(
|
||||
query,
|
||||
filterData,
|
||||
);
|
||||
this.logger.debug(
|
||||
`Drive shared with me by filter "${JSON.stringify(filterData)}". Found`,
|
||||
res.entities.length,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useCallback } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { DriveApiClient } from '../api-client/api-client';
|
||||
import { DriveItemAtom, DriveItemChildrenAtom } from '../state/store';
|
||||
import { DriveItem, DriveItemDetails, DriveItemVersion } from '../types';
|
||||
import { DriveItem, DriveItemVersion } from '../types';
|
||||
|
||||
/**
|
||||
* Returns the children of a drive item
|
||||
@@ -18,59 +18,20 @@ export const useDriveActions = () => {
|
||||
({ set, snapshot }) =>
|
||||
async (parentId: string) => {
|
||||
if (parentId) {
|
||||
if (parentId == "shared-with-me") {
|
||||
const details = {
|
||||
path: [
|
||||
{
|
||||
id: "shared-with-me",
|
||||
name: "Shared with me"
|
||||
}
|
||||
],
|
||||
item: {
|
||||
id: "root",
|
||||
parent_id: "",
|
||||
company_id: "",
|
||||
workspace_id: "",
|
||||
name: "Shared with me",
|
||||
size: 0,
|
||||
description: "",
|
||||
tags: [],
|
||||
in_trash: false,
|
||||
is_directory: true,
|
||||
extension: "",
|
||||
added: "",
|
||||
last_modified: "",
|
||||
last_version_cache: {},
|
||||
access_info: {},
|
||||
},
|
||||
versions: [],
|
||||
children: [],
|
||||
access: "manage",
|
||||
websockets: [
|
||||
{
|
||||
room: "/companies/aa7ffdd0-fadb-11ed-b891-510fe3501b2b/documents/item/root",
|
||||
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhYTU0Y2YyMC1mYWRiLTExZWQtYjg5MS01MTBmZTM1MDFiMmIiLCJuYW1lIjoiL2NvbXBhbmllcy9hYTdmZmRkMC1mYWRiLTExZWQtYjg5MS01MTBmZTM1MDFiMmIvZG9jdW1lbnRzL2l0ZW0vcm9vdCIsImlhdCI6MTY4ODk1MDk2OCwibmJmIjoxNjg2MjcyNTA4fQ.H6BFRcLG3Op32sqKi45Pf1s2YKcVbMxGZGPJal06l1g"
|
||||
}
|
||||
]
|
||||
};
|
||||
try {
|
||||
const details = await DriveApiClient.get(companyId, parentId);
|
||||
set(DriveItemChildrenAtom(parentId), details.children);
|
||||
return details;
|
||||
} else {
|
||||
try {
|
||||
const details = await DriveApiClient.get(companyId, parentId);
|
||||
set(DriveItemChildrenAtom(parentId), details.children);
|
||||
set(DriveItemAtom(parentId), details);
|
||||
for (const child of details.children) {
|
||||
const currentValue = snapshot.getLoadable(DriveItemAtom(child.id)).contents;
|
||||
if (!currentValue) {
|
||||
//only update if not already in cache to avoid concurrent updates
|
||||
set(DriveItemAtom(child.id), { item: child });
|
||||
}
|
||||
set(DriveItemAtom(parentId), details);
|
||||
for (const child of details.children) {
|
||||
const currentValue = snapshot.getLoadable(DriveItemAtom(child.id)).contents;
|
||||
if (!currentValue) {
|
||||
//only update if not already in cache to avoid concurrent updates
|
||||
set(DriveItemAtom(child.id), { item: child });
|
||||
}
|
||||
return details;
|
||||
} catch (e) {
|
||||
ToasterService.error('Unable to load your files.');
|
||||
}
|
||||
return details;
|
||||
} catch (e) {
|
||||
ToasterService.error('Unable to load your files.');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -7,16 +7,16 @@ import _ from 'lodash';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { SharedWithMeDriveItemsResultsState } from '../state/shared-with-me-drive-items-result';
|
||||
import { SearchInputState } from '../../search/state/search-input';
|
||||
import { SharedWithMeFilterState } from '../state/shared-with-me-filter';
|
||||
|
||||
export const useSharedWithMeDriveItemsLoading = () => {
|
||||
return useRecoilValue(LoadingState('useSearchDriveItems'));
|
||||
};
|
||||
|
||||
let currentQuery = '';
|
||||
|
||||
export const useSharedWithMeDriveItems = () => {
|
||||
const companyId = useRouterCompany();
|
||||
const searchInput = useRecoilValue(SearchInputState);
|
||||
const sharedFilter = useRecoilValue(SharedWithMeFilterState);
|
||||
const [loading, setLoading] = useRecoilState(LoadingState('useSearchDriveItems'));
|
||||
|
||||
const [items, setItems] = useRecoilState(SharedWithMeDriveItemsResultsState(companyId));
|
||||
@@ -34,11 +34,24 @@ export const useSharedWithMeDriveItems = () => {
|
||||
const refresh = async () => {
|
||||
setLoading(true);
|
||||
|
||||
const query = searchInput.query;
|
||||
currentQuery = query;
|
||||
let filter:any = {...sharedFilter};
|
||||
if (filter.date) {
|
||||
if (filter.date === "today") {
|
||||
filter = { ...filter, added_lt: '', added_gt: '' };
|
||||
}
|
||||
if (filter.date === "last_week") {
|
||||
const today = new Date();
|
||||
const lastWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7);
|
||||
filter = { ...filter, added_lt: lastWeek.toISOString(), added_gt: '' };
|
||||
}
|
||||
if (filter.date === "last_month") {
|
||||
const today = new Date();
|
||||
const lastMonth = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
|
||||
filter = { ...filter, added_lt: lastMonth.toISOString(), added_gt: '' };
|
||||
}
|
||||
}
|
||||
|
||||
const response = await DriveApiClient.sharedWithMe(opt);
|
||||
console.log("response is: ", response);
|
||||
const response = await DriveApiClient.sharedWithMe(filter, opt);
|
||||
const results = response.entities || [];
|
||||
|
||||
const update = {
|
||||
@@ -47,9 +60,6 @@ export const useSharedWithMeDriveItems = () => {
|
||||
// nextPage: response.next_page_token,
|
||||
};
|
||||
|
||||
if (currentQuery !== query) {
|
||||
return;
|
||||
}
|
||||
setItems(update);
|
||||
setLoading(false);
|
||||
};
|
||||
@@ -62,18 +72,18 @@ export const useSharedWithMeDriveItems = () => {
|
||||
useGlobalEffect(
|
||||
'useSearchDriveItems',
|
||||
() => {
|
||||
(async () => {
|
||||
setLoading(true);
|
||||
if (searchInput.query) {
|
||||
delayRequest('useSearchDriveItems', async () => {
|
||||
await refresh();
|
||||
});
|
||||
} else {
|
||||
refresh();
|
||||
}
|
||||
})();
|
||||
(async () => {
|
||||
setLoading(true);
|
||||
if (sharedFilter.mimeType) {
|
||||
delayRequest('useSearchDriveItems', async () => {
|
||||
await refresh();
|
||||
});
|
||||
} else {
|
||||
refresh();
|
||||
}
|
||||
})();
|
||||
},
|
||||
[searchInput.channelId, searchInput.workspaceId],
|
||||
[sharedFilter, searchInput.channelId, searchInput.workspaceId],
|
||||
);
|
||||
|
||||
return { loading, driveItems: [...items.results], loadMore, refresh };
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { string } from 'prop-types';
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export type SharedWithMeFilter = {
|
||||
mimeType: string;
|
||||
creator: string;
|
||||
date: string;
|
||||
};
|
||||
|
||||
export const SharedWithMeFilterState = atom<SharedWithMeFilter>({
|
||||
key: 'SharedWithMeFilterState',
|
||||
default: {
|
||||
mimeType: '',
|
||||
creator: '',
|
||||
date: '',
|
||||
},
|
||||
});
|
||||
@@ -77,6 +77,7 @@ export default memo(
|
||||
const uploadZoneRef = useRef<UploadZone | null>(null);
|
||||
|
||||
const setCreationModalState = useSetRecoilState(CreateModalAtom);
|
||||
|
||||
const [checked, setChecked] = useRecoilState(DriveItemSelectedList);
|
||||
|
||||
const setParentId = useCallback(
|
||||
@@ -130,6 +131,9 @@ export default memo(
|
||||
<>
|
||||
{viewId == 'shared-with-me' ? (
|
||||
<>
|
||||
<Suspense fallback={<></>}>
|
||||
<DrivePreview />
|
||||
</Suspense>
|
||||
<SharedFilesTable />
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { DriveCurrentFolderAtom } from './browser';
|
||||
import { ConfirmDeleteModalAtom } from './modals/confirm-delete';
|
||||
@@ -16,7 +16,10 @@ import { DriveItemSelectedList } from '@features/drive/state/store';
|
||||
import { DriveItem, DriveItemDetails } from '@features/drive/types';
|
||||
import { ToasterService } from '@features/global/services/toaster-service';
|
||||
import { copyToClipboard } from '@features/global/utils/CopyClipboard';
|
||||
import Languages from "features/global/services/languages-service";
|
||||
import { SharedWithMeFilterState } from '@features/drive/state/shared-with-me-filter';
|
||||
import { getCurrentUserList, getUser } from '@features/users/hooks/use-user-list';
|
||||
import _ from 'lodash';
|
||||
import Languages from 'features/global/services/languages-service';
|
||||
|
||||
/**
|
||||
* This will build the context menu in different contexts
|
||||
@@ -90,7 +93,9 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
|
||||
hide: !item.access_info.public?.level || item.access_info.public?.level === 'none',
|
||||
onClick: () => {
|
||||
copyToClipboard(getPublicLink(item || parent?.item));
|
||||
ToasterService.success(Languages.t('components.item_context_menu.copy_link.success'));
|
||||
ToasterService.success(
|
||||
Languages.t('components.item_context_menu.copy_link.success'),
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -108,7 +113,9 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
|
||||
open: true,
|
||||
parent_id: inTrash ? 'root' : item.parent_id,
|
||||
mode: 'move',
|
||||
title: Languages.t('components.item_context_menu.move.modal_header') + ` '${item.name}'`,
|
||||
title:
|
||||
Languages.t('components.item_context_menu.move.modal_header') +
|
||||
` '${item.name}'`,
|
||||
onSelected: async ids => {
|
||||
await update(
|
||||
{
|
||||
@@ -254,7 +261,9 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
|
||||
parent?.item?.access_info?.public?.level === 'none',
|
||||
onClick: () => {
|
||||
copyToClipboard(getPublicLink(item || parent?.item));
|
||||
ToasterService.success(Languages.t('components.item_context_menu.copy_link.success'));
|
||||
ToasterService.success(
|
||||
Languages.t('components.item_context_menu.copy_link.success'),
|
||||
);
|
||||
},
|
||||
},
|
||||
{ type: 'separator', hide: inTrash || parent.access === 'read' },
|
||||
@@ -297,63 +306,138 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
|
||||
);
|
||||
};
|
||||
|
||||
export const onBuildFileTypeContextMenu = () => {
|
||||
const menuItems = [
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'PDF',
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'DOC',
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'PNG',
|
||||
},
|
||||
export const useOnBuildFileTypeContextMenu = () => {
|
||||
const [filter, setFilter] = useRecoilState(SharedWithMeFilterState);
|
||||
const mimeTypes = [
|
||||
{ key: Languages.t('components.item_context_menu.all'), value: '' },
|
||||
{ key: 'PDF', value: 'application/pdf' },
|
||||
{ key: 'DOC', value: 'application/msword' },
|
||||
{ key: 'PNG', value: 'image/png' },
|
||||
];
|
||||
return menuItems;
|
||||
return useCallback(() => {
|
||||
const menuItems = mimeTypes.map(item => {
|
||||
return {
|
||||
type: 'menu',
|
||||
text: item.key,
|
||||
onClick: () => {
|
||||
setFilter(prevFilter => {
|
||||
const newFilter = {
|
||||
...prevFilter,
|
||||
mimeType: item.value,
|
||||
};
|
||||
return newFilter;
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
return menuItems;
|
||||
}, [setFilter]);
|
||||
};
|
||||
export const onBuildPeopleContextMenu = () => {
|
||||
const menuItems = [
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'Dwho',
|
||||
},
|
||||
];
|
||||
return menuItems;
|
||||
|
||||
export const useOnBuildPeopleContextMenu = () => {
|
||||
const [filter, setFilter] = useRecoilState(SharedWithMeFilterState);
|
||||
const [_userList, setUserList] = useState(getCurrentUserList());
|
||||
let userList = _userList;
|
||||
userList = _.uniqBy(userList, 'id');
|
||||
return useCallback(() => {
|
||||
const menuItems = userList.map(user => {
|
||||
return {
|
||||
type: 'menu',
|
||||
text: user.first_name,
|
||||
onClick: () => {
|
||||
setFilter(prevFilter => {
|
||||
const newFilter = {
|
||||
...prevFilter,
|
||||
creator: user.id ?? '',
|
||||
};
|
||||
return newFilter;
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
return menuItems;
|
||||
}, [setFilter]);
|
||||
};
|
||||
export const onBuildDateContextMenu = () => {
|
||||
const menuItems = [
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'All',
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'Today',
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'Last week',
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'Last month',
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'Range',
|
||||
},
|
||||
];
|
||||
return menuItems;
|
||||
|
||||
export const useOnBuildDateContextMenu = () => {
|
||||
const [filter, setFilter] = useRecoilState(SharedWithMeFilterState);
|
||||
return useCallback(() => {
|
||||
const menuItems = [
|
||||
{
|
||||
type: 'menu',
|
||||
text: Languages.t('components.item_context_menu.all'),
|
||||
onClick: () => {
|
||||
setFilter(prevFilter => {
|
||||
const newFilter = {
|
||||
...prevFilter,
|
||||
date: '',
|
||||
};
|
||||
return newFilter;
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: Languages.t('components.item_context_menu.today'),
|
||||
onClick: () => {
|
||||
setFilter(prevFilter => {
|
||||
const newFilter = {
|
||||
...prevFilter,
|
||||
date: 'today',
|
||||
};
|
||||
return newFilter;
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: Languages.t('components.item_context_menu.last_week'),
|
||||
onClick: () => {
|
||||
setFilter(prevFilter => {
|
||||
const newFilter = {
|
||||
...prevFilter,
|
||||
date: 'last_week',
|
||||
};
|
||||
return newFilter;
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: Languages.t('components.item_context_menu.last_month'),
|
||||
onClick: () => {
|
||||
setFilter(prevFilter => {
|
||||
const newFilter = {
|
||||
...prevFilter,
|
||||
date: 'last_month',
|
||||
};
|
||||
return newFilter;
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
return menuItems;
|
||||
}, [setFilter]);
|
||||
};
|
||||
export const onBuildFileContextMenu = (id: string) => {
|
||||
const menuItems = [
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'Download'
|
||||
export const useOnBuildFileContextMenu = () => {
|
||||
const { download } = useDriveActions();
|
||||
const { open: preview } = useDrivePreview();
|
||||
return useCallback(
|
||||
(item: DriveItem) => {
|
||||
const menuItems = [
|
||||
{
|
||||
type: 'menu',
|
||||
text: Languages.t('components.item_context_menu.preview'),
|
||||
onClick: () => preview(item),
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: Languages.t('components.item_context_menu.download'),
|
||||
onClick: () => download(item.id),
|
||||
},
|
||||
];
|
||||
return menuItems;
|
||||
},
|
||||
];
|
||||
return menuItems;
|
||||
[download, preview],
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,139 +1,138 @@
|
||||
import { useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { ChevronDownIcon, ChevronUpIcon, DotsHorizontalIcon } from '@heroicons/react/outline';
|
||||
import { Button } from '@atoms/button/button';
|
||||
import { Base, BaseSmall, Title } from '@atoms/text';
|
||||
import Menu from '@components/menus/menu';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import {
|
||||
onBuildFileTypeContextMenu,
|
||||
onBuildPeopleContextMenu,
|
||||
onBuildDateContextMenu,
|
||||
onBuildFileContextMenu,
|
||||
useOnBuildFileTypeContextMenu,
|
||||
useOnBuildPeopleContextMenu,
|
||||
useOnBuildDateContextMenu,
|
||||
useOnBuildFileContextMenu,
|
||||
} from './context-menu';
|
||||
import { useSharedWithMeDriveItems } from '@features/drive/hooks/use-shared-with-me-drive-items';
|
||||
import { SharedWithMeFilterState } from '@features/drive/state/shared-with-me-filter';
|
||||
import MenusManager from '@components/menus/menus-manager.jsx';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
|
||||
export const SharedFilesTable = () => {
|
||||
const { driveItems, loading } = useSharedWithMeDriveItems();
|
||||
const [sortBy, setSortBy] = useState('name');
|
||||
const [sortOrder, setSortOrder] = useState('asc');
|
||||
const [filter, setFilter] = useRecoilState(SharedWithMeFilterState);
|
||||
|
||||
const handleSort = (column: string) => {
|
||||
if (sortBy === column) {
|
||||
setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
|
||||
} else {
|
||||
setSortBy(column);
|
||||
setSortOrder('asc');
|
||||
}
|
||||
};
|
||||
// FILTER HOOKS
|
||||
const buildFileTypeContextMenu = useOnBuildFileTypeContextMenu();
|
||||
const buildPeopleContextMen = useOnBuildPeopleContextMenu();
|
||||
const buildFileContextMenu = useOnBuildFileContextMenu();
|
||||
const buildDateContextMenu = useOnBuildDateContextMenu();
|
||||
|
||||
const renderSortIcon = (column: string) => {
|
||||
if (sortBy === column) {
|
||||
if (sortOrder === 'asc') {
|
||||
return <ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />;
|
||||
} else {
|
||||
return <ChevronUpIcon className="h-4 w-4 ml-2 -mr-1" />;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
const fileAddedDate = (timestamp: number) => {
|
||||
const [formattedDate, formattedTime] = new Date(timestamp).toLocaleString().split(', ');
|
||||
return formattedDate;
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Title className="mb-4 block">Shared with me</Title>
|
||||
<div>
|
||||
<Title className="mb-4 block">{Languages.t('scenes.app.shared_with_me.shared_with_me')}</Title>
|
||||
{/* Filters */}
|
||||
<div className="flex items-center space-x-4 mb-6">
|
||||
<div className="">
|
||||
<Button
|
||||
theme="secondary"
|
||||
className="flex items-center"
|
||||
onClick={evt => {
|
||||
MenusManager.openMenu(
|
||||
buildFileTypeContextMenu(),
|
||||
{ x: evt.clientX, y: evt.clientY },
|
||||
'center',
|
||||
);
|
||||
}}
|
||||
>
|
||||
<span>{filter.mimeType ? filter.mimeType : Languages.t('scenes.app.shared_with_me.file_type')}</span>
|
||||
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Menu menu={() => onBuildFileTypeContextMenu()}>
|
||||
<Button theme="secondary" className="flex items-center">
|
||||
<span>File Type</span>
|
||||
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
|
||||
</Button>
|
||||
</Menu>
|
||||
<Button
|
||||
theme="secondary"
|
||||
className="flex items-center"
|
||||
onClick={evt => {
|
||||
MenusManager.openMenu(
|
||||
buildPeopleContextMen(),
|
||||
{ x: evt.clientX, y: evt.clientY },
|
||||
'center',
|
||||
);
|
||||
}}
|
||||
>
|
||||
<span>{Languages.t('scenes.app.shared_with_me.people')}</span>
|
||||
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Menu menu={() => onBuildPeopleContextMenu()}>
|
||||
<Button theme="secondary" className="flex items-center">
|
||||
<span>People</span>
|
||||
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
|
||||
</Button>
|
||||
</Menu>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Menu menu={() => onBuildDateContextMenu()}>
|
||||
<Button theme="secondary" className="flex items-center">
|
||||
<span>Last Modified</span>
|
||||
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
|
||||
</Button>
|
||||
</Menu>
|
||||
<Button
|
||||
theme="secondary"
|
||||
className="flex items-center"
|
||||
onClick={evt => {
|
||||
MenusManager.openMenu(
|
||||
buildDateContextMenu(),
|
||||
{ x: evt.clientX, y: evt.clientY },
|
||||
'center',
|
||||
);
|
||||
}}
|
||||
>
|
||||
<span>{Languages.t('scenes.app.shared_with_me.last_modified')}</span>
|
||||
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Title className="mb-4 block">Documents:</Title>
|
||||
<Title className="mb-4 block">{Languages.t('scenes.app.drive.documents')}:</Title>
|
||||
<div className="relative overflow-x-auto shadow-md sm:rounded-lg">
|
||||
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
||||
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-blue-500 dark:text-white">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
<span className="flex" onClick={() => handleSort('name')}>
|
||||
Name {renderSortIcon('name')}
|
||||
</span>
|
||||
<span className="flex">{Languages.t('scenes.app.shared_with_me.name')}</span>
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
<span className="flex" onClick={() => handleSort('name')}>
|
||||
Shared By {renderSortIcon('name')}
|
||||
</span>
|
||||
<span className="flex">{Languages.t('scenes.app.shared_with_me.shared_by')}</span>
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
<span className="flex" onClick={() => handleSort('name')}>
|
||||
Shared Date {renderSortIcon('name')}
|
||||
</span>
|
||||
<span className="flex">Shared Date{Languages.t('scenes.app.shared_with_me.shared_date')}</span>
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
<span className="sr-only">Edit</span>
|
||||
<span className="sr-only">{Languages.t('scenes.app.shared_with_me.edit')}</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{!loading &&
|
||||
driveItems
|
||||
.sort((a: any, b: any) => {
|
||||
// Perform sorting based on the selected column and order
|
||||
console.log("a is: ", a);
|
||||
console.log("b is: ", b);
|
||||
if (sortBy === 'name') {
|
||||
return sortOrder === 'asc'
|
||||
? a.name.localeCompare(b.name)
|
||||
: b.name.localeCompare(a.name);
|
||||
}
|
||||
return 0; // No sorting by default
|
||||
})
|
||||
.map((file: any, index: any) => (
|
||||
<tr
|
||||
key={index}
|
||||
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600"
|
||||
driveItems.map((file: any, index: any) => (
|
||||
<tr
|
||||
key={index}
|
||||
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600"
|
||||
>
|
||||
<th
|
||||
scope="row"
|
||||
className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
<th
|
||||
scope="row"
|
||||
className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
{file.name}
|
||||
</th>
|
||||
<td className="px-6 py-4">Dwho</td>
|
||||
<td className="px-6 py-4">2023-05-05</td>
|
||||
<td className="px-6 py-4 text-right">
|
||||
<Menu menu={onBuildFileContextMenu(file.id)}>
|
||||
<Button
|
||||
theme={'secondary'}
|
||||
size="sm"
|
||||
className={'!rounded-full '}
|
||||
icon={DotsHorizontalIcon}
|
||||
/>
|
||||
</Menu>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{file.name}
|
||||
</th>
|
||||
<td className="px-6 py-4">{file.creator?.id}</td>
|
||||
<td className="px-6 py-4">{fileAddedDate(file.added)}</td>
|
||||
<td className="px-6 py-4 text-right">
|
||||
<Menu menu={buildFileContextMenu(file)}>
|
||||
<Button
|
||||
theme={'secondary'}
|
||||
size="sm"
|
||||
className={'!rounded-full '}
|
||||
icon={DotsHorizontalIcon}
|
||||
/>
|
||||
</Menu>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user