Dark Theme

This commit is contained in:
Alexander Zinchuk 2021-04-12 17:52:01 +03:00
parent b37ec8bca2
commit ee1b137d0e
67 changed files with 527 additions and 131 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
node_modules
dist
.cache
.env
src/lib/gramjs/build/

View File

@ -1,10 +1,12 @@
import React, {
FC, memo, useCallback, useEffect, useMemo, useRef, useState,
} from '../../lib/teact/teact';
import { withGlobal } from '../../lib/teact/teactn';
import {
ApiAudio, ApiMessage, ApiVoice,
} from '../../api/types';
import { ISettings } from '../../types';
import { IS_MOBILE_SCREEN } from '../../util/environment';
import { formatMediaDateTime, formatMediaDuration, formatPastTimeShort } from '../../util/dateFormat';
@ -49,6 +51,10 @@ type OwnProps = {
onDateClick?: (messageId: number, chatId: number) => void;
};
type StateProps = {
theme: ISettings['theme'];
};
interface ISeekMethods {
handleStartSeek: (e: React.MouseEvent<HTMLElement>) => void;
handleSeek: (e: React.MouseEvent<HTMLElement>) => void;
@ -61,7 +67,8 @@ const MAX_SPIKES = IS_MOBILE_SCREEN ? 50 : 75;
// This is needed for browsers requiring user interaction before playing.
const PRELOAD = true;
const Audio: FC<OwnProps> = ({
const Audio: FC<OwnProps & StateProps> = ({
theme,
message,
senderTitle,
uploadProgress,
@ -201,8 +208,8 @@ const Audio: FC<OwnProps> = ({
const seekHandlers = { handleStartSeek, handleSeek, handleStopSeek };
const isOwn = isOwnMessage(message);
const renderedWaveform = useMemo(
() => voice && renderWaveform(voice, playProgress, isOwn, seekHandlers),
[voice, playProgress, isOwn, seekHandlers],
() => voice && renderWaveform(voice, playProgress, isOwn, seekHandlers, theme),
[voice, playProgress, isOwn, seekHandlers, theme],
);
const fullClassName = buildClassName(
@ -346,7 +353,11 @@ function renderVoice(voice: ApiVoice, renderedWaveform: any, isMediaUnread?: boo
}
function renderWaveform(
voice: ApiVoice, playProgress = 0, isOwn = false, { handleStartSeek, handleSeek, handleStopSeek }: ISeekMethods,
voice: ApiVoice,
playProgress = 0,
isOwn = false,
{ handleStartSeek, handleSeek, handleStopSeek }: ISeekMethods,
theme: ISettings['theme'],
) {
const { waveform, duration } = voice;
@ -354,14 +365,18 @@ function renderWaveform(
return undefined;
}
const fillColor = theme === 'dark' ? '#494B75' : '#CBCBCB';
const fillOwnColor = theme === 'dark' ? '#C69C85' : '#B0DEA6';
const progressFillColor = theme === 'dark' ? '#868DF5' : '#54a3e6';
const progressFillOwnColor = theme === 'dark' ? '#FFFFFF' : '#53ad53';
const durationFactor = Math.min(duration / AVG_VOICE_DURATION, 1);
const spikesCount = Math.round(MIN_SPIKES + (MAX_SPIKES - MIN_SPIKES) * durationFactor);
const decodedWaveform = decodeWaveform(new Uint8Array(waveform));
const { data: spikes, peak } = interpolateArray(decodedWaveform, spikesCount);
const { src, width, height } = renderWaveformToDataUri(spikes, playProgress, {
peak,
fillStyle: isOwn ? '#B0DEA6' : '#CBCBCB',
progressFillStyle: isOwn ? '#53ad53' : '#54a3e6',
fillStyle: isOwn ? fillOwnColor : fillColor,
progressFillStyle: isOwn ? progressFillOwnColor : progressFillColor,
});
return (
@ -414,4 +429,4 @@ function renderSeekline(
);
}
export default memo(Audio);
export default memo(withGlobal<OwnProps>((global) => ({ theme: global.settings.byKey.theme }))(Audio));

View File

@ -6,7 +6,7 @@
width: 3.375rem;
height: 3.375rem;
border-radius: 50%;
background: linear-gradient(white -125%, var(--color-user));
background: linear-gradient(var(--color-white) -125%, var(--color-user));
color: white;
font-weight: bold;
display: flex;
@ -109,7 +109,7 @@
width: 0.875rem;
height: 0.875rem;
border-radius: 50%;
border: 2px solid white;
border: 2px solid var(--color-background);
background-color: #0ac630;
flex-shrink: 0;
}

View File

@ -21,7 +21,7 @@
padding: 0.5rem;
margin: 0;
background-color: var(--background-color);
box-shadow: 0 1px 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 1px 2px var(--color-default-shadow);
&::before {
left: .625rem;

View File

@ -158,7 +158,7 @@
}
&.smaller {
--background-color: #fff;
--background-color: var(--color-background);
--border-radius-messages-small: .3125rem;
.icon-download,

View File

@ -8,7 +8,7 @@
align-items: center;
.MessageOutgoingStatus {
color: var(--color-text-green);
color: var(--color-text-meta-colored);
margin-right: 0.1rem;
font-size: 1.15rem;
}

View File

@ -32,7 +32,7 @@
.button-wrapper {
padding: 0.5rem 0;
border-top: 1px solid var(--color-borders);
box-shadow: 0 0 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 0 2px var(--color-default-shadow);
button {
display: inline-block;

View File

@ -18,7 +18,7 @@
.left {
flex: 1;
background: white;
background: var(--color-background);
min-width: 15.5rem;
max-width: 26.5rem;
@ -56,10 +56,19 @@
left: 0;
bottom: 0;
right: 0;
background: rgb(230, 235, 238) url('../../assets/chat-bg.jpg') no-repeat center;
background: no-repeat center;
background-size: cover;
z-index: -1;
transform-origin: left center;
.theme-dark body.initial & {
background-color: #0f0f0f;
}
.theme-light body.initial &,
body:not(.initial) & {
background-image: url('../../assets/chat-bg.jpg');
}
}
&.with-right-column::before {
@ -97,12 +106,12 @@
min-width: 15.5rem;
max-width: 26.5rem;
border-left: 1px solid var(--color-borders);
background: white;
background: var(--color-background);
}
}
.blank {
flex: 1;
background: white;
background: var(--color-background);
}
}

View File

@ -32,6 +32,7 @@ type OwnProps = {
type StateProps = Pick<GlobalState, 'uiReadyState'> & {
hasCustomBackground?: boolean;
isCustomBackgroundColor: boolean;
isRightColumnShown?: boolean;
};
@ -84,6 +85,7 @@ const UiLoader: FC<OwnProps & StateProps & DispatchProps> = ({
page,
children,
hasCustomBackground,
isCustomBackgroundColor,
isRightColumnShown,
setIsUiReady,
}) => {
@ -129,7 +131,8 @@ const UiLoader: FC<OwnProps & StateProps & DispatchProps> = ({
<div
className={buildClassName(
'middle',
hasCustomBackground && 'custom-bg-image',
hasCustomBackground && !isCustomBackgroundColor && 'custom-bg-image',
hasCustomBackground && isCustomBackgroundColor && 'custom-bg-color',
isRightColumnShown && 'with-right-column',
)}
/>
@ -149,6 +152,7 @@ export default withGlobal<OwnProps>(
return {
uiReadyState: global.uiReadyState,
hasCustomBackground: Boolean(global.settings.byKey.customBackground),
isCustomBackgroundColor: Boolean((global.settings.byKey.customBackground || '').match(/^#[a-f\d]{6,8}$/i)),
isRightColumnShown: selectIsRightColumnShown(global),
};
},

View File

@ -1,5 +1,5 @@
.Chat {
--background-color: white;
--background-color: var(--color-background);
position: absolute;
top: 0;

View File

@ -118,7 +118,6 @@ const LeftMain: FC<OwnProps & StateProps> = ({
onSearchQuery={onSearchQuery}
onSelectSettings={handleSelectSettings}
onSelectContacts={handleSelectContacts}
onSelectNewGroup={handleSelectNewGroup}
onSelectArchived={handleSelectArchived}
onReset={onReset}
/>

View File

@ -45,7 +45,7 @@
.archived-badge {
min-width: 1.5rem;
height: 1.5rem;
margin-left: 2rem;
margin-left: auto;
background: var(--color-gray);
border-radius: 0.75rem;
padding: 0 .45rem;

View File

@ -4,7 +4,7 @@ import React, {
import { withGlobal } from '../../../lib/teact/teactn';
import { GlobalActions } from '../../../global/types';
import { LeftColumnContent } from '../../../types';
import { LeftColumnContent, ISettings } from '../../../types';
import { ApiChat } from '../../../api/types';
import { IS_MOBILE_SCREEN } from '../../../util/environment';
@ -12,6 +12,7 @@ import buildClassName from '../../../util/buildClassName';
import { pick } from '../../../util/iteratees';
import { isChatArchived } from '../../../modules/helpers';
import { formatDateToString } from '../../../util/dateFormat';
import switchTheme from '../../../util/switchTheme';
import useLang from '../../../hooks/useLang';
import DropdownMenu from '../../ui/DropdownMenu';
@ -19,6 +20,7 @@ import MenuItem from '../../ui/MenuItem';
import Button from '../../ui/Button';
import SearchInput from '../../ui/SearchInput';
import PickerSelectedItem from '../../common/PickerSelectedItem';
import Switcher from '../../ui/Switcher';
import './LeftMainHeader.scss';
@ -28,7 +30,6 @@ type OwnProps = {
onSearchQuery: (query: string) => void;
onSelectSettings: () => void;
onSelectContacts: () => void;
onSelectNewGroup: () => void;
onSelectArchived: () => void;
onReset: () => void;
};
@ -39,11 +40,15 @@ type StateProps = {
currentUserId?: number;
globalSearchChatId?: number;
searchDate?: number;
theme: ISettings['theme'];
animationLevel: 0 | 1 | 2;
chatsById?: Record<number, ApiChat>;
};
type DispatchProps = Pick<GlobalActions,
'openChat'| 'openSupportChat' | 'setGlobalSearchDate' | 'setGlobalSearchChatId'>;
'openChat'| 'openSupportChat' | 'setGlobalSearchDate' | 'setGlobalSearchChatId' | 'setSettingOption'>;
const ANIMATION_LEVEL_OPTIONS = [0, 1, 2];
const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
content,
@ -51,7 +56,6 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
onSearchQuery,
onSelectSettings,
onSelectContacts,
onSelectNewGroup,
onSelectArchived,
setGlobalSearchChatId,
onReset,
@ -60,10 +64,13 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
currentUserId,
globalSearchChatId,
searchDate,
theme,
animationLevel,
chatsById,
openChat,
openSupportChat,
setGlobalSearchDate,
setSettingOption,
}) => {
const hasMenu = content === LeftColumnContent.ChatList;
const clearedDateSearchParam = { date: undefined };
@ -113,6 +120,28 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
openChat({ id: currentUserId });
}, [currentUserId, openChat]);
const handleDarkModeToggle = useCallback((e: React.SyntheticEvent<HTMLDivElement>) => {
e.stopPropagation();
const newTheme = theme === 'light' ? 'dark' : 'light';
setSettingOption({
theme: newTheme,
customBackground: newTheme === 'dark' ? '#0F0F0F' : undefined,
});
switchTheme(newTheme, animationLevel > 0);
}, [animationLevel, setSettingOption, theme]);
const handleAnimationLevelChange = useCallback((e: React.SyntheticEvent<HTMLDivElement>) => {
e.stopPropagation();
const newLevel = animationLevel === 0 ? 2 : 0;
ANIMATION_LEVEL_OPTIONS.forEach((_, i) => {
document.body.classList.toggle(`animation-level-${i}`, newLevel === i);
});
setSettingOption({ animationLevel: newLevel });
}, [animationLevel, setSettingOption]);
const lang = useLang();
const isSearchFocused = Boolean(globalSearchChatId)
@ -130,10 +159,19 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
trigger={MainButton}
>
<MenuItem
icon="group"
onClick={onSelectNewGroup}
icon="saved-messages"
onClick={handleSelectSaved}
>
{lang('NewGroup')}
{lang('SavedMessages')}
</MenuItem>
<MenuItem
icon="archive"
onClick={onSelectArchived}
>
<span className="menu-item-name">{lang('ArchivedChats')}</span>
{archivedUnreadChatsCount > 0 && (
<div className="archived-badge">{archivedUnreadChatsCount}</div>
)}
</MenuItem>
<MenuItem
icon="user"
@ -141,27 +179,34 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
>
{lang('Contacts')}
</MenuItem>
<MenuItem
icon="archive"
onClick={onSelectArchived}
>
{lang('Archived')}
{archivedUnreadChatsCount > 0 && (
<div className="archived-badge">{archivedUnreadChatsCount}</div>
)}
</MenuItem>
<MenuItem
icon="saved-messages"
onClick={handleSelectSaved}
>
{lang('Saved')}
</MenuItem>
<MenuItem
icon="settings"
onClick={onSelectSettings}
>
{lang('Settings')}
</MenuItem>
<MenuItem
icon="darkmode"
onClick={handleDarkModeToggle}
>
<span className="menu-item-name">Dark Mode</span>
<Switcher
id="darkmode"
label="Toggle Dark Mode"
checked={theme === 'dark'}
/>
</MenuItem>
<MenuItem
icon="animations"
onClick={handleAnimationLevelChange}
>
<span className="menu-item-name">{lang('SettingsSearch.Synonyms.Appearance.Animations')}</span>
<Switcher
id="animations"
label="Toggle Animations"
checked={animationLevel > 0}
/>
</MenuItem>
<MenuItem
icon="help"
onClick={openSupportChat}
@ -213,6 +258,7 @@ export default memo(withGlobal<OwnProps>(
} = global.globalSearch;
const { currentUserId } = global;
const { byId: chatsById } = global.chats;
const { theme, animationLevel } = global.settings.byKey;
return {
searchQuery,
@ -221,6 +267,8 @@ export default memo(withGlobal<OwnProps>(
chatsById,
globalSearchChatId: chatId,
searchDate: date,
theme,
animationLevel,
};
},
(setGlobal, actions): DispatchProps => pick(actions, [
@ -228,5 +276,6 @@ export default memo(withGlobal<OwnProps>(
'openSupportChat',
'setGlobalSearchDate',
'setGlobalSearchChatId',
'setSettingOption',
]),
)(LeftMainHeader));

View File

@ -163,7 +163,7 @@
justify-content: space-between;
align-items: flex-end;
box-shadow: inset 0 -1px 0 0 var(--color-borders);
background-color: white;
background-color: var(--color-background);
-webkit-overflow-scrolling: touch;
overflow-x: auto;
overflow-y: hidden;

View File

@ -31,7 +31,7 @@
left: -0.75rem;
width: 1.5rem;
height: 1.5rem;
border: 0.125rem solid white;
border: 0.125rem solid var(--color-white);
border-radius: 0.75rem;
cursor: grab;
}
@ -66,12 +66,12 @@
div {
cursor: pointer;
box-shadow: inset 0 0 0 0 white;
box-shadow: inset 0 0 0 0 var(--color-background);
transition: box-shadow 300ms ease;
&.active {
border: 0.125rem solid var(--color-primary);
box-shadow: inset 0 0 0 0.3125rem white;
box-shadow: inset 0 0 0 0.3125rem var(--color-background);
}
// A hack to make a square

View File

@ -2,7 +2,7 @@
height: calc(100% - var(--header-height));
.picker-header {
box-shadow: 0 0 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 0 2px var(--color-default-shadow);
.max-items-reached {
margin-bottom: 0.5rem;

View File

@ -9,6 +9,7 @@ import { pick } from '../../util/iteratees';
import { selectIsForwardModalOpen, selectIsMediaViewerOpen, selectIsRightColumnShown } from '../../modules/selectors';
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
import useShowTransition from '../../hooks/useShowTransition';
import buildClassName from '../../util/buildClassName';
import LeftColumn from '../left/LeftColumn';
import MiddleColumn from '../middle/MiddleColumn';
@ -19,7 +20,6 @@ import Notifications from './Notifications.async';
import Errors from './Errors.async';
import './Main.scss';
import buildClassName from '../../util/buildClassName';
type StateProps = {
animationLevel: number;

View File

@ -74,7 +74,7 @@
width: .75rem;
height: .75rem;
border-radius: 50%;
background-color: #fff;
background-color: var(--color-white);
right: 0;
top: 50%;
transform: translate(.325rem, -50%);

View File

@ -1,7 +1,7 @@
const units = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'];
export default (bytes: number) => {
const number = Math.floor(Math.log(bytes) / Math.log(1024));
const number = bytes === 0 ? 0 : Math.floor(Math.log(bytes) / Math.log(1024));
return `${(bytes / 1024 ** Math.floor(number)).toFixed(1)} ${units[number]}`;
};

View File

@ -5,14 +5,17 @@
overflow: scroll;
overflow-x: hidden;
overflow-y: overlay;
padding-bottom: .3125rem;
body.hide-mask-shadow .mask-image-disabled &,
.mask-image-enabled & {
mask-image: linear-gradient(to top, transparent 0, #000 1rem);
}
.custom-bg-color.mask-image-disabled &,
.custom-bg-image.mask-image-disabled & {
margin-bottom: .3rem;
margin-bottom: .3125rem;
padding-bottom: 0;
}
@media (pointer: coarse) {

View File

@ -77,9 +77,9 @@
align-items: center;
padding: 0.25rem;
background: white;
background: var(--color-background);
border-radius: var(--border-radius-messages);
box-shadow: 0 1px 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 1px 2px var(--color-default-shadow);
font-weight: 500;
@media (max-width: 600px) {

View File

@ -7,7 +7,8 @@
overflow: hidden;
z-index: -1;
&::before {
&::before,
&::after {
content: "";
display: block;
position: absolute;
@ -15,13 +16,18 @@
left: 0;
bottom: 0;
right: 0;
background-color: rgb(230, 235, 238);
background-color: #F2EBCE;
}
&::after {
background-image: url('../../assets/chat-bg.jpg');
background-position: center;
background-repeat: no-repeat;
background-size: cover;
transition: background-image .3s ease;
.disable-animations #root & {
transition: opacity .2s !important;
}
body.animation-level-0 & {
transition: none;
@ -32,19 +38,29 @@
}
}
.custom-bg-image > &::before {
.custom-bg-image > &::after {
background-image: var(--custom-background) !important;
filter: blur(0);
transform: scale(1.1);
}
.custom-bg-color > &::before {
background-color: var(--custom-background) !important;
filter: blur(0);
transform: scale(1.1);
}
.custom-bg-image.blurred > &::before {
.custom-bg-image.blurred > &::after {
filter: blur(12px);
}
.custom-bg-color > &::after {
opacity: 0;
}
@media screen and (min-width: 1276px) {
body.animation-level-2 &::before {
body.animation-level-2 &::before,
body.animation-level-2 &::after {
margin: -16rem -5rem -20rem 0;
overflow: hidden;
transform: scale(1);
@ -52,12 +68,12 @@
transition: transform var(--layer-transition);
}
body.animation-level-2 .custom-bg-image > &::before {
body.animation-level-2 .custom-bg-image > &::after {
margin: -16rem -5rem -20rem -1rem;
transition: transform var(--layer-transition), background .3s ease;
transition: transform var(--layer-transition);
}
body.animation-level-2 #Main.right-column-open :not(.custom-bg-image) > &::before {
body.animation-level-2 #Main.right-column-open :not(.custom-bg-image) > &::after {
transform: scale(0.67);
}
}
@ -101,10 +117,10 @@
width: 100%;
padding: 1rem;
border-radius: var(--border-radius-messages);
background: white;
background: var(--color-background);
color: var(--color-text-secondary);
text-align: center;
box-shadow: 0 1px 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 1px 2px var(--color-default-shadow);
}
}
@ -312,6 +328,7 @@
}
}
.custom-bg-color &::before,
.custom-bg-image &::before {
display: none;
}
@ -330,7 +347,7 @@
color: var(--color-black);
height: 3.125rem;
overflow: visible;
box-shadow: 0 1px 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 1px 2px var(--color-default-shadow);
&:hover {
.icon-unpin {

View File

@ -59,6 +59,7 @@ type StateProps = {
messageSendingRestrictionReason?: string;
hasPinnedOrAudioMessage?: boolean;
customBackground?: string;
isCustomBackgroundColor?: boolean;
isRightColumnShown?: boolean;
isBackgroundBlurred?: boolean;
isMobileSearchActive?: boolean;
@ -84,6 +85,7 @@ const MiddleColumn: FC<StateProps & DispatchProps> = ({
messageSendingRestrictionReason,
hasPinnedOrAudioMessage,
customBackground,
isCustomBackgroundColor,
isRightColumnShown,
isBackgroundBlurred,
isMobileSearchActive,
@ -166,7 +168,8 @@ const MiddleColumn: FC<StateProps & DispatchProps> = ({
const className = buildClassName(
hasTools && 'has-header-tools',
customBackground && 'custom-bg-image',
customBackground && !isCustomBackgroundColor && 'custom-bg-image',
customBackground && isCustomBackgroundColor && 'custom-bg-color',
customBackground && isBackgroundBlurred && 'blurred',
MASK_IMAGE_ENABLED ? 'mask-image-enabled' : 'mask-image-disabled',
);
@ -293,12 +296,14 @@ export default memo(withGlobal(
(global): StateProps => {
const { isBackgroundBlurred, customBackground } = global.settings.byKey;
const isCustomBackgroundColor = Boolean((customBackground || '').match(/^#[a-f\d]{6,8}$/i));
const currentMessageList = selectCurrentMessageList(global);
const { chats: { listIds } } = global;
if (!currentMessageList || !listIds.active) {
return {
customBackground,
isBackgroundBlurred,
isCustomBackgroundColor,
};
}
@ -320,6 +325,7 @@ export default memo(withGlobal(
messageSendingRestrictionReason: chat && getMessageSendingRestrictionReason(chat),
hasPinnedOrAudioMessage: Boolean(pinnedIds && pinnedIds.length) || Boolean(audioChatId && audioMessageId),
customBackground,
isCustomBackgroundColor,
isRightColumnShown: selectIsRightColumnShown(global),
isBackgroundBlurred,
isMobileSearchActive: Boolean(IS_MOBILE_SCREEN && selectCurrentTextSearch(global)),

View File

@ -9,7 +9,7 @@
right: 0;
height: 2.875rem;
overflow: hidden;
box-shadow: 0 2px 2px rgba(114, 114, 114, 0.17);
box-shadow: 0 2px 2px var(--color-light-shadow);
display: flex;
flex-direction: row-reverse;
@ -25,7 +25,7 @@
left: 0;
right: 0;
height: 2px;
box-shadow: 0 2px 2px rgba(114, 114, 114, 0.17);
box-shadow: 0 2px 2px var(--color-light-shadow);
}
.HeaderPinnedMessage {
@ -73,8 +73,8 @@
display: flex;
align-items: center;
width: 100%;
box-shadow: 0 2px 2px rgba(114, 114, 114, 0.17);
background: #fff;
box-shadow: 0 2px 2px var(--color-light-shadow);
background: var(--color-background);
padding: .5rem .8125rem .5rem 1.5rem;
position: relative;
z-index: var(--z-middle-header);

View File

@ -5,7 +5,7 @@
z-index: var(--z-mobile-search);
width: 100%;
height: 3.5rem;
background: white;
background: var(--color-background);
display: flex;
align-items: center;
padding: 0 0.5rem 0 0.25rem;
@ -23,7 +23,7 @@
z-index: var(--z-mobile-search);
width: 100%;
height: 3.5rem;
background: white;
background: var(--color-background);
display: flex;
align-items: center;
padding-left: 1rem;

View File

@ -27,7 +27,7 @@
align-items: center;
> .Button {
box-shadow: 0 1px 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 1px 2px var(--color-default-shadow);
i {
font-size: 1.75rem;

View File

@ -66,7 +66,7 @@
position: relative;
.form-control {
background: white;
background: var(--color-background);
}
.MentionMenu {

View File

@ -32,7 +32,7 @@
min-height: 3.0625rem;
border-radius: var(--border-radius-messages-small);
border: 2px solid var(--color-primary);
background: #fff;
background: var(--color-background);
color: var(--color-primary);
font-weight: 500;
text-transform: none;

View File

@ -150,10 +150,10 @@
#message-compose {
flex-grow: 1;
max-width: calc(100% - 4rem);
background: white;
background: var(--color-background);
border-radius: var(--border-radius-messages);
border-bottom-right-radius: 0;
box-shadow: 0 1px 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 1px 2px var(--color-default-shadow);
position: relative;
z-index: 1;
@ -169,6 +169,10 @@
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSIyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGRlZnM+PGZpbHRlciB4PSItNTAlIiB5PSItMTQuNyUiIHdpZHRoPSIyMDAlIiBoZWlnaHQ9IjE0MS4yJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94IiBpZD0iYSI+PGZlT2Zmc2V0IGR5PSIxIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMSIgaW49InNoYWRvd09mZnNldE91dGVyMSIgcmVzdWx0PSJzaGFkb3dCbHVyT3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggdmFsdWVzPSIwIDAgMCAwIDAuMDYyMTk2MjQ4MiAwIDAgMCAwIDAuMTM4NTc0MTQ0IDAgMCAwIDAgMC4xODUwMzczNjQgMCAwIDAgMC4xNSAwIiBpbj0ic2hhZG93Qmx1ck91dGVyMSIvPjwvZmlsdGVyPjxwYXRoIGQ9Ik0zIDE3aDZWMGMtLjE5MyAyLjg0LS44NzYgNS43NjctMi4wNSA4Ljc4Mi0uOTA0IDIuMzI1LTIuNDQ2IDQuNDg1LTQuNjI1IDYuNDhBMSAxIDAgMDAzIDE3eiIgaWQ9ImIiLz48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PC9nPjwvc3ZnPg==);
background-position: bottom left;
transform: scaleX(-1);
.theme-dark & {
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSIyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48ZmlsdGVyIGlkPSJhIiB4PSIwIiB5PSIwIiB3aWR0aD0iMjA1IiBoZWlnaHQ9IjIwMCI+PGZlT2Zmc2V0IHJlc3VsdD0ib2ZmT3V0IiBpbj0iU291cmNlQWxwaGEiIGR5PSIxIi8+PGZlQ29sb3JNYXRyaXggcmVzdWx0PSJtYXRyaXhPdXQiIGluPSJvZmZPdXQiIHZhbHVlcz0iMC4xMyAwIDAgMCAwIDAgMC4xMyAwIDAgMCAwIDAgMC4xMyAwIDAgMCAwIDAgMC42IDAiLz48ZmVHYXVzc2lhbkJsdXIgcmVzdWx0PSJibHVyT3V0IiBpbj0ibWF0cml4T3V0IiBzdGREZXZpYXRpb249IjEiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJibHVyT3V0Ii8+PC9maWx0ZXI+PC9kZWZzPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PHBhdGggZD0iTTMgMTdoNlYwYy0uMTkzIDIuODQtLjg3NiA1Ljc2Ny0yLjA1IDguNzgyLS45MDQgMi4zMjUtMi40NDYgNC40ODUtNC42MjUgNi40OEExIDEgMCAwMDMgMTd6IiBmaWxsPSIjMDAwIiBmaWx0ZXI9InVybCgjYSkiLz48cGF0aCBkPSJNMyAxN2g2VjBjLS4xOTMgMi44NC0uODc2IDUuNzY3LTIuMDUgOC43ODItLjkwNCAyLjMyNS0yLjQ0NiA0LjQ4NS00LjYyNSA2LjQ4QTEgMSAwIDAwMyAxN3oiIGZpbGw9IiMyMTIxMjEiLz48L2c+PC9zdmc+);
}
}
@media (max-width: 600px) {
@ -203,13 +207,13 @@
position: absolute;
top: .75rem;
right: .75rem;
border: .1875rem solid #fff;
border: .1875rem solid var(--color-background);
box-sizing: content-box;
width: .5rem;
height: .5rem;
border-radius: 50%;
background: var(--color-green-darker);
box-shadow: -.375rem -.25rem 0 -.1875rem #fff;
box-shadow: -.375rem -.25rem 0 -.1875rem var(--color-background);
@media (max-width: 600px) {
top: .5rem;
right: .5rem;

View File

@ -1,6 +1,6 @@
.DropTarget {
border-radius: var(--border-radius-default);
background: #fff;
background: var(--color-background);
padding: 1.25rem;
flex: 1 1 auto;
width: 100%;
@ -10,7 +10,7 @@
margin-bottom: .3125rem;
display: flex;
color: #A4ACB3;
box-shadow: 0 1px 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 1px 2px var(--color-default-shadow);
@media (max-height: 350px) {
padding: .75rem;

View File

@ -17,7 +17,7 @@
display: flex;
align-items: center;
justify-content: space-around;
box-shadow: 0 0 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 0 2px var(--color-default-shadow);
@media (max-width: 600px) {
overflow-x: auto;

View File

@ -3,7 +3,7 @@
bottom: calc(100% + .5rem);
left: 0;
width: 100%;
background: white;
background: var(--color-background);
border-radius: var(--border-radius-messages);
padding: 0.5rem 0;
max-height: 15rem;
@ -15,7 +15,7 @@
grid-auto-rows: auto;
place-items: center;
box-shadow: 0 1px 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 1px 2px var(--color-default-shadow);
opacity: 0;
transform: translateY(1.5rem);

View File

@ -4,14 +4,14 @@
left: 0;
width: calc(100% - 4rem);
max-width: 20rem;
background: white;
background: var(--color-background);
border-radius: var(--border-radius-messages);
padding: 0.5rem 0;
max-height: 15rem;
overflow-x: hidden;
overflow-y: auto;
box-shadow: 3px 3px 5px rgba(114, 114, 114, 0.25);
box-shadow: 3px 3px 5px var(--color-default-shadow);
z-index: -1;
opacity: 0;

View File

@ -19,7 +19,7 @@
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
box-shadow: 0 0 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 0 2px var(--color-default-shadow);
scrollbar-width: none;
scrollbar-color: rgba(0, 0, 0, 0);

View File

@ -32,7 +32,7 @@
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 0 2px var(--color-default-shadow);
position: relative;
.Button {

View File

@ -3,10 +3,10 @@
&, &-link-control {
position: absolute;
background: white;
background: var(--color-background);
border-radius: var(--border-radius-messages);
padding: 0.5rem 0.375rem;
box-shadow: 0 1px 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 1px 2px var(--color-default-shadow);
}
&-link-control {
@ -24,6 +24,8 @@
border: none !important;
outline: none !important;
width: 100%;
color: var(--color-text);
background-color: var(--color-background);
}
}
@ -62,12 +64,12 @@
&::before {
left: 0;
background: linear-gradient(to right, #fff .25rem, transparent 1rem)
background: linear-gradient(to right, var(--color-background) .25rem, transparent 1rem)
}
&::after {
right: 0;
background: linear-gradient(to left, #fff .25rem, transparent 1rem)
background: linear-gradient(to left, var(--color-background) .25rem, transparent 1rem)
}
&.mask-left {

View File

@ -1,6 +1,6 @@
.CommentButton {
--background-color: #fff;
--hover-color: #f4f4f4;
--background-color: var(--color-background);
--hover-color: var(--color-chat-hover);
display: flex;
width: 100%;
@ -30,9 +30,13 @@
width: .5625rem;
height: 1.25rem;
background-position: bottom left;
background-image: url("data:image/svg+xml,%3Csvg width='9' height='20' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cdefs%3E%3Cfilter x='-50%25' y='-14.7%25' width='200%25' height='141.2%25' filterUnits='objectBoundingBox' id='a'%3E%3CfeOffset dy='1' in='SourceAlpha' result='shadowOffsetOuter1'/%3E%3CfeGaussianBlur stdDeviation='1' in='shadowOffsetOuter1' result='shadowBlurOuter1'/%3E%3CfeColorMatrix values='0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0' in='shadowBlurOuter1'/%3E%3C/filter%3E%3Cpath d='M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z' id='b'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cuse fill='%23000' filter='url(%23a)' xlink:href='%23b'/%3E%3Cuse fill='%23f4f4f4' xlink:href='%23b'/%3E%3C/g%3E%3C/svg%3E");
background-image: url('data:image/svg+xml,%3Csvg width="9" height="20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"%3E%3Cdefs%3E%3Cfilter x="-50%25" y="-14.7%25" width="200%25" height="141.2%25" filterUnits="objectBoundingBox" id="a"%3E%3CfeOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/%3E%3CfeGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/%3E%3CfeColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/%3E%3C/filter%3E%3Cpath d="M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z" id="b"/%3E%3C/defs%3E%3Cg fill="none" fill-rule="evenodd"%3E%3Cuse fill="%23000" filter="url(%23a)" xlink:href="%23b"/%3E%3Cuse fill="%23FFF" xlink:href="%23b"/%3E%3C/g%3E%3C/svg%3E');
opacity: 0;
transition: opacity .15s;
transition: opacity .15s, filter .15s;
.theme-dark #root & {
filter: invert(.83);
}
body.animation-level-0 & {
transition: none !important;
@ -162,7 +166,7 @@
.Avatar {
transition: border .15s;
border: 2px solid #fff;
border: 2px solid var(--color-background);
margin-right: 0;
z-index: 3;

View File

@ -16,7 +16,7 @@
height: 10rem;
}
}
.description-text {
position: absolute;
top: 0;
@ -24,10 +24,10 @@
margin: .25rem;
background-color: rgba(90, 110, 70, 0.6);
border-radius: var(--border-radius-messages-small);
color: var(--color-white);
color: var(--color-text);
font-weight: 500;
}
}
}
}

View File

@ -5,7 +5,7 @@
margin-bottom: 0.375rem;
position: relative;
--background-color: white;
--background-color: var(--color-background);
--hover-color: rgba(var(--color-text-secondary-rgb), 0.08);
--active-color: rgba(var(--color-text-secondary-rgb), 0.16);
--max-width: 29rem;
@ -81,16 +81,19 @@
&.own {
flex-direction: row-reverse;
--background-color: var(--color-background-own);
--hover-color: rgba(var(--color-text-green-rgb), 0.12);
--active-color: rgba(var(--color-text-green-rgb), 0.24);
--hover-color: var(--color-reply-own-hover);
--active-color: var(--color-reply-own-active);
--max-width: 30rem;
--accent-color: var(--color-text-green);
--accent-color: var(--color-accent-own);
--accent-shade-color: var(--color-green);
--secondary-color: var(--color-text-green);
--secondary-color: var(--color-accent-own);
--color-code: var(--color-code-own);
--color-code-bg: var(--color-code-own-bg);
--color-links: var(--color-own-links);
--color-links-hover: var(--color-own-links);
--meta-safe-area-base: 3.5rem;
--deleting-translate-x: 50%;
--color-text-green: var(--color-accent-own);
@media (max-width: 600px) {
padding-right: 0.25rem;
@ -177,6 +180,15 @@
bottom: 0.813rem;
}
html.theme-dark &.own .Audio .ProgressSpinner {
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTguMjE4IDcuNWw1LjYzMy01LjYzM2EuNTA4LjUwOCAwIDEwLS43MTgtLjcxOEw3LjUgNi43ODIgMS44NjcgMS4xNDlhLjUwOC41MDggMCAxMC0uNzE4LjcxOEw2Ljc4MiA3LjVsLTUuNjMzIDUuNjMzYS41MDguNTA4IDAgMTAuNzE4LjcxOEw3LjUgOC4yMThsNS42MzMgNS42MzNhLjUwNi41MDYgMCAwMC43MTggMCAuNTA4LjUwOCAwIDAwMC0uNzE4TDguMjE4IDcuNXoiIGZpbGw9IiNGRkYiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjQTQ1RDM3IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PC9zdmc+);
circle {
stroke: var(--background-color);
}
}
.File {
position: relative;

View File

@ -72,12 +72,15 @@
}
.Message.own .has-solid-background & {
color: var(--color-text-green);
color: var(--color-message-meta-own);
}
.MessageOutgoingStatus {
margin-left: -.1875rem;
font-size: 1.1875rem;
.Message.own & {
color: var(--color-accent-own);
}
}
.message-content.has-replies:not(.custom-shape) & {

View File

@ -109,7 +109,7 @@
margin-top: -2px;
.Avatar {
border: 1px solid #fff;
border: 1px solid var(--color-white);
margin-right: 0;
box-sizing: content-box;

View File

@ -35,7 +35,7 @@
width: 1rem;
height: 1rem;
background: var(--accent-color);
color: #fff;
color: var(--background-color);
border-radius: .5rem;
font-size: .75rem;
text-align: center;

View File

@ -98,7 +98,7 @@
}
&.has-shadow {
box-shadow: 0 1px 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 1px 2px var(--color-default-shadow);
}
&.has-solid-background, .is-album & {
@ -123,6 +123,10 @@
background-position: bottom right;
background-image: url('data:image/svg+xml,%3Csvg width="9" height="20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"%3E%3Cdefs%3E%3Cfilter x="-50%25" y="-14.7%25" width="200%25" height="141.2%25" filterUnits="objectBoundingBox" id="a"%3E%3CfeOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/%3E%3CfeGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/%3E%3CfeColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/%3E%3C/filter%3E%3Cpath d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" id="b"/%3E%3C/defs%3E%3Cg fill="none" fill-rule="evenodd"%3E%3Cuse fill="%23000" filter="url(%23a)" xlink:href="%23b"/%3E%3Cuse fill="%23EEFFDE" xlink:href="%23b"/%3E%3C/g%3E%3C/svg%3E');
}
.theme-dark &:not([data-has-custom-appendix])::before {
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSIyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGRlZnM+PGZpbHRlciBpZD0iYSIgeD0iMCIgeT0iMCIgd2lkdGg9IjIwNSIgaGVpZ2h0PSIyMDAiPjxmZU9mZnNldCByZXN1bHQ9Im9mZk91dCIgaW49IlNvdXJjZUFscGhhIiBkeD0iLTEiIGR5PSIxIi8+PGZlQ29sb3JNYXRyaXggcmVzdWx0PSJtYXRyaXhPdXQiIGluPSJvZmZPdXQiIHZhbHVlcz0iMC4xMyAwIDAgMCAwIDAgMC4xMyAwIDAgMCAwIDAgMC4xMyAwIDAgMCAwIDAgMC42IDAiLz48ZmVHYXVzc2lhbkJsdXIgcmVzdWx0PSJibHVyT3V0IiBpbj0ibWF0cml4T3V0IiBzdGREZXZpYXRpb249IjEiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJibHVyT3V0Ii8+PC9maWx0ZXI+PHBhdGggZD0iTTYgMTdIMFYwYy4xOTMgMi44NC44NzYgNS43NjcgMi4wNSA4Ljc4Mi45MDQgMi4zMjUgMi40NDYgNC40ODUgNC42MjUgNi40OEExIDEgMCAwMTYgMTd6IiBpZD0iYiIvPjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjx1c2UgZmlsbD0iIzAwMCIgZmlsdGVyPSJ1cmwoI2EpIiB4bGluazpocmVmPSIjYiIvPjx1c2UgZmlsbD0iIzlBNUYzRiIgeGxpbms6aHJlZj0iI2IiLz48L2c+PC9zdmc+);
}
}
.Message:not(.own) & {
@ -131,6 +135,10 @@
background-position: bottom left;
background-image: url('data:image/svg+xml,%3Csvg width="9" height="20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"%3E%3Cdefs%3E%3Cfilter x="-50%25" y="-14.7%25" width="200%25" height="141.2%25" filterUnits="objectBoundingBox" id="a"%3E%3CfeOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/%3E%3CfeGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/%3E%3CfeColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/%3E%3C/filter%3E%3Cpath d="M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z" id="b"/%3E%3C/defs%3E%3Cg fill="none" fill-rule="evenodd"%3E%3Cuse fill="%23000" filter="url(%23a)" xlink:href="%23b"/%3E%3Cuse fill="%23FFF" xlink:href="%23b"/%3E%3C/g%3E%3C/svg%3E');
}
.theme-dark &:not([data-has-custom-appendix])::before {
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSIyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48ZmlsdGVyIGlkPSJhIiB4PSIwIiB5PSIwIiB3aWR0aD0iMjA1IiBoZWlnaHQ9IjIwMCI+PGZlT2Zmc2V0IHJlc3VsdD0ib2ZmT3V0IiBpbj0iU291cmNlQWxwaGEiIGR5PSIxIi8+PGZlQ29sb3JNYXRyaXggcmVzdWx0PSJtYXRyaXhPdXQiIGluPSJvZmZPdXQiIHZhbHVlcz0iMC4xMyAwIDAgMCAwIDAgMC4xMyAwIDAgMCAwIDAgMC4xMyAwIDAgMCAwIDAgMC42IDAiLz48ZmVHYXVzc2lhbkJsdXIgcmVzdWx0PSJibHVyT3V0IiBpbj0ibWF0cml4T3V0IiBzdGREZXZpYXRpb249IjEiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJibHVyT3V0Ii8+PC9maWx0ZXI+PC9kZWZzPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PHBhdGggZD0iTTMgMTdoNlYwYy0uMTkzIDIuODQtLjg3NiA1Ljc2Ny0yLjA1IDguNzgyLS45MDQgMi4zMjUtMi40NDYgNC40ODUtNC42MjUgNi40OEExIDEgMCAwMDMgMTd6IiBmaWxsPSIjMDAwIiBmaWx0ZXI9InVybCgjYSkiLz48cGF0aCBkPSJNMyAxN2g2VjBjLS4xOTMgMi44NC0uODc2IDUuNzY3LTIuMDUgOC43ODItLjkwNCAyLjMyNS0yLjQ0NiA0LjQ4NS00LjYyNSA2LjQ4QTEgMSAwIDAwMyAxN3oiIGZpbGw9IiMyMTIxMjEiLz48L2c+PC9zdmc+);
}
}
&:not(.has-solid-background) {

View File

@ -12,7 +12,7 @@
display: flex;
align-items: center;
flex-direction: row;
background: white;
background: var(--color-background);
border-bottom: 1px var(--color-borders) solid;
h3 {
@ -51,7 +51,7 @@
border-bottom-right-radius: 10px;
width: 100%;
padding: .75rem 1rem;
background: white;
background: var(--color-background);
border-top: 1px var(--color-borders) solid;
button {

View File

@ -14,7 +14,7 @@
padding: 1rem .75rem .5rem 1rem;
position: sticky;
top: 0;
background: #fff;
background: var(--color-background);
@media (max-width: 600px) {
padding: .5rem .25rem .5rem .5rem;

View File

@ -51,7 +51,7 @@
flex-direction: column-reverse;
.TabList {
background: #fff;
background: var(--color-background);
top: -1px;
.Tab {
padding: .6875rem .25rem;

View File

@ -23,7 +23,7 @@
}
@media (max-width: 1275px) {
box-shadow: 0 .25rem .5rem .1rem rgba(114, 114, 114, 0.25);
box-shadow: 0 .25rem .5rem .1rem var(--color-default-shadow);
}
@media (max-width: 600px) {

View File

@ -55,7 +55,7 @@
}
&.filled {
background-color: white;
background-color: var(--color-background);
&::after {
content: '';

View File

@ -79,7 +79,7 @@
&::before {
border: 2px solid var(--color-borders);
border-radius: .25rem;
background-color: white;
background-color: var(--color-white);
transition: border-color .1s ease;
}

View File

@ -26,7 +26,7 @@
position: absolute;
bottom: 1rem;
right: 1rem;
box-shadow: 0 1px 2px rgba(114, 114, 114, 0.25);
box-shadow: 0 1px 2px var(--color-default-shadow);
}
#avatar-crop {

View File

@ -19,8 +19,8 @@
padding: 0.5rem 0;
margin: 0;
position: absolute;
background-color: white;
box-shadow: 0 .25rem .5rem .1rem rgba(114, 114, 114, 0.25);
background-color: var(--color-background);
box-shadow: 0 .25rem .5rem .1rem var(--color-default-shadow);
border-radius: var(--border-radius-default);
min-width: 13.5rem;
z-index: var(--z-menu-bubble);

View File

@ -24,6 +24,10 @@
color: var(--color-text-secondary);
}
.menu-item-name {
margin-right: 2rem;
}
&.disabled {
opacity: 0.5 !important;
cursor: default !important;
@ -41,4 +45,8 @@
background-color: var(--color-chat-active);
transition: none !important;
}
& > .Switcher {
margin-left: auto;
}
}

View File

@ -47,8 +47,8 @@
max-width: 35rem;
min-width: 17.5rem;
margin: 2rem auto;
background-color: white;
box-shadow: 0 .25rem .5rem .1rem rgba(114, 114, 114, 0.25);
background-color: var(--color-background);
box-shadow: 0 .25rem .5rem .1rem var(--color-default-shadow);
border-radius: var(--border-radius-default);
transform: translate3d(0, -1rem, 0);

View File

@ -42,7 +42,7 @@
&::before {
border: 2px solid var(--color-borders);
border-radius: 50%;
background-color: white;
background-color: var(--color-white);
opacity: 1;
transition: border-color .1s ease, opacity .1s ease;
}

View File

@ -22,7 +22,7 @@
&.has-focus {
border-color: var(--color-primary);
caret-color: var(--color-primary);
background-color: #fff;
background-color: var(--color-background);
input {
& + i {

View File

@ -0,0 +1,53 @@
.Switcher {
display: inline-flex;
align-items: center;
position: relative;
margin: 0;
&.disabled {
pointer-events: none;
opacity: 0.5;
}
input {
height: 0;
width: 0;
visibility: hidden;
position: absolute;
z-index: var(--z-below);
opacity: 0;
}
.widget {
cursor: pointer;
text-indent: -999px;
width: 2.125rem;
height: 0.875rem;
background: var(--color-gray);
display: inline-block;
border-radius: .5rem;
position: relative;
}
.widget:after {
content: '';
position: absolute;
top: -.125rem;
left: 0;
width: 1.125rem;
height: 1.125rem;
background: var(--color-background);
border-radius: .75rem;
border: .125rem solid var(--color-gray);
}
input:checked + .widget {
background: var(--color-primary);
}
input:checked + .widget:after {
left: calc(100% - 1.125rem);
transform: translateX(calc(-100% + 1.125rem));
border-color: var(--color-primary);
}
}

View File

@ -0,0 +1,60 @@
import { ChangeEvent } from 'react';
import React, { FC, memo, useCallback } from '../../lib/teact/teact';
import buildClassName from '../../util/buildClassName';
import './Switcher.scss';
type OwnProps = {
id?: string;
name?: string;
value?: string;
label: string;
checked?: boolean;
disabled?: boolean;
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
onCheck?: (isChecked: boolean) => void;
};
const Switcher: FC<OwnProps> = ({
id,
name,
value,
label,
checked = false,
disabled,
onChange,
onCheck,
}) => {
const handleChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
if (onChange) {
onChange(event);
}
if (onCheck) {
onCheck(event.currentTarget.checked);
}
}, [onChange, onCheck]);
const className = buildClassName(
'Switcher',
disabled && 'disabled',
);
return (
<label className={className} title={label}>
<input
type="checkbox"
id={id}
name={name}
value={value}
checked={checked}
disabled={disabled}
onChange={handleChange}
/>
<span className="widget" />
</label>
);
};
export default memo(Switcher);

View File

@ -7,8 +7,8 @@
align-items: flex-end;
font-size: 0.875rem;
flex-wrap: nowrap;
box-shadow: 0 2px 2px rgba(114, 114, 114, 0.17);
background-color: white;
box-shadow: 0 2px 2px var(--color-light-shadow);
background-color: var(--color-background);
overflow-x: auto;
overflow-y: hidden;

View File

@ -5,6 +5,7 @@
width: 100%;
height: 100%;
animation-fill-mode: forwards !important;
transition: background-color .2s;
&.from, &.to {
position: absolute;
@ -245,7 +246,7 @@
*/
&.slide-layers {
--background-color: var(--color-white);
--background-color: var(--color-background);
background: black;
> div {
@ -290,7 +291,7 @@
&.push-slide {
> div {
background: white;
background: var(--color-background);
}
> .from {

View File

@ -103,6 +103,7 @@ export const INITIAL_STATE: GlobalState = {
isBackgroundBlurred: true,
animationLevel: ANIMATION_LEVEL_DEFAULT,
messageSendKeyCombo: 'enter',
theme: 'light',
shouldAutoDownloadMediaFromContacts: true,
shouldAutoDownloadMediaInPrivateChats: true,
shouldAutoDownloadMediaInGroups: true,

View File

@ -4,15 +4,20 @@ import {
IS_ANDROID, IS_IOS, IS_SAFARI, IS_TOUCH_ENV,
} from '../../../util/environment';
import { setLanguage } from '../../../util/langProvider';
import switchTheme from '../../../util/switchTheme';
addReducer('init', (global) => {
const { animationLevel, messageTextSize, language } = global.settings.byKey;
const {
theme, animationLevel, messageTextSize, language,
} = global.settings.byKey;
setLanguage(language);
document.documentElement.style.setProperty('--message-text-size', `${messageTextSize}px`);
document.body.classList.add('initial');
document.body.classList.add(`animation-level-${animationLevel}`);
document.body.classList.add(IS_TOUCH_ENV ? 'is-touch-env' : 'is-pointer-env');
switchTheme(theme, animationLevel > 0);
if (IS_SAFARI) {
document.body.classList.add('is-safari');
@ -27,6 +32,10 @@ addReducer('init', (global) => {
addReducer('setIsUiReady', (global, actions, payload) => {
const { uiReadyState } = payload!;
if (uiReadyState === 2) {
document.body.classList.remove('initial');
}
return {
...global,
uiReadyState,

View File

@ -70,9 +70,10 @@
width: 100%;
height: 3.375rem;
padding: calc(0.75rem - var(--border-width)) calc(.9rem - var(--border-width));
border: var(--border-width) solid var(--color-borders);
border: var(--border-width) solid var(--color-borders-input);
border-radius: var(--border-radius-default);
color: var(--color-text);
background-color: var(--color-background);
outline: none;
transition: border-color 0.15s ease;
word-break: break-word;
@ -82,7 +83,11 @@
line-height: 1.25rem;
&:hover {
border-color: var(--color-gray);
border-color: var(--color-primary);
& + label {
color: var(--color-primary);
}
}
&:focus,

View File

@ -64,9 +64,11 @@ $color-user-8: #faa774;
--color-text-secondary-rgb: #{toRGB($color-text-secondary)};
--color-text-meta: #{$color-text-meta};
--color-text-meta-rgb: #{toRGB($color-text-meta)};
--color-text-meta-colored: #{$color-text-green};
--color-text-green: #{$color-text-green};
--color-text-green-rgb: #{toRGB($color-text-green)};
--color-borders: #{$color-borders};
--color-borders-input: #{$color-borders};
--color-webpage-initial-background: #{$color-dark-gray};
--color-interactive-active: var(--color-primary);
--color-interactive-inactive: rgba(var(--color-text-secondary-rgb), 0.25);
@ -94,6 +96,8 @@ $color-user-8: #faa774;
--color-links-darker: #{darken($color-links, 15%)};
--color-links-darker-hover: #{darken($color-links, 23%)};
--color-own-links: #{$color-white};
--color-placeholders: #{$color-placeholders};
--color-code: #4a729a;
@ -101,6 +105,9 @@ $color-user-8: #faa774;
--color-code-own: #3c7940;
--color-code-own-bg: #{rgba($color-text-secondary, .08)};
--color-accent-own: #{$color-text-green};
--color-message-meta-own: #{$color-text-green};
--color-reply-hover: #{blend-normal(rgba($color-text-secondary, 0.08), $color-white)};
--color-reply-active: #{blend-normal(rgba($color-text-secondary, 0.16), $color-white)};
--color-reply-own-hover: #{blend-normal(rgba($color-text-green, 0.12), $color-light-green)};
@ -123,6 +130,9 @@ $color-user-8: #faa774;
--color-user-7: #{$color-user-7};
--color-user-8: #{$color-user-8};
--color-default-shadow: #72727240;
--color-light-shadow: #7272722B;
--border-radius-default: 0.75rem;
--border-radius-default-small: 0.625rem;
--border-radius-default-tiny: 0.375rem;

View File

@ -83,6 +83,10 @@ body.cursor-grabbing, body.cursor-grabbing * {
box-sizing: border-box;
}
.disable-animations #root * {
transition: none !important;
}
.custom-scroll,
.custom-scroll-x {
scrollbar-width: thin;

23
src/styles/themes.json Normal file
View File

@ -0,0 +1,23 @@
{
"--color-primary": ["#50A2E9", "#868DF5"],
"--color-background": ["#FFFFFF", "#212121"],
"--color-background-own": ["#EEFEDF", "#A45D37"],
"--color-chat-hover": ["#F4F4F5", "#2C2C2C"],
"--color-chat-active": ["#ededed", "#292929"],
"--color-text": ["#000000", "#FFFFFF"],
"--color-text-secondary": ["#707579", "#AAAAAA"],
"--color-borders": ["#DADCE0", "#100F10"],
"--color-borders-input": ["#DADCE0", "#5B5B5A"],
"--color-links": ["#52A1EF", "#868DF6"],
"--color-gray": ["#C4C9CC", "#808080"],
"--color-default-shadow": ["#72727240", "#00000099"],
"--color-light-shadow": ["#7272722B", "#00000040"],
"--color-green": ["#4DCD5E", "#868DF5"],
"--color-text-meta-colored": ["#4DCD5E", "#868DF5"],
"--color-reply-own-hover": ["#DBF4CE", "#A26947"],
"--color-reply-own-active": ["#C8EBBC", "#B0714C"],
"--color-accent-own": ["#4FAE4E", "#FFFFFF"],
"--color-message-meta-own": ["#4FAE4E", "#D9BDAD"],
"--color-own-links": ["#52A1EF", "#FFFFFF"],
"--color-code-own": ["#3C7940", "#FFFFFF"]
}

View File

@ -26,6 +26,7 @@ export interface ISettings extends Record<string, any> {
isBackgroundBlurred?: boolean;
animationLevel: 0 | 1 | 2;
messageSendKeyCombo: 'enter' | 'ctrl-enter';
theme: 'light' | 'dark';
shouldAutoDownloadMediaFromContacts: boolean;
shouldAutoDownloadMediaInPrivateChats: boolean;
shouldAutoDownloadMediaInGroups: boolean;

85
src/util/switchTheme.ts Normal file
View File

@ -0,0 +1,85 @@
import { ISettings } from '../types';
import { animateSingle } from './animation';
import themeColors from '../styles/themes.json';
type RGBAColor = {
r: number;
g: number;
b: number;
a?: number;
};
let isInitialized = false;
const HEX_COLOR_REGEX = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i;
const DURATION_MS = 200;
const ENABLE_ANIMATION_DELAY_MS = 500;
const lerp = (start: number, end: number, interpolationRatio: number) => {
return (1 - interpolationRatio) * start + interpolationRatio * end;
};
const colors = (Object.keys(themeColors) as Array<keyof typeof themeColors>).map((property) => ({
property,
colors: [hexToRgb(themeColors[property][0]), hexToRgb(themeColors[property][1])],
}));
export default (theme: ISettings['theme'], withAnimation: boolean) => {
const shouldAnimate = isInitialized && withAnimation;
const startIndex = theme === 'dark' ? 0 : 1;
const endIndex = theme === 'dark' ? 1 : 0;
const startAt = Date.now();
document.documentElement.classList.remove(`theme-${theme === 'dark' ? 'light' : 'dark'}`);
if (isInitialized) {
document.documentElement.classList.add('disable-animations');
}
document.documentElement.classList.add(`theme-${theme}`);
setTimeout(() => {
document.documentElement.classList.remove('disable-animations');
}, ENABLE_ANIMATION_DELAY_MS);
isInitialized = true;
if (shouldAnimate) {
animateSingle(() => {
const t = Math.min((Date.now() - startAt) / DURATION_MS, 1);
applyColorAnimationStep(startIndex, endIndex, transition(t));
return t < 1;
});
} else {
applyColorAnimationStep(startIndex, endIndex);
}
};
function transition(t: number) {
return 1 - ((1 - t) ** 3.5);
}
function hexToRgb(hex: string): RGBAColor {
const result = HEX_COLOR_REGEX.exec(hex)!;
return {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
a: result[4] ? parseInt(result[4], 16) : undefined,
};
}
function applyColorAnimationStep(startIndex: number, endIndex: number, interpolationRatio: number = 1) {
colors.forEach(({ property, colors: propertyColors }) => {
const r = Math.round(lerp(propertyColors[startIndex].r, propertyColors[endIndex].r, interpolationRatio));
const g = Math.round(lerp(propertyColors[startIndex].g, propertyColors[endIndex].g, interpolationRatio));
const b = Math.round(lerp(propertyColors[startIndex].b, propertyColors[endIndex].b, interpolationRatio));
const a = propertyColors[startIndex].a
&& Math.round(lerp(propertyColors[startIndex].a!, propertyColors[endIndex].a!, interpolationRatio));
document.documentElement.style.setProperty(property, a ? `rgba(${r},${g},${b},${a / 255})` : `rgb(${r},${g},${b})`);
});
}