Merge branch 'dev' into dev
This commit is contained in:
commit
05fc88a9fe
23 changed files with 1397 additions and 1309 deletions
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -1,4 +1,4 @@
|
||||||
<!-- Please read https://github.com/ajbura/cinny/CONTRIBUTING.md before submitting your pull request -->
|
<!-- Please read https://github.com/ajbura/cinny/blob/dev/CONTRIBUTING.md before submitting your pull request -->
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
<!-- Please include a summary of the change. Please also include relevant motivation and context. List any dependencies that are required for this change. -->
|
<!-- Please include a summary of the change. Please also include relevant motivation and context. List any dependencies that are required for this change. -->
|
||||||
|
|
4
.github/workflows/build-pull-request.yml
vendored
4
.github/workflows/build-pull-request.yml
vendored
|
@ -12,6 +12,10 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3.0.2
|
uses: actions/checkout@v3.0.2
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3.4.1
|
||||||
|
with:
|
||||||
|
node-version: 17.9.0
|
||||||
- name: Build app
|
- name: Build app
|
||||||
run: npm ci && npm run build
|
run: npm ci && npm run build
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
|
|
6
.github/workflows/netlify-dev.yml
vendored
6
.github/workflows/netlify-dev.yml
vendored
|
@ -14,8 +14,12 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3.0.2
|
uses: actions/checkout@v3.0.2
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3.4.1
|
||||||
|
with:
|
||||||
|
node-version: 17.9.0
|
||||||
- name: Build and deploy to Netlify
|
- name: Build and deploy to Netlify
|
||||||
uses: jsmrcaga/action-netlify-deploy@fb6a5f936a4b06a8f7793e69fc5a022ffe39807a
|
uses: jsmrcaga/action-netlify-deploy@53de32e559b0b3833615b9788c7a090cd2fddb03
|
||||||
with:
|
with:
|
||||||
install_command: "npm ci"
|
install_command: "npm ci"
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
|
10
.github/workflows/prod-deploy.yml
vendored
10
.github/workflows/prod-deploy.yml
vendored
|
@ -11,6 +11,10 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3.0.2
|
uses: actions/checkout@v3.0.2
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3.4.1
|
||||||
|
with:
|
||||||
|
node-version: 17.9.0
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
npm ci
|
npm ci
|
||||||
|
@ -45,8 +49,12 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3.0.2
|
uses: actions/checkout@v3.0.2
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3.4.1
|
||||||
|
with:
|
||||||
|
node-version: 17.9.0
|
||||||
- name: Build and deploy to Netlify
|
- name: Build and deploy to Netlify
|
||||||
uses: jsmrcaga/action-netlify-deploy@fb6a5f936a4b06a8f7793e69fc5a022ffe39807a
|
uses: jsmrcaga/action-netlify-deploy@53de32e559b0b3833615b9788c7a090cd2fddb03
|
||||||
with:
|
with:
|
||||||
install_command: "npm ci"
|
install_command: "npm ci"
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
|
|
@ -10,7 +10,7 @@ RUN npm run build
|
||||||
|
|
||||||
|
|
||||||
## App
|
## App
|
||||||
FROM nginx:1.21.6-alpine
|
FROM nginx:1.23.0-alpine
|
||||||
|
|
||||||
COPY --from=builder /src/dist /app
|
COPY --from=builder /src/dist /app
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
{
|
{
|
||||||
"defaultHomeserver": 4,
|
"defaultHomeserver": 3,
|
||||||
"homeserverList": [
|
"homeserverList": [
|
||||||
"converser.eu",
|
|
||||||
"envs.net",
|
"envs.net",
|
||||||
"halogen.city",
|
"halogen.city",
|
||||||
"kde.org",
|
"kde.org",
|
||||||
"matrix.org",
|
"matrix.org",
|
||||||
"chat.mozilla.org"
|
"mozilla.org"
|
||||||
],
|
],
|
||||||
"allowCustomHomeservers": true
|
"allowCustomHomeservers": true
|
||||||
}
|
}
|
2414
package-lock.json
generated
2414
package-lock.json
generated
File diff suppressed because it is too large
Load diff
32
package.json
32
package.json
|
@ -30,9 +30,11 @@
|
||||||
"i18next": "^21.8.9",
|
"i18next": "^21.8.9",
|
||||||
"i18next-browser-languagedetector": "^6.1.4",
|
"i18next-browser-languagedetector": "^6.1.4",
|
||||||
"i18next-http-backend": "^1.4.1",
|
"i18next-http-backend": "^1.4.1",
|
||||||
|
"html-react-parser": "^2.0.0",
|
||||||
"katex": "^0.15.6",
|
"katex": "^0.15.6",
|
||||||
"linkifyjs": "^2.1.9",
|
"linkify-html": "^4.0.0-beta.5",
|
||||||
"matrix-js-sdk": "^18.0.0",
|
"linkifyjs": "^4.0.0-beta.5",
|
||||||
|
"matrix-js-sdk": "^18.1.0",
|
||||||
"micromark": "^3.0.10",
|
"micromark": "^3.0.10",
|
||||||
"micromark-extension-gfm": "^2.0.1",
|
"micromark-extension-gfm": "^2.0.1",
|
||||||
"micromark-extension-math": "^2.0.2",
|
"micromark-extension-math": "^2.0.2",
|
||||||
|
@ -53,9 +55,9 @@
|
||||||
"twemoji": "^14.0.2"
|
"twemoji": "^14.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.18.2",
|
"@babel/core": "^7.18.6",
|
||||||
"@babel/preset-env": "^7.18.2",
|
"@babel/preset-env": "^7.18.6",
|
||||||
"@babel/preset-react": "^7.17.12",
|
"@babel/preset-react": "^7.18.6",
|
||||||
"assert": "^2.0.0",
|
"assert": "^2.0.0",
|
||||||
"babel-loader": "^8.2.5",
|
"babel-loader": "^8.2.5",
|
||||||
"browserify-fs": "^1.0.0",
|
"browserify-fs": "^1.0.0",
|
||||||
|
@ -65,27 +67,27 @@
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.0",
|
||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.1",
|
||||||
"css-minimizer-webpack-plugin": "^4.0.0",
|
"css-minimizer-webpack-plugin": "^4.0.0",
|
||||||
"eslint": "^8.17.0",
|
"eslint": "^8.19.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
"eslint-plugin-jsx-a11y": "^6.6.0",
|
||||||
"eslint-plugin-react": "^7.30.0",
|
"eslint-plugin-react": "^7.30.1",
|
||||||
"eslint-plugin-react-hooks": "^4.5.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"favicons": "^6.2.2",
|
"favicons": "^6.2.2",
|
||||||
"favicons-webpack-plugin": "^5.0.2",
|
"favicons-webpack-plugin": "^5.0.2",
|
||||||
"html-loader": "^3.1.0",
|
"html-loader": "^3.1.2",
|
||||||
"html-webpack-plugin": "^5.3.1",
|
"html-webpack-plugin": "^5.3.1",
|
||||||
"mini-css-extract-plugin": "^2.6.0",
|
"mini-css-extract-plugin": "^2.6.1",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"sass": "^1.52.2",
|
"sass": "^1.53.0",
|
||||||
"sass-loader": "^13.0.0",
|
"sass-loader": "^13.0.2",
|
||||||
"stream-browserify": "^3.0.0",
|
"stream-browserify": "^3.0.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0",
|
||||||
"util": "^0.12.4",
|
"util": "^0.12.4",
|
||||||
"webpack": "^5.73.0",
|
"webpack": "^5.73.0",
|
||||||
"webpack-cli": "^4.9.2",
|
"webpack-cli": "^4.10.0",
|
||||||
"webpack-dev-server": "^4.9.2",
|
"webpack-dev-server": "^4.9.3",
|
||||||
"webpack-merge": "^5.7.3"
|
"webpack-merge": "^5.7.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
4
public/res/ic/outlined/eye-blind.svg
Normal file
4
public/res/ic/outlined/eye-blind.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.92896 3.51471L3.51474 4.92892L5.97515 7.38933C4.46742 8.5776 3.32116 9.93994 2.7 10.8C2.1 11.5 2.1 12.5 2.7 13.2C4 15 7.6 19 12 19C13.5709 19 15.0398 18.4902 16.3384 17.7526L19.0711 20.4853L20.4853 19.0711L4.92896 3.51471ZM4.2 12C4.68291 11.3561 5.85678 9.9637 7.39721 8.81139L9.29238 10.7066C9.10496 11.0982 9 11.5368 9 12C9 13.6569 10.3431 15 12 15C12.4632 15 12.9018 14.895 13.2934 14.7076L14.8573 16.2715C13.9566 16.7128 12.9896 17 12 17C8.4 17 5.1 13.2 4.2 12Z" fill="black"/>
|
||||||
|
<path d="M9.6226 5.37995L11.2906 7.04797C11.5254 7.01661 11.762 7 12 7C15.6 7 18.9 10.8 19.8 12C19.493 12.4094 18.9066 13.1213 18.1244 13.8817L19.5194 15.2768C20.2973 14.4974 20.9049 13.7471 21.3 13.2C21.9 12.5 21.9 11.5 21.3 10.8C20 9 16.4 5 12 5C11.1762 5 10.3805 5.14021 9.6226 5.37995Z" fill="black"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 943 B |
|
@ -1,13 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<path d="M12 19C7.6 19 4 15 2.7 13.2C2.1 12.5 2.1 11.5 2.7 10.8C4 9 7.6 5 12 5C16.4 5 20 9 21.3 10.8C21.9 11.5 21.9 12.5 21.3 13.2C20 15 16.4 19 12 19ZM12 7C8.4 7 5.1 10.8 4.2 12C5.1 13.2 8.4 17 12 17C15.6 17 18.9 13.2 19.8 12C18.9 10.8 15.6 7 12 7Z" fill="black"/>
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
<path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" fill="black"/>
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<path d="M12,19c-4.4,0-8-4-9.3-5.8c-0.6-0.7-0.6-1.7,0-2.4C4,9,7.6,5,12,5s8,4,9.3,5.8c0.6,0.7,0.6,1.7,0,2.4C20,15,16.4,19,12,19
|
|
||||||
z M12,7c-3.6,0-6.9,3.8-7.8,5c0.9,1.2,4.2,5,7.8,5s6.9-3.8,7.8-5C18.9,10.8,15.6,7,12,7z"/>
|
|
||||||
</g>
|
|
||||||
<circle cx="12" cy="12" r="3"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 718 B After Width: | Height: | Size: 508 B |
|
@ -125,7 +125,7 @@ function RoomAliases({ roomId }) {
|
||||||
const loadLocalAliases = async () => {
|
const loadLocalAliases = async () => {
|
||||||
let local = [];
|
let local = [];
|
||||||
try {
|
try {
|
||||||
const result = await mx.unstableGetLocalAliases(roomId);
|
const result = await mx.getLocalAliases(roomId);
|
||||||
local = result.aliases.filter((alias) => !aliases.published.includes(alias));
|
local = result.aliases.filter((alias) => !aliases.published.includes(alias));
|
||||||
} catch {
|
} catch {
|
||||||
local = [];
|
local = [];
|
||||||
|
|
|
@ -242,12 +242,12 @@ function RoomPermissions({ roomId }) {
|
||||||
? permissions[permInfo.parent]?.[permKey]
|
? permissions[permInfo.parent]?.[permKey]
|
||||||
: permissions[permKey];
|
: permissions[permKey];
|
||||||
|
|
||||||
if (!permValue) permValue = permInfo.default;
|
if (permValue === undefined) permValue = permInfo.default;
|
||||||
|
|
||||||
if (typeof permValue === 'number') {
|
if (typeof permValue === 'number') {
|
||||||
powerLevel = permValue;
|
powerLevel = permValue;
|
||||||
} else if (permKey === 'notifications') {
|
} else if (permKey === 'notifications') {
|
||||||
powerLevel = permValue.room || 50;
|
powerLevel = permValue.room ?? 50;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<SettingTile
|
<SettingTile
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import emojisData from 'emojibase-data/en/compact.json';
|
import emojisData from 'emojibase-data/en/compact.json';
|
||||||
import shortcodes from 'emojibase-data/en/shortcodes/joypixels.json';
|
import joypixels from 'emojibase-data/en/shortcodes/joypixels.json';
|
||||||
|
import emojibase from 'emojibase-data/en/shortcodes/emojibase.json';
|
||||||
|
|
||||||
const emojiGroups = [{
|
const emojiGroups = [{
|
||||||
name: 'Smileys & people',
|
name: 'Smileys & people',
|
||||||
|
@ -52,7 +53,7 @@ function addToGroup(emoji) {
|
||||||
|
|
||||||
const emojis = [];
|
const emojis = [];
|
||||||
emojisData.forEach((emoji) => {
|
emojisData.forEach((emoji) => {
|
||||||
const myShortCodes = shortcodes[emoji.hexcode];
|
const myShortCodes = joypixels[emoji.hexcode] || emojibase[emoji.hexcode];
|
||||||
if (!myShortCodes) return;
|
if (!myShortCodes) return;
|
||||||
const em = {
|
const em = {
|
||||||
...emoji,
|
...emoji,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import initMatrix from '../../../client/initMatrix';
|
||||||
import cons from '../../../client/state/cons';
|
import cons from '../../../client/state/cons';
|
||||||
import * as roomActions from '../../../client/action/room';
|
import * as roomActions from '../../../client/action/room';
|
||||||
import { selectRoom } from '../../../client/action/navigation';
|
import { selectRoom } from '../../../client/action/navigation';
|
||||||
import { hasDMWith } from '../../../util/matrixUtil';
|
import { hasDMWith, hasDevices } from '../../../util/matrixUtil';
|
||||||
|
|
||||||
import Text from '../../atoms/text/Text';
|
import Text from '../../atoms/text/Text';
|
||||||
import Button from '../../atoms/button/Button';
|
import Button from '../../atoms/button/Button';
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { selectRoom, openReusableContextMenu } from '../../../client/action/navi
|
||||||
import * as roomActions from '../../../client/action/room';
|
import * as roomActions from '../../../client/action/room';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getUsername, getUsernameOfRoomMember, getPowerLabel, hasDMWith,
|
getUsername, getUsernameOfRoomMember, getPowerLabel, hasDMWith, hasDevices
|
||||||
} from '../../../util/matrixUtil';
|
} from '../../../util/matrixUtil';
|
||||||
import { getEventCords } from '../../../util/common';
|
import { getEventCords } from '../../../util/common';
|
||||||
import colorMXID from '../../../util/colorMXID';
|
import colorMXID from '../../../util/colorMXID';
|
||||||
|
@ -209,7 +209,7 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
|
||||||
// Create new DM
|
// Create new DM
|
||||||
try {
|
try {
|
||||||
setIsCreatingDM(true);
|
setIsCreatingDM(true);
|
||||||
await roomActions.createDM(userId);
|
await roomActions.createDM(userId, await hasDevices(userId));
|
||||||
} catch {
|
} catch {
|
||||||
if (isMountedRef.current === false) return;
|
if (isMountedRef.current === false) return;
|
||||||
setIsCreatingDM(false);
|
setIsCreatingDM(false);
|
||||||
|
|
|
@ -62,23 +62,25 @@ function AppearanceSection() {
|
||||||
)}
|
)}
|
||||||
content={<Text variant="b3">{t('Organisms.Settings.theme.follow_system.description')}</Text>}
|
content={<Text variant="b3">{t('Organisms.Settings.theme.follow_system.description')}</Text>}
|
||||||
/>
|
/>
|
||||||
{!settings.useSystemTheme && (
|
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title={t('Organisms.Settings.theme.title')}
|
title={t('Organisms.Settings.theme.title')}
|
||||||
content={(
|
content={(
|
||||||
<SegmentedControls
|
<SegmentedControls
|
||||||
selected={settings.getThemeIndex()}
|
selected={settings.useSystemTheme ? -1 : settings.getThemeIndex()}
|
||||||
segments={[
|
segments={[
|
||||||
{ text: t('Organisms.Settings.theme.theme_light') },
|
{ text: t('Organisms.Settings.theme.theme_light') },
|
||||||
{ text: t('Organisms.Settings.theme.theme_silver') },
|
{ text: t('Organisms.Settings.theme.theme_silver') },
|
||||||
{ text: t('Organisms.Settings.theme.theme_dark') },
|
{ text: t('Organisms.Settings.theme.theme_dark') },
|
||||||
{ text: t('Organisms.Settings.theme.theme_butter') },
|
{ text: t('Organisms.Settings.theme.theme_butter') },
|
||||||
]}
|
]}
|
||||||
onSelect={(index) => settings.setTheme(index)}
|
onSelect={(index) => {
|
||||||
|
if (settings.useSystemTheme) toggleSystemTheme();
|
||||||
|
settings.setTheme(index);
|
||||||
|
updateState({});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="settings-appearance__card">
|
<div className="settings-appearance__card">
|
||||||
<MenuHeader>Room messages</MenuHeader>
|
<MenuHeader>Room messages</MenuHeader>
|
||||||
|
|
|
@ -21,6 +21,8 @@ import Avatar from '../../atoms/avatar/Avatar';
|
||||||
import ContextMenu, { MenuItem, MenuHeader } from '../../atoms/context-menu/ContextMenu';
|
import ContextMenu, { MenuItem, MenuHeader } from '../../atoms/context-menu/ContextMenu';
|
||||||
|
|
||||||
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
|
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
|
||||||
|
import EyeIC from '../../../../public/res/ic/outlined/eye.svg';
|
||||||
|
import EyeBlindIC from '../../../../public/res/ic/outlined/eye-blind.svg';
|
||||||
import CinnySvg from '../../../../public/res/svg/cinny.svg';
|
import CinnySvg from '../../../../public/res/svg/cinny.svg';
|
||||||
import SSOButtons from '../../molecules/sso-buttons/SSOButtons';
|
import SSOButtons from '../../molecules/sso-buttons/SSOButtons';
|
||||||
|
|
||||||
|
@ -54,11 +56,8 @@ function Homeserver({ onChange }) {
|
||||||
const setupHsConfig = async (servername) => {
|
const setupHsConfig = async (servername) => {
|
||||||
setProcess({ isLoading: true, message: 'Looking for homeserver...' });
|
setProcess({ isLoading: true, message: 'Looking for homeserver...' });
|
||||||
let baseUrl = null;
|
let baseUrl = null;
|
||||||
try {
|
|
||||||
baseUrl = await getBaseUrl(servername);
|
baseUrl = await getBaseUrl(servername);
|
||||||
} catch (e) {
|
|
||||||
baseUrl = e.message;
|
|
||||||
}
|
|
||||||
if (searchingHs !== servername) return;
|
if (searchingHs !== servername) return;
|
||||||
setProcess({ isLoading: true, message: `Connecting to ${baseUrl}...` });
|
setProcess({ isLoading: true, message: `Connecting to ${baseUrl}...` });
|
||||||
const tempClient = auth.createTemporaryClient(baseUrl);
|
const tempClient = auth.createTemporaryClient(baseUrl);
|
||||||
|
@ -97,7 +96,7 @@ function Homeserver({ onChange }) {
|
||||||
if (!hsList?.length > 0 || selectedHs < 0 || selectedHs >= hsList?.length) {
|
if (!hsList?.length > 0 || selectedHs < 0 || selectedHs >= hsList?.length) {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
setHs({ selected: hsList[selectedHs], list: hsList, allowCustom: allowCustom });
|
setHs({ selected: hsList[selectedHs], list: hsList, allowCustom });
|
||||||
} catch {
|
} catch {
|
||||||
setHs({ selected: 'matrix.org', list: ['matrix.org'], allowCustom: true });
|
setHs({ selected: 'matrix.org', list: ['matrix.org'], allowCustom: true });
|
||||||
}
|
}
|
||||||
|
@ -114,8 +113,14 @@ function Homeserver({ onChange }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="homeserver-form">
|
<div className="homeserver-form">
|
||||||
<Input name="homeserver" onChange={handleHsInput} value={hs?.selected} forwardRef={hsRef} label="Homeserver"
|
<Input
|
||||||
disabled={hs === null || !hs.allowCustom} />
|
name="homeserver"
|
||||||
|
onChange={handleHsInput}
|
||||||
|
value={hs?.selected}
|
||||||
|
forwardRef={hsRef}
|
||||||
|
label="Homeserver"
|
||||||
|
disabled={hs === null || !hs.allowCustom}
|
||||||
|
/>
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
placement="right"
|
placement="right"
|
||||||
content={(hideMenu) => (
|
content={(hideMenu) => (
|
||||||
|
@ -156,6 +161,7 @@ Homeserver.propTypes = {
|
||||||
|
|
||||||
function Login({ loginFlow, baseUrl }) {
|
function Login({ loginFlow, baseUrl }) {
|
||||||
const [typeIndex, setTypeIndex] = useState(0);
|
const [typeIndex, setTypeIndex] = useState(0);
|
||||||
|
const [passVisible, setPassVisible] = useState(false);
|
||||||
const loginTypes = ['Username', 'Email'];
|
const loginTypes = ['Username', 'Email'];
|
||||||
const isPassword = loginFlow?.filter((flow) => flow.type === 'm.login.password')[0];
|
const isPassword = loginFlow?.filter((flow) => flow.type === 'm.login.password')[0];
|
||||||
const ssoProviders = loginFlow?.filter((flow) => flow.type === 'm.login.sso')[0];
|
const ssoProviders = loginFlow?.filter((flow) => flow.type === 'm.login.sso')[0];
|
||||||
|
@ -166,17 +172,23 @@ function Login({ loginFlow, baseUrl }) {
|
||||||
|
|
||||||
const validator = (values) => {
|
const validator = (values) => {
|
||||||
const errors = {};
|
const errors = {};
|
||||||
if (typeIndex === 0 && values.username.length > 0 && values.username.indexOf(':') > -1) {
|
|
||||||
errors.username = 'Username must contain local-part only';
|
|
||||||
}
|
|
||||||
if (typeIndex === 1 && values.email.length > 0 && !isValidInput(values.email, EMAIL_REGEX)) {
|
if (typeIndex === 1 && values.email.length > 0 && !isValidInput(values.email, EMAIL_REGEX)) {
|
||||||
errors.email = BAD_EMAIL_ERROR;
|
errors.email = BAD_EMAIL_ERROR;
|
||||||
}
|
}
|
||||||
return errors;
|
return errors;
|
||||||
};
|
};
|
||||||
const submitter = (values, actions) => auth.login(
|
const submitter = async (values, actions) => {
|
||||||
baseUrl,
|
let userBaseUrl = baseUrl;
|
||||||
typeIndex === 0 ? normalizeUsername(values.username) : undefined,
|
let { username } = values;
|
||||||
|
const mxIdMatch = username.match(/^@(.+):(.+\..+)$/);
|
||||||
|
if (typeIndex === 0 && mxIdMatch) {
|
||||||
|
[, username, userBaseUrl] = mxIdMatch;
|
||||||
|
userBaseUrl = await getBaseUrl(userBaseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth.login(
|
||||||
|
userBaseUrl,
|
||||||
|
typeIndex === 0 ? normalizeUsername(username) : undefined,
|
||||||
typeIndex === 1 ? values.email : undefined,
|
typeIndex === 1 ? values.email : undefined,
|
||||||
values.password,
|
values.password,
|
||||||
).then(() => {
|
).then(() => {
|
||||||
|
@ -191,6 +203,7 @@ function Login({ loginFlow, baseUrl }) {
|
||||||
});
|
});
|
||||||
actions.setSubmitting(false);
|
actions.setSubmitting(false);
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -236,7 +249,10 @@ function Login({ loginFlow, baseUrl }) {
|
||||||
{errors.username && <Text className="auth-form__error" variant="b3">{errors.username}</Text>}
|
{errors.username && <Text className="auth-form__error" variant="b3">{errors.username}</Text>}
|
||||||
{typeIndex === 1 && <Input values={values.email} name="email" onChange={handleChange} label="Email" type="email" required />}
|
{typeIndex === 1 && <Input values={values.email} name="email" onChange={handleChange} label="Email" type="email" required />}
|
||||||
{errors.email && <Text className="auth-form__error" variant="b3">{errors.email}</Text>}
|
{errors.email && <Text className="auth-form__error" variant="b3">{errors.email}</Text>}
|
||||||
<Input values={values.password} name="password" onChange={handleChange} label="Password" type="password" required />
|
<div className="auth-form__pass-eye-wrapper">
|
||||||
|
<Input values={values.password} name="password" onChange={handleChange} label="Password" type={passVisible ? 'text' : 'password'} required />
|
||||||
|
<IconButton onClick={() => setPassVisible(!passVisible)} src={passVisible ? EyeIC : EyeBlindIC} size="extra-small" />
|
||||||
|
</div>
|
||||||
{errors.password && <Text className="auth-form__error" variant="b3">{errors.password}</Text>}
|
{errors.password && <Text className="auth-form__error" variant="b3">{errors.password}</Text>}
|
||||||
{errors.other && <Text className="auth-form__error" variant="b3">{errors.other}</Text>}
|
{errors.other && <Text className="auth-form__error" variant="b3">{errors.other}</Text>}
|
||||||
<div className="auth-form__btns">
|
<div className="auth-form__btns">
|
||||||
|
@ -269,6 +285,8 @@ let sid;
|
||||||
let clientSecret;
|
let clientSecret;
|
||||||
function Register({ registerInfo, loginFlow, baseUrl }) {
|
function Register({ registerInfo, loginFlow, baseUrl }) {
|
||||||
const [process, setProcess] = useState({});
|
const [process, setProcess] = useState({});
|
||||||
|
const [passVisible, setPassVisible] = useState(false);
|
||||||
|
const [cPassVisible, setCPassVisible] = useState(false);
|
||||||
const formRef = useRef();
|
const formRef = useRef();
|
||||||
|
|
||||||
const ssoProviders = loginFlow?.filter((flow) => flow.type === 'm.login.sso')[0];
|
const ssoProviders = loginFlow?.filter((flow) => flow.type === 'm.login.sso')[0];
|
||||||
|
@ -319,6 +337,7 @@ function Register({ registerInfo, loginFlow, baseUrl }) {
|
||||||
if (!isAvail) {
|
if (!isAvail) {
|
||||||
actions.setErrors({ username: 'Username is already taken' });
|
actions.setErrors({ username: 'Username is already taken' });
|
||||||
actions.setSubmitting(false);
|
actions.setSubmitting(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (isEmail && values.email.length > 0) {
|
if (isEmail && values.email.length > 0) {
|
||||||
const result = await auth.verifyEmail(baseUrl, values.email, clientSecret, 1);
|
const result = await auth.verifyEmail(baseUrl, values.email, clientSecret, 1);
|
||||||
|
@ -437,9 +456,15 @@ function Register({ registerInfo, loginFlow, baseUrl }) {
|
||||||
<form className="auth-form" ref={formRef} onSubmit={handleSubmit}>
|
<form className="auth-form" ref={formRef} onSubmit={handleSubmit}>
|
||||||
<Input values={values.username} name="username" onChange={handleChange} label="Username" type="username" required />
|
<Input values={values.username} name="username" onChange={handleChange} label="Username" type="username" required />
|
||||||
{errors.username && <Text className="auth-form__error" variant="b3">{errors.username}</Text>}
|
{errors.username && <Text className="auth-form__error" variant="b3">{errors.username}</Text>}
|
||||||
<Input values={values.password} name="password" onChange={handleChange} label="Password" type="password" required />
|
<div className="auth-form__pass-eye-wrapper">
|
||||||
|
<Input values={values.password} name="password" onChange={handleChange} label="Password" type={passVisible ? 'text' : 'password'} required />
|
||||||
|
<IconButton onClick={() => setPassVisible(!passVisible)} src={passVisible ? EyeIC : EyeBlindIC} size="extra-small" />
|
||||||
|
</div>
|
||||||
{errors.password && <Text className="auth-form__error" variant="b3">{errors.password}</Text>}
|
{errors.password && <Text className="auth-form__error" variant="b3">{errors.password}</Text>}
|
||||||
<Input values={values.confirmPassword} name="confirmPassword" onChange={handleChange} label="Confirm password" type="password" required />
|
<div className="auth-form__pass-eye-wrapper">
|
||||||
|
<Input values={values.confirmPassword} name="confirmPassword" onChange={handleChange} label="Confirm password" type={cPassVisible ? 'text' : 'password'} required />
|
||||||
|
<IconButton onClick={() => setCPassVisible(!cPassVisible)} src={cPassVisible ? EyeIC : EyeBlindIC} size="extra-small" />
|
||||||
|
</div>
|
||||||
{errors.confirmPassword && <Text className="auth-form__error" variant="b3">{errors.confirmPassword}</Text>}
|
{errors.confirmPassword && <Text className="auth-form__error" variant="b3">{errors.confirmPassword}</Text>}
|
||||||
{isEmail && <Input values={values.email} name="email" onChange={handleChange} label={`Email${isEmailRequired ? '' : ' (optional)'}`} type="email" required={isEmailRequired} />}
|
{isEmail && <Input values={values.email} name="email" onChange={handleChange} label={`Email${isEmailRequired ? '' : ' (optional)'}`} type="email" required={isEmailRequired} />}
|
||||||
{errors.email && <Text className="auth-form__error" variant="b3">{errors.email}</Text>}
|
{errors.email && <Text className="auth-form__error" variant="b3">{errors.email}</Text>}
|
||||||
|
|
|
@ -97,7 +97,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-form {
|
.auth-form {
|
||||||
& > .input-container {
|
& > .input-container,
|
||||||
|
&__pass-eye-wrapper {
|
||||||
margin: var(--sp-tight) 0 var(--sp-ultra-tight);
|
margin: var(--sp-tight) 0 var(--sp-ultra-tight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +108,20 @@
|
||||||
margin-top: calc(var(--sp-extra-loose) + var(--sp-tight));
|
margin-top: calc(var(--sp-extra-loose) + var(--sp-tight));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__pass-eye-wrapper {
|
||||||
|
position: relative;
|
||||||
|
& .ic-btn {
|
||||||
|
position: absolute;
|
||||||
|
@include dir.prop(right, 6px, unset);
|
||||||
|
@include dir.prop(left, unset, 6px );
|
||||||
|
bottom: 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
& input {
|
||||||
|
@include dir.side(padding, var(--sp-normal), 46px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__btns {
|
&__btns {
|
||||||
padding-top: var(--sp-loose);
|
padding-top: var(--sp-loose);
|
||||||
margin-bottom: var(--sp-extra-loose);
|
margin-bottom: var(--sp-extra-loose);
|
||||||
|
|
|
@ -48,31 +48,43 @@ class Settings extends EventEmitter {
|
||||||
return this.themes[this.themeIndex];
|
return this.themes[this.themeIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
setTheme(themeIndex) {
|
_clearTheme() {
|
||||||
const appBody = document.getElementById('appBody');
|
document.body.classList.remove('system-theme');
|
||||||
|
|
||||||
appBody.classList.remove('system-theme');
|
|
||||||
this.themes.forEach((themeName) => {
|
this.themes.forEach((themeName) => {
|
||||||
if (themeName === '') return;
|
if (themeName === '') return;
|
||||||
appBody.classList.remove(themeName);
|
document.body.classList.remove(themeName);
|
||||||
});
|
});
|
||||||
// If use system theme is enabled
|
|
||||||
// we will override current theme choice with system theme
|
|
||||||
if (this.useSystemTheme) {
|
|
||||||
appBody.classList.add('system-theme');
|
|
||||||
} else if (this.themes[themeIndex] !== '') {
|
|
||||||
appBody.classList.add(this.themes[themeIndex]);
|
|
||||||
}
|
}
|
||||||
setSettings('themeIndex', themeIndex);
|
|
||||||
|
applyTheme() {
|
||||||
|
this._clearTheme();
|
||||||
|
if (this.useSystemTheme) {
|
||||||
|
document.body.classList.add('system-theme');
|
||||||
|
} else if (this.themes[this.themeIndex]) {
|
||||||
|
document.body.classList.add(this.themes[this.themeIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTheme(themeIndex) {
|
||||||
this.themeIndex = themeIndex;
|
this.themeIndex = themeIndex;
|
||||||
|
setSettings('themeIndex', this.themeIndex);
|
||||||
|
this.applyTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleUseSystemTheme() {
|
||||||
|
this.useSystemTheme = !this.useSystemTheme;
|
||||||
|
setSettings('useSystemTheme', this.useSystemTheme);
|
||||||
|
this.applyTheme();
|
||||||
|
|
||||||
|
this.emit(cons.events.settings.SYSTEM_THEME_TOGGLED, this.useSystemTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUseSystemTheme() {
|
getUseSystemTheme() {
|
||||||
if (typeof this.useSystemTheme === 'boolean') return this.useSystemTheme;
|
if (typeof this.useSystemTheme === 'boolean') return this.useSystemTheme;
|
||||||
|
|
||||||
const settings = getSettings();
|
const settings = getSettings();
|
||||||
if (settings === null) return false;
|
if (settings === null) return true;
|
||||||
if (typeof settings.useSystemTheme === 'undefined') return false;
|
if (typeof settings.useSystemTheme === 'undefined') return true;
|
||||||
return settings.useSystemTheme;
|
return settings.useSystemTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,12 +150,7 @@ class Settings extends EventEmitter {
|
||||||
setter(action) {
|
setter(action) {
|
||||||
const actions = {
|
const actions = {
|
||||||
[cons.actions.settings.TOGGLE_SYSTEM_THEME]: () => {
|
[cons.actions.settings.TOGGLE_SYSTEM_THEME]: () => {
|
||||||
this.useSystemTheme = !this.useSystemTheme;
|
this.toggleUseSystemTheme();
|
||||||
|
|
||||||
setSettings('useSystemTheme', this.useSystemTheme);
|
|
||||||
this.setTheme(this.themeIndex);
|
|
||||||
|
|
||||||
this.emit(cons.events.settings.SYSTEM_THEME_TOGGLED, this.useSystemTheme);
|
|
||||||
},
|
},
|
||||||
[cons.actions.settings.TOGGLE_MARKDOWN]: () => {
|
[cons.actions.settings.TOGGLE_MARKDOWN]: () => {
|
||||||
this.isMarkdown = !this.isMarkdown;
|
this.isMarkdown = !this.isMarkdown;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import settings from './client/state/settings';
|
||||||
|
|
||||||
import App from './app/pages/App';
|
import App from './app/pages/App';
|
||||||
|
|
||||||
settings.setTheme(settings.getThemeIndex());
|
settings.applyTheme();
|
||||||
|
|
||||||
ReactDom.render(
|
ReactDom.render(
|
||||||
<App />,
|
<App />,
|
||||||
|
|
|
@ -20,7 +20,7 @@ export async function getBaseUrl(servername) {
|
||||||
if (baseUrl === undefined) throw new Error();
|
if (baseUrl === undefined) throw new Error();
|
||||||
return baseUrl;
|
return baseUrl;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`${protocol}${servername}`);
|
return `${protocol}${servername}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,3 +204,15 @@ export function getSSKeyInfo(key) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function hasDevices(userId) {
|
||||||
|
const mx = initMatrix.matrixClient;
|
||||||
|
try {
|
||||||
|
const usersDeviceMap = await mx.downloadKeys([userId, mx.getUserId()]);
|
||||||
|
return Object.values(usersDeviceMap)
|
||||||
|
.every((userDevices) => (Object.keys(userDevices).length > 0));
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error determining if it's possible to encrypt to all users: ", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ function transformSpanTag(tagName, attribs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformATag(tagName, attribs) {
|
function transformATag(tagName, attribs) {
|
||||||
const userLink = attribs.href.match(/^https?:\/\/matrix.to\/#\/(@.+:.+)/);
|
const userLink = decodeURIComponent(attribs.href).match(/^https?:\/\/matrix.to\/#\/(@.+:.+)/);
|
||||||
if (userLink !== null) {
|
if (userLink !== null) {
|
||||||
// convert user link to pill
|
// convert user link to pill
|
||||||
const userId = userLink[1];
|
const userId = userLink[1];
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* eslint-disable import/prefer-default-export */
|
/* eslint-disable import/prefer-default-export */
|
||||||
import React, { lazy, Suspense } from 'react';
|
import React, { lazy, Suspense } from 'react';
|
||||||
|
|
||||||
import linkifyHtml from 'linkifyjs/html';
|
import linkifyHtml from 'linkify-html';
|
||||||
import parse from 'html-react-parser';
|
import parse from 'html-react-parser';
|
||||||
import twemoji from 'twemoji';
|
import twemoji from 'twemoji';
|
||||||
import { sanitizeText } from './sanitize';
|
import { sanitizeText } from './sanitize';
|
||||||
|
|
Loading…
Reference in a new issue