Skip to content
This repository was archived by the owner on Feb 6, 2024. It is now read-only.

Commit c29ef46

Browse files
feat: display gifs category
1 parent 5596d38 commit c29ef46

File tree

7 files changed

+187
-22
lines changed

7 files changed

+187
-22
lines changed

studio/src/app/components/feed/card/app-feed-card-content/app-feed-card-content.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@ import {Component, Prop, State, Watch} from '@stencil/core';
22

33
import DateTimeFormatOptions = Intl.DateTimeFormatOptions;
44

5-
interface InputTargetEvent extends EventTarget {
6-
value: string;
7-
}
8-
95
@Component({
106
tag: 'app-feed-card-content',
117
styleUrl: 'app-feed-card-content.scss',

studio/src/app/modals/editor/app-gif/app-gif.scss

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,39 @@ app-gif {
1414
flex-basis: 50%;
1515
max-width: 50%;
1616

17-
img {
18-
width: calc(100% - 4px);
19-
padding: 2px;
20-
border-radius: 4px;
17+
div.gifs-category, div.gif {
18+
position: relative;
19+
20+
div.gifs-category-container, div.gif-container {
21+
border-radius: 4px;
22+
overflow: hidden;
23+
position: relative;
24+
25+
img {
26+
width: 100%;
27+
height: auto;
28+
min-height: 150px;
29+
vertical-align: top;
30+
}
31+
32+
div.gifs-category-placeholder {
33+
position: absolute;
34+
top: 0;
35+
left: 0;
36+
right: 0;
37+
bottom: 0;
38+
39+
background: rgba(var(--ion-color-dark-rgb), 0.6);
40+
41+
h2 {
42+
position: absolute;
43+
top: 50%;
44+
left: 50%;
45+
transform: translate(-50%, -50%);
46+
color: white;
47+
}
48+
}
49+
}
2150
}
2251
}
2352
}

studio/src/app/modals/editor/app-gif/app-gif.tsx

Lines changed: 108 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,29 @@ export class AppGif {
1414

1515
private gifService: GifService;
1616

17+
@State()
18+
private categoriesOdd: TenorCategory[];
19+
20+
@State()
21+
private categoriesEven: TenorCategory[];
22+
1723
@State()
1824
private gifsOdd: TenorGif[];
1925

2026
@State()
2127
private gifsEven: TenorGif[];
2228

29+
@State()
30+
private searchTerm: string;
31+
2332
constructor() {
2433
this.gifService = GifService.getInstance();
2534
}
2635

2736
async componentDidLoad() {
2837
history.pushState({modal: true}, null);
2938

30-
const gifs: TenorGif[] = await this.gifService.getTrending();
31-
this.gifsOdd = gifs.filter((_a, i) => i % 2);
32-
this.gifsEven = gifs.filter((_a, i) => !(i % 2));
39+
await this.fetchCategories();
3340
}
3441

3542
@Listen('window:popstate')
@@ -52,6 +59,64 @@ export class AppGif {
5259
});
5360
}
5461

62+
private fetchCategories(): Promise<void> {
63+
return new Promise<void>(async (resolve) => {
64+
const categories: TenorCategory[] = await this.gifService.getCategories();
65+
66+
if (!categories || categories.length <= 0) {
67+
resolve();
68+
return;
69+
}
70+
71+
this.categoriesOdd = categories.filter((_a, i) => i % 2);
72+
this.categoriesEven = categories.filter((_a, i) => !(i % 2));
73+
74+
resolve();
75+
});
76+
}
77+
78+
private selectCategory(searchTerm: string) {
79+
this.searchTerm = searchTerm;
80+
}
81+
82+
private search(): Promise<void> {
83+
return new Promise<void>(async (resolve) => {
84+
if (!this.searchTerm || this.searchTerm.length <= 0) {
85+
await this.clear();
86+
resolve();
87+
return;
88+
}
89+
90+
const gifs: TenorGif[] = await this.gifService.getGifs(this.searchTerm);
91+
92+
if (!gifs || gifs.length <= 0) {
93+
this.gifsOdd = [];
94+
this.gifsEven = [];
95+
96+
resolve();
97+
return;
98+
}
99+
100+
this.gifsOdd = gifs.filter((_a, i) => i % 2);
101+
this.gifsEven = gifs.filter((_a, i) => !(i % 2));
102+
103+
resolve();
104+
});
105+
}
106+
107+
private clear(): Promise<void> {
108+
return new Promise<void>((resolve) => {
109+
this.gifsOdd = null;
110+
this.gifsEven = null;
111+
112+
resolve();
113+
});
114+
}
115+
116+
private handleInput($event: CustomEvent<KeyboardEvent>) {
117+
this.searchTerm = ($event.target as InputTargetEvent).value;
118+
}
119+
55120
render() {
56121
return [
57122
<ion-header>
@@ -67,30 +132,67 @@ export class AppGif {
67132
<ion-content padding>
68133
<div class="gifs-container">
69134
<div class="gifs-column">
135+
{this.renderCategories(this.categoriesOdd)}
70136
{this.renderGifs(this.gifsOdd)}
71137
</div>
72138
<div class="gifs-column">
139+
{this.renderCategories(this.categoriesEven)}
73140
{this.renderGifs(this.gifsEven)}
74141
</div>
75142
</div>
76143
</ion-content>,
77144
<ion-footer>
78145
<ion-toolbar>
79-
<ion-searchbar debounce={500} placeholder="Search Tenor"></ion-searchbar>
146+
<ion-searchbar debounce={500} placeholder="Search Tenor" value={this.searchTerm}
147+
onIonClear={() => this.clear()}
148+
onIonInput={(e: CustomEvent<KeyboardEvent>) => this.handleInput(e)}
149+
onIonChange={() => {
150+
this.search()
151+
}}></ion-searchbar>
80152
</ion-toolbar>
81153
</ion-footer>
82154
];
83155
}
84156

157+
private renderCategories(categories: TenorCategory[]) {
158+
if (this.gifsEven || this.gifsOdd) {
159+
return undefined;
160+
}
161+
162+
if (categories && categories.length > 0) {
163+
return (
164+
categories.map((category: TenorCategory) => {
165+
if (category.image) {
166+
return <div custom-tappable class="gifs-category ion-padding"
167+
onClick={() => this.selectCategory(category.searchterm)}>
168+
<div class="gifs-category-container">
169+
<img src={category.image}></img>
170+
<div class="gifs-category-placeholder">
171+
<h2 class="ion-no-margin">{category.name}</h2>
172+
</div>
173+
</div>
174+
</div>
175+
} else {
176+
return undefined;
177+
}
178+
})
179+
);
180+
} else {
181+
return undefined;
182+
}
183+
}
184+
85185
private renderGifs(gifs: TenorGif[]) {
86186
if (gifs && gifs.length > 0) {
87187
return (
88188
gifs.map((gif: TenorGif) => {
89189
if (gif.media && gif.media.length > 0
90190
&& gif.media[0].tinygif && gif.media[0].tinygif.url
91191
&& gif.media[0].gif && gif.media[0].gif.url) {
92-
return <div custom-tappable onClick={() => this.addSlide(gif)}>
93-
<img src={gif.media[0].tinygif.url} alt={gif.title ? gif.title : gif.url}></img>
192+
return <div class="gif ion-padding" custom-tappable onClick={() => this.addSlide(gif)}>
193+
<div class="gif-container">
194+
<img src={gif.media[0].tinygif.url} alt={gif.title ? gif.title : gif.url}></img>
195+
</div>
94196
</div>
95197
} else {
96198
return undefined;

studio/src/app/services/gif/gif.service.tsx

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,35 @@ export class GifService {
2323
return GifService.instance;
2424
}
2525

26-
// call the trending and category endpoints
27-
getTrending(): Promise<TenorGif[]> {
28-
return new Promise<TenorGif[]>(async (resolve, reject) => {
26+
getCategories(): Promise<TenorCategory[]> {
27+
return new Promise<TenorCategory[]>(async (resolve) => {
2928
const config: EnvironmentTenorConfig = EnvironmentConfigService.getInstance().get('tenor');
3029

31-
const searchTerm: string = 'excited';
30+
const anonymousId: string = await this.getAnonymousId();
31+
32+
const searchUrl = config.url + 'categories?key=' + config.key + '&anon_id=' + anonymousId + '&media_filter=minimal';
33+
34+
try {
35+
const rawResponse: Response = await fetch(searchUrl);
36+
37+
const response: TenorCategoryResponse = JSON.parse(await rawResponse.text());
38+
39+
if (!response) {
40+
this.errorService.error('Tenor trending could not be fetched');
41+
return;
42+
}
43+
44+
resolve(response.tags);
45+
} catch (err) {
46+
this.errorService.error(err.message);
47+
resolve(err);
48+
}
49+
});
50+
}
51+
52+
getGifs(searchTerm: string): Promise<TenorGif[]> {
53+
return new Promise<TenorGif[]>(async (resolve) => {
54+
const config: EnvironmentTenorConfig = EnvironmentConfigService.getInstance().get('tenor');
3255

3356
const anonymousId: string = await this.getAnonymousId();
3457

@@ -41,13 +64,15 @@ export class GifService {
4164
const response: TenorTrendingResponse = JSON.parse(await rawResponse.text());
4265

4366
if (!response) {
44-
reject('Tenor trending could not be fetched');
67+
this.errorService.error('Tenor trending could not be fetched');
68+
resolve();
69+
return;
4570
}
4671

4772
resolve(response.results);
4873
} catch (err) {
4974
this.errorService.error(err.message);
50-
reject(err);
75+
resolve();
5176
}
5277
});
5378
}

studio/src/app/utils/input.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
interface InputTargetEvent extends EventTarget {
2+
value: string;
3+
}

studio/src/app/utils/tenor.d.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,21 @@ interface TenorGif {
3636
media: TenorMedia[];
3737
}
3838

39-
4039
interface TenorTrendingResponse {
4140
results: TenorGif[];
4241
}
4342

43+
interface TenorCategory {
44+
searchterm: string;
45+
path: string;
46+
image: string;
47+
name: string;
48+
}
49+
50+
interface TenorCategoryResponse {
51+
tags: TenorCategory[];
52+
}
53+
4454
interface TenorAnonymousResponse {
4555
anon_id: string;
4656
}

studio/src/components.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
import '@stencil/core';
99

1010
import '@ionic/core';
11-
import 'ionicons';
1211
import 'deckdeckgo';
1312
import 'deckdeckgo-inline-editor';
13+
import 'ionicons';
1414
import {
1515
EventEmitter,
1616
} from '@stencil/core';

0 commit comments

Comments
 (0)