mirror of
https://github.com/irislib/iris-messenger.git
synced 2024-10-19 06:33:32 +00:00
language selector & settings fixes
This commit is contained in:
parent
d170b6c31b
commit
027d7bad7f
@ -1900,6 +1900,10 @@ form.public {
|
||||
background-color: var(--chat-hover);
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.explorer-row {
|
||||
border-radius: 3px;
|
||||
padding: 3px;
|
||||
|
@ -112,4 +112,5 @@ export default {
|
||||
</svg>
|
||||
`,
|
||||
menu: html`<svg fill="currentColor" x="0px" y="0px" viewBox="0 0 384 384" width="24px" height="24px" enable-background="new 0 0 384 384;"><g><rect x="0" y="277.333" width="384" height="42.667"/><rect x="0" y="170.667" width="384" height="42.667"/><rect x="0" y="64" width="384" height="42.667"/></g></svg>`,
|
||||
language: html`<svg width="14" height="14" style="margin-bottom: -1px" x="0px" y="0px" viewBox="0 0 469.333 469.333" style="enable-background:new 0 0 469.333 469.333;" xml:space="preserve"><path fill="currentColor" d="M253.227,300.267L253.227,300.267L199.04,246.72l0.64-0.64c37.12-41.387,63.573-88.96,79.147-139.307h62.507V64H192 V21.333h-42.667V64H0v42.453h238.293c-14.4,41.173-36.907,80.213-67.627,114.347c-19.84-22.08-36.267-46.08-49.28-71.467H78.72 c15.573,34.773,36.907,67.627,63.573,97.28l-108.48,107.2L64,384l106.667-106.667l66.347,66.347L253.227,300.267z"/><path fill="currentColor" d="M373.333,192h-42.667l-96,256h42.667l24-64h101.333l24,64h42.667L373.333,192z M317.333,341.333L352,248.853 l34.667,92.48H317.333z"/></svg>`,
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ import QRScanner from './QRScanner.js';
|
||||
import PeerManager from './PeerManager.js';
|
||||
import Session from './Session.js';
|
||||
|
||||
import Settings from './views/Settings.js';
|
||||
import Settings from './views/settings/Settings.js';
|
||||
import LogoutConfirmation from './views/LogoutConfirmation.js';
|
||||
import Chat from './views/Chat.js';
|
||||
import Store from './views/Store.js';
|
||||
|
@ -2,6 +2,7 @@ import { html } from 'htm/preact';
|
||||
import {AVAILABLE_LANGUAGES, language} from '../Translation.js';
|
||||
import Translations from '../Translations.js';
|
||||
import $ from 'jquery';
|
||||
import Icons from '../Icons.js';
|
||||
|
||||
function onLanguageChange(e) {
|
||||
const l = $(e.target).val();
|
||||
@ -12,6 +13,7 @@ function onLanguageChange(e) {
|
||||
}
|
||||
|
||||
const LanguageSelector = () => html`
|
||||
${Icons.language}
|
||||
<select class="language-selector" onChange=${e => onLanguageChange(e)} value=${language}>${
|
||||
AVAILABLE_LANGUAGES.map(l =>
|
||||
html`<option value=${l}>${Translations[l].language_name}</option>`
|
||||
|
@ -88,7 +88,6 @@ class Login extends Component {
|
||||
<br/>
|
||||
<p><a href="#" id="show-existing-account-login" onClick=${() => this.setState({showSwitchAccount: true})}>${t('already_have_an_account')}</a></p>
|
||||
<p>
|
||||
<svg width="14" height="14" style="margin-bottom: -1px" x="0px" y="0px" viewBox="0 0 469.333 469.333" style="enable-background:new 0 0 469.333 469.333;" xml:space="preserve"><path fill="currentColor" d="M253.227,300.267L253.227,300.267L199.04,246.72l0.64-0.64c37.12-41.387,63.573-88.96,79.147-139.307h62.507V64H192 V21.333h-42.667V64H0v42.453h238.293c-14.4,41.173-36.907,80.213-67.627,114.347c-19.84-22.08-36.267-46.08-49.28-71.467H78.72 c15.573,34.773,36.907,67.627,63.573,97.28l-108.48,107.2L64,384l106.667-106.667l66.347,66.347L253.227,300.267z"/><path fill="currentColor" d="M373.333,192h-42.667l-96,256h42.667l24-64h101.333l24,64h42.667L373.333,192z M317.333,341.333L352,248.853 l34.667,92.48H317.333z"/></svg>
|
||||
<${LanguageSelector}/>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -1,312 +0,0 @@
|
||||
import Helpers from '../Helpers.js';
|
||||
import { html } from 'htm/preact';
|
||||
import State from '../State.js';
|
||||
import Session from '../Session.js';
|
||||
import LanguageSelector from '../components/LanguageSelector.js';
|
||||
import {translate as t} from '../Translation.js';
|
||||
import PeerManager from '../PeerManager.js';
|
||||
import {setRTCConfig, getRTCConfig, DEFAULT_RTC_CONFIG} from '../components/VideoCall.js';
|
||||
import CopyButton from '../components/CopyButton.js';
|
||||
import View from './View.js';
|
||||
import { route } from 'preact-router';
|
||||
import {ExistingAccountLogin} from './Login.js';
|
||||
import Notifications from '../Notifications.js';
|
||||
import $ from 'jquery';
|
||||
import Icons from '../Icons.js';
|
||||
import _ from 'lodash';
|
||||
import QRCode from '../lib/qrcode.min';
|
||||
|
||||
class Settings extends View {
|
||||
constructor() {
|
||||
super();
|
||||
this.eventListeners = {};
|
||||
this.state = Session.DEFAULT_SETTINGS;
|
||||
this.state.webPushSubscriptions = {};
|
||||
this.state.blockedUsers = {};
|
||||
this.id = "settings";
|
||||
}
|
||||
|
||||
onProfilePhotoSet(src) {
|
||||
State.public.user().get('profile').get('photo').put(src);
|
||||
}
|
||||
|
||||
mailtoSubmit(e) {
|
||||
e.preventDefault();
|
||||
if (this.state.email && this.state.email === this.state.retypeEmail) {
|
||||
window.location.href = `mailto:${this.state.email}?&subject=Iris%20private%20key&body=${JSON.stringify(Session.getKey())}`;
|
||||
}
|
||||
}
|
||||
|
||||
renderView() {
|
||||
const blockedUsers = _.filter(Object.keys(this.state.blockedUsers), user => this.state.blockedUsers[user]);
|
||||
return html`
|
||||
<div class="centered-container">
|
||||
<h3>${t('account')}</h3>
|
||||
<p>
|
||||
<b>${t('save_backup_of_privkey_first')}</b> ${t('otherwise_cant_log_in_again')}
|
||||
</p>
|
||||
<p>
|
||||
<button onClick=${() => route('/logout')}>${t('log_out')}</button>
|
||||
</p>
|
||||
<p>
|
||||
<button onClick=${() => this.setState({showSwitchAccount: !this.state.showSwitchAccount})}>${t('switch_account')}</button>
|
||||
</p>
|
||||
${this.state.showSwitchAccount ? html`<${ExistingAccountLogin}/>` : ''}
|
||||
<h4>${t('private_key')}</h4>
|
||||
<p dangerouslySetInnerHTML=${{ __html: t('private_key_warning') }} ></p>
|
||||
<p>
|
||||
<button onClick=${() => downloadKey()}>${t('download_private_key')}</button>
|
||||
<${CopyButton} notShareable=${true} text=${t('copy_private_key')} copyStr=${JSON.stringify(Session.getKey())}/>
|
||||
</p>
|
||||
<p>
|
||||
<button onClick=${e => togglePrivateKeyQR(e)}>${t('show_privkey_qr')}</button>
|
||||
</p>
|
||||
<div id="private-key-qr" class="qr-container"></div>
|
||||
<p>
|
||||
${t('email_privkey_to_yourself')}:
|
||||
</p>
|
||||
<p>
|
||||
<form onSubmit=${e => this.mailtoSubmit(e)}>
|
||||
<input name="email" type="email" onChange=${e => this.setState({email:e.target.value.trim()})} placeholder=${t('email')}/>
|
||||
<input name="verify_email" type="email" onChange=${e => this.setState({retypeEmail:e.target.value.trim()})} placeholder=${t('retype_email')}/>
|
||||
<button type="submit">${t('go')}</button>
|
||||
</form>
|
||||
</p>
|
||||
<p><small dangerouslySetInnerHTML=${{ __html: t('privkey_storage_recommendation')}}></small></p>
|
||||
<hr/>
|
||||
<h3>${t('language')}</h3>
|
||||
<p><${LanguageSelector}/></p>
|
||||
<hr/>
|
||||
<h3>${t('notifications')}</h3>
|
||||
<p>${t('web_push_subscriptions')}</p>
|
||||
<div class="flex-table">
|
||||
${Object.keys(this.state.webPushSubscriptions).map(k => {
|
||||
const v = this.state.webPushSubscriptions[k];
|
||||
return html`
|
||||
<div class="flex-row">
|
||||
<div class="flex-cell">${v.endpoint}</div>
|
||||
<div class="flex-cell no-flex">
|
||||
<button onClick=${() => Notifications.removeSubscription(k)}>${t('remove')}</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
<hr/>
|
||||
<h3>${t('peers')}</h3>
|
||||
${this.renderPeerList()}
|
||||
<p><input type="checkbox" checked=${this.state.local.enablePublicPeerDiscovery} onChange=${() => State.local.get('settings').get('enablePublicPeerDiscovery').put(!this.state.local.enablePublicPeerDiscovery)} id="enablePublicPeerDiscovery"/><label for="enablePublicPeerDiscovery">${t('enable_public_peer_discovery')}</label></p>
|
||||
<h4>${t('maximum_number_of_peer_connections')}</h4>
|
||||
<p>
|
||||
<small>${t('there_is_a_bug')}</small>
|
||||
</p>
|
||||
<p>
|
||||
<input type="number" value=${this.state.local.maxConnectedPeers} onChange=${e => State.local.get('settings').get('maxConnectedPeers').put(e.target.value || 0)}/>
|
||||
</p>
|
||||
${Helpers.isElectron ? html`
|
||||
<h4>${t('your_public_address')}</h4>
|
||||
<p>http://${this.state.electron.publicIp || '-'}:8767/gun</p>
|
||||
<p><small>If you're behind NAT (likely) and want to accept incoming connections, you need to configure your router to forward the port 8767 to this computer.</small></p>
|
||||
`: ''}
|
||||
<h4>${t('set_up_your_own_peer')}</h4>
|
||||
<p>
|
||||
<small dangerouslySetInnerHTML=${{ __html: t('peers_info', "href=\"https://github.com/amark/gun#deploy\"")}}></small>
|
||||
</p>
|
||||
<p><a href="https://heroku.com/deploy?template=https://github.com/amark/gun">
|
||||
${Icons.herokuButton}
|
||||
</a></p>
|
||||
<p>${t('also')} <a href="https://github.com/amark/gun#docker">Docker</a> ${t('or_small')} <a href="https://github.com/irislib/iris-electron">Iris-electron</a>.</p>
|
||||
${Helpers.isElectron ? html`
|
||||
<hr/>
|
||||
<h3>Desktop</h3>
|
||||
<p><input type="checkbox" checked=${this.state.electron.openAtLogin} onChange=${() => State.electron.get('settings').get('openAtLogin').put(!this.state.electron.openAtLogin)} id="openAtLogin"/><label for="openAtLogin">Open at login</label></p>
|
||||
<p><input type="checkbox" checked=${this.state.electron.minimizeOnClose} onChange=${() => State.electron.get('settings').get('minimizeOnClose').put(!this.state.electron.minimizeOnClose)} id="minimizeOnClose"/><label for="minimizeOnClose">Minimize on close</label></p>
|
||||
`: ''}
|
||||
<hr/>
|
||||
<h3>${t('webtorrent')}</h3>
|
||||
<p><input type="checkbox" checked=${this.state.local.enableWebtorrent} onChange=${() => State.local.get('settings').get('enableWebtorrent').put(!this.state.local.enableWebtorrent)} id="enableWebtorrent"/><label for="enableWebtorrent">${t('automatically_load_webtorrent_attachments')}</label></p>
|
||||
<p><input type="checkbox" checked=${this.state.local.autoplayWebtorrent} onChange=${() => State.local.get('settings').get('autoplayWebtorrent').put(!this.state.local.autoplayWebtorrent)} id="autoplayWebtorrent"/><label for="autoplayWebtorrent">${t('autoplay_webtorrent_videos')}</label></p>
|
||||
<hr/>
|
||||
<h3>${t('webrtc_connection_options')}</h3>
|
||||
<p><small>${t('webrtc_info')}</small></p>
|
||||
<p><textarea rows="4" id="rtc-config" placeholder="${t('webrtc_connection_options')}" onChange=${() => this.rtcConfigChanged()}></textarea></p>
|
||||
<button onClick=${() => this.restoreDefaultRtcConfig()}>${t('restore_defaults')}</button>
|
||||
<hr/>
|
||||
<h3>${t('blocked_users')}</h3>
|
||||
${blockedUsers.map(user => {
|
||||
if (this.state.blockedUsers[user]) {
|
||||
return html`<p><a href="/profile/${encodeURIComponent(user)}"><iris-text user=${user} path="profile/name" placeholder="User"/></a></p>`;
|
||||
}
|
||||
})}
|
||||
${blockedUsers.length === 0 ? t('none') : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
rtcConfigChanged() {
|
||||
setRTCConfig(JSON.parse($('#rtc-config').val()));
|
||||
}
|
||||
|
||||
resetPeersClicked() {
|
||||
PeerManager.resetPeers();
|
||||
this.setState({});
|
||||
}
|
||||
|
||||
removePeerClicked(url, peerFromGun) {
|
||||
PeerManager.removePeer(url);
|
||||
peerFromGun && PeerManager.disconnectPeer(peerFromGun);
|
||||
}
|
||||
|
||||
enablePeerClicked(url, peerFromGun, peer) {
|
||||
peer.enabled ? PeerManager.disablePeer(url,peerFromGun) : PeerManager.connectPeer(url);
|
||||
}
|
||||
|
||||
renderPeerList() {
|
||||
let urls = Object.keys(PeerManager.getKnownPeers());
|
||||
if (this.state.peersFromGun) {
|
||||
Object.keys(this.state.peersFromGun).forEach(url => urls.indexOf(url) === -1 && urls.push(url));
|
||||
}
|
||||
|
||||
return html`
|
||||
<div id="peers" class="flex-table">
|
||||
${urls.length === 0 ? html`
|
||||
<button id="reset-peers" style="margin-bottom: 15px" onClick=${() => this.resetPeersClicked()}>${t('restore_defaults')}</button>
|
||||
`: ''}
|
||||
${urls.map(url => {
|
||||
if (url == 1) {return;} // weirdness
|
||||
const peer = PeerManager.getKnownPeers()[url] || {};
|
||||
const peerFromGun = this.state.peersFromGun && this.state.peersFromGun[url];
|
||||
const connected = peerFromGun && peerFromGun.wire && peerFromGun.wire.hied === 'hi';
|
||||
return html`
|
||||
<div class="flex-row peer">
|
||||
<div class="flex-cell">
|
||||
${connected ? html`
|
||||
<span class="tooltip" style="color: var(--positive-color);margin-right:15px">
|
||||
<span class="tooltiptext">Connected</span>
|
||||
<svg height="14" width="14" x="0px" y="0px" viewBox="0 0 191.667 191.667"><path fill="currentColor" d="M95.833,0C42.991,0,0,42.99,0,95.833s42.991,95.834,95.833,95.834s95.833-42.991,95.833-95.834S148.676,0,95.833,0z M150.862,79.646l-60.207,60.207c-2.56,2.56-5.963,3.969-9.583,3.969c-3.62,0-7.023-1.409-9.583-3.969l-30.685-30.685 c-2.56-2.56-3.97-5.963-3.97-9.583c0-3.621,1.41-7.024,3.97-9.584c2.559-2.56,5.962-3.97,9.583-3.97c3.62,0,7.024,1.41,9.583,3.971 l21.101,21.1l50.623-50.623c2.56-2.56,5.963-3.969,9.583-3.969c3.62,0,7.023,1.409,9.583,3.969 C156.146,65.765,156.146,74.362,150.862,79.646z"/></svg>
|
||||
</span>
|
||||
` : html`
|
||||
<small class="tooltip" style="margin-right:15px">
|
||||
<span class="tooltiptext">Disconnected</span>
|
||||
<svg width="14" height="14" x="0px" y="0px" viewBox="0 0 512 512" fill="currentColor"><path d="M257,0C116.39,0,0,114.39,0,255s116.39,257,257,257s255-116.39,255-257S397.61,0,257,0z M383.22,338.79 c11.7,11.7,11.7,30.73,0,42.44c-11.61,11.6-30.64,11.79-42.44,0L257,297.42l-85.79,83.82c-11.7,11.7-30.73,11.7-42.44,0 c-11.7-11.7-11.7-30.73,0-42.44l83.8-83.8l-83.8-83.8c-11.7-11.71-11.7-30.74,0-42.44c11.71-11.7,30.74-11.7,42.44,0L257,212.58 l83.78-83.82c11.68-11.68,30.71-11.72,42.44,0c11.7,11.7,11.7,30.73,0,42.44l-83.8,83.8L383.22,338.79z"/></svg>
|
||||
</small>
|
||||
`}
|
||||
${url}
|
||||
${peer.from ? html`
|
||||
<br/><small style="cursor:pointer" onClick=${() => route(`/profile/${ peer.from}`)}>${t('from')} ${Helpers.truncateString(peer.from, 10)}</small>
|
||||
` : ''}
|
||||
</div>
|
||||
<div class="flex-cell no-flex">
|
||||
<button onClick=${() => this.removePeerClicked(url, peerFromGun)}>${t('remove')}</button>
|
||||
<button onClick=${() => this.enablePeerClicked(url, peerFromGun, peer)}>${peer.enabled ? t('disable') : t('enable')}</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
}
|
||||
|
||||
<div class="flex-row" id="add-peer-row">
|
||||
<div class="flex-cell">
|
||||
<input type="url" id="add-peer-url" placeholder="${t('peer_url')}"/>
|
||||
<input type="checkbox" id="add-peer-public"/>
|
||||
<label for="add-peer-public">${t('public')}</label>
|
||||
<button id="add-peer-btn" onClick=${() => this.addPeerClicked()}>${t('add')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<small dangerouslySetInnerHTML=${{ __html:t('public_peer_info') }}></small>
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
updatePeersFromGun() {
|
||||
const peersFromGun = State.public.back('opt.peers') || {};
|
||||
this.setState({peersFromGun});
|
||||
}
|
||||
|
||||
addPeerClicked() {
|
||||
let url = $('#add-peer-url').val();
|
||||
let visibility = $('#add-peer-public').is(':checked') ? 'public' : undefined;
|
||||
PeerManager.addPeer({url, visibility});
|
||||
$('#add-peer-url').val('');
|
||||
}
|
||||
|
||||
restoreDefaultRtcConfig() {
|
||||
setRTCConfig(DEFAULT_RTC_CONFIG);
|
||||
$('#rtc-config').val(JSON.stringify(getRTCConfig()));
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const blockedUsers = {};
|
||||
this.updatePeersFromGun();
|
||||
this.updatePeersFromGunInterval = setInterval(() => this.updatePeersFromGun(), 2000);
|
||||
|
||||
$('#rtc-config').val(JSON.stringify(getRTCConfig()));
|
||||
|
||||
State.electron && State.electron.get('settings').on(electron => {
|
||||
this.setState({electron});
|
||||
});
|
||||
State.local.get('settings').on(local => {
|
||||
this.setState({local})
|
||||
});
|
||||
State.public.user().get('webPushSubscriptions').map().on(() => this.setState({webPushSubscriptions: Notifications.webPushSubscriptions}));
|
||||
State.public.user().get('block').map().on((v,k,x,e) => {
|
||||
this.eventListeners['block'] = e;
|
||||
blockedUsers[k] = v;
|
||||
this.setState({blockedUsers});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
Object.values(this.eventListeners).forEach(e => e.off());
|
||||
clearInterval(this.updatePeersFromGunInterval);
|
||||
}
|
||||
}
|
||||
|
||||
function togglePrivateKeyQR(e) {
|
||||
let btn = $(e.target);
|
||||
let show = $('#private-key-qr img').length === 0;
|
||||
let SHOW_TEXT = t('show_privkey_qr');
|
||||
let hidePrivateKeyInterval;
|
||||
function reset() {
|
||||
clearInterval(hidePrivateKeyInterval);
|
||||
$('#private-key-qr').empty();
|
||||
btn.text(SHOW_TEXT);
|
||||
}
|
||||
function hideText(s) { return `${t('hide_privkey_qr') } (${ s })`; }
|
||||
if (show) {
|
||||
let showPrivateKeySecondsRemaining = 20;
|
||||
btn.text(hideText(showPrivateKeySecondsRemaining));
|
||||
hidePrivateKeyInterval = setInterval(() => {
|
||||
if ($('#private-key-qr img').length === 0) {
|
||||
clearInterval(hidePrivateKeyInterval);return;
|
||||
}
|
||||
showPrivateKeySecondsRemaining -= 1;
|
||||
if (showPrivateKeySecondsRemaining === 0) {
|
||||
reset();
|
||||
} else {
|
||||
btn.text(hideText(showPrivateKeySecondsRemaining));
|
||||
}
|
||||
}, 1000);
|
||||
let qrCodeEl = $('#private-key-qr');
|
||||
new QRCode(qrCodeEl[0], {
|
||||
text: JSON.stringify(Session.getKey()),
|
||||
width: 300,
|
||||
height: 300,
|
||||
colorDark : "#000000",
|
||||
colorLight : "#ffffff",
|
||||
correctLevel : QRCode.CorrectLevel.H
|
||||
});
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
function downloadKey() {
|
||||
const key = Session.getKey();
|
||||
delete key['#'];
|
||||
return Helpers.download('iris_private_key.txt', JSON.stringify(key), 'text/plain', 'utf-8');
|
||||
}
|
||||
|
||||
export default Settings;
|
137
src/js/views/settings/PeerSettings.js
Normal file
137
src/js/views/settings/PeerSettings.js
Normal file
@ -0,0 +1,137 @@
|
||||
import Component from "../../BaseComponent";
|
||||
import Session from "../../Session";
|
||||
import State from "../../State";
|
||||
import {html} from "htm/preact";
|
||||
import {translate as t} from "../../Translation";
|
||||
import Helpers from "../../Helpers";
|
||||
import Icons from "../../Icons";
|
||||
import PeerManager from "../../PeerManager";
|
||||
import {route} from "preact-router";
|
||||
import $ from "jquery";
|
||||
|
||||
export default class PeerSettings extends Component {
|
||||
state = Session.DEFAULT_SETTINGS;
|
||||
|
||||
componentDidMount() {
|
||||
State.local.get('settings').on(this.inject('local'));
|
||||
this.updatePeersFromGun();
|
||||
this.updatePeersFromGunInterval = setInterval(() => this.updatePeersFromGun(), 2000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
clearInterval(this.updatePeersFromGunInterval);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<h3>${t('peers')}</h3>
|
||||
${this.renderPeerList()}
|
||||
<p><input type="checkbox" checked=${this.state.local.enablePublicPeerDiscovery} onChange=${() => State.local.get('settings').get('enablePublicPeerDiscovery').put(!this.state.local.enablePublicPeerDiscovery)} id="enablePublicPeerDiscovery"/><label for="enablePublicPeerDiscovery">${t('enable_public_peer_discovery')}</label></p>
|
||||
<h4>${t('maximum_number_of_peer_connections')}</h4>
|
||||
<p>
|
||||
<small>${t('there_is_a_bug')}</small>
|
||||
</p>
|
||||
<p>
|
||||
<input type="number" value=${this.state.local.maxConnectedPeers} onChange=${e => State.local.get('settings').get('maxConnectedPeers').put(e.target.value || 0)}/>
|
||||
</p>
|
||||
${Helpers.isElectron ? html`
|
||||
<h4>${t('your_public_address')}</h4>
|
||||
<p>http://${this.state.electron.publicIp || '-'}:8767/gun</p>
|
||||
<p><small>If you're behind NAT (likely) and want to accept incoming connections, you need to configure your router to forward the port 8767 to this computer.</small></p>
|
||||
`: ''}
|
||||
<h4>${t('set_up_your_own_peer')}</h4>
|
||||
<p>
|
||||
<small dangerouslySetInnerHTML=${{ __html: t('peers_info', "href=\"https://github.com/amark/gun#deploy\"")}}></small>
|
||||
</p>
|
||||
<p><a href="https://heroku.com/deploy?template=https://github.com/amark/gun">
|
||||
${Icons.herokuButton}
|
||||
</a></p>
|
||||
`;
|
||||
}
|
||||
|
||||
resetPeersClicked() {
|
||||
PeerManager.resetPeers();
|
||||
this.setState({});
|
||||
}
|
||||
|
||||
removePeerClicked(url, peerFromGun) {
|
||||
PeerManager.removePeer(url);
|
||||
peerFromGun && PeerManager.disconnectPeer(peerFromGun);
|
||||
}
|
||||
|
||||
enablePeerClicked(url, peerFromGun, peer) {
|
||||
peer.enabled ? PeerManager.disablePeer(url,peerFromGun) : PeerManager.connectPeer(url);
|
||||
}
|
||||
|
||||
renderPeerList() {
|
||||
let urls = Object.keys(PeerManager.getKnownPeers());
|
||||
if (this.state.peersFromGun) {
|
||||
Object.keys(this.state.peersFromGun).forEach(url => urls.indexOf(url) === -1 && urls.push(url));
|
||||
}
|
||||
|
||||
return html`
|
||||
<div id="peers" class="flex-table">
|
||||
${urls.length === 0 ? html`
|
||||
<button id="reset-peers" style="margin-bottom: 15px" onClick=${() => this.resetPeersClicked()}>${t('restore_defaults')}</button>
|
||||
`: ''}
|
||||
${urls.map(url => {
|
||||
if (url == 1) {return;} // weirdness
|
||||
const peer = PeerManager.getKnownPeers()[url] || {};
|
||||
const peerFromGun = this.state.peersFromGun && this.state.peersFromGun[url];
|
||||
const connected = peerFromGun && peerFromGun.wire && peerFromGun.wire.hied === 'hi';
|
||||
return html`
|
||||
<div class="flex-row peer">
|
||||
<div class="flex-cell">
|
||||
${connected ? html`
|
||||
<span class="tooltip" style="color: var(--positive-color);margin-right:15px">
|
||||
<span class="tooltiptext">Connected</span>
|
||||
<svg height="14" width="14" x="0px" y="0px" viewBox="0 0 191.667 191.667"><path fill="currentColor" d="M95.833,0C42.991,0,0,42.99,0,95.833s42.991,95.834,95.833,95.834s95.833-42.991,95.833-95.834S148.676,0,95.833,0z M150.862,79.646l-60.207,60.207c-2.56,2.56-5.963,3.969-9.583,3.969c-3.62,0-7.023-1.409-9.583-3.969l-30.685-30.685 c-2.56-2.56-3.97-5.963-3.97-9.583c0-3.621,1.41-7.024,3.97-9.584c2.559-2.56,5.962-3.97,9.583-3.97c3.62,0,7.024,1.41,9.583,3.971 l21.101,21.1l50.623-50.623c2.56-2.56,5.963-3.969,9.583-3.969c3.62,0,7.023,1.409,9.583,3.969 C156.146,65.765,156.146,74.362,150.862,79.646z"/></svg>
|
||||
</span>
|
||||
` : html`
|
||||
<small class="tooltip" style="margin-right:15px">
|
||||
<span class="tooltiptext">Disconnected</span>
|
||||
<svg width="14" height="14" x="0px" y="0px" viewBox="0 0 512 512" fill="currentColor"><path d="M257,0C116.39,0,0,114.39,0,255s116.39,257,257,257s255-116.39,255-257S397.61,0,257,0z M383.22,338.79 c11.7,11.7,11.7,30.73,0,42.44c-11.61,11.6-30.64,11.79-42.44,0L257,297.42l-85.79,83.82c-11.7,11.7-30.73,11.7-42.44,0 c-11.7-11.7-11.7-30.73,0-42.44l83.8-83.8l-83.8-83.8c-11.7-11.71-11.7-30.74,0-42.44c11.71-11.7,30.74-11.7,42.44,0L257,212.58 l83.78-83.82c11.68-11.68,30.71-11.72,42.44,0c11.7,11.7,11.7,30.73,0,42.44l-83.8,83.8L383.22,338.79z"/></svg>
|
||||
</small>
|
||||
`}
|
||||
${url}
|
||||
${peer.from ? html`
|
||||
<br/><small style="cursor:pointer" onClick=${() => route(`/profile/${ peer.from}`)}>${t('from')} ${Helpers.truncateString(peer.from, 10)}</small>
|
||||
` : ''}
|
||||
</div>
|
||||
<div class="flex-cell no-flex">
|
||||
<button onClick=${() => this.removePeerClicked(url, peerFromGun)}>${t('remove')}</button>
|
||||
<button onClick=${() => this.enablePeerClicked(url, peerFromGun, peer)}>${peer.enabled ? t('disable') : t('enable')}</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
}
|
||||
|
||||
<div class="flex-row" id="add-peer-row">
|
||||
<div class="flex-cell">
|
||||
<input type="url" id="add-peer-url" placeholder="${t('peer_url')}"/>
|
||||
<input type="checkbox" id="add-peer-public"/>
|
||||
<label for="add-peer-public">${t('public')}</label>
|
||||
<button id="add-peer-btn" onClick=${() => this.addPeerClicked()}>${t('add')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<small dangerouslySetInnerHTML=${{ __html:t('public_peer_info') }}></small>
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
updatePeersFromGun() {
|
||||
const peersFromGun = State.public.back('opt.peers') || {};
|
||||
this.setState({peersFromGun});
|
||||
}
|
||||
|
||||
addPeerClicked() {
|
||||
let url = $('#add-peer-url').val();
|
||||
let visibility = $('#add-peer-public').is(':checked') ? 'public' : undefined;
|
||||
PeerManager.addPeer({url, visibility});
|
||||
$('#add-peer-url').val('');
|
||||
}
|
||||
}
|
192
src/js/views/settings/Settings.js
Normal file
192
src/js/views/settings/Settings.js
Normal file
@ -0,0 +1,192 @@
|
||||
import Helpers from '../../Helpers.js';
|
||||
import { html } from 'htm/preact';
|
||||
import State from '../../State.js';
|
||||
import Session from '../../Session.js';
|
||||
import LanguageSelector from '../../components/LanguageSelector.js';
|
||||
import {translate as t} from '../../Translation.js';
|
||||
import {setRTCConfig, getRTCConfig, DEFAULT_RTC_CONFIG} from '../../components/VideoCall.js';
|
||||
import CopyButton from '../../components/CopyButton.js';
|
||||
import View from '../View.js';
|
||||
import { route } from 'preact-router';
|
||||
import {ExistingAccountLogin} from '../Login.js';
|
||||
import Notifications from '../../Notifications.js';
|
||||
import PeerSettings from './PeerSettings';
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
import QRCode from '../../lib/qrcode.min';
|
||||
|
||||
class Settings extends View {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = Session.DEFAULT_SETTINGS;
|
||||
this.state.webPushSubscriptions = {};
|
||||
this.state.blockedUsers = {};
|
||||
this.id = "settings";
|
||||
}
|
||||
|
||||
mailtoSubmit(e) {
|
||||
e.preventDefault();
|
||||
if (this.state.email && this.state.email === this.state.retypeEmail) {
|
||||
window.location.href = `mailto:${this.state.email}?&subject=Iris%20private%20key&body=${JSON.stringify(Session.getKey())}`;
|
||||
}
|
||||
}
|
||||
|
||||
renderView() {
|
||||
const blockedUsers = _.filter(Object.keys(this.state.blockedUsers), user => this.state.blockedUsers[user]);
|
||||
return html`
|
||||
<div class="centered-container">
|
||||
<h3>${t('account')}</h3>
|
||||
<p>
|
||||
<b>${t('save_backup_of_privkey_first')}</b> ${t('otherwise_cant_log_in_again')}
|
||||
</p>
|
||||
<p>
|
||||
<button onClick=${() => route('/logout')}>${t('log_out')}</button>
|
||||
</p>
|
||||
<p>
|
||||
<button onClick=${() => this.setState({showSwitchAccount: !this.state.showSwitchAccount})}>${t('switch_account')}</button>
|
||||
</p>
|
||||
${this.state.showSwitchAccount ? html`<${ExistingAccountLogin}/>` : ''}
|
||||
<h4>${t('private_key')}</h4>
|
||||
<p dangerouslySetInnerHTML=${{ __html: t('private_key_warning') }} ></p>
|
||||
<p>
|
||||
<button onClick=${() => downloadKey()}>${t('download_private_key')}</button>
|
||||
<${CopyButton} notShareable=${true} text=${t('copy_private_key')} copyStr=${JSON.stringify(Session.getKey())}/>
|
||||
</p>
|
||||
<p>
|
||||
<button onClick=${e => togglePrivateKeyQR(e)}>${t('show_privkey_qr')}</button>
|
||||
</p>
|
||||
<div id="private-key-qr" class="qr-container"></div>
|
||||
<p>
|
||||
${t('email_privkey_to_yourself')}:
|
||||
</p>
|
||||
<p>
|
||||
<form onSubmit=${e => this.mailtoSubmit(e)}>
|
||||
<input name="email" type="email" onChange=${e => this.setState({email:e.target.value.trim()})} placeholder=${t('email')}/>
|
||||
<input name="verify_email" type="email" onChange=${e => this.setState({retypeEmail:e.target.value.trim()})} placeholder=${t('retype_email')}/>
|
||||
<button type="submit">${t('go')}</button>
|
||||
</form>
|
||||
</p>
|
||||
<p><small dangerouslySetInnerHTML=${{ __html: t('privkey_storage_recommendation')}}></small></p>
|
||||
<hr/>
|
||||
<h3>${t('language')}</h3>
|
||||
<p><${LanguageSelector} key="moi"/></p>
|
||||
<hr/>
|
||||
<h3>${t('notifications')}</h3>
|
||||
<p>${t('web_push_subscriptions')}</p>
|
||||
<div class="flex-table">
|
||||
${Object.keys(this.state.webPushSubscriptions).map(k => {
|
||||
const v = this.state.webPushSubscriptions[k];
|
||||
return html`
|
||||
<div class="flex-row">
|
||||
<div class="flex-cell">${v.endpoint}</div>
|
||||
<div class="flex-cell no-flex">
|
||||
<button onClick=${() => Notifications.removeSubscription(k)}>${t('remove')}</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
<hr/>
|
||||
<${PeerSettings}/>
|
||||
<p>${t('also')} <a href="https://github.com/amark/gun#docker">Docker</a> ${t('or_small')} <a href="https://github.com/irislib/iris-electron">Iris-electron</a>.</p>
|
||||
${Helpers.isElectron ? html`
|
||||
<hr/>
|
||||
<h3>Desktop</h3>
|
||||
<p><input type="checkbox" checked=${this.state.electron.openAtLogin} onChange=${() => State.electron.get('settings').get('openAtLogin').put(!this.state.electron.openAtLogin)} id="openAtLogin"/><label for="openAtLogin">Open at login</label></p>
|
||||
<p><input type="checkbox" checked=${this.state.electron.minimizeOnClose} onChange=${() => State.electron.get('settings').get('minimizeOnClose').put(!this.state.electron.minimizeOnClose)} id="minimizeOnClose"/><label for="minimizeOnClose">Minimize on close</label></p>
|
||||
`: ''}
|
||||
<hr/>
|
||||
<h3>${t('webtorrent')}</h3>
|
||||
<p><input type="checkbox" checked=${this.state.local.enableWebtorrent} onChange=${() => State.local.get('settings').get('enableWebtorrent').put(!this.state.local.enableWebtorrent)} id="enableWebtorrent"/><label for="enableWebtorrent">${t('automatically_load_webtorrent_attachments')}</label></p>
|
||||
<p><input type="checkbox" checked=${this.state.local.autoplayWebtorrent} onChange=${() => State.local.get('settings').get('autoplayWebtorrent').put(!this.state.local.autoplayWebtorrent)} id="autoplayWebtorrent"/><label for="autoplayWebtorrent">${t('autoplay_webtorrent_videos')}</label></p>
|
||||
<hr/>
|
||||
<h3>${t('webrtc_connection_options')}</h3>
|
||||
<p><small>${t('webrtc_info')}</small></p>
|
||||
<p><textarea rows="4" id="rtc-config" placeholder="${t('webrtc_connection_options')}" onChange=${e => this.rtcConfigChanged(e)}></textarea></p>
|
||||
<button onClick=${() => this.restoreDefaultRtcConfig()}>${t('restore_defaults')}</button>
|
||||
<hr/>
|
||||
<h3>${t('blocked_users')}</h3>
|
||||
${blockedUsers.map(user => {
|
||||
if (this.state.blockedUsers[user]) {
|
||||
return html`<p><a href="/profile/${encodeURIComponent(user)}"><iris-text user=${user} path="profile/name" placeholder="User"/></a></p>`;
|
||||
}
|
||||
})}
|
||||
${blockedUsers.length === 0 ? t('none') : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
rtcConfigChanged(e) {
|
||||
setRTCConfig(JSON.parse(e.target.value));
|
||||
}
|
||||
|
||||
restoreDefaultRtcConfig() {
|
||||
setRTCConfig(DEFAULT_RTC_CONFIG);
|
||||
$('#rtc-config').val(JSON.stringify(getRTCConfig()));
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const blockedUsers = {};
|
||||
|
||||
$('#rtc-config').val(JSON.stringify(getRTCConfig()));
|
||||
|
||||
State.electron && State.electron.get('settings').on(this.inject('electron', 'electron'));
|
||||
State.local.get('settings').on(this.inject('local', 'local'));
|
||||
State.public.user().get('webPushSubscriptions').map().on(this.sub(
|
||||
() => this.setState({webPushSubscriptions: Notifications.webPushSubscriptions})
|
||||
));
|
||||
State.public.user().get('block').map().on(this.sub(
|
||||
(v,k) => {
|
||||
blockedUsers[k] = v;
|
||||
this.setState({blockedUsers});
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function togglePrivateKeyQR(e) {
|
||||
let btn = $(e.target);
|
||||
let show = $('#private-key-qr img').length === 0;
|
||||
let SHOW_TEXT = t('show_privkey_qr');
|
||||
let hidePrivateKeyInterval;
|
||||
function reset() {
|
||||
clearInterval(hidePrivateKeyInterval);
|
||||
$('#private-key-qr').empty();
|
||||
btn.text(SHOW_TEXT);
|
||||
}
|
||||
function hideText(s) { return `${t('hide_privkey_qr') } (${ s })`; }
|
||||
if (show) {
|
||||
let showPrivateKeySecondsRemaining = 20;
|
||||
btn.text(hideText(showPrivateKeySecondsRemaining));
|
||||
hidePrivateKeyInterval = setInterval(() => {
|
||||
if ($('#private-key-qr img').length === 0) {
|
||||
clearInterval(hidePrivateKeyInterval);return;
|
||||
}
|
||||
showPrivateKeySecondsRemaining -= 1;
|
||||
if (showPrivateKeySecondsRemaining === 0) {
|
||||
reset();
|
||||
} else {
|
||||
btn.text(hideText(showPrivateKeySecondsRemaining));
|
||||
}
|
||||
}, 1000);
|
||||
let qrCodeEl = $('#private-key-qr');
|
||||
new QRCode(qrCodeEl[0], {
|
||||
text: JSON.stringify(Session.getKey()),
|
||||
width: 300,
|
||||
height: 300,
|
||||
colorDark : "#000000",
|
||||
colorLight : "#ffffff",
|
||||
correctLevel : QRCode.CorrectLevel.H
|
||||
});
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
function downloadKey() {
|
||||
const key = Session.getKey();
|
||||
delete key['#'];
|
||||
return Helpers.download('iris_private_key.txt', JSON.stringify(key), 'text/plain', 'utf-8');
|
||||
}
|
||||
|
||||
export default Settings;
|
Loading…
Reference in New Issue
Block a user