Skip to content

Commit

Permalink
feat: allow lazy loading emojis
Browse files Browse the repository at this point in the history
  • Loading branch information
arturovt committed Jul 26, 2023
1 parent 4424719 commit ce0ea13
Show file tree
Hide file tree
Showing 25 changed files with 451 additions and 276 deletions.
45 changes: 15 additions & 30 deletions scripts/build-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import fs from 'fs';
import path from 'node:path';
import inflection from 'inflection';
import stringifyObject from 'stringify-object';
import { fileURLToPath } from "node:url";

import { EmojiData } from './emoji.js';
import { EmojiData } from './emoji';

// @ts-ignore
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

import * as emojiLib from 'emojilib';

// cast types to emojiData
// @ts-expect-error
const emojiData: EmojiData[] = emojiDataRaw.default;
Expand All @@ -29,14 +29,14 @@ const catPairs = [
['Travel & Places', 'places'],
['Objects', 'objects'],
['Symbols', 'symbols'],
['Flags', 'flags']
['Flags', 'flags'],
];
const sets = ['apple', 'google', 'twitter', 'facebook'];

const unifiedToNative = (unified: string) => {
const codePoints = unified.split('-').map(u => parseInt(`0x${u}`, 16));
return String.fromCodePoint(...codePoints);
}
};

catPairs.forEach((category, i) => {
const [name, id] = category;
Expand Down Expand Up @@ -136,9 +136,7 @@ emojiData.forEach((datum: any) => {
delete datum.skin_variations;
}

datum.shortNames = datum.short_names.filter(
(i: any) => i !== datum.short_name
);
datum.shortNames = datum.short_names.filter((i: any) => i !== datum.short_name);
delete datum.short_names;

// renaming
Expand Down Expand Up @@ -192,51 +190,38 @@ const people = categories[1];
const smileysAndPeople = {
id: 'people',
name: 'Smileys & People',
emojis: [
...smileys.emojis.slice(0, 114),
...people.emojis,
...smileys.emojis.slice(114)
]
emojis: [...smileys.emojis.slice(0, 114), ...people.emojis, ...smileys.emojis.slice(114)],
};

categories.unshift(smileysAndPeople);
categories.splice(1, 2);

const sEmojis = stringifyObject(emojis, {
inlineCharacterLimit: 25,
indent: ' '
indent: ' ',
});
let doc = `import { CompressedEmojiData } from './data.interfaces';
let doc = `import type { CompressedEmojiData } from '@ctrl/ngx-emoji-mart/ngx-emoji';
export const emojis: CompressedEmojiData[] = ${sEmojis};
`;
fs.writeFileSync(
path.join(__dirname, '../src/lib/picker/ngx-emoji/data/emojis.ts'),
doc
);
fs.writeFileSync(path.join(__dirname, '../src/lib/picker/emojis/emojis.ts'), doc);

const sCategories = stringifyObject(categories, {
inlineCharacterLimit: 25,
indent: ' '
indent: ' ',
});
doc = `import { EmojiCategory } from './data.interfaces';
doc = `import type { EmojiCategory } from '@ctrl/ngx-emoji-mart/ngx-emoji';
export const categories: EmojiCategory[] = ${sCategories};
`;
fs.writeFileSync(
path.join(__dirname, '../src/lib/picker/ngx-emoji/data/categories.ts'),
doc
);
fs.writeFileSync(path.join(__dirname, '../src/lib/picker/emojis/categories.ts'), doc);

const sSkins = stringifyObject(skins, {
inlineCharacterLimit: 25,
indent: ' '
indent: ' ',
});
doc = `import { SkinData } from './data.interfaces';
doc = `import type { SkinData } from '@ctrl/ngx-emoji-mart/ngx-emoji';
export const skins: SkinData[] = ${sSkins};
`;
fs.writeFileSync(
path.join(__dirname, '../src/lib/picker/ngx-emoji/data/skins.ts'),
doc
);
fs.writeFileSync(path.join(__dirname, '../src/lib/picker/emojis/skins.ts'), doc);

// const sShortNames = stringifyObject(short_names, {
// inlineCharacterLimit: 25,
Expand Down
10 changes: 10 additions & 0 deletions src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { TestBed, waitForAsync } from '@angular/core/testing';
import { provideEmojiLoader } from '@ctrl/ngx-emoji-mart/ngx-emoji';
import { categories, emojis, skins } from '@ctrl/ngx-emoji-mart/emojis';
import { of } from 'rxjs';

import { AppComponent } from './app.component';

describe('AppComponent', () => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [AppComponent],
providers: [
provideEmojiLoader({
skins: () => of(skins),
emojis: () => of(emojis),
categories: () => of(categories),
}),
],
}).compileComponents();
}));

Expand Down
13 changes: 13 additions & 0 deletions src/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApplicationConfig } from '@angular/core';
import { provideEmojiLoader } from '@ctrl/ngx-emoji-mart/ngx-emoji';
import { from, map } from 'rxjs';

export const appConfig: ApplicationConfig = {
providers: [
provideEmojiLoader({
skins: () => from(import('@ctrl/ngx-emoji-mart/emojis')).pipe(map(m => m.skins)),
emojis: () => from(import('@ctrl/ngx-emoji-mart/emojis')).pipe(map(m => m.emojis)),
categories: () => from(import('@ctrl/ngx-emoji-mart/emojis')).pipe(map(m => m.categories)),
}),
],
};
66 changes: 34 additions & 32 deletions src/lib/picker/emoji-frequency.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,45 @@
import { inject, TestBed, waitForAsync } from '@angular/core/testing';
import { of } from 'rxjs';

import { EmojiService } from '@ctrl/ngx-emoji-mart/ngx-emoji';
import { EmojiService, provideEmojiLoader } from './ngx-emoji';
import { categories, emojis, skins } from './emojis';
import { EmojiFrequentlyService } from './emoji-frequently.service';

describe('EmojiFrequently', () => {
beforeEach(
waitForAsync(() => {
localStorage.clear();
TestBed.configureTestingModule({}).compileComponents();
}),
);
beforeEach(waitForAsync(() => {
localStorage.clear();
TestBed.configureTestingModule({
providers: [
provideEmojiLoader({
skins: () => of(skins),
emojis: () => of(emojis),
categories: () => of(categories),
}),
],
}).compileComponents();
}));

it(
'should get default',
inject([EmojiFrequentlyService], (ef: EmojiFrequentlyService) => {
const defaults = ef.get(ef.DEFAULTS.length, 4);
expect(defaults.length).toEqual(ef.DEFAULTS.length);
}),
);
it('should get default', inject([EmojiFrequentlyService], (ef: EmojiFrequentlyService) => {
const defaults = ef.get(ef.DEFAULTS.length, 4);
expect(defaults.length).toEqual(ef.DEFAULTS.length);
}));

it(
'should get shorter default',
inject([EmojiFrequentlyService], (ef: EmojiFrequentlyService) => {
it('should get shorter default', inject(
[EmojiFrequentlyService],
(ef: EmojiFrequentlyService) => {
const defaults = ef.get(8, 4);
expect(defaults.length).toEqual(8);
}),
);
},
));

it(
'should add emoji',
inject(
[EmojiFrequentlyService, EmojiService],
(ef: EmojiFrequentlyService, es: EmojiService) => {
const pineapple = es.getData('pineapple');
ef.get(8, 4);
ef.add(pineapple!);
const result = ef.get(8, 4);
expect(result.length).toEqual(9);
},
),
);
it('should add emoji', inject(
[EmojiFrequentlyService, EmojiService],
(ef: EmojiFrequentlyService, es: EmojiService) => {
const pineapple = es.getData('pineapple');
ef.get(8, 4);
ef.add(pineapple!);
const result = ef.get(8, 4);
expect(result.length).toEqual(9);
},
));
});
96 changes: 50 additions & 46 deletions src/lib/picker/emoji-search.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,62 @@
import { inject, TestBed, waitForAsync } from '@angular/core/testing';
import { TestBed, waitForAsync } from '@angular/core/testing';
import { firstValueFrom, of } from 'rxjs';

import { EmojiData } from '@ctrl/ngx-emoji-mart/ngx-emoji';
import { EmojiData, provideEmojiLoader } from '@ctrl/ngx-emoji-mart/ngx-emoji';

import { categories, emojis, skins } from './emojis';
import { EmojiSearch } from './emoji-search.service';

describe('EmojiSearch', () => {
let es: EmojiSearch;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({}).compileComponents();
TestBed.configureTestingModule({
providers: [
provideEmojiLoader({
skins: () => of(skins),
emojis: () => of(emojis),
categories: () => of(categories),
}),
],
});
}));

it('should return nothing', inject([EmojiSearch], (es: EmojiSearch) => {
expect(es.search('')).toEqual(null);
}));
beforeEach(() => {
es = TestBed.inject(EmojiSearch);
});

it('should search', inject([EmojiSearch], (es: EmojiSearch) => {
const res = es.search('pineapple');
it('should return nothing', async () => {
expect(await firstValueFrom(es.search(''))).toEqual(null);
});

it('should search', async () => {
const res = await firstValueFrom(es.search('pineapple'));
expect(res).toBeDefined();
expect(res!.length).toBe(1);
expect(res![0].name).toBe('Pineapple');
}));

it('should filter only emojis we care about, exclude pineapple', inject(
[EmojiSearch],
(es: EmojiSearch) => {
const emojisToShowFilter = (data: EmojiData) => {
return data.unified !== '1F34D';
};
const apples = es.search('apple', emojisToShowFilter)!.map(obj => obj.id);
expect(apples.length).toBe(3);
expect(apples).not.toContain('pineapple');
},
));

it('can include/exclude categories', inject(
[EmojiSearch],
(es: EmojiSearch) => {
expect(es.search('flag', undefined, undefined, ['people'])).toEqual([]);
},
));

it('can search for thinking_face', inject(
[EmojiSearch],
(es: EmojiSearch) => {
expect(es.search('thinking_fac')!.map((x: any) => x.id)).toEqual([
'thinking_face',
]);
},
));

it('can search for woman-facepalming', inject(
[EmojiSearch],
(es: EmojiSearch) => {
expect(es.search('woman-facep')!.map(x => x.id)).toEqual([
'woman-facepalming',
]);
},
));
});

it('should filter only emojis we care about, exclude pineapple', async () => {
const emojisToShowFilter = (data: EmojiData) => {
return data.unified !== '1F34D';
};
const res = await firstValueFrom(es.search('apple', emojisToShowFilter));
const apples = res!.map(obj => obj.id);
expect(apples.length).toBe(3);
expect(apples).not.toContain('pineapple');
});

it('can include/exclude categories', async () => {
expect(await firstValueFrom(es.search('flag', undefined, undefined, ['people']))).toEqual([]);
});

it('can search for thinking_face', async () => {
const res = await firstValueFrom(es.search('thinking_fac'));
expect(res!.map((x: any) => x.id)).toEqual(['thinking_face']);
});

it('can search for woman-facepalming', async () => {
const res = await firstValueFrom(es.search('woman-facep'));
expect(res!.map(x => x.id)).toEqual(['woman-facepalming']);
});
});
Loading

0 comments on commit ce0ea13

Please sign in to comment.