Skip to content

Commit

Permalink
New conversation view with styles and theme support.
Browse files Browse the repository at this point in the history
  • Loading branch information
nchapman committed Jun 18, 2020
1 parent 69b4cb1 commit 8ca996e
Show file tree
Hide file tree
Showing 14 changed files with 211 additions and 90 deletions.
65 changes: 39 additions & 26 deletions App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as eva from "@eva-design/eva";

import { Appearance, AppearanceProvider } from "react-native-appearance";
import { ApplicationProvider, IconRegistry } from "@ui-kitten/components";

import { AppLoading } from "expo";
Expand All @@ -20,14 +21,24 @@ class App extends React.Component {

this.state = {
authToken: null,
isReady: false
isReady: false,
systemColorScheme: Appearance.getColorScheme()
};
}

async componentDidMount() {
const authToken = await AsyncStorage.getItem("authToken");

this.setState({ authToken, isReady: true });

this.appearanceListener = Appearance.addChangeListener(({ colorScheme }) => {
this.setState({ systemColorScheme: colorScheme });
});
}

componentWillUnmount() {
if (this.appearanceListener) {
this.appearanceListener.remove();
}
}

handleAuthTokenUpdate(authToken) {
Expand All @@ -42,32 +53,34 @@ class App extends React.Component {
}

return (
<ApplicationProvider {...eva} theme={eva.light}>
<IconRegistry icons={EvaIconsPack} />
<NavigationContainer>
<Navigator headerMode="none">
{this.state.authToken ? (
<>
<Screen
name="Inbox"
component={ConversationList}
initialParams={{ authToken: this.state.authToken }}
/>
<ApplicationProvider {...eva} theme={eva[this.state.systemColorScheme]}>
<AppearanceProvider>
<IconRegistry icons={EvaIconsPack} />
<NavigationContainer>
<Navigator headerMode="none">
{this.state.authToken ? (
<>
<Screen
name="Inbox"
component={ConversationList}
initialParams={{ authToken: this.state.authToken }}
/>
<Screen
name="conversationDetail"
component={ConversationDetail}
options={({ route }) => ({ title: route.params.subject })}
/>
</>
) : (
<Screen
name="conversationDetail"
component={ConversationDetail}
options={({ route }) => ({ title: route.params.subject })}
name="Sign In"
component={SignIn}
initialParams={{ onAuthTokenUpdate: this.handleAuthTokenUpdate.bind(this) }}
/>
</>
) : (
<Screen
name="Sign In"
component={SignIn}
initialParams={{ onAuthTokenUpdate: this.handleAuthTokenUpdate.bind(this) }}
/>
)}
</Navigator>
</NavigationContainer>
)}
</Navigator>
</NavigationContainer>
</AppearanceProvider>
</ApplicationProvider>
);
}
Expand Down
3 changes: 2 additions & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
],
"ios": {
"supportsTablet": true
}
},
"userInterfaceStyle": "automatic"
}
}
6 changes: 4 additions & 2 deletions components/account/SignIn.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Button, Input, Layout, Text } from "@ui-kitten/components";
import { Button, Input, Layout, Text, useStyleSheet } from "@ui-kitten/components";
import React, { useState } from "react";

import { createZimbraClient } from "../../utils";
import styles from "../../styles";
import themedStyles from "../../styles";

function SignIn({ route }) {
const styles = useStyleSheet(themedStyles);

const [username, setUsername] = useState(null);
const [password, setPassword] = useState(null);
const [errorMessage, setErrorMessage] = useState(null);
Expand Down
47 changes: 31 additions & 16 deletions components/conversation/Detail.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import { Divider, Icon, ListItem, TopNavigation, TopNavigationAction } from "@ui-kitten/components";
import { SafeAreaView, Text } from "react-native";
import { Divider, Icon, TopNavigation, TopNavigationAction, useStyleSheet } from "@ui-kitten/components";

import LoadingScreen from "../shared/LoadingScreen";
import MessageList from "../message/List";
import React from "react";
import { SafeAreaView } from "react-native";
import Subject from "./DetailSubject";
import { createZimbraClient } from "../../utils";
import themedStyles from "../../styles";
import useSWR from "swr";

const BackIcon = (props) => <Icon {...props} name="arrow-back" />;
const TrashIcon = (props) => <Icon {...props} name="trash-2-outline" />;
const ArchiveIcon = (props) => <Icon {...props} name="archive-outline" />;

const RightActions = () => (
<>
<TopNavigationAction icon={ArchiveIcon} />
<TopNavigationAction icon={TrashIcon} />
</>
);

function ConversationDetail({ navigation, route }) {
const styles = useStyleSheet(themedStyles);

async function fetcher(_key, id) {
return (await createZimbraClient()).getConversation({
id,
Expand All @@ -15,29 +31,28 @@ function ConversationDetail({ navigation, route }) {
});
}

const { data, error } = useSWR(["getConversation", route.params.id], fetcher);

function handleBackActionPress() {
navigation.goBack();
}

function BackIcon(props) {
return <Icon {...props} name="arrow-back" />;
}
const BackButtonWrapper = () => <TopNavigationAction icon={BackIcon} onPress={handleBackActionPress} />;
const SubjectWrapper = () => (
<Subject subject={route.params.subject} />
);

function renderBackButton() {
return <TopNavigationAction icon={BackIcon} onPress={handleBackActionPress} />;
}
const { data, error } = useSWR(["getConversation", route.params.id], fetcher);

return (
<SafeAreaView style={{ flex: 1, backgroundColor: "#ffffff" }}>
<TopNavigation accessoryLeft={renderBackButton} />
<SafeAreaView style={styles.safeAreaView}>
<TopNavigation accessoryLeft={BackButtonWrapper} accessoryRight={RightActions} />
<Divider />
<ListItem title={route.params.subject} />
{ data ? (
<MessageList messages={data.messages} />
{data ? (
<MessageList messages={data.messages} ListHeaderComponent={SubjectWrapper} />
) : (
<Text>Loading...</Text>
<>
<SubjectWrapper />
<LoadingScreen error={error} />
</>
)}
</SafeAreaView>
);
Expand Down
24 changes: 24 additions & 0 deletions components/conversation/DetailSubject.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Icon, Text, TopNavigation, TopNavigationAction, useStyleSheet } from "@ui-kitten/components";

import React from "react";
import themedStyles from "../../styles";

const StarIcon = (props) => <Icon {...props} fill="#8d9bb5" name="star-outline" />;

function ConversationDetailSubject({ subject }) {
const styles = useStyleSheet(themedStyles);

const SubjectHeading = () => (
<Text style={styles.subjectHeading} category="h6">{subject}</Text>
);

const StarAction = () => (
<TopNavigationAction icon={StarIcon} />
);

return (
<TopNavigation accessoryLeft={SubjectHeading} accessoryRight={StarAction} />
);
}

export default ConversationDetailSubject;
26 changes: 15 additions & 11 deletions components/conversation/List.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { Divider, List, Text, TopNavigation } from "@ui-kitten/components";
import { Divider, Icon, List, TopNavigation, TopNavigationAction, useStyleSheet } from "@ui-kitten/components";
import { Platform, SafeAreaView } from "react-native";

import ConversationListItem from "./ListItem";
import LoadingScreen from "../shared/LoadingScreen";
import React from "react";
import { SafeAreaView } from "react-native";
import { createZimbraClient } from "../../utils";
import themedStyles from "../../styles";
import useSWR from "swr";

const REFRESH_INTERVAL_SECONDS = 60 * 1000;

const MenuIcon = (props) => <Icon {...props} name="menu" />;

function ConversationList({ navigation }) {
const styles = useStyleSheet(themedStyles);

async function fetcher(_key, query) {
return (await createZimbraClient()).search({ query });
}
Expand All @@ -19,25 +25,23 @@ function ConversationList({ navigation }) {
return <ConversationListItem {...item} navigation={navigation} />;
}

if (error) {
console.error(error);

return <Text>Error</Text>;
}
const MenuAction = () => (
<TopNavigationAction icon={MenuIcon} />
);

return (
<SafeAreaView style={{ flex: 1, backgroundColor: "#ffffff" }}>
<TopNavigation title="Inbox" alignment="center" />
<SafeAreaView style={styles.safeAreaView}>
<TopNavigation style={styles.topNavigation} title="Inbox" alignment={Platform.select({ android: "start", default: "center" })} accessoryLeft={MenuAction} />
<Divider />
{data ? (
<List
style={{ flex: 1 }}
style={styles.list}
data={data.conversations}
renderItem={renderItem}
ItemSeparatorComponent={Divider}
/>
) : (
<Text>Loading...</Text>
<LoadingScreen error={error} />
)}
</SafeAreaView>
);
Expand Down
6 changes: 2 additions & 4 deletions components/conversation/ListItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ function ConversationListItem({ navigation, id, subject, emailAddresses }) {
navigation.navigate("conversationDetail", { id: id, subject: subject });
}

function renderIcon(props) {
return <Icon {...props} name="person" />;
}
const PersonIcon = (props) => <Icon {...props} name="person" />;

return (
<ListItem
title={emailAddresses[0].name}
description={subject}
accessoryLeft={renderIcon}
accessoryLeft={PersonIcon}
onPress={navigateToConversation}
/>
);
Expand Down
28 changes: 19 additions & 9 deletions components/message/HtmlViewer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Dimensions, Text } from "react-native";
import React, { useState } from "react";

import { Dimensions } from "react-native";
import { WebView } from "react-native-webview";
import { useTheme } from "@ui-kitten/components";

const injectedJavascript = `
setTimeout(function() {
Expand All @@ -12,19 +13,28 @@ const injectedJavascript = `
}, 10);
true;`;

const injectedCss = `
function HtmlViewer({ html }) {
const theme = useTheme();

// Higher starting height seems to lead to better height/width calculations
const [height, setHeight] = useState(1000);

const injectedCss = `
<style>
#bipbopmail {
body {
background: ${theme["background-basic-color-1"]};
color: ${theme["text-basic-color"]};
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 16px;
max-width: 100%;
margin: 0;
padding: 0;
}
a {
color: ${theme["color-primary-default"]};
}
</style>`;

function HtmlViewer({ html }) {
// Higher starting height seems to lead to better height/width calculations
const [height, setHeight] = useState(1000);

function handleWebViewMessage(event) {
const { height, width } = JSON.parse(event.nativeEvent.data);

Expand Down Expand Up @@ -62,7 +72,7 @@ function HtmlViewer({ html }) {
onMessage={handleWebViewMessage}
injectedJavaScript={injectedJavascript}
scrollEnabled={false}
style={{ flex: 0, height: height }}
style={{ backgroundColor: theme["background-basic-color-1"], flex: 0, height: height }}
/>
);
}
Expand Down
21 changes: 16 additions & 5 deletions components/message/List.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import { List } from "@ui-kitten/components";
import { List, useStyleSheet } from "@ui-kitten/components";

import MessageListItem from "./ListItem";
import React from "react";
import themedStyles from "../../styles";

function MessageList({ navigation, messages, ListHeaderComponent }) {
const styles = useStyleSheet(themedStyles);

function MessageList({ navigation, messages }) {
function renderItem({ item }) {
return <MessageListItem {...item} navigation={navigation} />;
}

return <List style={{ flex: 1 }} data={messages} renderItem={renderItem} />;

return (
<List
data={messages}
style={styles.list}
renderItem={renderItem}
ListHeaderComponent={ListHeaderComponent}
/>
);
}

export default MessageList;
export default MessageList;
Loading

0 comments on commit 8ca996e

Please sign in to comment.