diff --git a/app/mobile/src/context/useAppContext.hook.js b/app/mobile/src/context/useAppContext.hook.js
index c101612a..f1a57bec 100644
--- a/app/mobile/src/context/useAppContext.hook.js
+++ b/app/mobile/src/context/useAppContext.hook.js
@@ -48,7 +48,7 @@ export function useAppContext() {
}
access.current = await store.actions.init();
if (access.current) {
- await setSession(access.current);
+ await setSession();
}
else {
updateState({ session: false });
@@ -58,7 +58,8 @@ export function useAppContext() {
}, []);
const setSession = async () => {
- updateState({ session: true, status: 'connecting' });
+ const { loginTimestamp } = access.current;
+ updateState({ session: true, loginTimestamp, status: 'connecting' });
await account.actions.setSession(access.current);
await profile.actions.setSession(access.current);
await card.actions.setSession(access.current);
diff --git a/app/mobile/src/session/Session.jsx b/app/mobile/src/session/Session.jsx
index 06effb2f..1701858f 100644
--- a/app/mobile/src/session/Session.jsx
+++ b/app/mobile/src/session/Session.jsx
@@ -148,6 +148,10 @@ export function Session() {
conversation.actions.setConversation(cardId, channelId);
setChannel(true);
};
+ const clearConversation = () => {
+ conversation.actions.clearConversation();
+ setChannel(false);
+ };
const openDetails = () => {
navParams.detailNav.openDrawer();
};
diff --git a/app/mobile/src/session/channels/Channels.jsx b/app/mobile/src/session/channels/Channels.jsx
index 08478e92..4822c2f0 100644
--- a/app/mobile/src/session/channels/Channels.jsx
+++ b/app/mobile/src/session/channels/Channels.jsx
@@ -1,9 +1,10 @@
import { useEffect } from 'react';
-import { View, Text, TextInput, TouchableOpacity } from 'react-native';
+import { View, FlatList, Text, TextInput, TouchableOpacity } from 'react-native';
import Ionicons from 'react-native-vector-icons/AntDesign';
import { styles } from './Channels.styled';
import { useChannels } from './useChannels.hook';
import { Colors } from 'constants/Colors';
+import { ChannelItem } from './channelItem/ChannelItem';
export function Channels({ navigation, openConversation }) {
@@ -40,9 +41,20 @@ export function Channels({ navigation, openConversation }) {
)}
-
- Channels
-
+ { state.channels.length == 0 && (
+
+ No Topics Found
+
+ )}
+ { state.channels.length != 0 && (
+ }
+ keyExtractor={item => (`${item.cardId}:${item.channelId}`)}
+ />
+ )}
{ !navigation && (
diff --git a/app/mobile/src/session/channels/Channels.styled.js b/app/mobile/src/session/channels/Channels.styled.js
index 42db3601..5a1d19ba 100644
--- a/app/mobile/src/session/channels/Channels.styled.js
+++ b/app/mobile/src/session/channels/Channels.styled.js
@@ -60,6 +60,8 @@ export const styles = StyleSheet.create({
},
content: {
flexGrow: 1,
+ flexShrink: 1,
+ paddingLeft: 4,
},
columnbottom: {
paddingLeft: 24,
diff --git a/app/mobile/src/session/channels/channelItem/ChannelItem.jsx b/app/mobile/src/session/channels/channelItem/ChannelItem.jsx
new file mode 100644
index 00000000..829df563
--- /dev/null
+++ b/app/mobile/src/session/channels/channelItem/ChannelItem.jsx
@@ -0,0 +1,31 @@
+import { Text, View } from 'react-native';
+import { TouchableOpacity } from 'react-native-gesture-handler';
+import { Logo } from 'utils/Logo';
+import { styles } from './ChannelItem.styled';
+import { useChannelItem } from './useChannelItem.hook';
+import Colors from 'constants/Colors';
+import Ionicons from 'react-native-vector-icons/AntDesign';
+
+export function ChannelItem({ item, openConversation }) {
+
+ return (
+ openConversation(item.cardId, item.channelId, item.revision)}>
+
+
+
+ { item.locked && !item.unlocked && (
+
+ )}
+ { item.locked && item.unlocked && (
+
+ )}
+ { item.subject }
+
+ { item.message }
+
+ { item.updated && (
+
+ )}
+
+ )
+}
diff --git a/app/mobile/src/session/channels/channelItem/ChannelItem.styled.js b/app/mobile/src/session/channels/channelItem/ChannelItem.styled.js
new file mode 100644
index 00000000..a8cbecde
--- /dev/null
+++ b/app/mobile/src/session/channels/channelItem/ChannelItem.styled.js
@@ -0,0 +1,45 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from 'constants/Colors';
+
+export const styles = StyleSheet.create({
+ container: {
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'row',
+ height: 48,
+ alignItems: 'center',
+ borderBottomWidth: 1,
+ borderColor: Colors.itemDivider,
+ paddingLeft: 16,
+ paddingRight: 16,
+ },
+ detail: {
+ paddingLeft: 12,
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ flexGrow: 1,
+ flexShrink: 1,
+ },
+ subject: {
+ display: 'flex',
+ flexDirection: 'row',
+ },
+ subjectIcon: {
+ paddingRight: 4,
+ },
+ subjectText: {
+ color: Colors.text,
+ fontSize: 14,
+ },
+ message: {
+ color: Colors.disabled,
+ fontSize: 12,
+ },
+ dot: {
+ width: 8,
+ height: 8,
+ borderRadius: 4,
+ backgroundColor: Colors.background,
+ }
+})
diff --git a/app/mobile/src/session/channels/useChannels.hook.js b/app/mobile/src/session/channels/useChannels.hook.js
index f66d6ec3..318b03e5 100644
--- a/app/mobile/src/session/channels/useChannels.hook.js
+++ b/app/mobile/src/session/channels/useChannels.hook.js
@@ -1,16 +1,163 @@
-import { useState } from 'react';
+import { useState, useEffect, useContext } from 'react';
+import { ChannelContext } from 'context/ChannelContext';
+import { CardContext } from 'context/CardContext';
+import { AccountContext } from 'context/AccountContext';
+import { AppContext } from 'context/AppContext';
+import { ProfileContext } from 'context/ProfileContext';
+import { getChannelSeals, isUnsealed } from 'context/sealUtil';
+import { getCardByGuid } from 'context/cardUtil';
export function useChannels() {
const [state, setState] = useState({
filter: null,
+ channels: [],
});
+ const channel = useContext(ChannelContext);
+ const card = useContext(CardContext);
+ const account = useContext(AccountContext);
+ const profile = useContext(ProfileContext);
+ const app = useContext(AppContext);
+
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
+ const setChannelItem = (loginTimestamp, cardId, channelId, item) => {
+ const timestamp = item.summary.lastTopic.created;
+ const { readRevision, topicRevision } = item;
+
+ // extract or decrypt subject
+ let locked;
+ let unlocked;
+ let message;
+ let subject;
+ if (item.detail.dataType === 'sealed') {
+ locked = true;
+ const seals = getChannelSeals(item.detail.data);
+ if (isUnsealed(seals, account.state.sealKey)) {
+ unlocked = true;
+ if (item.detail.unsealedDetail) {
+ subject = item.detail.unsealedDetail.subject;
+ }
+ else {
+ // decrypt detail
+ }
+ if (item.summary.lastTopic.dataType === 'sealedtopic') {
+ if (item.summary.unsealedSummary) {
+ message = item.detail.unsealedSummary.message;
+ }
+ else {
+ // decrypt message
+ }
+ }
+ }
+ }
+ if (item.detail.dataType === 'superbasic') {
+ locked = false;
+ unlocked = false;
+ try {
+ subject = JSON.parse(item.detail.data).subject;
+ }
+ catch(err) {
+ console.log(err);
+ }
+ if (item.summary.lastTopic.dataType === 'superbasictopic') {
+ try {
+ message = JSON.parse(item.summary.lastTopic.data).text;
+ }
+ catch(err) {
+ console.log(err);
+ }
+ }
+ }
+
+ const contacts = [];
+ if (cardId) {
+ contacts.push(cardId);
+ }
+ item.detail.members.forEach(guid => {
+ if (guid !== profile.state.identity.guid) {
+ contacts.push(getCardByGuid(card.state.cards, guid)?.cardId);
+ }
+ })
+
+ if (!subject) {
+ if (contacts.length === 0) {
+ subject = 'Notes';
+ }
+ else {
+ const names = [];
+ contacts.forEach(id => {
+ const contact = card.state.cards.get(id);
+ if (contact?.card.profile?.name) {
+ names.push(contact.card.profile.name);
+ }
+ else {
+ names.push(contact?.card.profile?.handle);
+ }
+ });
+ subject = names.join(', ');
+ }
+ }
+
+ if (contacts.length === 0) {
+ logo = 'solution';
+ }
+ else if (contacts.length === 1) {
+ const contact = card.state.cards.get(contacts[0]);
+ if (contact?.card?.profile?.imageSet) {
+ logo = card.actions.getCardImageUrl(contacts[0])
+ }
+ else {
+ logo = 'avatar';
+ }
+ }
+ else {
+ logo = 'appstore';
+ }
+
+ const updated = (loginTimestamp < timestamp) && (readRevision < topicRevision);
+
+ return { cardId, channelId, subject, message, logo, updated, locked, unlocked };
+ }
+
+ useEffect(() => {
+ const { loginTimestamp } = app.state;
+ const channels = [];
+ channel.state.channels.forEach((item, channelId) => {
+ channels.push(setChannelItem(loginTimestamp, null, channelId, item));
+ });
+ card.state.cards.forEach((cardItem, cardId) => {
+ cardItem.channels.forEach((channelItem, channelId) => {
+ channels.push(setChannelItem(loginTimestamp, cardId, channelId, channelItem));
+ });
+ });
+ const filtered = channels.filter(item => {
+ if (!state.filter) {
+ return true;
+ }
+ const filterCase = state.filter.toUpperCase();
+ const subjectCase = item.subject.toUpperCase();
+ return subjectCase.includes(filterCase);
+ });
+ const sorted = filtered.sort((a, b) => {
+ const aCreated = a?.timestamp;
+ const bCreated = b?.timestamp;
+ if (aCreated === bCreated) {
+ return 0;
+ }
+ if (!aCreated || aCreated < bCreated) {
+ return 1;
+ }
+ return -1;
+ });
+ updateState({ channels: sorted });
+ }, [app.state, card.state, channel.state, state.filter]);
+
const actions = {
- setFilter: () => {
+ setFilter: (filter) => {
+ updateState({ filter });
},
};