mirror of
https://github.com/balzack/databag.git
synced 2025-05-04 23:45:21 +00:00
building mobile message thread
This commit is contained in:
parent
d7991ab0f0
commit
ebc3855df3
@ -1633,7 +1633,7 @@ SPEC CHECKSUMS:
|
|||||||
RNVectorIcons: 845eda5c7819bd29699cafd0fc98c9d4afe28c96
|
RNVectorIcons: 845eda5c7819bd29699cafd0fc98c9d4afe28c96
|
||||||
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
|
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
|
||||||
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
|
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
|
||||||
Yoga: 88480008ccacea6301ff7bf58726e27a72931c8d
|
Yoga: 04f1db30bb810187397fa4c37dd1868a27af229c
|
||||||
|
|
||||||
PODFILE CHECKSUM: 8461018d8deceb200962c829584af7c2eb345c80
|
PODFILE CHECKSUM: 8461018d8deceb200962c829584af7c2eb345c80
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ export function Content({openConversation}: {openConversation: ()=>void}) {
|
|||||||
const addTopic = async () => {
|
const addTopic = async () => {
|
||||||
setAdding(true);
|
setAdding(true);
|
||||||
try {
|
try {
|
||||||
await actions.addTopic(
|
const id = await actions.addTopic(
|
||||||
sealedTopic,
|
sealedTopic,
|
||||||
subjectTopic,
|
subjectTopic,
|
||||||
members.filter(id => Boolean(cards.find(card => card.cardId === id))),
|
members.filter(id => Boolean(cards.find(card => card.cardId === id))),
|
||||||
@ -40,6 +40,8 @@ export function Content({openConversation}: {openConversation: ()=>void}) {
|
|||||||
setSubjectTopic('');
|
setSubjectTopic('');
|
||||||
setMembers([]);
|
setMembers([]);
|
||||||
setSealedTopic(false);
|
setSealedTopic(false);
|
||||||
|
actions.setFocus(null, id);
|
||||||
|
openConversation();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
setAdd(false);
|
setAdd(false);
|
||||||
|
@ -230,11 +230,13 @@ export function useContent() {
|
|||||||
app.actions.setFocus(cardId, channelId);
|
app.actions.setFocus(cardId, channelId);
|
||||||
},
|
},
|
||||||
addTopic: async (sealed: boolean, subject: string, contacts: string[]) => {
|
addTopic: async (sealed: boolean, subject: string, contacts: string[]) => {
|
||||||
const content = app.state.session.getContent();
|
const content = app.state.session.getContent()
|
||||||
if (sealed) {
|
if (sealed) {
|
||||||
await content.addChannel(true, 'sealed', {subject}, contacts);
|
const topic = await content.addChannel(true, 'sealed', { subject }, contacts)
|
||||||
|
return topic.id;
|
||||||
} else {
|
} else {
|
||||||
await content.addChannel(false, 'superbasic', {subject}, contacts);
|
const topic = await content.addChannel(false, 'superbasic', { subject }, contacts)
|
||||||
|
return topic.id;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@ import {DatabagSDK, Session, Focus} from 'databag-client-sdk';
|
|||||||
import {SessionStore} from '../SessionStore';
|
import {SessionStore} from '../SessionStore';
|
||||||
import {NativeCrypto} from '../NativeCrypto';
|
import {NativeCrypto} from '../NativeCrypto';
|
||||||
import {LocalStore} from '../LocalStore';
|
import {LocalStore} from '../LocalStore';
|
||||||
const DATABAG_DB = 'db_v231.db';
|
const DATABAG_DB = 'db_v234.db';
|
||||||
const SETTINGS_DB = 'ls_v001.db';
|
const SETTINGS_DB = 'ls_v001.db';
|
||||||
|
|
||||||
const databag = new DatabagSDK(
|
const databag = new DatabagSDK(
|
||||||
|
@ -2,4 +2,46 @@ import {StyleSheet} from 'react-native';
|
|||||||
import {Colors} from '../constants/Colors';
|
import {Colors} from '../constants/Colors';
|
||||||
|
|
||||||
export const styles = StyleSheet.create({
|
export const styles = StyleSheet.create({
|
||||||
|
conversation: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
paddingBottom: 64,
|
||||||
|
},
|
||||||
|
thread: {
|
||||||
|
width: '100%',
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
add: {
|
||||||
|
height: 72,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
back: {
|
||||||
|
flexShrink: 0,
|
||||||
|
marginRight: 0,
|
||||||
|
marginLeft: 0,
|
||||||
|
marginTop: 0,
|
||||||
|
marginBottom: 0,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
iconSpace: {
|
||||||
|
width: '10%',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
width: '80%',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: 24,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,11 +1,121 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
import {TouchableOpacity} from 'react-native';
|
import {SafeAreaView, View, FlatList, TouchableOpacity} from 'react-native';
|
||||||
import {Text} from 'react-native-paper';
|
|
||||||
import {styles} from './Conversation.styled';
|
import {styles} from './Conversation.styled';
|
||||||
import {useConversation} from './useConversation.hook';
|
import {useConversation} from './useConversation.hook';
|
||||||
|
import {Message} from '../message/Message';
|
||||||
|
import {Icon, Text, IconButton, Divider} from 'react-native-paper';
|
||||||
|
|
||||||
|
const SCROLL_THRESHOLD = 16;
|
||||||
|
|
||||||
|
export type MediaAsset = {
|
||||||
|
encrypted?: { type: string, thumb: string, label: string, extension: string, parts: { blockIv: string, partId: string }[] },
|
||||||
|
image?: { thumb: string, full: string },
|
||||||
|
audio?: { label: string, full: string },
|
||||||
|
video?: { thumb: string, lq: string, hd: string },
|
||||||
|
binary?: { label: string, extension: string, data: string }
|
||||||
|
}
|
||||||
|
|
||||||
export function Conversation({close}: {close: ()=>void}) {
|
export function Conversation({close}: {close: ()=>void}) {
|
||||||
const { state, actions } = useConversation();
|
const { state, actions } = useConversation();
|
||||||
|
const [ more, setMore ] = useState(false);
|
||||||
|
const thread = useRef();
|
||||||
|
|
||||||
return <TouchableOpacity onPress={close}><Text>CONVERSATION</Text></TouchableOpacity>;
|
const scrolled = useRef(false);
|
||||||
|
const contentHeight = useRef(0);
|
||||||
|
const contentLead = useRef(null);
|
||||||
|
const scrollOffset = useRef(0);
|
||||||
|
|
||||||
|
const loadMore = async () => {
|
||||||
|
if (!more) {
|
||||||
|
setMore(true);
|
||||||
|
await actions.more();
|
||||||
|
setMore(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onContent = (width, height) => {
|
||||||
|
const currentLead = state.topics.length > 0 ? state.topics[0].topicId : null;
|
||||||
|
if (scrolled.current) {
|
||||||
|
if (currentLead !== contentLead.current) {
|
||||||
|
const offset = scrollOffset.current + (height - contentHeight.current);
|
||||||
|
const animated = false;
|
||||||
|
thread.current.scrollToOffset({offset, animated});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contentLead.current = currentLead;
|
||||||
|
contentHeight.current = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onScroll = (ev) => {
|
||||||
|
const { contentOffset } = ev.nativeEvent;
|
||||||
|
const offset = contentOffset.y;
|
||||||
|
if (offset > scrollOffset.current) {
|
||||||
|
if (offset > SCROLL_THRESHOLD) {
|
||||||
|
scrolled.current = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (offset < SCROLL_THRESHOLD) {
|
||||||
|
scrolled.current = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scrollOffset.current = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.conversation}>
|
||||||
|
<SafeAreaView style={styles.header}>
|
||||||
|
{close && (
|
||||||
|
<View style={styles.iconSpace}>
|
||||||
|
<IconButton style={styles.back} compact="true" mode="contained" icon="arrow-left" size={28} onPress={close} />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<View style={styles.title}>
|
||||||
|
{ state.detailSet && state.subject && (
|
||||||
|
<Text adjustsFontSizeToFit={true} numberOfLines={1} style={styles.label}>{ state.subject }</Text>
|
||||||
|
)}
|
||||||
|
{ state.detailSet && state.host && !state.subject && state.subjectNames.length == 0 && (
|
||||||
|
<Text adjustsFontSizeToFit={true} numberOfLines={1} styles={styles.label}>{ state.strings.notes }</Text>
|
||||||
|
)}
|
||||||
|
{ state.detailSet && !state.subject && state.subjectNames.length > 0 && (
|
||||||
|
<Text adjustsFontSizeToFit={true} numberOfLines={1} style={styles.label}>{ state.subjectNames.join(', ') }</Text>
|
||||||
|
)}
|
||||||
|
{ state.detailSet && !state.subject && state.unknownContacts > 0 && (
|
||||||
|
<Text adjustsFontSizeToFit={true} numberOfLines={1} style={styles.unknown}>{ `, ${state.strings.unknownContact} (${state.unknownContacts})` }</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
{close && <View style={styles.iconSpace} />}
|
||||||
|
</SafeAreaView>
|
||||||
|
<Divider style={styles.border} bold={true} />
|
||||||
|
<FlatList
|
||||||
|
inverted
|
||||||
|
ref={thread}
|
||||||
|
onScroll={onScroll}
|
||||||
|
style={styles.messages}
|
||||||
|
data={state.topics}
|
||||||
|
initialNumToRender={32}
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
onContentSizeChange={onContent}
|
||||||
|
onEndReached={loadMore}
|
||||||
|
onEndReachedThreshold={0}
|
||||||
|
contentContainerStyle={styles.messages}
|
||||||
|
renderItem={({item}) => {
|
||||||
|
const { host } = state;
|
||||||
|
const card = state.cards.get(item.guid) || null;
|
||||||
|
const profile = state.profile?.guid === item.guid ? state.profile : null;
|
||||||
|
return (
|
||||||
|
<Message
|
||||||
|
topic={item}
|
||||||
|
card={card}
|
||||||
|
profile={profile}
|
||||||
|
host={host}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
keyExtractor={topic => (topic.topicId)}
|
||||||
|
/>
|
||||||
|
<TouchableOpacity style={styles.add} onPress={() => thread.current.scrollToEnd()}>
|
||||||
|
<Text>ADD</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ export function useConversation() {
|
|||||||
focus: null as Focus | null,
|
focus: null as Focus | null,
|
||||||
layout: null,
|
layout: null,
|
||||||
topics: [] as Topic[],
|
topics: [] as Topic[],
|
||||||
|
topicCount: 0,
|
||||||
loaded: false,
|
loaded: false,
|
||||||
loadingMore: false,
|
loadingMore: false,
|
||||||
profile: null as Profile | null,
|
profile: null as Profile | null,
|
||||||
@ -90,20 +91,18 @@ export function useConversation() {
|
|||||||
const { contact, identity } = app.state.session || { };
|
const { contact, identity } = app.state.session || { };
|
||||||
if (focus && contact && identity) {
|
if (focus && contact && identity) {
|
||||||
const setTopics = (topics: Topic[]) => {
|
const setTopics = (topics: Topic[]) => {
|
||||||
console.log(">>>", topics);
|
|
||||||
|
|
||||||
if (topics) {
|
if (topics) {
|
||||||
const filtered = topics.filter(topic => !topic.blocked);
|
const filtered = topics.filter(topic => !topic.blocked);
|
||||||
const sorted = filtered.sort((a, b) => {
|
const sorted = filtered.sort((a, b) => {
|
||||||
if (a.created < b.created) {
|
if (a.created < b.created) {
|
||||||
return -1;
|
|
||||||
} else if (a.created > b.created) {
|
|
||||||
return 1;
|
return 1;
|
||||||
|
} else if (a.created > b.created) {
|
||||||
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
updateState({ topics: sorted, loaded: true });
|
updateState({ topics: sorted, topicCount: topics.length, loaded: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const setCards = (cards: Card[]) => {
|
const setCards = (cards: Card[]) => {
|
||||||
|
192
app/client/mobile/src/message/Message.module.css
Normal file
192
app/client/mobile/src/message/Message.module.css
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
.topic {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.media {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.goleft {
|
||||||
|
position: absolute;
|
||||||
|
left: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goright {
|
||||||
|
position: absolute;
|
||||||
|
right: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.assets {
|
||||||
|
width: 100%;
|
||||||
|
height: 128px;
|
||||||
|
padding-left: 72px;
|
||||||
|
padding-right: 32px;
|
||||||
|
margin-top: 8px;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
min-width: 0;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
overflow: auto;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assets::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editing {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
padding: 4px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
flex-grow: 1;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.failed {
|
||||||
|
margin-left: 72px;
|
||||||
|
margin-right: 32px;
|
||||||
|
margin-top: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--mantine-color-red-9);
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.incomplete {
|
||||||
|
margin-left: 72px;
|
||||||
|
margin-right: 32px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
width: calc(100% - 32px);
|
||||||
|
margin-left: 16px;
|
||||||
|
margin-right: 16px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid var(--mantine-color-text-8);
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.padding {
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
word-wrap:break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.locked {
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--mantine-color-text-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.unconfirmed {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
line-height: 16px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
gap: 16px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.options {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.surface {
|
||||||
|
display: flex;
|
||||||
|
background-color: var(--mantine-color-surface-4);
|
||||||
|
gap: 10px;
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
color: var(--mantine-color-dbgreen-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.careful {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
color: var(--mantine-color-red-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.timestamp {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unknown {
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--mantine-color-text-7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
app/client/mobile/src/message/Message.styled.ts
Normal file
5
app/client/mobile/src/message/Message.styled.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import {StyleSheet} from 'react-native';
|
||||||
|
import {Colors} from '../constants/Colors';
|
||||||
|
|
||||||
|
export const styles = StyleSheet.create({
|
||||||
|
});
|
16
app/client/mobile/src/message/Message.tsx
Normal file
16
app/client/mobile/src/message/Message.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { useRef, useEffect, useState, useCallback } from 'react';
|
||||||
|
import { avatar } from '../constants/Icons'
|
||||||
|
import {Icon, Text, IconButton, Divider} from 'react-native-paper';
|
||||||
|
import { Topic, Card, Profile } from 'databag-client-sdk';
|
||||||
|
import classes from './Message.styles.ts'
|
||||||
|
import { ImageAsset } from './imageAsset/ImageAsset';
|
||||||
|
import { AudioAsset } from './audioAsset/AudioAsset';
|
||||||
|
import { VideoAsset } from './videoAsset/VideoAsset';
|
||||||
|
import { BinaryAsset } from './binaryAsset/BinaryAsset';
|
||||||
|
import { useMessage } from './useMessage.hook';
|
||||||
|
|
||||||
|
export function Message({ topic, card, profile, host }: { topic: Topic, card: Card | null, profile: Profile | null, host: boolean }) {
|
||||||
|
const { state, actions } = useMessage();
|
||||||
|
|
||||||
|
return (<Text style={{ height: 32 }}>{ JSON.stringify(topic.data) }</Text>)
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import {StyleSheet} from 'react-native';
|
||||||
|
import {Colors} from '../constants/Colors';
|
||||||
|
|
||||||
|
export const styles = StyleSheet.create({
|
||||||
|
});
|
11
app/client/mobile/src/message/audioAsset/AudioAsset.tsx
Normal file
11
app/client/mobile/src/message/audioAsset/AudioAsset.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Text } from 'react-native'
|
||||||
|
import { useAudioAsset } from './useAudioAsset.hook';
|
||||||
|
import { MediaAsset } from '../../conversation/Conversation';
|
||||||
|
|
||||||
|
export function AudioAsset({ topicId, asset }: { topicId: string, asset: MediaAsset }) {
|
||||||
|
const { state, actions } = useAudioAsset(topicId, asset);
|
||||||
|
|
||||||
|
return (<Text>AUDIO</Text>);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
|||||||
|
import { useState, useContext, useEffect } from 'react'
|
||||||
|
import { AppContext } from '../../context/AppContext'
|
||||||
|
import { Focus } from 'databag-client-sdk'
|
||||||
|
import { ContextType } from '../../context/ContextType'
|
||||||
|
import { MediaAsset } from '../../conversation/Conversation';
|
||||||
|
|
||||||
|
export function useAudioAsset(topicId: string, asset: MediaAsset) {
|
||||||
|
const app = useContext(AppContext) as ContextType
|
||||||
|
const [state, setState] = useState({
|
||||||
|
dataUrl: null,
|
||||||
|
loading: false,
|
||||||
|
loadPercent: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const updateState = (value: any) => {
|
||||||
|
setState((s) => ({ ...s, ...value }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
unloadAudio: () => {
|
||||||
|
updateState({ dataUrl: null });
|
||||||
|
},
|
||||||
|
loadAudio: async () => {
|
||||||
|
const { focus } = app.state;
|
||||||
|
const assetId = asset.audio ? asset.audio.full : asset.encrypted ? asset.encrypted.parts : null;
|
||||||
|
if (focus && assetId != null && !state.loading) {
|
||||||
|
updateState({ loading: true, loadPercent: 0 });
|
||||||
|
try {
|
||||||
|
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }) });
|
||||||
|
updateState({ dataUrl, loading: false });
|
||||||
|
} catch (err) {
|
||||||
|
updateState({ loading: false });
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { state, actions }
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import {StyleSheet} from 'react-native';
|
||||||
|
import {Colors} from '../constants/Colors';
|
||||||
|
|
||||||
|
export const styles = StyleSheet.create({
|
||||||
|
});
|
11
app/client/mobile/src/message/binaryAsset/BinaryAsset.tsx
Normal file
11
app/client/mobile/src/message/binaryAsset/BinaryAsset.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Text } from 'react-native'
|
||||||
|
import { useBinaryAsset } from './useBinaryAsset.hook';
|
||||||
|
import { MediaAsset } from '../../conversation/Conversation';
|
||||||
|
|
||||||
|
export function BinaryAsset({ topicId, asset }: { topicId: string, asset: MediaAsset }) {
|
||||||
|
const { state, actions } = useBinaryAsset(topicId, asset);
|
||||||
|
|
||||||
|
return (<Text>BINARY</Text>);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
|||||||
|
import { useState, useContext, useEffect } from 'react'
|
||||||
|
import { AppContext } from '../../context/AppContext'
|
||||||
|
import { Focus } from 'databag-client-sdk'
|
||||||
|
import { ContextType } from '../../context/ContextType'
|
||||||
|
import { MediaAsset } from '../../conversation/Conversation';
|
||||||
|
|
||||||
|
export function useBinaryAsset(topicId: string, asset: MediaAsset) {
|
||||||
|
const app = useContext(AppContext) as ContextType
|
||||||
|
const [state, setState] = useState({
|
||||||
|
dataUrl: '',
|
||||||
|
loading: false,
|
||||||
|
loadPercent: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const updateState = (value: any) => {
|
||||||
|
setState((s) => ({ ...s, ...value }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
unloadBinary: () => {
|
||||||
|
updateState({ dataUrl: null });
|
||||||
|
},
|
||||||
|
loadBinary: async () => {
|
||||||
|
const { focus } = app.state;
|
||||||
|
const assetId = asset.binary ? asset.binary.data : asset.encrypted ? asset.encrypted.parts : null;
|
||||||
|
if (focus && assetId != null && !state.loading) {
|
||||||
|
updateState({ loading: true, loadPercent: 0 });
|
||||||
|
try {
|
||||||
|
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }) });
|
||||||
|
updateState({ dataUrl, loading: false });
|
||||||
|
} catch (err) {
|
||||||
|
updateState({ loading: false });
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { state, actions }
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import {StyleSheet} from 'react-native';
|
||||||
|
import {Colors} from '../constants/Colors';
|
||||||
|
|
||||||
|
export const styles = StyleSheet.create({
|
||||||
|
});
|
11
app/client/mobile/src/message/imageAsset/ImageAsset.tsx
Normal file
11
app/client/mobile/src/message/imageAsset/ImageAsset.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Text } from 'react-native'
|
||||||
|
import { useImageAsset } from './useImageAsset.hook';
|
||||||
|
import { MediaAsset } from '../../conversation/Conversation';
|
||||||
|
|
||||||
|
export function ImageAsset({ topicId, asset }: { topicId: string, asset: MediaAsset }) {
|
||||||
|
const { state, actions } = useImageAsset(topicId, asset);
|
||||||
|
|
||||||
|
return (<Text>IMAGE</Text>);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
|||||||
|
import { useState, useContext, useEffect } from 'react'
|
||||||
|
import { AppContext } from '../../context/AppContext'
|
||||||
|
import { Focus } from 'databag-client-sdk'
|
||||||
|
import { ContextType } from '../../context/ContextType'
|
||||||
|
import { MediaAsset } from '../../conversation/Conversation';
|
||||||
|
|
||||||
|
export function useImageAsset(topicId: string, asset: MediaAsset) {
|
||||||
|
const app = useContext(AppContext) as ContextType
|
||||||
|
const [state, setState] = useState({
|
||||||
|
thumbUrl: null,
|
||||||
|
dataUrl: null,
|
||||||
|
loading: false,
|
||||||
|
loadPercent: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const updateState = (value: any) => {
|
||||||
|
setState((s) => ({ ...s, ...value }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const setThumb = async () => {
|
||||||
|
const { focus } = app.state;
|
||||||
|
const assetId = asset.image ? asset.image.thumb : asset.encrypted ? asset.encrypted.thumb : null;
|
||||||
|
if (focus && assetId != null) {
|
||||||
|
try {
|
||||||
|
const thumbUrl = await focus.getTopicAssetUrl(topicId, assetId);
|
||||||
|
updateState({ thumbUrl });
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setThumb();
|
||||||
|
}, [asset]);
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
unloadImage: () => {
|
||||||
|
updateState({ dataUrl: null });
|
||||||
|
},
|
||||||
|
loadImage: async () => {
|
||||||
|
const { focus } = app.state;
|
||||||
|
const assetId = asset.image ? asset.image.full : asset.encrypted ? asset.encrypted.parts : null;
|
||||||
|
if (focus && assetId != null && !state.loading) {
|
||||||
|
updateState({ loading: true, loadPercent: 0 });
|
||||||
|
try {
|
||||||
|
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }) });
|
||||||
|
updateState({ dataUrl });
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
updateState({ loading: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { state, actions }
|
||||||
|
}
|
82
app/client/mobile/src/message/useMessage.hook.ts
Normal file
82
app/client/mobile/src/message/useMessage.hook.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { useState, useContext, useEffect } from 'react'
|
||||||
|
import { DisplayContext } from '../context/DisplayContext'
|
||||||
|
import { AppContext } from '../context/AppContext';
|
||||||
|
import { ContextType } from '../context/ContextType'
|
||||||
|
|
||||||
|
export function useMessage() {
|
||||||
|
const app = useContext(AppContext) as ContextType
|
||||||
|
const display = useContext(DisplayContext) as ContextType
|
||||||
|
const [state, setState] = useState({
|
||||||
|
strings: display.state.strings,
|
||||||
|
timeFormat: display.state.timeFormat,
|
||||||
|
dateFormat: display.state.dateFormat,
|
||||||
|
})
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const updateState = (value: any) => {
|
||||||
|
setState((s) => ({ ...s, ...value }))
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { strings, timeFormat, dateFormat } = display.state;
|
||||||
|
updateState({ strings, timeFormat, dateFormat });
|
||||||
|
}, [display.state]);
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
block: async (topicId: string) => {
|
||||||
|
const focus = app.state.focus;
|
||||||
|
if (focus) {
|
||||||
|
await focus.setBlockTopic(topicId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
flag: async (topicId: string) => {
|
||||||
|
const focus = app.state.focus;
|
||||||
|
if (focus) {
|
||||||
|
await focus.flagTopic(topicId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remove: async (topicId: string) => {
|
||||||
|
const focus = app.state.focus;
|
||||||
|
if (focus) {
|
||||||
|
await focus.removeTopic(topicId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveSubject: async (topicId: string, sealed: boolean, subject: any) => {
|
||||||
|
const focus = app.state.focus;
|
||||||
|
if (focus) {
|
||||||
|
await focus.setTopicSubject(topicId, sealed ? 'sealedtopic' : 'superbasictopic', ()=>subject, [], ()=>true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getTimestamp: (created: number) => {
|
||||||
|
const now = Math.floor((new Date()).getTime() / 1000)
|
||||||
|
const date = new Date(created * 1000);
|
||||||
|
const offset = now - created;
|
||||||
|
if(offset < 43200) {
|
||||||
|
if (state.timeFormat === '12h') {
|
||||||
|
return date.toLocaleTimeString("en-US", {hour: 'numeric', minute:'2-digit'});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return date.toLocaleTimeString("en-GB", {hour: 'numeric', minute:'2-digit'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (offset < 31449600) {
|
||||||
|
if (state.dateFormat === 'mm/dd') {
|
||||||
|
return date.toLocaleDateString("en-US", {day: 'numeric', month:'numeric'});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return date.toLocaleDateString("en-GB", {day: 'numeric', month:'numeric'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (state.dateFormat === 'mm/dd') {
|
||||||
|
return date.toLocaleDateString("en-US");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return date.toLocaleDateString("en-GB");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { state, actions }
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import {StyleSheet} from 'react-native';
|
||||||
|
import {Colors} from '../constants/Colors';
|
||||||
|
|
||||||
|
export const styles = StyleSheet.create({
|
||||||
|
});
|
11
app/client/mobile/src/message/videoAsset/VideoAsset.tsx
Normal file
11
app/client/mobile/src/message/videoAsset/VideoAsset.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Text } from 'react-native'
|
||||||
|
import { useVideoAsset } from './useVideoAsset.hook';
|
||||||
|
import { MediaAsset } from '../../conversation/Conversation';
|
||||||
|
|
||||||
|
export function VideoAsset({ topicId, asset }: { topicId: string, asset: MediaAsset }) {
|
||||||
|
const { state, actions } = useVideoAsset(topicId, asset);
|
||||||
|
|
||||||
|
return (<Text>VIDEO</Text>);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
|||||||
|
import { useState, useContext, useEffect } from 'react'
|
||||||
|
import { AppContext } from '../../context/AppContext'
|
||||||
|
import { Focus } from 'databag-client-sdk'
|
||||||
|
import { ContextType } from '../../context/ContextType'
|
||||||
|
import { MediaAsset } from '../../conversation/Conversation';
|
||||||
|
|
||||||
|
export function useVideoAsset(topicId: string, asset: MediaAsset) {
|
||||||
|
const app = useContext(AppContext) as ContextType
|
||||||
|
const [state, setState] = useState({
|
||||||
|
thumbUrl: null,
|
||||||
|
dataUrl: null,
|
||||||
|
loading: false,
|
||||||
|
loadPercent: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const updateState = (value: any) => {
|
||||||
|
setState((s) => ({ ...s, ...value }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const setThumb = async () => {
|
||||||
|
const { focus } = app.state;
|
||||||
|
const assetId = asset.video ? asset.video.thumb : asset.encrypted ? asset.encrypted.thumb : null;
|
||||||
|
if (focus && assetId != null) {
|
||||||
|
try {
|
||||||
|
const thumbUrl = await focus.getTopicAssetUrl(topicId, assetId);
|
||||||
|
updateState({ thumbUrl });
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setThumb();
|
||||||
|
}, [asset]);
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
unloadVideo: () => {
|
||||||
|
updateState({ dataUrl: null });
|
||||||
|
},
|
||||||
|
loadVideo: async () => {
|
||||||
|
const { focus } = app.state;
|
||||||
|
const assetId = asset.video ? asset.video.hd : asset.encrypted ? asset.encrypted.parts : null;
|
||||||
|
if (focus && assetId != null && !state.loading) {
|
||||||
|
updateState({ loading: true, loadPercent: 0 });
|
||||||
|
try {
|
||||||
|
const dataUrl = await focus.getTopicAssetUrl(topicId, assetId, (loadPercent: number)=>{ updateState({ loadPercent }) });
|
||||||
|
updateState({ dataUrl, loading: false });
|
||||||
|
} catch (err) {
|
||||||
|
updateState({ loading: false });
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { state, actions }
|
||||||
|
}
|
@ -157,7 +157,11 @@ function ContentTab({scheme}: {scheme: string}) {
|
|||||||
<Content openConversation={()=>props.navigation.navigate('conversation')} />
|
<Content openConversation={()=>props.navigation.navigate('conversation')} />
|
||||||
)}
|
)}
|
||||||
</ContentStack.Screen>
|
</ContentStack.Screen>
|
||||||
<ContentStack.Screen name="conversation" options={{headerBackTitleVisible: false}}>
|
<ContentStack.Screen name="conversation"
|
||||||
|
options={{
|
||||||
|
headerBackTitleVisible: false,
|
||||||
|
...TransitionPresets.ScaleFromCenterAndroid,
|
||||||
|
}}>
|
||||||
{props => (
|
{props => (
|
||||||
<Conversation close={()=>props.navigation.goBack()} />
|
<Conversation close={()=>props.navigation.goBack()} />
|
||||||
)}
|
)}
|
||||||
|
@ -182,7 +182,7 @@ export class FocusModule implements Focus {
|
|||||||
if (data) {
|
if (data) {
|
||||||
const { detailRevision, topicDetail } = data;
|
const { detailRevision, topicDetail } = data;
|
||||||
const detail = topicDetail ? topicDetail : await this.getRemoteChannelTopicDetail(id);
|
const detail = topicDetail ? topicDetail : await this.getRemoteChannelTopicDetail(id);
|
||||||
if (!this.cacheView || this.cacheView.position > detail.created || (this.cacheView.position === detail.created && this.cacheView.topicId >= id)) {
|
if (!this.cacheView || this.cacheView.position < detail.created || (this.cacheView.position === detail.created && this.cacheView.topicId >= id)) {
|
||||||
const entry = await this.getTopicEntry(id);
|
const entry = await this.getTopicEntry(id);
|
||||||
if (detailRevision > entry.item.detail.revision) {
|
if (detailRevision > entry.item.detail.revision) {
|
||||||
entry.item.detail = this.getTopicDetail(detail, detailRevision);
|
entry.item.detail = this.getTopicDetail(detail, detailRevision);
|
||||||
@ -208,6 +208,7 @@ export class FocusModule implements Focus {
|
|||||||
if (this.nextRevision === nextRev) {
|
if (this.nextRevision === nextRev) {
|
||||||
this.nextRevision = null;
|
this.nextRevision = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.markRead();
|
await this.markRead();
|
||||||
this.emitTopics();
|
this.emitTopics();
|
||||||
this.log.info(`topic revision: ${nextRev}`);
|
this.log.info(`topic revision: ${nextRev}`);
|
||||||
|
@ -153,7 +153,7 @@ export class OfflineStore implements Store {
|
|||||||
try {
|
try {
|
||||||
return JSON.parse(value);
|
return JSON.parse(value);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -166,7 +166,7 @@ export class OfflineStore implements Store {
|
|||||||
return JSON.parse(rows[0].value);
|
return JSON.parse(rows[0].value);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
return unset;
|
return unset;
|
||||||
}
|
}
|
||||||
@ -182,17 +182,17 @@ export class OfflineStore implements Store {
|
|||||||
private async getTableValue(guid: string, table: string, field: string, where: {field: string, value: string}[], unset: any): Promise<any> {
|
private async getTableValue(guid: string, table: string, field: string, where: {field: string, value: string}[], unset: any): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const params = where.map(({value}) => value);
|
const params = where.map(({value}) => value);
|
||||||
const rows = await this.sql.get(`SELECT ${field} FROM ${table} WHERE ${where.map(column => (column.field + '=?')).join(' AND ')}`, params)
|
const rows = await this.sql.get(`SELECT ${field} FROM ${table}_${guid} WHERE ${where.map(column => (column.field + '=?')).join(' AND ')}`, params)
|
||||||
if (rows.length == 1 && rows[0].value != null) {
|
if (rows.length == 1 && rows[0][field]) {
|
||||||
return this.parse(rows[0].value);
|
return this.parse(rows[0][field]);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
return unset;
|
return unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setTableValue(guid: string, table: string, record: {field: string, value: string}[], where: {field: string, value: string}[]): Promise<void> {
|
private async setTableValue(guid: string, table: string, record: {field: string, value: any}[], where: {field: string, value: string}[]): Promise<void> {
|
||||||
const params = [...record.map(({value}) => JSON.stringify(value)), ...where.map(({value}) => value)]
|
const params = [...record.map(({value}) => JSON.stringify(value)), ...where.map(({value}) => value)]
|
||||||
await this.sql.set(`UPDATE ${table}_${guid} SET ${record.map(({field}) => (field + '=?')).join(', ')} WHERE ${where.map(({field}) => (field + '=?')).join(' AND ')}`, params);
|
await this.sql.set(`UPDATE ${table}_${guid} SET ${record.map(({field}) => (field + '=?')).join(', ')} WHERE ${where.map(({field}) => (field + '=?')).join(' AND ')}`, params);
|
||||||
}
|
}
|
||||||
@ -506,7 +506,7 @@ export class OfflineStore implements Store {
|
|||||||
return await this.getTableValue(guid, 'channel', 'sync', [{field: 'channel_id', value: channelId}], { revision: null, marker: null });
|
return await this.getTableValue(guid, 'channel', 'sync', [{field: 'channel_id', value: channelId}], { revision: null, marker: null });
|
||||||
}
|
}
|
||||||
public async setContentChannelTopicRevision(guid: string, channelId: string, sync: { revision: number | null, marker: number | null }): Promise<void> {
|
public async setContentChannelTopicRevision(guid: string, channelId: string, sync: { revision: number | null, marker: number | null }): Promise<void> {
|
||||||
await this.setTableValue(guid, 'channel', [{field: 'sync', value: JSON.stringify(sync)}], [{field: 'channel_id', value: channelId}]);
|
await this.setTableValue(guid, 'channel', [{field: 'sync', value: sync}], [{field: 'channel_id', value: channelId}]);
|
||||||
}
|
}
|
||||||
public async getContentChannelTopics(guid: string, channelId: string, count: number, offset: { topicId: string, position: number } | null): Promise<{ topicId: string, item: TopicItem }[]> {
|
public async getContentChannelTopics(guid: string, channelId: string, count: number, offset: { topicId: string, position: number } | null): Promise<{ topicId: string, item: TopicItem }[]> {
|
||||||
const fields = ['topic_id', 'detail', 'unsealed_detail', 'position'];
|
const fields = ['topic_id', 'detail', 'unsealed_detail', 'position'];
|
||||||
@ -543,7 +543,7 @@ export class OfflineStore implements Store {
|
|||||||
return await this.getTableValue(guid, 'card_channel', 'sync', [{field: 'card_id', value: cardId},{field: 'channel_id', value: channelId}], { revision: null, marker: null });
|
return await this.getTableValue(guid, 'card_channel', 'sync', [{field: 'card_id', value: cardId},{field: 'channel_id', value: channelId}], { revision: null, marker: null });
|
||||||
}
|
}
|
||||||
public async setContactCardChannelTopicRevision(guid: string, cardId: string, channelId: string, sync: { revision: number | null, marker: number | null }): Promise<void> {
|
public async setContactCardChannelTopicRevision(guid: string, cardId: string, channelId: string, sync: { revision: number | null, marker: number | null }): Promise<void> {
|
||||||
return await this.setTableValue(guid, 'card_channel', [{field: 'sync', value: JSON.stringify(sync)}], [{field: 'card_id', value: cardId}, {field: 'channel_id', value: channelId}]);
|
return await this.setTableValue(guid, 'card_channel', [{field: 'sync', value: sync}], [{field: 'card_id', value: cardId}, {field: 'channel_id', value: channelId}]);
|
||||||
}
|
}
|
||||||
public async getContactCardChannelTopics(guid: string, cardId: string, channelId: string, count: number, offset: { topicId: string, position: number } | null): Promise<{ topicId: string, item: TopicItem }[]> {
|
public async getContactCardChannelTopics(guid: string, cardId: string, channelId: string, count: number, offset: { topicId: string, position: number } | null): Promise<{ topicId: string, item: TopicItem }[]> {
|
||||||
const fields = ['topic_id', 'detail', 'unsealed_detail', 'position'];
|
const fields = ['topic_id', 'detail', 'unsealed_detail', 'position'];
|
||||||
@ -614,7 +614,7 @@ export class OnlineStore implements Store {
|
|||||||
updated.push({ id, value });
|
updated.push({ id, value });
|
||||||
this.setAppValue(guid, `marker_${type}`, updated);
|
this.setAppValue(guid, `marker_${type}`, updated);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,7 +624,7 @@ export class OnlineStore implements Store {
|
|||||||
const updated = markers.filter((marker: {id: string, value: string}) => (marker.id !== id));
|
const updated = markers.filter((marker: {id: string, value: string}) => (marker.id !== id));
|
||||||
this.setAppValue(guid, `marker_${type}`, updated);
|
this.setAppValue(guid, `marker_${type}`, updated);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
this.log.error(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -633,7 +633,7 @@ export class OnlineStore implements Store {
|
|||||||
try {
|
try {
|
||||||
return markers.map((marker: {id: string, value: string}) => ({ id: marker.id, value: marker.value }));
|
return markers.map((marker: {id: string, value: string}) => ({ id: marker.id, value: marker.value }));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
this.log.error(err);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user