feat: upgraded emoji-mart and added theme support

This commit is contained in:
William Grant 2022-09-30 13:55:38 +10:00
parent dd58d29450
commit c6af1a7468
11 changed files with 109 additions and 3000 deletions

View File

@ -83,7 +83,8 @@
"rebuild-curve25519-js": "cd node_modules/curve25519-js && yarn install && yarn build && cd ../../"
},
"dependencies": {
"@emoji-mart/data": "1.0.2",
"@emoji-mart/data": "^1.0.6",
"@emoji-mart/react": "^1.0.1",
"@reduxjs/toolkit": "^1.4.0",
"abort-controller": "3.0.0",
"auto-bind": "^4.0.0",
@ -104,7 +105,7 @@
"electron-is-dev": "^1.1.0",
"electron-localshortcut": "^3.2.1",
"electron-updater": "^4.2.2",
"emoji-mart": "5.1.0",
"emoji-mart": "^5.2.2",
"filesize": "3.6.1",
"firstline": "1.2.1",
"fs-extra": "9.0.0",

File diff suppressed because one or more lines are too long

View File

@ -170,77 +170,6 @@
}
}
.session-emoji-panel {
position: absolute;
bottom: 68px;
right: 0px;
padding: var(--margins-lg);
z-index: 5;
opacity: 0;
visibility: hidden;
transition: var(--default-duration);
button:focus {
outline: none;
}
&.show {
opacity: 1;
visibility: visible;
}
& > section.emoji-mart {
font-family: $session-font-default;
font-size: $session-font-sm;
background-color: var(--color-cell-background);
border: 1px solid var(--color-session-border);
border-radius: 8px;
padding-bottom: var(--margins-sm);
.emoji-mart-category-label {
top: -2px;
span {
font-family: $session-font-default;
padding-top: var(--margins-sm);
background-color: var(--color-cell-background);
}
}
.emoji-mart-scroll {
height: 340px;
}
.emoji-mart-category .emoji-mart-emoji span {
cursor: pointer;
}
.emoji-mart-bar:last-child {
border: none;
.emoji-mart-preview {
display: none;
}
}
&:after {
content: '';
position: absolute;
top: calc(100% - 40px);
left: calc(100% - 79px);
width: 22px;
height: 22px;
background-color: var(--color-cell-background);
transform: rotate(45deg);
border-radius: 3px;
transform: scaleY(1.4) rotate(45deg);
border: 0.7px solid var(--color-session-border);
clip-path: polygon(100% 100%, 7.2px 100%, 100% 7.2px);
}
}
}
.send-message-button {
animation: fadein var(--default-duration);

View File

@ -1,17 +1,23 @@
import React, { forwardRef, MutableRefObject, useEffect } from 'react';
import React, { forwardRef, useEffect, useState } from 'react';
import classNames from 'classnames';
import styled from 'styled-components';
import data from '@emoji-mart/data';
// @ts-ignore
import { Picker } from '../../../node_modules/emoji-mart/dist/index.cjs';
import Picker from '@emoji-mart/react';
import { useSelector } from 'react-redux';
import { getTheme } from '../../state/selectors/theme';
import { noop } from 'lodash';
import { loadEmojiPanelI18n } from '../../util/i18n';
import { FixedBaseEmoji, FixedPickerProps } from '../../types/Reaction';
import { ThemeStateType } from '../../themes/colors.js';
import { COLORS, PrimaryColorStateType, THEMES, ThemeStateType } from '../../themes/colors.js';
import { hexColorToRGB } from '../../util/hexColorToRGB';
import { getPrimaryColor } from '../../state/selectors/primaryColor';
import { i18nEmojiData } from '../../util/emoji';
export const StyledEmojiPanel = styled.div<{ isModal: boolean; theme: ThemeStateType }>`
export const StyledEmojiPanel = styled.div<{
isModal: boolean;
primaryColor: PrimaryColorStateType;
theme: ThemeStateType;
panelBackgroundRGB: string;
panelTextRGB: string;
}>`
padding: var(--margins-lg);
z-index: 5;
opacity: 0;
@ -29,41 +35,25 @@ export const StyledEmojiPanel = styled.div<{ isModal: boolean; theme: ThemeState
}
em-emoji-picker {
background-color: var(--color-cell-background);
border: 1px solid var(--color-session-border);
${props => props.panelBackgroundRGB && `background-color: rgb(${props.panelBackgroundRGB})`};
border: 1px solid var(--border-color);
padding-bottom: var(--margins-sm);
--shadow: none;
--border-radius: 8px;
--color-border: var(--color-session-border);
--font-family: var(--font-default);
--font-size: var(--font-size-sm);
--rgb-accent: 0, 247, 130; // Constants.UI.COLORS.GREEN
${props => {
switch (props.theme) {
case 'ocean-dark':
// TODO Theming
return ``;
case 'ocean-light':
// TODO Theming
return ``;
case 'classic-dark':
return `
--background-rgb: 27, 27, 27; // var(--color-cell-background)
--rgb-background: 27, 27, 27;
--rgb-color: 255, 255, 255; // var(--color-text)
--rgb-input: 27, 27, 27;
`;
case 'classic-light':
default:
return `
--background-rgb: 249, 249, 249; // var(--color-cell-background)
--rgb-background: 249, 249, 249;
--rgb-color: 0, 0, 0; // var(--color-text)
--rgb-input: 249, 249, 249;
`;
}
}}
--shadow: none;
--border-radius: 8px;
--color-border: var(--border-color);
--color-border-over: var(--border-color);
--background-rgb: ${props => props.panelBackgroundRGB};
--rgb-background: ${props => props.panelBackgroundRGB};
--rgb-color: ${props => props.panelTextRGB};
--rgb-input: ${props => props.panelBackgroundRGB};
--rgb-accent: ${props =>
hexColorToRGB(
props.primaryColor
? (COLORS.PRIMARY as any)[`${props.primaryColor.toUpperCase()}`]
: COLORS.PRIMARY.GREEN
)};
${props =>
!props.isModal &&
@ -75,14 +65,14 @@ export const StyledEmojiPanel = styled.div<{ isModal: boolean; theme: ThemeState
left: calc(100% - 79px);
width: 22px;
height: 22px;
background-color: var(--color-cell-background);
transform: rotate(45deg);
border-radius: 3px;
transform: scaleY(1.4) rotate(45deg);
border: 0.7px solid var(--color-session-border);
border: 0.7px solid var(--border-color);
clip-path: polygon(100% 100%, 7.2px 100%, 100% 7.2px);
${props.panelBackgroundRGB && `background-color: rgb(${props.panelBackgroundRGB})`};
}
`}
`};
}
`;
@ -90,6 +80,7 @@ type Props = {
onEmojiClicked: (emoji: FixedBaseEmoji) => void;
show: boolean;
isModal?: boolean;
// NOTE Currently this doesn't work but we have a PR waiting to be merged to resolve this. William Grant 30/09/2022
onKeyDown?: (event: any) => void;
};
@ -102,45 +93,51 @@ const pickerProps: FixedPickerProps = {
export const SessionEmojiPanel = forwardRef<HTMLDivElement, Props>((props: Props, ref) => {
const { onEmojiClicked, show, isModal = false, onKeyDown } = props;
const primaryColor = useSelector(getPrimaryColor);
const theme = useSelector(getTheme);
const emojiPanelTheme = theme.includes('light') ? 'light' : 'dark';
const pickerRef = ref as MutableRefObject<HTMLDivElement>;
const [panelBackgroundRGB, setPanelBackgroundRGB] = useState('');
const [panelTextRGB, setPanelTextRGB] = useState('');
useEffect(() => {
let isCancelled = false;
if (pickerRef.current !== null) {
if (pickerRef.current.children.length === 0) {
loadEmojiPanelI18n()
.then(async i18n => {
if (isCancelled) {
return;
}
// tslint:disable-next-line: no-unused-expression
new Picker({
data,
ref,
i18n,
theme: emojiPanelTheme,
onEmojiSelect: onEmojiClicked,
onKeyDown,
...pickerProps,
});
})
.catch(noop);
}
switch (theme) {
case 'ocean-dark':
setPanelBackgroundRGB(hexColorToRGB(THEMES.OCEAN_DARK.COLOR1));
setPanelTextRGB(hexColorToRGB(THEMES.OCEAN_DARK.COLOR6));
break;
case 'ocean-light':
setPanelBackgroundRGB(hexColorToRGB(THEMES.OCEAN_LIGHT.COLOR7!));
setPanelTextRGB(hexColorToRGB(THEMES.OCEAN_LIGHT.COLOR1));
break;
case 'classic-dark':
setPanelBackgroundRGB(hexColorToRGB(THEMES.CLASSIC_DARK.COLOR1));
setPanelTextRGB(hexColorToRGB(THEMES.CLASSIC_DARK.COLOR6));
break;
case 'classic-light':
default:
setPanelBackgroundRGB(hexColorToRGB(THEMES.CLASSIC_LIGHT.COLOR6));
setPanelTextRGB(hexColorToRGB(THEMES.CLASSIC_LIGHT.COLOR0));
break;
}
return () => {
isCancelled = true;
};
}, [data, pickerProps]);
}, [theme]);
return (
<StyledEmojiPanel
isModal={isModal}
primaryColor={primaryColor}
theme={theme}
panelBackgroundRGB={panelBackgroundRGB}
panelTextRGB={panelTextRGB}
className={classNames(show && 'show')}
ref={ref}
/>
>
<Picker
theme={theme.includes('light') ? 'light' : 'dark'}
i18n={i18nEmojiData}
onEmojiSelect={onEmojiClicked}
onKeyDown={onKeyDown}
{...pickerProps}
/>
</StyledEmojiPanel>
);
});

View File

@ -2,7 +2,7 @@ import React from 'react';
import { SuggestionDataItem } from 'react-mentions';
import styled from 'styled-components';
// @ts-ignore
import { SearchIndex } from '../../../../node_modules/emoji-mart/dist/index.cjs';
import { SearchIndex } from 'emoji-mart';
import { searchSync } from '../../../util/emoji.js';
const EmojiQuickResult = styled.span`

View File

@ -109,7 +109,7 @@ export const MessageContextMenu = (props: Props) => {
const emojiPanelRef = useRef<HTMLDivElement>(null);
const [showEmojiPanel, setShowEmojiPanel] = useState(false);
// emoji-mart v5.1 default dimensions
// emoji-mart v5.2.2 default dimensions
const emojiPanelWidth = 354;
const emojiPanelHeight = 435;

View File

@ -21,7 +21,6 @@ import { OpenGroupData } from '../data/opengroups';
import { loadKnownBlindedKeys } from '../session/apis/open_group_api/sogsv3/knownBlindedkeys';
import nativeEmojiData from '@emoji-mart/data';
import { initialiseEmojiData } from '../util/emoji';
import { loadEmojiPanelI18n } from '../util/i18n';
// tslint:disable: max-classes-per-file
// Globally disable drag and drop
@ -176,7 +175,7 @@ Storage.onready(async () => {
await window.Events.setThemeSetting(newThemeSetting);
try {
initialiseEmojiData(nativeEmojiData);
await initialiseEmojiData(nativeEmojiData);
await AttachmentDownloads.initAttachmentPaths();
await Promise.all([
@ -184,7 +183,6 @@ Storage.onready(async () => {
BlockedNumberController.load(),
OpenGroupData.opengroupRoomsLoad(),
loadKnownBlindedKeys(),
loadEmojiPanelI18n(),
]);
} catch (error) {
window.log.error(

View File

@ -1,8 +1,13 @@
import { AbortSignal } from 'abort-controller';
import { getEmojiDataFromNative } from 'emoji-mart';
import { Data } from '../../../../data/data';
import { ConversationModel } from '../../../../models/conversation';
import { Action, OpenGroupReactionResponse, Reaction } from '../../../../types/Reaction';
import { getEmojiDataFromNative } from '../../../../util/emoji';
import {
Action,
FixedBaseEmoji,
OpenGroupReactionResponse,
Reaction,
} from '../../../../types/Reaction';
import { Reactions } from '../../../../util/reactions';
import { OnionSending } from '../../../onions/onionSend';
import { UserUtils } from '../../../utils';
@ -68,7 +73,9 @@ export const sendSogsReactionOnionV4 = async (
// The SOGS endpoint supports any text input so we need to make sure we are sending a valid unicode emoji
// for an invalid input we use https://emojipedia.org/frame-with-an-x/ as a replacement since it cannot rendered as an emoji but is valid unicode
const emoji = getEmojiDataFromNative(reaction.emoji) ? reaction.emoji : '🖾';
// NOTE emoji-mart v5.2.2 types for getEmojiDataFromNative are broken
// @ts-ignore
const emoji = (getEmojiDataFromNative(reaction.emoji) as FixedBaseEmoji) ? reaction.emoji : '🖾';
const endpoint = `/room/${room}/reaction/${reaction.id}/${emoji}`;
const method = reaction.action === Action.REACT ? 'PUT' : 'DELETE';
const serverPubkey = allValidRoomInfos[0].serverPublicKey;

View File

@ -1,4 +1,4 @@
import { EmojiSet } from 'emoji-mart';
import { EmojiSet, PartialI18n } from 'emoji-mart';
export const reactionLimit: number = 6;
@ -76,6 +76,7 @@ export interface FixedPickerProps {
noResultsEmoji?: string | undefined;
previewPosition?: 'bottom' | 'top' | 'none' | undefined;
skinTonePosition?: 'preview' | 'search' | 'none';
i18n?: PartialI18n | undefined;
onEmojiSelect?: (emoji: FixedBaseEmoji) => void;
onClickOutside?: () => void;
onKeyDown?: (event: any) => void;
@ -83,7 +84,6 @@ export interface FixedPickerProps {
getImageURL?: () => void;
getSpritesheetURL?: () => void;
// Below here I'm currently unsure of usage
// i18n?: PartialI18n | undefined;
// style?: React.CSSProperties | undefined;
// color?: string | undefined;
// skin?: EmojiSkin | undefined;

View File

@ -1,4 +1,7 @@
import { FixedBaseEmoji, NativeEmojiData } from '../types/Reaction';
// @ts-ignore
import { init, PartialI18n } from 'emoji-mart';
import { loadEmojiPanelI18n } from './i18n';
export type SizeClassType = 'default' | 'small' | 'medium' | 'large' | 'jumbo';
@ -40,8 +43,9 @@ export function getEmojiSizeClass(str: string): SizeClassType {
}
export let nativeEmojiData: NativeEmojiData | null = null;
export let i18nEmojiData: PartialI18n | null = null;
export function initialiseEmojiData(data: any) {
export async function initialiseEmojiData(data: any): Promise<void> {
const ariaLabels: Record<string, string> = {};
Object.entries(data.emojis).forEach(([key, value]: [string, any]) => {
value.search = `,${[
@ -73,6 +77,11 @@ export function initialiseEmojiData(data: any) {
data.ariaLabels = ariaLabels;
nativeEmojiData = data;
i18nEmojiData = await loadEmojiPanelI18n();
// Data needs to be initialised once per page load for the emoji components
// See https://github.com/missive/emoji-mart#%EF%B8%8F%EF%B8%8F-headless-search
init({ data, i18n: i18nEmojiData });
}
// Synchronous version of Emoji Mart's SearchIndex.search()
@ -148,23 +157,3 @@ export function searchSync(query: string, args?: any): Array<any> {
}
return results;
}
// No longer exists on emoji-mart v5.1
export function getEmojiDataFromNative(nativeString: string): FixedBaseEmoji | null {
if (!nativeEmojiData) {
return null;
}
const matches = Object.values(nativeEmojiData.emojis).filter((emoji: any) => {
const skinMatches = (emoji as FixedBaseEmoji).skins.filter((skin: any) => {
return skin.native === nativeString;
});
return skinMatches.length > 0;
});
if (matches.length === 0) {
return null;
}
return matches[0] as FixedBaseEmoji;
}

View File

@ -645,10 +645,15 @@
global-agent "^3.0.0"
global-tunnel-ng "^2.7.1"
"@emoji-mart/data@1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@emoji-mart/data/-/data-1.0.2.tgz#2b94c5b5f2c79611c12238438dad9516576a09ab"
integrity sha512-+ZdzBM4llDJJvjuCEsdOYVoSlNA16MMmxKG3oF5LARkwhx6N5clr6phzneWV1qIwJsywqwG7NaBjH8DV6yzjcA==
"@emoji-mart/data@^1.0.6":
version "1.0.6"
resolved "https://registry.yarnpkg.com/@emoji-mart/data/-/data-1.0.6.tgz#68f71be5e023653a3f9f73f4beadfd50848e2131"
integrity sha512-8wu3ec/kLCB0Y3K+pOKyY6Ob+xtQu3XhZvntdrpOTUQZ/PO6FW5PpFw7RE1kQ/up1fsVSJBl5mZ8Gs4SPwTYeg==
"@emoji-mart/react@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@emoji-mart/react/-/react-1.0.1.tgz#46b6a2e92faf16fa9b7f9471f137fa2e3d1e8ac3"
integrity sha512-ALhLD96BOL5w+a4NI5NpmfqfF1aVjjj2qJE0dLst/OhjBfVmpteWNgn/h8LZy9ulU6AnbeS+13KnPFzDjCvRRw==
"@emotion/is-prop-valid@^0.8.8":
version "0.8.8"
@ -3823,10 +3828,10 @@ elliptic@^6.5.3:
minimalistic-assert "^1.0.1"
minimalistic-crypto-utils "^1.0.1"
emoji-mart@5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/emoji-mart/-/emoji-mart-5.1.0.tgz#8a36a872e1297747342d1385bd7b7141ac2f4365"
integrity sha512-ytXgeemyw4FormPQqWd35Vh06ZSnQFhVUqW51kASZzzjhQOPSGtiN3VCC7vDq94Pkxmsbet+Gps/qj5N90mEnw==
emoji-mart@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/emoji-mart/-/emoji-mart-5.2.2.tgz#1c093ffc19554dd6edfcfeec9aca43ff38bcc16e"
integrity sha512-BvcrX+Ps9MxSVEjnvxupclU3MBD6WVC4WZOY26csfC6oFdaWpFhdrzeVNVBmCLPOmzY1SE0aAsqZJRNVbZ1yhQ==
emoji-regex@^8.0.0:
version "8.0.0"