diff --git a/app/assets/styles/blink/_ContactCard.scss b/app/assets/styles/blink/_ContactCard.scss
index af91b89..7dc6077 100644
--- a/app/assets/styles/blink/_ContactCard.scss
+++ b/app/assets/styles/blink/_ContactCard.scss
@@ -1,115 +1,155 @@
.containerPortrait {
}
.containerLandscape {
}
.cardPortraitContainer {
margin-top: 0.5px;
}
.cardLandscapeContainer {
flex: 1;
margin-left: 1px;
margin-top: 1px;
border-radius: 2px;
}
.cardLandscapeTabletContainer {
flex: 1;
border: 1px;
border-radius: 2px;
}
.cardPortraitTabletContainer {
flex: 1;
border: 1px;
border-radius: 2px;
}
.rowContent {
flex: 1;
flex-direction: row;
justify-content: space-between;
}
.cardContent {
flex: 1;
flex-direction: row;
}
.title {
- padding-top:7px;
font-size: 18px;
line-height: 20px;
flex: 1;
}
+.titlePaddingSmall {
+ padding-top:0px;
+}
+
+.titlePadding {
+ padding-top:7px;
+}
+
+.titlePaddingBig {
+ padding-top:14px;
+}
+
.subtitle {
font-size:14px;
line-height: 20px;
flex: 1;
}
.description {
font-size:12px;
flex: 1;
}
.avatarContent {
- margin-top: 5px;
+ margin-top: 10px;
}
.gravatar {
width: 50px;
height: 50px;
border-width: 2px;
border-color: white;
border-radius: 50px;
}
.mainContent {
margin-left: 10px;
}
.rightContent {
margin-top: 10px;
margin-left: 60px;
margin-right: 10px;
align-items: flex-end;
border: 0px;
}
.badgeTextStyle {
font-size: 12px;
}
.badgeContainer {
position: absolute;
bottom: 10;
right: 0;
size: 30;
}
.selectedContact {
margin-top: 15px;
}
+.timestamp {
+ margin-top: -5px;
+}
+
.participants {
margin-top: 10px;
}
.participant {
font-size: 14px;
}
.buttonContainer {
margin: 0 auto;
margin-top:auto;
}
.button {
border-radius: 2px;
padding-left: 30px;
padding-right: 30px;
}
+
+.greenButtonContainer {
+ padding-right: 15px;
+}
+
+.recordingLabel {
+ margin-top: 7px;
+}
+
+.greenButton {
+ background-color: rgba(#6DAA63, .8);
+ margin-left: 0px;
+}
+
+.audioButton {
+ background-color: white;
+}
+
+.redButton {
+ background-color: red;
+}
+
+.callButtons {
+ flex-direction: row;
+}
diff --git a/app/components/ChatActions.js b/app/components/ChatActions.js
index b28c905..3e348ff 100644
--- a/app/components/ChatActions.js
+++ b/app/components/ChatActions.js
@@ -1,206 +1,204 @@
import PropTypes from 'prop-types'
import React from 'react'
import autoBind from 'auto-bind';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
import AudioRecorderPlayer from 'react-native-audio-recorder-player'
import {TouchableOpacity, View, Platform} from 'react-native'
import styles from '../assets/styles/blink/_ContactsListBox.scss';
const RNFS = require('react-native-fs');
import AudioRecord from 'react-native-audio-record';
const options = {
sampleRate: 16000, // default 44100
channels: 1, // 1 or 2, default 1
bitsPerSample: 16, // 8 or 16, default 16
audioSource: 6, // android only (see below)
wavFile: 'sylk-audio-recording.wav' // default 'audio.wav'
};
AudioRecord.init(options);
class CustomActions extends React.Component {
constructor(props) {
super(props);
autoBind(this);
this.state = {recording: false, texting: false, sendingImage: false}
this.timer = null;
this.audioRecorderPlayer = new AudioRecorderPlayer();
this.ended = false;
}
componentWillUnmount() {
this.ended = true;
this.stopRecording();
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({texting: nextProps.texting,
playing: nextProps.playing,
audioSendFinished: nextProps.audioSendFinished,
sendingImage: nextProps.sendingImage
});
if (nextProps.audioSendFinished) {
this.deleteAudioRecording()
}
}
onActionsPress = () => {
if (this.state.audioRecording) {
this.setState({audioRecording: false});
this.props.audioRecorded(null);
/*
if (this.state.playing) {
this.setState({playing: false});
this.onStopPlay();
} else {
this.setState({playing: true});
this.onStartPlay()
}
*/
} else {
if (this.state.playing) {
this.props.stopPlaying();
} else if (!this.state.recording) {
this.setState({recording: true});
this.props.onRecording(true);
console.log('Recording audio start');
this.onStartRecord();
this.timer = setTimeout(() => {
this.stopRecording();
}, 20000);
} else {
this.stopRecording();
}
}
}
stopRecording() {
if (this.timer !== null) {
clearTimeout(this.timer);
this.timer = null;
}
this.setState({recording: false});
this.props.onRecording(false);
this.onStopRecord();
}
renderIcon () {
let color = "green";
let name = this.state.recording ? "pause" : "microphone";
if (this.state.audioRecording) {
name = "delete";
color = "red"
}
if (this.state.texting || this.state.sendingImage || this.state.playing || (this.props.selectedContact && this.props.selectedContact.tags.indexOf('test') > -1)) {
return ()
}
return (
)
}
deleteAudioRecording() {
this.setState({audioRecording: null});
}
onStartRecord = async () => {
AudioRecord.start();
/* bellow code only works on Android
let path = RNFS.DocumentDirectoryPath + "/" + 'sylk-audio-recording.mp4';
const result = await this.audioRecorderPlayer.startRecorder(path);
this.audioRecorderPlayer.addRecordBackListener((e) => {
this.setState({
recordSecs: e.currentPosition,
recordTime: this.audioRecorderPlayer.mmssss(
Math.floor(e.currentPosition),
),
});
});
*/
};
onStopRecord = async () => {
if (this.ended) {
return;
}
const result = await AudioRecord.stop();
this.props.audioRecorded(result);
this.setState({audioRecording: result});
/* bellow code only works on Android
const result = await this.audioRecorderPlayer.stopRecorder();
this.audioRecorderPlayer.removeRecordBackListener();
this.setState({recordSecs: 0});
*/
-
- this.props.audioRecorded(result);
};
async onStartPlay () {
const msg = await this.audioRecorderPlayer.startPlayer();
this.audioRecorderPlayer.addPlayBackListener((e) => {
this.setState({
currentPositionSec: e.currentPosition,
currentDurationSec: e.duration,
playTime: this.audioRecorderPlayer.mmssss(Math.floor(e.currentPosition)),
duration: this.audioRecorderPlayer.mmssss(Math.floor(e.duration)),
});
});
};
onPausePlay = async () => {
await this.audioRecorderPlayer.pausePlayer();
};
onStopPlay = async () => {
console.log('onStopPlay');
this.audioRecorderPlayer.stopPlayer();
this.audioRecorderPlayer.removePlayBackListener();
this.setState({playing: false});
};
render() {
let chatLeftActionsContainer = Platform.OS === 'ios' ? styles.chatLeftActionsContaineriOS : styles.chatLeftActionsContainer;
return (
{this.renderIcon()}
)
}
}
CustomActions.propTypes = {
audioRecorded: PropTypes.func,
onRecording: PropTypes.func,
stopPlaying: PropTypes.func,
options: PropTypes.object,
texting: PropTypes.bool,
sendingImage: PropTypes.bool,
audioSendFinished: PropTypes.bool,
selectedContact: PropTypes.object
}
export default CustomActions;
diff --git a/app/components/ContactCard.js b/app/components/ContactCard.js
index 59bb62f..587b5ab 100644
--- a/app/components/ContactCard.js
+++ b/app/components/ContactCard.js
@@ -1,432 +1,498 @@
import React, { Component, Fragment} from 'react';
import { View, SafeAreaView, FlatList } from 'react-native';
import { Badge } from 'react-native-elements'
import autoBind from 'auto-bind';
import PropTypes from 'prop-types';
import moment from 'moment';
import momentFormat from 'moment-duration-format';
import { Card, IconButton, Button, Caption, Title, Subheading, List, Text, Menu} from 'react-native-paper';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import uuid from 'react-native-uuid';
import styles from '../assets/styles/blink/_ContactCard.scss';
import UserIcon from './UserIcon';
import { GiftedChat } from 'react-native-gifted-chat'
import {Gravatar, GravatarApi} from 'react-native-gravatar';
+import {Keyboard} from 'react-native';
import utils from '../utils';
function toTitleCase(str) {
return str.replace(
/\w\S*/g,
function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
}
);
}
const Item = ({ nr, uri, name }) => (
{name !== uri?
{name} ({uri})
:
{uri}
}
);
const renderItem = ({ item }) => (
-
);
function isIp(ipaddress) {
if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipaddress)) {
return (true)
}
return (false)
}
class ContactCard extends Component {
constructor(props) {
super(props);
autoBind(this);
this.state = {
id: this.props.contact.id,
contact: this.props.contact,
invitedParties: this.props.invitedParties,
orientation: this.props.orientation,
isTablet: this.props.isTablet,
isLandscape: this.props.isLandscape,
favorite: (this.props.contact.tags.indexOf('favorite') > -1)? true : false,
blocked: (this.props.contact.tags.indexOf('blocked') > -1)? true : false,
confirmRemoveFavorite: false,
confirmPurgeChat: false,
messages: this.props.messages,
unread: this.props.unread,
chat: this.props.chat,
pinned: this.props.pinned,
fontScale: this.props.fontScale,
selectMode: this.props.selectMode
}
this.menuRef = React.createRef();
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
id: nextProps.contact.id,
contact: nextProps.contact,
invitedParties: nextProps.invitedParties,
isLandscape: nextProps.isLandscape,
orientation: nextProps.orientation,
favorite: (nextProps.contact.tags.indexOf('favorite') > -1)? true : false,
blocked: (nextProps.contact.tags.indexOf('blocked') > -1)? true : false,
chat: nextProps.chat,
+ recording: nextProps.recording,
+ audioRecording: nextProps.audioRecording,
pinned: nextProps.pinned,
messages: nextProps.messages,
unread: nextProps.unread,
fontScale: nextProps.fontScale,
selectMode: nextProps.selectMode
});
}
shouldComponentUpdate(nextProps) {
//https://medium.com/sanjagh/how-to-optimize-your-react-native-flatlist-946490c8c49b
return true;
}
findObjectByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
return array[i];
}
}
return null;
}
- toggleBlocked() {
- this.props.toggleBlocked(this.state.contact.uri);
- }
-
- setBlockedDomain() {
- let newBlockedState = this.props.toggleBlocked('@' + this.state.contact.uri.split('@')[1]);
- this.setState({blocked: newBlockedState});
- }
-
undo() {
this.setState({confirmRemoveFavorite: false,
confirmDeleteChat: false,
action: null});
}
onSendMessage(messages) {
messages.forEach((message) => {
// TODO send messages using account API
});
this.setState({messages: GiftedChat.append(this.state.messages, messages)});
}
setTargetUri(uri, contact) {
this.props.setTargetUri(uri, this.state.contact);
}
renderChatComposer () {
return null;
}
+ startCall(event) {
+ event.preventDefault();
+ Keyboard.dismiss();
+ this.props.startCall(this.state.contact.uri, {audio: true, video: false});
+ }
+
+ recordAudio(event) {
+ event.preventDefault();
+ Keyboard.dismiss();
+ this.props.recordAudio();
+ }
+
+ sendAudio(event) {
+ event.preventDefault();
+ Keyboard.dismiss();
+ this.props.sendAudio();
+ }
+
+ deleteAudio(event) {
+ event.preventDefault();
+ Keyboard.dismiss();
+ this.props.deleteAudio();
+ }
+
+ startVideoCall(event) {
+ event.preventDefault();
+ Keyboard.dismiss();
+ this.props.startCall(this.state.contact.uri, {audio: true, video: true});
+ }
+
render () {
let showActions = this.state.contact.showActions &&
this.state.contact.tags.indexOf('test') === -1 &&
this.state.contact.tags !== ["synthetic"];
let tags = this.state.contact ? this.state.contact.tags : [];
let uri = this.state.contact.uri;
let username = uri.split('@')[0];
let domain = uri.split('@')[1];
let isPhoneNumber = username.match(/^(\+|0)(\d+)$/);
let name = this.state.contact.name;
if (this.state.contact.organization) {
name = name + ' - ' + this.state.contact.organization;
}
- let showBlockButton = !this.state.contact.conference && !this.state.chat;
- let showBlockDomainButton = false;
- let blockTextbutton = 'Block';
- let blockDomainTextbutton = 'Block domain';
let contact_ts = '';
let participantsData = [];
- if (this.state.favorite) {
- if (!this.state.blocked) {
- showBlockButton = false;
- }
- }
-
- if (tags.indexOf('test') > -1) {
- showBlockButton = false;
- }
-
- if (name === 'Myself') {
- showBlockButton = false;
- }
-
- if (this.state.blocked) {
- blockTextbutton = 'Unblock';
- }
-
let color = {};
let title = name || username;
let subtitle = uri;
let description = 'No calls or messages';
var todayStart = new Date();
todayStart.setHours(0,0,0,0);
if (this.state.contact.timestamp) {
description = moment(this.state.contact.timestamp).format('MMM D HH:mm');
if (this.state.contact.timestamp > todayStart) {
contact_ts = moment(this.state.contact.timestamp).format('HH:mm');
} else {
contact_ts = moment(this.state.contact.timestamp).format('DD-MM');
}
}
if (name === uri) {
title = toTitleCase(username);
}
if (isPhoneNumber && isIp(domain)) {
title = 'Tel ' + username;
subtitle = 'From @' + domain;
- showBlockDomainButton = true;
}
if (utils.isAnonymous(uri)) {
//uri = 'anonymous@anonymous.invalid';
if (uri.indexOf('@guest.') > -1) {
subtitle = 'From the Web';
} else {
name = 'Anonymous';
}
- showBlockDomainButton = true;
- if (!this.state.blocked) {
- showBlockButton = false;
- }
- blockDomainTextbutton = 'Block Web callers';
}
if (!username || username.length === 0) {
if (isIp(domain)) {
title = 'IP domain';
} else if (domain.indexOf('guest.') > -1) {
title = 'Calls from the Web';
} else {
title = 'Domain';
}
}
let cardContainerClass = styles.portraitContainer;
+ let callButtonsEnabled = this.state.chat;
if (this.state.isTablet) {
cardContainerClass = (this.state.orientation === 'landscape') ? styles.cardLandscapeTabletContainer : styles.cardPortraitTabletContainer;
} else {
cardContainerClass = (this.state.orientation === 'landscape') ? styles.cardLandscapeContainer : styles.cardPortraitContainer;
}
let cardHeight = this.state.fontScale <= 1 ? 75 : 70;
let duration;
if (this.state.contact.tags.indexOf('history') > -1) {
duration = moment.duration(this.state.contact.lastCallDuration, 'seconds').format('HH:mm:ss', {trim: false});
if (this.state.contact.direction === 'incoming' && this.state.contact.lastCallDuration === 0) {
duration = 'missed';
} else if (this.state.contact.direction === 'outgoing' && this.state.contact.lastCallDuration === 0) {
duration = 'cancelled';
}
}
if (this.state.contact.conference) {
let participants = this.state.contact.participants;
if (this.state.invitedParties && this.state.invitedParties.length > 0 ) {
participants = this.state.invitedParties;
}
if (participants && participants.length > 0) {
const p_text = participants.length > 1 ? 'participants' : 'participant';
subtitle = 'With ' + participants.length + ' ' + p_text;
let i = 1;
let contact_obj;
let dn;
let _item;
participants.forEach((participant) => {
contact_obj = this.findObjectByKey(this.props.contacts, 'uri', participant);
dn = contact_obj ? contact_obj.name : participant;
_item = {nr: i, id: uuid.v4(), uri: participant, name: dn};
participantsData.push(_item);
i = i + 1;
});
} else {
subtitle = 'With no participants';
}
}
if (!name) {
title = uri;
}
if (duration === 'missed') {
subtitle = 'Last call missed from ', + uri;
} else if (duration === 'cancelled') {
subtitle = 'Last call cancelled to ' + uri;
} else {
subtitle = uri;
}
if (subtitle !== uri) {
subtitle = subtitle + ' ' + uri;
}
if (title.indexOf('@videoconference') > -1) {
title = name || username;
}
if (uri === 'anonymous@anonymous.invalid') {
title = 'Anonymous caller';
} else {
let isAnonymous = uri.indexOf('@guest.') > -1;
if (isAnonymous) {
title = title + ' (Web call)';
}
}
if (duration && duration !== "00:00:00") {
let media = 'Audio call';
if (this.state.contact.lastCallMedia.indexOf('video') > -1) {
media = 'Video call';
}
description = description + ' (' + media + ' ' + duration + ')';
}
+ let greenButtonClass = Platform.OS === 'ios' ? styles.greenButtoniOS : styles.greenButton;
+
const container = this.state.isLandscape ? styles.containerLandscape : styles.containerPortrait;
const chatContainer = this.state.isLandscape ? styles.chatLandscapeContainer : styles.chatPortraitContainer;
let showSubtitle = (showActions || this.state.isTablet || !description);
let label = this.state.contact.label ? (" (" + this.state.contact.label + ")" ) : '';
if (this.state.contact.lastMessage) {
subtitle = this.state.contact.lastMessage.split("\n")[0];
//description = description + ': ' + this.state.contact.lastMessage;
} else {
subtitle = subtitle + label;
}
let unread = (this.state.contact && this.state.contact.unread && !this.state.selectMode) ? this.state.contact.unread.length : 0;
let selectCircle = this.state.contact.selected ? 'check-circle' : 'circle-outline';
+ let recordIcon = this.state.recording ? 'pause' : 'microphone';
+ let recordStyle = this.state.recording ? styles.greenButton : styles.greenButton;
+
+ if (this.state.audioRecording) {
+ recordIcon = 'delete';
+ recordStyle = styles.redButton;
+ }
+
+ let titlePadding = styles.titlePadding;
+
+ if (this.state.fontScale < 1) {
+ titlePadding = styles.titlePaddingBig;
+ } else if (this.state.fontScale > 1.2) {
+ titlePadding = styles.titlePaddingSmall;
+ }
+
return (
{this.setTargetUri(uri, this.state.contact)}}
>
{ this.state.contact.photo || ! this.state.contact.email ?
:
}
- {title}
- {subtitle}
- {this.state.fontScale <= 1 ?
+ {title}
+ {callButtonsEnabled ?
+
+
+
+
+ {!this.state.recording && !this.state.audioRecording ?
+
+
+
+ : null
+ }
+
+ { this.state.recording && !this.state.audioRecording ?
+ Recording audio...
+ : null
+ }
+
+ {!this.state.recording && !this.state.audioRecording ?
+
+
+
+ : null}
+
+ {!this.state.recording && this.state.audioRecording ?
+
+
+
+ : null
+ }
+
+
+ : {subtitle}}
+
+ {this.state.fontScale <= 1 && !callButtonsEnabled ?
{this.state.contact.direction ?
: null}
{description}
: null}
{participantsData && participantsData.length && showActions && false?
item.id}
key={item => item.id}
/>
: null}
+
{ this.state.selectMode ?
:
- {contact_ts}
+ {contact_ts}
}
+
{unread ?
: null
}
+
-
- {showActions && false ?
-
-
- {showBlockButton? : null}
- {showBlockDomainButton? : null}
-
-
- : null}
);
}
}
ContactCard.propTypes = {
id : PropTypes.string,
contact : PropTypes.object,
setTargetUri : PropTypes.func,
chat : PropTypes.bool,
orientation : PropTypes.string,
isTablet : PropTypes.bool,
isLandscape : PropTypes.bool,
contacts : PropTypes.array,
defaultDomain : PropTypes.string,
accountId : PropTypes.string,
favoriteUris : PropTypes.array,
messages : PropTypes.array,
pinned : PropTypes.bool,
unread : PropTypes.array,
- toggleBlocked : PropTypes.func,
sendPublicKey : PropTypes.func,
fontScale : PropTypes.number,
- selectMode : PropTypes.bool
+ selectMode : PropTypes.bool,
+ startCall : PropTypes.func,
+ recordAudio : PropTypes.func,
+ sendAudio : PropTypes.func,
+ deleteAudio : PropTypes.func,
+ recording : PropTypes.bool,
+ audioRecording : PropTypes.string
};
export default ContactCard;
diff --git a/app/components/ContactsListBox.js b/app/components/ContactsListBox.js
index 59a318c..0181873 100644
--- a/app/components/ContactsListBox.js
+++ b/app/components/ContactsListBox.js
@@ -1,2298 +1,2363 @@
import React, { Component} from 'react';
import autoBind from 'auto-bind';
import PropTypes from 'prop-types';
import { Image, Clipboard, Dimensions, SafeAreaView, View, FlatList, Text, Linking, PermissionsAndroid, Switch, TouchableOpacity, BackHandler, TouchableHighlight} from 'react-native';
import ContactCard from './ContactCard';
import utils from '../utils';
import DigestAuthRequest from 'digest-auth-request';
import uuid from 'react-native-uuid';
import { GiftedChat, IMessage, Bubble, MessageText, Send, InputToolbar, MessageImage, Time} from 'react-native-gifted-chat'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
import MessageInfoModal from './MessageInfoModal';
import EditMessageModal from './EditMessageModal';
import ShareMessageModal from './ShareMessageModal';
import DeleteMessageModal from './DeleteMessageModal';
import CustomChatActions from './ChatActions';
import FileViewer from 'react-native-file-viewer';
import OpenPGP from "react-native-fast-openpgp";
import DocumentPicker from 'react-native-document-picker';
import AudioRecorderPlayer from 'react-native-audio-recorder-player';
import VideoPlayer from 'react-native-video-player';
import RNFetchBlob from "rn-fetch-blob";
import { IconButton} from 'react-native-paper';
import ImageViewer from 'react-native-image-zoom-viewer';
import fileType from 'react-native-file-type';
import path from 'react-native-path';
import Sound from 'react-native-sound';
import SoundPlayer from 'react-native-sound-player';
import moment from 'moment';
import momenttz from 'moment-timezone';
import Video from 'react-native-video';
const RNFS = require('react-native-fs');
import CameraRoll from "@react-native-community/cameraroll";
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
+import AudioRecord from 'react-native-audio-record';
import styles from '../assets/styles/blink/_ContactsListBox.scss';
String.prototype.toDate = function(format)
{
var normalized = this.replace(/[^a-zA-Z0-9]/g, '-');
var normalizedFormat= format.toLowerCase().replace(/[^a-zA-Z0-9]/g, '-');
var formatItems = normalizedFormat.split('-');
var dateItems = normalized.split('-');
var monthIndex = formatItems.indexOf("mm");
var dayIndex = formatItems.indexOf("dd");
var yearIndex = formatItems.indexOf("yyyy");
var hourIndex = formatItems.indexOf("hh");
var minutesIndex = formatItems.indexOf("ii");
var secondsIndex = formatItems.indexOf("ss");
var today = new Date();
var year = yearIndex>-1 ? dateItems[yearIndex] : today.getFullYear();
var month = monthIndex>-1 ? dateItems[monthIndex]-1 : today.getMonth()-1;
var day = dayIndex>-1 ? dateItems[dayIndex] : today.getDate();
var hour = hourIndex>-1 ? dateItems[hourIndex] : today.getHours();
var minute = minutesIndex>-1 ? dateItems[minutesIndex] : today.getMinutes();
var second = secondsIndex>-1 ? dateItems[secondsIndex] : today.getSeconds();
return new Date(year,month,day,hour,minute,second);
};
const audioRecorderPlayer = new AudioRecorderPlayer();
+const options = {
+ sampleRate: 16000, // default 44100
+ channels: 1, // 1 or 2, default 1
+ bitsPerSample: 16, // 8 or 16, default 16
+ audioSource: 6, // android only (see below)
+ wavFile: 'sylk-audio-recording.wav' // default 'audio.wav'
+};
+
+AudioRecord.init(options);
+
// Note: copy and paste all styles in App.js from my repository
function renderBubble (props) {
let leftColor = 'green';
let rightColor = '#fff';
if (props.currentMessage.failed) {
rightColor = 'red';
leftColor = 'red';
} else {
if (props.currentMessage.pinned) {
rightColor = '#2ecc71';
leftColor = '#2ecc71';
}
}
if (props.currentMessage.image) {
return (
)
} else if (props.currentMessage.video) {
return (
)
} else if (props.currentMessage.audio) {
return (
)
} else {
return (
)
}
}
class ContactsListBox extends Component {
constructor(props) {
super(props);
autoBind(this);
this.chatListRef = React.createRef();
this.default_placeholder = 'Enter message...'
let renderMessages = [];
if (this.props.selectedContact) {
let uri = this.props.selectedContact.uri;
if (uri in this.props.messages) {
renderMessages = this.props.messages[uri];
//renderMessages.sort((a, b) => (a.createdAt < b.createdAt) ? 1 : -1);
renderMessages = renderMessages.sort(function(a, b) {
if (a.createdAt < b.createdAt) {
return 1; //nameA comes first
}
if (a.createdAt > b.createdAt) {
return -1; // nameB comes first
}
if (a.createdAt === b.createdAt) {
if (a.msg_id < b.msg_id) {
return 1; //nameA comes first
}
if (a.msg_id > b.msg_id) {
return -1; // nameB comes first
}
}
return 0; // names must be equal
});
}
}
this.state = {
accountId: this.props.account ? this.props.account.id : null,
password: this.props.password,
targetUri: this.props.selectedContact ? this.props.selectedContact.uri : this.props.targetUri,
favoriteUris: this.props.favoriteUris,
blockedUris: this.props.blockedUris,
isRefreshing: false,
isLandscape: this.props.isLandscape,
contacts: this.props.contacts,
myInvitedParties: this.props.myInvitedParties,
refreshHistory: this.props.refreshHistory,
selectedContact: this.props.selectedContact,
myContacts: this.props.myContacts,
messages: this.props.messages,
renderMessages: GiftedChat.append(renderMessages, []),
chat: this.props.chat,
pinned: false,
message: null,
inviteContacts: this.props.inviteContacts,
shareToContacts: this.props.shareToContacts,
selectMode: this.props.shareToContacts || this.props.inviteContacts,
selectedContacts: this.props.selectedContacts,
pinned: this.props.pinned,
filter: this.props.filter,
periodFilter: this.props.periodFilter,
scrollToBottom: true,
messageZoomFactor: this.props.messageZoomFactor,
isTyping: false,
isLoadingEarlier: false,
fontScale: this.props.fontScale,
call: this.props.call,
isTablet: this.props.isTablet,
ssiCredentials: this.props.ssiCredentials,
ssiConnections: this.props.ssiConnections,
keys: this.props.keys,
recording: false,
playing: false,
texting: false,
audioRecording: null,
cameraAsset: null,
placeholder: this.default_placeholder,
audioSendFinished: false,
messagesCategoryFilter: this.props.messagesCategoryFilter,
isTexting: this.props.isTexting,
showDeleteMessageModal: false
}
this.ended = false;
this.recordingTimer = null;
this.outgoingPendMessages = {};
BackHandler.addEventListener('hardwareBackPress', this.backPressed);
this.listenforSoundNotifications()
}
componentDidMount() {
this.ended = false;
}
componentWillUnmount() {
this.ended = true;
this.stopRecordingTimer()
}
backPressed() {
this.stopRecordingTimer()
}
//getDerivedStateFromProps(nextProps, state) {
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.ended) {
return;
}
if (nextProps.myInvitedParties !== this.state.myInvitedParties) {
this.setState({myInvitedParties: nextProps.myInvitedParties});
}
if (nextProps.contacts !== this.state.contacts) {
this.setState({contacts: nextProps.contacts});
}
if (nextProps.favoriteUris !== this.state.favoriteUris) {
this.setState({favoriteUris: nextProps.favoriteUris});
}
if (nextProps.blockedUris !== this.state.blockedUris) {
this.setState({blockedUris: nextProps.blockedUris});
}
if (nextProps.account !== null && nextProps.account !== this.props.account) {
this.setState({accountId: nextProps.account.id});
}
if (nextProps.refreshHistory !== this.state.refreshHistory) {
this.setState({refreshHistory: nextProps.refreshHistory});
this.getServerHistory();
}
if (nextProps.messageZoomFactor !== this.state.messageZoomFactor) {
this.setState({scrollToBottom: false, messageZoomFactor: nextProps.messageZoomFactor});
}
if (nextProps.messagesCategoryFilter !== this.state.messagesCategoryFilter && nextProps.selectedContact) {
this.props.getMessages(nextProps.selectedContact.uri, {category: nextProps.messagesCategoryFilter, pinned: this.state.pinned});
}
if (nextProps.pinned !== this.state.pinned && nextProps.selectedContact) {
this.props.getMessages(nextProps.selectedContact.uri, {category: nextProps.messagesCategoryFilter, pinned: nextProps.pinned});
}
if (nextProps.selectedContact !== this.state.selectedContact) {
//console.log('Selected contact changed to', nextProps.selectedContact);
this.resetContact()
this.setState({selectedContact: nextProps.selectedContact});
if (nextProps.selectedContact) {
this.setState({scrollToBottom: true});
if (Object.keys(this.state.messages).indexOf(nextProps.selectedContact.uri) === -1 && nextProps.selectedContact) {
this.props.getMessages(nextProps.selectedContact.uri);
}
} else {
this.setState({renderMessages: []});
}
};
if (nextProps.myContacts !== this.state.myContacts) {
this.setState({myContacts: nextProps.myContacts});
};
if (nextProps.selectedContact) {
let renderMessages = [];
let uri = nextProps.selectedContact.uri;
if (uri in nextProps.messages) {
renderMessages = nextProps.messages[uri];
// remove duplicate messages no mater what
renderMessages = renderMessages.filter((v,i,a)=>a.findIndex(v2=>['_id'].every(k=>v2[k] ===v[k]))===i);
if (this.state.renderMessages.length < renderMessages.length) {
//console.log('Number of messages changed', this.state.renderMessages.length, '->', renderMessages.length);
this.setState({isLoadingEarlier: false});
this.props.confirmRead(uri);
if (this.state.renderMessages.length > 0 && renderMessages.length > 0) {
let last_message_ts = this.state.renderMessages[0].createdAt;
if (renderMessages[0].createdAt > last_message_ts) {
this.setState({scrollToBottom: true});
}
}
}
}
let delete_ids = [];
Object.keys(this.outgoingPendMessages).forEach((_id) => {
if (renderMessages.some((obj) => obj._id === _id)) {
//console.log('Remove pending message id', _id);
delete_ids.push(_id);
// message exists
} else {
if (this.state.renderMessages.some((obj) => obj._id === _id)) {
//console.log('Pending message id', _id, 'already exists');
} else {
//console.log('Adding pending message id', _id);
renderMessages.push(this.outgoingPendMessages[_id]);
}
}
});
delete_ids.forEach((_id) => {
delete this.outgoingPendMessages[_id];
});
renderMessages = renderMessages.sort(function(a, b) {
if (a.createdAt < b.createdAt) {
return 1; //nameA comes first
}
if (a.createdAt > b.createdAt) {
return -1; // nameB comes first
}
if (a.createdAt === b.createdAt) {
if (a.msg_id < b.msg_id) {
return 1; //nameA comes first
}
if (a.msg_id > b.msg_id) {
return -1; // nameB comes first
}
}
return 0; // names must be equal
});
this.setState({renderMessages: GiftedChat.append(renderMessages, [])});
if (!this.state.scrollToBottom && renderMessages.length > 0) {
//console.log('Scroll to first message');
//this.scrollToMessage(0);
}
}
this.setState({isLandscape: nextProps.isLandscape,
isTablet: nextProps.isTablet,
chat: nextProps.chat,
fontScale: nextProps.fontScale,
filter: nextProps.filter,
call: nextProps.call,
password: nextProps.password,
messages: nextProps.messages,
inviteContacts: nextProps.inviteContacts,
shareToContacts: nextProps.shareToContacts,
selectedContacts: nextProps.selectedContacts,
pinned: nextProps.pinned,
isTyping: nextProps.isTyping,
periodFilter: nextProps.periodFilter,
ssiCredentials: nextProps.ssiCredentials,
ssiConnections: nextProps.ssiConnections,
messagesCategoryFilter: nextProps.messagesCategoryFilter,
targetUri: nextProps.selectedContact ? nextProps.selectedContact.uri : nextProps.targetUri,
keys: nextProps.keys,
isTexting: nextProps.isTexting,
showDeleteMessageModal: nextProps.showDeleteMessageModal,
selectMode: nextProps.shareToContacts || nextProps.inviteContacts
});
if (nextProps.isTyping) {
setTimeout(() => {
this.setState({isTyping: false});
}, 3000);
}
}
listenforSoundNotifications() {
// Subscribe to event(s) you want when component mounted
this._onFinishedPlayingSubscription = SoundPlayer.addEventListener('FinishedPlaying', ({ success }) => {
//console.log('finished playing', success)
this.setState({playing: false, placeholder: this.default_placeholder});
})
this._onFinishedLoadingSubscription = SoundPlayer.addEventListener('FinishedLoading', ({ success }) => {
//console.log('finished loading', success)
})
this._onFinishedLoadingFileSubscription = SoundPlayer.addEventListener('FinishedLoadingFile', ({ success, name, type }) => {
//console.log('finished loading file', success, name, type)
})
this._onFinishedLoadingURLSubscription = SoundPlayer.addEventListener('FinishedLoadingURL', ({ success, url }) => {
//console.log('finished loading url', success, url)
})
}
async _launchCamera() {
let options = {maxWidth: 2000,
maxHeight: 2000,
mediaType: 'mixed',
quality:0.8,
cameraType: 'front',
formatAsMp4: true
}
const cameraAllowed = await this.props.requestCameraPermission();
if (cameraAllowed) {
await launchCamera(options, this.cameraCallback);
}
}
async _launchImageLibrary() {
let options = {maxWidth: 2000,
maxHeight: 2000,
mediaType: 'mixed',
formatAsMp4: true
}
await launchImageLibrary(options, this.libraryCallback);
}
async libraryCallback(result) {
if (!result.assets || result.assets.length === 0) {
return;
}
result.assets.forEach((asset) => {
this.cameraCallback({assets: [asset]});
});
}
async cameraCallback(result) {
if (!result.assets || result.assets.length === 0) {
return;
}
this.setState({scrollToBottom: true});
let asset = result.assets[0];
asset.preview = true;
let msg = await this.file2GiftedChat(asset);
let assetType = 'file';
if (msg.video) {
assetType = 'movie';
} else if (msg.image) {
assetType = 'photo';
}
this.outgoingPendMessages[msg.metadata.transfer_id] = msg;
this.setState({renderMessages: GiftedChat.append(this.state.renderMessages, [msg]),
cameraAsset: msg,
placeholder: 'Send ' + assetType + ' of ' + utils.beautySize(msg.metadata.filesize)
});
}
renderMessageImage =(props) => {
/*
return(
this.onMessagePress(context, props.currentMessage)}>
);
*/
return (
)
}
renderCustomActions = props =>
(
)
customInputToolbar = props => {
return (
{this.renderComposer}}
containerStyle={styles.chatInsideRightActionsContainer}
/>
);
};
chatInputChanged(text) {
this.setState({texting: (text.length > 0)})
}
+ recordAudio() {
+ if (!this.state.recording) {
+ if (this.state.audioRecording) {
+ this.deleteAudio();
+ } else {
+ this.onStartRecord();
+ }
+ } else {
+ this.onStopRecord();
+ }
+ }
+
+ deleteAudio() {
+ console.log('Delete audio');
+ this.setState({audioRecording: null, recording: false});
+ if (this.recordingStopTimer !== null) {
+ clearTimeout(this.recordingStopTimer);
+ this.recordingStopTimer = null;
+ }
+ }
+
+ async onStartRecord () {
+ console.log('Start recording...');
+ this.setState({recording: true});
+ this.recordingStopTimer = setTimeout(() => {
+ this.stopRecording();
+ }, 20000);
+ AudioRecord.start();
+ };
+
+ stopRecording() {
+ console.log('Stop recording...');
+ this.setState({recording: false});
+ if (this.recordingStopTimer !== null) {
+ clearTimeout(this.recordingStopTimer);
+ this.recordingStopTimer = null;
+ }
+ this.onRecording(false);
+ this.onStopRecord();
+ }
+
+ async onStopRecord () {
+ console.log('Stop recording...');
+ const result = await AudioRecord.stop();
+ this.audioRecorded(result);
+ this.setState({audioRecording: result});
+ };
+
resetContact() {
this.stopRecordingTimer()
this.outgoingPendMessages = {};
this.setState({
recording: false,
texting: false,
audioRecording: null,
cameraAsset: null,
placeholder: this.default_placeholder,
audioSendFinished: false
});
}
renderComposer(props) {
return(
this.setState({ composerText: text })}
text={this.state.composerText}
multiline={true}
placeholderTextColor={'red'}
>
)
}
onRecording(state) {
this.setState({recording: state});
if (state) {
this.startRecordingTimer();
} else {
this.stopRecordingTimer()
}
}
startRecordingTimer() {
let i = 0;
this.setState({placeholder: 'Recording audio'});
this.recordingTimer = setInterval(() => {
i = i + 1
this.setState({placeholder: 'Recording audio ' + i + 's'});
}, 1000);
}
stopRecordingTimer() {
if (this.recordingTimer) {
clearInterval(this.recordingTimer);
this.recordingTimer = null;
this.setState({placeholder: this.default_placeholder});
}
}
updateMessageMetadata(metadata) {
let renderMessages = this.state.renderMessages;
let newRenderMessages = [];
renderMessages.forEach((message) => {
if (metadata.transfer_id === message._id) {
message.metadata = metadata;
}
newRenderMessages.push(message);
});
this.setState({renderMessages: GiftedChat.append(newRenderMessages, [])});
}
async startPlaying(message) {
if (this.state.playing || this.state.recording) {
console.log('Already playing or recording');
return;
}
this.setState({playing: true, placeholder: 'Playing audio message'});
message.metadata.playing = true;
this.updateMessageMetadata(message.metadata);
if (Platform.OS === "android") {
const msg = await audioRecorderPlayer.startPlayer(message.audio);
console.log('Audio playback started', message.audio);
audioRecorderPlayer.addPlayBackListener((e) => {
//console.log('duration', e.duration, e.currentPosition);
if (e.duration === e.currentPosition) {
this.setState({playing: false, placeholder: this.default_placeholder});
//console.log('Audio playback ended', message.audio);
message.metadata.playing = false;
this.updateMessageMetadata(message.metadata);
}
this.setState({
currentPositionSec: e.currentPosition,
currentDurationSec: e.duration,
playTime: audioRecorderPlayer.mmssss(Math.floor(e.currentPosition)),
duration: audioRecorderPlayer.mmssss(Math.floor(e.duration)),
});
});
} else {
/*
console.log('startPlaying', file);
this.sound = new Sound(file, '', error => {
if (error) {
console.log('failed to load the file', file, error);
}
});
return;
*/
try {
SoundPlayer.playUrl('file://'+message.audio);
this.setState({playing: true, placeholder: 'Playing audio message'});
} catch (e) {
console.log(`cannot play the sound file`, e)
}
try {
const info = await SoundPlayer.getInfo() // Also, you need to await this because it is async
console.log('Sound info', info) // {duration: 12.416, currentTime: 7.691}
} catch (e) {
console.log('There is no song playing', e)
}
}
};
async stopPlaying(message) {
console.log('Audio playback ended', message.audio);
this.setState({playing: false, placeholder: this.default_placeholder});
message.metadata.playing = false;
this.updateMessageMetadata(message.metadata);
if (Platform.OS === "android") {
const msg = await audioRecorderPlayer.stopPlayer();
} else {
SoundPlayer.stop();
}
}
async audioRecorded(file) {
const placeholder = file ? 'Delete or send audio...' : this.default_placeholder;
if (file) {
console.log('Audio recording ready to send', file);
} else {
console.log('Audio recording removed');
}
this.setState({recording: false, placeholder: placeholder, audioRecording: file});
}
renderSend = (props) => {
let chatRightActionsContainer = Platform.OS === 'ios' ? styles.chatRightActionsContaineriOS : styles.chatRightActionsContainer;
if (this.state.recording) {
return (
);
} else {
if (this.state.cameraAsset) {
return (
);
} else if (this.state.audioRecording) {
return (
);
} else {
if (this.state.playing || (this.state.selectedContact && this.state.selectedContact.tags.indexOf('test') > -1)) {
return ;
} else {
return (
{this.state.texting ?
null
:
}
{this.state.texting ?
null
:
}
);
}
}
}
};
setTargetUri(uri, contact) {
//console.log('Set target uri uri in history list', uri);
this.props.setTargetUri(uri, contact);
}
setFavoriteUri(uri) {
return this.props.setFavoriteUri(uri);
}
setBlockedUri(uri) {
return this.props.setBlockedUri(uri);
}
renderItem(object) {
let item = object.item || object;
let invitedParties = [];
let uri = item.uri;
let myDisplayName;
let username = uri.split('@')[0];
if (this.state.myContacts && this.state.myContacts.hasOwnProperty(uri)) {
myDisplayName = this.state.myContacts[uri].name;
}
if (this.state.myInvitedParties && this.state.myInvitedParties.hasOwnProperty(username)) {
invitedParties = this.state.myInvitedParties[username];
}
if (myDisplayName) {
if (item.name === item.uri || item.name !== myDisplayName) {
item.name = myDisplayName;
}
}
return(
);
}
findObjectByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
return array[i];
}
}
return null;
}
closeMessageModal() {
this.setState({showMessageModal: false, message: null});
}
closeEditMessageModal() {
this.setState({showEditMessageModal: false, message: null});
}
loadEarlierMessages() {
//console.log('Load earlier messages...');
this.setState({scrollToBottom: false, isLoadingEarlier: true});
this.props.loadEarlierMessages();
}
sendEditedMessage(message, text) {
if (!this.state.selectedContact.uri) {
return;
}
if (message.text === text) {
return;
}
this.props.deleteMessage(message._id, this.state.selectedContact.uri);
message._id = uuid.v4();
message.key = message._id;
message.text = text;
this.props.sendMessage(this.state.selectedContact.uri, message);
}
onSendMessage(messages) {
let uri;
if (!this.state.selectedContact) {
if (this.state.targetUri && this.state.chat) {
let contacts = this.searchedContact(this.state.targetUri);
if (contacts.length !== 1) {
return;
}
uri = contacts[0].uri;
} else {
return;
}
} else {
uri = this.state.selectedContact.uri;
}
messages.forEach((message) => {
/*
sent: true,
// Mark the message as received, using two tick
received: true,
// Mark the message as pending with a clock loader
pending: true,
*/
message.encrypted = this.state.selectedContact && this.state.selectedContact.publicKey ? 2 : 0;
this.props.sendMessage(uri, message);
});
this.setState({renderMessages: GiftedChat.append(this.state.renderMessages, messages)});
}
searchedContact(uri, contact=null) {
if (uri.indexOf(' ') > -1) {
return [];
}
const item = this.props.newContactFunc(uri.toLowerCase(), null, {src: 'search_contact'});
if (!item) {
return [];
}
if (contact) {
item.name = contact.name;
item.photo = contact.photo;
}
return [item];
}
getServerHistory() {
if (!this.state.accountId) {
return;
}
if (this.ended || !this.state.accountId || this.state.isRefreshing) {
return;
}
//console.log('Get server history...');
this.setState({isRefreshing: true});
let history = [];
let localTime;
let getServerCallHistory = new DigestAuthRequest(
'GET',
`${this.props.config.serverCallHistoryUrl}?action=get_history&realm=${this.state.accountId.split('@')[1]}`,
this.state.accountId.split('@')[0],
this.state.password
);
// Disable logging
getServerCallHistory.loggingOn = false;
getServerCallHistory.request((data) => {
if (data.success !== undefined && data.success === false) {
console.log('Error getting call history from server', data.error_message);
return;
}
if (data.received) {
data.received.map(elem => {elem.direction = 'incoming'; return elem});
history = history.concat(data.received);
}
if (data.placed) {
data.placed.map(elem => {elem.direction = 'outgoing'; return elem});
history = history.concat(data.placed);
}
history.sort((a, b) => (a.startTime < b.startTime) ? 1 : -1)
if (history) {
const known = [];
history = history.filter((elem) => {
elem.conference = false;
elem.id = uuid.v4();
if (!elem.tags) {
elem.tags = [];
}
if (elem.remoteParty.indexOf('@conference.') > -1) {
return null;
}
elem.uri = elem.remoteParty.toLowerCase();
let uri_els = elem.uri.split('@');
let username = uri_els[0];
let domain;
if (uri_els.length > 1) {
domain = uri_els[1];
}
if (elem.uri.indexOf('@guest.') > -1) {
if (!elem.displayName) {
elem.uri = 'guest@' + elem.uri.split('@')[1];
} else {
elem.uri = elem.displayName.toLowerCase().replace(/\s|\-|\(|\)/g, '') + '@' + elem.uri.split('@')[1];
}
}
if (utils.isPhoneNumber(elem.uri)) {
username = username.replace(/\s|\-|\(|\)/g, '');
username = username.replace(/^00/, "+");
elem.uri = username;
}
if (known.indexOf(elem.uri) > -1) {
return null;
}
known.push(elem.uri);
if (elem.displayName) {
elem.name = elem.displayName;
} else {
elem.name = elem.uri;
}
if (elem.remoteParty.indexOf('@videoconference.') > -1) {
elem.conference = true;
elem.media = ['audio', 'video', 'chat'];
}
if (elem.uri === this.state.accountId) {
elem.name = this.props.myDisplayName || 'Myself';
}
if (!elem.media || !Array.isArray(elem.media)) {
elem.media = ['audio'];
}
if (elem.timezone !== undefined) {
localTime = momenttz.tz(elem.startTime, elem.timezone).toDate();
elem.startTime = localTime;
elem.timestamp = localTime;
localTime = momenttz.tz(elem.stopTime, elem.timezone).toDate();
elem.stopTime = localTime;
}
if (elem.direction === 'incoming' && elem.duration === 0) {
elem.tags.push('missed');
}
return elem;
});
this.props.saveHistory(history);
if (this.ended) {
return;
}
this.setState({isRefreshing: false});
}
}, (errorCode) => {
console.log('Error getting call history from server', errorCode);
});
this.setState({isRefreshing: false});
}
deleteCameraAsset() {
if (this.state.cameraAsset && this.state.cameraAsset.metadata.transfer_id in this.outgoingPendMessages) {
delete this.outgoingPendMessages[this.state.cameraAsset.metadata.transfer_id]
}
this.setState({cameraAsset: null, placeholder: this.default_placeholder});
this.props.getMessages(this.state.selectedContact.uri);
}
sendCameraAsset() {
this.transferFile(this.state.cameraAsset);
this.setState({cameraAsset: null, placeholder: this.default_placeholder});
}
async sendAudioFile() {
if (this.state.audioRecording) {
this.setState({audioSendFinished: true, placeholder: this.default_placeholder});
setTimeout(() => {
this.setState({audioSendFinished: false});
}, 10);
let msg = await this.file2GiftedChat(this.state.audioRecording);
this.transferFile(msg);
this.setState({audioRecording: null});
}
}
async _pickDocument() {
try {
const result = await DocumentPicker.pick({
type: [DocumentPicker.types.allFiles],
copyTo: 'documentDirectory',
mode: 'import',
allowMultiSelection: false,
});
const fileUri = result[0].fileCopyUri;
if (!fileUri) {
console.log('File URI is undefined or null');
return;
}
let msg = await this.file2GiftedChat(fileUri);
this.transferFile(msg);
} catch (err) {
if (DocumentPicker.isCancel(err)) {
console.log('User cancelled file picker');
} else {
console.log('DocumentPicker err => ', err);
throw err;
}
}
};
postChatSystemMessage(text, imagePath=null) {
var id = uuid.v4();
let giftedChatMessage;
if (imagePath) {
giftedChatMessage = {
_id: id,
key: id,
createdAt: new Date(),
text: text,
image: 'file://' + imagePath,
user: {}
};
} else {
giftedChatMessage = {
_id: id,
key: id,
createdAt: new Date(),
text: text,
system: true,
};
}
this.setState({renderMessages: GiftedChat.append(this.state.renderMessages, [giftedChatMessage])});
}
transferComplete(evt) {
console.log("Upload has finished", evt);
this.postChatSystemMessage('Upload has finished');
}
transferFailed(evt) {
console.log("An error occurred while transferring the file.", evt);
this.postChatSystemMessage('Upload failed')
}
transferCanceled(evt) {
console.log("The transfer has been canceled by the user.");
this.postChatSystemMessage('Upload has canceled')
}
async transferFile(msg) {
msg.metadata.preview = false;
this.props.sendMessage(msg.metadata.receiver.uri, msg, 'application/sylk-file-transfer');
}
async file2GiftedChat(fileObject) {
var id = uuid.v4();
let uri;
if (!this.state.selectedContact) {
if (this.state.targetUri && this.state.chat) {
let contacts = this.searchedContact(this.state.targetUri);
if (contacts.length !== 1) {
return;
- }
+ }
uri = contacts[0].uri;
} else {
return;
}
} else {
uri = this.state.selectedContact.uri;
}
let filepath = fileObject.uri ? fileObject.uri : fileObject;
let basename = fileObject.fileName || filepath.split('\\').pop().split('/').pop();
basename = basename.replace(/\s|:/g, '_');
let file_transfer = { 'path': filepath,
'filename': basename,
'sender': {'uri': this.state.accountId},
'receiver': {'uri': uri},
'transfer_id': id,
'direction': 'outgoing'
};
if (filepath.startsWith('content://')) {
// on android we must copy this file early
const localPath = RNFS.DocumentDirectoryPath + "/" + this.state.accountId + "/" + uri + "/" + id + "/" + basename;
const dirname = path.dirname(localPath);
await RNFS.mkdir(dirname);
console.log('Copy', filepath, localPath);
await RNFS.copyFile(filepath, localPath);
filepath = localPath;
file_transfer.local_url = localPath;
}
let stats_filename = filepath.startsWith('file://') ? filepath.substr(7, filepath.length - 1) : filepath;
const { size } = await RNFetchBlob.fs.stat(stats_filename);
file_transfer.filesize = fileObject.fileSize || size;
if (fileObject.preview) {
file_transfer.preview = fileObject.preview;
}
if (fileObject.duration) {
file_transfer.duration = fileObject.duration;
}
if (fileObject.fileType) {
file_transfer.filetype = fileObject.fileType;
} else {
try {
let mime = await fileType(filepath);
if (mime.mime) {
file_transfer.filetype = mime.mime;
}
} catch (e) {
console.log('Error getting mime type', e.message);
}
}
let text = utils.beautyFileNameForBubble(file_transfer);
let msg = {
_id: id,
key: id,
text: text,
metadata: file_transfer,
createdAt: new Date(),
direction: 'outgoing',
user: {}
}
if (utils.isImage(basename)) {
msg.image = filepath;
} else if (utils.isAudio(basename)) {
msg.audio = filepath;
} else if (utils.isVideo(basename) || file_transfer.duration) {
msg.video = filepath;
}
return msg;
}
matchContact(contact, filter='', tags=[]) {
if (!contact) {
return false;
}
if (tags.indexOf('conference') > -1 && contact.conference) {
return true;
}
if (tags.length > 0 && !tags.some(item => contact.tags.includes(item))) {
return false;
}
if (contact.name && contact.name.toLowerCase().indexOf(filter.toLowerCase()) > -1) {
return true;
}
if (contact.uri.toLowerCase().startsWith(filter.toLowerCase())) {
return true;
}
if (!this.state.selectedContact && contact.conference && contact.metadata && filter.length > 2 && contact.metadata.indexOf(filter) > -1) {
return true;
}
return false;
}
noChatInputToolbar () {
return null;
}
onMessagePress(context, message) {
if (message.metadata && message.metadata.filename) {
//console.log('File metadata', message.metadata);
let file_transfer = message.metadata;
if (!file_transfer.local_url) {
if (!file_transfer.path) {
// this was a local created upload, don't download as the file has not yet been uploaded
this.props.downloadFunc(message.metadata, true);
}
return;
}
RNFS.exists(file_transfer.local_url).then((exists) => {
if (exists) {
if (file_transfer.local_url.endsWith('.asc')) {
if (file_transfer.error === 'decryption failed') {
this.onLongMessagePress(context, message);
} else {
this.props.decryptFunc(message.metadata);
}
} else {
this.onLongMessagePress(context, message);
//this.openFile(message)
}
} else {
if (file_transfer.path) {
// this was a local created upload, don't download as the file has not yet been uploaded
this.onLongMessagePress(context, message);
} else {
this.props.downloadFunc(message.metadata, true);
}
}
});
} else {
this.onLongMessagePress(context, message);
}
}
openFile(message) {
let file_transfer = message.metadata;
let file_path = file_transfer.local_url;
if (!file_path) {
console.log('Cannot open empty path');
return;
}
if (file_path.endsWith('.asc')) {
file_path = file_path.slice(0, -4);
console.log('Open decrypted file', file_path)
} else {
console.log('Open file', file_path)
}
if (utils.isAudio(file_transfer.filename)) {
// this.startPlaying(file_path);
return;
}
RNFS.exists(file_path).then((exists) => {
if (exists) {
FileViewer.open(file_path, { showOpenWithDialog: true })
.then(() => {
// success
})
.catch(error => {
// error
});
} else {
console.log(file_path, 'does not exist');
return;
}
});
}
onLongMessagePress(context, currentMessage) {
if (!currentMessage.metadata) {
currentMessage.metadata = {};
}
//console.log('currentMessage metadata', currentMessage.metadata);
if (currentMessage && currentMessage.text) {
let isSsiMessage = this.state.selectedContact && this.state.selectedContact.tags.indexOf('ssi') > -1;
let options = []
if (currentMessage.metadata && !currentMessage.metadata.error) {
if (!isSsiMessage && this.isMessageEditable(currentMessage)) {
options.push('Edit');
}
if (currentMessage.metadata && currentMessage.metadata.local_url) {
options.push('Open')
//
} else {
options.push('Copy');
}
}
if (!isSsiMessage) {
options.push('Delete');
}
let showResend = currentMessage.failed;
if (currentMessage.metadata && currentMessage.metadata.error === 'decryption failed') {
showResend = false;
}
if (this.state.targetUri.indexOf('@videoconference') === -1) {
if (currentMessage.direction === 'outgoing') {
if (showResend) {
options.push('Resend')
}
}
}
if (currentMessage.pinned) {
options.push('Unpin');
} else {
if (!isSsiMessage && !currentMessage.metadata.error) {
options.push('Pin');
}
}
//options.push('Info');
if (!isSsiMessage && !currentMessage.metadata.error) {
options.push('Forward');
}
if (!isSsiMessage && !currentMessage.metadata.error) {
options.push('Share');
}
if (currentMessage.local_url) {
if (utils.isImage(currentMessage.local_url)) {
options.push('Save');
}
options.push('Open');
}
if (currentMessage.metadata && currentMessage.metadata.filename) {
if (!currentMessage.metadata.filename.local_url || currentMessage.metadata.filename.error === 'decryption failed') {
if (currentMessage.metadata.direction !== 'outgoing') {
options.push('Download again');
}
} else {
options.push('Download');
}
}
options.push('Cancel');
let l = options.length - 1;
context.actionSheet().showActionSheetWithOptions({options, l}, (buttonIndex) => {
let action = options[buttonIndex];
if (action === 'Copy') {
Clipboard.setString(currentMessage.text);
} else if (action === 'Delete') {
this.setState({showDeleteMessageModal: true, currentMessage: currentMessage});
} else if (action === 'Pin') {
this.props.pinMessage(currentMessage._id);
} else if (action === 'Unpin') {
this.props.unpinMessage(currentMessage._id);
} else if (action === 'Info') {
this.setState({message: currentMessage, showMessageModal: true});
} else if (action === 'Edit') {
this.setState({message: currentMessage, showEditMessageModal: true});
} else if (action.startsWith('Share')) {
this.setState({message: currentMessage, showShareMessageModal: true});
} else if (action.startsWith('Forward')) {
this.props.forwardMessageFunc(currentMessage, this.state.targetUri);
} else if (action === 'Resend') {
this.props.reSendMessage(currentMessage, this.state.targetUri);
} else if (action === 'Save') {
this.savePicture(currentMessage.local_url);
} else if (action.startsWith('Download')) {
console.log('Starting download...');
this.props.downloadFunc(currentMessage.metadata, true);
} else if (action === 'Open') {
FileViewer.open(currentMessage.metadata.local_url, { showOpenWithDialog: true })
.then(() => {
// success
})
.catch(error => {
console.log('Failed to open', currentMessage, error.message);
});
}
});
}
};
isMessageEditable(message) {
if (message.direction === 'incoming') {
return false;
}
if (message.image || message.audio || message.video) {
return false;
}
if (message.metadata && message.metadata.filename) {
return false;
}
return true;
}
closeDeleteMessageModal() {
this.setState({showDeleteMessageModal: false});
}
async hasAndroidPermission() {
const permission = PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE;
const hasPermission = await PermissionsAndroid.check(permission);
if (hasPermission) {
return true;
}
const status = await PermissionsAndroid.request(permission);
return status === 'granted';
}
async savePicture(file) {
if (Platform.OS === "android" && !(await this.hasAndroidPermission())) {
return;
}
file = 'file://' + file;
console.log('Save to camera roll', file);
CameraRoll.save(file);
};
shouldUpdateMessage(props, nextProps) {
return true;
}
toggleShareMessageModal() {
this.setState({showShareMessageModal: !this.state.showShareMessageModal});
}
renderMessageVideo(props){
const { currentMessage } = props;
return (
);
};
renderMessageAudio(props){
const { currentMessage } = props;
let playAudioButtonStyle = Platform.OS === 'ios' ? styles.playAudioButtoniOS : styles.playAudioButton;
if (currentMessage.metadata.playing === true) {
return (
this.stopPlaying(currentMessage)}
style={playAudioButtonStyle}
icon="pause"
/>
);
} else {
return (
this.startPlaying(currentMessage)}
style={playAudioButtonStyle}
icon="play"
/>
);
}
};
videoError() {
console.log('Video streaming error');
}
onBuffer() {
console.log('Video buffer error');
}
// https://github.com/FaridSafi/react-native-gifted-chat/issues/571
// add view after bubble
renderMessageText(props) {
const { currentMessage } = props;
if (currentMessage.video || currentMessage.image || currentMessage.audio) {
return (
);
} else {
return (
);
}
};
renderTime = (props) => {
const { currentMessage } = props;
if (currentMessage.metadata && currentMessage.metadata.preview) {
return null;
}
if (currentMessage.video) {
return (
)
} else if (currentMessage.audio) {
return (
)
} else {
return (
)
}
}
scrollToMessage(id) {
//console.log('scrollToMessage', id);
//https://github.com/FaridSafi/react-native-gifted-chat/issues/938
this.chatListRef.current?._messageContainerRef?.current?.scrollToIndex({
animated: true,
index: id
});
}
get showChat() {
if (this.state.selectedContact) {
if (this.state.selectedContact.tags && this.state.selectedContact.tags.indexOf('blocked') > -1) {
return false;
}
if (this.state.selectedContact.uri.indexOf('@guest.') > -1) {
return false;
}
if (this.state.selectedContact.uri.indexOf('anonymous@') > -1) {
return false;
}
}
let username = this.state.targetUri ? this.state.targetUri.split('@')[0] : null;
let isPhoneNumber = username ? username.match(/^(\+|0)(\d+)$/) : false;
if (isPhoneNumber) {
return false;
}
if (this.props.selectedContact) {
return true;
}
return false;
}
ssi2GiftedChat(from_uri, content, timestamp) {
let id = uuid.v4();
let msg;
msg = {
_id: id,
key: id,
text: content,
createdAt: timestamp,
direction: 'incoming',
sent: false,
received: true,
pending: false,
system: false,
failed: false,
user: {_id: from_uri, name: from_uri}
}
return msg;
}
getSsiContacts() {
//console.log('Get SSI contacts');
let contacts = [];
if (this.state.ssiCredentials) {
this.state.ssiCredentials.forEach((item) => {
let contact = this.props.newContactFunc(item.id, 'Credential');
contact.ssiCredential = item;
contact.credential = new Object();
const schemaId = item.metadata.data['_internal/indyCredential'].schemaId;
if (schemaId === 'EwAf16U6ZphXsZq6E5qmPz:2:Bloqzone_IDIN_ver5:5.0') {
contact.schemaId = schemaId;
item.credentialAttributes.forEach((attribute) => {
contact.credential[attribute.name] = attribute.value;
if (attribute.value.length > 0) {
if (attribute.name === 'legalName') {
contact.name = attribute.value;
} else if (attribute.name === 'acceptDateTime') {
contact.timestamp = attribute.value.toDate("dd-mm-yy hh:ii:ss");
} else if (attribute.name === 'createdAt') {
contact.timestamp = attribute.value;
} else if (attribute.name === 'emailAddress') {
contact.email = attribute.value;
}
}
});
}
if (contact.credential.initials) {
contact.name = contact.credential.initials;
}
if (contact.credential.legalName) {
contact.name = contact.name + ' ' + contact.credential.legalName;
}
if (contact.credential.dob) {
contact.name = contact.name + ' (' + contact.credential.dob + ')';
}
if (contact.credential.birthDate) {
contact.name = contact.name + ' (' + contact.credential.birthDate + ')';
}
if (contact.credential.acceptDateTime && item.state === 'done') {
contact.lastMessage = 'Credential issued at ' + contact.credential.acceptDateTime + ' (' + item.state + ')';
}
contact.tags.push('ssi');
contact.tags.push('ssi-credential');
contact.tags.push('readonly');
contacts.push(contact);
});
}
if (this.state.ssiConnections) {
this.state.ssiConnections.forEach((item) => {
//console.log('Contacts SSI connection', item);
let uri = item.id;
let contact = this.props.newContactFunc(uri, item.theirLabel);
contact.credential = new Object();
contact.timestamp = item.createdAt;
contact.lastMessage = 'Connection is ' + item.state;
contact.tags.push('ssi');
contact.tags.push('ssi-connection');
contact.ssiConnection = item;
contacts.push(contact);
});
}
return contacts;
}
render() {
let searchExtraItems = [];
let items = [];
let matchedContacts = [];
let ssiContacts = [];
let messages = this.state.renderMessages;
let contacts = [];
//console.log('----');
//console.log('--- Render contacts with filter', this.state.filter);
//console.log('--- Render contacts', this.state.selectedContact);
//console.log(this.state.renderMessages);
if (this.state.filter === 'ssi') {
contacts = this.getSsiContacts();
} else {
Object.keys(this.state.myContacts).forEach((uri) => {
contacts.push(this.state.myContacts[uri]);
});
}
//console.log(contacts);
let chatInputClass = this.customInputToolbar;
if (this.state.selectedContact) {
if (this.state.selectedContact.uri.indexOf('@videoconference') > -1) {
chatInputClass = this.noChatInputToolbar;
}
if (this.state.selectedContact.tags.indexOf('test') > -1) {
chatInputClass = this.noChatInputToolbar;
}
} else if (!this.state.chat) {
chatInputClass = this.noChatInputToolbar;
}
if (!this.state.selectedContact && this.state.filter) {
items = contacts.filter(contact => this.matchContact(contact, this.state.targetUri, [this.state.filter]));
} else {
items = contacts.filter(contact => this.matchContact(contact, this.state.targetUri));
searchExtraItems = searchExtraItems.concat(this.state.contacts);
if (this.state.targetUri && this.state.targetUri.length > 2 && !this.state.selectedContact && !this.state.inviteContacts) {
matchedContacts = searchExtraItems.filter(contact => this.matchContact(contact, this.state.targetUri));
} else if (this.state.selectedContact && this.state.selectedContact.type === 'contact') {
matchedContacts.push(this.state.selectedContact);
} else if (this.state.selectedContact) {
items = [this.state.selectedContact];
}
items = items.concat(matchedContacts);
}
if (this.state.targetUri) {
items = items.concat(this.searchedContact(this.state.targetUri, this.state.selectedContact));
}
if (this.state.filter && this.state.targetUri) {
items = contacts.filter(contact => this.matchContact(contact, this.state.targetUri));
}
const known = [];
items = items.filter((elem) => {
//console.log(elem.uri);
if (this.state.shareToContacts && elem.tags.indexOf('test') > -1) {
//console.log('Remove', elem.uri, 'test');
return;
}
if (this.state.inviteContacts && elem.tags.indexOf('conference') > -1 ) {
//console.log('Remove', elem.uri, 'conf');
return;
}
if (this.state.shareToContacts && elem.uri === this.state.accountId) {
//console.log('Remove', elem.uri, 'myself');
return;
}
if (this.state.accountId === elem.uri && elem.tags.length === 0) {
//console.log('Remove', elem.uri, 'no tags');
return;
}
if (this.state.shareToContacts && elem.uri.indexOf('@') === -1) {
//console.log('Remove', elem.uri, 'no @');
return;
}
if (known.indexOf(elem.uri) <= -1) {
known.push(elem.uri);
return elem;
}
});
items.forEach((item) => {
item.showActions = false;
if (item.uri.indexOf('@videoconference.') === -1) {
item.conference = false;
} else {
item.conference = true;
}
if (this.state.selectedContacts && this.state.selectedContacts.indexOf(item.uri) > -1) {
item.selected = true;
} else {
item.selected = false;
}
});
let filteredItems = [];
items.reverse();
var todayStart = new Date();
todayStart.setHours(0,0,0,0);
var yesterdayStart = new Date();
yesterdayStart.setDate(todayStart.getDate() - 2);
yesterdayStart.setHours(0,0,0,0);
items.forEach((item) => {
const fromDomain = '@' + item.uri.split('@')[1];
if (this.state.periodFilter === 'today') {
if(item.timestamp < todayStart) {
return;
}
}
if (item.uri === 'anonymous@anonymous.invalid' && this.state.filter !== 'blocked') {
return;
}
if (this.state.periodFilter === 'yesterday') {
if(item.timestamp < yesterdayStart || item.timestamp > todayStart) {
return;
}
}
if (this.state.inviteContacts && item.uri.indexOf('@videoconference.') > -1) {
return;
}
if (item.uri === this.state.accountId && !item.direction) {
return;
}
if (this.state.filter && item.tags.indexOf(this.state.filter) > -1) {
filteredItems.push(item);
} else if (this.state.blockedUris.indexOf(item.uri) === -1 && this.state.blockedUris.indexOf(fromDomain) === -1) {
filteredItems.push(item);
}
//console.log(item.uri, item.tags);
});
items = filteredItems;
items.sort((a, b) => (a.timestamp < b.timestamp) ? 1 : -1)
if (items.length === 1) {
items[0].showActions = true;
if (items[0].tags.indexOf('ssi-credential') > -1) {
let content = '';
let m;
chatInputClass = this.noChatInputToolbar;
items[0].ssiCredential.credentialAttributes.forEach((attribute) => {
content = content + attribute.name + ": " + attribute.value + '\n';
});
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
m = this.ssi2GiftedChat(items[0].uri, 'SSI credential body' , items[0].timestamp);
m.system = true;
messages.push(m);
content = '';
content = content + 'Id: ' + items[0].ssiCredential.id;
content = content + '\nState: ' + items[0].ssiCredential.state;
content = content + '\nSchema Id:' + items[0].schemaId;
let issuer = this.state.ssiConnections.filter(x => x.id === items[0].ssiCredential.connectionId);
if (issuer.length === 1) {
content = content + '\nIssuer: ' + issuer[0].theirLabel;
} else {
content = content + '\nIssuer: : ' + items[0].ssiCredential.connectionId;
}
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
m = this.ssi2GiftedChat(items[0].uri, 'SSI credential details' , items[0].timestamp);
m.system = true;
messages.push(m);
}
if (items[0].tags.indexOf('ssi-connection') > -1) {
let content = '';
let m;
m = this.ssi2GiftedChat(items[0].uri, 'SSI messages' , items[0].timestamp);
m.system = true;
messages.push(m);
chatInputClass = this.noChatInputToolbar;
content = 'Role: ' + items[0].ssiConnection.role;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
content = 'State: ' + items[0].ssiConnection.state;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
content = 'Multiple use: ' + items[0].ssiConnection.multiUseInvitation;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
if (items[0].ssiConnection.mediatorId) {
content = 'Mediator: ' + items[0].ssiConnection.mediatorId;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
}
content = 'Id: ' + items[0].ssiConnection.id;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
content = 'Did: ' + items[0].ssiConnection.did;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
content = 'From: ' + items[0].ssiConnection.theirLabel;
m = this.ssi2GiftedChat(items[0].uri, content.trim(), items[0].timestamp);
messages.push(m);
m = this.ssi2GiftedChat(items[0].uri, 'SSI connection details' , items[0].timestamp);
m.system = true;
messages.push(m);
}
}
let columns = 1;
if (this.state.isTablet) {
columns = this.props.orientation === 'landscape' ? 3 : 2;
} else {
columns = this.props.orientation === 'landscape' ? 2 : 1;
}
const chatContainer = this.props.orientation === 'landscape' ? styles.chatLandscapeContainer : styles.chatPortraitContainer;
const container = this.props.orientation === 'landscape' ? styles.landscapeContainer : styles.portraitContainer;
const contactsContainer = this.props.orientation === 'landscape' ? styles.contactsLandscapeContainer : styles.contactsPortraitContainer;
const borderClass = (messages.length > 0 && !this.state.chat) ? styles.chatBorder : null;
let pinned_messages = [];
if (this.state.pinned) {
messages.forEach((m) => {
if (m.pinned) {
pinned_messages.push(m);
}
});
messages = pinned_messages;
if (pinned_messages.length === 0) {
let msg = {
_id: uuid.v4(),
text: 'No pinned messages found. Touch individual messages to pin them.',
system: true
}
pinned_messages.push(msg);
}
}
let showLoadEarlier = (this.state.myContacts && this.state.selectedContact && this.state.selectedContact.uri in this.state.myContacts && this.state.myContacts[this.state.selectedContact.uri].totalMessages && this.state.myContacts[this.state.selectedContact.uri].totalMessages > messages.length) ? true: false;
messages.forEach((m) => {
});
return (
{items.length === 1 ?
(this.renderItem(items[0]))
:
item.id}
key={this.props.orientation}
loadEarlier
/>
}
{this.showChat && !this.state.inviteContacts?
this.chatInputChanged(text)}
/>
: (items.length === 1) ?
{ return null }}
renderBubble={renderBubble}
renderMessageText={this.renderMessageText}
renderMessageImage={this.renderMessageImage}
renderMessageAudio={this.renderMessageAudio}
renderMessageVideo={this.renderMessageVideo}
onSend={this.onSendMessage}
lockStyle={styles.lock}
onLongPress={this.onLongMessagePress}
shouldUpdateMessage={this.shouldUpdateMessage}
onPress={this.onMessagePress}
scrollToBottom={this.state.scrollToBottom}
inverted={true}
timeTextStyle={{ left: { color: 'red' }, right: { color: 'black' } }}
infiniteScroll
loadEarlier={showLoadEarlier}
onLoadEarlier={this.loadEarlierMessages}
/>
: null
}
);
}
}
ContactsListBox.propTypes = {
account : PropTypes.object,
password : PropTypes.string.isRequired,
config : PropTypes.object.isRequired,
targetUri : PropTypes.string,
selectedContact : PropTypes.object,
contacts : PropTypes.array,
chat : PropTypes.bool,
orientation : PropTypes.string,
setTargetUri : PropTypes.func,
isTablet : PropTypes.bool,
isLandscape : PropTypes.bool,
refreshHistory : PropTypes.bool,
saveHistory : PropTypes.func,
myDisplayName : PropTypes.string,
myPhoneNumber : PropTypes.string,
setFavoriteUri : PropTypes.func,
saveConference : PropTypes.func,
myInvitedParties: PropTypes.object,
setBlockedUri : PropTypes.func,
favoriteUris : PropTypes.array,
blockedUris : PropTypes.array,
filter : PropTypes.string,
periodFilter : PropTypes.string,
defaultDomain : PropTypes.string,
saveContact : PropTypes.func,
myContacts : PropTypes.object,
messages : PropTypes.object,
getMessages : PropTypes.func,
sendMessage : PropTypes.func,
reSendMessage : PropTypes.func,
deleteMessage : PropTypes.func,
pinMessage : PropTypes.func,
unpinMessage : PropTypes.func,
deleteMessages : PropTypes.func,
sendPublicKey : PropTypes.func,
inviteContacts : PropTypes.bool,
shareToContacts : PropTypes.bool,
selectedContacts: PropTypes.array,
toggleBlocked : PropTypes.func,
togglePinned : PropTypes.func,
loadEarlierMessages: PropTypes.func,
newContactFunc : PropTypes.func,
messageZoomFactor: PropTypes.string,
isTyping : PropTypes.bool,
fontScale : PropTypes.number,
call : PropTypes.object,
ssiCredentials : PropTypes.array,
ssiConnections : PropTypes.array,
keys : PropTypes.object,
downloadFunc : PropTypes.func,
decryptFunc : PropTypes.func,
forwardMessageFunc: PropTypes.func,
messagesCategoryFilter: PropTypes.string,
- requestCameraPermission: PropTypes.func
+ requestCameraPermission: PropTypes.func,
+ startCall: PropTypes.func
};
export default ContactsListBox;
exports.renderBubble = renderBubble;
diff --git a/app/components/ReadyBox.js b/app/components/ReadyBox.js
index 45f6970..0afaf6d 100644
--- a/app/components/ReadyBox.js
+++ b/app/components/ReadyBox.js
@@ -1,1106 +1,1105 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import autoBind from 'auto-bind';
import { FlatList, View, Platform, TouchableHighlight, TouchableOpacity} from 'react-native';
import { IconButton, Title, Button, Colors, Text } from 'react-native-paper';
import ConferenceModal from './ConferenceModal';
import ContactsListBox from './ContactsListBox';
import FooterBox from './FooterBox';
import URIInput from './URIInput';
import config from '../config';
import utils from '../utils';
import styles from '../assets/styles/blink/_ReadyBox.scss';
import {Keyboard} from 'react-native';
import QRCodeScanner from 'react-native-qrcode-scanner';
import { RNCamera } from 'react-native-camera';
class ReadyBox extends Component {
constructor(props) {
super(props);
autoBind(this);
this.state = {
targetUri: this.props.selectedContact ? this.props.selectedContact.uri : '',
contacts: this.props.contacts,
selectedContact: this.props.selectedContact,
showConferenceModal: this.props.showConferenceModal,
sticky: false,
favoriteUris: this.props.favoriteUris,
blockedUris: this.props.blockedUris,
historyCategoryFilter: null,
messagesCategoryFilter: null,
historyPeriodFilter: null,
missedCalls: this.props.missedCalls,
isLandscape: this.props.isLandscape,
participants: null,
myInvitedParties: this.props.myInvitedParties,
messages: this.props.messages,
myDisplayName: this.props.myDisplayName,
chat: (this.props.selectedContact !== null) && (this.props.call !== null),
call: this.props.call,
inviteContacts: this.props.inviteContacts,
shareToContacts: this.props.shareToContacts,
selectedContacts: this.props.selectedContacts,
pinned: this.props.pinned,
messageZoomFactor: this.props.messageZoomFactor,
isTyping: this.props.isTyping,
navigationItems: this.props.navigationItems,
fontScale: this.props.fontScale,
historyFilter: this.props.historyFilter,
isTablet: this.props.isTablet,
myContacts: this.props.myContacts,
showQRCodeScanner: this.props.showQRCodeScanner,
ssiCredentials: this.props.ssiCredentials,
ssiConnections: this.props.ssiConnections,
keys: this.props.keys,
isTexting: this.props.isTexting,
keyboardVisible: this.props.keyboardVisible,
contentTypes: this.props.contentTypes,
sourceContact: this.props.sourceContact
};
this.ended = false;
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.ended) {
return;
}
- if (this.state.selectedContact) {
- this.setState({targetUri: nextProps.selectedContact ? nextProps.selectedContact.uri : '', chat: false});
- }
-
if (!this.state.inviteContacts && nextProps.inviteContacts) {
this.handleTargetChange('');
this.setState({chat: false});
}
- if (this.state.selectedContact !== nextProps.selectedContact && nextProps.selectedContact) {
- this.setState({chat: !this.chatDisabledForUri(nextProps.selectedContact.uri)});
+ if (nextProps.selectedContact) {
+ this.setState({chat: !this.chatDisabledForUri(nextProps.selectedContact.uri), targetUri: nextProps.selectedContact.uri});
+ } else {
+ this.setState({chat: false, targetUri: ''});
}
if (nextProps.selectedContact !== this.state.selectedContact) {
this.setState({'messagesCategoryFilter': null});
if (this.navigationRef && !this.state.selectedContact) {
this.navigationRef.scrollToIndex({animated: true, index: 0});
}
if (this.state.selectedContact && this.state.pinned) {
this.props.togglePinned(this.state.selectedContact.uri);
}
}
if (!nextProps.historyFilter && this.state.historyFilter) {
this.filterHistory(null);
}
if (nextProps.historyFilter === 'ssi' && !nextProps.selectedContact) {
this.setState({'historyCategoryFilter': 'ssi'});
if (this.navigationRef && !this.state.selectedContact) {
this.navigationRef.scrollToIndex({animated: true, index: this.navigationItems.length-1});
}
}
if (nextProps.missedCalls.length === 0 && this.state.historyCategoryFilter === 'missed') {
this.setState({'historyCategoryFilter': null});
}
if (nextProps.blockedUris.length === 0 && this.state.historyCategoryFilter === 'blocked') {
this.setState({'historyCategoryFilter': null});
}
if (nextProps.favoriteUris.length === 0 && this.state.historyCategoryFilter === 'favorite') {
this.setState({'historyCategoryFilter': null});
}
if (Object.keys(this.state.myContacts).length === 0 && nextProps.myContacts && Object.keys(nextProps.myContacts).length > 0) {
this.bounceNavigation();
}
let newMyContacts = nextProps.myContacts;
if (nextProps.sourceContact && nextProps.sourceContact.uri in newMyContacts) {
console.log('Discard contact', nextProps.sourceContact.uri);
delete newMyContacts[nextProps.sourceContact.uri];
}
this.setState({myInvitedParties: nextProps.myInvitedParties,
myContacts: newMyContacts,
messages: nextProps.messages,
historyFilter: nextProps.historyFilter,
myDisplayName: nextProps.myDisplayName,
call: nextProps.call,
showConferenceModal: nextProps.showConferenceModal,
isTyping: nextProps.isTyping,
navigationItems: nextProps.navigationItems,
messageZoomFactor: nextProps.messageZoomFactor,
contacts: nextProps.contacts,
inviteContacts: nextProps.inviteContacts,
shareToContacts: nextProps.shareToContacts,
selectedContacts: nextProps.selectedContacts,
selectedContact: nextProps.selectedContact,
pinned: nextProps.pinned,
favoriteUris: nextProps.favoriteUris,
blockedUris: nextProps.blockedUris,
missedCalls: nextProps.missedCalls,
fontScale: nextProps.fontScale,
isTablet: nextProps.isTablet,
showQRCodeScanner: nextProps.showQRCodeScanner,
isLandscape: nextProps.isLandscape,
ssiCredentials: nextProps.ssiCredentials,
ssiConnections: nextProps.ssiConnections,
keys: nextProps.keys,
isTexting: nextProps.isTexting,
keyboardVisible: nextProps.keyboardVisible,
contentTypes: nextProps.contentTypes,
sourceContact: nextProps.sourceContact
});
}
getTargetUri(uri) {
return utils.normalizeUri(uri, this.props.defaultDomain);
}
async componentDidMount() {
this.ended = false;
}
componentWillUnmount() {
this.ended = true;
}
filterHistory(filter) {
if (this.ended) {
return;
}
//console.log('filterHistory', filter);
if (this.state.selectedContact) {
if (!filter && this.state.pinned) {
this.props.togglePinned(this.state.selectedContact.uri);
}
if (filter === 'pinned') {
this.props.togglePinned(this.state.selectedContact.uri);
return;
}
if (filter === this.state.messagesCategoryFilter) {
this.setState({'messagesCategoryFilter': null});
} else {
this.setState({'messagesCategoryFilter': filter});
}
return;
}
this.props.filterHistoryFunc(filter);
if (!filter) {
this.setState({'historyPeriodFilter': null, historyCategoryFilter: null});
} else if (filter === 'today' || filter === 'yesterday') {
filter = this.state.historyPeriodFilter === filter ? null : filter;
this.setState({'historyPeriodFilter': filter});
} else {
this.setState({'historyCategoryFilter': filter});
}
this.handleTargetChange('');
}
chatDisabledForUri(uri) {
if (uri.indexOf('@videoconference') > -1) {
return true;
}
if (uri.indexOf('@guest') > -1) {
return true;
}
if (uri.indexOf('3333@') > -1) {
return true;
}
if (uri.indexOf('4444@') > -1) {
return true;
}
return false;
}
get showNavigationBar() {
if (this.state.keyboardVisible) {
return;
}
if (this.state.selectedContact) {
//return false;
}
return true;
}
get showSearchBar() {
if (this.state.selectedContact && !this.state.isTablet) {
return false;
}
if (this.state.showQRCodeScanner) {
return false;
}
if (this.state.isTablet || (!this.state.isLandscape && this.state.selectedContact)) {
return true;
}
if (this.state.call && this.state.call.state !== 'incoming' && !this.state.inviteContacts) {
return false;
}
return true;
}
get showButtonsBar() {
if (this.state.historyCategoryFilter === 'blocked') {
return false;
}
if (this.state.historyCategoryFilter === 'ssi') {
return false;
}
if (this.state.showQRCodeScanner) {
return false;
}
if (this.state.isTablet) {
return true;
}
if (this.state.call) {
return true;
}
if (!this.state.targetUri) {
return true;
}
if (this.state.selectedContact) {
if (this.state.isLandscape && !this.state.isTablet) {
return false;
}
return false;
}
return true;
}
handleTargetChange(new_uri, contact) {
//console.log('---handleTargetChange new_uri =', new_uri);
//console.log('handleTargetChange contact =', contact);
if ((this.state.inviteContacts || this.state.shareToContacts) && contact) {
const uri = contact.uri;
this.props.updateSelection(uri);
return;
}
// This URLs are used to request SSI credentials
if (new_uri && new_uri.startsWith('https://didcomm.issuer.bloqzone.com?c_i=')) {
this.props.handleSSIEnrolment(new_uri);
return;
}
// This URLs are used to request SSI credentials
if (new_uri && new_uri.startsWith('https://ssimandate.vismaconnect.nl/api/acapy?c_i=')) {
this.props.handleSSIEnrolment(new_uri);
return;
}
if (contact && contact.tags.indexOf('ssi') > -1 && this.state.selectedContact !== contact) {
this.setState({'historyCategoryFilter': 'ssi'});
}
if (this.state.selectedContact === contact) {
if (this.state.chat) {
this.setState({chat: false});
}
return;
} else {
this.setState({chat: false});
}
let new_value = new_uri;
if (contact) {
if (this.state.targetUri === contact.uri) {
new_value = '';
}
} else {
contact = null;
}
if (this.state.targetUri === new_uri) {
new_value = '';
}
if (new_value === '') {
contact = null;
}
if (new_value.indexOf(' ') === -1) {
new_value = new_value.trim().toLowerCase();
}
//new_value = new_value.replace(' ','');
//console.log('--- Select new contact', contact? contact.uri : null);
//console.log('--- Select new targetUri', new_value);
this.props.selectContact(contact);
this.setState({targetUri: new_value});
}
handleTargetSelect() {
if (this.props.connection === null) {
this.props._notificationCenter.postSystemNotification("Server unreachable");
return;
}
let uri = this.state.targetUri.toLowerCase();
if (uri.endsWith(`@${config.defaultConferenceDomain}`)) {
let participants;
if (this.state.myInvitedParties && this.state.myInvitedParties.hasOwnProperty(uri)) {
participants = this.state.myInvitedParties[uri];
}
this.props.startConference(uri, {audio: true, video: true, participants: this.state.participants});
} else {
this.props.startCall(this.getTargetUri(uri), {audio: true, video: true});
}
}
shareContent() {
this.props.shareContent();
}
showConferenceModal(event) {
event.preventDefault();
this.props.showConferenceModalFunc();
}
handleChat(event) {
event.preventDefault();
let targetUri;
if (!this.state.chat && !this.state.selectedContact) {
targetUri = this.getTargetUri(this.state.targetUri);
this.setState({targetUri: targetUri});
}
let uri = this.state.targetUri.trim().toLowerCase();
if (!this.state.chat && !this.selectedContact && uri) {
if (uri.indexOf('@') === -1) {
uri = uri + '@' + this.props.defaultDomain;
}
let contact = this.props.newContactFunc(uri, null, {src: 'new chat'});
console.log('Create synthetic contact', contact);
this.props.selectContact(contact);
this.setState({targetUri: uri, chat: true});
Keyboard.dismiss();
}
this.setState({chat: !this.state.chat});
}
handleAudioCall(event) {
event.preventDefault();
Keyboard.dismiss();
let uri = this.state.targetUri.trim().toLowerCase();
var uri_parts = uri.split("/");
if (uri_parts.length === 5 && uri_parts[0] === 'https:') {
// https://webrtc.sipthor.net/conference/DaffodilFlyChill0 from external web link
// https://webrtc.sipthor.net/call/alice@example.com from external web link
let event = uri_parts[3];
uri = uri_parts[4];
if (event === 'conference') {
uri = uri.split("@")[0] + '@' + config.defaultConferenceDomain;
}
}
if (uri.endsWith(`@${config.defaultConferenceDomain}`)) {
this.props.startConference(uri, {audio: true, video: false});
} else {
this.props.startCall(this.getTargetUri(uri), {audio: true, video: false});
}
}
handleVideoCall(event) {
event.preventDefault();
Keyboard.dismiss();
let uri = this.state.targetUri.toLowerCase();
var uri_parts = uri.split("/");
if (uri_parts.length === 5 && uri_parts[0] === 'https:') {
// https://webrtc.sipthor.net/conference/DaffodilFlyChill0 from external web link
// https://webrtc.sipthor.net/call/alice@example.com from external web link
let event = uri_parts[3];
uri = uri_parts[4];
if (event === 'conference') {
uri = uri.split("@")[0] + '@' + config.defaultConferenceDomain;
}
}
if (uri.endsWith(`@${config.defaultConferenceDomain}`)) {
this.props.startConference(uri, {audio: true, video: true});
} else {
this.props.startCall(this.getTargetUri(uri), {audio: true, video: true});
}
}
handleConferenceCall(targetUri, options={audio: true, video: true, participants: []}) {
Keyboard.dismiss();
this.props.startConference(targetUri, {audio: options.audio, video: options.video, participants: options.participants});
this.props.hideConferenceModalFunc();
}
get chatButtonDisabled() {
let uri = this.state.targetUri.trim();
if (this.state.selectedContact) {
return true;
}
if (this.state.shareToContacts) {
return true;
}
if (!uri || uri.indexOf(' ') > -1 || uri.indexOf('@guest.') > -1 || uri.indexOf('@videoconference') > -1) {
return true;
}
let username = uri.split('@')[0];
let isPhoneNumber = username.match(/^(\+|0)(\d+)$/);
if (isPhoneNumber) {
return true;
}
if (uri.indexOf('@') > -1) {
let email_reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,})+$/;
let validEmail = email_reg.test(uri);
if (!validEmail) {
return true;
}
}
if (this.chatDisabledForUri(uri)) {
return true;
}
return false;
}
get callButtonDisabled() {
let uri = this.state.targetUri.trim();
if (!uri || uri.indexOf(' ') > -1 || uri.indexOf('@guest.') > -1 || uri.indexOf('@videoconference') > -1) {
return true;
}
if (this.state.shareToContacts) {
return true;
}
if (uri.indexOf('@') > -1) {
let email_reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,})+$/;
let validEmail = email_reg.test(uri);
if (!validEmail) {
return true;
}
}
return false;
}
get videoButtonDisabled() {
let uri = this.state.targetUri.trim();
if (!uri || uri.indexOf(' ') > -1 || uri.indexOf('@guest.') > -1 || uri.indexOf('@videoconference') > -1) {
return true;
}
if (uri.indexOf('4444@') > -1) {
return true;
}
if (this.state.shareToContacts) {
return true;
}
let username = uri.split('@')[0];
let isPhoneNumber = username.match(/^(\+|0)(\d+)$/);
if (isPhoneNumber) {
return true;
}
return this.callButtonDisabled;
}
get conferenceButtonDisabled() {
if (!this.props.canSend()) {
return true;
}
let uri = this.state.targetUri.trim();
if (uri.indexOf(' ') > -1) {
return true;
}
if (this.state.shareToContacts) {
return true;
}
let username = uri.split('@')[0];
let isPhoneNumber = username.match(/^(\+|0)(\d+)$/);
if (isPhoneNumber) {
return true;
}
if (uri.indexOf('@') > -1 && uri.indexOf(config.defaultConferenceDomain) === -1) {
return true;
}
var uri_parts = uri.split("/");
if (uri_parts.length === 5 && uri_parts[0] === 'https:') {
// https://webrtc.sipthor.net/conference/DaffodilFlyChill0 from external web link
// https://webrtc.sipthor.net/call/alice@example.com from external web link
let event = uri_parts[3];
if (event === 'call') {
return true;
}
}
return false;
}
renderNavigationItem(object) {
if (!object.item.enabled) {
return (null);
}
let title = object.item.title;
let key = object.item.key;
let buttonStyle = object.item.selected ? styles.navigationButtonSelected : styles.navigationButton;
if (key === "hideQRCodeScanner") {
return ();
}
return ();
}
bounceNavigation() {
if (this.ended) {
return;
}
setTimeout(() => {
if (this.ended) {
return;
}
if (this.navigationRef && !this.state.selectedContact) {
this.navigationRef.scrollToIndex({animated: true, index: Math.floor(this.navigationItems.length / 2)});
}
}, 3000);
setTimeout(() => {
if (this.ended) {
return;
}
if (this.navigationRef && !this.state.selectedContact) {
this.navigationRef.scrollToIndex({animated: true, index: this.navigationItems.length-1});
}
}, 4500);
setTimeout(() => {
if (this.ended) {
return;
}
if (this.navigationRef && !this.state.selectedContact) {
this.navigationRef.scrollToIndex({animated: true, index: 0});
}
}, 6000);
}
get navigationItems() {
let conferenceEnabled = Object.keys(this.state.myInvitedParties).length > 0 || this.state.navigationItems['conference'];
if (this.state.inviteContacts) {
conferenceEnabled = false;
}
if (this.state.showQRCodeScanner) {
return [
{key: "hideQRCodeScanner", title: 'Cancel', enabled: true, selected: false}
];
}
if (this.state.selectedContact) {
let content_items = [];
if ('pinned' in this.state.contentTypes) {
content_items.push({key: 'pinned', title: 'Pinned', enabled: true, selected: this.state.pinned});
}
if ('text' in this.state.contentTypes) {
content_items.push({key: 'text', title: 'Text', enabled: true, selected: this.state.messagesCategoryFilter === 'text'});
}
if ('audio' in this.state.contentTypes) {
content_items.push({key: 'audio', title: 'Audio', enabled: true, selected: this.state.messagesCategoryFilter === 'audio'});
}
if ('image' in this.state.contentTypes) {
content_items.push({key: 'image', title: 'Images', enabled: true, selected: this.state.messagesCategoryFilter === 'image'});
}
if ('movie' in this.state.contentTypes) {
content_items.push({key: 'movie', title: 'Movies', enabled: true, selected: this.state.messagesCategoryFilter === 'movie'});
}
if ('failed' in this.state.contentTypes) {
content_items.push({key: 'failed', title: 'Failed', enabled: true, selected: this.state.messagesCategoryFilter === 'failed'});
}
if ('paused' in this.state.contentTypes) {
content_items.push({key: 'paused', title: 'Paused', enabled: true, selected: this.state.messagesCategoryFilter === 'paused'});
}
if ('large' in this.state.contentTypes) {
content_items.push({key: 'large', title: 'Large', enabled: true, selected: this.state.messagesCategoryFilter === 'large'});
}
return content_items;
}
return [
{key: null, title: 'All', enabled: true, selected: false},
+ {key: 'favorite', title: 'Favorites', enabled: this.state.favoriteUris.length > 0, selected: this.state.historyCategoryFilter === 'favorite'},
{key: 'history', title: 'Calls', enabled: true, selected: this.state.historyCategoryFilter === 'history'},
{key: 'chat', title: 'Chat', enabled: true, selected: this.state.historyCategoryFilter === 'chat'},
{key: 'today', title: 'Today', enabled: this.state.navigationItems['today'], selected: this.state.historyPeriodFilter === 'today'},
{key: 'yesterday', title: 'Yesterday', enabled: this.state.navigationItems['yesterday'], selected: this.state.historyPeriodFilter === 'yesterday'},
{key: 'missed', title: 'Missed', enabled: this.state.missedCalls.length > 0, selected: this.state.historyCategoryFilter === 'missed'},
- {key: 'favorite', title: 'Favorites', enabled: this.state.favoriteUris.length > 0, selected: this.state.historyCategoryFilter === 'favorite'},
{key: 'blocked', title: 'Blocked', enabled: this.state.blockedUris.length > 0, selected: this.state.historyCategoryFilter === 'blocked'},
{key: 'conference', title: 'Conference', enabled: conferenceEnabled, selected: this.state.historyCategoryFilter === 'conference'},
{key: 'test', title: 'Test', enabled: !this.state.shareToContacts && !this.state.inviteContacts, selected: this.state.historyCategoryFilter === 'test'},
{key: 'ssi', title: 'SSI', enabled: (this.state.ssiConnections && this.state.ssiConnections.length > 0) || (this.state.ssiCredentials && this.state.ssiCredentials.length > 0), selected: this.state.historyCategoryFilter === 'ssi'},
];
}
toggleQRCodeScanner(event) {
if (event) {
event.preventDefault();
}
console.log('Scan QR code...');
this.props.toggleQRCodeScannerFunc();
}
QRCodeRead(e) {
//console.log('QR code object:', e);
console.log('QR code data:', e.data);
this.props.toggleQRCodeScannerFunc();
this.handleTargetChange(e.data);
}
get showQRCodeButton() {
if (!this.props.canSend()) {
return false;
}
let uri = this.state.targetUri.toLowerCase();
return uri.length === 0 && !this.state.shareToContacts && !this.state.inviteContacts;
}
render() {
let URIContainerClass = styles.portraitUriInputBox;
let uriGroupClass = styles.portraitUriButtonGroup;
let titleClass = styles.portraitTitle;
let uri = this.state.targetUri.toLowerCase();
var uri_parts = uri.split("/");
if (uri_parts.length === 5 && uri_parts[0] === 'https:') {
// https://webrtc.sipthor.net/conference/DaffodilFlyChill0 from external web link
// https://webrtc.sipthor.net/call/alice@example.com from external web link
let event = uri_parts[3];
uri = uri_parts[4];
if (event === 'conference') {
uri = uri.split("@")[0] + '@' + config.defaultConferenceDomain;
}
}
//console.log('RB', this.state.myContacts);
if (this.state.isTablet) {
titleClass = this.props.orientation === 'landscape' ? styles.landscapeTabletTitle : styles.portraitTabletTitle;
} else {
titleClass = this.props.orientation === 'landscape' ? styles.landscapeTitle : styles.portraitTitle;
}
if (this.state.isTablet) {
uriGroupClass = this.props.orientation === 'landscape' ? styles.landscapeTabletUriButtonGroup : styles.portraitTabletUriButtonGroup;
} else {
uriGroupClass = this.props.orientation === 'landscape' ? styles.landscapeUriButtonGroup : styles.portraitUriButtonGroup;
}
if (this.state.isTablet) {
URIContainerClass = this.props.orientation === 'landscape' ? styles.landscapeTabletUriInputBox : styles.portraitTabletUriInputBox;
} else {
URIContainerClass = this.props.orientation === 'landscape' ? styles.landscapeUriInputBox : styles.portraitUriInputBox;
}
const historyContainer = this.props.orientation === 'landscape' ? styles.historyLandscapeContainer : styles.historyPortraitContainer;
const buttonGroupClass = this.props.orientation === 'landscape' ? styles.landscapeButtonGroup : styles.buttonGroup;
const borderClass = this.state.chat ? null : styles.historyBorder;
let backButtonTitle = 'Back to call';
const showBackToCallButton = this.state.call && this.state.call.state !== 'incoming' && this.state.call.state !== 'terminated' ? true : false ;
if (showBackToCallButton) {
if (this.state.call.hasOwnProperty('_participants')) {
backButtonTitle = this.state.selectedContacts.length > 0 ? 'Invite people' : 'Back to conference';
} else {
backButtonTitle = this.state.selectedContacts.length > 0 ? 'Invite people' : 'Back to call';
}
}
let greenButtonClass = Platform.OS === 'ios' ? styles.greenButtoniOS : styles.greenButton;
let blueButtonClass = Platform.OS === 'ios' ? styles.blueButtoniOS : styles.blueButton;
let disabledGreenButtonClass = Platform.OS === 'ios' ? styles.disabledGreenButtoniOS : styles.disabledGreenButton;
let disabledBlueButtonClass = Platform.OS === 'ios' ? styles.disabledBlueButtoniOS : styles.disabledBlueButton;
return (
{this.showSearchBar && !this.state.isLandscape ?
: null}
{this.showButtonsBar ?
{this.showSearchBar && this.state.isLandscape ?
: null}
{showBackToCallButton ?
:
{ this.showQRCodeButton ?
: null}
}
: null}
{ this.state.showQRCodeScanner && !showBackToCallButton ?
:
}
{this.showNavigationBar ?
{ this.navigationRef = ref; }}
onScrollToIndexFailed={info => {
const wait = new Promise(resolve => setTimeout(resolve, 10));
wait.then(() => {
if (!this.state.selectedContact) {
this.navigationRef.current?.scrollToIndex({ index: info.index, animated: true/false });
}
});
}}
data={this.navigationItems}
extraData={this.state}
keyExtractor={(item, index) => item.key}
renderItem={this.renderNavigationItem}
/>
: null}
{this.state.isTablet && 0?
: null}
);
}
}
ReadyBox.propTypes = {
account : PropTypes.object,
password : PropTypes.string.isRequired,
config : PropTypes.object.isRequired,
startCall : PropTypes.func.isRequired,
startConference : PropTypes.func.isRequired,
contacts : PropTypes.array,
orientation : PropTypes.string,
isTablet : PropTypes.bool,
isLandscape : PropTypes.bool,
refreshHistory : PropTypes.bool,
refreshFavorites: PropTypes.bool,
saveHistory : PropTypes.func,
localHistory : PropTypes.array,
myDisplayName : PropTypes.string,
myPhoneNumber : PropTypes.string,
toggleFavorite : PropTypes.func,
myInvitedParties: PropTypes.object,
toggleBlocked : PropTypes.func,
favoriteUris : PropTypes.array,
blockedUris : PropTypes.array,
defaultDomain : PropTypes.string,
saveContact : PropTypes.func,
selectContact : PropTypes.func,
lookupContacts : PropTypes.func,
call : PropTypes.object,
goBackFunc : PropTypes.func,
messages : PropTypes.object,
sendMessage : PropTypes.func,
reSendMessage : PropTypes.func,
confirmRead : PropTypes.func,
deleteMessage : PropTypes.func,
expireMessage : PropTypes.func,
getMessages : PropTypes.func,
deleteMessages : PropTypes.func,
pinMessage : PropTypes.func,
unpinMessage : PropTypes.func,
sendPublicKey : PropTypes.func,
inviteContacts : PropTypes.bool,
shareToContacts : PropTypes.bool,
showQRCodeScanner : PropTypes.bool,
selectedContacts: PropTypes.array,
updateSelection : PropTypes.func,
loadEarlierMessages: PropTypes.func,
newContactFunc : PropTypes.func,
missedCalls : PropTypes.array,
messageZoomFactor: PropTypes.string,
isTyping: PropTypes.bool,
navigationItems: PropTypes.object,
showConferenceModal: PropTypes.bool,
showConferenceModalFunc: PropTypes.func,
hideConferenceModalFunc: PropTypes.func,
shareContent: PropTypes.func,
fetchSharedItems: PropTypes.func,
filterHistoryFunc: PropTypes.func,
historyFilter: PropTypes.string,
fontScale: PropTypes.number,
inviteToConferenceFunc: PropTypes.func,
toggleQRCodeScannerFunc: PropTypes.func,
myContacts: PropTypes.object,
handleSSIEnrolment: PropTypes.func,
ssiCredentials: PropTypes.array,
ssiConnections: PropTypes.array,
keys : PropTypes.object,
downloadFunc : PropTypes.func,
decryptFunc : PropTypes.func,
isTexting :PropTypes.bool,
keyboardVisible: PropTypes.bool,
filteredMessageIds: PropTypes.array,
contentTypes: PropTypes.object,
canSend: PropTypes.func,
forwardMessageFunc: PropTypes.func,
sourceContact: PropTypes.object,
requestCameraPermission: PropTypes.func
};
export default ReadyBox;