diff --git a/app/assets/styles/blink/_EditDisplayNameModal.scss b/app/assets/styles/blink/_EditDisplayNameModal.scss
new file mode 100644
index 0000000..57f0d1d
--- /dev/null
+++ b/app/assets/styles/blink/_EditDisplayNameModal.scss
@@ -0,0 +1,27 @@
+.container {
+ padding: 10px;
+ margin: 00px;
+}
+
+.title {
+ padding: 5px;
+ font-size: 24px;
+ text-align: center;
+}
+
+.body {
+ padding: 10px;
+ font-size: 18px;
+ text-align: center;
+}
+
+.button {
+ margin: 10px;
+ width: 120;
+}
+
+.buttonRow {
+ flex-direction: row;
+ justify-content: center;
+}
+
diff --git a/app/components/EditDisplayNameModal.js b/app/components/EditDisplayNameModal.js
index 81c8fd0..c13a457 100644
--- a/app/components/EditDisplayNameModal.js
+++ b/app/components/EditDisplayNameModal.js
@@ -1,74 +1,81 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import autoBind from 'auto-bind';
import { View } from 'react-native';
import { Chip, Dialog, Portal, Text, Button, Surface, TextInput, Paragraph } from 'react-native-paper';
import KeyboardAwareDialog from './KeyBoardAwareDialog';
const DialogType = Platform.OS === 'ios' ? KeyboardAwareDialog : Dialog;
-import styles from '../assets/styles/blink/_InviteParticipantsModal.scss';
+import styles from '../assets/styles/blink/_EditDisplayNameModal.scss';
class EditDisplayNameModal extends Component {
constructor(props) {
super(props);
autoBind(this);
this.state = {
displayName: this.props.displayName
}
}
saveDisplayName(event) {
event.preventDefault();
this.props.saveDisplayName(this.state.displayName);
this.props.close();
}
onInputChange(value) {
this.setState({displayName: value});
}
render() {
return (
{this.props.uri}
-
+ Please set your name seen by others when you call them
+
+ : null}
+
-
);
}
}
EditDisplayNameModal.propTypes = {
show : PropTypes.bool.isRequired,
close : PropTypes.func.isRequired,
displayName : PropTypes.string,
uri : PropTypes.string,
+ accountId : PropTypes.string,
saveDisplayName : PropTypes.func
};
export default EditDisplayNameModal;
diff --git a/app/components/HistoryCard.js b/app/components/HistoryCard.js
index cf47849..7c422c7 100644
--- a/app/components/HistoryCard.js
+++ b/app/components/HistoryCard.js
@@ -1,483 +1,483 @@
import React, { Component, Fragment} from 'react';
import { View, SafeAreaView, FlatList } 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, Button, Caption, Title, Subheading, List, Text} from 'react-native-paper';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import uuid from 'react-native-uuid';
import EditConferenceModal from './EditConferenceModal';
import EditDisplayNameModal from './EditDisplayNameModal';
import styles from '../assets/styles/blink/_HistoryCard.scss';
import UserIcon from './UserIcon';
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, displayName }) => (
{displayName !== uri?
{displayName} ({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 HistoryCard extends Component {
constructor(props) {
super(props);
autoBind(this);
this.state = {
id: this.props.contact.id,
contact: this.props.contact,
displayName: this.props.contact.displayName,
filter: this.props.filter,
uri: this.props.contact.remoteParty,
invitedParties: this.props.invitedParties,
participants: this.props.contact.participants,
conference: this.props.contact.conference,
type: this.props.contact.type,
photo: this.props.contact.photo,
label: this.props.contact.label,
orientation: this.props.orientation,
isTablet: this.props.isTablet,
favorite: (this.props.contact.tags.indexOf('favorite') > -1)? true : false,
blocked: (this.props.contact.tags.indexOf('blocked') > -1)? true : false,
confirmed: false,
showEditConferenceModal: false,
showEditDisplayNameModal: false
}
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
id: nextProps.contact.id,
contact: nextProps.contact,
displayName: nextProps.contact.displayName,
uri: nextProps.contact.remoteParty,
invitedParties: nextProps.invitedParties,
participants: nextProps.contact.participants,
conference: nextProps.contact.conference,
type: nextProps.contact.type,
photo: nextProps.contact.photo,
label: nextProps.contact.label,
orientation: nextProps.orientation,
favorite: (nextProps.contact.tags.indexOf('favorite') > -1)? true : false,
blocked: (nextProps.contact.tags.indexOf('blocked') > -1)? true : false,
});
}
shouldComponentUpdate(nextProps) {
//https://medium.com/sanjagh/how-to-optimize-your-react-native-flatlist-946490c8c49b
return true;
}
toggleEdit() {
if (this.state.conference) {
this.setState({showEditConferenceModal: !this.state.showEditConferenceModal});
} else {
this.setState({showEditDisplayNameModal: !this.state.showEditDisplayNameModal});
}
}
setFavoriteUri() {
console.log('Card setFavoriteUri', this.state.uri);
if (this.state.favorite) {
if (this.state.confirmed) {
let newFavoriteState = this.props.setFavoriteUri(this.state.uri);
this.setState({favorite: newFavoriteState, action: null, confirmed: false});
this.props.setTargetUri(this.state.uri);
} else {
this.setState({confirmed: true});
}
} else {
let newFavoriteState = this.props.setFavoriteUri(this.state.uri);
this.setState({favorite: newFavoriteState});
}
}
saveInvitedParties(uris) {
this.setState({invitedParties: uris});
this.props.saveInvitedParties(this.state.uri.split('@')[0], uris);
}
handleParticipant() {
}
findObjectByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
return array[i];
}
}
return null;
}
setBlockedUri() {
let newBlockedState = this.props.setBlockedUri(this.state.uri);
this.setState({blocked: newBlockedState});
}
setBlockedDomain() {
let newBlockedState = this.props.setBlockedUri('@' + this.state.uri.split('@')[1]);
this.setState({blocked: newBlockedState});
}
deleteHistoryEntry() {
this.props.deleteHistoryEntry(this.state.uri);
}
undo() {
this.setState({confirmed: false, action: null});
}
saveDisplayName(displayName) {
this.props.saveDisplayName(this.state.uri, displayName);
}
setFavoriteUri() {
if (this.state.favorite) {
if (this.state.confirmed) {
let newFavoriteState = this.props.setFavoriteUri(this.state.uri);
this.setState({favorite: newFavoriteState, action: null, confirmed: false});
this.props.setTargetUri(this.state.uri);
} else {
this.setState({confirmed: true});
}
} else {
let newFavoriteState = this.props.setFavoriteUri(this.state.uri);
this.setState({favorite: newFavoriteState});
}
}
setTargetUri(uri, contact) {
this.props.setTargetUri(uri, this.state.contact);
}
render () {
let containerClass = styles.portraitContainer;
let cardClass = styles.card;
let showActions = this.props.contact.showActions && this.props.contact.tags.indexOf('test') === -1;
let uri = this.state.uri;
let username = uri.split('@')[0];
let domain = uri.split('@')[1];
let isPhoneNumber = username.match(/^(\+|0)(\d+)$/);
let displayName = this.state.displayName;
let buttonMode = 'text';
let showBlockButton = !this.state.conference;
let showBlockDomainButton = false;
let showFavoriteButton = true;
let showUndoButton = this.state.confirmed ? true : false;
let showDeleteButton = (this.props.contact.tags.indexOf('local') > -1 && !this.state.favorite) ? true: false;
let showEditButton = (this.state.favorite && !this.state.confirmed ) ? true: false;
let blockTextbutton = 'Block';
let blockDomainTextbutton = 'Block domain';
let editTextbutton = 'Edit';
let favoriteTextbutton = 'Favorite';
let undoTextbutton = 'Undo';
let deleteTextbutton = 'Delete';
let participantsData = [];
if (this.state.favorite) {
favoriteTextbutton = this.state.confirmed ? 'Confirm' : 'Remove';
if (!this.state.blocked) {
showBlockButton = false;
}
}
if (uri.indexOf('3333@') > -1) {
showBlockButton = false;
}
if (uri.indexOf('4444@') > -1) {
showBlockButton = false;
}
if (displayName === 'Myself') {
showBlockButton = false;
}
if (this.state.blocked) {
blockTextbutton = 'Unblock';
showFavoriteButton = false;
}
if (this.state.isTablet) {
containerClass = (this.state.orientation === 'landscape') ? styles.landscapeTabletContainer : styles.portraitTabletContainer;
} else {
containerClass = (this.state.orientation === 'landscape') ? styles.landscapeContainer : styles.portraitContainer;
}
if (showActions) {
cardClass = styles.expandedCard;
}
let color = {};
let title = displayName || username;
let subtitle = uri;
let description = this.props.contact.startTime;
if (displayName === uri) {
title = toTitleCase(username);
}
if (isPhoneNumber && isIp(domain)) {
title = 'Tel ' + username;
subtitle = 'From @' + domain;
showBlockDomainButton = true;
showFavoriteButton = false;
}
if (utils.isAnonymous(uri)) {
//uri = 'anonymous@anonymous.invalid';
displayName = 'Anonymous';
if (uri.indexOf('@guest.') > -1) {
subtitle = 'From the Web';
}
showFavoriteButton = false;
showBlockDomainButton = true;
if (!this.state.blocked) {
showBlockButton = false;
}
blockDomainTextbutton = 'Block Web calls';
}
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';
}
}
if (this.props.contact.tags.indexOf('history') > -1) {
let duration = moment.duration(this.props.contact.duration, 'seconds').format('HH:mm:ss', {trim: false});
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 (this.state.conference) {
let participants = this.state.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, 'remoteParty', participant);
dn = contact_obj ? contact_obj.displayName : participant;
- if (participant === dn && this.props.myDisplayNames.hasOwnProperty(participant)) {
+ if (participant === dn && this.props.myDisplayNames && this.props.myDisplayNames.hasOwnProperty(participant)) {
dn = this.props.myDisplayNames[participant];
}
_item = {nr: i, id: uuid.v4(), uri: participant, displayName: dn};
participantsData.push(_item);
i = i + 1;
});
} else {
subtitle = 'With no participants';
}
let dn;
if (participantsData.length > 4 || participantsData.length < 2) {
title = username.length > 10 ? 'Conference' : toTitleCase(username);
} else if (participantsData.length > 1 || participantsData.length <= 4 ) {
let j = 0;
if (username.length < 10) {
title = toTitleCase(username);
} else {
participantsData.forEach((participant) => {
if (participant.displayName === participant.uri) {
dn = toTitleCase(participant.uri.split('@')[0]);
} else {
dn = participant.displayName.split(' ')[0];
}
title = title + dn;
if (j < participantsData.length - 1) {
title = title + ' & ';
}
j = j + 1;
});
}
}
}
if (!displayName) {
title = 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 (
{this.setTargetUri(uri, this.props.contact)}}
style={[containerClass, cardClass]}
>
{title}
{subtitle}
{description}
{participantsData && participantsData.length && showActions ?
item.id}
key={item => item.id}
/>
: null}
{showActions ?
{showEditButton? : null}
{showDeleteButton? : null}
{showBlockButton? : null}
{showBlockDomainButton? : null}
{showFavoriteButton?: null}
{showUndoButton?: null}
: null}
{ this.state.showEditConferenceModal ?
: null}
{ this.state.showEditDisplayNameModal ?
: null}
);
} else {
return (
{this.props.setTargetUri(uri, this.props.contact)}}
style={[containerClass, cardClass]}
>
{title}
{uri}
{this.state.label}
{showActions ?
{showBlockButton? : null}
{showFavoriteButton?: null}
{showUndoButton?: null}
: null}
);
}
}
}
HistoryCard.propTypes = {
id : PropTypes.string,
contact : PropTypes.object,
setTargetUri : PropTypes.func,
setBlockedUri : PropTypes.func,
setFavoriteUri : PropTypes.func,
saveInvitedParties : PropTypes.func,
deleteHistoryEntry : PropTypes.func,
orientation : PropTypes.string,
isTablet : PropTypes.bool,
contacts : PropTypes.array,
defaultDomain : PropTypes.string,
accountId : PropTypes.string,
favoriteUris : PropTypes.array,
myDisplayNames : PropTypes.object
};
export default HistoryCard;
diff --git a/app/components/NavigationBar.js b/app/components/NavigationBar.js
index 47f9a98..542cc90 100644
--- a/app/components/NavigationBar.js
+++ b/app/components/NavigationBar.js
@@ -1,193 +1,194 @@
import React, { Component } from 'react';
import { Linking, Image, View } from 'react-native';
import PropTypes from 'prop-types';
import autoBind from 'auto-bind';
import { Appbar, Menu, Divider, Text } from 'react-native-paper';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import config from '../config';
import AboutModal from './AboutModal';
import CallMeMaybeModal from './CallMeMaybeModal';
import EditDisplayNameModal from './EditDisplayNameModal';
import styles from '../assets/styles/blink/_NavigationBar.scss';
const blinkLogo = require('../assets/images/blink-white-big.png');
class NavigationBar extends Component {
constructor(props) {
super(props);
autoBind(this);
this.state = {
showAboutModal: false,
showCallMeMaybeModal: false,
showEditDisplayNameModal: false,
registrationState: this.props.registrationState,
connection: this.props.connection,
mute: false,
menuVisible: false,
accountId: this.props.account ? this.props.account.id : null,
displayName: this.props.displayName
}
this.menuRef = React.createRef();
}
//getDerivedStateFromProps(nextProps, state) {
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.account !== null && nextProps.account.id !== this.state.accountId) {
this.setState({accountId: nextProps.account.id});
}
this.setState({registrationState: nextProps.registrationState,
connection: nextProps.connection,
displayName: nextProps.displayName
});
}
handleMenu(event) {
this.callUrl = `${config.publicUrl}/call/${this.state.accountId}`;
switch (event) {
case 'about':
this.toggleAboutModal();
break;
case 'callMeMaybe':
this.toggleCallMeMaybeModal();
break;
case 'displayName':
this.toggleEditDisplayNameModal();
break;
case 'logOut':
this.props.logout();
break;
case 'preview':
this.props.preview();
break;
case 'settings':
Linking.openURL(config.serverSettingsUrl);
break;
default:
break;
}
this.setState({menuVisible: false});
}
saveDisplayName(displayName) {
if (!displayName) {
return;
}
this.setState({displayName: displayName});
this.props.saveDisplayName(this.state.accountId, displayName);
}
toggleMute() {
this.setState(prevState => ({mute: !prevState.mute}));
this.props.toggleMute();
}
toggleAboutModal() {
this.setState({showAboutModal: !this.state.showAboutModal});
}
toggleCallMeMaybeModal() {
this.setState({showCallMeMaybeModal: !this.state.showCallMeMaybeModal});
}
toggleEditDisplayNameModal() {
this.setState({showEditDisplayNameModal: !this.state.showEditDisplayNameModal});
}
render() {
const muteIcon = this.state.mute ? 'bell-off' : 'bell';
let subtitleStyle = this.props.isTablet ? styles.tabletSubtitle: styles.subtitle;
let titleStyle = this.props.isTablet ? styles.tabletTitle: styles.title;
let statusIcon = null;
let statusColor = 'green';
statusIcon = 'check-circle';
if (!this.state.connection || this.state.connection.state !== 'ready') {
statusIcon = 'alert-circle';
statusColor = 'red';
} else if (this.state.registrationState !== 'registered') {
statusIcon = 'alert-circle';
statusColor = 'orange';
}
let callUrl = callUrl = config.publicUrl + "/call/" + this.state.accountId;
let subtitle = 'Signed in as ' + this.state.accountId;
return (
{this.props.isTablet?
{subtitle}
: null}
{statusIcon ?
: null }
);
}
}
NavigationBar.propTypes = {
notificationCenter : PropTypes.func.isRequired,
logout : PropTypes.func.isRequired,
preview : PropTypes.func.isRequired,
saveDisplayName : PropTypes.func.isRequired,
displayName : PropTypes.string,
account : PropTypes.object,
connection : PropTypes.object,
toggleMute : PropTypes.func,
orientation : PropTypes.string,
isTablet : PropTypes.bool
};
export default NavigationBar;