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

Commit b8fd440

Browse files
merge: dashboard (= your presentations)
2 parents 2e6205c + 4c883e6 commit b8fd440

File tree

8 files changed

+327
-8
lines changed

8 files changed

+327
-8
lines changed

studio/src/app/app-root.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ export class AppRoot {
111111

112112
<ion-route url="/settings" component="app-settings"/>
113113

114+
<ion-route url="/dashboard" component="app-dashboard"/>
115+
114116
<ion-route url="/signin" component="app-signin"/>
115117
<ion-route url="/signin/:redirect" component="app-signin"/>
116118

studio/src/app/components/core/app-menu/app-menu.scss

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,8 @@ ion-menu {
88
color: white;
99
--background: transparent;
1010

11-
div.sc-ion-searchbar-ios {
12-
margin-left: 12px;
13-
}
14-
1511
input.searchbar-input[class*="sc-ion-searchbar"] {
16-
box-shadow: none;
1712
font-size: var(--font-size-small);
18-
19-
-webkit-padding-start: 48px;
20-
padding-inline-start: 48px;
2113
}
2214
}
2315

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
app-dashboard {
2+
main {
3+
background: white;
4+
5+
a,
6+
a:link,
7+
a:visited,
8+
a:hover,
9+
a:active{
10+
text-decoration: none;
11+
12+
touch-action: manipulation;
13+
cursor: pointer;
14+
}
15+
16+
deckgo-deck {
17+
--background: white;
18+
--color: black;
19+
20+
--pager-display: none;
21+
22+
--font-size-very-small: 4px;
23+
--font-size-small: 8px;
24+
--font-size-normal: 10px;
25+
26+
--font-size-h1: 18px;
27+
--font-size-h2: 10px;
28+
--font-size-h3: 7px;
29+
30+
--slide-width: 100%;
31+
--slide-height: 100%;
32+
33+
--slide-padding-top-default: 0;
34+
--slide-padding-end-default: 0;
35+
--slide-padding-bottom-default: 0;
36+
--slide-padding-start-default: 0;
37+
}
38+
39+
ion-searchbar[class*="sc-ion-searchbar"] {
40+
--background: var(--ion-color-light);
41+
background: var(--ion-color-light);
42+
border: 1px solid #dedede;
43+
border-radius: 4px;
44+
45+
--placeholder-opacity: 0.33;
46+
}
47+
48+
div.container {
49+
display: grid;
50+
grid-gap: 16px;
51+
grid-template-columns: repeat(auto-fill, 1fr);
52+
53+
ion-card.item {
54+
position: relative;
55+
56+
width: 100%;
57+
height: 24vh;
58+
59+
@media screen and (min-width: 540px) {
60+
height: 38vh;
61+
}
62+
}
63+
64+
@media screen and (min-width: 540px) {
65+
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
66+
}
67+
68+
}
69+
}
70+
}
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
import {Component, h, State} from '@stencil/core';
2+
3+
import {filter, take} from 'rxjs/operators';
4+
5+
import {AuthUser} from '../../../models/auth/auth.user';
6+
import {Deck} from '../../../models/data/deck';
7+
import {Slide} from '../../../models/data/slide';
8+
9+
import {AuthService} from '../../../services/auth/auth.service';
10+
import {DeckService} from '../../../services/data/deck/deck.service';
11+
import {NavDirection, NavService} from '../../../services/core/nav/nav.service';
12+
import {ParseSlidesUtils} from '../../../utils/editor/parse-slides.utils';
13+
import {SlideService} from '../../../services/data/slide/slide.service';
14+
import {ParseStyleUtils} from '../../../utils/editor/parse-style.utils';
15+
import {ParseBackgroundUtils} from '../../../utils/editor/parse-background.utils';
16+
17+
interface DeckAndFirstSlide {
18+
deck: Deck;
19+
slide: any;
20+
style: any;
21+
background: any;
22+
}
23+
24+
@Component({
25+
tag: 'app-dashboard',
26+
styleUrl: 'app-dashboard.scss'
27+
})
28+
export class AppDashboard {
29+
30+
@State()
31+
private authUser: AuthUser;
32+
33+
@State()
34+
private filteredDecks: DeckAndFirstSlide[] = null;
35+
36+
private decks: DeckAndFirstSlide[] = null;
37+
38+
private authService: AuthService;
39+
40+
private navService: NavService;
41+
42+
private deckService: DeckService;
43+
private slideService: SlideService;
44+
45+
constructor() {
46+
this.authService = AuthService.getInstance();
47+
this.navService = NavService.getInstance();
48+
this.deckService = DeckService.getInstance();
49+
this.slideService = SlideService.getInstance();
50+
}
51+
52+
componentWillLoad() {
53+
this.authService.watch().pipe(
54+
filter((authUser: AuthUser) => authUser !== null && authUser !== undefined && !authUser.anonymous),
55+
take(1)).subscribe(async (authUser: AuthUser) => {
56+
this.authUser = authUser;
57+
58+
const userDecks: Deck[] = await this.deckService.getUserDecks(authUser.uid);
59+
this.decks = await this.fetchFirstSlides(userDecks);
60+
await this.filterDecks(null);
61+
});
62+
}
63+
64+
private fetchFirstSlides(decks: Deck[]): Promise<DeckAndFirstSlide[]> {
65+
return new Promise<DeckAndFirstSlide[]>(async (resolve) => {
66+
if (!decks || decks.length <= 0) {
67+
resolve([]);
68+
return;
69+
}
70+
71+
const promises = [];
72+
decks.forEach((deck: Deck) => {
73+
if (deck && deck.data && deck.data.slides && deck.data.slides.length >= 1) {
74+
promises.push(this.initDeckAndFirstSlide(deck, deck.data.slides[0]));
75+
}
76+
});
77+
78+
const results: DeckAndFirstSlide[] = await Promise.all(promises);
79+
80+
resolve(results);
81+
});
82+
}
83+
84+
private initDeckAndFirstSlide(deck: Deck, slideId: string): Promise<DeckAndFirstSlide> {
85+
return new Promise<DeckAndFirstSlide>(async (resolve) => {
86+
try {
87+
const slide: Slide = await this.slideService.get(deck.id, slideId);
88+
const element: any = await ParseSlidesUtils.parseSlide(slide);
89+
90+
let style: any;
91+
if (deck.data && deck.data.attributes && deck.data.attributes.style) {
92+
style = await ParseStyleUtils.convertStyle(deck.data.attributes.style);
93+
} else {
94+
style = undefined;
95+
}
96+
97+
const background: any = await ParseBackgroundUtils.convertBackground(deck.data.background);
98+
99+
resolve({
100+
deck: deck,
101+
slide: element,
102+
style: style,
103+
background: background
104+
});
105+
} catch (err) {
106+
resolve(undefined);
107+
}
108+
});
109+
}
110+
111+
private filterDecks(value: string): Promise<void> {
112+
return new Promise<void>((resolve) => {
113+
if (!value || value === undefined || value === '') {
114+
this.filteredDecks = this.decks ? [...this.decks] : null;
115+
116+
resolve();
117+
return;
118+
}
119+
120+
if (!this.decks || this.decks.length <= 0) {
121+
this.filteredDecks = this.decks ? [...this.decks] : null;
122+
123+
resolve();
124+
return;
125+
}
126+
127+
const matchingDecks: DeckAndFirstSlide[] = this.decks.filter((matchDeck: DeckAndFirstSlide) => {
128+
return matchDeck.deck && matchDeck.deck.data && matchDeck.deck.data.name && matchDeck.deck.data.name.toLowerCase().indexOf(value.toLowerCase()) > -1
129+
});
130+
131+
this.filteredDecks = [...matchingDecks];
132+
133+
resolve();
134+
});
135+
}
136+
137+
private async signIn() {
138+
this.navService.navigate({
139+
url: '/signin' + (window && window.location ? window.location.pathname : ''),
140+
direction: NavDirection.FORWARD
141+
});
142+
}
143+
144+
private async filterDecksOnChange(e: CustomEvent) {
145+
if (e && e.detail) {
146+
await this.filterDecks(e.detail.value);
147+
} else {
148+
await this.filterDecks(null);
149+
}
150+
}
151+
152+
render() {
153+
return [
154+
<app-navigation presentation={true}></app-navigation>,
155+
<ion-content class="ion-padding">
156+
157+
{this.renderGuardedContent()}
158+
159+
</ion-content>
160+
];
161+
}
162+
163+
private renderGuardedContent() {
164+
if (!this.authUser) {
165+
return this.renderNotLoggedInContent();
166+
} else {
167+
return this.renderContent();
168+
}
169+
}
170+
171+
private renderNotLoggedInContent() {
172+
return <main class="ion-padding">
173+
<h1>Oh, hi! Good to have you.</h1>
174+
<p class="ion-padding-top">
175+
<ion-router-link onClick={() => this.signIn()}>Sign in</ion-router-link>
176+
to access your presentations.</p>
177+
</main>
178+
}
179+
180+
private renderContent() {
181+
return <main class="ion-padding">
182+
<h1>Your presentations</h1>
183+
{this.renderDecksFilter()}
184+
{this.renderDecks()}
185+
</main>
186+
}
187+
188+
private renderDecksFilter() {
189+
return <ion-searchbar debounce={500} animated={false} placeholder="Filter your presentations"
190+
onClick={($event) => $event.stopImmediatePropagation()}
191+
onIonChange={(e: CustomEvent) => this.filterDecksOnChange(e)}
192+
class="ion-no-padding ion-margin-top ion-margin-bottom"></ion-searchbar>;
193+
}
194+
195+
private renderDecks() {
196+
if (this.filteredDecks && this.filteredDecks.length > 0) {
197+
return <div class="container">
198+
{this.renderDecksCards()}
199+
</div>
200+
} else {
201+
return undefined;
202+
}
203+
}
204+
205+
private renderDecksCards() {
206+
return (
207+
this.filteredDecks.map((deck: DeckAndFirstSlide) => {
208+
const url: string = `/editor/${deck.deck.id}`;
209+
210+
return <ion-card class="item ion-no-margin" href={url} routerDirection="root">
211+
{this.renderDeck(deck)}
212+
</ion-card>;
213+
})
214+
);
215+
}
216+
217+
private renderDeck(deck: DeckAndFirstSlide) {
218+
if (!deck) {
219+
return undefined;
220+
} else {
221+
return <deckgo-deck embedded={true} keyboard={false} style={deck.style}>
222+
{deck.slide}
223+
{deck.background}
224+
</deckgo-deck>
225+
}
226+
}
227+
228+
}

studio/src/app/popovers/core/app-user-menu/app-user-menu.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ export class AppUserMenu {
6363
<ion-label>Write a presentation</ion-label>
6464
</ion-item>
6565

66+
<ion-item onClick={() => this.closePopover()}>
67+
<ion-router-link href="/dashboard" routerDirection="forward"><ion-label>Dashboard</ion-label></ion-router-link>
68+
</ion-item>
69+
6670
<ion-item onClick={() => this.closePopover()}>
6771
<ion-router-link href="/settings" routerDirection="forward"><ion-label>Settings</ion-label></ion-router-link>
6872
</ion-item>

studio/src/components.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export namespace Components {
3838
interface AppContact {}
3939
interface AppCreateSlide {}
4040
interface AppCustomImages {}
41+
interface AppDashboard {}
4142
interface AppDeckOrSlide {
4243
'deckOrSlide': boolean;
4344
}
@@ -191,6 +192,12 @@ declare global {
191192
new (): HTMLAppCustomImagesElement;
192193
};
193194

195+
interface HTMLAppDashboardElement extends Components.AppDashboard, HTMLStencilElement {}
196+
var HTMLAppDashboardElement: {
197+
prototype: HTMLAppDashboardElement;
198+
new (): HTMLAppDashboardElement;
199+
};
200+
194201
interface HTMLAppDeckOrSlideElement extends Components.AppDeckOrSlide, HTMLStencilElement {}
195202
var HTMLAppDeckOrSlideElement: {
196203
prototype: HTMLAppDeckOrSlideElement;
@@ -487,6 +494,7 @@ declare global {
487494
'app-contact': HTMLAppContactElement;
488495
'app-create-slide': HTMLAppCreateSlideElement;
489496
'app-custom-images': HTMLAppCustomImagesElement;
497+
'app-dashboard': HTMLAppDashboardElement;
490498
'app-deck-or-slide': HTMLAppDeckOrSlideElement;
491499
'app-developer': HTMLAppDeveloperElement;
492500
'app-editor': HTMLAppEditorElement;
@@ -562,6 +570,7 @@ declare namespace LocalJSX {
562570
'onSignIn'?: (event: CustomEvent<void>) => void;
563571
}
564572
interface AppCustomImages extends JSXBase.HTMLAttributes<HTMLAppCustomImagesElement> {}
573+
interface AppDashboard extends JSXBase.HTMLAttributes<HTMLAppDashboardElement> {}
565574
interface AppDeckOrSlide extends JSXBase.HTMLAttributes<HTMLAppDeckOrSlideElement> {
566575
'deckOrSlide'?: boolean;
567576
'onApplyTo'?: (event: CustomEvent<boolean>) => void;
@@ -693,6 +702,7 @@ declare namespace LocalJSX {
693702
'app-contact': AppContact;
694703
'app-create-slide': AppCreateSlide;
695704
'app-custom-images': AppCustomImages;
705+
'app-dashboard': AppDashboard;
696706
'app-deck-or-slide': AppDeckOrSlide;
697707
'app-developer': AppDeveloper;
698708
'app-editor': AppEditor;

studio/src/global/app.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
@import "theme/popover";
4141
@import "theme/modal";
4242
@import "theme/input";
43+
@import "theme/searchbar";
4344
@import "theme/select";
4445
@import "theme/loading";
4546
@import "theme/tappable";

0 commit comments

Comments
 (0)