diff --git a/app/components/HistoryCard.js b/app/components/HistoryCard.js
index b8af4cc..9e9c234 100644
--- a/app/components/HistoryCard.js
+++ b/app/components/HistoryCard.js
@@ -1,164 +1,159 @@
-import React from 'react';
+import React, { Component} from 'react';
import { View } from 'react-native';
+import autoBind from 'auto-bind';
import PropTypes from 'prop-types';
import moment from 'moment';
import momentFormat from 'moment-duration-format';
import { Card, IconButton, Caption, Title, Subheading } from 'react-native-paper';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import styles from '../assets/styles/blink/_HistoryCard.scss';
import UserIcon from './UserIcon';
-const HistoryCard = (props) => {
- const identity = {
- displayName: props.historyItem.displayName || props.historyItem.name,
- uri: props.historyItem.remoteParty || props.historyItem.uri,
- type: props.historyItem.type || 'contact',
- photo: props.historyItem.photo,
- label: props.historyItem.label
- }
- //console.log('History card', props.historyItem);
+class HistoryCard extends Component {
+ constructor(props) {
+ super(props);
+ autoBind(this);
+
+ this.identity = {
+ displayName: this.props.contact.displayName || this.props.contact.name,
+ uri: this.props.contact.remoteParty || this.props.contact.uri,
+ type: this.props.contact.type || 'contact',
+ photo: this.props.contact.photo,
+ label: this.props.contact.label
+ }
- const startVideoCall = (e) => {
+ //console.log(this.props.contact);
+ }
+
+ startVideoCall(e) {
e.stopPropagation();
- props.setTargetUri(identity.uri);
+ this.props.setTargetUri(this.identity.uri);
// We need to wait for targetURI
setImmediate(() => {
- props.startVideoCall(e);
+ this.props.startVideoCall(e);
});
}
- const startAudioCall = (e) => {
+ startAudioCall(e) {
e.stopPropagation();
- props.setTargetUri(identity.uri);
+ this.props.setTargetUri(this.identity.uri);
// We need to wait for targetURI
setImmediate(() => {
- props.startAudioCall(e);
+ this.props.startAudioCall(e);
});
}
- let containerClass = styles.portraitContainer;
+ render () {
+ //console.log(this.props.contact.index, 'Render card', this.identity.uri, 'in', this.props.orientation);
- if (props.isTablet) {
- containerClass = (props.orientation === 'landscape') ? styles.landscapeTabletContainer : styles.portraitTabletContainer;
- } else {
- containerClass = (props.orientation === 'landscape') ? styles.landscapeContainer : styles.portraitContainer;
- }
+ let containerClass = styles.portraitContainer;
- let color = {};
-
- const name = identity.displayName || identity.uri;
-
- let title = identity.displayName || identity.uri;
- let subtitle = identity.uri;
-
- if (props.historyItem.conference) {
-// console.log('Item participants', props.historyItem.participants);
- if (props.historyItem.participants) {
- if (props.historyItem.participants.length === 0) {
- subtitle = 'No participants';
- } else if (props.historyItem.participants.length === 1) {
- subtitle = 'One participant';
- } else {
- subtitle = props.historyItem.participants.length + ' participants';
- }
+ if (this.props.isTablet) {
+ containerClass = (this.props.orientation === 'landscape') ? styles.landscapeTabletContainer : styles.portraitTabletContainer;
+ } else {
+ containerClass = (this.props.orientation === 'landscape') ? styles.landscapeContainer : styles.portraitContainer;
}
- }
- let description = props.historyItem.startTime;
+ let color = {};
+ const name = this.identity.displayName || this.identity.uri;
+ let title = this.identity.displayName || this.identity.uri;
+ let subtitle = this.identity.uri;
- if (identity.type === 'history') {
- let duration = moment.duration(props.historyItem.duration, 'seconds').format('hh:mm:ss', {trim: false});
+ let description = this.props.contact.startTime;
- if (props.historyItem.direction === 'received' && props.historyItem.duration === 0) {
- color.color = '#a94442';
- duration = 'missed';
- } else if (props.historyItem.direction === 'placed' && props.historyItem.duration === 0) {
- duration = 'cancelled';
- }
+ if (this.identity.type === 'history') {
+ let duration = moment.duration(this.props.contact.duration, 'seconds').format('hh:mm:ss', {trim: false});
- if (duration) {
- let subtitle = identity.uri + ' (' + duration + ')';
- }
+ if (this.props.contact.direction === 'received' && this.props.contact.duration === 0) {
+ color.color = '#a94442';
+ duration = 'missed';
+ } else if (this.props.contact.direction === 'placed' && this.props.contact.duration === 0) {
+ duration = 'cancelled';
+ }
- if (!identity.displayName) {
- title = identity.uri;
- if (duration === 'missed') {
- subtitle = 'Last call missed';
- } else if (duration === 'cancelled') {
- subtitle = 'Last call cancelled';
- } else {
- subtitle = 'Last call duration ' + duration ;
+ if (duration) {
+ let subtitle = this.identity.uri + ' (' + duration + ')';
+ }
+
+ if (!this.identity.displayName) {
+ title = this.identity.uri;
+ if (duration === 'missed') {
+ subtitle = 'Last call missed';
+ } else if (duration === 'cancelled') {
+ subtitle = 'Last call cancelled';
+ } else {
+ subtitle = 'Last call duration ' + duration ;
+ }
}
- }
- description = description + ' (' + duration + ')';
-
- return (
- {props.setTargetUri(identity.uri)}}
- onLongPress={startVideoCall}
- style={containerClass}
- >
-
-
- {title}
- {subtitle}
-
- {description}
-
-
-
-
-
-
-
- );
-
- } else {
- if (identity.label) {
- subtitle = identity.uri + ' (' + identity.label + ')';
+ description = description + ' (' + duration + ')';
+
+ return (
+ {this.props.setTargetUri(this.identity.uri)}}
+ onLongPress={this.startVideoCall}
+ style={containerClass}
+ >
+
+
+ {title}
+ {subtitle}
+
+ {description}
+
+
+
+
+
+
+
+ );
+
+ } else {
+ return (
+ {this.props.setTargetUri(this.identity.uri, this.props.contact)}}
+ onLongPress={this.startVideoCall}
+ style={containerClass}
+ >
+
+
+ {title}
+ {this.identity.uri}
+
+ {this.identity.label}
+
+
+
+
+
+
+
+ );
}
- return (
- {props.setTargetUri(identity.uri, props.historyItem)}}
- onLongPress={startVideoCall}
- style={containerClass}
- >
-
-
- {title}
- {subtitle}
-
-
-
-
-
-
- );
}
-
/*
*/
}
HistoryCard.propTypes = {
- historyItem : PropTypes.object,
+ contact : PropTypes.object,
startAudioCall : PropTypes.func,
startVideoCall : PropTypes.func,
setTargetUri : PropTypes.func,
orientation : PropTypes.string,
isTablet : PropTypes.bool
};
export default HistoryCard;
diff --git a/app/components/HistoryTileBox.js b/app/components/HistoryTileBox.js
index 890c870..41719a5 100644
--- a/app/components/HistoryTileBox.js
+++ b/app/components/HistoryTileBox.js
@@ -1,61 +1,72 @@
-import React from 'react';
+import React, { Component} from 'react';
+import autoBind from 'auto-bind';
+
import PropTypes from 'prop-types';
import { SafeAreaView, ScrollView, View, FlatList, Text } from 'react-native';
import HistoryCard from './HistoryCard';
+import utils from '../utils';
import styles from '../assets/styles/blink/_HistoryTileBox.scss';
-const HistoryTileBox = (props) => {
-
- const renderItem = ({ item }) => (
-
- );
-
- let columns = 1;
- if (props.isTablet) {
- columns = props.orientation === 'landscape' ? 3 : 2;
- } else {
- columns = props.orientation === 'landscape' ? 2 : 1;
- }
-
- let items = props.historyItems.concat(props.contactItems);
-
-/*
- console.log('History items', props.historyItems);
- console.log('Contacts items', props.contactItems);
- console.log('All items', items);
-*/
-
- return (
-
- item.sessionId}
- key={props.orientation}
- />
-
- );
+class HistoryTileBox extends Component {
+ constructor(props) {
+ super(props);
+ autoBind(this);
+ this.startItem = 0;
+ this.maxItems = 10;
+ }
+
+ renderItem(item) {
+ return(
+ );
+ }
+
+ render() {
+ //utils.timestampedLog('Render history in', this.props.orientation);
+
+ let columns = 1;
+
+ if (this.props.isTablet) {
+ columns = this.props.orientation === 'landscape' ? 3 : 2;
+ } else {
+ columns = this.props.orientation === 'landscape' ? 2 : 1;
+ this.maxItems = this.props.orientation === 'landscape' ? 50 : 8;
+ }
+
+ let allItems = this.props.historyItems.concat(this.props.contactItems);
+ let items = allItems.slice(this.startItem, this.maxItems);
+
+ return (
+
+ item.sessionId}
+ key={this.props.orientation}
+ />
+
+ );
+ }
}
HistoryTileBox.propTypes = {
historyItems : PropTypes.array,
contactItems : PropTypes.array,
- orientation : PropTypes.string,
- startAudioCall : PropTypes.func,
- startVideoCall : PropTypes.func,
- setTargetUri : PropTypes.func,
- isTablet : PropTypes.bool
+ orientation : PropTypes.string,
+ startAudioCall : PropTypes.func,
+ startVideoCall : PropTypes.func,
+ setTargetUri : PropTypes.func,
+ isTablet : PropTypes.bool
};
export default HistoryTileBox;
diff --git a/app/components/ReadyBox.js b/app/components/ReadyBox.js
index 8564a66..2959364 100644
--- a/app/components/ReadyBox.js
+++ b/app/components/ReadyBox.js
@@ -1,251 +1,255 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
// import VizSensor = require('react-visibility-sensor').default;
import autoBind from 'auto-bind';
import { View, Platform} from 'react-native';
import { IconButton, Title } from 'react-native-paper';
import ConferenceModal from './ConferenceModal';
import HistoryTileBox from './HistoryTileBox';
import FooterBox from './FooterBox';
import URIInput from './URIInput';
import config from '../config';
import utils from '../utils';
import styles from '../assets/styles/blink/_ReadyBox.scss';
class ReadyBox extends Component {
constructor(props) {
super(props);
autoBind(this);
this.state = {
targetUri: this.props.missedTargetUri,
showConferenceModal: false,
- sticky: false,
- matchedContacts: []
+ sticky: false
};
+
+ this.matchedContacts = [];
+
}
getTargetUri() {
const defaultDomain = this.props.account.id.substring(this.props.account.id.indexOf('@') + 1);
return utils.normalizeUri(this.state.targetUri, defaultDomain);
}
async componentDidMount() {
//console.log('Ready now');
if (this.state.targetUri) {
console.log('We must call', this.state.targetUri);
}
}
handleTargetChange(value, contact) {
let matchedContacts = [];
let new_value = value;
if (contact) {
if (this.state.targetUri != contact.uri) {
matchedContacts = matchedContacts.concat(contact);
} else {
new_value = '';
}
}
if (this.state.targetUri) {
let currentUri = this.getTargetUri();
if (currentUri.trim() === value.trim()) {
new_value = '';
}
}
+ this.setState({targetUri: new_value});
+
if (new_value.length > 2 && !contact) {
matchedContacts = this.props.contacts.filter(contact => (contact.uri.toLowerCase().search(new_value) > -1 || contact.name.toLowerCase().search(new_value) > -1));
}
- this.setState({targetUri: new_value, matchedContacts: matchedContacts});
- //this.forceUpdate();
+ this.matchedContacts = matchedContacts;
}
handleTargetSelect() {
if (this.props.connection === null) {
this.props._notificationCenter.postSystemNotification("Server unreachable", {timeout: 2});
return;
}
// the user pressed enter, start a video call by default
if (this.state.targetUri.endsWith(`@${config.defaultConferenceDomain}`)) {
this.props.startConference(this.state.targetUri, {audio: true, video: true});
} else {
this.props.startCall(this.getTargetUri(), {audio: true, video: true});
}
}
showConferenceModal(event) {
event.preventDefault();
if (this.state.targetUri.length !== 0) {
const uri = `${this.state.targetUri.split('@')[0].replace(/[\s()-]/g, '')}@${config.defaultConferenceDomain}`;
this.handleConferenceCall(uri.toLowerCase());
} else {
this.setState({showConferenceModal: true});
}
}
handleAudioCall(event) {
if (this.props.connection === null) {
this.props._notificationCenter.postSystemNotification("Server unreachable", {timeout: 2});
return;
}
event.preventDefault();
if (this.state.targetUri.endsWith(`@${config.defaultConferenceDomain}`)) {
this.props.startConference(this.state.targetUri, {audio: true, video: false});
} else {
this.props.startCall(this.getTargetUri(), {audio: true, video: false});
}
}
handleVideoCall(event) {
if (this.props.connection === null) {
this.props._notificationCenter.postSystemNotification("Server unreachable", {timeout: 2});
return;
}
event.preventDefault();
if (this.state.targetUri.endsWith(`@${config.defaultConferenceDomain}`)) {
this.props.startConference(this.state.targetUri, {audio: true, video: false});
} else {
this.props.startCall(this.getTargetUri(), {audio: true, video: true});
}
}
handleConferenceCall(targetUri, options={audio: true, video: true}) {
if (targetUri) {
if (!options.video) {
console.log('ReadyBox: Handle audio only conference call to',targetUri);
} else {
console.log('ReadyBox: Handle video conference call to',targetUri);
}
this.props.startConference(targetUri, options);
}
this.setState({showConferenceModal: false});
}
render() {
- //console.log('Render ready box');
+ //utils.timestampedLog('Render ready box');
const defaultDomain = `${config.defaultDomain}`;
let uriClass = styles.portraitUriInputBox;
let uriGroupClass = styles.portraitUriButtonGroup;
let titleClass = styles.portraitTitle;
const buttonClass = (Platform.OS === 'ios') ? styles.iosButton : styles.androidButton;
if (this.props.isTablet) {
titleClass = this.props.orientation === 'landscape' ? styles.landscapeTabletTitle : styles.portraitTabletTitle;
} else {
titleClass = this.props.orientation === 'landscape' ? styles.landscapeTitle : styles.portraitTitle;
}
if (this.props.isTablet) {
uriGroupClass = this.props.orientation === 'landscape' ? styles.landscapeTabletUriButtonGroup : styles.portraitTabletUriButtonGroup;
} else {
uriGroupClass = this.props.orientation === 'landscape' ? styles.landscapeUriButtonGroup : styles.portraitUriButtonGroup;
}
if (this.props.isTablet) {
uriClass = this.props.orientation === 'landscape' ? styles.landscapeTabletUriInputBox : styles.portraitTabletUriInputBox;
} else {
uriClass = this.props.orientation === 'landscape' ? styles.landscapeUriInputBox : styles.portraitUriInputBox;
}
+
const historyClass = this.props.orientation === 'landscape' ? styles.landscapeHistory : styles.portraitHistory;
// Join URIs from local and server history for input
let history = this.props.history.concat(
this.props.serverHistory.map(e => e.remoteParty)
);
history = [...new Set(history)];
//console.log('history from server is', this.props.serverHistory);
const placehoder = 'Enter a SIP address like alice@' + defaultDomain;
let historyItems = this.props.serverHistory.filter(historyItem => historyItem.remoteParty.startsWith(this.state.targetUri))
return (
Enter address or phone number
);
}
}
ReadyBox.propTypes = {
account : PropTypes.object.isRequired,
startCall : PropTypes.func.isRequired,
startConference : PropTypes.func.isRequired,
missedTargetUri : PropTypes.string,
history : PropTypes.array,
serverHistory : PropTypes.array,
orientation : PropTypes.string,
contacts : PropTypes.array,
isTablet : PropTypes.bool
};
export default ReadyBox;
diff --git a/app/components/UserIcon.js b/app/components/UserIcon.js
index 8c08917..cf206ad 100644
--- a/app/components/UserIcon.js
+++ b/app/components/UserIcon.js
@@ -1,70 +1,73 @@
import React, { useEffect, useState } from'react';
import PropTypes from 'prop-types';
import utils from '../utils';
import { Avatar } from 'react-native-paper';
const UserIcon = (props) => {
/*
const [photo, setPhoto] = useState('');
useEffect(() => {
// You need to restrict it at some point
// This is just dummy code and should be replaced by actual
if (!photo && props.identity.uri) {
getPhoto();
}
}, []);
const getPhoto = async () => {
try {
let contacts = await utils.findContact(props.identity.uri);
contacts.some((contact) => {
if (contact.hasThumbnail) {
setPhoto(contact.thumbnailPath);
return true;
}
});
} catch (err) {
console.log('error getting contacts', err);
}
}
*/
+ if (!props.identity) {
+ return (null)
+ }
const name = props.identity.displayName || props.identity.uri;
const photo = props.identity.photo;
let initials = name.split(' ', 2).map(x => x[0]).join('');
const color = utils.generateMaterialColor(props.identity.uri)['300'];
const avatarSize = props.large ? 120: 50;
if (photo) {
return
}
if (props.identity.uri.search('anonymous') !== -1) {
return (
)
}
if (props.identity.uri.search('videoconference') !== -1) {
return (
)
}
return (
);
};
UserIcon.propTypes = {
identity: PropTypes.object.isRequired,
large: PropTypes.bool,
card: PropTypes.bool,
active: PropTypes.bool
};
export default UserIcon;