diff --git a/app/components/EditDisplayNameModal.js b/app/components/EditDisplayNameModal.js index c13a457..9fc6c26 100644 --- a/app/components/EditDisplayNameModal.js +++ b/app/components/EditDisplayNameModal.js @@ -1,81 +1,90 @@ 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/_EditDisplayNameModal.scss'; class EditDisplayNameModal extends Component { constructor(props) { super(props); autoBind(this); this.state = { - displayName: this.props.displayName + displayName: this.props.displayName, + show: this.props.show } } + UNSAFE_componentWillReceiveProps(nextProps) { + this.setState({show: nextProps.show}); + } + saveDisplayName(event) { event.preventDefault(); this.props.saveDisplayName(this.state.displayName); - this.props.close(); + if (!this.props.myself) { + this.props.close(); + } else if (this.props.displayName) { + this.props.close(); + } } onInputChange(value) { this.setState({displayName: value}); } render() { return ( - + {this.props.uri} - {this.props.uri === this.props.accountId ? + {this.props.myself ? 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, + myself : PropTypes.bool, saveDisplayName : PropTypes.func }; export default EditDisplayNameModal; diff --git a/app/components/HistoryCard.js b/app/components/HistoryCard.js index 7c422c7..eea5d3d 100644 --- a/app/components/HistoryCard.js +++ b/app/components/HistoryCard.js @@ -1,483 +1,484 @@ 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 && 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 542cc90..2c8c5a4 100644 --- a/app/components/NavigationBar.js +++ b/app/components/NavigationBar.js @@ -1,194 +1,193 @@ 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 } this.setState({menuVisible: !this.state.menuVisible})} anchor={ this.setState({menuVisible: !this.state.menuVisible})} /> } > this.handleMenu('about')} icon="information" title="About Sylk" /> this.handleMenu('preview')} icon="video" title="Video preview" /> this.handleMenu('callMeMaybe')} icon="share" title="Call me, maybe?" /> this.handleMenu('displayName')} icon="rename-box" title="My display name" /> this.handleMenu('settings')} icon="wrench" title="Server settings" /> this.handleMenu('logOut')} icon="logout" title="Sign out" /> ); } } 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;