From f282a1dd849470461bb893dfa541eaf6c5cfb5b1 Mon Sep 17 00:00:00 2001
From: Arnaud Vergnet <vergnet@etud.insa-toulouse.fr>
Date: Thu, 9 Apr 2020 23:59:45 +0200
Subject: [PATCH] Further theme switching improvements

---
 src/components/Custom/CustomAgenda.js         |  3 +-
 src/components/Custom/CustomHTML.js           | 44 +++++++++++++++++++
 .../Home/PreviewEventDashboardItem.js         | 14 ++----
 .../Amicale/Clubs/ClubDisplayScreen.js        | 14 +-----
 src/screens/Amicale/ProfileScreen.js          | 39 ++++++----------
 src/screens/Planning/PlanningDisplayScreen.js | 17 ++-----
 6 files changed, 68 insertions(+), 63 deletions(-)
 create mode 100644 src/components/Custom/CustomHTML.js

diff --git a/src/components/Custom/CustomAgenda.js b/src/components/Custom/CustomAgenda.js
index 3d9d15f..b490fc7 100644
--- a/src/components/Custom/CustomAgenda.js
+++ b/src/components/Custom/CustomAgenda.js
@@ -45,7 +45,8 @@ class CustomAgenda extends React.Component<Props> {
     }
 
     render() {
-        if (this.props.theme.colors.text === "#ffffff") // We are in light mode
+        // Completely recreate the component on theme change to force theme reload
+        if (this.props.theme.dark)
             return (
                 <View style={{flex: 1}}>
                     {this.getAgenda()}
diff --git a/src/components/Custom/CustomHTML.js b/src/components/Custom/CustomHTML.js
new file mode 100644
index 0000000..f04adca
--- /dev/null
+++ b/src/components/Custom/CustomHTML.js
@@ -0,0 +1,44 @@
+import * as React from 'react';
+import {View} from "react-native";
+import {withTheme} from 'react-native-paper';
+import HTML from "react-native-render-html";
+import {Linking} from "expo";
+
+type Props = {
+    theme: Object,
+    html: string,
+}
+
+/**
+ * Abstraction layer for Agenda component, using custom configuration
+ */
+class CustomHTML extends React.Component<Props> {
+
+    openWebLink = (event, link) => {
+        Linking.openURL(link).catch((err) => console.error('Error opening link', err));
+    };
+
+    getHTML() {
+        // Surround description with div to allow text styling if the description is not html
+        return <HTML html={"<div>" + this.props.html + "</div>"}
+                     tagsStyles={{
+                         p: {color: this.props.theme.colors.text},
+                         div: {color: this.props.theme.colors.text}
+                     }}
+                     onLinkPress={this.openWebLink}/>;
+    }
+
+    render() {
+        // Completely recreate the component on theme change to force theme reload
+        if (this.props.theme.dark)
+            return (
+                <View style={{flex: 1}}>
+                    {this.getHTML()}
+                </View>
+            );
+        else
+            return this.getHTML();
+    }
+}
+
+export default withTheme(CustomHTML);
diff --git a/src/components/Home/PreviewEventDashboardItem.js b/src/components/Home/PreviewEventDashboardItem.js
index d77b3f1..90aa197 100644
--- a/src/components/Home/PreviewEventDashboardItem.js
+++ b/src/components/Home/PreviewEventDashboardItem.js
@@ -2,10 +2,10 @@
 
 import * as React from 'react';
 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 {Avatar, Button, Card} from 'react-native-paper';
 import {getFormattedEventTime, isDescriptionEmpty} from "../../utils/Planning";
+import CustomHTML from "../Custom/CustomHTML";
 
 /**
  * Component used to display an event preview if an event is available
@@ -14,7 +14,6 @@ import {getFormattedEventTime, isDescriptionEmpty} from "../../utils/Planning";
  * @return {*}
  */
 function PreviewEventDashboardItem(props) {
-    const {colors} = props.theme;
     const isEmpty = props.event === undefined
         ? true
         : isDescriptionEmpty(props.event['description']);
@@ -43,12 +42,7 @@ function PreviewEventDashboardItem(props) {
                     />}
                 {!isEmpty ?
                     <Card.Content style={styles.content}>
-                        <HTML html={"<div>" + props.event['description'] + "</div>"}
-                              tagsStyles={{
-                                  p: {color: colors.text,},
-                                  div: {color: colors.text},
-                              }}/>
-
+                        <CustomHTML html={props.event['description']}/>
                     </Card.Content> : null}
 
                 <Card.Actions style={styles.actions}>
@@ -82,4 +76,4 @@ const styles = StyleSheet.create({
     }
 });
 
-export default withTheme(PreviewEventDashboardItem);
+export default PreviewEventDashboardItem;
diff --git a/src/screens/Amicale/Clubs/ClubDisplayScreen.js b/src/screens/Amicale/Clubs/ClubDisplayScreen.js
index 2feafd0..54a2c08 100644
--- a/src/screens/Amicale/Clubs/ClubDisplayScreen.js
+++ b/src/screens/Amicale/Clubs/ClubDisplayScreen.js
@@ -2,12 +2,11 @@
 
 import * as React from 'react';
 import {ScrollView, View} from 'react-native';
-import HTML from "react-native-render-html";
-import {Linking} from "expo";
 import {Avatar, Card, Chip, Paragraph, withTheme} from 'react-native-paper';
 import ImageModal from 'react-native-image-modal';
 import i18n from "i18n-js";
 import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen";
+import CustomHTML from "../../../components/Custom/CustomHTML";
 
 type Props = {
     navigation: Object,
@@ -18,10 +17,6 @@ type State = {
     imageModalVisible: boolean,
 };
 
-function openWebLink(event, link) {
-    Linking.openURL(link).catch((err) => console.error('Error opening link', err));
-}
-
 /**
  * Class defining a club event information page.
  * If called with data and categories navigation parameters, will use those to display the data.
@@ -146,12 +141,7 @@ class ClubDisplayScreen extends React.Component<Props, State> {
                 {data.description !== null ?
                     // Surround description with div to allow text styling if the description is not html
                     <Card.Content>
-                        <HTML html={"<div>" + data.description + "</div>"}
-                              tagsStyles={{
-                                  p: {color: this.colors.text,},
-                                  div: {color: this.colors.text}
-                              }}
-                              onLinkPress={openWebLink}/>
+                        <CustomHTML html={data.description}/>
                     </Card.Content>
                     : <View/>}
                 {this.getManagersRender(data.responsibles)}
diff --git a/src/screens/Amicale/ProfileScreen.js b/src/screens/Amicale/ProfileScreen.js
index 10f6dbd..4f029dc 100644
--- a/src/screens/Amicale/ProfileScreen.js
+++ b/src/screens/Amicale/ProfileScreen.js
@@ -24,15 +24,12 @@ class ProfileScreen extends React.Component<Props, State> {
         dialogVisible: false,
     };
 
-    colors: Object;
-
     data: Object;
 
     flatListData: Array<Object>;
 
-    constructor(props) {
-        super(props);
-        this.colors = props.theme.colors;
+    constructor() {
+        super();
         this.flatListData = [
             {id: '0'},
             {id: '1'},
@@ -104,18 +101,6 @@ class ProfileScreen extends React.Component<Props, State> {
             : i18n.t("profileScreen.noData");
     }
 
-    /**
-     * Gets the color depending on the value.
-     *
-     * @param field The field to get the color for
-     * @return {*}
-     */
-    getFieldColor(field: ?string) {
-        return this.isFieldAvailable(field)
-            ? this.colors.text
-            : this.colors.textDisabled;
-    }
-
     /**
      * Gets a list item showing personal information
      *
@@ -124,15 +109,17 @@ class ProfileScreen extends React.Component<Props, State> {
      * @return {*}
      */
     getPersonalListItem(field: ?string, icon: string) {
+        let title = this.isFieldAvailable(field) ? this.getFieldValue(field) : ':(';
+        let subtitle = this.isFieldAvailable(field) ? '' : this.getFieldValue(field);
         return (
             <List.Item
-                title={this.getFieldValue(field)}
+                title={title}
+                description={subtitle}
                 left={props => <List.Icon
                     {...props}
                     icon={icon}
-                    color={this.getFieldColor(field)}
+                    color={this.isFieldAvailable(field) ? undefined : this.props.theme.colors.textDisabled}
                 />}
-                titleStyle={{color: this.getFieldColor(field)}}
             />
         );
     }
@@ -151,7 +138,7 @@ class ProfileScreen extends React.Component<Props, State> {
                     left={(props) => <Avatar.Icon
                         {...props}
                         icon="account"
-                        color={this.colors.primary}
+                        color={this.props.theme.colors.primary}
                         style={styles.icon}
                     />}
                 />
@@ -169,7 +156,7 @@ class ProfileScreen extends React.Component<Props, State> {
                         <Button
                             icon="account-edit"
                             mode="contained"
-                            onPress={() => openBrowser(this.data.link, this.colors.primary)}
+                            onPress={() => openBrowser(this.data.link, this.props.theme.colors.primary)}
                             style={styles.editButton}>
                             {i18n.t("profileScreen.editInformation")}
                         </Button>
@@ -193,7 +180,7 @@ class ProfileScreen extends React.Component<Props, State> {
                     left={(props) => <Avatar.Icon
                         {...props}
                         icon="account-group"
-                        color={this.colors.primary}
+                        color={this.props.theme.colors.primary}
                         style={styles.icon}
                     />}
                 />
@@ -219,7 +206,7 @@ class ProfileScreen extends React.Component<Props, State> {
                     left={(props) => <Avatar.Icon
                         {...props}
                         icon="credit-card"
-                        color={this.colors.primary}
+                        color={this.props.theme.colors.primary}
                         style={styles.icon}
                     />}
                 />
@@ -243,7 +230,7 @@ class ProfileScreen extends React.Component<Props, State> {
                 title={state ? i18n.t("profileScreen.membershipPayed") : i18n.t("profileScreen.membershipNotPayed")}
                 left={props => <List.Icon
                     {...props}
-                    color={state ? this.colors.success : this.colors.danger}
+                    color={state ? this.props.theme.colors.success : this.props.theme.colors.danger}
                     icon={state ? 'check' : 'close'}
                 />}
             />
@@ -270,7 +257,7 @@ class ProfileScreen extends React.Component<Props, State> {
         let icon = (props) => <List.Icon {...props} icon="chevron-right"/>;
         if (item.is_manager) {
             description = i18n.t("profileScreen.isManager");
-            icon = (props) => <List.Icon {...props} icon="star" color={this.colors.primary}/>;
+            icon = (props) => <List.Icon {...props} icon="star" color={this.props.theme.colors.primary}/>;
         }
         return <List.Item
             title={item.name}
diff --git a/src/screens/Planning/PlanningDisplayScreen.js b/src/screens/Planning/PlanningDisplayScreen.js
index c3efca0..07254a1 100644
--- a/src/screens/Planning/PlanningDisplayScreen.js
+++ b/src/screens/Planning/PlanningDisplayScreen.js
@@ -2,8 +2,6 @@
 
 import * as React from 'react';
 import {ScrollView, View} from 'react-native';
-import HTML from "react-native-render-html";
-import {Linking} from "expo";
 import {getDateOnlyString, getFormattedEventTime} from '../../utils/Planning';
 import {Card, withTheme} from 'react-native-paper';
 import DateManager from "../../managers/DateManager";
@@ -11,6 +9,7 @@ import ImageModal from 'react-native-image-modal';
 import BasicLoadingScreen from "../../components/Custom/BasicLoadingScreen";
 import {apiRequest} from "../../utils/WebData";
 import ErrorView from "../../components/Custom/ErrorView";
+import CustomHTML from "../../components/Custom/CustomHTML";
 
 type Props = {
     navigation: Object,
@@ -21,10 +20,6 @@ type State = {
     loading: boolean
 };
 
-function openWebLink(event, link) {
-    Linking.openURL(link).catch((err) => console.error('Error opening link', err));
-}
-
 const CLUB_INFO_PATH = "event/info";
 
 /**
@@ -111,14 +106,8 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
                     : <View/>}
 
                 {this.displayData.description !== null ?
-                    // Surround description with div to allow text styling if the description is not html
                     <Card.Content>
-                        <HTML html={"<div>" + this.displayData.description + "</div>"}
-                              tagsStyles={{
-                                  p: {color: this.colors.text,},
-                                  div: {color: this.colors.text}
-                              }}
-                              onLinkPress={openWebLink}/>
+                        <CustomHTML html={this.displayData.description}/>
                     </Card.Content>
                     : <View/>}
             </ScrollView>
@@ -131,7 +120,7 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
         else if (this.errorCode === 0)
             return this.getContent();
         else
-            return <ErrorView {...this.props} errorCode={this.errorCode}   onRefresh={this.fetchData}/>;
+            return <ErrorView {...this.props} errorCode={this.errorCode} onRefresh={this.fetchData}/>;
     }
 }