Skip to content

Commit 74dbfdb

Browse files
Implement firmware list + refactoring
1 parent 041c67d commit 74dbfdb

File tree

9 files changed

+267
-22
lines changed

9 files changed

+267
-22
lines changed

src/App.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { store, persistor, useAppSelector } from '@/store';
2828
import { ReactNavigationDarkTheme, ReactNavigationLightTheme } from '@/style';
2929
import '@/translations';
3030
import NavigationStack from '@/views/navigation/NavigationStack';
31+
import type { TranslationsType } from 'react-native-paper-dates';
3132

3233
const log = rootLogger.extend('App');
3334

@@ -219,7 +220,7 @@ const _App: FC = () => {
219220
}, [i18nLanguageMatchesSettings]);
220221

221222
useEffect(() => {
222-
registerTranslation(i18n.language, {
223+
const translation: TranslationsType = {
223224
selectSingle: t('RNPaperDates.selectSingle'),
224225
selectMultiple: t('RNPaperDates.selectMultiple'),
225226
selectRange: t('RNPaperDates.selectRange'),
@@ -238,7 +239,11 @@ const _App: FC = () => {
238239
typeInDate: t('RNPaperDates.typeInDate'),
239240
pickDateFromCalendar: t('RNPaperDates.pickDateFromCalendar'),
240241
close: t('RNPaperDates.close'),
241-
});
242+
hour: t('RNPaperDates.hour'),
243+
minute: t('RNPaperDates.minute'),
244+
};
245+
246+
registerTranslation(i18n.language, translation);
242247
}, [i18n.language, t]);
243248

244249
const showEnableAppUpdatesModal = useAppSelector(
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import type { FC } from 'react';
2+
import { useCallback } from 'react';
3+
import type { Release } from '@octokit/webhooks-types';
4+
import moment from 'moment/moment';
5+
import { Badge, Divider, List, Text } from 'react-native-paper';
6+
import useDtuState from '@/hooks/useDtuState';
7+
import { Linking, Text as RNText, View } from 'react-native';
8+
// import { useNavigation } from '@react-navigation/native';
9+
// import type { NavigationProp, ParamListBase } from '@react-navigation/native';
10+
import { useTranslation } from 'react-i18next';
11+
12+
import type { RenderRules } from 'react-native-markdown-display';
13+
import Markdown from 'react-native-markdown-display';
14+
import SettingsSurface, {
15+
settingsSurfaceBorderRadius,
16+
} from '@/components/styled/SettingsSurface';
17+
18+
export interface FirmwareListItemProps {
19+
release: Release;
20+
latestReleaseTag?: string;
21+
}
22+
23+
const rules: RenderRules = {
24+
link: (node, children) => <RNText key={node.key}>{children}</RNText>,
25+
};
26+
27+
const FirmwareListItem: FC<FirmwareListItemProps> = ({
28+
release,
29+
latestReleaseTag,
30+
}) => {
31+
const { t } = useTranslation();
32+
const currentRelease = useDtuState(state => state?.systemStatus?.git_hash);
33+
// const navigation = useNavigation() as NavigationProp<ParamListBase>;
34+
35+
const handleOpenGithub = useCallback(async () => {
36+
const url = release.html_url;
37+
38+
if (await Linking.canOpenURL(url)) {
39+
await Linking.openURL(url);
40+
}
41+
}, [release.html_url]);
42+
43+
/*const handleInstallFirmware = useCallback(() => {
44+
45+
}, []);*/
46+
47+
return (
48+
<List.Accordion
49+
key={`firmware-${release.id}`}
50+
title={
51+
<View style={{ flexDirection: 'row', gap: 8 }}>
52+
<Text variant="titleMedium" numberOfLines={1}>
53+
{release.name}
54+
</Text>
55+
{release.tag_name === latestReleaseTag ? (
56+
<Badge style={{ alignSelf: 'center' }}>
57+
{t('firmwares.latestFirmware')}
58+
</Badge>
59+
) : release.tag_name === currentRelease ? (
60+
<Badge style={{ alignSelf: 'center' }}>
61+
{t('firmwares.installedFirmware')}
62+
</Badge>
63+
) : null}
64+
</View>
65+
}
66+
description={moment(release.published_at).format('lll')}
67+
>
68+
<SettingsSurface style={{ marginHorizontal: 8, flex: 1 }}>
69+
<View style={{ padding: 8, flex: 1 }}>
70+
<Markdown
71+
style={{ link: { textDecorationLine: 'none' } }}
72+
rules={rules}
73+
>
74+
{release.body}
75+
</Markdown>
76+
</View>
77+
<Divider />
78+
<List.Item
79+
title={t('firmwares.view_on_github')}
80+
onPress={handleOpenGithub}
81+
left={props => <List.Icon {...props} icon="github" />}
82+
/>
83+
<List.Item
84+
title={t('firmwares.install_firmware_on_device')}
85+
onPress={() => {
86+
// navigation.navigate('FirmwareDownload', { release });
87+
}}
88+
left={props => <List.Icon {...props} icon="download" />}
89+
borderless
90+
style={{
91+
borderBottomLeftRadius: settingsSurfaceBorderRadius,
92+
borderBottomRightRadius: settingsSurfaceBorderRadius,
93+
opacity: 0.5,
94+
}}
95+
disabled={true || release.tag_name === currentRelease}
96+
/>
97+
</SettingsSurface>
98+
</List.Accordion>
99+
);
100+
};
101+
102+
export default FirmwareListItem;

src/components/styled/SettingsSurface.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import styled from 'styled-components';
22

33
import { Surface } from 'react-native-paper';
44

5+
export const settingsSurfaceBorderRadius = 16;
6+
57
const SettingsSurface = styled(Surface)`
68
margin: 4px 8px 12px;
7-
border-radius: 16px;
9+
border-radius: ${settingsSurfaceBorderRadius}px;
810
`;
911

1012
export default SettingsSurface;

src/constants/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ export const colors = {
44
};
55

66
export const defaultUser = 'admin';
7+
8+
export const minimumOpenDtuFirmwareVersion = 'v23.11.16';

src/translations/translation-files

src/views/navigation/NavigationStack.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import SelectDatabaseScreen from '@/views/navigation/screens/SelectDatabaseScree
2626
import SetupAddOpenDTUScreen from '@/views/navigation/screens/SetupAddOpenDTUScreen';
2727
import SetupAuthenticateOpenDTUInstanceScreen from '@/views/navigation/screens/SetupAuthenticateOpenDTUInstanceScreen';
2828
import SetupOpenDTUCompleteScreen from '@/views/navigation/screens/SetupOpenDTUCompleteScreen';
29+
import FirmwareListScreen from '@/views/navigation/screens/FirmwareListScreen';
2930

3031
export type PropsWithNavigation = {
3132
navigation: NavigationProp<ParamListBase>;
@@ -103,6 +104,7 @@ const NavigationStack: FC = () => {
103104
name="InverterEventLogScreen"
104105
component={InverterEventLogScreen}
105106
/>
107+
<Stack.Screen name="FirmwareListScreen" component={FirmwareListScreen} />
106108
</Stack.Navigator>
107109
);
108110
};

src/views/navigation/screens/AboutOpenDTUScreen.tsx

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import moment from 'moment';
44
import type { FC } from 'react';
55
import { useCallback, useMemo } from 'react';
66
import { useTranslation } from 'react-i18next';
7-
import { Linking, ScrollView } from 'react-native';
7+
import { Linking, ScrollView, View } from 'react-native';
88
import { Box } from 'react-native-flex-layout';
99
import { Appbar, Badge, List, Switch, useTheme } from 'react-native-paper';
1010

1111
import { setEnableFetchOpenDTUReleases } from '@/slices/settings';
1212

13-
import SettingsSurface from '@/components/styled/SettingsSurface';
13+
import SettingsSurface, {
14+
settingsSurfaceBorderRadius,
15+
} from '@/components/styled/SettingsSurface';
1416

1517
import useDtuState from '@/hooks/useDtuState';
1618
import useHasNewOpenDtuVersion from '@/hooks/useHasNewOpenDtuVersion';
@@ -117,6 +119,10 @@ const AboutOpenDTUScreen: FC<PropsWithNavigation> = ({ navigation }) => {
117119
usedForIndicatorOnly: true,
118120
});
119121

122+
const handleNavigateToFirmwareList = useCallback(() => {
123+
navigation.navigate('FirmwareListScreen');
124+
}, [navigation]);
125+
120126
return (
121127
<>
122128
<Appbar.Header>
@@ -140,22 +146,43 @@ const AboutOpenDTUScreen: FC<PropsWithNavigation> = ({ navigation }) => {
140146
onValueChange={handleToggleFetchFromGithub}
141147
/>
142148
)}
149+
onPress={handleToggleFetchFromGithub}
150+
style={{
151+
borderTopLeftRadius: settingsSurfaceBorderRadius,
152+
borderTopRightRadius: settingsSurfaceBorderRadius,
153+
}}
154+
borderless
143155
/>
144-
<List.Item
145-
title={t('opendtu.changelog.updatesChangelog')}
146-
description={t('opendtu.changelog.updatesChangelogDescription')}
147-
right={props =>
148-
hasNewOpenDtuVersion ? (
149-
<Badge visible={true} style={{ marginTop: 8 }} {...props}>
150-
{t('settings.newOpenDtuRelease')}
151-
</Badge>
152-
) : (
153-
<List.Icon {...props} icon="arrow-right" />
154-
)
155-
}
156-
disabled
157-
style={{ opacity: 0.5 }}
158-
/>
156+
{fetchFromGithubEnabled ? (
157+
<List.Item
158+
title={t('opendtu.changelog.updatesChangelog')}
159+
description={t(
160+
'opendtu.changelog.updatesChangelogDescription',
161+
)}
162+
right={props =>
163+
hasNewOpenDtuVersion ? (
164+
<View
165+
style={{
166+
display: 'flex',
167+
justifyContent: 'center',
168+
}}
169+
>
170+
<Badge visible={true} {...props}>
171+
{t('settings.newOpenDtuRelease')}
172+
</Badge>
173+
</View>
174+
) : (
175+
<List.Icon {...props} icon="arrow-right" />
176+
)
177+
}
178+
onPress={handleNavigateToFirmwareList}
179+
style={{
180+
borderBottomLeftRadius: settingsSurfaceBorderRadius,
181+
borderBottomRightRadius: settingsSurfaceBorderRadius,
182+
}}
183+
borderless
184+
/>
185+
) : null}
159186
</SettingsSurface>
160187
<SettingsSurface>
161188
<List.Section
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import type { FC } from 'react';
2+
import { useMemo } from 'react';
3+
import { Appbar, Text, useTheme } from 'react-native-paper';
4+
import type { PropsWithNavigation } from '@/views/navigation/NavigationStack';
5+
import { StyledSafeAreaView } from '@/style';
6+
import { Box } from 'react-native-flex-layout';
7+
import { useAppSelector } from '@/store';
8+
import { ScrollView } from 'react-native';
9+
import FirmwareListItem from '@/components/firmware/FirmwareListItem';
10+
import { useTranslation } from 'react-i18next';
11+
import useDtuState from '@/hooks/useDtuState';
12+
import { compare } from 'compare-versions';
13+
14+
const FirmwareListScreen: FC<PropsWithNavigation> = ({ navigation }) => {
15+
const theme = useTheme();
16+
const { t } = useTranslation();
17+
18+
const releases = useAppSelector(state => state.github.releases.data);
19+
const currentRelease = useDtuState(state => state?.systemStatus?.git_hash);
20+
21+
const newReleases = useMemo(() => {
22+
if (!currentRelease) {
23+
return releases;
24+
}
25+
26+
return releases.filter(release =>
27+
compare(release.tag_name, currentRelease, '>'),
28+
);
29+
}, [currentRelease, releases]);
30+
31+
const outdatedReleases = useMemo(() => {
32+
if (!currentRelease) {
33+
return [];
34+
}
35+
36+
return releases.filter(release =>
37+
compare(release.tag_name, currentRelease, '<='),
38+
);
39+
}, [currentRelease, releases]);
40+
41+
const latestReleaseTag = releases[0]?.tag_name;
42+
43+
return (
44+
<>
45+
<Appbar.Header>
46+
<Appbar.BackAction onPress={() => navigation.goBack()} />
47+
<Appbar.Content title={t('firmwares.title')} />
48+
</Appbar.Header>
49+
<StyledSafeAreaView theme={theme}>
50+
<Box style={{ width: '100%', flex: 1 }}>
51+
<ScrollView
52+
contentInsetAdjustmentBehavior="automatic"
53+
style={{ width: '100%' }}
54+
>
55+
{newReleases.length ? (
56+
<>
57+
<Box style={{ marginHorizontal: 4 }}>
58+
<Text variant="titleLarge">{t('firmwares.newReleases')}</Text>
59+
</Box>
60+
{newReleases.map(release => (
61+
<FirmwareListItem
62+
key={`firmware-${release.id}`}
63+
release={release}
64+
latestReleaseTag={latestReleaseTag}
65+
/>
66+
))}
67+
</>
68+
) : null}
69+
{outdatedReleases.length ? (
70+
<>
71+
<Box style={{ marginHorizontal: 4 }}>
72+
<Text variant="titleLarge">
73+
{t('firmwares.outdatedReleases')}
74+
</Text>
75+
</Box>
76+
{outdatedReleases.map(release => (
77+
<FirmwareListItem
78+
key={`firmware-${release.id}`}
79+
release={release}
80+
latestReleaseTag={latestReleaseTag}
81+
/>
82+
))}
83+
</>
84+
) : null}
85+
{newReleases.length === 0 && outdatedReleases.length === 0 ? (
86+
<Box style={{ marginHorizontal: 4, flex: 1 }}>
87+
<Text variant="titleLarge" style={{ textAlign: 'center' }}>
88+
{t('firmwares.noReleases')}
89+
</Text>
90+
</Box>
91+
) : null}
92+
</ScrollView>
93+
</Box>
94+
</StyledSafeAreaView>
95+
</>
96+
);
97+
};
98+
99+
export default FirmwareListScreen;

src/views/navigation/screens/NetworkInformationScreen.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,13 @@ const NetworkInformationScreen: FC<PropsWithNavigation> = ({ navigation }) => {
8585
/>
8686
<List.Item
8787
title={t('opendtu.networkInformationScreen.rssi')}
88-
description={networkStatus?.sta_rssi}
88+
description={
89+
typeof networkStatus?.sta_rssi === 'number'
90+
? t('opendtu.networkInformationScreen.dBm', {
91+
rssi: networkStatus.sta_rssi,
92+
})
93+
: ''
94+
}
8995
/>
9096
</List.Section>
9197
</SettingsSurface>

0 commit comments

Comments
 (0)