Skip to content

Commit d3884a6

Browse files
author
Garrett Downs
committed
feat(views): use combined statusbar and view title
1 parent cb63db4 commit d3884a6

22 files changed

Lines changed: 245 additions & 224 deletions
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
.root {
2+
display: flex;
3+
align-items: center;
4+
padding: 0px 5px 0px 5px;
5+
font-size: 1.2rem;
6+
font-weight: bold;
7+
}
8+
9+
.root > div {
10+
margin-left: 5px;
11+
}
12+
.root > div:first-child {
13+
margin-left: 0px;
14+
}
15+
16+
.text {
17+
flex: 1;
18+
white-space: nowrap;
19+
overflow: hidden;
20+
text-overflow: ellipsis;
21+
}
22+
23+
.battery > span {
24+
font-size: 1rem;
25+
}

src/components/Statusbar.tsx

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { format } from 'date-fns';
2+
import { h, VNode } from 'preact';
3+
import { useEffect, useState } from 'preact/hooks';
4+
import { ComponentBaseProps } from '../models';
5+
import { KaiOS } from '../services/kaios';
6+
import styles from './Statusbar.module.css';
7+
8+
type StatusbarProps = ComponentBaseProps & {
9+
text?: string;
10+
};
11+
12+
export default function Statusbar(props: StatusbarProps): VNode {
13+
const [time, setTime] = useState(new Date());
14+
const [battery, setBattery] = useState(0);
15+
const [connection, setConnection] = useState<'wifi' | 'cellular' | 'none'>('none');
16+
17+
useEffect(() => {
18+
const timeInterval = setInterval(() => setTime(new Date()), 1000);
19+
20+
setBattery(KaiOS.battery.level * 100);
21+
const handleLevelChange = () => setBattery(KaiOS.battery.level * 100);
22+
KaiOS.battery.addEventListener('levelchange', handleLevelChange);
23+
24+
setConnection(KaiOS.connection.type);
25+
const handleTypeChange = () => setConnection(KaiOS.connection.type);
26+
KaiOS.connection.addEventListener('typechange', handleTypeChange);
27+
28+
return () => {
29+
clearInterval(timeInterval);
30+
KaiOS.battery.removeEventListener('levelchange', handleLevelChange);
31+
KaiOS.connection.removeEventListener('typechange', handleTypeChange);
32+
};
33+
}, []);
34+
35+
function renderIcon() {
36+
switch (connection) {
37+
case 'cellular':
38+
return (
39+
<svg
40+
xmlns="http://www.w3.org/2000/svg"
41+
height="18px"
42+
viewBox="0 0 24 24"
43+
width="18px"
44+
fill="var(--primary-text-color)"
45+
>
46+
<path d="M0 0h24v24H0V0z" fill="none" />
47+
<path d="M17 4h3v16h-3V4zM5 14h3v6H5v-6zm6-5h3v11h-3V9z" />
48+
</svg>
49+
);
50+
case 'wifi':
51+
return (
52+
<svg
53+
xmlns="http://www.w3.org/2000/svg"
54+
height="18px"
55+
viewBox="0 0 24 24"
56+
width="18px"
57+
fill="var(--primary-text-color)"
58+
>
59+
<path d="M0 0h24v24H0V0zm0 0h24v24H0V0z" fill="none" />
60+
<path d="M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.08 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z" />
61+
</svg>
62+
);
63+
default:
64+
return null;
65+
}
66+
}
67+
68+
return (
69+
<div className={styles.root}>
70+
<div className={styles.text}>{props.text}</div>
71+
{renderIcon()}
72+
<div className={styles.battery}>
73+
{battery}
74+
<span>%</span>
75+
</div>
76+
<div className={styles.time}>{format(time, 'h:mm')}</div>
77+
</div>
78+
);
79+
}

src/routes/AppSettings.tsx

Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
11
import { AppBar } from 'mai-ui/dist/components/appbar';
2-
import {
3-
Input,
4-
RangeRow,
5-
Select,
6-
ToggleRow,
7-
} from 'mai-ui/dist/components/form';
8-
import {
9-
View,
10-
ViewHeader,
11-
ViewTab,
12-
ViewTabBar,
13-
} from 'mai-ui/dist/components/view';
2+
import { Input, RangeRow, Select, ToggleRow } from 'mai-ui/dist/components/form';
3+
import { View, ViewTab, ViewTabBar } from 'mai-ui/dist/components/view';
144
import { useListNav } from 'mai-ui/dist/hooks';
155
import { h, VNode } from 'preact';
166
import { route } from 'preact-router';
177
import { FoxcastsAppMenu } from '../components/FoxcastsAppMenu';
8+
import Statusbar from '../components/Statusbar';
189
import { useSettings } from '../contexts/SettingsProvider';
1910
import { SelectablePriority } from '../enums';
2011
import {
@@ -40,10 +31,7 @@ export default function AppSettings({ tabId }: Props): VNode {
4031
updateRouteOnChange: false,
4132
});
4233

43-
function handleSettingSelect<T extends keyof Settings>(
44-
key: T,
45-
value: Settings[T]
46-
): void {
34+
function handleSettingSelect<T extends keyof Settings>(key: T, value: Settings[T]): void {
4735
if (key === 'theme') {
4836
// We want to use the theme's original accent color
4937
const theme = themes.find((a) => a.id === value) as ThemeConfig;
@@ -59,7 +47,7 @@ export default function AppSettings({ tabId }: Props): VNode {
5947

6048
return (
6149
<View>
62-
<ViewHeader>Settings</ViewHeader>
50+
<Statusbar text="Settings" />
6351
<ViewTabBar
6452
tabs={[
6553
{ id: 'display', label: 'display' },
@@ -150,8 +138,7 @@ export default function AppSettings({ tabId }: Props): VNode {
150138
selected: selectedId === 'accentColor',
151139
}}
152140
onChange={(value) =>
153-
value.match(/[0-9a-fA-F]{6}/) &&
154-
handleSettingSelect('accentColor', value)
141+
value.match(/[0-9a-fA-F]{6}/) && handleSettingSelect('accentColor', value)
155142
}
156143
/>
157144
<ToggleRow
@@ -170,9 +157,7 @@ export default function AppSettings({ tabId }: Props): VNode {
170157
id: 'dynamicThemeColor',
171158
selected: selectedId === 'dynamicThemeColor',
172159
}}
173-
onChange={(value): void =>
174-
handleSettingSelect('dynamicThemeColor', value)
175-
}
160+
onChange={(value): void => handleSettingSelect('dynamicThemeColor', value)}
176161
/>
177162
<ToggleRow
178163
label="Dynamic Background"
@@ -181,9 +166,7 @@ export default function AppSettings({ tabId }: Props): VNode {
181166
id: 'dynamicBackgrounds',
182167
selected: selectedId === 'dynamicBackgrounds',
183168
}}
184-
onChange={(value): void =>
185-
handleSettingSelect('dynamicBackgrounds', value)
186-
}
169+
onChange={(value): void => handleSettingSelect('dynamicBackgrounds', value)}
187170
/>
188171
</ViewTab>
189172
<ViewTab tabId="player" activeTabId={tabId}>
@@ -224,9 +207,7 @@ export default function AppSettings({ tabId }: Props): VNode {
224207
id: 'playbackSkipBack',
225208
selected: selectedId === 'playbackSkipBack',
226209
}}
227-
onChange={(value): void =>
228-
handleSettingSelect('playbackSkipBack', value)
229-
}
210+
onChange={(value): void => handleSettingSelect('playbackSkipBack', value)}
230211
/>
231212
<RangeRow
232213
label="Skip Forward"
@@ -239,9 +220,7 @@ export default function AppSettings({ tabId }: Props): VNode {
239220
id: 'playbackSkipForward',
240221
selected: selectedId === 'playbackSkipForward',
241222
}}
242-
onChange={(value): void =>
243-
handleSettingSelect('playbackSkipForward', value)
244-
}
223+
onChange={(value): void => handleSettingSelect('playbackSkipForward', value)}
245224
/>
246225
<RangeRow
247226
label="Playback Speed"
@@ -254,9 +233,7 @@ export default function AppSettings({ tabId }: Props): VNode {
254233
id: 'playbackSpeed',
255234
selected: selectedId === 'playbackSpeed',
256235
}}
257-
onChange={(value): void =>
258-
handleSettingSelect('playbackSpeed', value)
259-
}
236+
onChange={(value): void => handleSettingSelect('playbackSpeed', value)}
260237
/>
261238
<ToggleRow
262239
label="Auto Delete Download"
@@ -266,9 +243,7 @@ export default function AppSettings({ tabId }: Props): VNode {
266243
id: 'autoDeleteDownload',
267244
selected: selectedId === 'autoDeleteDownload',
268245
}}
269-
onChange={(value): void =>
270-
handleSettingSelect('autoDeleteDownload', value)
271-
}
246+
onChange={(value): void => handleSettingSelect('autoDeleteDownload', value)}
272247
/>
273248
</ViewTab>
274249
<AppBar appMenuContent={<FoxcastsAppMenu />} />

src/routes/Downloads.tsx

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import { formatFileSize } from 'foxcasts-core/lib/utils';
22
import { AppBar, AppBarAction } from 'mai-ui/dist/components/appbar';
33
import { ListItem } from 'mai-ui/dist/components/list';
44
import { Typography } from 'mai-ui/dist/components/Typography';
5-
import { View, ViewContent, ViewHeader } from 'mai-ui/dist/components/view';
5+
import { View, ViewContent } from 'mai-ui/dist/components/view';
66
import { useListNav } from 'mai-ui/dist/hooks';
77
import { Fragment, h, VNode } from 'preact';
88
import { route } from 'preact-router';
99
import { useEffect, useState } from 'preact/hooks';
1010
import { FoxcastsAppMenu } from '../components/FoxcastsAppMenu';
1111
import ProgressBar from '../components/ProgressBar';
12+
import Statusbar from '../components/Statusbar';
1213
import { useDownloadManager } from '../contexts/DownloadManagerProvider';
1314
import { Download, DownloadStatus } from '../models';
1415
import styles from './Downloads.module.css';
@@ -90,26 +91,19 @@ export default function Downloads({ selectedItemId }: Props): VNode {
9091

9192
const bytes = (item.currentBytes / item.totalBytes) * 100 || 0;
9293
if (bytes < 100) {
93-
return `${formatFileSize(item.currentBytes)}/${formatFileSize(
94-
item.totalBytes
95-
)}`;
94+
return `${formatFileSize(item.currentBytes)}/${formatFileSize(item.totalBytes)}`;
9695
}
9796

9897
return 'Complete';
9998
}
10099

101100
function getActions(): AppBarAction[] {
102-
const actions: AppBarAction[] = [
103-
{ id: 'test', label: 'Test', actionFn: () => runTest() },
104-
];
101+
const actions: AppBarAction[] = [{ id: 'test', label: 'Test', actionFn: () => runTest() }];
105102

106103
const item = getSelectedItem();
107104
if (!item) return actions;
108105

109-
if (
110-
item.status === DownloadStatus.Queued ||
111-
item.status === DownloadStatus.Downloading
112-
) {
106+
if (item.status === DownloadStatus.Queued || item.status === DownloadStatus.Downloading) {
113107
actions.push({
114108
id: 'remove',
115109
label: 'Cancel download',
@@ -118,10 +112,7 @@ export default function Downloads({ selectedItemId }: Props): VNode {
118112
route(`/downloads/`, true);
119113
},
120114
});
121-
} else if (
122-
item.status === DownloadStatus.Cancelled ||
123-
item.status === DownloadStatus.Error
124-
) {
115+
} else if (item.status === DownloadStatus.Cancelled || item.status === DownloadStatus.Error) {
125116
actions.push({
126117
id: 'retry',
127118
label: 'Retry download',
@@ -143,7 +134,7 @@ export default function Downloads({ selectedItemId }: Props): VNode {
143134

144135
return (
145136
<View>
146-
<ViewHeader>Downloads</ViewHeader>
137+
<Statusbar text="Downloads" />
147138
<ViewContent>
148139
{downloads.downloading.map((item) => (
149140
<div key={item.episodeId}>

src/routes/EpisodeDetail.tsx

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { Fragment, h, VNode } from 'preact';
1212
import { route } from 'preact-router';
1313
import { useEffect, useState } from 'preact/hooks';
1414
import { FoxcastsAppMenu } from '../components/FoxcastsAppMenu';
15+
import Statusbar from '../components/Statusbar';
1516
import { useDownloadManager } from '../contexts/DownloadManagerProvider';
1617
import { usePlayer } from '../contexts/playerContext';
1718
import { useSettings } from '../contexts/SettingsProvider';
@@ -25,13 +26,12 @@ interface EpisodeDetailProps {
2526
tabId: string;
2627
}
2728

28-
export default function EpisodeDetail({
29-
episodeId,
30-
tabId,
31-
}: EpisodeDetailProps): VNode {
29+
export default function EpisodeDetail({ episodeId, tabId }: EpisodeDetailProps): VNode {
3230
const [episode, setEpisode] = useState<EpisodeExtended>();
3331
const [chapters, setChapters] = useState<Chapter[] | null>();
3432

33+
console.log(episode);
34+
3535
const player = usePlayer();
3636
const { addToQueue } = useDownloadManager();
3737
const { settings } = useSettings();
@@ -54,13 +54,8 @@ export default function EpisodeDetail({
5454

5555
const { selectedId } = useListNav({
5656
onSelect: (itemId) => {
57-
if (
58-
itemId.startsWith('chapter') &&
59-
chapters &&
60-
Number(episodeId) === player.episode?.id
61-
) {
57+
if (itemId.startsWith('chapter') && chapters && Number(episodeId) === player.episode?.id) {
6258
const index = parseInt(itemId.split('_')[1], 10);
63-
console.log('chapter selected', index);
6459
player.goTo(Math.floor(chapters[index].startTime / 1000));
6560
}
6661
},
@@ -90,9 +85,7 @@ export default function EpisodeDetail({
9085
},
9186
{
9287
id: 'toggleFavorite',
93-
label: episode.isFavorite
94-
? 'Remove from favorites'
95-
: 'Add to favorites',
88+
label: episode.isFavorite ? 'Remove from favorites' : 'Add to favorites',
9689
actionFn: () =>
9790
Core.episodes
9891
.update(episode.id, {
@@ -138,14 +131,11 @@ export default function EpisodeDetail({
138131

139132
return (
140133
<View
141-
backgroundImageUrl={
142-
settings.dynamicBackgrounds ? artwork?.image : undefined
143-
}
144-
accentColor={
145-
settings.dynamicThemeColor ? episode?.accentColor : undefined
146-
}
134+
backgroundImageUrl={settings.dynamicBackgrounds ? artwork?.image : undefined}
135+
accentColor={settings.dynamicThemeColor ? episode?.accentColor : undefined}
147136
enableCustomColor={true}
148137
>
138+
<Statusbar text={episode?.podcastTitle} />
149139
<ViewTabBar
150140
tabs={[
151141
{ id: 'info', label: 'info' },
@@ -165,11 +155,7 @@ export default function EpisodeDetail({
165155
<Fragment>
166156
<LabeledRow
167157
label="Published"
168-
text={
169-
episode
170-
? format(new Date(episode.date), 'ccc, MMMM do p')
171-
: null
172-
}
158+
text={episode ? format(new Date(episode.date), 'ccc, MMMM do p') : null}
173159
/>
174160
<LabeledRow
175161
label="Progress"
@@ -179,14 +165,9 @@ export default function EpisodeDetail({
179165
/>
180166
<LabeledRow
181167
label="File Size"
182-
text={
183-
episode.fileSize ? formatFileSize(episode?.fileSize) : 'Unknown'
184-
}
185-
/>
186-
<LabeledRow
187-
label="Downloaded"
188-
text={episode.localFileUrl || 'No'}
168+
text={episode.fileSize ? formatFileSize(episode?.fileSize) : 'Unknown'}
189169
/>
170+
<LabeledRow label="Downloaded" text={episode.localFileUrl || 'No'} />
190171
</Fragment>
191172
) : null}
192173

0 commit comments

Comments
 (0)