add Nord Dark theme

This commit is contained in:
array-in-a-matrix 2023-03-20 13:40:32 -04:00
parent 111af25c3a
commit a4e0f7a2c8
3 changed files with 264 additions and 119 deletions

View file

@ -6,8 +6,12 @@ import cons from '../../../client/state/cons';
import settings from '../../../client/state/settings';
import navigation from '../../../client/state/navigation';
import {
toggleSystemTheme, toggleMarkdown, toggleMembershipEvents, toggleNickAvatarEvents,
toggleNotifications, toggleNotificationSounds,
toggleSystemTheme,
toggleMarkdown,
toggleMembershipEvents,
toggleNickAvatarEvents,
toggleNotifications,
toggleNotificationSounds,
} from '../../../client/action/settings';
import { usePermission } from '../../hooks/usePermission';
@ -53,17 +57,20 @@ function AppearanceSection() {
<MenuHeader>Theme</MenuHeader>
<SettingTile
title="Follow system theme"
options={(
options={
<Toggle
isActive={settings.useSystemTheme}
onToggle={() => { toggleSystemTheme(); updateState({}); }}
onToggle={() => {
toggleSystemTheme();
updateState({});
}}
/>
)}
}
content={<Text variant="b3">Use light or dark mode based on the system settings.</Text>}
/>
<SettingTile
title="Theme"
content={(
content={
<SegmentedControls
selected={settings.useSystemTheme ? -1 : settings.getThemeIndex()}
segments={[
@ -71,6 +78,7 @@ function AppearanceSection() {
{ text: 'Silver' },
{ text: 'Dark' },
{ text: 'Butter' },
{ text: 'Nord Dark' },
]}
onSelect={(index) => {
if (settings.useSystemTheme) toggleSystemTheme();
@ -78,40 +86,56 @@ function AppearanceSection() {
updateState({});
}}
/>
)}
}
/>
</div>
<div className="settings-appearance__card">
<MenuHeader>Room messages</MenuHeader>
<SettingTile
title="Markdown formatting"
options={(
options={
<Toggle
isActive={settings.isMarkdown}
onToggle={() => { toggleMarkdown(); updateState({}); }}
onToggle={() => {
toggleMarkdown();
updateState({});
}}
/>
)}
}
content={<Text variant="b3">Format messages with markdown syntax before sending.</Text>}
/>
<SettingTile
title="Hide membership events"
options={(
options={
<Toggle
isActive={settings.hideMembershipEvents}
onToggle={() => { toggleMembershipEvents(); updateState({}); }}
onToggle={() => {
toggleMembershipEvents();
updateState({});
}}
/>
)}
content={<Text variant="b3">Hide membership change messages from room timeline. (Join, Leave, Invite, Kick and Ban)</Text>}
}
content={
<Text variant="b3">
Hide membership change messages from room timeline. (Join, Leave, Invite, Kick and
Ban)
</Text>
}
/>
<SettingTile
title="Hide nick/avatar events"
options={(
options={
<Toggle
isActive={settings.hideNickAvatarEvents}
onToggle={() => { toggleNickAvatarEvents(); updateState({}); }}
onToggle={() => {
toggleNickAvatarEvents();
updateState({});
}}
/>
)}
content={<Text variant="b3">Hide nick and avatar change messages from room timeline.</Text>}
}
content={
<Text variant="b3">Hide nick and avatar change messages from room timeline.</Text>
}
/>
</div>
</div>
@ -119,13 +143,20 @@ function AppearanceSection() {
}
function NotificationsSection() {
const [permission, setPermission] = usePermission('notifications', window.Notification?.permission);
const [permission, setPermission] = usePermission(
'notifications',
window.Notification?.permission
);
const [, updateState] = useState({});
const renderOptions = () => {
if (window.Notification === undefined) {
return <Text className="settings-notifications__not-supported">Not supported in this browser.</Text>;
return (
<Text className="settings-notifications__not-supported">
Not supported in this browser.
</Text>
);
}
if (permission === 'granted') {
@ -162,12 +193,15 @@ function NotificationsSection() {
/>
<SettingTile
title="Notification Sound"
options={(
options={
<Toggle
isActive={settings.isNotificationSounds}
onToggle={() => { toggleNotificationSounds(); updateState({}); }}
onToggle={() => {
toggleNotificationSounds();
updateState({});
}}
/>
)}
}
content={<Text variant="b3">Play sound when new messages arrive.</Text>}
/>
</div>
@ -181,8 +215,12 @@ function NotificationsSection() {
function EmojiSection() {
return (
<>
<div className="settings-emoji__card"><ImagePackUser /></div>
<div className="settings-emoji__card"><ImagePackGlobal /></div>
<div className="settings-emoji__card">
<ImagePackUser />
</div>
<div className="settings-emoji__card">
<ImagePackGlobal />
</div>
</>
);
}
@ -200,21 +238,29 @@ function SecuritySection() {
<MenuHeader>Export/Import encryption keys</MenuHeader>
<SettingTile
title="Export E2E room keys"
content={(
content={
<>
<Text variant="b3">Export end-to-end encryption room keys to decrypt old messages in other session. In order to encrypt keys you need to set a password, which will be used while importing.</Text>
<Text variant="b3">
Export end-to-end encryption room keys to decrypt old messages in other session. In
order to encrypt keys you need to set a password, which will be used while
importing.
</Text>
<ExportE2ERoomKeys />
</>
)}
}
/>
<SettingTile
title="Import E2E room keys"
content={(
content={
<>
<Text variant="b3">{'To decrypt older messages, Export E2EE room keys from Element (Settings > Security & Privacy > Encryption > Cryptography) and import them here. Imported keys are encrypted so you\'ll have to enter the password you set in order to decrypt it.'}</Text>
<Text variant="b3">
{
"To decrypt older messages, Export E2EE room keys from Element (Settings > Security & Privacy > Encryption > Cryptography) and import them here. Imported keys are encrypted so you'll have to enter the password you set in order to decrypt it."
}
</Text>
<ImportE2ERoomKeys />
</>
)}
}
/>
</div>
</div>
@ -231,14 +277,21 @@ function AboutSection() {
<div>
<Text variant="h2" weight="medium">
Cinny
<span className="text text-b3" style={{ margin: '0 var(--sp-extra-tight)' }}>{`v${cons.version}`}</span>
<span
className="text text-b3"
style={{ margin: '0 var(--sp-extra-tight)' }}
>{`v${cons.version}`}</span>
</Text>
<Text>Yet another matrix client</Text>
<div className="settings-about__btns">
<Button onClick={() => window.open('https://github.com/ajbura/cinny')}>Source code</Button>
<Button onClick={() => window.open('https://github.com/ajbura/cinny')}>
Source code
</Button>
<Button onClick={() => window.open('https://cinny.in/#sponsor')}>Support</Button>
<Button onClick={() => initMatrix.clearCacheAndReload()} variant="danger">Clear cache & reload</Button>
<Button onClick={() => initMatrix.clearCacheAndReload()} variant="danger">
Clear cache & reload
</Button>
</div>
</div>
</div>
@ -248,16 +301,78 @@ function AboutSection() {
<div className="settings-about__credits">
<ul>
<li>
{/* eslint-disable-next-line react/jsx-one-expression-per-line */ }
<Text>The <a href="https://github.com/matrix-org/matrix-js-sdk" rel="noreferrer noopener" target="_blank">matrix-js-sdk</a> is © <a href="https://matrix.org/foundation" rel="noreferrer noopener" target="_blank">The Matrix.org Foundation C.I.C</a> used under the terms of <a href="http://www.apache.org/licenses/LICENSE-2.0" rel="noreferrer noopener" target="_blank">Apache 2.0</a>.</Text>
{/* eslint-disable-next-line react/jsx-one-expression-per-line */}
<Text>
The{' '}
<a
href="https://github.com/matrix-org/matrix-js-sdk"
rel="noreferrer noopener"
target="_blank"
>
matrix-js-sdk
</a>{' '}
is ©{' '}
<a href="https://matrix.org/foundation" rel="noreferrer noopener" target="_blank">
The Matrix.org Foundation C.I.C
</a>{' '}
used under the terms of{' '}
<a
href="http://www.apache.org/licenses/LICENSE-2.0"
rel="noreferrer noopener"
target="_blank"
>
Apache 2.0
</a>
.
</Text>
</li>
<li>
{/* eslint-disable-next-line react/jsx-one-expression-per-line */ }
<Text>The <a href="https://twemoji.twitter.com" target="_blank" rel="noreferrer noopener">Twemoji</a> emoji art is © <a href="https://twemoji.twitter.com" target="_blank" rel="noreferrer noopener">Twitter, Inc and other contributors</a> used under the terms of <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noreferrer noopener">CC-BY 4.0</a>.</Text>
{/* eslint-disable-next-line react/jsx-one-expression-per-line */}
<Text>
The{' '}
<a href="https://twemoji.twitter.com" target="_blank" rel="noreferrer noopener">
Twemoji
</a>{' '}
emoji art is ©{' '}
<a href="https://twemoji.twitter.com" target="_blank" rel="noreferrer noopener">
Twitter, Inc and other contributors
</a>{' '}
used under the terms of{' '}
<a
href="https://creativecommons.org/licenses/by/4.0/"
target="_blank"
rel="noreferrer noopener"
>
CC-BY 4.0
</a>
.
</Text>
</li>
<li>
{/* eslint-disable-next-line react/jsx-one-expression-per-line */ }
<Text>The <a href="https://material.io/design/sound/sound-resources.html" target="_blank" rel="noreferrer noopener">Material sound resources</a> are © <a href="https://google.com" target="_blank" rel="noreferrer noopener">Google</a> used under the terms of <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noreferrer noopener">CC-BY 4.0</a>.</Text>
{/* eslint-disable-next-line react/jsx-one-expression-per-line */}
<Text>
The{' '}
<a
href="https://material.io/design/sound/sound-resources.html"
target="_blank"
rel="noreferrer noopener"
>
Material sound resources
</a>{' '}
are ©{' '}
<a href="https://google.com" target="_blank" rel="noreferrer noopener">
Google
</a>{' '}
used under the terms of{' '}
<a
href="https://creativecommons.org/licenses/by/4.0/"
target="_blank"
rel="noreferrer noopener"
>
CC-BY 4.0
</a>
.
</Text>
</li>
</ul>
</div>
@ -273,32 +388,38 @@ export const tabText = {
SECURITY: 'Security',
ABOUT: 'About',
};
const tabItems = [{
text: tabText.APPEARANCE,
iconSrc: SunIC,
disabled: false,
render: () => <AppearanceSection />,
}, {
text: tabText.NOTIFICATIONS,
iconSrc: BellIC,
disabled: false,
render: () => <NotificationsSection />,
}, {
text: tabText.EMOJI,
iconSrc: EmojiIC,
disabled: false,
render: () => <EmojiSection />,
}, {
text: tabText.SECURITY,
iconSrc: LockIC,
disabled: false,
render: () => <SecuritySection />,
}, {
text: tabText.ABOUT,
iconSrc: InfoIC,
disabled: false,
render: () => <AboutSection />,
}];
const tabItems = [
{
text: tabText.APPEARANCE,
iconSrc: SunIC,
disabled: false,
render: () => <AppearanceSection />,
},
{
text: tabText.NOTIFICATIONS,
iconSrc: BellIC,
disabled: false,
render: () => <NotificationsSection />,
},
{
text: tabText.EMOJI,
iconSrc: EmojiIC,
disabled: false,
render: () => <EmojiSection />,
},
{
text: tabText.SECURITY,
iconSrc: LockIC,
disabled: false,
render: () => <SecuritySection />,
},
{
text: tabText.ABOUT,
iconSrc: InfoIC,
disabled: false,
render: () => <AboutSection />,
},
];
function useWindowToggle(setSelectedTab) {
const [isOpen, setIsOpen] = useState(false);
@ -326,7 +447,14 @@ function Settings() {
const handleTabChange = (tabItem) => setSelectedTab(tabItem);
const handleLogout = async () => {
if (await confirmDialog('Logout', 'Are you sure that you want to logout your session?', 'Logout', 'danger')) {
if (
await confirmDialog(
'Logout',
'Are you sure that you want to logout your session?',
'Logout',
'danger'
)
) {
initMatrix.logout();
}
};
@ -335,15 +463,19 @@ function Settings() {
<PopupWindow
isOpen={isOpen}
className="settings-window"
title={<Text variant="s1" weight="medium" primary>Settings</Text>}
contentOptions={(
title={
<Text variant="s1" weight="medium" primary>
Settings
</Text>
}
contentOptions={
<>
<Button variant="danger" iconSrc={PowerIC} onClick={handleLogout}>
Logout
</Button>
<IconButton src={CrossIC} onClick={requestClose} tooltip="Close" />
</>
)}
}
onRequestClose={requestClose}
>
{isOpen && (
@ -354,9 +486,7 @@ function Settings() {
defaultSelected={tabItems.findIndex((tab) => tab.text === selectedTab.text)}
onSelect={handleTabChange}
/>
<div className="settings-window__cards-wrapper">
{ selectedTab.render() }
</div>
<div className="settings-window__cards-wrapper">{selectedTab.render()}</div>
</div>
)}
</PopupWindow>

View file

@ -20,7 +20,7 @@ class Settings extends EventEmitter {
constructor() {
super();
this.themes = ['', 'silver-theme', 'dark-theme', 'butter-theme'];
this.themes = ['', 'silver-theme', 'dark-theme', 'butter-theme', 'nord-dark-theme'];
this.themeIndex = this.getThemeIndex();
this.useSystemTheme = this.getUseSystemTheme();

View file

@ -1,14 +1,13 @@
@use './app/partials/screen';
:root {
/* background color | --bg-[background type]: value */
--bg-surface: #FFFFFF;
--bg-surface-transparent: #FFFFFF00;
--bg-surface-low: #F6F6F6;
--bg-surface-low-transparent: #F6F6F600;
--bg-surface-extra-low: #F6F6F6;
--bg-surface-extra-low-transparent: #F6F6F600;
--bg-surface: #ffffff;
--bg-surface-transparent: #ffffff00;
--bg-surface-low: #f6f6f6;
--bg-surface-low-transparent: #f6f6f600;
--bg-surface-extra-low: #f6f6f6;
--bg-surface-extra-low-transparent: #f6f6f600;
--bg-surface-hover: rgba(0, 0, 0, 3%);
--bg-surface-active: rgba(0, 0, 0, 5%);
--bg-surface-border: rgba(0, 0, 0, 6%);
@ -22,7 +21,7 @@
--bg-positive-hover: rgba(69, 184, 59, 8%);
--bg-positive-active: rgba(69, 184, 59, 15%);
--bg-positive-border: rgba(69, 184, 59, 40%);
--bg-caution: rgb(255, 179, 0);
--bg-caution-hover: rgba(255, 179, 0, 8%);
--bg-caution-active: rgba(255, 179, 0, 15%);
@ -37,18 +36,18 @@
--bg-badge: #989898;
--bg-ping: hsla(137deg, 100%, 68%, 40%);
--bg-ping-hover: hsla(137deg, 100%, 68%, 50%);
--bg-divider: hsla(0, 0%, 0%, .1);
--bg-divider: hsla(0, 0%, 0%, 0.1);
/* text color | --tc-[background type]-[priority]: value */
--tc-surface-high: #000000;
--tc-surface-normal: rgba(0, 0, 0, 78%);
--tc-surface-normal-low: rgba(0, 0, 0, 60%);
--tc-surface-low: rgba(0, 0, 0, 48%);
--tc-primary-high: #ffffff;
--tc-primary-normal: rgba(255, 255, 255, 68%);
--tc-primary-low: rgba(255, 255, 255, 40%);
--tc-positive-high: var(--bg-positive);
--tc-positive-normal: rgb(69, 184, 59, 80%);
--tc-positive-low: rgb(69, 184, 59, 60%);
@ -56,7 +55,7 @@
--tc-caution-high: var(--bg-caution);
--tc-caution-normal: rgb(255, 179, 0, 80%);
--tc-caution-low: rgb(255, 179, 0, 60%);
--tc-danger-high: var(--bg-danger);
--tc-danger-normal: rgba(240, 71, 71, 88%);
--tc-danger-low: rgba(240, 71, 71, 60%);
@ -66,7 +65,6 @@
--tc-tooltip: white;
--tc-badge: white;
/* system icons | --ic-[background type]-[priority]: value */
--ic-surface-high: #272727;
--ic-surface-normal: #626262;
@ -102,7 +100,6 @@
--av-small: 36px;
--av-extra-small: 24px;
/* shadow and overlay */
--bg-overlay: rgba(0, 0, 0, 20%);
--bg-overlay-low: rgba(0, 0, 0, 50%);
@ -124,11 +121,9 @@
--bs-danger-border: inset 0 0 0 1px var(--bg-danger-border);
--bs-danger-outline: 0 0 0 2px var(--bg-danger-border);
/* border */
--bo-radius: 8px;
/* font styles: font-size, letter-spacing, line-hight */
--fs-h1: 36px;
--ls-h1: -1.5px;
@ -160,7 +155,6 @@
--fw-medium: 500;
--fw-bold: 700;
/* spacing | --sp-[space]: value */
--sp-none: 0px;
--sp-ultra-tight: 4px;
@ -170,7 +164,6 @@
--sp-loose: 20px;
--sp-extra-loose: 32px;
/* other */
--border-width: 1px;
--header-height: 54px;
@ -180,7 +173,7 @@
--people-drawer-width: calc(268px - var(--border-width));
--popup-window-drawer-width: 280px;
@include screen.smallerThan(tabletBreakpoint) {
--navigation-drawer-width: calc(240px + var(--border-width));
--people-drawer-width: calc(256px - var(--border-width));
@ -191,12 +184,11 @@
--fluid-push: cubic-bezier(0, 0.8, 0.67, 0.97);
--fluid-slide-down: cubic-bezier(0.02, 0.82, 0.4, 0.96);
--fluid-slide-up: cubic-bezier(0.13, 0.56, 0.25, 0.99);
--font-primary: 'Roboto', sans-serif;
--font-secondary: 'Roboto', sans-serif;
}
.silver-theme {
/* background color | --bg-[background type]: value */
--bg-surface: hsl(0, 0%, 95%);
@ -228,15 +220,14 @@
--bg-badge: hsl(0, 0%, 75%);
--bg-ping: hsla(137deg, 100%, 38%, 40%);
--bg-ping-hover: hsla(137deg, 100%, 38%, 50%);
--bg-divider: hsla(0, 0%, 100%, .1);
--bg-divider: hsla(0, 0%, 100%, 0.1);
/* text color | --tc-[background type]-[priority]: value */
--tc-surface-high: rgba(255, 255, 255, 98%);
--tc-surface-normal: rgba(255, 255, 255, 94%);
--tc-surface-normal-low: rgba(255, 255, 255, 60%);
--tc-surface-low: rgba(255, 255, 255, 58%);
--tc-primary-high: #ffffff;
--tc-primary-normal: rgba(255, 255, 255, 0.68);
--tc-primary-low: rgba(255, 255, 255, 0.4);
@ -262,7 +253,7 @@
--mx-uc-7: hsl(243, 100%, 74%);
--mx-uc-8: hsl(94, 66%, 50%);
}
/* shadow and overlay */
--bg-overlay: rgba(0, 0, 0, 60%);
--bg-overlay-low: rgba(0, 0, 0, 80%);
@ -274,7 +265,7 @@
--bs-primary-border: inset 0 0 0 1px var(--bg-primary-border);
--bs-primary-outline: 0 0 0 2px var(--bg-primary-border);
/* font styles: font-size, letter-spacing, line-hight */
--fs-h1: 35.6px;
@ -296,7 +287,8 @@
}
.dark-theme,
.butter-theme {
.butter-theme,
.nord-dark-theme {
@include dark-mode();
}
@ -317,20 +309,41 @@
--bg-badge: #c4c1ab;
/* text color | --tc-[background type]-[priority]: value */
--tc-surface-high: rgb(255, 251, 222, 94%);
--tc-surface-normal: rgba(255, 251, 222, 94%);
--tc-surface-normal-low: rgba(255, 251, 222, 60%);
--tc-surface-normal-low: rgba(255, 251, 222, 60%);
--tc-surface-low: rgba(255, 251, 222, 58%);
/* system icons | --ic-[background type]-[priority]: value */
--ic-surface-high: rgb(255, 251, 222);
--ic-surface-normal: rgba(255, 251, 222, 84%);
--ic-surface-low: rgba(255, 251, 222, 64%);
}
.nord-dark-theme {
/* background color | --bg-[background type]: value */
--bg-surface: hsl(220, 16%, 22%);
--bg-surface-transparent: hsl(220, 16%, 22%, 0%);
--bg-surface-low: hsl(222, 16%, 28%);
--bg-surface-low-transparent: hsl(222, 16%, 28%, 0%);
--bg-surface-extra-low: hsl(220, 17%, 32%);
--bg-surface-extra-low-transparent: hsl(220, 17%, 32%, 0%);
--bg-badge: #5e81ac;
/* text color | --tc-[background type]-[priority]: value */
--tc-surface-high: rgba(216, 222, 233, 94%);
--tc-surface-normal: rgba(216, 222, 233, 94%);
--tc-surface-normal-low: rgba(216, 222, 233, 60%);
--tc-surface-low: rgba(216, 222, 233, 58%);
/* system icons | --ic-[background type]-[priority]: value */
--ic-surface-high: rgb(216, 222, 233);
--ic-surface-normal: rgba(216, 222, 233, 84%);
--ic-surface-low: rgba(216, 222, 233, 64%);
}
.font-primary {
font-family: var(--font-primary);
@ -387,9 +400,11 @@ body {
height: 100%;
}
*, *::before, *::after {
*,
*::before,
*::after {
box-sizing: border-box;
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-tap-highlight-color: transparent;
}
a {
@ -428,16 +443,16 @@ button {
textarea,
input,
input[type],
input[type=text],
input[type=username],
input[type=password],
input[type=email],
input[type=checkbox] {
input[type='text'],
input[type='username'],
input[type='password'],
input[type='email'],
input[type='checkbox'] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
input[type=checkbox] {
input[type='checkbox'] {
margin: 0;
padding: 0;
width: 20px;
@ -451,7 +466,7 @@ input[type=checkbox] {
&:checked {
background-color: var(--bg-primary);
&::before {
content: "";
content: '';
display: inline-block;
width: 12px;
height: 6px;
@ -468,11 +483,11 @@ textarea {
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
}
@ -484,4 +499,4 @@ audio:not([controls]) {
display: flex;
justify-content: center;
align-items: center;
}
}