mirror of
https://github.com/balzack/databag.git
synced 2025-02-14 20:49:16 +00:00
Merge branch 'main' into webrtc
This commit is contained in:
commit
0efa818249
@ -571,7 +571,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
|
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
@ -643,7 +643,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
|
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@braintree/sanitize-url": "^6.0.2",
|
||||||
"@react-native-clipboard/clipboard": "^1.11.1",
|
"@react-native-clipboard/clipboard": "^1.11.1",
|
||||||
"@react-native-firebase/app": "^17.2.0",
|
"@react-native-firebase/app": "^17.2.0",
|
||||||
"@react-native-firebase/messaging": "^17.2.0",
|
"@react-native-firebase/messaging": "^17.2.0",
|
||||||
|
@ -56,6 +56,7 @@ export function Admin() {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{ Platform.OS !== 'ios' && (
|
||||||
<View style={styles.tos}>
|
<View style={styles.tos}>
|
||||||
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
|
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
|
||||||
<Text style={styles.viewtermstext}>View Terms of Service</Text>
|
<Text style={styles.viewtermstext}>View Terms of Service</Text>
|
||||||
@ -70,8 +71,9 @@ export function Admin() {
|
|||||||
<Text style={styles.agreetermstext}>I agree to Terms of Service</Text>
|
<Text style={styles.agreetermstext}>I agree to Terms of Service</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{ state.enabled && state.agree && (
|
{ state.enabled && (Platform.OS === 'ios' || state.agree) && (
|
||||||
<TouchableOpacity style={styles.reset} onPress={admin}>
|
<TouchableOpacity style={styles.reset} onPress={admin}>
|
||||||
{ state.busy && (
|
{ state.busy && (
|
||||||
<ActivityIndicator size="small" color="#ffffff" />
|
<ActivityIndicator size="small" color="#ffffff" />
|
||||||
@ -81,7 +83,7 @@ export function Admin() {
|
|||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
{ (!state.enabled || !state.agree) && (
|
{ (!state.enabled || (Platform.OS !== 'ios' && !state.agree)) && (
|
||||||
<View style={styles.noreset}>
|
<View style={styles.noreset}>
|
||||||
<Text style={styles.noresettext}>Access</Text>
|
<Text style={styles.noresettext}>Access</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -138,6 +138,7 @@ export function Create() {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{ Platform.OS !== 'ios' && (
|
||||||
<View style={styles.tos}>
|
<View style={styles.tos}>
|
||||||
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
|
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
|
||||||
<Text style={styles.viewtermstext}>View Terms of Service</Text>
|
<Text style={styles.viewtermstext}>View Terms of Service</Text>
|
||||||
@ -152,9 +153,10 @@ export function Create() {
|
|||||||
<Text style={styles.agreetermstext}>I agree to Terms of Service</Text>
|
<Text style={styles.agreetermstext}>I agree to Terms of Service</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
<View style={styles.buttons}>
|
<View style={styles.buttons}>
|
||||||
{ state.enabled && state.agree && (
|
{ state.enabled && (Platform.OS === 'ios' || state.agree) && (
|
||||||
<TouchableOpacity style={styles.create} onPress={create}>
|
<TouchableOpacity style={styles.create} onPress={create}>
|
||||||
{ state.busy && (
|
{ state.busy && (
|
||||||
<ActivityIndicator size="small" color="#ffffff" />
|
<ActivityIndicator size="small" color="#ffffff" />
|
||||||
@ -164,7 +166,7 @@ export function Create() {
|
|||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
{ (!state.enabled || !state.agree) && (
|
{ (!state.enabled || (Platform.OS !== 'ios' && !state.agree)) && (
|
||||||
<View style={styles.nocreate}>
|
<View style={styles.nocreate}>
|
||||||
<Text style={styles.nocreatetext}>Create Account</Text>
|
<Text style={styles.nocreatetext}>Create Account</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { KeyboardAvoidingView, ActivityIndicator, Modal, ScrollView, Alert, Text, TextInput, View, TouchableOpacity } from 'react-native';
|
import { Platform, KeyboardAvoidingView, ActivityIndicator, Modal, ScrollView, Alert, Text, TextInput, View, TouchableOpacity } from 'react-native';
|
||||||
import { styles } from './Login.styled';
|
import { styles } from './Login.styled';
|
||||||
import Ionicons from 'react-native-vector-icons/AntDesign';
|
import Ionicons from 'react-native-vector-icons/AntDesign';
|
||||||
import { useLogin } from './useLogin.hook';
|
import { useLogin } from './useLogin.hook';
|
||||||
@ -63,6 +63,7 @@ export function Login() {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{ Platform.OS !== 'ios' && (
|
||||||
<View style={styles.tos}>
|
<View style={styles.tos}>
|
||||||
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
|
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
|
||||||
<Text style={styles.viewtermstext}>View Terms of Service</Text>
|
<Text style={styles.viewtermstext}>View Terms of Service</Text>
|
||||||
@ -77,8 +78,9 @@ export function Login() {
|
|||||||
<Text style={styles.agreetermstext}>I agree to Terms of Service</Text>
|
<Text style={styles.agreetermstext}>I agree to Terms of Service</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{ state.enabled && state.agree && (
|
{ state.enabled && (Platform.OS === 'ios' || state.agree) && (
|
||||||
<TouchableOpacity style={styles.login} onPress={login}>
|
<TouchableOpacity style={styles.login} onPress={login}>
|
||||||
{ state.busy && (
|
{ state.busy && (
|
||||||
<ActivityIndicator size="small" color="#ffffff" />
|
<ActivityIndicator size="small" color="#ffffff" />
|
||||||
@ -88,7 +90,7 @@ export function Login() {
|
|||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
{ (!state.enabled || !state.agree) && (
|
{ (!state.enabled || (Platform.OS !== 'ios' && !state.agree)) && (
|
||||||
<View style={styles.nologin}>
|
<View style={styles.nologin}>
|
||||||
<Text style={styles.nologintext}>Login</Text>
|
<Text style={styles.nologintext}>Login</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -48,6 +48,7 @@ export function Reset() {
|
|||||||
<View style={styles.space} />
|
<View style={styles.space} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{ Platform.OS !== 'ios' && (
|
||||||
<View style={styles.tos}>
|
<View style={styles.tos}>
|
||||||
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
|
<TouchableOpacity style={styles.viewterms} onPress={actions.showTerms}>
|
||||||
<Text style={styles.viewtermstext}>View Terms of Service</Text>
|
<Text style={styles.viewtermstext}>View Terms of Service</Text>
|
||||||
@ -62,8 +63,9 @@ export function Reset() {
|
|||||||
<Text style={styles.agreetermstext}>I agree to Terms of Service</Text>
|
<Text style={styles.agreetermstext}>I agree to Terms of Service</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{ state.enabled && state.agree && (
|
{ state.enabled && (Platform.OS === 'ios' || state.agree) && (
|
||||||
<TouchableOpacity style={styles.reset} onPress={reset}>
|
<TouchableOpacity style={styles.reset} onPress={reset}>
|
||||||
{ state.busy && (
|
{ state.busy && (
|
||||||
<ActivityIndicator size="small" color="#ffffff" />
|
<ActivityIndicator size="small" color="#ffffff" />
|
||||||
@ -73,7 +75,7 @@ export function Reset() {
|
|||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
{ (!state.enabled || !state.agree) && (
|
{ (!state.enabled || (Platform.OS !== 'ios' && !state.agree)) && (
|
||||||
<View style={styles.noreset}>
|
<View style={styles.noreset}>
|
||||||
<Text style={styles.noresettext}>Access</Text>
|
<Text style={styles.noresettext}>Access</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -131,8 +131,8 @@ export function TopicItem({ item, focused, focus, hosting, remove, update, block
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ ...styles.wrapper, transform: [{rotate: '180deg'}]}}>
|
<View style={{ ...styles.wrapper, transform: [{rotate: '180deg'}]}}>
|
||||||
<TouchableOpacity activeOpacity={1} style={styles.item} onPress={focus}>
|
<View style={styles.item}>
|
||||||
<View style={styles.header}>
|
<TouchableOpacity activeOpacity={1} onPress={focus} style={styles.header}>
|
||||||
{ state.logo !== 'avatar' && state.logo && (
|
{ state.logo !== 'avatar' && state.logo && (
|
||||||
<Image source={{ uri: state.logo }} style={{ width: 28, height: 28, borderRadius: 6 }} />
|
<Image source={{ uri: state.logo }} style={{ width: 28, height: 28, borderRadius: 6 }} />
|
||||||
)}
|
)}
|
||||||
@ -141,7 +141,7 @@ export function TopicItem({ item, focused, focus, hosting, remove, update, block
|
|||||||
)}
|
)}
|
||||||
<Text style={{ ...styles.name, color: state.nameSet ? Colors.text : Colors.grey }}>{ state.name }</Text>
|
<Text style={{ ...styles.name, color: state.nameSet ? Colors.text : Colors.grey }}>{ state.name }</Text>
|
||||||
<Text style={styles.timestamp}>{ state.timestamp }</Text>
|
<Text style={styles.timestamp}>{ state.timestamp }</Text>
|
||||||
</View>
|
</TouchableOpacity>
|
||||||
{ state.status === 'confirmed' && (
|
{ state.status === 'confirmed' && (
|
||||||
<>
|
<>
|
||||||
{ state.transform === 'complete' && state.assets && (
|
{ state.transform === 'complete' && state.assets && (
|
||||||
@ -162,8 +162,8 @@ export function TopicItem({ item, focused, focus, hosting, remove, update, block
|
|||||||
<MatIcons name="weather-cloudy-alert" size={32} color={Colors.alert} />
|
<MatIcons name="weather-cloudy-alert" size={32} color={Colors.alert} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{ state.message && !state.sealed && (
|
{ state.clickable && !state.sealed && (
|
||||||
<Text style={{ ...styles.message, fontSize: state.fontSize, color: state.fontColor }}>{ state.message }</Text>
|
<Text style={{ ...styles.message, fontSize: state.fontSize, color: state.fontColor }}>{ state.clickable }</Text>
|
||||||
)}
|
)}
|
||||||
{ state.sealed && (
|
{ state.sealed && (
|
||||||
<Text style={ styles.sealed }>sealed message</Text>
|
<Text style={ styles.sealed }>sealed message</Text>
|
||||||
@ -175,7 +175,7 @@ export function TopicItem({ item, focused, focus, hosting, remove, update, block
|
|||||||
<MatIcons name="cloud-refresh" size={32} color={Colors.divider} />
|
<MatIcons name="cloud-refresh" size={32} color={Colors.divider} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</View>
|
||||||
{ focused && (
|
{ focused && (
|
||||||
<View style={styles.focused}>
|
<View style={styles.focused}>
|
||||||
{ state.editable && (
|
{ state.editable && (
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { useState, useEffect, useContext } from 'react';
|
import { useState, useEffect, useContext } from 'react';
|
||||||
|
import { Linking } from 'react-native';
|
||||||
import { ConversationContext } from 'context/ConversationContext';
|
import { ConversationContext } from 'context/ConversationContext';
|
||||||
import { CardContext } from 'context/CardContext';
|
import { CardContext } from 'context/CardContext';
|
||||||
import { ProfileContext } from 'context/ProfileContext';
|
import { ProfileContext } from 'context/ProfileContext';
|
||||||
import { AccountContext } from 'context/AccountContext';
|
import { AccountContext } from 'context/AccountContext';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { useWindowDimensions } from 'react-native';
|
import { useWindowDimensions, Text } from 'react-native';
|
||||||
import Colors from 'constants/Colors';
|
import Colors from 'constants/Colors';
|
||||||
import { getCardByGuid } from 'context/cardUtil';
|
import { getCardByGuid } from 'context/cardUtil';
|
||||||
import { decryptTopicSubject } from 'context/sealUtil';
|
import { decryptTopicSubject } from 'context/sealUtil';
|
||||||
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
|
|
||||||
export function useTopicItem(item, hosting, remove, contentKey) {
|
export function useTopicItem(item, hosting, remove, contentKey) {
|
||||||
|
|
||||||
@ -18,6 +20,7 @@ export function useTopicItem(item, hosting, remove, contentKey) {
|
|||||||
logo: null,
|
logo: null,
|
||||||
timestamp: null,
|
timestamp: null,
|
||||||
message: null,
|
message: null,
|
||||||
|
clickable: null,
|
||||||
carousel: false,
|
carousel: false,
|
||||||
carouselIndex: 0,
|
carouselIndex: 0,
|
||||||
width: null,
|
width: null,
|
||||||
@ -90,12 +93,13 @@ export function useTopicItem(item, hosting, remove, contentKey) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed, sealed, message, assets, fontSize, fontColor;
|
let parsed, sealed, message, clickable, assets, fontSize, fontColor;
|
||||||
if (dataType === 'superbasictopic') {
|
if (dataType === 'superbasictopic') {
|
||||||
try {
|
try {
|
||||||
sealed = false;
|
sealed = false;
|
||||||
parsed = JSON.parse(data);
|
parsed = JSON.parse(data);
|
||||||
message = parsed.text;
|
message = parsed?.text;
|
||||||
|
clickable = clickableText(parsed.text);
|
||||||
assets = parsed.assets;
|
assets = parsed.assets;
|
||||||
if (parsed.textSize === 'small') {
|
if (parsed.textSize === 'small') {
|
||||||
fontSize = 10;
|
fontSize = 10;
|
||||||
@ -139,6 +143,7 @@ export function useTopicItem(item, hosting, remove, contentKey) {
|
|||||||
sealed = false;
|
sealed = false;
|
||||||
parsed = unsealed.message;
|
parsed = unsealed.message;
|
||||||
message = parsed?.text;
|
message = parsed?.text;
|
||||||
|
clickable = clickableText(parsed?.text);
|
||||||
if (parsed?.textSize === 'small') {
|
if (parsed?.textSize === 'small') {
|
||||||
fontSize = 10;
|
fontSize = 10;
|
||||||
}
|
}
|
||||||
@ -177,7 +182,7 @@ export function useTopicItem(item, hosting, remove, contentKey) {
|
|||||||
const editable = guid === identity?.guid && parsed;
|
const editable = guid === identity?.guid && parsed;
|
||||||
const deletable = editable || hosting;
|
const deletable = editable || hosting;
|
||||||
|
|
||||||
updateState({ logo, name, nameSet, known, sealed, message, fontSize, fontColor, timestamp, transform, status, assets, deletable, editable, editData: parsed, editMessage: message, editType: dataType });
|
updateState({ logo, name, nameSet, known, sealed, message, clickable, fontSize, fontColor, timestamp, transform, status, assets, deletable, editable, editData: parsed, editMessage: message, editType: dataType });
|
||||||
}, [conversation.state, card.state, account.state, item, contentKey]);
|
}, [conversation.state, card.state, account.state, item, contentKey]);
|
||||||
|
|
||||||
const unsealTopic = async (topicId, revision, topicDetail) => {
|
const unsealTopic = async (topicId, revision, topicDetail) => {
|
||||||
@ -194,6 +199,31 @@ export function useTopicItem(item, hosting, remove, contentKey) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const clickableText = (text) => {
|
||||||
|
var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
|
||||||
|
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
|
||||||
|
'((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
|
||||||
|
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
|
||||||
|
'(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
|
||||||
|
'(\\#[-a-z\\d_]*)?$','i'); // fragment locator
|
||||||
|
|
||||||
|
let clickable = [];
|
||||||
|
let group = '';
|
||||||
|
const words = text == null ? [''] : text.split(' ');
|
||||||
|
words.forEach((word, index) => {
|
||||||
|
if (!!pattern.test(word)) {
|
||||||
|
clickable.push(<Text key={index}>{ group }</Text>);
|
||||||
|
group = '';
|
||||||
|
clickable.push(<Text key={'link-' + index} onPress={() => Linking.openURL(sanitizeUrl(word))} style={{ fontStyle: 'italic' }}>{ sanitizeUrl(word) + ' ' }</Text>);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
group += `${word} `;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
clickable.push(<Text key={words.length}>{ group }</Text>);
|
||||||
|
return <Text>{ clickable }</Text>;
|
||||||
|
};
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
showCarousel: (index) => {
|
showCarousel: (index) => {
|
||||||
updateState({ carousel: true, carouselIndex: index });
|
updateState({ carousel: true, carouselIndex: index });
|
||||||
|
@ -1077,6 +1077,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz"
|
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
|
"@braintree/sanitize-url@^6.0.2":
|
||||||
|
version "6.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz#6110f918d273fe2af8ea1c4398a88774bb9fc12f"
|
||||||
|
integrity sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==
|
||||||
|
|
||||||
"@egjs/hammerjs@^2.0.17":
|
"@egjs/hammerjs@^2.0.17":
|
||||||
version "2.0.17"
|
version "2.0.17"
|
||||||
resolved "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz"
|
resolved "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz"
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"base-64": "^1.0.0",
|
"base-64": "^1.0.0",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
|
"dompurify": "^3.0.1",
|
||||||
"jsencrypt": "^2.3.1",
|
"jsencrypt": "^2.3.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
|
@ -123,7 +123,7 @@ export function TopicItem({ host, sealed, topic, update, remove }) {
|
|||||||
)}
|
)}
|
||||||
{ !sealed && !state.editing && (
|
{ !sealed && !state.editing && (
|
||||||
<div class="message">
|
<div class="message">
|
||||||
<div style={{ color: topic.textColor, fontSize: topic.textSize }}>{ topic.text }</div>
|
<div style={{ color: topic.textColor, fontSize: topic.textSize }}>{ topic.clickable }</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{ state.editing && (
|
{ state.editing && (
|
||||||
|
@ -9,6 +9,7 @@ import { ProfileContext } from 'context/ProfileContext';
|
|||||||
import { isUnsealed, getChannelSeals, getContentKey, encryptTopicSubject } from 'context/sealUtil';
|
import { isUnsealed, getChannelSeals, getContentKey, encryptTopicSubject } from 'context/sealUtil';
|
||||||
import { decryptTopicSubject } from 'context/sealUtil';
|
import { decryptTopicSubject } from 'context/sealUtil';
|
||||||
import { getProfileByGuid } from 'context/cardUtil';
|
import { getProfileByGuid } from 'context/cardUtil';
|
||||||
|
import * as DOMPurify from 'dompurify';
|
||||||
|
|
||||||
export function useConversation(cardId, channelId) {
|
export function useConversation(cardId, channelId) {
|
||||||
|
|
||||||
@ -132,6 +133,31 @@ export function useConversation(cardId, channelId) {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [state.contentKey]);
|
}, [state.contentKey]);
|
||||||
|
|
||||||
|
const clickableText = (text) => {
|
||||||
|
var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
|
||||||
|
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
|
||||||
|
'((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
|
||||||
|
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
|
||||||
|
'(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
|
||||||
|
'(\\#[-a-z\\d_]*)?$','i'); // fragment locator
|
||||||
|
|
||||||
|
let group = '';
|
||||||
|
let clickable = [];
|
||||||
|
const words = text == null ? '' : DOMPurify.sanitize(text).split(' ');
|
||||||
|
words.forEach((word, index) => {
|
||||||
|
if (!!pattern.test(word)) {
|
||||||
|
clickable.push(<span key={index}>{ group }</span>);
|
||||||
|
group = '';
|
||||||
|
clickable.push(<a key={'link-'+index} target="_blank" rel="noopener noreferrer" href={word}>{ `${word} ` }</a>);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
group += `${word} `;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
clickable.push(<span key={words.length}>{ group }</span>);
|
||||||
|
return <p>{ clickable }</p>;
|
||||||
|
};
|
||||||
|
|
||||||
const syncTopic = (item, value) => {
|
const syncTopic = (item, value) => {
|
||||||
const revision = value.data?.detailRevision;
|
const revision = value.data?.detailRevision;
|
||||||
const detail = value.data?.topicDetail || {};
|
const detail = value.data?.topicDetail || {};
|
||||||
@ -190,6 +216,7 @@ export function useConversation(cardId, channelId) {
|
|||||||
const message = JSON.parse(detail.data);
|
const message = JSON.parse(detail.data);
|
||||||
item.assets = message.assets;
|
item.assets = message.assets;
|
||||||
item.text = message.text;
|
item.text = message.text;
|
||||||
|
item.clickable = clickableText(message.text);
|
||||||
item.textColor = message.textColor ? message.textColor : '#444444';
|
item.textColor = message.textColor ? message.textColor : '#444444';
|
||||||
item.textSize = message.textSize ? message.textSize : 14;
|
item.textSize = message.textSize ? message.textSize : 14;
|
||||||
}
|
}
|
||||||
@ -197,6 +224,7 @@ export function useConversation(cardId, channelId) {
|
|||||||
const subject = decryptTopicSubject(detail.data, state.contentKey);
|
const subject = decryptTopicSubject(detail.data, state.contentKey);
|
||||||
item.assets = subject.message.assets;
|
item.assets = subject.message.assets;
|
||||||
item.text = subject.message.text;
|
item.text = subject.message.text;
|
||||||
|
item.clickable = clickableText(subject.message.text);
|
||||||
item.textColor = subject.message.textColor ? subject.message.textColor : '#444444';
|
item.textColor = subject.message.textColor ? subject.message.textColor : '#444444';
|
||||||
item.textSize = subject.message.textSize ? subject.message.textSize : 14;
|
item.textSize = subject.message.textSize ? subject.message.textSize : 14;
|
||||||
}
|
}
|
||||||
|
@ -4366,6 +4366,11 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
domelementtype "^2.2.0"
|
domelementtype "^2.2.0"
|
||||||
|
|
||||||
|
dompurify@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.1.tgz#a0933f38931b3238934dd632043b727e53004289"
|
||||||
|
integrity sha512-60tsgvPKwItxZZdfLmamp0MTcecCta3avOhsLgPZ0qcWt96OasFfhkeIRbJ6br5i0fQawT1/RBGB5L58/Jpwuw==
|
||||||
|
|
||||||
domutils@^1.7.0:
|
domutils@^1.7.0:
|
||||||
version "1.7.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz"
|
resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz"
|
||||||
|
Loading…
Reference in New Issue
Block a user