Improved documentation

This commit is contained in:
Arnaud Vergnet 2020-03-29 14:46:44 +02:00
parent a533f48a12
commit 4cdfc607e6
21 changed files with 380 additions and 156 deletions

View file

@ -2,6 +2,12 @@ import * as React from 'react';
import {withTheme} from 'react-native-paper';
import {Agenda} from "react-native-calendars";
/**
* Abstraction layer for Agenda component, using custom configuration
*
* @param props Props to pass to the element. Must specify an onRef prop to get an Agenda ref.
* @return {*}
*/
function CustomAgenda(props) {
const {colors} = props.theme;
return (

View file

@ -9,47 +9,24 @@ import i18n from 'i18n-js';
import AppIntroSlider from "react-native-app-intro-slider";
import Update from "../constants/Update";
// Content to be used int the intro slides
const styles = StyleSheet.create({
mainContent: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingBottom: 100
},
image: {
width: 300,
height: 300,
marginBottom: -50,
},
text: {
color: 'rgba(255, 255, 255, 0.8)',
backgroundColor: 'transparent',
textAlign: 'center',
paddingHorizontal: 16,
},
title: {
fontSize: 22,
color: 'white',
backgroundColor: 'transparent',
textAlign: 'center',
marginBottom: 16,
},
});
type Props = {
onDone: Function,
isUpdate: boolean,
isAprilFools: boolean,
};
/**
* Class used to create intro slides
*/
export default class CustomIntroSlider extends React.Component<Props> {
introSlides: Array<Object>;
updateSlides: Array<Object>;
aprilFoolsSlides: Array<Object>;
/**
* Generates intro slides
*/
constructor() {
super();
this.introSlides = [
@ -126,8 +103,9 @@ export default class CustomIntroSlider extends React.Component<Props> {
/**
* Render item to be used for the intro introSlides
* @param item
* @param dimensions
*
* @param item The item to be displayed
* @param dimensions Dimensions of the item
*/
static getIntroRenderItem({item, dimensions}: Object) {
@ -178,3 +156,29 @@ export default class CustomIntroSlider extends React.Component<Props> {
}
const styles = StyleSheet.create({
mainContent: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingBottom: 100
},
image: {
width: 300,
height: 300,
marginBottom: -50,
},
text: {
color: 'rgba(255, 255, 255, 0.8)',
backgroundColor: 'transparent',
textAlign: 'center',
paddingHorizontal: 16,
},
title: {
fontSize: 22,
color: 'white',
backgroundColor: 'transparent',
textAlign: 'center',
marginBottom: 16,
},
});

View file

@ -4,6 +4,12 @@ import * as React from 'react';
import {withTheme} from 'react-native-paper';
import {Modalize} from "react-native-modalize";
/**
* Abstraction layer for Modalize component, using custom configuration
*
* @param props Props to pass to the element. Must specify an onRef prop to get an Modalize ref.
* @return {*}
*/
function CustomModal(props) {
const {colors} = props.theme;
return (

View file

@ -1,19 +1,19 @@
import * as React from 'react';
import {ActivityIndicator, Subheading, withTheme} from 'react-native-paper';
import {View} from "react-native";
import {StyleSheet, View} from "react-native";
import {MaterialCommunityIcons} from "@expo/vector-icons";
function EmptyWebSectionListItem(props) {
/**
* Component used to display a message when a list is empty
*
* @param props Props to pass to the component
* @return {*}
*/
function EmptyWebSectionListItem(props: { text: string, icon: string, refreshing: boolean, theme: {} }) {
const {colors} = props.theme;
return (
<View>
<View style={{
justifyContent: 'center',
alignItems: 'center',
width: '100%',
height: 100,
marginBottom: 20
}}>
<View style={styles.iconContainer}>
{props.refreshing ?
<ActivityIndicator
animating={true}
@ -27,9 +27,7 @@ function EmptyWebSectionListItem(props) {
</View>
<Subheading style={{
textAlign: 'center',
marginRight: 20,
marginLeft: 20,
...styles.subheading,
color: colors.textDisabled
}}>
{props.text}
@ -38,4 +36,19 @@ function EmptyWebSectionListItem(props) {
);
}
const styles = StyleSheet.create({
iconContainer: {
justifyContent: 'center',
alignItems: 'center',
width: '100%',
height: 100,
marginBottom: 20
},
subheading: {
textAlign: 'center',
marginRight: 20,
marginLeft: 20,
}
});
export default withTheme(EmptyWebSectionListItem);

View file

@ -2,7 +2,14 @@
import * as React from 'react';
import {Avatar, Card, withTheme} from 'react-native-paper';
import {StyleSheet} from "react-native";
/**
* Component used to display a dashboard item containing a preview event
*
* @param props Props to pass to the component
* @return {*}
*/
function EventDashBoardItem(props) {
const {colors} = props.theme;
const iconColor = props.isAvailable ?
@ -13,13 +20,7 @@ function EventDashBoardItem(props) {
colors.textDisabled;
return (
<Card
style={{
width: 'auto',
marginLeft: 10,
marginRight: 10,
marginTop: 10,
overflow: 'hidden',
}}
style={styles.card}
onPress={props.clickAction}>
<Card.Title
@ -32,7 +33,7 @@ function EventDashBoardItem(props) {
icon={props.icon}
color={iconColor}
size={60}
style={{backgroundColor: 'transparent'}}/>}
style={styles.avatar}/>}
/>
<Card.Content>
{props.children}
@ -41,4 +42,17 @@ function EventDashBoardItem(props) {
);
}
const styles = StyleSheet.create({
card: {
width: 'auto',
marginLeft: 10,
marginRight: 10,
marginTop: 10,
overflow: 'hidden',
},
avatar: {
backgroundColor: 'transparent'
}
});
export default withTheme(EventDashBoardItem);

View file

@ -6,6 +6,11 @@ import i18n from "i18n-js";
const ICON_AMICALE = require('../assets/amicale.png');
/**
* Gets the amicale INSAT logo
*
* @return {*}
*/
function getAvatar() {
return (
<Avatar.Image size={48} source={ICON_AMICALE}
@ -13,6 +18,12 @@ function getAvatar() {
);
}
/**
* Component used to display a feed item
*
* @param props Props to pass to the component
* @return {*}
*/
function FeedItem(props) {
const {colors} = props.theme;
return (
@ -39,7 +50,9 @@ function FeedItem(props) {
<Button
color={'#57aeff'}
onPress={props.onOutLinkPress}
icon={'facebook'}>{i18n.t('homeScreen.dashboard.seeMore')}</Button>
icon={'facebook'}>
{i18n.t('homeScreen.dashboard.seeMore')}
</Button>
</Card.Actions>
</Card>
);

View file

@ -1,6 +1,12 @@
import * as React from 'react';
import {IconButton, withTheme} from 'react-native-paper';
/**
* Component used to display a header button
*
* @param props Props to pass to the component
* @return {*}
*/
function HeaderButton(props) {
const {colors} = props.theme;
return (

View file

@ -1,25 +1,33 @@
// @flow
import * as React from 'react';
import {View} from "react-native";
import {StyleSheet, View} from "react-native";
import HTML from "react-native-render-html";
import i18n from "i18n-js";
import {Avatar, Button, Card, withTheme} from 'react-native-paper';
import PlanningEventManager from "../utils/PlanningEventManager";
/**
* Component used to display an event preview if an event is available
*
* @param props Props to pass to the component
* @return {*}
*/
function PreviewEventDashboardItem(props) {
const {colors} = props.theme;
const isEmpty = props.event === undefined ? true : PlanningEventManager.isDescriptionEmpty(props.event['description']);
const isEmpty = props.event === undefined
? true
: PlanningEventManager.isDescriptionEmpty(props.event['description']);
if (props.event !== undefined && props.event !== null) {
const hasImage = props.event['logo'] !== '' && props.event['logo'] !== null;
const getImage = () => <Avatar.Image
source={{uri: props.event['logo']}}
size={50}
style={{backgroundColor: 'transparent'}}/>;
style={styles.avatar}/>;
return (
<Card
style={{marginBottom: 10}}
style={styles.card}
onPress={props.clickAction}
elevation={3}
>
@ -34,10 +42,7 @@ function PreviewEventDashboardItem(props) {
subtitle={PlanningEventManager.getFormattedEventTime(props.event['date_begin'], props.event['date_end'])}
/>}
{!isEmpty ?
<Card.Content style={{
maxHeight: 150,
overflow: 'hidden',
}}>
<Card.Content style={styles.content}>
<HTML html={"<div>" + props.event['description'] + "</div>"}
tagsStyles={{
p: {color: colors.text,},
@ -46,11 +51,7 @@ function PreviewEventDashboardItem(props) {
</Card.Content> : null}
<Card.Actions style={{
marginLeft: 'auto',
marginTop: 'auto',
flexDirection: 'row'
}}>
<Card.Actions style={styles.actions}>
<Button
icon={'chevron-right'}
>
@ -63,4 +64,22 @@ function PreviewEventDashboardItem(props) {
return <View/>
}
const styles = StyleSheet.create({
card: {
marginBottom: 10
},
content: {
maxHeight: 150,
overflow: 'hidden',
},
actions: {
marginLeft: 'auto',
marginTop: 'auto',
flexDirection: 'row'
},
avatar: {
backgroundColor: 'transparent'
}
});
export default withTheme(PreviewEventDashboardItem);

View file

@ -1,8 +1,14 @@
import * as React from 'react';
import {Avatar, Card, Text, withTheme} from 'react-native-paper';
import {View} from "react-native";
import {StyleSheet, View} from "react-native";
import ProxiwashConstants from "../constants/ProxiwashConstants";
/**
* Component used to display a proxiwash item, showing machine progression and state
*
* @param props Props to pass to the component
* @return {*}
*/
function ProxiwashListItem(props) {
const {colors} = props.theme;
let stateColors = {};
@ -17,13 +23,13 @@ function ProxiwashListItem(props) {
icon={'bell-ring'}
size={45}
color={colors.primary}
style={{backgroundColor: 'transparent'}}
style={styles.icon}
/> :
<Avatar.Icon
icon={props.isDryer ? 'tumble-dryer' : 'washing-machine'}
color={colors.text}
size={40}
style={{backgroundColor: 'transparent'}}
style={styles.icon}
/>
);
return (
@ -35,37 +41,26 @@ function ProxiwashListItem(props) {
>
{ProxiwashConstants.machineStates[props.state] === ProxiwashConstants.machineStates["EN COURS"] ?
<Card style={{
height: '100%',
position: 'absolute',
left: 0,
width: '100%',
...styles.backgroundCard,
backgroundColor: colors.proxiwashRunningBgColor,
elevation: 0
}}/> : null
}
<Card style={{
height: '100%',
position: 'absolute',
left: 0,
...styles.progressionCard,
width: props.progress,
backgroundColor: stateColors[ProxiwashConstants.machineStates[props.state]],
elevation: 0
}}/>
<Card.Title
title={props.title}
titleStyle={{fontSize: 17}}
subtitle={props.description}
style={{
backgroundColor: 'transparent',
height: 64
}}
style={styles.title}
left={() => icon}
right={() => (
<View style={{flexDirection: 'row'}}>
<View style={{
justifyContent: 'center',
}}>
<View style={{justifyContent: 'center'}}>
<Text style={
ProxiwashConstants.machineStates[props.state] === ProxiwashConstants.machineStates.TERMINE ?
{fontWeight: 'bold',} : {}}
@ -73,12 +68,11 @@ function ProxiwashListItem(props) {
{props.statusText}
</Text>
</View>
<Avatar.Icon
icon={props.statusIcon}
color={colors.text}
size={30}
style={{backgroundColor: 'transparent'}}
style={styles.icon}
/>
</View>)}
/>
@ -86,4 +80,27 @@ function ProxiwashListItem(props) {
);
}
const styles = StyleSheet.create({
icon: {
backgroundColor: 'transparent'
},
backgroundCard: {
height: '100%',
position: 'absolute',
left: 0,
width: '100%',
elevation: 0,
},
progressionCard: {
height: '100%',
position: 'absolute',
left: 0,
elevation: 0,
},
title: {
backgroundColor: 'transparent',
height: 64
}
});
export default withTheme(ProxiwashListItem);

View file

@ -9,6 +9,8 @@ type Props = {
}
/**
* FlatList implementing PureComponent for increased performance.
*
* This is a pure component, meaning it will only update if a shallow comparison of state and props is different.
* To force the component to update, change the value of updateData.
*/

View file

@ -20,7 +20,7 @@ type State = {
};
/**
* Class used to define a navigation drawer
* Component used to render the drawer menu content
*/
export default class SideBar extends React.PureComponent<Props, State> {
@ -33,7 +33,7 @@ export default class SideBar extends React.PureComponent<Props, State> {
getRenderItem: Function;
/**
* Generate the datasets
* Generate the dataset
*
* @param props
*/
@ -123,6 +123,12 @@ export default class SideBar extends React.PureComponent<Props, State> {
this.getRenderItem = this.getRenderItem.bind(this);
}
/**
* Callback when a drawer item is pressed.
* It will either navigate to the associated screen, or open the browser to the associated link
*
* @param item The item pressed
*/
onListItemPress(item: Object) {
if (item.link === undefined)
this.props.navigation.navigate(item.route);
@ -130,12 +136,22 @@ export default class SideBar extends React.PureComponent<Props, State> {
WebBrowser.openBrowserAsync(item.link);
}
listKeyExtractor(item: Object) {
/**
* Key extractor for list items
*
* @param item The item to extract the key from
* @return {string} The extracted key
*/
listKeyExtractor(item: Object): string {
return item.route;
}
/**
* Gets the render item for the given list item
*
* @param item The item to render
* @return {*}
*/
getRenderItem({item}: Object) {
const onListItemPress = this.onListItemPress.bind(this, item);
if (item.icon !== undefined) {

View file

@ -2,6 +2,12 @@ import * as React from 'react';
import {withTheme} from 'react-native-paper';
import {DrawerItem} from "@react-navigation/drawer";
/**
* Component used to render a drawer menu item divider
*
* @param props Props to pass to the component
* @return {*}
*/
function SidebarDivider(props) {
const {colors} = props.theme;
return (

View file

@ -3,6 +3,12 @@ import {withTheme} from 'react-native-paper';
import {DrawerItem} from "@react-navigation/drawer";
import {MaterialCommunityIcons} from "@expo/vector-icons";
/**
* Component used to render a drawer menu item
*
* @param props Props to pass to the component
* @return {*}
*/
function SidebarItem(props) {
const {colors} = props.theme;
return (

View file

@ -2,6 +2,12 @@ import * as React from 'react';
import {Badge, IconButton, withTheme} from 'react-native-paper';
import {View} from "react-native";
/**
* Component used to render a small dashboard item
*
* @param props Props to pass to the component
* @return {*}
*/
function SquareDashboardItem(props) {
const {colors} = props.theme;
return (
@ -9,9 +15,9 @@ function SquareDashboardItem(props) {
<IconButton
icon={props.icon}
color={
props.isAvailable ?
props.color :
colors.textDisabled
props.isAvailable
? props.color
: colors.textDisabled
}
size={35}
onPress={props.clickAction}
@ -23,9 +29,10 @@ function SquareDashboardItem(props) {
position: 'absolute',
top: 5,
right: 5
}}>{props.badgeNumber}</Badge> : null
}}>
{props.badgeNumber}
</Badge> : null
}
</View>
);
}

View file

@ -29,6 +29,8 @@ type State = {
const MIN_REFRESH_TIME = 5 * 1000;
/**
* Component used to render a SectionList with data fetched from the web
*
* This is a pure component, meaning it will only update if a shallow comparison of state and props is different.
* To force the component to update, change the value of updateData.
*/
@ -73,7 +75,7 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
}
/**
* Register react navigation events on first screen load.
* Registers react navigation events on first screen load.
* Allows to detect when the screen is focused
*/
componentDidMount() {
@ -86,7 +88,7 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
}
/**
* Refresh data when focusing the screen and setup a refresh interval if asked to
* Refreshes data when focusing the screen and setup a refresh interval if asked to
*/
onScreenFocus() {
if (this.props.refreshOnFocus && this.lastRefresh !== undefined)
@ -96,13 +98,19 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
}
/**
* Remove any interval on un-focus
* Removes any interval on un-focus
*/
onScreenBlur() {
clearInterval(this.refreshInterval);
}
/**
* Callback used when fetch is successful.
* It will update the displayed data and stop the refresh animation
*
* @param fetchedData The newly fetched data
*/
onFetchSuccess(fetchedData: Object) {
this.setState({
fetchedData: fetchedData,
@ -112,6 +120,10 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
this.lastRefresh = new Date();
}
/**
* Callback used when fetch encountered an error.
* It will reset the displayed data and show an error.
*/
onFetchError() {
this.setState({
fetchedData: {},
@ -119,12 +131,10 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
firstLoading: false
});
this.showSnackBar();
// this.webDataManager.showUpdateToast(this.props.updateErrorText);
}
/**
* Refresh data and show a toast if any error occurred
* @private
* Refreshes data and shows an animations while doing it
*/
onRefresh() {
let canRefresh;
@ -140,10 +150,22 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
}
}
/**
* Gets an empty section header
*
* @param section The current section
* @return {*}
*/
getEmptySectionHeader({section}: Object) {
return <View/>;
}
/**
* Gets an empty render item
*
* @param item The data to display
* @return {*}
*/
getEmptyRenderItem({item}: Object) {
return (
<EmptyWebSectionListItem
@ -154,6 +176,11 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
);
}
/**
* Creates an empty dataset
*
* @return {*}
*/
createEmptyDataset() {
return [
{
@ -174,14 +201,26 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
];
}
datasetKeyExtractor(item: Object) {
/**
* Extracts a key from the given item
*
* @param item The item to extract the key from
* @return {string} The extracted key
*/
datasetKeyExtractor(item: Object): string {
return item.text
}
/**
* Shows the error popup
*/
showSnackBar() {
this.setState({snackbarVisible: true})
}
/**
* Hides the error popup
*/
hideSnackBar() {
this.setState({snackbarVisible: false})
}

View file

@ -34,8 +34,6 @@ class WebViewScreen extends React.PureComponent<Props> {
onRefreshClicked: Function;
onWebviewRef: Function;
onGoBackWebview: Function;
onGoForwardWebview: Function;
getRenderLoading: Function;
colors: Object;
@ -44,12 +42,13 @@ class WebViewScreen extends React.PureComponent<Props> {
super(props);
this.onRefreshClicked = this.onRefreshClicked.bind(this);
this.onWebviewRef = this.onWebviewRef.bind(this);
this.onGoBackWebview = this.onGoBackWebview.bind(this);
this.onGoForwardWebview = this.onGoForwardWebview.bind(this);
this.getRenderLoading = this.getRenderLoading.bind(this);
this.colors = props.theme.colors;
}
/**
* Creates refresh button after mounting
*/
componentDidMount() {
const rightButton = this.getRefreshButton.bind(this);
this.props.navigation.setOptions({
@ -57,42 +56,37 @@ class WebViewScreen extends React.PureComponent<Props> {
});
}
getHeaderButton(clickAction: Function, icon: string) {
return (
<HeaderButton icon={icon} onPress={clickAction}/>
);
}
/**
* Gets a header refresh button
*
* @return {*}
*/
getRefreshButton() {
return (
<View style={{
flexDirection: 'row',
marginRight: 10
}}>
{this.getHeaderButton(this.onRefreshClicked, 'refresh')}
</View>
);
return <HeaderButton icon={'refresh'} onPress={this.onRefreshClicked}/>
};
/**
* Callback to use when refresh button is clicked. Reloads the webview.
*/
onRefreshClicked() {
if (this.webviewRef !== null)
this.webviewRef.reload();
}
onGoBackWebview() {
if (this.webviewRef !== null)
this.webviewRef.goBack();
}
onGoForwardWebview() {
if (this.webviewRef !== null)
this.webviewRef.goForward();
}
/**
* Callback used when receiving the webview ref. Stores the ref for later use
*
* @param ref
*/
onWebviewRef(ref: Object) {
this.webviewRef = ref
}
/**
* Gets the loading indicator
*
* @return {*}
*/
getRenderLoading() {
return (
<View style={{
@ -115,7 +109,6 @@ class WebViewScreen extends React.PureComponent<Props> {
}
render() {
// console.log("rendering WebViewScreen");
return (
<WebView
ref={this.onWebviewRef}

View file

@ -1,12 +1,31 @@
import i18n from "i18n-js";
/**
* Singleton used to manage update slides.
* Must be a singleton because it uses translations.
*
* Change values in this class to change the update slide.
* You will also need to update those translations:
* <ul>
* <li>intro.updateSlide.title</li>
* <li>intro.updateSlide.text</li>
* </ul>
*/
export default class Update {
// Increment the number to show the update slide
static number = 5;
// Change the icon to be displayed on the update slide
static icon = 'surround-sound-2-0';
static instance: Update | null = null;
title: string;
description: string;
/**
* Init translations
*/
constructor() {
this.title = i18n.t('intro.updateSlide.title');
this.description = i18n.t('intro.updateSlide.text');
@ -14,6 +33,7 @@ export default class Update {
/**
* Get this class instance or create one if none is found
*
* @returns {Update}
*/
static getInstance(): Update {

View file

@ -3,7 +3,7 @@
import {AsyncStorage} from "react-native";
/**
* Static class used to manage preferences.
* Singleton used to manage preferences.
* Preferences are fetched at the start of the app and saved in an instance object.
* This allows for a synchronous access to saved data.
*/
@ -14,7 +14,7 @@ export default class AsyncStorageManager {
/**
* Get this class instance or create one if none is found
* @returns {ThemeManager}
* @returns {AsyncStorageManager}
*/
static getInstance(): AsyncStorageManager {
return AsyncStorageManager.instance === null ?
@ -113,7 +113,7 @@ export default class AsyncStorageManager {
/**
* Save the value associated to the given key to preferences.
* This updates the preferences object and saves it to AsynStorage.
* This updates the preferences object and saves it to AsyncStorage.
*
* @param key
* @param val

View file

@ -2,10 +2,13 @@
import i18n from 'i18n-js';
/**
* Singleton used to manage date translations.
* Translations are hardcoded as toLocaleDateString does not work on current android JS engine
*/
export default class DateManager {
static instance: DateManager | null = null;
// Hard code strings as toLocaleDateString does not work on current android JS engine
daysOfWeek = [];
monthsOfYear = [];
@ -42,6 +45,12 @@ export default class DateManager {
DateManager.instance;
}
/**
* Gets a translated string representing the given date.
*
* @param dateString The date with the format YYYY-MM-DD
* @return {string} The translated string
*/
getTranslatedDate(dateString: string) {
let dateArray = dateString.split('-');
let date = new Date();

View file

@ -49,6 +49,11 @@ export default class NotificationsManager {
}
}
/**
* Gets the machines watched from the server
*
* @param callback Function to execute with the fetched data
*/
static getMachineNotificationWatchlist(callback: Function) {
let token = AsyncStorageManager.getInstance().preferences.expoToken.current;
if (token !== '') {
@ -72,10 +77,10 @@ export default class NotificationsManager {
}
/**
* Ask the server to enable/disable notifications for the specified machine
* Asks the server to enable/disable notifications for the specified machine
*
* @param machineID
* @param isEnabled
* @param machineID The machine ID
* @param isEnabled True to enable notifications, false to disable
*/
static setupMachineNotification(machineID: string, isEnabled: boolean) {
let token = AsyncStorageManager.getInstance().preferences.expoToken.current;
@ -100,8 +105,9 @@ export default class NotificationsManager {
}
/**
* Send the selected reminder time for notifications to the server
* @param time
* Sends the selected reminder time for notifications to the server
*
* @param time The reminder time to use
*/
static setMachineReminderNotificationTime(time: number) {
let token = AsyncStorageManager.getInstance().preferences.expoToken.current;

View file

@ -19,7 +19,12 @@ export default class ThemeManager {
this.updateThemeCallback = null;
}
static getWhiteTheme() {
/**
* Gets the light theme
*
* @return {Object} Object containing theme variables
* */
static getWhiteTheme(): Object {
return {
...DefaultTheme,
colors: {
@ -70,7 +75,12 @@ export default class ThemeManager {
};
}
static getDarkTheme() {
/**
* Gets the dark theme
*
* @return {Object} Object containing theme variables
* */
static getDarkTheme(): Object {
return {
...DarkTheme,
colors: {
@ -124,6 +134,7 @@ export default class ThemeManager {
/**
* Get this class instance or create one if none is found
*
* @returns {ThemeManager}
*/
static getInstance(): ThemeManager {
@ -133,6 +144,10 @@ export default class ThemeManager {
}
/**
* Gets night mode status.
* If Follow System Preferences is enabled, will first use system theme.
* If disabled or not available, will use value stored din preferences
*
* @returns {boolean} Night mode state
*/
static getNightMode(): boolean {
@ -143,8 +158,9 @@ export default class ThemeManager {
}
/**
* Get the current theme based on night mode
* @returns {Object}
* Get the current theme based on night mode and events
*
* @returns {Object} The current theme
*/
static getCurrentTheme(): Object {
if (AprilFoolsManager.getInstance().isAprilFoolsEnabled())
@ -153,6 +169,11 @@ export default class ThemeManager {
return ThemeManager.getBaseTheme()
}
/**
* Get the theme based on night mode
*
* @return {Object} The theme
*/
static getBaseTheme() {
if (ThemeManager.getNightMode())
return ThemeManager.getDarkTheme();
@ -161,7 +182,8 @@ export default class ThemeManager {
}
/**
* Set the function to be called when the theme is changed (allows for general reload of the app)
* Sets the function to be called when the theme is changed (allows for general reload of the app)
*
* @param callback Function to call after theme change
*/
setUpdateThemeCallback(callback: ?Function) {
@ -171,7 +193,7 @@ export default class ThemeManager {
/**
* Set night mode and save it to preferences
*
* @param isNightMode Whether to enable night mode
* @param isNightMode True to enable night mode, false to disable
*/
setNightMode(isNightMode: boolean) {
let nightModeKey = AsyncStorageManager.getInstance().preferences.nightMode.key;