diff --git a/app/mobile/App.js b/app/mobile/App.js index 08213db9..caabfdaa 100644 --- a/app/mobile/App.js +++ b/app/mobile/App.js @@ -39,7 +39,6 @@ export default function App() { const clearSharing = () => { setSharing(null); - ReceiveSharingIntent.clearReceivedFiles(); }; return ( diff --git a/app/mobile/src/constants/Colors.js b/app/mobile/src/constants/Colors.js index ff256256..eb4a3f83 100644 --- a/app/mobile/src/constants/Colors.js +++ b/app/mobile/src/constants/Colors.js @@ -16,6 +16,7 @@ export const Colors = { disabled: '#aaaaaa', text: '#444444', link: '#0077CC', + lightText: '#686868', active: '#222222', idle: '#707070', diff --git a/app/mobile/src/session/Session.jsx b/app/mobile/src/session/Session.jsx index c656ed26..f0065fc7 100644 --- a/app/mobile/src/session/Session.jsx +++ b/app/mobile/src/session/Session.jsx @@ -39,7 +39,7 @@ const CardDrawer = createDrawerNavigator(); const RegistryDrawer = createDrawerNavigator(); const Tab = createBottomTabNavigator(); -function ConversationStackScreen({ dmChannel }) { +function ConversationStackScreen({ dmChannel, shareChannel, shareIntent, setShareIntent }) { const stackParams = { headerStyle: { backgroundColor: Colors.titleBackground }, headerBackTitleVisible: false }; const screenParams = { headerShown: true, headerTintColor: Colors.primary }; @@ -76,11 +76,11 @@ function ConversationStackScreen({ dmChannel }) { (screenParams)} > - {(props) => openConversation(props.navigation, cardId, channelId)} />} + {(props) => openConversation(props.navigation, cardId, channelId)} />} - {(props) => openDetails(props.navigation)} closeConversation={(pop) => closeConversation(props.navigation, pop)} /> } + {(props) => openDetails(props.navigation)} closeConversation={(pop) => closeConversation(props.navigation, pop)} shareIntent={shareIntent} setShareIntent={setShareIntent} /> } ( @@ -216,13 +216,13 @@ function HomeScreen({ navParams }) { - + { channel && ( - + )} { !channel && ( @@ -330,17 +330,18 @@ export function Session({ sharing, clearSharing }) { setDmChannel({ id }); }; - const setShare = async (cardId, channelId) => { - console.log("SET SHARE CHANNEL"); + const [shareIntent, setShareIntent] = useState(null); + const [shareChannel, setShareChannel] = useState(null); + const setShare = async ({ cardId, channelId }) => { + setShareIntent(sharing); + setShareChannel({ cardId, channelId }); clearSharing(); } const clearShare = async () => { - console.log("CLEAR SHARE CHANNEL"); clearSharing(); } useEffect(() => { - console.log("COMPARE", sharing, intent); if (sharing != intent && sharing != null) { navigate('/'); } @@ -408,7 +409,7 @@ export function Session({ sharing, clearSharing }) { )}> - {(props) => } + {(props) => } )} @@ -432,7 +433,7 @@ export function Session({ sharing, clearSharing }) { tabBarActiveTintColor: Colors.white, tabBarInactiveTintColor: Colors.disabled, })}> - } /> + } /> } /> @@ -464,7 +465,7 @@ export function Session({ sharing, clearSharing }) { diff --git a/app/mobile/src/session/channels/Channels.jsx b/app/mobile/src/session/channels/Channels.jsx index e8948c66..821bcf49 100644 --- a/app/mobile/src/session/channels/Channels.jsx +++ b/app/mobile/src/session/channels/Channels.jsx @@ -7,7 +7,7 @@ import { Colors } from 'constants/Colors'; import { ChannelItem } from './channelItem/ChannelItem'; import { AddMember } from './addMember/AddMember'; -export function Channels({ cardId, channelId, navigation, openConversation, dmChannel }) { +export function Channels({ cardId, channelId, navigation, openConversation, dmChannel, shareChannel }) { const { state, actions } = useChannels(); @@ -32,6 +32,12 @@ export function Channels({ cardId, channelId, navigation, openConversation, dmCh } }, [dmChannel]); + useEffect(() => { + if (shareChannel) { + openConversation(shareChannel.cardId, shareChannel.channelId); + } + }, [shareChannel]); + useEffect(() => { if (navigation) { navigation.setOptions({ diff --git a/app/mobile/src/session/channels/channelItem/ChannelItem.jsx b/app/mobile/src/session/channels/channelItem/ChannelItem.jsx index a0b28d41..2e7271c4 100644 --- a/app/mobile/src/session/channels/channelItem/ChannelItem.jsx +++ b/app/mobile/src/session/channels/channelItem/ChannelItem.jsx @@ -2,7 +2,6 @@ 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/MaterialCommunityIcons'; diff --git a/app/mobile/src/session/conversation/Conversation.jsx b/app/mobile/src/session/conversation/Conversation.jsx index 38cb26bf..f1b05baf 100644 --- a/app/mobile/src/session/conversation/Conversation.jsx +++ b/app/mobile/src/session/conversation/Conversation.jsx @@ -9,7 +9,7 @@ import { Logo } from 'utils/Logo'; import { AddTopic } from './addTopic/AddTopic'; import { TopicItem } from './topicItem/TopicItem'; -export function Conversation({ navigation, cardId, channelId, closeConversation, openDetails }) { +export function Conversation({ navigation, cardId, channelId, closeConversation, openDetails, shareIntent, setShareIntent }) { const { state, actions } = useConversation(); @@ -113,7 +113,7 @@ export function Conversation({ navigation, cardId, channelId, closeConversation, )} - + { + if (shareIntent) { + Alert.alert('SHARING', JSON.stringify(shareIntent)); + shareIntent.forEach(share => { + if (share.text) { + actions.setMessage(share.text); + } + if (share.weblink) { + actions.setMessage(share.weblink); + } + const mimeType = share.mimeType?.toLowerCase(); + if (mimeType === '.jpg' || mimeType === '.png') { + actions.addImage(share.filePath) + } + if (mimeType === '.mp4') { + actions.addVideo(share.filePath) + } + if (mimeType === '.mp3') { + actions.addAudio(share.filePath) + } + }); + setShareIntent(null); + } + }, [shareIntent]); + const addImage = async () => { try { const full = await ImagePicker.openPicker({ mediaType: 'photo' }); diff --git a/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js b/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js index da4ec842..23c2ae49 100644 --- a/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js +++ b/app/mobile/src/session/conversation/topicItem/useTopicItem.hook.js @@ -289,9 +289,9 @@ export function useTopicItem(item, hosting, remove, contentKey) { } } - await Share.open({ urls: files, message: data.text, title: 'Databag', subject: 'Shared from Databag' }) + await Share.open({ urls: files, message: files.length > 0 ? null : data.text, title: 'Databag', subject: 'Shared from Databag' }) while (unlink.length > 0) { - const file = fs.unlink.shift(); + const file = unlink.shift(); await fs.unlink(file); } } diff --git a/app/mobile/src/session/sharing/Sharing.jsx b/app/mobile/src/session/sharing/Sharing.jsx index d95ac8da..64e3a1ea 100644 --- a/app/mobile/src/session/sharing/Sharing.jsx +++ b/app/mobile/src/session/sharing/Sharing.jsx @@ -1,15 +1,42 @@ -import { Text, View } from 'react-native'; +import { useState } from 'react'; +import { TouchableOpacity, Text, View, FlatList } from 'react-native'; import { useSharing } from './useSharing.hook'; import { styles } from './Sharing.styled'; +import { SharingItem } from './sharingItem/SharingItem'; -export function Sharing({ setShare, clearShare }) { +export function Sharing({ select, cancel }) { + const [selection, setSelection] = useState(null); const { state, actions } = useSharing(); return ( - SHARING + + Select Topic for Sharing + + } + keyExtractor={item => (`${item.cardId}:${item.channelId}`)} + /> + + { !selection && ( + + Select + + )} + { selection && ( + select(selection)}> + Select + + )} + + Cancel + + ) diff --git a/app/mobile/src/session/sharing/Sharing.styled.js b/app/mobile/src/session/sharing/Sharing.styled.js index 80db8078..2e6621cf 100644 --- a/app/mobile/src/session/sharing/Sharing.styled.js +++ b/app/mobile/src/session/sharing/Sharing.styled.js @@ -12,10 +12,81 @@ export const styles = StyleSheet.create({ }, sharingFrame: { backgroundColor: Colors.formBackground, - padding: 8, + width: '80%', + height: '80%', + maxWidth: 400, + maxHeight: 600, borderRadius: 4, display: 'flex', alignItems: 'center', }, + header: { + padding: 8, + borderBottomWidth: 1, + borderColor: Colors.divider, + width: '100%', + alignItems: 'center', + }, + headerText: { + fontSize: 18, + }, + content: { + width: '100%', + flexGrow: 1, + flexShrink: 1, + }, + controls: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'flex-end', + width: '100%', + padding: 4, + borderTopWidth: 1, + borderColor: Colors.divider, + }, + select: { + borderRadius: 3, + borderWidth: 1, + borderColor: Colors.primary, + backgroundColor: Colors.primary, + paddingLeft: 16, + paddingRight: 16, + paddingTop: 4, + paddingBottom: 4, + margin: 8, + }, + selectText: { + fontSize: 16, + color: Colors.white, + }, + disabled: { + borderRadius: 3, + borderWidth: 1, + borderColor: Colors.grey, + paddingLeft: 16, + paddingRight: 16, + paddingTop: 4, + paddingBottom: 4, + margin: 8, + }, + disabledText: { + fontSize: 16, + color: Colors.grey, + }, + cancel: { + borderRadius: 3, + borderWidth: 1, + borderColor: Colors.text, + paddingLeft: 16, + paddingRight: 16, + paddingTop: 4, + paddingBottom: 4, + margin: 8, + }, + cancelText: { + fontSize: 16, + color: Colors.text, + }, + }); diff --git a/app/mobile/src/session/sharing/useSharing.hook.js b/app/mobile/src/session/sharing/useSharing.hook.js index 28f4cbeb..14f19127 100644 --- a/app/mobile/src/session/sharing/useSharing.hook.js +++ b/app/mobile/src/session/sharing/useSharing.hook.js @@ -1,16 +1,125 @@ -import { useState } from 'react'; +import { useState, useRef, useEffect, useContext } from 'react'; +import { ChannelContext } from 'context/ChannelContext'; +import { CardContext } from 'context/CardContext'; +import { AccountContext } from 'context/AccountContext'; +import { ProfileContext } from 'context/ProfileContext'; +import { getChannelSeals, isUnsealed, getContentKey, encryptChannelSubject, decryptChannelSubject, decryptTopicSubject } from 'context/sealUtil'; +import { getCardByGuid } from 'context/cardUtil'; +import { getChannelSubjectLogo } from 'context/channelUtil'; export function useSharing() { const [state, setState] = useState({ + channels: [], }); + const channel = useContext(ChannelContext); + const card = useContext(CardContext); + const account = useContext(AccountContext); + const profile = useContext(ProfileContext); + + const resync = useRef(false); + const syncing = useRef(false); + const updateState = (value) => { setState((s) => ({ ...s, ...value })); } + const setChannelItem = async (cardId, channelId, item) => { + const timestamp = item.summary.lastTopic.created; + + // decrypt subject and message + let locked = false; + let unlocked = false; + if (item.detail.dataType === 'sealed') { + locked = true; + const seals = getChannelSeals(item.detail.data); + if (isUnsealed(seals, account.state.sealKey)) { + unlocked = true; + } + } + + let message; + if (item?.detail?.dataType === 'sealed') { + if (typeof item?.unsealedSummary?.message?.text === 'string') { + message = item.unsealedSummary.message.text; + } + } + if (item.detail.dataType === 'superbasic') { + if (item.summary.lastTopic.dataType === 'superbasictopic') { + try { + const data = JSON.parse(item.summary.lastTopic.data); + if (typeof data.text === 'string') { + message = data.text; + } + } + catch(err) { + console.log(err); + } + } + } + + const profileGuid = profile.state?.identity?.guid; + const { logo, subject } = getChannelSubjectLogo(cardId, profileGuid, item, card.state.cards, card.actions.getCardImageUrl); + + return { cardId, channelId, subject, message, logo, timestamp, locked, unlocked }; + } + + useEffect(() => { + syncChannels(); + }, [account.state, card.state, channel.state]); + + const syncChannels = async () => { + if (syncing.current) { + resync.current = true; + } + else { + syncing.current = true; + + const items = []; + channel.state.channels.forEach((item, channelId) => { + items.push({ channelId, channelItem: item }); + }); + card.state.cards.forEach((cardItem, cardId) => { + cardItem.channels.forEach((channelItem, channelId) => { + items.push({ cardId, channelId, channelItem }); + }); + }); + const channels = []; + for (let i = 0; i < items.length; i++) { + const { cardId, channelId, channelItem } = items[i]; + channels.push(await setChannelItem(cardId, channelId, channelItem)); + } + const filtered = channels.filter(item => { + if (!item.locked || item.unlocked) { + return true; + } + return false; + }); + 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 }); + + syncing.current = false; + if(resync.current) { + resync.current = false; + await syncChannels(); + } + } + }; + const actions = { }; return { state, actions }; } +