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

Invite users to service rooms #42

Merged
merged 109 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
6b41482
add icon and rudimentary component for inviting users
Aug 23, 2023
d040393
add logic to search for matrix users
Aug 23, 2023
43ba9b9
add invite user button
Aug 23, 2023
5851589
use react-modal package
Aug 23, 2023
5018104
add invitation logic and user feedback, close modal after sucessfull …
Aug 24, 2023
81f677a
add comments and TODO
Aug 24, 2023
cf3ec1e
improve object name
Aug 24, 2023
7369a8d
fix document undefined error
Aug 24, 2023
baf3db1
check for userId and displayName manually
Aug 24, 2023
73fceee
close modal after successful invitation
Aug 29, 2023
3f8bde8
merge main into invite-users
Aug 29, 2023
5b73822
merge main into invite-users
Aug 31, 2023
e1f40f2
remove unnecessary function and call function directly from matrixClient
Aug 31, 2023
77f8c79
use logger instead of console.log
Sep 5, 2023
1c935a0
replace `datalist` with `ServiceTable` and update imports
Sep 5, 2023
84cf241
remove unused imports
Sep 5, 2023
1036306
css clean up
Sep 5, 2023
3772fe4
clear search results on successful invitation
Sep 5, 2023
b7aa8ea
add translations and fix typos
Sep 5, 2023
882ad9a
remove duplicate
Sep 5, 2023
a44410a
add default modal component
Sep 5, 2023
5cb4e99
change modal style
Sep 6, 2023
67f7ac7
remove unused import
Sep 6, 2023
ef16180
change variable to from `name` to `roomName` and user user-add icon i…
Sep 6, 2023
626f47e
introduce and implement custom datalist component using ServiceTable
Sep 8, 2023
69bb68f
Create Datalist component and use when searching for users to invite
Sep 12, 2023
c52e8c9
Merge pull request #57 from medienhaus/invite-users-custom-datalist
andirueckel Sep 12, 2023
1df63a7
add translation
Sep 12, 2023
b0812bf
add translation
Sep 12, 2023
d6ca6a2
fix: disable stylelint for Modal custom styles to not trigger eslint
andirueckel Sep 12, 2023
12fa478
merge main into invite-users
Sep 13, 2023
f13dae0
Merge branch 'main' into invite-users
Sep 13, 2023
d9c00b4
refactor: close datalist when string empty
Sep 20, 2023
d8dc5bc
refactor: improve and shorten functions
Sep 20, 2023
5360bd6
merge main into invite-users
Sep 21, 2023
f42995c
chore: add comment and improve JSDoc
Sep 21, 2023
5b6d2c6
Merge branch 'main' into invite-users
Sep 21, 2023
9fca902
refactor: add InviteUsers
Sep 21, 2023
5226795
refactor: add InviteUsers to spacedeck
Sep 21, 2023
4b74202
Merge remote-tracking branch 'origin/invite-users' into invite-users
Sep 21, 2023
89357c5
Merge branch 'main' into invite-users
Sep 21, 2023
a17dfd6
Merge branch 'main' into invite-users
Sep 26, 2023
8e49701
fix: invitations not working and wrap user id in brackets
Sep 26, 2023
35dd409
refactor: remove unused import
Sep 26, 2023
1e2f1ec
merge main into invite-users
Oct 10, 2023
e4ce0c3
update package-lock.json
Oct 10, 2023
95c217a
refactor: use TextButton for svg
Oct 10, 2023
7a87e2a
chore: sort alphabetically after merge
Oct 10, 2023
79a597b
update package-lock
Oct 10, 2023
558f33b
refactor: move close icon and header inside DefaultModal
Oct 11, 2023
3414bc8
Merge branch 'main' into invite-users
Oct 17, 2023
f4a3d19
refactor: remove modal and display InviteUsersToMatrixRoom component …
Oct 24, 2023
98aa41b
refactor: uninstall react-modal
Oct 24, 2023
93cf51a
refactor: change button texts and headline
Oct 24, 2023
1a84ad0
Merge pull request #82 from medienhaus/invite-users-sans-modal
aofn Oct 24, 2023
eed8e0b
refactor: invite multiple users and display selected user below
Oct 24, 2023
6c9a543
small fixes and refactors
Oct 24, 2023
b4c3b49
merge main into invite-users
Oct 24, 2023
56041db
refactor: add onClick event to table row and add arrow indicating sel…
Oct 24, 2023
ba30b45
merge main into invite-users
Oct 25, 2023
5a11f66
Merge branch 'main' into invite-users
Oct 25, 2023
8d0ff23
refactor: use npm package for icons
Oct 25, 2023
c69e55d
refactor: remove unnecessary div
Oct 25, 2023
421b797
fix: comments
Oct 25, 2023
939b222
refactor: html structure
Oct 25, 2023
9bdc60a
WIP: align buttons on bottom
Oct 25, 2023
40d29f4
refactor: change structure to fit refactor of InviteUsersToMatrixRoom…
Oct 25, 2023
31c982b
refactor: remove unused parameter
Oct 25, 2023
0fa7757
style: introduce `--line-height` and `--icon-size` css variables
andirueckel Oct 25, 2023
810cc4c
Merge pull request #87 from medienhaus/invite-users-css-improvements
aofn Oct 25, 2023
fae3538
merge main into invite-users
Oct 31, 2023
b5dea4e
fix: remove unused imports and eslint problems
Oct 31, 2023
8d98fb6
merge main into invite-users
Oct 31, 2023
a913a6d
refactor: use checkboxes fir datalist
Oct 31, 2023
602cf45
merge main into invite-users
Nov 1, 2023
d751b01
Merge branch 'main' into invite-users
Nov 1, 2023
230baa2
Merge branch 'main' into invite-users
Nov 7, 2023
59f6d11
chore: make linter happy
Nov 7, 2023
e1aa86a
refactor update render of selected items only on input change so item…
Nov 8, 2023
5184844
refactor: move all state handling inside DataList.js
Nov 8, 2023
0fde3a4
refactor: add accessibility feature so arrow keys and tab serve the s…
Nov 8, 2023
8dd8458
refactor: only parse error messages instead of object, wrap success a…
Nov 8, 2023
b56b515
refactor: remove unnecessary code
Nov 8, 2023
fd01fdc
merge main into invite-users
Nov 8, 2023
ddbb984
fix: only display user feedback once
Nov 8, 2023
ede9bc1
Merge branch 'main' into invite-users
Nov 8, 2023
2073b33
chore: use newly introduced `Icon` component for svg icons
andirueckel Nov 8, 2023
e3cc50e
refactor: unfocus list elements when focusing text input
Nov 8, 2023
f476aa0
refactor: deselect element on mous press, when unchecking element
Nov 8, 2023
fdad060
fix: use onMouseDown instead of onClick
Nov 8, 2023
088c2c7
fix: use onMouseUp instead of onMouseDown
Nov 8, 2023
66abff3
Merge branch 'main' into invite-users
Nov 15, 2023
c62c2b6
refactor: use DataListRow for selected items, change variable name to…
Nov 15, 2023
101a3e4
fix: table breaking mobile width
Nov 15, 2023
73ec8ca
Merge branch 'main' into invite-users
fnwbr Nov 15, 2023
6112aa3
InviteUsersToMatrixRoom: Filter the user themselves from search results
fnwbr Nov 15, 2023
5b9e0a3
Use transient props with styled-components
fnwbr Nov 15, 2023
7c95e57
Remove jumpy rendering if there are no results / clean up JSX code
fnwbr Nov 15, 2023
b62f64a
refactor: use <Trans /> for complicated translation
Nov 16, 2023
f0dda56
chore: fix translations for invitations
Nov 16, 2023
7d71222
remove unused file
Nov 16, 2023
67150e7
create button component within InviteUsersToMatrixRoom component
Nov 16, 2023
eddff13
chore: add jsDoc for DataListRow
Nov 16, 2023
fefe201
refactor: use button component from InviteUsersToMatrixRoom
Nov 16, 2023
f0ef1a4
fix: success message not properly displaying after inviting users
Nov 17, 2023
b1e3486
Merge branch 'main' into invite-users
fnwbr Nov 28, 2023
b54cdd6
Allow checking a data list option by clicking it
fnwbr Nov 28, 2023
1d95be6
doc: Use React.ReactNode where possible
fnwbr Nov 28, 2023
522245d
Undo change that we might want, but let's not do it as part of this p…
fnwbr Nov 28, 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
1 change: 1 addition & 0 deletions assets/icons/user-add.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions components/UI/InviteUsersToMatrixRoom/UserListEntry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';

import { ServiceTable } from '../ServiceTable';
import LoadingSpinnerInline from '../LoadingSpinnerInline';

const UserListEntry = ({ user, handleInvite }) => {
const [isInviting, setIsInviting] = useState(false);
const { t } = useTranslation();

const handleClick = async (e) => {
e.preventDefault();
setIsInviting(true);
await handleInvite(user.user_id, user.display_name);
setIsInviting(false);
};

return <ServiceTable.Row>
<ServiceTable.Cell>{ user.display_name } ({ user.user_id })</ServiceTable.Cell>
<ServiceTable.Cell>
<button type="submit"
onClick={handleClick}
disabled={isInviting}
>{ isInviting ? <LoadingSpinnerInline /> || '✓' : t('Invite') }
</button>
</ServiceTable.Cell>

</ServiceTable.Row>;
};
export default UserListEntry;
Fixed Show fixed Hide fixed
170 changes: 170 additions & 0 deletions components/UI/InviteUsersToMatrixRoom/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/**
* This component renders a button whoch onClick opens a Modal.
* `activeContexts` is the array of room IDs for the currently set context spaces.
*
* @param {string} roomId (valid matrix roomId)
* @param {string} name (name of the matrix room)
*
* @return {React.ReactElement}
*
* @TODO
* - create seperate component for the invitation dialogue so it can be used without the button and maybe wthout the modal view.
* - maybe swap datalist for a different UI element. datalist handleing is far from optimal, since we have to manually get the userId and displayName after a user has selected the user to invite.
* Even though we already have it from the `matrixClient.searchUserDirectory` call. The problem is that afaik there is no way to parse the object from the <option>.
*
*/

import React, { useCallback, useEffect, useState } from 'react';
Fixed Show fixed Hide fixed
import { useTranslation } from 'react-i18next';
import _, { debounce } from 'lodash';
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
import Modal from 'react-modal';
import styled from 'styled-components';
Fixed Show fixed Hide fixed
import { logger } from 'matrix-js-sdk/lib/logger';

import TextButton from '../TextButton';
import UserAddIcon from '../../../assets/icons/user-add.svg';
import Form from '../Form';
import { useAuth } from '../../../lib/Auth';
import LoadingSpinnerInline from '../LoadingSpinnerInline';
Fixed Show fixed Hide fixed
import CloseIcon from '../../../assets/icons/close.svg';
import ErrorMessage from '../ErrorMessage';
import { ServiceTable } from '../ServiceTable';
import UserListEntry from './UserListEntry';

if (typeof window !== 'undefined') Modal.setAppElement(document.body);

const Header = styled.header`
display: grid;
grid-template-columns: 1fr auto;
margin-bottom: calc(var(--margin) * 2);

`;

const CloseButton = styled(TextButton)`
/* unset globally defined button styles; set height to line-height */
width: unset;
height: calc(var(--margin) * 1.3);
padding: unset;
background-color: unset;
border: unset;
`;

const SearchResults = styled.div`
height: 250px;
overflow-y: auto;
`;

export default function InviteUserToMatrixRoom({ roomId, name }) {
const auth = useAuth();
const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient();
const [isInviteDialogueOpen, setIsInviteDialogueOpen] = useState(false);
const [searchInput, setSearchInput] = useState('');
const [searchResults, setSearchResults] = useState([]);
const [userFeedback, setUserFeedback] = useState('');

const customModalStyles = {
content: {
top: '50%',
right: 'auto',
bottom: 'auto',
left: '50%',
minWidth: '60%',
padding: 'calc(var(--margin) * 2)',
marginRight: '-50%',
transform: 'translate(-50%, -50%)',
},
};

const { t } = useTranslation();

const handleClick = () => {
setIsInviteDialogueOpen(prevState => !prevState);
};

const handleChange = (event) => {
setSearchInput(event.target.value);
debouncedFetchUsersForContributorSearch(event.target.value);
};

// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedFetchUsersForContributorSearch = useCallback(debounce((val) => fetchUsersForContributorSearch(val), 300), []);
Dismissed Show dismissed Hide dismissed

const fetchUsersForContributorSearch = useCallback(async (a) => {
try {
const users = await matrixClient.searchUserDirectory({ term: a });
// we only update the state if the returned array has entries, to be able to check if users a matrix users or not further down in the code (otherwise the array gets set to [] as soon as you selected an option from the datalist)
users.results.length > 0 && setSearchResults(users.results);
} catch (err) {
logger.error('Error while trying to fetch users: ' + err);
}
}, [matrixClient]);
Fixed Show fixed Hide fixed

const handleInvite = async (userId, displayName) => {
function clearInputs() {
setUserFeedback('');
setSearchInput('');
}

await matrixClient.invite(roomId, userId)
.catch(async err => {
// if something went wrong we display the error and clear all inputs
setUserFeedback(<ErrorMessage>{ err.data?.error }</ErrorMessage>);
await new Promise(() => setTimeout(() => {
clearInputs();
}, 3000));

return;
});

// if everything is okay, we let the user know and exit the modal view.
setUserFeedback('✓ ' + displayName + t(' was invited and needs to accept your invitation'));
await new Promise(() => setTimeout(() => {
clearInputs();
setIsInviteDialogueOpen(false);
}, 3000));
};

return <>
<button title={t('Invite a user to ' + name)} onClick={handleClick}>
<UserAddIcon fill="var(--color-foreground)" />
</button>
{ isInviteDialogueOpen && (
<Modal
isOpen={isInviteDialogueOpen}
onRequestClose={() => setIsInviteDialogueOpen(false)}
contentLabel="Invite Users"
style={customModalStyles}
shouldCloseOnOverlayClick={true}>

<Header>
{ t('Invite users to') } { name } <CloseButton onClick={() => setIsInviteDialogueOpen(false)}>
<CloseIcon />
</CloseButton>
</Header>
{ userFeedback ? <div>{ userFeedback }</div> :
<Form>
<input
type="text"
list="userSearch"
placeholder={t('user name')}
value={searchInput}
onChange={handleChange}
autoComplete="off"
/>
<SearchResults>
<ServiceTable>
{ searchResults.map((user, i) => {
return <UserListEntry
user={user}
handleInvite={handleInvite}
key={i} />;
}) }
</ServiceTable>
</SearchResults>
</Form>
}
</Modal>
) }
</>;
}

42 changes: 38 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^12.2.2",
"react-modal": "^3.16.1",
"styled-components": "^5.3.3"
},
"devDependencies": {
Expand Down
2 changes: 2 additions & 0 deletions pages/etherpad/[[...roomId]].js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import CreateAnonymousPad from './actions/CreateAnonymousPad';
import AddExistingPad from './actions/AddExistingPad';
import CreateAuthoredPad from './actions/CreateAuthoredPad';
import CreatePasswordPad from './actions/CreatePasswordPad';
import InviteUserToMatrixRoom from '../../components/UI/InviteUsersToMatrixRoom';

export default function Etherpad() {
const auth = useAuth();
Expand Down Expand Up @@ -234,6 +235,7 @@ export default function Etherpad() {
<button title={t('Copy pad link to clipboard')} onClick={copyToClipboard}>
<ClipboardIcon fill="var(--color-foreground)" />
</button>
<InviteUserToMatrixRoom roomId={roomId} name={matrix.rooms.get(roomId).name} />
<button title={t(mypadsPadObject ? 'Delete pad' : 'Remove pad from my library')} onClick={deletePad}>
{ isDeletingPad ? <LoadingSpinnerInline /> : <BinIcon fill="var(--color-foreground)" /> }
</button>
Expand Down
Loading