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

Exporter les membres d'un salon dans un fichier csv #625

Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3032915
Add tab to download csv file of the list of users in a room
aulamber Jun 1, 2023
f3ff280
Put download file in tchap utils, and change csv to txt donwload
aulamber Jun 1, 2023
71b0cbf
Add tooltip
aulamber Jun 20, 2023
d52fa19
Fix conflict and change wording
aulamber Jun 21, 2023
1e29119
Add tab to download csv file of the list of users in a room (#613)
aulamber Jun 22, 2023
18cc90f
Add tests for button download txt file
aulamber Jun 29, 2023
8cb2c16
Undo cypress modifs
aulamber Jun 29, 2023
3c51d99
Fix eslint
aulamber Jun 29, 2023
a098545
Add cypress test
aulamber Jun 30, 2023
140532f
Merge develop and fix conflict
aulamber Jun 30, 2023
eb2d9ed
Fix eslint
aulamber Jun 30, 2023
a7eab5a
Merge branch 'develop_tchap' into add-tab-to-download-csv-file-of-the…
estellecomment Aug 24, 2023
471612d
Cleanup
estellecomment Aug 24, 2023
f77581b
Move button to Members menu
estellecomment Aug 24, 2023
4c04bbb
Add back tooltip, using AccessibleTooltipButton
estellecomment Aug 24, 2023
998b37c
Move button to the top, with other button
estellecomment Aug 24, 2023
9dabf94
Use FileDownloader from react-sdk rather than implement our own
estellecomment Aug 24, 2023
d36f930
Remove previous implementation
estellecomment Aug 24, 2023
f4c8ad3
add room name in exported filename
estellecomment Aug 24, 2023
6fcd45f
Rename patch, shorter name
estellecomment Aug 24, 2023
078c839
Update translations
estellecomment Aug 24, 2023
0c8b690
Add icon (better icon coming)
estellecomment Aug 24, 2023
8538a09
Button styling
estellecomment Aug 24, 2023
533880e
Final icon
estellecomment Aug 24, 2023
de3f331
Better names for things
estellecomment Aug 24, 2023
455cb1b
Separate ids with commas in text file
estellecomment Aug 24, 2023
44df2ae
Cypress test WIP
estellecomment Aug 25, 2023
751134d
Cypress : use normal room name
estellecomment Aug 25, 2023
7fc4249
Cypress tooltip test
estellecomment Aug 25, 2023
e2996db
Cypress : leave room when done
estellecomment Aug 25, 2023
ba4129f
Rename file
estellecomment Aug 25, 2023
51db713
Fit linter
estellecomment Aug 25, 2023
0cd42ff
Make separate component for minimal footprint in patch
estellecomment Aug 29, 2023
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
46 changes: 46 additions & 0 deletions cypress/e2e/export-room-members/export-room-members.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/// <reference types="cypress" />

import RoomUtils from "../../utils/room-utils";
import RandomUtils from "../../utils/random-utils";
import { normalize } from "../../../yarn-linked-dependencies/matrix-js-sdk/src/utils";

describe("Export room members feature", () => {
const homeserverUrl = Cypress.env("E2E_TEST_USER_HOMESERVER_URL");
const homeserverShort = Cypress.env("E2E_TEST_USER_HOMESERVER_SHORT");
const email = Cypress.env("E2E_TEST_USER_EMAIL");
const password = Cypress.env("E2E_TEST_USER_PASSWORD");
const today = new Date().toISOString().slice(0, 10).replace(/-/g, "");
// This will fail if email has special characters.
const userId = "@" + email.replace("@", "-") + ":" + homeserverShort;

beforeEach(() => {
cy.loginUser(homeserverUrl, email, password);
});

afterEach(() => {});

it("should display the tooltip on button hover", () => {
const roomName = "test/" + today + "/export_room_members/" + RandomUtils.generateRandom(4);
RoomUtils.createPublicRoom(roomName).then((roomId) => {
RoomUtils.openPeopleMenu(roomName);
cy.get('[data-testid="tc_exportRoomMembersButton"]')
.trigger("mouseover")
.get(".tc_exportRoomMembersTooltip"); // tooltip should show
cy.leaveRoom(roomId);
});
});

it("downloads the file when button is clicked", () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

const roomName = "test/" + today + "/export_room_members/" + RandomUtils.generateRandom(4);
const normalizedRoomName = normalize(roomName);
RoomUtils.createPublicRoom(roomName).then((roomId) => {
RoomUtils.openPeopleMenu(roomName);
cy.get('[data-testid="tc_exportRoomMembersButton"]')
.click()
.then(() => {
cy.readFile("cypress/downloads/membres_de_" + normalizedRoomName + ".txt").should("eq", userId);
});
cy.leaveRoom(roomId);
});
});
});
21 changes: 16 additions & 5 deletions cypress/utils/room-utils.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
import Chainable = Cypress.Chainable;
import TchapCreateRoom from "../../src/tchap/lib/createTchapRoom";
import { TchapRoomType } from "../../src/tchap/@types/tchap";

export default class RoomUtils {
public static createPublicRoom(roomName: string): Chainable<string> {
return cy.createRoom(TchapCreateRoom.roomCreateOptions(roomName, TchapRoomType.Forum).createOpts);
return cy.createRoom(TchapCreateRoom.roomCreateOptions(roomName, TchapRoomType.Forum, false).createOpts);
}
public static createPrivateRoom(roomName: string): Chainable<string> {
return cy.createRoom(TchapCreateRoom.roomCreateOptions(roomName, TchapRoomType.Private).createOpts);
return cy.createRoom(TchapCreateRoom.roomCreateOptions(roomName, TchapRoomType.Private, false).createOpts);
}
public static createPrivateWithExternalRoom(roomName: string): Chainable<string> {
return cy.createRoom(TchapCreateRoom.roomCreateOptions(roomName, TchapRoomType.External).createOpts);
return cy.createRoom(TchapCreateRoom.roomCreateOptions(roomName, TchapRoomType.External, false).createOpts);
}
public static openRoom(roomName: string): Chainable<JQuery<HTMLElement>> {
return cy.get('[aria-label="' + roomName + '"]').click();
}
public static openRoomAccessSettings(roomName: string): Chainable<JQuery<HTMLElement>> {
//open room
cy.get('[aria-label="' + roomName + '"]').click();
cy.get('[aria-label="' + roomName + '"]').click(); //open room
cy.get(".mx_RoomHeader_chevron").click();
cy.get('[aria-label="Paramètres"] > .mx_IconizedContextMenu_label').click();
return cy.get('[data-testid="settings-tab-ROOM_SECURITY_TAB"] > .mx_TabbedView_tabLabel_text').click();
}
public static openRoomInformation(roomName: string): Chainable<JQuery<HTMLElement>> {
cy.get('[aria-label="' + roomName + '"]').click(); //open room
return cy.get('[aria-label="Information du salon"]').click();
}
public static openPeopleMenu(roomName: string): Chainable<JQuery<HTMLElement>> {
this.openRoomInformation(roomName);
return cy.get(".mx_RoomSummaryCard_icon_people").click();
}
}
86 changes: 86 additions & 0 deletions patches/export-room-members/matrix-react-sdk+3.71.1.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
diff --git a/node_modules/matrix-react-sdk/src/components/views/rooms/MemberList.tsx b/node_modules/matrix-react-sdk/src/components/views/rooms/MemberList.tsx
index 44dba2e..370224e 100644
--- a/node_modules/matrix-react-sdk/src/components/views/rooms/MemberList.tsx
+++ b/node_modules/matrix-react-sdk/src/components/views/rooms/MemberList.tsx
@@ -39,6 +39,7 @@ import TruncatedList from "../elements/TruncatedList";
import Spinner from "../elements/Spinner";
import SearchBox from "../../structures/SearchBox";
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
+import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; // TCHAP
import EntityTile from "./EntityTile";
import MemberTile from "./MemberTile";
import BaseAvatar from "../avatars/BaseAvatar";
@@ -46,6 +47,8 @@ import { shouldShowComponent } from "../../../customisations/helpers/UIComponent
import { UIComponent } from "../../../settings/UIFeature";
import PosthogTrackers from "../../../PosthogTrackers";
import { SDKContext } from "../../../contexts/SDKContext";
+import { FileDownloader } from "../../../utils/FileDownloader"; // TCHAP
+import "../../../../../../res/css/views/rooms/TchapMemberListExport.pcss"; // TCHAP

const INITIAL_LOAD_NUM_MEMBERS = 30;
const INITIAL_LOAD_NUM_INVITED = 5;
@@ -73,6 +76,7 @@ export default class MemberList extends React.Component<IProps, IState> {

public static contextType = SDKContext;
public context!: React.ContextType<typeof SDKContext>;
+ private downloader = new FileDownloader(); // TCHAP

public constructor(props: IProps, context: React.ContextType<typeof SDKContext>) {
super(props);
@@ -368,6 +372,23 @@ export default class MemberList extends React.Component<IProps, IState> {
);
}

+ /* TCHAP */
+ let exportMembersButton;
+ if (room?.getMyMembership() === "join" && !room.isSpaceRoom() && this.getChildCountJoined() > 0) {
+ exportMembersButton = (
+ <AccessibleTooltipButton
+ data-testid="tc_exportRoomMembersButton"
+ className="tc_MemberList_export mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
+ onClick={this.onExportButtonClick}
+ tooltip={_t("Download the list of all this room's members, in a text file. Useful for adding them all to another room.")}
+ tooltipClassName="tc_exportRoomMembersTooltip"
+ >
+ <span>{_t("Export room members")}</span>
+ </AccessibleTooltipButton>
+ );
+ }
+ /* end TCHAP */
+
let invitedHeader;
let invitedSection;
if (this.getChildCountInvited() > 0) {
@@ -409,6 +430,7 @@ export default class MemberList extends React.Component<IProps, IState> {
<React.Fragment>
{scopeHeader}
{inviteButton}
+ {exportMembersButton /* TCHAP */}
</React.Fragment>
}
footer={footer}
@@ -443,4 +465,24 @@ export default class MemberList extends React.Component<IProps, IState> {
roomId: this.props.roomId,
});
};
+
+ /* TCHAP */
+ private onExportButtonClick = (ev: ButtonEvent): void => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

along with the button, could it have been a separated component to aliviate the patch footprint?

+ const members = this.state.filteredJoinedMembers.map(roomMember => roomMember.userId);
+ const blob = new Blob([members.join()], { type : 'plain/text' })
+
+ const cli = MatrixClientPeg.get();
+ const room = cli.getRoom(this.props.roomId);
+ const filename = _t('members_of_%(roomName)s.txt', {
+ roomName: room.normalizedName,
+ });
+
+ this.downloader.download({
+ blob: blob,
+ name: filename,
+ });
+
+ return;
+ };
+ /* end TCHAP */
}
7 changes: 7 additions & 0 deletions patches/patches.json
Original file line number Diff line number Diff line change
Expand Up @@ -222,5 +222,12 @@
"src/components/structures/RoomSearchView.tsx",
"src/components/structures/RoomStatusBar.tsx"
]
},
"export-room-members": {
"github-issue": "https://github.com/tchapgouv/tchap-web-v4/issues/593",
"package": "matrix-react-sdk",
"files": [
"src/components/views/rooms/MemberList.tsx"
]
}
}
31 changes: 31 additions & 0 deletions res/css/views/rooms/TchapMemberListExport.pcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright DINUM 2023
*/

.mx_AccessibleButton.mx_AccessibleButton_hasKind.tc_MemberList_export {
/* Copy the style from mx_MemberList_invite button just above. For some reason it doesn't follow the standard style. */
display: flex;
justify-content: center;
margin: 5px 9px 9px;
padding: 0px;
border-radius: 4px;
font-weight: 600;
}

.tc_MemberList_export span {
padding: 8px 0;
display: inline-flex;

&::before {
content: "";
display: inline-block;
background-color: var(--accent);
mask-image: url("../../../img/tchap/user-export.svg");
mask-position: center;
mask-repeat: no-repeat;
mask-size: 20px;
width: 20px;
height: 20px;
margin-right: 5px;
}
}
4 changes: 4 additions & 0 deletions res/img/tchap/user-export.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/tchap/i18n/strings/tchap_translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,18 @@
"fr": "Veuillez ne continuer que si vous êtes certain d’avoir perdu tous vos autres appareils et votre Code de Récupération.",
"en": "Please only proceed if you're sure you've lost all of your other devices and your Recovery Code."
},
"Export room members": {
"fr": "Exporter les membres du salon",
"en": "Export room members"
},
"Download the list of all this room's members, in a text file. Useful for adding them all to another room.": {
"fr": "Récupérer la liste des membres de ce salon, dans un fichier texte. Utile pour inviter toutes ces personnes à un autre salon.",
"en": "Download the list of all this room's members, in a text file. Useful for adding them all to another room."
},
"members_of_%(roomName)s.txt": {
"fr": "membres_de_%(roomName)s.txt",
"en": "members_of_%(roomName)s.txt"
},
"Destroy cross-signing keys?": {
"fr": "Réinitialiser les clés de signature croisée ?",
"en": "Reset cross-signing keys?"
Expand Down
Loading