1
0
Fork 0
mirror of https://github.com/mastodon/mastodon.git synced 2024-08-20 21:08:15 -07:00

Compare commits

...

21 commits

Author SHA1 Message Date
たいち ひ
f5c9f12295
Merge 8585e26aae into a50c8e951f 2024-07-31 14:07:08 +00:00
Claire
a50c8e951f
Fix issue with grouped notifications UI due to recent API change (#31224) 2024-07-31 13:23:08 +00:00
Claire
2c1e75727d
Change filtered notification banner design to take up less space (#31222) 2024-07-31 12:36:08 +00:00
taichi221228
8585e26aae Remove no-unsafe-return rule from eslint-disable in emoji_utils.ts
Removed the no-unsafe-return rule from the eslint-disable and eslint-enable comments in the emoji_utils.ts file, as it is no longer required. This helps to improve code readability and reduces unnecessary complexity.
2024-05-02 15:54:22 +09:00
taichi221228
28b4212206 Remove measureScrollbar function from emoji_utils
The measureScrollbar function has been deleted from the emoji_utils.ts file. This simplifies the module export list by removing a function that was unnecessary or out of context.
2024-05-02 15:40:06 +09:00
taichi221228
2bcd355289 Add comments for improving code modularity in emoji_utils.ts
New comments were added in the 'emoji_utils.ts' file to suggest separating the general array operations from the emoji-related functions into different files. This aims to increase the readability and maintainability of the code.
2024-05-02 15:39:52 +09:00
taichi221228
bee50d831c Remove deepMerge function from emoji_utils
The deepMerge function was removed from the emoji_utils file. This function was not used in the code and was deemed unnecessary, hence it was removed. Also, with its removal, the corresponding import was deleted.
2024-05-02 15:37:11 +09:00
taichi221228
22b75382d1 Merge branch 'refs/heads/main' into refactor/rewrite-emoji_utils-to-ts 2024-05-02 15:36:01 +09:00
taichi221228
78248aea26 Refactor intersect function in emoji_utils.ts
This commit refactors the intersect function in the emoji_utils.ts file. The `a` and `b` parameters are more explicitly defined as empty arrays, and the indexOf method is replaced with the includes method for better readability and performance. Additionally, the placement of the eslint-disable directive has been adjusted to improve linting results.
2024-05-02 13:38:48 +09:00
taichi221228
33bd4e67e9 Refactor uniq function for TS compliance and efficiency improvements
This commit refactors the `uniq` function found in the emoji utilities to accept an explicitly defined array as an argument, improving overall TypeScript compliance. It also replaces the `.indexOf` method with the more efficient `.includes` method for better performance.
2024-05-02 13:37:22 +09:00
taichi221228
5a23b7ed4f Added clarifying comment to emoji_utils.ts function
A comment has been added to the 'getData' function in emoji_utils.ts to clarify that the source code version does not match that of DefinitelyTyped. The note also highlights the challenges in maintaining type consistency due to outdated and non-existing properties.
2024-05-02 13:36:19 +09:00
taichi221228
5735e09738 Refactor emoji utilities by introducing type aliases
This commit introduces type aliases to the emoji utilities in JavaScript code, specifically `RawEmoji` and `GetDataArgs`. These changes help to simplify function signatures. By extracting complex types into separate type aliases, the code readability has been improved significantly.
2024-05-02 13:22:56 +09:00
taichi221228
ca30b5b3ef Remove unused JSON constant from emoji utilities
The unused constant `_JSON` was removed from the `emoji_utils.ts` file. This decluttering enhances the manageability and readability of the code.
2024-05-02 11:25:55 +09:00
taichi221228
d6015029e2 Refactor emoji utils in mastodon features
The refactoring of emoji utilities in the mastodon feature has been done to enhance readability and maintenance of the code. The types and functions involved in sanitizing and getting data have been clarified, including the introduction of an interface for skin tones. Additionally, erroneous typescript comments have been removed and the emojis type has been exported for further use.
2024-05-02 11:22:49 +09:00
taichi221228
a603a353d4 Add comments for skin_tone in emoji_utils.ts
This commit adds detailed comments explaining the usage of `skin_tone` in emoji_utils.ts, noting its absence in the type definition link and the need for a separate type with DefinitelyTyped. It also highlights potential mismatch issues between versions of `@types/emoji-mart` and `emoji-mart`, given they have different maintainers and packages.
2024-04-24 10:22:32 +09:00
taichi221228
2236a16d55 Refactor and improve emoji sanitization function
The emoji sanitization function has been refactored for better handling of emoji variations. New types have been imported from 'emoji-mart', allowing for a more precise type assignment for the emoji input and output. Bound checking operations have been adjusted to better accommodate and handle custom emojis and skin variations.
2024-04-24 10:04:33 +09:00
taichi221228
3c7ccb2e62 Refactor unifiedToNative function in emoji_utils
The unifiedToNative function in emoji_utils.ts has been refactored for cleaner coding practices. Lint disabling has been moved to a different part of the code, and type checking has been revised to specify that the input unified is of type Emoji['unified']. The function's variables have been updated to const for better securities against undesired changes.
2024-04-24 10:01:27 +09:00
taichi221228
1c4cf654d7 Remove unnecessary stringFromCodePoint function polyfill in emoji_utils
Removed the unnecessary stringFromCodePoint function polyfill from the emoji_utils.ts file. Instead, we use the built-in JavaScript function String.fromCodePoint to simplify the code.
2024-04-24 10:00:13 +09:00
taichi221228
47b82ee7c8 Refactor buildSearch function in emoji_utils.ts
The buildSearch function has been refactored for better coding practices. More explicit typings and declaration were used, and the logic related to array search has been simplified for better readability. The 'eslint-disable' comment line was moved to a more appropriate location after these changes.
2024-04-24 09:55:00 +09:00
taichi221228
6d873032de Add '@ts-expect-error' to suppress TypeScript errors 2024-04-24 09:53:56 +09:00
taichi221228
4f337b9804 Rename emoji_utils.js to TS 2024-04-23 22:20:53 +09:00
8 changed files with 232 additions and 292 deletions

View file

@ -60,7 +60,7 @@ export interface BaseNotificationGroupJSON {
interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON { interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON {
type: NotificationWithStatusType; type: NotificationWithStatusType;
status: ApiStatusJSON; status_id: string;
} }
interface NotificationWithStatusJSON extends BaseNotificationJSON { interface NotificationWithStatusJSON extends BaseNotificationJSON {

View file

@ -8,7 +8,7 @@ import type { Search, ShortCodesToEmojiData } from './emoji_compressed';
import emojiCompressed from './emoji_compressed'; import emojiCompressed from './emoji_compressed';
import { unicodeToUnifiedName } from './unicode_to_unified_name'; import { unicodeToUnifiedName } from './unicode_to_unified_name';
type Emojis = { export type Emojis = {
[key in NonNullable<keyof ShortCodesToEmojiData>]: { [key in NonNullable<keyof ShortCodesToEmojiData>]: {
native: BaseEmoji['native']; native: BaseEmoji['native'];
search: Search; search: Search;

View file

@ -1,258 +0,0 @@
// This code is largely borrowed from:
// https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/index.js
import * as data from './emoji_mart_data_light';
const buildSearch = (data) => {
const search = [];
let addToSearch = (strings, split) => {
if (!strings) {
return;
}
(Array.isArray(strings) ? strings : [strings]).forEach((string) => {
(split ? string.split(/[-|_|\s]+/) : [string]).forEach((s) => {
s = s.toLowerCase();
if (search.indexOf(s) === -1) {
search.push(s);
}
});
});
};
addToSearch(data.short_names, true);
addToSearch(data.name, true);
addToSearch(data.keywords, false);
addToSearch(data.emoticons, false);
return search.join(',');
};
const _String = String;
const stringFromCodePoint = _String.fromCodePoint || function () {
let MAX_SIZE = 0x4000;
let codeUnits = [];
let highSurrogate;
let lowSurrogate;
let index = -1;
let length = arguments.length;
if (!length) {
return '';
}
let result = '';
while (++index < length) {
let codePoint = Number(arguments[index]);
if (
!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
codePoint < 0 || // not a valid Unicode code point
codePoint > 0x10FFFF || // not a valid Unicode code point
Math.floor(codePoint) !== codePoint // not an integer
) {
throw RangeError('Invalid code point: ' + codePoint);
}
if (codePoint <= 0xFFFF) { // BMP code point
codeUnits.push(codePoint);
} else { // Astral code point; split in surrogate halves
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
codePoint -= 0x10000;
highSurrogate = (codePoint >> 10) + 0xD800;
lowSurrogate = (codePoint % 0x400) + 0xDC00;
codeUnits.push(highSurrogate, lowSurrogate);
}
if (index + 1 === length || codeUnits.length > MAX_SIZE) {
result += String.fromCharCode.apply(null, codeUnits);
codeUnits.length = 0;
}
}
return result;
};
const _JSON = JSON;
const COLONS_REGEX = /^(?::([^:]+):)(?::skin-tone-(\d):)?$/;
const SKINS = [
'1F3FA', '1F3FB', '1F3FC',
'1F3FD', '1F3FE', '1F3FF',
];
function unifiedToNative(unified) {
let unicodes = unified.split('-'),
codePoints = unicodes.map((u) => `0x${u}`);
return stringFromCodePoint.apply(null, codePoints);
}
function sanitize(emoji) {
let { name, short_names, skin_tone, skin_variations, emoticons, unified, custom, imageUrl } = emoji,
id = emoji.id || short_names[0],
colons = `:${id}:`;
if (custom) {
return {
id,
name,
colons,
emoticons,
custom,
imageUrl,
};
}
if (skin_tone) {
colons += `:skin-tone-${skin_tone}:`;
}
return {
id,
name,
colons,
emoticons,
unified: unified.toLowerCase(),
skin: skin_tone || (skin_variations ? 1 : null),
native: unifiedToNative(unified),
};
}
function getSanitizedData() {
return sanitize(getData(...arguments));
}
function getData(emoji, skin, set) {
let emojiData = {};
if (typeof emoji === 'string') {
let matches = emoji.match(COLONS_REGEX);
if (matches) {
emoji = matches[1];
if (matches[2]) {
skin = parseInt(matches[2]);
}
}
if (Object.hasOwn(data.short_names, emoji)) {
emoji = data.short_names[emoji];
}
if (Object.hasOwn(data.emojis, emoji)) {
emojiData = data.emojis[emoji];
}
} else if (emoji.id) {
if (Object.hasOwn(data.short_names, emoji.id)) {
emoji.id = data.short_names[emoji.id];
}
if (Object.hasOwn(data.emojis, emoji.id)) {
emojiData = data.emojis[emoji.id];
skin = skin || emoji.skin;
}
}
if (!Object.keys(emojiData).length) {
emojiData = emoji;
emojiData.custom = true;
if (!emojiData.search) {
emojiData.search = buildSearch(emoji);
}
}
emojiData.emoticons = emojiData.emoticons || [];
emojiData.variations = emojiData.variations || [];
if (emojiData.skin_variations && skin > 1 && set) {
emojiData = JSON.parse(_JSON.stringify(emojiData));
let skinKey = SKINS[skin - 1],
variationData = emojiData.skin_variations[skinKey];
if (!variationData.variations && emojiData.variations) {
delete emojiData.variations;
}
if (variationData[`has_img_${set}`]) {
emojiData.skin_tone = skin;
for (let k in variationData) {
let v = variationData[k];
emojiData[k] = v;
}
}
}
if (emojiData.variations && emojiData.variations.length) {
emojiData = JSON.parse(_JSON.stringify(emojiData));
emojiData.unified = emojiData.variations.shift();
}
return emojiData;
}
function uniq(arr) {
return arr.reduce((acc, item) => {
if (acc.indexOf(item) === -1) {
acc.push(item);
}
return acc;
}, []);
}
function intersect(a, b) {
const uniqA = uniq(a);
const uniqB = uniq(b);
return uniqA.filter(item => uniqB.indexOf(item) >= 0);
}
function deepMerge(a, b) {
let o = {};
for (let key in a) {
let originalValue = a[key],
value = originalValue;
if (Object.hasOwn(b, key)) {
value = b[key];
}
if (typeof value === 'object') {
value = deepMerge(originalValue, value);
}
o[key] = value;
}
return o;
}
// https://github.com/sonicdoe/measure-scrollbar
function measureScrollbar() {
const div = document.createElement('div');
div.style.width = '100px';
div.style.height = '100px';
div.style.overflow = 'scroll';
div.style.position = 'absolute';
div.style.top = '-9999px';
document.body.appendChild(div);
const scrollbarWidth = div.offsetWidth - div.clientWidth;
document.body.removeChild(div);
return scrollbarWidth;
}
export {
getData,
getSanitizedData,
uniq,
intersect,
deepMerge,
unifiedToNative,
measureScrollbar,
};

View file

@ -0,0 +1,221 @@
// This code is largely borrowed from:
// https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/index.js
import type {
BaseEmoji,
CustomEmoji,
EmojiSkin,
PickerProps,
} from 'emoji-mart';
import type { Emoji, SkinVariation } from 'emoji-mart/dist-es/utils/data';
import * as data from './emoji_mart_data_light';
type Data = Pick<Emoji, 'short_names' | 'name' | 'keywords' | 'emoticons'>;
const buildSearch = (data: Data) => {
const search: string[] = [];
const addToSearch = (strings: Data[keyof Data], split: boolean) => {
if (!strings) {
return;
}
(Array.isArray(strings) ? strings : [strings]).forEach((string) => {
(split ? string.split(/[-|_|\s]+/) : [string]).forEach((s) => {
s = s.toLowerCase();
if (!search.includes(s)) {
search.push(s);
}
});
});
};
addToSearch(data.short_names, true);
addToSearch(data.name, true);
addToSearch(data.keywords, false);
addToSearch(data.emoticons, false);
return search.join(',');
};
const COLONS_REGEX = /^(?::([^:]+):)(?::skin-tone-(\d):)?$/;
const SKINS = ['1F3FA', '1F3FB', '1F3FC', '1F3FD', '1F3FE', '1F3FF'];
function unifiedToNative(unified: Emoji['unified']) {
const unicodes = unified?.split('-') ?? [];
const codePoints = unicodes.map((u) => +`0x${u}`);
return String.fromCodePoint(...codePoints);
}
/*
* `skin_tone` is used [here]{@link node_modules/emoji-mart/dist-es/utils/index.js#19}, but is not found in the [type definition]{@link node_modules/@types/emoji-mart/dist-es/utils/emoji-index/nimble-emoji-index.d.ts}.
* `emoji-mart` does not come with a built-in type, so you need to add a separate type with DefinitelyTyped.
* The type and implementation have different maintainers and packages, so the installed versions of `@types/emoji-mart` and `emoji-mart` may not match.
*/
interface SkinTone {
skin_tone?: EmojiSkin;
}
type RawEmoji = BaseEmoji &
CustomEmoji &
Pick<Emoji, 'skin_variations'> &
Pick<PickerProps, 'custom'> &
SkinTone;
function sanitize(
emoji: RawEmoji,
):
| BaseEmoji
| (Omit<CustomEmoji, 'short_names'> & Pick<PickerProps, 'custom'>) {
const {
name = '',
short_names = [],
skin_tone,
skin_variations,
emoticons = [],
unified = '',
custom,
imageUrl,
} = emoji;
const id = emoji.id || short_names[0];
let colons = `:${id}:`;
if (custom) {
return {
id,
name,
colons,
emoticons,
custom,
imageUrl,
};
}
if (skin_tone) {
colons += `:skin-tone-${skin_tone}:`;
}
return {
id,
name,
colons,
emoticons,
unified: unified.toLowerCase(),
skin: skin_tone ?? (skin_variations ? 1 : null),
native: unifiedToNative(unified),
};
}
type GetDataArgs = [
emoji: BaseEmoji | string,
skin: EmojiSkin | null,
set?: 'apple' | 'google' | 'twitter' | 'facebook' | 'emojione' | 'messenger',
];
function getSanitizedData(...args: GetDataArgs) {
return sanitize(getData(...args));
}
function getData(...[emoji, skin, set]: GetDataArgs) {
/*
The version of [the referenced source code]{@link https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/index.js} does not match that of DefinitelyTyped.
It is also old, and non-existent properties have been added or removed, making it difficult to achieve type consistency.
*/
/* eslint-disable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
let emojiData: any = {};
if (typeof emoji === 'string') {
const matches = emoji.match(COLONS_REGEX);
if (matches) {
emoji = matches[1];
const int = parseInt(matches[2]);
const isValid = (value: number): value is EmojiSkin =>
([1, 2, 3, 4, 5, 6] satisfies EmojiSkin[]).some(
(skin) => skin === value,
);
if (isValid(int)) {
skin = int;
}
}
if (Object.hasOwn(data.short_names, emoji)) {
emoji = data.short_names[emoji];
}
if (Object.hasOwn(data.emojis, emoji)) {
emojiData = data.emojis[emoji];
}
} else if (emoji.id) {
if (Object.hasOwn(data.short_names, emoji.id)) {
emoji.id = data.short_names[emoji.id];
}
if (Object.hasOwn(data.emojis, emoji.id)) {
emojiData = data.emojis[emoji.id];
skin = skin ?? emoji.skin;
}
}
if (!Object.keys(emojiData).length && typeof emoji === 'object') {
emojiData = emoji;
emojiData.custom = true;
if (!emojiData.search) {
emojiData.search = buildSearch(emoji);
}
}
emojiData.emoticons = emojiData.emoticons || [];
emojiData.variations = emojiData.variations || [];
if (emojiData.skin_variations && skin && skin > 1 && set) {
const skinKey = SKINS[skin - 1];
const variationData = emojiData.skin_variations[skinKey];
if (variationData[`has_img_${set}`]) {
emojiData.skin_tone = skin;
for (const k in variationData) {
type K = keyof typeof emojiData;
emojiData[k as K] = variationData[k as keyof SkinVariation];
}
}
}
if (emojiData.variations.length) {
emojiData.unified = emojiData.variations.shift();
}
return emojiData as RawEmoji;
/* eslint-enable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */
}
// TODO: General array operations not related to emojis. Consider separating them into separate files.
function uniq(arr: []) {
return arr.reduce((acc, item) => {
if (!acc.includes(item)) {
acc.push(item);
}
return acc;
}, []);
}
// TODO: General array operations not related to emojis. Consider separating them into separate files.
function intersect(a: [], b: []) {
const uniqA = uniq(a);
const uniqB = uniq(b);
return uniqA.filter((item) => uniqB.includes(item));
}
export { getData, getSanitizedData, uniq, intersect, unifiedToNative };

View file

@ -49,21 +49,14 @@ export const FilteredNotificationsBanner: React.FC = () => {
<span> <span>
<FormattedMessage <FormattedMessage
id='filtered_notifications_banner.pending_requests' id='filtered_notifications_banner.pending_requests'
defaultMessage='Notifications from {count, plural, =0 {no one} one {one person} other {# people}} you may know' defaultMessage='From {count, plural, =0 {no one} one {one person} other {# people}} you may know'
values={{ count: policy.summary.pending_requests_count }} values={{ count: policy.summary.pending_requests_count }}
/> />
</span> </span>
</div> </div>
<div className='filtered-notifications-banner__badge'> <div className='filtered-notifications-banner__badge'>
<div className='filtered-notifications-banner__badge__badge'> {toCappedNumber(policy.summary.pending_notifications_count)}
{toCappedNumber(policy.summary.pending_notifications_count)}
</div>
<FormattedMessage
id='filtered_notifications_banner.mentions'
defaultMessage='{count, plural, one {mention} other {mentions}}'
values={{ count: policy.summary.pending_notifications_count }}
/>
</div> </div>
</Link> </Link>
); );

View file

@ -300,8 +300,7 @@
"filter_modal.select_filter.subtitle": "Use an existing category or create a new one", "filter_modal.select_filter.subtitle": "Use an existing category or create a new one",
"filter_modal.select_filter.title": "Filter this post", "filter_modal.select_filter.title": "Filter this post",
"filter_modal.title.status": "Filter a post", "filter_modal.title.status": "Filter a post",
"filtered_notifications_banner.mentions": "{count, plural, one {mention} other {mentions}}", "filtered_notifications_banner.pending_requests": "From {count, plural, =0 {no one} one {one person} other {# people}} you may know",
"filtered_notifications_banner.pending_requests": "Notifications from {count, plural, =0 {no one} one {one person} other {# people}} you may know",
"filtered_notifications_banner.title": "Filtered notifications", "filtered_notifications_banner.title": "Filtered notifications",
"firehose.all": "All", "firehose.all": "All",
"firehose.local": "This server", "firehose.local": "This server",

View file

@ -124,9 +124,9 @@ export function createNotificationGroupFromJSON(
case 'mention': case 'mention':
case 'poll': case 'poll':
case 'update': { case 'update': {
const { status, ...groupWithoutStatus } = group; const { status_id: statusId, ...groupWithoutStatus } = group;
return { return {
statusId: status.id, statusId,
sampleAccountIds, sampleAccountIds,
...groupWithoutStatus, ...groupWithoutStatus,
}; };

View file

@ -10171,25 +10171,10 @@ noscript {
} }
&__badge { &__badge {
display: flex; background: $ui-button-background-color;
align-items: center; color: $white;
border-radius: 999px; border-radius: 100px;
background: var(--background-border-color); padding: 2px 8px;
color: $darker-text-color;
padding: 4px;
padding-inline-end: 8px;
gap: 6px;
font-weight: 500;
font-size: 11px;
line-height: 16px;
word-break: keep-all;
&__badge {
background: $ui-button-background-color;
color: $white;
border-radius: 100px;
padding: 2px 8px;
}
} }
} }