Message List: Pattern color (#1019)
This commit is contained in:
parent
8c4345f6af
commit
c63947edac
@ -7,7 +7,9 @@ import { GlobalActions } from '../../../global/types';
|
|||||||
import { LeftColumnContent, ISettings } from '../../../types';
|
import { LeftColumnContent, ISettings } from '../../../types';
|
||||||
import { ApiChat } from '../../../api/types';
|
import { ApiChat } from '../../../api/types';
|
||||||
|
|
||||||
import { APP_INFO, FEEDBACK_URL } from '../../../config';
|
import {
|
||||||
|
APP_INFO, DEFAULT_PATTERN_COLOR, FEEDBACK_URL, DARK_THEME_BG_COLOR, DARK_THEME_PATTERN_COLOR,
|
||||||
|
} from '../../../config';
|
||||||
import { IS_MOBILE_SCREEN } from '../../../util/environment';
|
import { IS_MOBILE_SCREEN } from '../../../util/environment';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
@ -125,10 +127,12 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
const handleDarkModeToggle = useCallback((e: React.SyntheticEvent<HTMLDivElement>) => {
|
const handleDarkModeToggle = useCallback((e: React.SyntheticEvent<HTMLDivElement>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const newTheme = theme === 'light' ? 'dark' : 'light';
|
const newTheme = theme === 'light' ? 'dark' : 'light';
|
||||||
|
const isNewThemeDark = newTheme === 'dark';
|
||||||
|
|
||||||
setSettingOption({
|
setSettingOption({
|
||||||
theme: newTheme,
|
theme: newTheme,
|
||||||
customBackground: newTheme === 'dark' ? '#0F0F0F' : undefined,
|
customBackground: isNewThemeDark ? DARK_THEME_BG_COLOR : undefined,
|
||||||
|
patternColor: isNewThemeDark ? DARK_THEME_PATTERN_COLOR : DEFAULT_PATTERN_COLOR,
|
||||||
});
|
});
|
||||||
switchTheme(newTheme, animationLevel > 0);
|
switchTheme(newTheme, animationLevel > 0);
|
||||||
}, [animationLevel, setSettingOption, theme]);
|
}, [animationLevel, setSettingOption, theme]);
|
||||||
|
|||||||
@ -7,9 +7,11 @@ import { GlobalActions } from '../../../global/types';
|
|||||||
import { SettingsScreens, UPLOADING_WALLPAPER_SLUG } from '../../../types';
|
import { SettingsScreens, UPLOADING_WALLPAPER_SLUG } from '../../../types';
|
||||||
import { ApiWallpaper } from '../../../api/types';
|
import { ApiWallpaper } from '../../../api/types';
|
||||||
|
|
||||||
|
import { DEFAULT_PATTERN_COLOR } from '../../../config';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
import { openSystemFilesDialog } from '../../../util/systemFilesDialog';
|
import { openSystemFilesDialog } from '../../../util/systemFilesDialog';
|
||||||
|
import { getAverageColor, getPatternColor } from '../../../util/colors';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -69,12 +71,19 @@ const SettingsGeneralBackground: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}, [onScreenSelect]);
|
}, [onScreenSelect]);
|
||||||
|
|
||||||
const handleResetToDefault = useCallback(() => {
|
const handleResetToDefault = useCallback(() => {
|
||||||
setSettingOption({ customBackground: undefined });
|
setSettingOption({ customBackground: undefined, patternColor: DEFAULT_PATTERN_COLOR });
|
||||||
}, [setSettingOption]);
|
}, [setSettingOption]);
|
||||||
|
|
||||||
const handleWallPaperSelect = useCallback((slug: string) => {
|
const handleWallPaperSelect = useCallback((slug: string) => {
|
||||||
setSettingOption({ customBackground: slug });
|
setSettingOption({ customBackground: slug });
|
||||||
}, [setSettingOption]);
|
const currentWallpaper = loadedWallpapers && loadedWallpapers.find((wallpaper) => wallpaper.slug === slug);
|
||||||
|
if (currentWallpaper && currentWallpaper.document.thumbnail) {
|
||||||
|
getAverageColor(currentWallpaper.document.thumbnail.dataUri)
|
||||||
|
.then((color) => {
|
||||||
|
setSettingOption({ patternColor: getPatternColor(color) });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [loadedWallpapers, setSettingOption]);
|
||||||
|
|
||||||
const handleWallPaperBlurChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleWallPaperBlurChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSettingOption({ isBackgroundBlurred: e.target.checked });
|
setSettingOption({ isBackgroundBlurred: e.target.checked });
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { SettingsScreens } from '../../../types';
|
|||||||
|
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import {
|
import {
|
||||||
hex2rgb, hsb2rgb, rgb2hex, rgb2hsb,
|
getPatternColor, hex2rgb, hsb2rgb, rgb2hex, rgb2hsb,
|
||||||
} from '../../../util/colors';
|
} from '../../../util/colors';
|
||||||
import { captureEvents, RealTouchEvent } from '../../../util/captureEvents';
|
import { captureEvents, RealTouchEvent } from '../../../util/captureEvents';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
@ -139,7 +139,10 @@ const SettingsGeneralBackground: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
setHexInput(color);
|
setHexInput(color);
|
||||||
|
|
||||||
if (!isFirstRunRef.current) {
|
if (!isFirstRunRef.current) {
|
||||||
setSettingOption({ customBackground: color });
|
setSettingOption({
|
||||||
|
customBackground: color,
|
||||||
|
patternColor: getPatternColor(rgb),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
isFirstRunRef.current = false;
|
isFirstRunRef.current = false;
|
||||||
}, [hsb, setSettingOption]);
|
}, [hsb, setSettingOption]);
|
||||||
|
|||||||
@ -131,7 +131,7 @@
|
|||||||
|
|
||||||
> span {
|
> span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: rgba(var(--color-text-secondary-rgb), 0.45);
|
background: var(--pattern-color);
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 0.9375rem;
|
font-size: 0.9375rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|||||||
@ -59,6 +59,7 @@ type StateProps = {
|
|||||||
messageSendingRestrictionReason?: string;
|
messageSendingRestrictionReason?: string;
|
||||||
hasPinnedOrAudioMessage?: boolean;
|
hasPinnedOrAudioMessage?: boolean;
|
||||||
customBackground?: string;
|
customBackground?: string;
|
||||||
|
patternColor?: string;
|
||||||
isCustomBackgroundColor?: boolean;
|
isCustomBackgroundColor?: boolean;
|
||||||
isRightColumnShown?: boolean;
|
isRightColumnShown?: boolean;
|
||||||
isBackgroundBlurred?: boolean;
|
isBackgroundBlurred?: boolean;
|
||||||
@ -85,6 +86,7 @@ const MiddleColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
messageSendingRestrictionReason,
|
messageSendingRestrictionReason,
|
||||||
hasPinnedOrAudioMessage,
|
hasPinnedOrAudioMessage,
|
||||||
customBackground,
|
customBackground,
|
||||||
|
patternColor,
|
||||||
isCustomBackgroundColor,
|
isCustomBackgroundColor,
|
||||||
isRightColumnShown,
|
isRightColumnShown,
|
||||||
isBackgroundBlurred,
|
isBackgroundBlurred,
|
||||||
@ -203,6 +205,7 @@ const MiddleColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
--toolbar-unpin-hidden-scale: ${toolbarForUnpinHiddenScale};
|
--toolbar-unpin-hidden-scale: ${toolbarForUnpinHiddenScale};
|
||||||
--composer-translate-x: ${composerTranslateX}px;
|
--composer-translate-x: ${composerTranslateX}px;
|
||||||
--toolbar-translate-x: ${toolbarTranslateX}px;
|
--toolbar-translate-x: ${toolbarTranslateX}px;
|
||||||
|
--pattern-color: ${patternColor};
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -297,7 +300,7 @@ const MiddleColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
export default memo(withGlobal(
|
export default memo(withGlobal(
|
||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
const { isBackgroundBlurred, customBackground } = global.settings.byKey;
|
const { isBackgroundBlurred, customBackground, patternColor } = global.settings.byKey;
|
||||||
|
|
||||||
const isCustomBackgroundColor = Boolean((customBackground || '').match(/^#[a-f\d]{6,8}$/i));
|
const isCustomBackgroundColor = Boolean((customBackground || '').match(/^#[a-f\d]{6,8}$/i));
|
||||||
const currentMessageList = selectCurrentMessageList(global);
|
const currentMessageList = selectCurrentMessageList(global);
|
||||||
@ -329,6 +332,7 @@ export default memo(withGlobal(
|
|||||||
messageSendingRestrictionReason: chat && getMessageSendingRestrictionReason(chat),
|
messageSendingRestrictionReason: chat && getMessageSendingRestrictionReason(chat),
|
||||||
hasPinnedOrAudioMessage: Boolean(pinnedIds && pinnedIds.length) || Boolean(audioChatId && audioMessageId),
|
hasPinnedOrAudioMessage: Boolean(pinnedIds && pinnedIds.length) || Boolean(audioChatId && audioMessageId),
|
||||||
customBackground,
|
customBackground,
|
||||||
|
patternColor,
|
||||||
isCustomBackgroundColor,
|
isCustomBackgroundColor,
|
||||||
isRightColumnShown: selectIsRightColumnShown(global),
|
isRightColumnShown: selectIsRightColumnShown(global),
|
||||||
isBackgroundBlurred,
|
isBackgroundBlurred,
|
||||||
|
|||||||
@ -11,13 +11,32 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
width: auto;
|
width: auto;
|
||||||
margin: 0.125rem;
|
margin: 0.125rem;
|
||||||
background: rgba(102, 102, 102, 0.4);
|
background: var(--pattern-color);
|
||||||
border-radius: var(--border-radius-messages-small);
|
border-radius: var(--border-radius-messages-small);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
background-color: var(--color-white);
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: var(--border-radius-messages-small);
|
||||||
|
z-index: var(--z-below);
|
||||||
|
transition: opacity 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(130, 130, 130, 0.4) !important;
|
background: var(--pattern-color) !important;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
opacity: .4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:first-of-type {
|
&:first-of-type {
|
||||||
|
|||||||
@ -119,3 +119,6 @@ export const DEFAULT_LANG_PACK = 'android';
|
|||||||
export const LANG_PACKS = ['android', 'ios'];
|
export const LANG_PACKS = ['android', 'ios'];
|
||||||
export const TIPS_USERNAME = 'TelegramTips';
|
export const TIPS_USERNAME = 'TelegramTips';
|
||||||
export const FEEDBACK_URL = 'https://bugs.telegram.org/?tag_ids=41&sort=time';
|
export const FEEDBACK_URL = 'https://bugs.telegram.org/?tag_ids=41&sort=time';
|
||||||
|
export const DARK_THEME_BG_COLOR = '#0F0F0F';
|
||||||
|
export const DARK_THEME_PATTERN_COLOR = 'hsla(0, 0%, 3.82353%, 0.55)';
|
||||||
|
export const DEFAULT_PATTERN_COLOR = 'rgba(90, 110, 70, 0.6)';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { GlobalState } from './types';
|
import { GlobalState } from './types';
|
||||||
|
|
||||||
import { ANIMATION_LEVEL_DEFAULT, DEFAULT_MESSAGE_TEXT_SIZE_PX } from '../config';
|
import { ANIMATION_LEVEL_DEFAULT, DEFAULT_MESSAGE_TEXT_SIZE_PX, DEFAULT_PATTERN_COLOR } from '../config';
|
||||||
|
|
||||||
export const INITIAL_STATE: GlobalState = {
|
export const INITIAL_STATE: GlobalState = {
|
||||||
isLeftColumnShown: true,
|
isLeftColumnShown: true,
|
||||||
@ -101,6 +101,7 @@ export const INITIAL_STATE: GlobalState = {
|
|||||||
byKey: {
|
byKey: {
|
||||||
messageTextSize: DEFAULT_MESSAGE_TEXT_SIZE_PX,
|
messageTextSize: DEFAULT_MESSAGE_TEXT_SIZE_PX,
|
||||||
isBackgroundBlurred: true,
|
isBackgroundBlurred: true,
|
||||||
|
patternColor: DEFAULT_PATTERN_COLOR,
|
||||||
animationLevel: ANIMATION_LEVEL_DEFAULT,
|
animationLevel: ANIMATION_LEVEL_DEFAULT,
|
||||||
messageSendKeyCombo: 'enter',
|
messageSendKeyCombo: 'enter',
|
||||||
theme: 'light',
|
theme: 'light',
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export interface IAlbum {
|
|||||||
export interface ISettings extends Record<string, any> {
|
export interface ISettings extends Record<string, any> {
|
||||||
messageTextSize: number;
|
messageTextSize: number;
|
||||||
customBackground?: string;
|
customBackground?: string;
|
||||||
|
patternColor?: string;
|
||||||
isBackgroundBlurred?: boolean;
|
isBackgroundBlurred?: boolean;
|
||||||
animationLevel: 0 | 1 | 2;
|
animationLevel: 0 | 1 | 2;
|
||||||
messageSendKeyCombo: 'enter' | 'ctrl-enter';
|
messageSendKeyCombo: 'enter' | 'ctrl-enter';
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
/* eslint-disable one-var */
|
/* eslint-disable one-var */
|
||||||
/* eslint-disable one-var-declaration-per-line */
|
/* eslint-disable one-var-declaration-per-line */
|
||||||
|
|
||||||
|
import { preloadImage } from './files';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HEX > RGB
|
* HEX > RGB
|
||||||
* input: 'xxxxxx' (ex. 'ed15fa') case-insensitive
|
* input: 'xxxxxx' (ex. 'ed15fa') case-insensitive
|
||||||
@ -132,3 +134,65 @@ export function hsb2rgb([h, s, v]: [number, number, number]): [number, number, n
|
|||||||
Math.round(b * 255),
|
Math.round(b * 255),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getAverageColor(url: string): Promise<[number, number, number]> {
|
||||||
|
// Only visit every 5 pixels
|
||||||
|
const blockSize = 5;
|
||||||
|
const defaultRGB: [number, number, number] = [0, 0, 0];
|
||||||
|
let data;
|
||||||
|
let width;
|
||||||
|
let height;
|
||||||
|
let i = -4;
|
||||||
|
let length;
|
||||||
|
let rgb: [number, number, number] = [0, 0, 0];
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const context = canvas.getContext && canvas.getContext('2d');
|
||||||
|
if (!context) {
|
||||||
|
return defaultRGB;
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = await preloadImage(url);
|
||||||
|
height = image.naturalHeight || image.offsetHeight || image.height;
|
||||||
|
width = image.naturalWidth || image.offsetWidth || image.width;
|
||||||
|
canvas.height = height;
|
||||||
|
canvas.width = width;
|
||||||
|
|
||||||
|
context.drawImage(image, 0, 0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = context.getImageData(0, 0, width, height);
|
||||||
|
} catch (e) {
|
||||||
|
return defaultRGB;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = data.data.length;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-cond-assign
|
||||||
|
while ((i += blockSize * 4) < length) {
|
||||||
|
++count;
|
||||||
|
rgb[0] += data.data[i];
|
||||||
|
rgb[1] += data.data[i + 1];
|
||||||
|
rgb[2] += data.data[i + 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
rgb[0] = Math.floor(rgb[0] / count);
|
||||||
|
rgb[1] = Math.floor(rgb[1] / count);
|
||||||
|
rgb[2] = Math.floor(rgb[2] / count);
|
||||||
|
|
||||||
|
return rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
// Function was adapted from https://github.com/telegramdesktop/tdesktop/blob/35ff621b5b52f7e3553fb0f990ea13ade7101b8e/Telegram/SourceFiles/data/data_wall_paper.cpp#L518
|
||||||
|
export function getPatternColor(rgbColor: [number, number, number]) {
|
||||||
|
let [hue, saturation, value] = rgb2hsb(rgbColor);
|
||||||
|
|
||||||
|
saturation = Math.min(1, saturation + 0.05 + 0.1 * (1 - saturation));
|
||||||
|
value = value > 0.5
|
||||||
|
? Math.max(0, value * 0.65)
|
||||||
|
: Math.max(0, Math.min(1, 1 - value * 0.65));
|
||||||
|
|
||||||
|
return `hsla(${hue * 360}, ${saturation * 100}%, ${value * 100}%, .4)`;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user