From b4d4cefe23bb46343d938df69bace83d73bc7e61 Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Wed, 6 Oct 2021 13:48:30 +0530 Subject: [PATCH 01/25] Updated support link Signed-off-by: Ajay Bura --- src/app/organisms/settings/Settings.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/organisms/settings/Settings.jsx b/src/app/organisms/settings/Settings.jsx index 1917bd2f..de76f4c5 100644 --- a/src/app/organisms/settings/Settings.jsx +++ b/src/app/organisms/settings/Settings.jsx @@ -110,7 +110,7 @@ function AboutSection() {
- +
From 824a7f20953450605888e658ce89f8b441f17590 Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Mon, 11 Oct 2021 11:01:37 +0530 Subject: [PATCH 02/25] Fix make both user admin on DM create Signed-off-by: Ajay Bura --- src/client/action/room.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client/action/room.js b/src/client/action/room.js index 08e73e21..c887d247 100644 --- a/src/client/action/room.js +++ b/src/client/action/room.js @@ -143,6 +143,7 @@ async function create(opts) { is_direct: opts.isDirect === true, invite: opts.invite || [], initial_state: [], + preset: opts.isDirect === true ? 'trusted_private_chat' : undefined, }; if (opts.isPublic !== true && opts.isEncrypted === true) { From 7b67e4a6e6fd667d8c49ecac349fadecbf8c484b Mon Sep 17 00:00:00 2001 From: kfiven <33421343+kfiven@users.noreply.github.com> Date: Sun, 10 Oct 2021 19:33:20 +0530 Subject: [PATCH 03/25] Fix unable to send msg in DM from IRC users (#135) --- src/app/organisms/room/RoomViewInput.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/organisms/room/RoomViewInput.jsx b/src/app/organisms/room/RoomViewInput.jsx index 6c354eda..5ca4fdd3 100644 --- a/src/app/organisms/room/RoomViewInput.jsx +++ b/src/app/organisms/room/RoomViewInput.jsx @@ -327,8 +327,7 @@ function RoomViewInput({ if (file !== null) roomsInput.setAttachment(roomId, file); } - const myPowerlevel = roomTimeline.room.getMember(mx.getUserId()).powerLevel; - const canISend = roomTimeline.room.currentState.hasSufficientPowerLevelFor('events_default', myPowerlevel); + const canISend = roomTimeline.room.currentState.maySendMessage(mx.getUserId()); function renderInputs() { if (!canISend) { From 8a1b749667b2d2a25d297df53fb0e17d76c96ebf Mon Sep 17 00:00:00 2001 From: jamesjulich <51384945+jamesjulich@users.noreply.github.com> Date: Sun, 10 Oct 2021 16:36:44 -0500 Subject: [PATCH 04/25] Add support for SSO login. --- src/app/molecules/sso-buttons/SSOButtons.jsx | 98 +++++++++++++++++++ src/app/molecules/sso-buttons/SSOButtons.scss | 31 ++++++ src/app/templates/auth/Auth.jsx | 31 +++++- src/client/action/auth.js | 45 ++++++++- 4 files changed, 200 insertions(+), 5 deletions(-) create mode 100644 src/app/molecules/sso-buttons/SSOButtons.jsx create mode 100644 src/app/molecules/sso-buttons/SSOButtons.scss diff --git a/src/app/molecules/sso-buttons/SSOButtons.jsx b/src/app/molecules/sso-buttons/SSOButtons.jsx new file mode 100644 index 00000000..82e8cc4c --- /dev/null +++ b/src/app/molecules/sso-buttons/SSOButtons.jsx @@ -0,0 +1,98 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import './SSOButtons.scss'; + +import { createTemporaryClient, getLoginFlows, startSsoLogin } from '../../../client/action/auth'; + +function SSOButtons({ homeserver }) { + const [identityProviders, setIdentityProviders] = useState([]); + + useEffect(() => { + // If the homeserver passed in is not a fully-qualified domain name, do not update. + if (!homeserver.match('(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(? { + const providers = []; + getLoginFlows(client).then((flows) => { + if (flows.flows !== undefined) { + const ssoFlows = flows.flows.filter((flow) => flow.type === 'm.login.sso' || flow.type === 'm.login.cas'); + ssoFlows.forEach((flow) => { + if (flow.identity_providers !== undefined) { + const type = flow.type.substring(8); + flow.identity_providers.forEach((idp) => { + const imageSrc = client.mxcUrlToHttp(idp.icon); + providers.push({ + homeserver, id: idp.id, name: idp.name, type, imageSrc, + }); + }); + } + }); + } + setIdentityProviders(providers); + }).catch(() => {}); + }).catch(() => { + setIdentityProviders([]); + }); + }, [homeserver]); + + // TODO Render all non-icon providers at the end so that they are never inbetween icons. + return ( +
+ {identityProviders.map((idp) => { + if (idp.imageSrc == null || idp.imageSrc === undefined || idp.imageSrc === '') { + return ( + + ); + } + return ( + + ); + })} +
+ ); +} + +function SSOButton({ + homeserver, id, name, type, imageSrc, +}) { + function handleClick() { + startSsoLogin(homeserver, type, id); + } + return ( + + ); +} + +SSOButtons.propTypes = { + homeserver: PropTypes.string.isRequired, +}; + +SSOButton.propTypes = { + homeserver: PropTypes.string.isRequired, + id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + type: PropTypes.string.isRequired, + imageSrc: PropTypes.string.isRequired, +}; + +export default SSOButtons; diff --git a/src/app/molecules/sso-buttons/SSOButtons.scss b/src/app/molecules/sso-buttons/SSOButtons.scss new file mode 100644 index 00000000..61d48bc5 --- /dev/null +++ b/src/app/molecules/sso-buttons/SSOButtons.scss @@ -0,0 +1,31 @@ +.sso-buttons { + margin-top: var(--sp-extra-loose); + + display: flex; + justify-content: center; + flex-wrap: wrap; + + &__fallback-text { + margin: var(--sp-tight) 0px; + + flex-basis: 100%; + text-align: center; + + color: var(--bg-primary); + cursor: pointer; + } +} + +.sso-button { + margin-bottom: var(--sp-normal); + + display: flex; + justify-content: center; + flex-basis: 20%; + + cursor: pointer; + + &__img { + height: var(--av-normal); + } +} \ No newline at end of file diff --git a/src/app/templates/auth/Auth.jsx b/src/app/templates/auth/Auth.jsx index 3d97ca51..3eaf16a8 100644 --- a/src/app/templates/auth/Auth.jsx +++ b/src/app/templates/auth/Auth.jsx @@ -3,8 +3,9 @@ import PropTypes from 'prop-types'; import './Auth.scss'; import ReCAPTCHA from 'react-google-recaptcha'; -import { Link } from 'react-router-dom'; +import { Link, useLocation } from 'react-router-dom'; import * as auth from '../../../client/action/auth'; +import cons from '../../../client/state/cons'; import Text from '../../atoms/text/Text'; import Button from '../../atoms/button/Button'; @@ -15,6 +16,7 @@ import ScrollView from '../../atoms/scroll/ScrollView'; import EyeIC from '../../../../public/res/ic/outlined/eye.svg'; import CinnySvg from '../../../../public/res/svg/cinny.svg'; +import SSOButtons from '../../molecules/sso-buttons/SSOButtons'; // This regex validates historical usernames, which don't satisfy today's username requirements. // See https://matrix.org/docs/spec/appendices#id13 for more info. @@ -75,12 +77,35 @@ function normalizeUsername(rawUsername) { function Auth({ type }) { const [process, changeProcess] = useState(null); + const [homeserver, changeHomeserver] = useState('matrix.org'); + const usernameRef = useRef(null); const homeserverRef = useRef(null); const passwordRef = useRef(null); const confirmPasswordRef = useRef(null); const emailRef = useRef(null); + const { search } = useLocation(); + const searchParams = new URLSearchParams(search); + if (searchParams.has('loginToken')) { + const loginToken = searchParams.get('loginToken'); + if (loginToken !== undefined) { + if (localStorage.getItem(cons.secretKey.BASE_URL) !== undefined) { + const baseUrl = localStorage.getItem(cons.secretKey.BASE_URL); + auth.loginWithToken(baseUrl, loginToken) + .then(() => { + window.location.replace('/'); + }) + .catch((error) => { + changeProcess(null); + if (!error.contains('CORS request rejected')) { + renderErrorMessage(error); + } + }); + } + } + } + function register(recaptchaValue, terms, verified) { auth.register( usernameRef.current.value, @@ -205,6 +230,7 @@ function Auth({ type }) { /> changeHomeserver(e.target.value)} id="auth_homeserver" placeholder="Homeserver" value="matrix.org" @@ -281,6 +307,9 @@ function Auth({ type }) { {type === 'login' ? 'Login' : 'Register' } + {type === 'login' && ( + + )} diff --git a/src/client/action/auth.js b/src/client/action/auth.js index 6c77aa81..47fe2ba2 100644 --- a/src/client/action/auth.js +++ b/src/client/action/auth.js @@ -2,7 +2,8 @@ import * as sdk from 'matrix-js-sdk'; import cons from '../state/cons'; import { getBaseUrl } from '../../util/matrixUtil'; -async function login(username, homeserver, password) { +// This method inspired by a similar one in matrix-react-sdk +async function createTemporaryClient(homeserver) { let baseUrl = null; try { baseUrl = await getBaseUrl(homeserver); @@ -12,7 +13,25 @@ async function login(username, homeserver, password) { if (typeof baseUrl === 'undefined') throw new Error('Homeserver not found'); - const client = sdk.createClient({ baseUrl }); + return sdk.createClient({ baseUrl }); +} + +async function getLoginFlows(client) { + const flows = await client.loginFlows(); + if (flows !== undefined) { + return flows; + } + return null; +} + +async function startSsoLogin(homeserver, type, idpId) { + const client = await createTemporaryClient(homeserver); + localStorage.setItem(cons.secretKey.BASE_URL, client.baseUrl); + window.location.href = client.getSsoLoginUrl(window.location.href, type, idpId); +} + +async function login(username, homeserver, password) { + const client = await createTemporaryClient(homeserver); const response = await client.login('m.login.password', { identifier: { @@ -26,7 +45,21 @@ async function login(username, homeserver, password) { localStorage.setItem(cons.secretKey.ACCESS_TOKEN, response.access_token); localStorage.setItem(cons.secretKey.DEVICE_ID, response.device_id); localStorage.setItem(cons.secretKey.USER_ID, response.user_id); - localStorage.setItem(cons.secretKey.BASE_URL, response?.well_known?.['m.homeserver']?.base_url || baseUrl); + localStorage.setItem(cons.secretKey.BASE_URL, response?.well_known?.['m.homeserver']?.base_url || client.baseUrl); +} + +async function loginWithToken(baseUrl, token) { + const client = sdk.createClient(baseUrl); + + const response = await client.login('m.login.token', { + token, + initial_device_display_name: cons.DEVICE_DISPLAY_NAME, + }); + + localStorage.setItem(cons.secretKey.ACCESS_TOKEN, response.access_token); + localStorage.setItem(cons.secretKey.DEVICE_ID, response.device_id); + localStorage.setItem(cons.secretKey.USER_ID, response.user_id); + localStorage.setItem(cons.secretKey.BASE_URL, response?.well_known?.['m.homeserver']?.base_url || client.baseUrl); } async function getAdditionalInfo(baseUrl, content) { @@ -45,6 +78,7 @@ async function getAdditionalInfo(baseUrl, content) { throw new Error(e); } } + async function verifyEmail(baseUrl, content) { try { const res = await fetch(`${baseUrl}/_matrix/client/r0/register/email/requestToken`, { @@ -149,4 +183,7 @@ async function register(username, homeserver, password, email, recaptchaValue, t return {}; } -export { login, register }; +export { + createTemporaryClient, getLoginFlows, login, + loginWithToken, register, startSsoLogin, +}; From 9fad8a1098cad917332b670f1d453115f955d8ff Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Mon, 11 Oct 2021 15:22:15 +0530 Subject: [PATCH 05/25] added action for pull request previews --- .github/workflows/pull-request.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/pull-request.yml diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 00000000..96dbc729 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,30 @@ +name: 'Netlify Preview Deploy' + +on: + pull_request: + types: ['opened', 'synchronize'] + +jobs: + deploy: + name: "Deploy to Netlify" + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: jsmrcaga/action-netlify-deploy@master + with: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE3_ID }} + BUILD_DIRECTORY: "dist" + - name: Deploy to Netlify + uses: nwtgck/actions-netlify@v1.1 + with: + publish-dir: 'dist' + github-token: ${{ secrets.GITHUB_TOKEN }} + deploy-message: "Deploy from GitHub Actions" + enable-pull-request-comment: true + enable-commit-comment: false + overwrites-pull-request-comment: true + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE3_ID }} From d067fe776df75b9dd2d659ad9da1473e35a81949 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Tue, 12 Oct 2021 15:00:09 +0530 Subject: [PATCH 06/25] Update pull-request.yml --- .github/workflows/pull-request.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 96dbc729..8e547967 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -13,18 +13,18 @@ jobs: - uses: actions/checkout@v1 - uses: jsmrcaga/action-netlify-deploy@master with: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH2_TOKEN }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE3_ID }} BUILD_DIRECTORY: "dist" - name: Deploy to Netlify uses: nwtgck/actions-netlify@v1.1 with: - publish-dir: 'dist' + publish-dir: "dist" github-token: ${{ secrets.GITHUB_TOKEN }} deploy-message: "Deploy from GitHub Actions" enable-pull-request-comment: true enable-commit-comment: false overwrites-pull-request-comment: true env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH2_TOKEN }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE3_ID }} From 8b4beccf82a5ad278d6b3f970970927e0c603b07 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Thu, 14 Oct 2021 10:28:31 +0530 Subject: [PATCH 07/25] Fixed deploy on PR --- .github/workflows/pull-request.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 8e547967..ad510fee 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -1,7 +1,7 @@ name: 'Netlify Preview Deploy' on: - pull_request: + pull_request_target: types: ['opened', 'synchronize'] jobs: @@ -13,10 +13,10 @@ jobs: - uses: actions/checkout@v1 - uses: jsmrcaga/action-netlify-deploy@master with: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH2_TOKEN }} + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE3_ID }} BUILD_DIRECTORY: "dist" - - name: Deploy to Netlify + - name: Post on PR uses: nwtgck/actions-netlify@v1.1 with: publish-dir: "dist" @@ -26,5 +26,5 @@ jobs: enable-commit-comment: false overwrites-pull-request-comment: true env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH2_TOKEN }} + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE3_ID }} From a34c35fbe3dc69789d7119b2bd22bce8aa866758 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Thu, 14 Oct 2021 10:34:04 +0530 Subject: [PATCH 08/25] Update pull-request.yml --- .github/workflows/pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index ad510fee..e8f73f5b 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -2,7 +2,7 @@ name: 'Netlify Preview Deploy' on: pull_request_target: - types: ['opened', 'synchronize'] + types: ['opened', 'edited', 'synchronize'] jobs: deploy: From 4dfe40333eabd2c670ed4f49f41244f6746030f3 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Thu, 14 Oct 2021 10:42:07 +0530 Subject: [PATCH 09/25] Update pull-request.yml --- .github/workflows/pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index e8f73f5b..ad510fee 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -2,7 +2,7 @@ name: 'Netlify Preview Deploy' on: pull_request_target: - types: ['opened', 'edited', 'synchronize'] + types: ['opened', 'synchronize'] jobs: deploy: From 825db633a053d4cbfbf2cb078fb06cdcd6cb870f Mon Sep 17 00:00:00 2001 From: Gero Gerke Date: Mon, 18 Oct 2021 17:25:52 +0200 Subject: [PATCH 10/25] Implement Profile Viewer (#130) * Implement Profile Viewer Fixes #111 * Make user avatar in chat clickable * design progress * Refactored code * progress * Updated chip comp Signed-off-by: Ajay Bura * Refactored ProfileViewer comp Signed-off-by: Ajay Bura * Added msg functionality in ProfileViewer Signed-off-by: Ajay Bura * Added Ignore functionality in ProfileViewer Signed-off-by: Ajay Bura * Fixed Ignore btn bug Signed-off-by: Ajay Bura * Refectored ProfileViewer comp Signed-off-by: Ajay Bura Co-authored-by: Ajay Bura --- .gitignore | 4 +- public/res/ic/outlined/shield-empty.svg | 7 + src/app/atoms/avatar/Avatar.scss | 1 - src/app/atoms/chip/Chip.jsx | 11 +- src/app/atoms/chip/Chip.scss | 20 +- src/app/molecules/message/Message.scss | 7 +- src/app/molecules/room-intro/RoomIntro.jsx | 2 +- .../profile-viewer/ProfileViewer.jsx | 255 ++++++++++++++++++ .../profile-viewer/ProfileViewer.scss | 89 ++++++ src/app/organisms/pw/Dialogs.jsx | 2 + .../organisms/read-receipts/ReadReceipts.jsx | 6 +- src/app/organisms/room/PeopleDrawer.jsx | 13 +- src/app/organisms/room/RoomViewContent.jsx | 16 +- src/client/action/navigation.js | 9 + src/client/state/cons.js | 2 + src/client/state/navigation.js | 3 + src/util/matrixUtil.js | 10 +- 17 files changed, 425 insertions(+), 32 deletions(-) create mode 100644 public/res/ic/outlined/shield-empty.svg create mode 100644 src/app/organisms/profile-viewer/ProfileViewer.jsx create mode 100644 src/app/organisms/profile-viewer/ProfileViewer.scss diff --git a/.gitignore b/.gitignore index 6fb5a7fe..397d2434 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ experiment dist node_modules -devAssets \ No newline at end of file +devAssets + +.DS_Store diff --git a/public/res/ic/outlined/shield-empty.svg b/public/res/ic/outlined/shield-empty.svg new file mode 100644 index 00000000..6bc9d304 --- /dev/null +++ b/public/res/ic/outlined/shield-empty.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/app/atoms/avatar/Avatar.scss b/src/app/atoms/avatar/Avatar.scss index d7ddc6e8..42efbe79 100644 --- a/src/app/atoms/avatar/Avatar.scss +++ b/src/app/atoms/avatar/Avatar.scss @@ -24,7 +24,6 @@ height: var(--av-extra-small); } - img { width: 100%; height: 100%; diff --git a/src/app/atoms/chip/Chip.jsx b/src/app/atoms/chip/Chip.jsx index 3cededff..b21cf9ed 100644 --- a/src/app/atoms/chip/Chip.jsx +++ b/src/app/atoms/chip/Chip.jsx @@ -7,13 +7,14 @@ import RawIcon from '../system-icons/RawIcon'; function Chip({ iconSrc, iconColor, text, children, + onClick, }) { return ( -
- {iconSrc != null && } - {(text != null && text !== '') && {text}} +
+ ); } @@ -22,6 +23,7 @@ Chip.propTypes = { iconColor: PropTypes.string, text: PropTypes.string, children: PropTypes.element, + onClick: PropTypes.func, }; Chip.defaultProps = { @@ -29,6 +31,7 @@ Chip.defaultProps = { iconColor: null, text: null, children: null, + onClick: null, }; export default Chip; diff --git a/src/app/atoms/chip/Chip.scss b/src/app/atoms/chip/Chip.scss index c53f5ba7..8318fcf6 100644 --- a/src/app/atoms/chip/Chip.scss +++ b/src/app/atoms/chip/Chip.scss @@ -7,13 +7,27 @@ background: var(--bg-surface-low); border-radius: var(--bo-radius); - border: 1px solid var(--bg-surface-border); + box-shadow: var(--bs-surface-border); + cursor: pointer; + + @media (hover: hover) { + &:hover { + background-color: var(--bg-surface-hover); + } + } + + & > .text { + flex: 1; + color: var(--tc-surface-high); + } & > .ic-raw { - margin-right: var(--sp-extra-tight); + width: 16px; + height: 16px; + margin-right: var(--sp-ultra-tight); [dir=rtl] & { margin-right: 0; - margin-left: var(--sp-extra-tight); + margin-left: var(--sp-ultra-tight); } } } \ No newline at end of file diff --git a/src/app/molecules/message/Message.scss b/src/app/molecules/message/Message.scss index 60886ca9..2b757b08 100644 --- a/src/app/molecules/message/Message.scss +++ b/src/app/molecules/message/Message.scss @@ -22,11 +22,12 @@ &__avatar-container { padding-top: 6px; - } - - &__avatar-container{ margin-right: var(--sp-tight); + & button { + cursor: pointer; + } + [dir=rtl] & { margin: { left: var(--sp-tight); diff --git a/src/app/molecules/room-intro/RoomIntro.jsx b/src/app/molecules/room-intro/RoomIntro.jsx index df5618dc..5c437d79 100644 --- a/src/app/molecules/room-intro/RoomIntro.jsx +++ b/src/app/molecules/room-intro/RoomIntro.jsx @@ -28,7 +28,7 @@ function RoomIntro({ } RoomIntro.defaultProps = { - avatarSrc: false, + avatarSrc: null, time: null, }; diff --git a/src/app/organisms/profile-viewer/ProfileViewer.jsx b/src/app/organisms/profile-viewer/ProfileViewer.jsx new file mode 100644 index 00000000..17405e92 --- /dev/null +++ b/src/app/organisms/profile-viewer/ProfileViewer.jsx @@ -0,0 +1,255 @@ +import React, { useState, useEffect, useRef } from 'react'; +import PropTypes from 'prop-types'; +import './ProfileViewer.scss'; + +import initMatrix from '../../../client/initMatrix'; +import cons from '../../../client/state/cons'; +import navigation from '../../../client/state/navigation'; +import { selectRoom } from '../../../client/action/navigation'; +import * as roomActions from '../../../client/action/room'; + +import { getUsername, getUsernameOfRoomMember, getPowerLabel } from '../../../util/matrixUtil'; +import colorMXID from '../../../util/colorMXID'; + +import Text from '../../atoms/text/Text'; +import Chip from '../../atoms/chip/Chip'; +import IconButton from '../../atoms/button/IconButton'; +import Avatar from '../../atoms/avatar/Avatar'; +import Button from '../../atoms/button/Button'; +import Dialog from '../../molecules/dialog/Dialog'; +import SettingTile from '../../molecules/setting-tile/SettingTile'; + +import ShieldEmptyIC from '../../../../public/res/ic/outlined/shield-empty.svg'; +import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg'; +import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; + +function SessionInfo({ userId }) { + const [devices, setDevices] = useState(null); + const mx = initMatrix.matrixClient; + + useEffect(() => { + let isUnmounted = false; + + async function loadDevices() { + try { + await mx.downloadKeys([userId], true); + const myDevices = mx.getStoredDevicesForUser(userId); + + if (isUnmounted) return; + setDevices(myDevices); + } catch { + setDevices([]); + } + } + loadDevices(); + + return () => { + isUnmounted = true; + }; + }, [userId]); + + function renderSessionChips() { + return ( +
+ {devices === null && Loading sessions...} + {devices?.length === 0 && No session found.} + {devices !== null && (devices.map((device) => ( + + )))} +
+ ); + } + + return ( +
+ +
+ ); +} + +SessionInfo.propTypes = { + userId: PropTypes.string.isRequired, +}; + +function ProfileFooter({ userId, onRequestClose }) { + const [isCreatingDM, setIsCreatingDM] = useState(false); + const [isIgnoring, setIsIgnoring] = useState(false); + const [isUserIgnored, setIsUserIgnored] = useState(initMatrix.matrixClient.isUserIgnored(userId)); + + const mx = initMatrix.matrixClient; + const isMountedRef = useRef(true); + + useEffect(() => () => { + isMountedRef.current = false; + }, []); + useEffect(() => { + setIsUserIgnored(initMatrix.matrixClient.isUserIgnored(userId)); + }, [userId]); + + async function openDM() { + const directIds = [...initMatrix.roomList.directs]; + + // Check and open if user already have a DM with userId. + for (let i = 0; i < directIds.length; i += 1) { + const dRoom = mx.getRoom(directIds[i]); + const roomMembers = dRoom.getMembers(); + if (roomMembers.length <= 2 && dRoom.currentState.members[userId]) { + selectRoom(directIds[i]); + onRequestClose(); + return; + } + } + + // Create new DM + try { + setIsCreatingDM(true); + const result = await roomActions.create({ + isEncrypted: true, + isDirect: true, + invite: [userId], + }); + + if (isMountedRef.current === false) return; + setIsCreatingDM(false); + selectRoom(result.room_id); + onRequestClose(); + } catch { + setIsCreatingDM(false); + } + } + + async function toggleIgnore() { + const ignoredUsers = mx.getIgnoredUsers(); + const uIndex = ignoredUsers.indexOf(userId); + if (uIndex >= 0) { + if (uIndex === -1) return; + ignoredUsers.splice(uIndex, 1); + } else ignoredUsers.push(userId); + + try { + setIsIgnoring(true); + await mx.setIgnoredUsers(ignoredUsers); + + if (isMountedRef.current === false) return; + setIsUserIgnored(uIndex < 0); + setIsIgnoring(false); + } catch { + setIsIgnoring(false); + } + } + return ( +
+ + + +
+ ); +} +ProfileFooter.propTypes = { + userId: PropTypes.string.isRequired, + onRequestClose: PropTypes.func.isRequired, +}; + +function ProfileViewer() { + const [isOpen, setIsOpen] = useState(false); + const [roomId, setRoomId] = useState(null); + const [userId, setUserId] = useState(null); + + const mx = initMatrix.matrixClient; + const room = roomId ? mx.getRoom(roomId) : null; + let username = ''; + if (room !== null) { + const roomMember = room.getMember(userId); + if (roomMember) username = getUsernameOfRoomMember(roomMember); + else username = getUsername(userId); + } + + function loadProfile(uId, rId) { + setIsOpen(true); + setUserId(uId); + setRoomId(rId); + } + + useEffect(() => { + navigation.on(cons.events.navigation.PROFILE_VIEWER_OPENED, loadProfile); + return () => { + navigation.removeListener(cons.events.navigation.PROFILE_VIEWER_OPENED, loadProfile); + }; + }, []); + + useEffect(() => { + if (isOpen) return; + setUserId(null); + setRoomId(null); + }, [isOpen]); + + function renderProfile() { + const member = room.getMember(userId) || mx.getUser(userId); + const avatarMxc = member.getMxcAvatarUrl() || member.avatarUrl; + + return ( +
+
+ +
+ {username} + {userId} +
+
+ Role + +
+
+ + { userId !== mx.getUserId() && ( + setIsOpen(false)} + /> + )} +
+ ); + } + + return ( + setIsOpen(false)} + contentOptions={ setIsOpen(false)} tooltip="Close" />} + > + {isOpen && renderProfile()} + + ); +} + +export default ProfileViewer; diff --git a/src/app/organisms/profile-viewer/ProfileViewer.scss b/src/app/organisms/profile-viewer/ProfileViewer.scss new file mode 100644 index 00000000..b10b1aa4 --- /dev/null +++ b/src/app/organisms/profile-viewer/ProfileViewer.scss @@ -0,0 +1,89 @@ +.profile-viewer__dialog { + & .dialog__content__wrapper { + position: relative; + } + & .dialog__content-container { + padding: var(--sp-normal); + padding-bottom: 89px; + padding-right: var(--sp-extra-tight); + [dir=rtl] & { + padding-right: var(--sp-normal); + padding-left: var(--sp-extra-tight); + } + } +} + +.profile-viewer { + &__user { + display: flex; + padding-bottom: var(--sp-normal); + border-bottom: 1px solid var(--bg-surface-border); + + &__info { + align-self: end; + flex: 1; + min-width: 0; + + margin: 0 var(--sp-normal); + + & .text-s1 { + font-weight: 500; + } + & .text { + white-space: pre-wrap; + word-break: break-word; + } + } + &__role { + align-self: end; + & > .text { + margin-bottom: var(--sp-ultra-tight); + } + } + } + + & .session-info { + margin-top: var(--sp-normal); + } + + &__buttons { + position: absolute; + left: 0; + bottom: 0; + + width: 100%; + padding: var(--sp-normal); + background-color: var(--bg-surface); + border-top: 1px solid var(--bg-surface-border); + display: flex; + + & > *:nth-child(2n) { + margin: 0 var(--sp-normal) + } + & > *:last-child { + margin-left: auto; + [dir=rtl] & { + margin-left: 0; + margin-right: auto; + } + } + } +} + +.session-info { + & .setting-tile__title .text { + color: var(--tc-surface-high); + } + &__chips { + padding-top: var(--sp-ultra-tight); + & .chip { + margin: { + top: var(--sp-extra-tight); + right: var(--sp-extra-tight); + } + [dir=rtl] & { + margin: 0 0 var(--sp-extra-tight) var(--sp-extra-tight); + } + } + } +} \ No newline at end of file diff --git a/src/app/organisms/pw/Dialogs.jsx b/src/app/organisms/pw/Dialogs.jsx index d878ed9c..3448ebe9 100644 --- a/src/app/organisms/pw/Dialogs.jsx +++ b/src/app/organisms/pw/Dialogs.jsx @@ -1,11 +1,13 @@ import React from 'react'; import ReadReceipts from '../read-receipts/ReadReceipts'; +import ProfileViewer from '../profile-viewer/ProfileViewer'; function Dialogs() { return ( <> + ); } diff --git a/src/app/organisms/read-receipts/ReadReceipts.jsx b/src/app/organisms/read-receipts/ReadReceipts.jsx index ea6ae286..689a045b 100644 --- a/src/app/organisms/read-receipts/ReadReceipts.jsx +++ b/src/app/organisms/read-receipts/ReadReceipts.jsx @@ -11,6 +11,7 @@ import PeopleSelector from '../../molecules/people-selector/PeopleSelector'; import Dialog from '../../molecules/dialog/Dialog'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; +import { openProfileViewer } from '../../../client/action/navigation'; function ReadReceipts() { const [isOpen, setIsOpen] = useState(false); @@ -58,7 +59,10 @@ function ReadReceipts() { return ( alert('Viewing profile is yet to be implemented')} + onClick={() => { + setIsOpen(false); + openProfileViewer(receipt.userId, roomId); + }} avatarSrc={member?.getAvatarUrl(initMatrix.matrixClient.baseUrl, 24, 24, 'crop')} name={getUserDisplayName(receipt.userId)} color={colorMXID(receipt.userId)} diff --git a/src/app/organisms/room/PeopleDrawer.jsx b/src/app/organisms/room/PeopleDrawer.jsx index ca975d13..205047dc 100644 --- a/src/app/organisms/room/PeopleDrawer.jsx +++ b/src/app/organisms/room/PeopleDrawer.jsx @@ -3,9 +3,9 @@ import PropTypes from 'prop-types'; import './PeopleDrawer.scss'; import initMatrix from '../../../client/initMatrix'; -import { getUsernameOfRoomMember } from '../../../util/matrixUtil'; +import { getPowerLabel, getUsernameOfRoomMember } from '../../../util/matrixUtil'; import colorMXID from '../../../util/colorMXID'; -import { openInviteUser } from '../../../client/action/navigation'; +import { openInviteUser, openProfileViewer } from '../../../client/action/navigation'; import Text from '../../atoms/text/Text'; import Header, { TitleWrapper } from '../../atoms/header/Header'; @@ -17,13 +17,6 @@ import PeopleSelector from '../../molecules/people-selector/PeopleSelector'; import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg'; -function getPowerLabel(powerLevel) { - if (powerLevel > 9000) return 'Goku'; - if (powerLevel > 100) return 'Founder'; - if (powerLevel === 100) return 'Admin'; - if (powerLevel >= 50) return 'Mod'; - return null; -} function AtoZ(m1, m2) { const aName = m1.name; const bName = m2.name; @@ -88,7 +81,7 @@ function PeopleDrawer({ roomId }) { memberList.map((member) => ( alert('Viewing profile is yet to be implemented')} + onClick={() => openProfileViewer(member.userId, roomId)} avatarSrc={member.getAvatarUrl(initMatrix.matrixClient.baseUrl, 24, 24, 'crop')} name={getUsernameOfRoomMember(member)} color={colorMXID(member.userId)} diff --git a/src/app/organisms/room/RoomViewContent.jsx b/src/app/organisms/room/RoomViewContent.jsx index efa8318c..170d25d1 100644 --- a/src/app/organisms/room/RoomViewContent.jsx +++ b/src/app/organisms/room/RoomViewContent.jsx @@ -11,7 +11,7 @@ import { redactEvent, sendReaction } from '../../../client/action/roomTimeline'; import { getUsername, getUsernameOfRoomMember, doesRoomHaveUnread } from '../../../util/matrixUtil'; import colorMXID from '../../../util/colorMXID'; import { diffMinutes, isNotInSameDay, getEventCords } from '../../../util/common'; -import { openEmojiBoard, openReadReceipts } from '../../../client/action/navigation'; +import { openEmojiBoard, openProfileViewer, openReadReceipts } from '../../../client/action/navigation'; import Divider from '../../atoms/divider/Divider'; import Avatar from '../../atoms/avatar/Avatar'; @@ -353,12 +353,14 @@ function RoomViewContent({ const senderMXIDColor = colorMXID(mEvent.sender.userId); const userAvatar = isContentOnly ? null : ( - + ); const userHeader = isContentOnly ? null : ( { this.emit(cons.events.navigation.INVITE_USER_OPENED, action.roomId, action.searchTerm); }, + [cons.actions.navigation.OPEN_PROFILE_VIEWER]: () => { + this.emit(cons.events.navigation.PROFILE_VIEWER_OPENED, action.userId, action.roomId); + }, [cons.actions.navigation.OPEN_SETTINGS]: () => { this.emit(cons.events.navigation.SETTINGS_OPENED); }, diff --git a/src/util/matrixUtil.js b/src/util/matrixUtil.js index 1dbd5fb1..e40fa73c 100644 --- a/src/util/matrixUtil.js +++ b/src/util/matrixUtil.js @@ -69,7 +69,15 @@ function doesRoomHaveUnread(room) { return true; } +function getPowerLabel(powerLevel) { + if (powerLevel > 9000) return 'Goku'; + if (powerLevel > 100) return 'Founder'; + if (powerLevel === 100) return 'Admin'; + if (powerLevel >= 50) return 'Mod'; + return null; +} + export { getBaseUrl, getUsername, getUsernameOfRoomMember, - isRoomAliasAvailable, doesRoomHaveUnread, + isRoomAliasAvailable, doesRoomHaveUnread, getPowerLabel, }; From 012e933e43c83792d5602f93e0ff47aa6b359813 Mon Sep 17 00:00:00 2001 From: James Julich <51384945+jamesjulich@users.noreply.github.com> Date: Tue, 19 Oct 2021 07:39:30 -0500 Subject: [PATCH 11/25] Address 301 redirect issue and Safari regex issue. (#143) * Address 301 redirect issue and Safari regex issue. * Restored login redirect as this doesn't not fix the sso redirect #143. Co-authored-by: Ajay Bura <32841439+ajbura@users.noreply.github.com> --- src/app/molecules/sso-buttons/SSOButtons.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/molecules/sso-buttons/SSOButtons.jsx b/src/app/molecules/sso-buttons/SSOButtons.jsx index 82e8cc4c..76f865a1 100644 --- a/src/app/molecules/sso-buttons/SSOButtons.jsx +++ b/src/app/molecules/sso-buttons/SSOButtons.jsx @@ -9,7 +9,7 @@ function SSOButtons({ homeserver }) { useEffect(() => { // If the homeserver passed in is not a fully-qualified domain name, do not update. - if (!homeserver.match('(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(? Date: Tue, 19 Oct 2021 19:38:09 +0530 Subject: [PATCH 12/25] Fix redirect on SSO login (#142), #27, #94 Signed-off-by: Ajay Bura --- src/app/pages/App.jsx | 14 ++------------ src/app/templates/auth/Auth.jsx | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/app/pages/App.jsx b/src/app/pages/App.jsx index bb265056..ad73e989 100644 --- a/src/app/pages/App.jsx +++ b/src/app/pages/App.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { - BrowserRouter, Switch, Route, Redirect, + BrowserRouter, } from 'react-router-dom'; import { isAuthenticated } from '../../client/state/auth'; @@ -11,17 +11,7 @@ import Client from '../templates/client/Client'; function App() { return ( - - - { isAuthenticated() ? : } - - - { isAuthenticated() ? : } - - - { isAuthenticated() ? : } - - + { isAuthenticated() ? : } ); } diff --git a/src/app/templates/auth/Auth.jsx b/src/app/templates/auth/Auth.jsx index 3eaf16a8..f2d2b1eb 100644 --- a/src/app/templates/auth/Auth.jsx +++ b/src/app/templates/auth/Auth.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import './Auth.scss'; import ReCAPTCHA from 'react-google-recaptcha'; -import { Link, useLocation } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import * as auth from '../../../client/action/auth'; import cons from '../../../client/state/cons'; @@ -75,7 +75,8 @@ function normalizeUsername(rawUsername) { return noLeadingAt.trim(); } -function Auth({ type }) { +function Auth() { + const [type, setType] = useState('login'); const [process, changeProcess] = useState(null); const [homeserver, changeHomeserver] = useState('matrix.org'); @@ -316,9 +317,16 @@ function Auth({ type }) {
{`${(type === 'login' ? 'Don\'t have' : 'Already have')} an account?`} - +
@@ -326,10 +334,6 @@ function Auth({ type }) { ); } -Auth.propTypes = { - type: PropTypes.string.isRequired, -}; - function StaticWrapper({ children }) { return ( From 5f6667debf9607b9abb8ea15158b3ba8f5cc41f0 Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Tue, 19 Oct 2021 20:23:15 +0530 Subject: [PATCH 13/25] SSO login bug fixed Signed-off-by: Ajay Bura --- src/app/templates/auth/Auth.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app/templates/auth/Auth.jsx b/src/app/templates/auth/Auth.jsx index f2d2b1eb..6586e588 100644 --- a/src/app/templates/auth/Auth.jsx +++ b/src/app/templates/auth/Auth.jsx @@ -95,7 +95,8 @@ function Auth() { const baseUrl = localStorage.getItem(cons.secretKey.BASE_URL); auth.loginWithToken(baseUrl, loginToken) .then(() => { - window.location.replace('/'); + const { href } = window.location; + window.location.replace(href.slice(0, href.indexOf('?'))); }) .catch((error) => { changeProcess(null); @@ -314,7 +315,7 @@ function Auth() { -
+
{`${(type === 'login' ? 'Don\'t have' : 'Already have')} an account?`}
From 5018df11f15e88ac34ff060a3680665ec41a28f8 Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Thu, 21 Oct 2021 17:50:49 +0530 Subject: [PATCH 14/25] Add search in People drawer Signed-off-by: Ajay Bura --- src/app/organisms/room/PeopleDrawer.jsx | 86 ++++++++++++++++++++---- src/app/organisms/room/PeopleDrawer.scss | 9 ++- 2 files changed, 76 insertions(+), 19 deletions(-) diff --git a/src/app/organisms/room/PeopleDrawer.jsx b/src/app/organisms/room/PeopleDrawer.jsx index 205047dc..dcddc76d 100644 --- a/src/app/organisms/room/PeopleDrawer.jsx +++ b/src/app/organisms/room/PeopleDrawer.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import PropTypes from 'prop-types'; import './PeopleDrawer.scss'; @@ -6,6 +6,7 @@ import initMatrix from '../../../client/initMatrix'; import { getPowerLabel, getUsernameOfRoomMember } from '../../../util/matrixUtil'; import colorMXID from '../../../util/colorMXID'; import { openInviteUser, openProfileViewer } from '../../../client/action/navigation'; +import AsyncSearch from '../../../util/AsyncSearch'; import Text from '../../atoms/text/Text'; import Header, { TitleWrapper } from '../../atoms/header/Header'; @@ -37,31 +38,88 @@ function sortByPowerLevel(m1, m2) { if (pl1 < pl2) return 1; return 0; } +function simplyfiMembers(members) { + const mx = initMatrix.matrixClient; + return members.map((member) => ({ + userId: member.userId, + name: getUsernameOfRoomMember(member), + username: member.userId.slice(1, member.userId.indexOf(':')), + avatarSrc: member.getAvatarUrl(mx.baseUrl, 24, 24, 'crop'), + peopleRole: getPowerLabel(member.powerLevel), + powerLevel: members.powerLevel, + })); +} +const asyncSearch = new AsyncSearch(); function PeopleDrawer({ roomId }) { const PER_PAGE_MEMBER = 50; - const room = initMatrix.matrixClient.getRoom(roomId); - const totalMemberList = room.getJoinedMembers().sort(AtoZ).sort(sortByPowerLevel); - const [memberList, updateMemberList] = useState([]); + const mx = initMatrix.matrixClient; + const room = mx.getRoom(roomId); let isRoomChanged = false; + const [itemCount, setItemCount] = useState(PER_PAGE_MEMBER); + const [membership, setMembership] = useState('join'); + const [memberList, setMemberList] = useState([]); + const [searchedMembers, setSearchedMembers] = useState(null); + + const getMembersWithMembership = useCallback( + (mship) => room.getMembersWithMembership(mship), + [roomId, membership], + ); + function loadMorePeople() { - updateMemberList(totalMemberList.slice(0, memberList.length + PER_PAGE_MEMBER)); + setItemCount(itemCount + PER_PAGE_MEMBER); + } + + function handleSearchData(data) { + // NOTICE: data is passed as object property + // because react sucks at handling state update with array. + setSearchedMembers({ data }); + setItemCount(PER_PAGE_MEMBER); + } + + function handleSearch(e) { + if (e.target.value === '') { + setSearchedMembers(null); + setItemCount(PER_PAGE_MEMBER); + } else asyncSearch.search(e.target.value); } useEffect(() => { - updateMemberList(totalMemberList.slice(0, PER_PAGE_MEMBER)); + asyncSearch.setup(memberList, { + keys: ['name', 'username', 'userId'], + limit: PER_PAGE_MEMBER, + }); + }, [memberList]); + + useEffect(() => { + setMemberList( + simplyfiMembers( + getMembersWithMembership(membership) + .sort(AtoZ).sort(sortByPowerLevel), + ), + ); room.loadMembersIfNeeded().then(() => { if (isRoomChanged) return; - const newTotalMemberList = room.getJoinedMembers().sort(AtoZ).sort(sortByPowerLevel); - updateMemberList(newTotalMemberList.slice(0, PER_PAGE_MEMBER)); + setMemberList( + simplyfiMembers( + getMembersWithMembership(membership) + .sort(AtoZ).sort(sortByPowerLevel), + ), + ); }); + asyncSearch.on(asyncSearch.RESULT_SENT, handleSearchData); return () => { isRoomChanged = true; + setMemberList([]); + setSearchedMembers(null); + setItemCount(PER_PAGE_MEMBER); + asyncSearch.removeListener(asyncSearch.RESULT_SENT, handleSearchData); }; }, [roomId]); + const mList = searchedMembers !== null ? searchedMembers.data : memberList.slice(0, itemCount); return (
@@ -78,20 +136,20 @@ function PeopleDrawer({ roomId }) {
{ - memberList.map((member) => ( + mList.map((member) => ( openProfileViewer(member.userId, roomId)} - avatarSrc={member.getAvatarUrl(initMatrix.matrixClient.baseUrl, 24, 24, 'crop')} - name={getUsernameOfRoomMember(member)} + avatarSrc={member.avatarSrc} + name={member.name} color={colorMXID(member.userId)} - peopleRole={getPowerLabel(member.powerLevel)} + peopleRole={member.peopleRole} /> )) }
{ - memberList.length !== totalMemberList.length && ( + mList.length !== 0 && mList.length > itemCount && ( ) } @@ -101,7 +159,7 @@ function PeopleDrawer({ roomId }) {
e.preventDefault()} className="people-search"> - +
diff --git a/src/app/organisms/room/PeopleDrawer.scss b/src/app/organisms/room/PeopleDrawer.scss index 56ac29e0..b3a2182f 100644 --- a/src/app/organisms/room/PeopleDrawer.scss +++ b/src/app/organisms/room/PeopleDrawer.scss @@ -36,10 +36,9 @@ } &__sticky { - display: none; - & .people-search { - min-height: 48px; + --search-input-height: 40px; + min-height: var(--search-input-height); margin: 0 var(--sp-normal); @@ -47,7 +46,7 @@ bottom: var(--sp-normal); & .input { - height: 48px; + height: var(--search-input-height); } } } @@ -55,7 +54,7 @@ .people-drawer__content { padding-top: var(--sp-extra-tight); - padding-bottom: calc( var(--sp-extra-tight) + var(--sp-normal)); + padding-bottom: calc(2 * var(--sp-normal)); } .people-drawer__load-more { padding: var(--sp-normal); From e6f228b63e3b184564b4e0a08db0b3f5039cf26d Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Fri, 22 Oct 2021 17:02:42 +0530 Subject: [PATCH 15/25] Fixed inconsistent search in emojiboard. Signed-off-by: Ajay Bura --- src/app/organisms/emoji-board/EmojiBoard.jsx | 6 +++--- src/app/organisms/room/PeopleDrawer.jsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/organisms/emoji-board/EmojiBoard.jsx b/src/app/organisms/emoji-board/EmojiBoard.jsx index 3f473e4b..2cdef537 100644 --- a/src/app/organisms/emoji-board/EmojiBoard.jsx +++ b/src/app/organisms/emoji-board/EmojiBoard.jsx @@ -87,10 +87,10 @@ function SearchedEmoji() { function handleSearchEmoji(resultEmojis, term) { if (term === '' || resultEmojis.length === 0) { if (term === '') setSearchedEmojis(null); - else setSearchedEmojis([]); + else setSearchedEmojis({ emojis: [] }); return; } - setSearchedEmojis(resultEmojis); + setSearchedEmojis({ emojis: resultEmojis }); } useEffect(() => { @@ -102,7 +102,7 @@ function SearchedEmoji() { if (searchedEmojis === null) return false; - return ; + return ; } function EmojiBoard({ onSelect }) { diff --git a/src/app/organisms/room/PeopleDrawer.jsx b/src/app/organisms/room/PeopleDrawer.jsx index dcddc76d..dc580157 100644 --- a/src/app/organisms/room/PeopleDrawer.jsx +++ b/src/app/organisms/room/PeopleDrawer.jsx @@ -149,7 +149,7 @@ function PeopleDrawer({ roomId }) { }
{ - mList.length !== 0 && mList.length > itemCount && ( + mList.length !== 0 && memberList.length > itemCount && ( ) } From 5ece37fffe293a2a084aefa2655519333289d85f Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Fri, 22 Oct 2021 17:13:57 +0530 Subject: [PATCH 16/25] Added button reset type. Signed-off-by: Ajay Bura --- src/app/atoms/button/Button.jsx | 5 +++-- src/app/atoms/button/IconButton.jsx | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/atoms/button/Button.jsx b/src/app/atoms/button/Button.jsx index bebca861..7fbf8c57 100644 --- a/src/app/atoms/button/Button.jsx +++ b/src/app/atoms/button/Button.jsx @@ -17,7 +17,8 @@ function Button({ className={`${className ? `${className} ` : ''}btn-${variant} ${iconClass} noselect`} onMouseUp={(e) => blurOnBubbling(e, `.btn-${variant}`)} onClick={onClick} - type={type === 'button' ? 'button' : 'submit'} + // eslint-disable-next-line react/button-has-type + type={type} disabled={disabled} > {iconSrc !== null && } @@ -42,7 +43,7 @@ Button.propTypes = { className: PropTypes.string, variant: PropTypes.oneOf(['surface', 'primary', 'positive', 'caution', 'danger']), iconSrc: PropTypes.string, - type: PropTypes.oneOf(['button', 'submit']), + type: PropTypes.oneOf(['button', 'submit', 'reset']), onClick: PropTypes.func, children: PropTypes.node.isRequired, disabled: PropTypes.bool, diff --git a/src/app/atoms/button/IconButton.jsx b/src/app/atoms/button/IconButton.jsx index 4ed2b930..f249f1b0 100644 --- a/src/app/atoms/button/IconButton.jsx +++ b/src/app/atoms/button/IconButton.jsx @@ -17,7 +17,8 @@ const IconButton = React.forwardRef(({ className={`ic-btn ic-btn-${variant}`} onMouseUp={(e) => blurOnBubbling(e, `.ic-btn-${variant}`)} onClick={onClick} - type={type === 'button' ? 'button' : 'submit'} + // eslint-disable-next-line react/button-has-type + type={type} > @@ -45,7 +46,7 @@ IconButton.defaultProps = { IconButton.propTypes = { variant: PropTypes.oneOf(['surface', 'positive', 'caution', 'danger']), size: PropTypes.oneOf(['normal', 'small', 'extra-small']), - type: PropTypes.oneOf(['button', 'submit']), + type: PropTypes.oneOf(['button', 'submit', 'reset']), tooltip: PropTypes.string, tooltipPlacement: PropTypes.oneOf(['top', 'right', 'bottom', 'left']), src: PropTypes.string.isRequired, From e12a75e431aaa176fe7d2b162827f4b2e0a2237b Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Fri, 22 Oct 2021 20:02:01 +0530 Subject: [PATCH 17/25] Enhanced people search UX Signed-off-by: Ajay Bura --- src/app/organisms/room/PeopleDrawer.jsx | 36 ++++++++++++++++++++---- src/app/organisms/room/PeopleDrawer.scss | 30 ++++++++++++++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/app/organisms/room/PeopleDrawer.jsx b/src/app/organisms/room/PeopleDrawer.jsx index dc580157..7cf9a59b 100644 --- a/src/app/organisms/room/PeopleDrawer.jsx +++ b/src/app/organisms/room/PeopleDrawer.jsx @@ -1,4 +1,6 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { + useState, useEffect, useCallback, useRef, +} from 'react'; import PropTypes from 'prop-types'; import './PeopleDrawer.scss'; @@ -10,6 +12,7 @@ import AsyncSearch from '../../../util/AsyncSearch'; import Text from '../../atoms/text/Text'; import Header, { TitleWrapper } from '../../atoms/header/Header'; +import RawIcon from '../../atoms/system-icons/RawIcon'; import IconButton from '../../atoms/button/IconButton'; import Button from '../../atoms/button/Button'; import ScrollView from '../../atoms/scroll/ScrollView'; @@ -17,6 +20,8 @@ import Input from '../../atoms/input/Input'; import PeopleSelector from '../../molecules/people-selector/PeopleSelector'; import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg'; +import SearchIC from '../../../../public/res/ic/outlined/search.svg'; +import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; function AtoZ(m1, m2) { const aName = m1.name; @@ -61,6 +66,7 @@ function PeopleDrawer({ roomId }) { const [membership, setMembership] = useState('join'); const [memberList, setMemberList] = useState([]); const [searchedMembers, setSearchedMembers] = useState(null); + const searchRef = useRef(null); const getMembersWithMembership = useCallback( (mship) => room.getMembersWithMembership(mship), @@ -79,10 +85,13 @@ function PeopleDrawer({ roomId }) { } function handleSearch(e) { - if (e.target.value === '') { + const term = e.target.value; + if (term === '' || term === undefined) { + searchRef.current.value = ''; + searchRef.current.focus(); setSearchedMembers(null); setItemCount(PER_PAGE_MEMBER); - } else asyncSearch.search(e.target.value); + } else asyncSearch.search(term); } useEffect(() => { @@ -93,6 +102,7 @@ function PeopleDrawer({ roomId }) { }, [memberList]); useEffect(() => { + searchRef.current.value = ''; setMemberList( simplyfiMembers( getMembersWithMembership(membership) @@ -147,9 +157,20 @@ function PeopleDrawer({ roomId }) { /> )) } + { + searchedMembers?.data.length === 0 + && ( +
+ No result found! +
+ ) + }
{ - mList.length !== 0 && memberList.length > itemCount && ( + mList.length !== 0 + && memberList.length > itemCount + && searchedMembers === null + && ( ) } @@ -159,7 +180,12 @@ function PeopleDrawer({ roomId }) {
e.preventDefault()} className="people-search"> - + + + { + searchedMembers !== null + && + }
diff --git a/src/app/organisms/room/PeopleDrawer.scss b/src/app/organisms/room/PeopleDrawer.scss index b3a2182f..85a4c12a 100644 --- a/src/app/organisms/room/PeopleDrawer.scss +++ b/src/app/organisms/room/PeopleDrawer.scss @@ -35,6 +35,11 @@ @extend .people-drawer-flexItem; } + &__noresult { + padding: var(--sp-extra-tight) var(--sp-normal); + text-align: center; + } + &__sticky { & .people-search { --search-input-height: 40px; @@ -44,8 +49,33 @@ position: relative; bottom: var(--sp-normal); + display: flex; + align-items: center; + & > .ic-raw, + & > .ic-btn { + position: absolute; + z-index: 99; + } + & > .ic-raw { + left: var(--sp-tight); + [dir=rtl] & { + right: var(--sp-tight); + left: unset; + } + } + & > .ic-btn { + right: 2px; + [dir=rtl] & { + left: 2px; + right: unset; + } + } + & .input-container { + flex: 1; + } & .input { + padding: 0 calc(var(--sp-loose) + var(--sp-normal)); height: var(--search-input-height); } } From d0e9aea78825c97580911bde04b7857f3d404746 Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Sat, 23 Oct 2021 15:27:54 +0530 Subject: [PATCH 18/25] Add option to filter PeopleDrawer Signed-off-by: Ajay Bura --- src/app/organisms/room/PeopleDrawer.jsx | 29 +++++++++++++++++++++++- src/app/organisms/room/PeopleDrawer.scss | 14 ++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/app/organisms/room/PeopleDrawer.jsx b/src/app/organisms/room/PeopleDrawer.jsx index 7cf9a59b..098c4502 100644 --- a/src/app/organisms/room/PeopleDrawer.jsx +++ b/src/app/organisms/room/PeopleDrawer.jsx @@ -17,6 +17,7 @@ import IconButton from '../../atoms/button/IconButton'; import Button from '../../atoms/button/Button'; import ScrollView from '../../atoms/scroll/ScrollView'; import Input from '../../atoms/input/Input'; +import SegmentedControl from '../../atoms/segmented-controls/SegmentedControls'; import PeopleSelector from '../../molecules/people-selector/PeopleSelector'; import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg'; @@ -64,6 +65,7 @@ function PeopleDrawer({ roomId }) { const [itemCount, setItemCount] = useState(PER_PAGE_MEMBER); const [membership, setMembership] = useState('join'); + window.setMemberShip = setMembership; const [memberList, setMemberList] = useState([]); const [searchedMembers, setSearchedMembers] = useState(null); const searchRef = useRef(null); @@ -127,6 +129,10 @@ function PeopleDrawer({ roomId }) { setItemCount(PER_PAGE_MEMBER); asyncSearch.removeListener(asyncSearch.RESULT_SENT, handleSearchData); }; + }, [roomId, membership]); + + useEffect(() => { + setMembership('join'); }, [roomId]); const mList = searchedMembers !== null ? searchedMembers.data : memberList.slice(0, itemCount); @@ -145,6 +151,27 @@ function PeopleDrawer({ roomId }) {
+ { + const getSegmentIndex = { + join: 0, + invite: 1, + ban: 2, + }; + return getSegmentIndex[membership]; + })() + } + segments={[{ text: 'Joined' }, { text: 'Invited' }, { text: 'Banned' }]} + onSelect={(index) => { + const selectSegment = [ + () => setMembership('join'), + () => setMembership('invite'), + () => setMembership('ban'), + ]; + selectSegment[index]?.(); + }} + /> { mList.map((member) => ( No result found! diff --git a/src/app/organisms/room/PeopleDrawer.scss b/src/app/organisms/room/PeopleDrawer.scss index 85a4c12a..5ec90ddb 100644 --- a/src/app/organisms/room/PeopleDrawer.scss +++ b/src/app/organisms/room/PeopleDrawer.scss @@ -85,6 +85,20 @@ .people-drawer__content { padding-top: var(--sp-extra-tight); padding-bottom: calc(2 * var(--sp-normal)); + + & .segmented-controls { + display: flex; + margin-bottom: var(--sp-extra-tight); + margin-left: var(--sp-extra-tight); + [dir=rtl] & { + margin-left: unset; + margin-right: var(--sp-extra-tight); + } + } + & .segment-btn { + flex: 1; + padding: var(--sp-ultra-tight) 0; + } } .people-drawer__load-more { padding: var(--sp-normal); From 2e62f103f22215b68435ea572a8b9b2c6835006c Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Sat, 23 Oct 2021 15:41:16 +0530 Subject: [PATCH 19/25] SegmentedControl bug fixed Signed-off-by: Ajay Bura --- src/app/atoms/segmented-controls/SegmentedControls.jsx | 6 +++++- src/app/organisms/room/PeopleDrawer.jsx | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/app/atoms/segmented-controls/SegmentedControls.jsx b/src/app/atoms/segmented-controls/SegmentedControls.jsx index 2faaf2b6..1d54dd06 100644 --- a/src/app/atoms/segmented-controls/SegmentedControls.jsx +++ b/src/app/atoms/segmented-controls/SegmentedControls.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import './SegmentedControls.scss'; @@ -17,6 +17,10 @@ function SegmentedControls({ onSelect(segmentIndex); } + useEffect(() => { + setSelect(selected); + }, [selected]); + return (
{ diff --git a/src/app/organisms/room/PeopleDrawer.jsx b/src/app/organisms/room/PeopleDrawer.jsx index 098c4502..050d8b6a 100644 --- a/src/app/organisms/room/PeopleDrawer.jsx +++ b/src/app/organisms/room/PeopleDrawer.jsx @@ -65,7 +65,6 @@ function PeopleDrawer({ roomId }) { const [itemCount, setItemCount] = useState(PER_PAGE_MEMBER); const [membership, setMembership] = useState('join'); - window.setMemberShip = setMembership; const [memberList, setMemberList] = useState([]); const [searchedMembers, setSearchedMembers] = useState(null); const searchRef = useRef(null); From 1dc0f9288a0fb561c33969c47f50700a9902d611 Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Sun, 24 Oct 2021 17:33:56 +0530 Subject: [PATCH 20/25] UI improvement in SSO Signed-off-by: Ajay Bura --- src/app/molecules/sso-buttons/SSOButtons.jsx | 38 +++++++------- src/app/molecules/sso-buttons/SSOButtons.scss | 50 ++++++++++++------- 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/app/molecules/sso-buttons/SSOButtons.jsx b/src/app/molecules/sso-buttons/SSOButtons.jsx index 76f865a1..47d001c7 100644 --- a/src/app/molecules/sso-buttons/SSOButtons.jsx +++ b/src/app/molecules/sso-buttons/SSOButtons.jsx @@ -4,6 +4,8 @@ import './SSOButtons.scss'; import { createTemporaryClient, getLoginFlows, startSsoLogin } from '../../../client/action/auth'; +import Text from '../../atoms/text/Text'; + function SSOButtons({ homeserver }) { const [identityProviders, setIdentityProviders] = useState([]); @@ -39,23 +41,15 @@ function SSOButtons({ homeserver }) { }); }, [homeserver]); - // TODO Render all non-icon providers at the end so that they are never inbetween icons. + if (identityProviders.length === 0) return <>; + return (
- {identityProviders.map((idp) => { - if (idp.imageSrc == null || idp.imageSrc === undefined || idp.imageSrc === '') { - return ( - - ); - } - return ( +
+ OR +
+
+ {identityProviders.sort((idp) => !!idp.imageSrc).map((idp) => ( - ); - })} + ))} +
); } @@ -73,12 +67,18 @@ function SSOButtons({ homeserver }) { function SSOButton({ homeserver, id, name, type, imageSrc, }) { + const isImageAvail = !!imageSrc; function handleClick() { startSsoLogin(homeserver, type, id); } return ( - ); } diff --git a/src/app/molecules/sso-buttons/SSOButtons.scss b/src/app/molecules/sso-buttons/SSOButtons.scss index 61d48bc5..76f9b46b 100644 --- a/src/app/molecules/sso-buttons/SSOButtons.scss +++ b/src/app/molecules/sso-buttons/SSOButtons.scss @@ -1,31 +1,43 @@ .sso-buttons { - margin-top: var(--sp-extra-loose); + &__divider { + display: flex; + align-items: center; - display: flex; - justify-content: center; - flex-wrap: wrap; - - &__fallback-text { - margin: var(--sp-tight) 0px; - - flex-basis: 100%; - text-align: center; - - color: var(--bg-primary); - cursor: pointer; + &::before, + &::after { + flex: 1; + content: ''; + margin: var(--sp-tight); + border-bottom: 1px solid var(--bg-surface-border); + } + } + &__container { + margin-bottom: var(--sp-extra-loose); + display: flex; + justify-content: center; + flex-wrap: wrap; } } -.sso-button { - margin-bottom: var(--sp-normal); - - display: flex; +.sso-btn { + margin: var(--sp-tight); + display: inline-flex; justify-content: center; - flex-basis: 20%; cursor: pointer; &__img { - height: var(--av-normal); + height: var(--av-small); + width: var(--av-small); + } + &__text-only { + flex-basis: 100%; + text-align: center; + + margin: var(--sp-tight) 0px; + cursor: pointer; + & .text { + color: var(--tc-link); + } } } \ No newline at end of file From 32923a1c34c38fcd2badd647c8471d3ac4dd451c Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Mon, 25 Oct 2021 14:25:06 +0530 Subject: [PATCH 21/25] Fix profile picture inconsistency (#104, #147) Signed-off-by: Ajay Bura --- src/app/organisms/navigation/SideBar.jsx | 28 +++++++++++++++++-- .../profile-editor/ProfileEditor.jsx | 10 +++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/app/organisms/navigation/SideBar.jsx b/src/app/organisms/navigation/SideBar.jsx index cd6de37e..294431ea 100644 --- a/src/app/organisms/navigation/SideBar.jsx +++ b/src/app/organisms/navigation/SideBar.jsx @@ -24,6 +24,28 @@ import PowerIC from '../../../../public/res/ic/outlined/power.svg'; function ProfileAvatarMenu() { const mx = initMatrix.matrixClient; + const [profile, setProfile] = useState({ + avatarUrl: null, + displayName: mx.getUser(mx.getUserId()).displayName, + }); + + useEffect(() => { + const user = mx.getUser(mx.getUserId()); + const setNewProfile = (avatarUrl, displayName) => setProfile({ + avatarUrl: avatarUrl || null, + displayName: displayName || profile.displayName, + }); + const onAvatarChange = (event, myUser) => { + setNewProfile(myUser.avatarUrl, myUser.displayName); + }; + mx.getProfileInfo(mx.getUserId()).then((info) => { + setNewProfile(info.avatar_url, info.displayname); + }); + user.on('User.avatarUrl', onAvatarChange); + return () => { + user.removeListener('User.avatarUrl', onAvatarChange); + }; + }, []); return ( ( )} /> diff --git a/src/app/organisms/profile-editor/ProfileEditor.jsx b/src/app/organisms/profile-editor/ProfileEditor.jsx index 7125f441..82e65794 100644 --- a/src/app/organisms/profile-editor/ProfileEditor.jsx +++ b/src/app/organisms/profile-editor/ProfileEditor.jsx @@ -1,4 +1,4 @@ -import React, { useState, useRef } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import initMatrix from '../../../client/initMatrix'; @@ -17,11 +17,17 @@ function ProfileEditor({ const mx = initMatrix.matrixClient; const displayNameRef = useRef(null); const bgColor = colorMXID(userId); - const [avatarSrc, setAvatarSrc] = useState(mx.mxcUrlToHttp(mx.getUser(mx.getUserId()).avatarUrl, 80, 80, 'crop') || null); + const [avatarSrc, setAvatarSrc] = useState(null); const [disabled, setDisabled] = useState(true); let username = mx.getUser(mx.getUserId()).displayName; + useEffect(() => { + mx.getProfileInfo(mx.getUserId()).then((info) => { + setAvatarSrc(info.avatar_url ? mx.mxcUrlToHttp(info.avatar_url, 80, 80, 'crop') : null); + }); + }, [userId]); + // Sets avatar URL and updates the avatar component in profile editor to reflect new upload function handleAvatarUpload(url) { if (url === null) { From 6703614c78c23c36ac4db2653a0c7f12427981dd Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Mon, 25 Oct 2021 16:46:23 +0530 Subject: [PATCH 22/25] Fix wildcard matching in emojisearch (#121) Signed-off-by: Ajay Bura --- src/app/organisms/emoji-board/EmojiBoard.jsx | 2 +- src/app/organisms/room/RoomViewCmdBar.jsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/organisms/emoji-board/EmojiBoard.jsx b/src/app/organisms/emoji-board/EmojiBoard.jsx index 2cdef537..05f73952 100644 --- a/src/app/organisms/emoji-board/EmojiBoard.jsx +++ b/src/app/organisms/emoji-board/EmojiBoard.jsx @@ -80,7 +80,7 @@ EmojiGroup.propTypes = { }; const asyncSearch = new AsyncSearch(); -asyncSearch.setup(emojis, { keys: ['shortcode'], limit: 30 }); +asyncSearch.setup(emojis, { keys: ['shortcode'], isContain: true, limit: 40 }); function SearchedEmoji() { const [searchedEmojis, setSearchedEmojis] = useState(null); diff --git a/src/app/organisms/room/RoomViewCmdBar.jsx b/src/app/organisms/room/RoomViewCmdBar.jsx index 329d46a5..1f632810 100644 --- a/src/app/organisms/room/RoomViewCmdBar.jsx +++ b/src/app/organisms/room/RoomViewCmdBar.jsx @@ -319,6 +319,7 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) { else if (searchTerm.match(/^[-]?(\()$/)) searchTerm = 'pleading_face'; else if (searchTerm.match(/^[-]?(\$)$/)) searchTerm = 'money'; else if (searchTerm.match(/^(<3)$/)) searchTerm = 'heart'; + else if (searchTerm.match(/^(c|ca|cat)$/)) searchTerm = '_cat'; } } @@ -343,7 +344,7 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) { '>*': () => asyncSearch.setup(getRooms([...roomList.spaces]), { keys: ['name'], limit: 20 }), '>#': () => asyncSearch.setup(getRooms([...roomList.rooms]), { keys: ['name'], limit: 20 }), '>@': () => asyncSearch.setup(getRooms([...roomList.directs]), { keys: ['name'], limit: 20 }), - ':': () => asyncSearch.setup(emojis, { keys: ['shortcode'], limit: 20 }), + ':': () => asyncSearch.setup(emojis, { keys: ['shortcode'], isContain: true, limit: 20 }), '@': () => asyncSearch.setup(matrixClient.getRoom(roomId).getJoinedMembers().map((member) => ({ name: member.name, userId: member.userId.slice(1), From 0bd22bee13dd5b608891433ac6763a7e93c019a4 Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Mon, 25 Oct 2021 17:19:11 +0530 Subject: [PATCH 23/25] Fix message menu placement on large screen (#113) Signed-off-by: Ajay Bura --- src/app/molecules/message/Message.scss | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/app/molecules/message/Message.scss b/src/app/molecules/message/Message.scss index 2b757b08..89979ccf 100644 --- a/src/app/molecules/message/Message.scss +++ b/src/app/molecules/message/Message.scss @@ -262,6 +262,16 @@ right: unset; } } +@media (min-width: 1620px) { + .message__options { + right: unset; + left: 770px; + [dir=rtl] { + left: unset; + right: 770px + } + } +} // markdown formating .message__content { From 54dd5a4edbb664f12c80bb37bcd4a508a8a579e8 Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Mon, 25 Oct 2021 17:28:14 +0530 Subject: [PATCH 24/25] Fix reaction selector doesn't focus msg input (#62) Signed-off-by: Ajay Bura --- src/app/organisms/room/RoomViewInput.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/organisms/room/RoomViewInput.jsx b/src/app/organisms/room/RoomViewInput.jsx index 5ca4fdd3..6d4a22f6 100644 --- a/src/app/organisms/room/RoomViewInput.jsx +++ b/src/app/organisms/room/RoomViewInput.jsx @@ -313,6 +313,7 @@ function RoomViewInput({ function addEmoji(emoji) { textAreaRef.current.value += emoji.unicode; + textAreaRef.current.focus(); } function handleUploadClick() { From 253e3eb855bf52eaeb9127383d8d7218a52ef5c7 Mon Sep 17 00:00:00 2001 From: Ajay Bura Date: Mon, 25 Oct 2021 17:53:25 +0530 Subject: [PATCH 25/25] v1.4.0: SSO login and profile viewer Signed-off-by: Ajay Bura --- package-lock.json | 2 +- package.json | 2 +- src/app/organisms/settings/Settings.jsx | 2 +- src/app/templates/auth/Auth.jsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4e48653..c7307241 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cinny", - "version": "1.3.2", + "version": "1.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a5f99451..e568b7b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cinny", - "version": "1.3.2", + "version": "1.4.0", "description": "Yet another matrix client", "main": "index.js", "engines": { diff --git a/src/app/organisms/settings/Settings.jsx b/src/app/organisms/settings/Settings.jsx index de76f4c5..10945114 100644 --- a/src/app/organisms/settings/Settings.jsx +++ b/src/app/organisms/settings/Settings.jsx @@ -104,7 +104,7 @@ function AboutSection() {
Cinny - v1.3.2 + v1.4.0 Yet another matrix client diff --git a/src/app/templates/auth/Auth.jsx b/src/app/templates/auth/Auth.jsx index 6586e588..121f4200 100644 --- a/src/app/templates/auth/Auth.jsx +++ b/src/app/templates/auth/Auth.jsx @@ -330,7 +330,7 @@ function Auth() { - v1.3.2 + v1.4.0