New media permission, show dialog when not enabled for voice msg
UI now in separate renderer: - the permissions popup - settings dialog - debug log dialog - about window Couple bug fixes: - About Window: Fix 'escape' to close window - Remove outdated dist/copy tasks from Gruntfile Eslintified settings_view.js
This commit is contained in:
parent
9d9a797bda
commit
ad4387803b
33 changed files with 1192 additions and 383 deletions
|
@ -40,6 +40,7 @@ ts/**/*.js
|
||||||
!js/expiring_messages.js
|
!js/expiring_messages.js
|
||||||
!js/views/attachment_view.js
|
!js/views/attachment_view.js
|
||||||
!js/views/backbone_wrapper_view.js
|
!js/views/backbone_wrapper_view.js
|
||||||
|
!js/views/clear_data_view.js
|
||||||
!js/views/conversation_search_view.js
|
!js/views/conversation_search_view.js
|
||||||
!js/views/conversation_view.js
|
!js/views/conversation_view.js
|
||||||
!js/views/debug_log_view.js
|
!js/views/debug_log_view.js
|
||||||
|
|
|
@ -105,12 +105,15 @@ module.exports = function(grunt) {
|
||||||
'!js/expiring_messages.js',
|
'!js/expiring_messages.js',
|
||||||
'!js/modules/**/*.js',
|
'!js/modules/**/*.js',
|
||||||
'!js/Mp3LameEncoder.min.js',
|
'!js/Mp3LameEncoder.min.js',
|
||||||
|
'!js/settings_start.js',
|
||||||
'!js/signal_protocol_store.js',
|
'!js/signal_protocol_store.js',
|
||||||
|
'!js/views/clear_data_view.js',
|
||||||
'!js/views/conversation_search_view.js',
|
'!js/views/conversation_search_view.js',
|
||||||
'!js/views/conversation_view.js',
|
'!js/views/conversation_view.js',
|
||||||
'!js/views/debug_log_view.js',
|
'!js/views/debug_log_view.js',
|
||||||
'!js/views/file_input_view.js',
|
'!js/views/file_input_view.js',
|
||||||
'!js/views/message_view.js',
|
'!js/views/message_view.js',
|
||||||
|
'!js/views/settings_view.js',
|
||||||
'!js/models/conversations.js',
|
'!js/models/conversations.js',
|
||||||
'!js/models/messages.js',
|
'!js/models/messages.js',
|
||||||
'!js/WebAudioRecorderMp3.js',
|
'!js/WebAudioRecorderMp3.js',
|
||||||
|
@ -134,10 +137,6 @@ module.exports = function(grunt) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
dist: {
|
|
||||||
files: ['<%= dist.src %>', '<%= dist.res %>'],
|
|
||||||
tasks: ['copy_dist'],
|
|
||||||
},
|
|
||||||
libtextsecure: {
|
libtextsecure: {
|
||||||
files: ['./libtextsecure/*.js', './libtextsecure/storage/*.js'],
|
files: ['./libtextsecure/*.js', './libtextsecure/storage/*.js'],
|
||||||
tasks: ['concat:libtextsecure'],
|
tasks: ['concat:libtextsecure'],
|
||||||
|
@ -461,7 +460,6 @@ module.exports = function(grunt) {
|
||||||
grunt.registerTask('dev', ['default', 'watch']);
|
grunt.registerTask('dev', ['default', 'watch']);
|
||||||
grunt.registerTask('lint', ['jshint']);
|
grunt.registerTask('lint', ['jshint']);
|
||||||
grunt.registerTask('test', ['unit-tests', 'lib-unit-tests']);
|
grunt.registerTask('test', ['unit-tests', 'lib-unit-tests']);
|
||||||
grunt.registerTask('copy_dist', ['gitinfo', 'copy:res', 'copy:src']);
|
|
||||||
grunt.registerTask('date', ['gitinfo', 'getExpireTime']);
|
grunt.registerTask('date', ['gitinfo', 'getExpireTime']);
|
||||||
grunt.registerTask('default', [
|
grunt.registerTask('default', [
|
||||||
'exec:build-protobuf',
|
'exec:build-protobuf',
|
||||||
|
|
|
@ -514,6 +514,10 @@
|
||||||
"message": "Report an Issue",
|
"message": "Report an Issue",
|
||||||
"description": "Item under the Help menu, takes you to GitHub new issue form (title case)"
|
"description": "Item under the Help menu, takes you to GitHub new issue form (title case)"
|
||||||
},
|
},
|
||||||
|
"signalDesktopPreferences": {
|
||||||
|
"message": "Signal Desktop Preferences",
|
||||||
|
"description": "Title of the window that pops up with Signal Desktop preferences in it"
|
||||||
|
},
|
||||||
"aboutSignalDesktop": {
|
"aboutSignalDesktop": {
|
||||||
"message": "About Signal Desktop",
|
"message": "About Signal Desktop",
|
||||||
"description": "Item under the Help menu, which opens a small about window"
|
"description": "Item under the Help menu, which opens a small about window"
|
||||||
|
@ -610,6 +614,18 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"audioPermissionNeeded": {
|
||||||
|
"message": "To send audio messages, allow Signal Desktop to access your microphone.",
|
||||||
|
"description": "Shown if the user attempts to send an audio message without audio permssions turned on"
|
||||||
|
},
|
||||||
|
"allowAccess": {
|
||||||
|
"message": "Allow Access",
|
||||||
|
"description": "Button shown in popup asking to enable microphon/video permissions to send audio messages"
|
||||||
|
},
|
||||||
|
"showSettings": {
|
||||||
|
"message": "Show Settings",
|
||||||
|
"description": "A button shown in dialog requesting the user to turn on audio permissions"
|
||||||
|
},
|
||||||
"audio": {
|
"audio": {
|
||||||
"message": "Audio",
|
"message": "Audio",
|
||||||
"description": "Shown in a quotation of a message containing an audio attachment if no text was originally provided with that attachment"
|
"description": "Shown in a quotation of a message containing an audio attachment if no text was originally provided with that attachment"
|
||||||
|
@ -795,6 +811,14 @@
|
||||||
"message": "Theme",
|
"message": "Theme",
|
||||||
"description": "Header for theme settings"
|
"description": "Header for theme settings"
|
||||||
},
|
},
|
||||||
|
"permissions": {
|
||||||
|
"message": "Permissions",
|
||||||
|
"description": "Header for permissions section of settings"
|
||||||
|
},
|
||||||
|
"mediaPermissionsDescription": {
|
||||||
|
"message": "Allow access to camera and microphone",
|
||||||
|
"description": "Description of the media permission description"
|
||||||
|
},
|
||||||
"clearDataHeader": {
|
"clearDataHeader": {
|
||||||
"message": "Clear Data",
|
"message": "Clear Data",
|
||||||
"description": "Header in the settings dialog for the section dealing with data deletion"
|
"description": "Header in the settings dialog for the section dealing with data deletion"
|
||||||
|
|
98
about.html
98
about.html
|
@ -1,68 +1,50 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link href="stylesheets/manifest.css" rel="stylesheet" type="text/css" />
|
<meta http-equiv="Content-Security-Policy"
|
||||||
<style>
|
content="default-src 'none';
|
||||||
|
child-src 'self';
|
||||||
|
connect-src 'self' https: wss:;
|
||||||
|
font-src 'self';
|
||||||
|
form-action 'self';
|
||||||
|
frame-src 'none';
|
||||||
|
img-src 'self' blob: data:;
|
||||||
|
media-src 'self' blob:;
|
||||||
|
object-src 'none';
|
||||||
|
script-src 'self';
|
||||||
|
style-src 'self' 'unsafe-inline';"
|
||||||
|
>
|
||||||
|
<link href="stylesheets/manifest.css" rel="stylesheet" type="text/css" />
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
text-align: center;
|
||||||
|
background-color: #2090EA;
|
||||||
|
color: white;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
img {
|
||||||
text-align: center;
|
margin-top: 1em;
|
||||||
background-color: #2090EA;
|
}
|
||||||
color: white;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
a {
|
||||||
margin-top: 1em;
|
color: white;
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
a {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<img src='images/icon_250.png'>
|
||||||
|
|
||||||
<img src='images/icon_250.png'>
|
<div class='version'></div>
|
||||||
|
<div class='environment'></div>
|
||||||
<div>
|
<div>
|
||||||
<script>
|
<a href="https://signal.org">signal.org</a>
|
||||||
document.write('v', window.getVersion());
|
</div>
|
||||||
</script>
|
<br>
|
||||||
</div>
|
<div>
|
||||||
<div>
|
<a class="privacy" href="https://signal.org/legal">Terms & Privacy Policy</a>
|
||||||
<script>
|
</div>
|
||||||
const states = [];
|
|
||||||
|
|
||||||
if (window.getEnvironment() !== 'production') {
|
|
||||||
states.push(window.getEnvironment());
|
|
||||||
}
|
|
||||||
if (window.getAppInstance()) {
|
|
||||||
states.push(window.getAppInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
document.write(states.join(' - '));
|
|
||||||
</script>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a href="https://signal.org">signal.org</a>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<div>
|
|
||||||
<a class="privacy" href="https://signal.org/legal">Terms & Privacy Policy</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type='text/javascript' src='node_modules/jquery/dist/jquery.js'></script>
|
|
||||||
<script>
|
|
||||||
$(document).on('keyup', function(e) {
|
|
||||||
if (e.keyCode === 27) {
|
|
||||||
window.closeAbout();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.privacy').text(window.i18n('privacyPolicy'));
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
<script type='text/javascript' src='node_modules/jquery/dist/jquery.js'></script>
|
||||||
|
<script type='text/javascript' src='js/about_start.js'></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -10,6 +10,8 @@ window.getEnvironment = () => config.environment;
|
||||||
window.getVersion = () => config.version;
|
window.getVersion = () => config.version;
|
||||||
window.getAppInstance = () => config.appInstance;
|
window.getAppInstance = () => config.appInstance;
|
||||||
|
|
||||||
window.closeAbout = () => ipc.send('close-about');
|
window.closeAbout = () => ipcRenderer.send('close-about');
|
||||||
|
|
||||||
window.i18n = i18n.setup(locale, localeMessages);
|
window.i18n = i18n.setup(locale, localeMessages);
|
||||||
|
|
||||||
|
require('./js/logging');
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
const PERMISSIONS = {
|
const PERMISSIONS = {
|
||||||
// Allowed
|
// Allowed
|
||||||
fullscreen: true, // required to show videos in full-screen
|
fullscreen: true, // required to show videos in full-screen
|
||||||
media: true, // required for access to microphone, used for voice notes
|
|
||||||
notifications: true, // required to show OS notifications for new messages
|
notifications: true, // required to show OS notifications for new messages
|
||||||
|
|
||||||
|
// Off by default, can be enabled by user
|
||||||
|
media: false, // required for access to microphone, used for voice notes
|
||||||
|
|
||||||
// Not allowed
|
// Not allowed
|
||||||
geolocation: false,
|
geolocation: false,
|
||||||
midiSysex: false,
|
midiSysex: false,
|
||||||
|
@ -14,18 +16,32 @@ const PERMISSIONS = {
|
||||||
pointerLock: false,
|
pointerLock: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
function _permissionHandler(webContents, permission, callback) {
|
function _createPermissionHandler(userConfig) {
|
||||||
if (PERMISSIONS[permission]) {
|
return (webContents, permission, callback) => {
|
||||||
console.log(`Approving request for permission '${permission}'`);
|
// We default 'media' permission to false, but the user can override that
|
||||||
return callback(true);
|
if (permission === 'media' && userConfig.get('mediaPermissions')) {
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`Denying request for permission '${permission}'`);
|
if (PERMISSIONS[permission]) {
|
||||||
return callback(false);
|
console.log(`Approving request for permission '${permission}'`);
|
||||||
|
return callback(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Denying request for permission '${permission}'`);
|
||||||
|
return callback(false);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function installPermissionsHandler({ session }) {
|
function installPermissionsHandler({ session, userConfig }) {
|
||||||
session.defaultSession.setPermissionRequestHandler(_permissionHandler);
|
// Setting the permission request handler to null first forces any permissions to be
|
||||||
|
// requested again. Without this, revoked permissions might still be available if
|
||||||
|
// they've already been used successfully.
|
||||||
|
session.defaultSession.setPermissionRequestHandler(null);
|
||||||
|
|
||||||
|
session.defaultSession.setPermissionRequestHandler(
|
||||||
|
_createPermissionHandler(userConfig)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
111
background.html
111
background.html
|
@ -519,114 +519,6 @@
|
||||||
{{ learnMore }}
|
{{ learnMore }}
|
||||||
</a>
|
</a>
|
||||||
</script>
|
</script>
|
||||||
<script type='text/x-tmpl-mustache' id='debug-log'>
|
|
||||||
<div class='content'>
|
|
||||||
<div>
|
|
||||||
<a class='x close' alt='close debug log' href='#'></a>
|
|
||||||
<h1> {{ title }} </h1>
|
|
||||||
<p> {{ debugLogExplanation }}</p>
|
|
||||||
</div>
|
|
||||||
<textarea spellcheck='false' rows='5'></textarea>
|
|
||||||
<div class='buttons'>
|
|
||||||
<button class='grey submit'>{{ submit }}</button>
|
|
||||||
</div>
|
|
||||||
<div class='result'>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
<script type='text/x-tmpl-mustache' id='debug-log-link'>
|
|
||||||
<div class='input-group clearfix'>
|
|
||||||
<input type='text' class='link' readonly value='{{ url }}' />
|
|
||||||
<a class='open' alt='open in a new browser tab' target='_blank' href='{{ url }}'></a>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
<a class='report-link' target='_blank'
|
|
||||||
href='https://github.com/signalapp/Signal-Desktop/issues/new/'>
|
|
||||||
{{ reportIssue }}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</script>
|
|
||||||
<script type='text/x-tmpl-mustache' id='settings'>
|
|
||||||
<div class='content'>
|
|
||||||
<a class='x close' alt='close settings' href='#'></a>
|
|
||||||
<h2>{{ settings }}</h2>
|
|
||||||
<div class='device-name-settings'>
|
|
||||||
<b>{{ deviceNameLabel }}:</b> {{ deviceName }}
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class='theme-settings'>
|
|
||||||
<h3>{{ theme }}</h3>
|
|
||||||
<div>
|
|
||||||
<input type='radio' name='theme' id='theme-setting-android' value='android'>
|
|
||||||
<label for='theme-setting-android'>Android</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type='radio' name='theme' id='theme-setting-android-dark' value='android-dark'>
|
|
||||||
<label for='theme-setting-android-dark'>{{ themeAndroidDark }}</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type='radio' name='theme' id='theme-setting-ios' value='ios'/>
|
|
||||||
<label for='theme-setting-ios'>iOS</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<div class='menu-bar-setting'>
|
|
||||||
<input type='checkbox' name='hide-menu-bar' id='hide-menu-bar'/>
|
|
||||||
<label for='hide-menu-bar'>{{ hideMenuBar }}</label>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class='notification-settings'>
|
|
||||||
<h3>{{ notifications }}</h3>
|
|
||||||
<p>{{ notificationSettingsDialog }}</p>
|
|
||||||
<div>
|
|
||||||
<input type='radio' name='notifications' id='notification-setting-message' value='message'>
|
|
||||||
<label for='notification-setting-message'>{{ nameAndMessage }} </label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type='radio' name='notifications' id='notification-setting-name' value='name'/>
|
|
||||||
<label for='notification-setting-name'>{{ nameOnly }} </label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type='radio' name='notifications' id='notification-setting-count' value='count'/>
|
|
||||||
<label for='notification-setting-count'>{{ noNameOrMessage }} </label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type='radio' name='notifications' id='notification-setting-off' value='off'/>
|
|
||||||
<label for='notification-setting-off'>{{ disableNotifications }} </label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
{{ #isAudioNotificationSupported }}
|
|
||||||
<div class='audio-notification-setting'>
|
|
||||||
<input type='checkbox' name='audio-notification' id='audio-notification'/>
|
|
||||||
<label for='audio-notification'>{{ audioNotificationDescription }}</label>
|
|
||||||
</div>
|
|
||||||
{{ /isAudioNotificationSupported }}
|
|
||||||
<div class='sync-setting'></div>
|
|
||||||
<hr>
|
|
||||||
<div class='clear-data-settings'>
|
|
||||||
<h3>{{ clearDataHeader }}</h3>
|
|
||||||
<div>
|
|
||||||
<button class='grey destructive clear-data'>{{ clearDataButton }}</button>
|
|
||||||
<p>{{ clearDataExplanation }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
<script type='text/x-tmpl-mustache' id='syncSettings'>
|
|
||||||
<hr>
|
|
||||||
<h3>{{ sync }}</h3>
|
|
||||||
<div>
|
|
||||||
<button class='grey sync'>{{ syncNow }}</button>
|
|
||||||
<p>
|
|
||||||
{{ syncExplanation }}
|
|
||||||
<div class='synced_at'>
|
|
||||||
{{ lastSynced }} {{ syncDate }} {{ syncTime }}
|
|
||||||
</div>
|
|
||||||
<div class='sync_failed'>{{ syncFailed }}</div>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
<script type='text/x-tmpl-mustache' id='clear-data'>
|
<script type='text/x-tmpl-mustache' id='clear-data'>
|
||||||
{{#isStep1}}
|
{{#isStep1}}
|
||||||
<div class='step'>
|
<div class='step'>
|
||||||
|
@ -904,7 +796,6 @@
|
||||||
<script type='text/javascript' src='js/views/whisper_view.js'></script>
|
<script type='text/javascript' src='js/views/whisper_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/last_seen_indicator_view.js'></script>
|
<script type='text/javascript' src='js/views/last_seen_indicator_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/scroll_down_button_view.js'></script>
|
<script type='text/javascript' src='js/views/scroll_down_button_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/debug_log_view.js'></script>
|
|
||||||
<script type='text/javascript' src='js/views/toast_view.js'></script>
|
<script type='text/javascript' src='js/views/toast_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/attachment_preview_view.js'></script>
|
<script type='text/javascript' src='js/views/attachment_preview_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/file_input_view.js'></script>
|
<script type='text/javascript' src='js/views/file_input_view.js'></script>
|
||||||
|
@ -929,7 +820,6 @@
|
||||||
<script type='text/javascript' src='js/views/network_status_view.js'></script>
|
<script type='text/javascript' src='js/views/network_status_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/confirmation_dialog_view.js'></script>
|
<script type='text/javascript' src='js/views/confirmation_dialog_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/identicon_svg_view.js'></script>
|
<script type='text/javascript' src='js/views/identicon_svg_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/settings_view.js'></script>
|
|
||||||
<script type='text/javascript' src='js/views/install_view.js'></script>
|
<script type='text/javascript' src='js/views/install_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/banner_view.js'></script>
|
<script type='text/javascript' src='js/views/banner_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/identity_key_send_error_view.js'></script>
|
<script type='text/javascript' src='js/views/identity_key_send_error_view.js'></script>
|
||||||
|
@ -937,6 +827,7 @@
|
||||||
<script type='text/javascript' src='js/views/standalone_registration_view.js'></script>
|
<script type='text/javascript' src='js/views/standalone_registration_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/app_view.js'></script>
|
<script type='text/javascript' src='js/views/app_view.js'></script>
|
||||||
<script type='text/javascript' src='js/views/import_view.js'></script>
|
<script type='text/javascript' src='js/views/import_view.js'></script>
|
||||||
|
<script type='text/javascript' src='js/views/clear_data_view.js'></script>
|
||||||
|
|
||||||
<script type='text/javascript' src='js/wall_clock_listener.js'></script>
|
<script type='text/javascript' src='js/wall_clock_listener.js'></script>
|
||||||
<script type='text/javascript' src='js/rotate_signed_prekey_listener.js'></script>
|
<script type='text/javascript' src='js/rotate_signed_prekey_listener.js'></script>
|
||||||
|
|
53
debug_log.html
Normal file
53
debug_log.html
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'none';
|
||||||
|
child-src 'self';
|
||||||
|
connect-src 'self' https: wss:;
|
||||||
|
font-src 'self';
|
||||||
|
form-action 'self';
|
||||||
|
frame-src 'none';
|
||||||
|
img-src 'self' blob: data:;
|
||||||
|
media-src 'self' blob:;
|
||||||
|
object-src 'none';
|
||||||
|
script-src 'self';
|
||||||
|
style-src 'self' 'unsafe-inline';"
|
||||||
|
>
|
||||||
|
<link href="stylesheets/manifest.css" rel="stylesheet" type="text/css" />
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class='debug-log-window'>
|
||||||
|
</body>
|
||||||
|
<script type='text/x-tmpl-mustache' id='debug-log'>
|
||||||
|
<div class='content'>
|
||||||
|
<div>
|
||||||
|
<a class='x close' alt='close debug log' href='#'></a>
|
||||||
|
<h1> {{ title }} </h1>
|
||||||
|
<p> {{ debugLogExplanation }}</p>
|
||||||
|
</div>
|
||||||
|
<textarea spellcheck='false' rows='5'></textarea>
|
||||||
|
<div class='buttons'>
|
||||||
|
<button class='grey submit'>{{ submit }}</button>
|
||||||
|
</div>
|
||||||
|
<div class='result'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
<script type='text/x-tmpl-mustache' id='debug-log-link'>
|
||||||
|
<div class='input-group clearfix'>
|
||||||
|
<input type='text' class='link' readonly value='{{ url }}' />
|
||||||
|
<a class='open' alt='open in a new browser tab' target='_blank' href='{{ url }}'></a>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
<a class='report-link' target='_blank'
|
||||||
|
href='https://github.com/signalapp/Signal-Desktop/issues/new/'>
|
||||||
|
{{ reportIssue }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</script>
|
||||||
|
<script type='text/javascript' src='js/components.js'></script>
|
||||||
|
<script type='text/javascript' src='js/views/whisper_view.js'></script>
|
||||||
|
<script type='text/javascript' src='js/views/debug_log_view.js'></script>
|
||||||
|
<script type='text/javascript' src='js/debug_log_start.js'></script>
|
||||||
|
</html>
|
19
debug_log_preload.js
Normal file
19
debug_log_preload.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
const { ipcRenderer } = require('electron');
|
||||||
|
const url = require('url');
|
||||||
|
const i18n = require('./js/modules/i18n');
|
||||||
|
|
||||||
|
const config = url.parse(window.location.toString(), true).query;
|
||||||
|
const { locale } = config;
|
||||||
|
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||||
|
|
||||||
|
window.i18n = i18n.setup(locale, localeMessages);
|
||||||
|
|
||||||
|
// got.js appears to need this to successfully submit debug logs to the cloud
|
||||||
|
window.nodeSetImmediate = setImmediate;
|
||||||
|
|
||||||
|
window.getNodeVersion = () => config.node_version;
|
||||||
|
window.getEnvironment = () => config.environment;
|
||||||
|
|
||||||
|
require('./js/logging');
|
||||||
|
|
||||||
|
window.closeDebugLog = () => ipcRenderer.send('close-debug-log');
|
24
js/about_start.js
Normal file
24
js/about_start.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Add version
|
||||||
|
$('.version').text(`v${window.getVersion()}`);
|
||||||
|
|
||||||
|
// Add debugging metadata - environment if not production, app instance name
|
||||||
|
const states = [];
|
||||||
|
|
||||||
|
if (window.getEnvironment() !== 'production') {
|
||||||
|
states.push(window.getEnvironment());
|
||||||
|
}
|
||||||
|
if (window.getAppInstance()) {
|
||||||
|
states.push(window.getAppInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.environment').text(states.join(' - '));
|
||||||
|
|
||||||
|
// Install the 'dismiss with escape key' handler
|
||||||
|
$(document).on('keyup', function(e) {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
window.closeAbout();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Localize the privacy string
|
||||||
|
$('.privacy').text(window.i18n('privacyPolicy'));
|
|
@ -123,6 +123,54 @@
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
|
|
||||||
|
// These make key operations available to IPC handlers created in preload.js
|
||||||
|
window.Events = {
|
||||||
|
getDeviceName: () => textsecure.storage.user.getDeviceName(),
|
||||||
|
|
||||||
|
getThemeSetting: () => storage.get('theme-setting'),
|
||||||
|
setThemeSetting: value => {
|
||||||
|
storage.put('theme-setting', value);
|
||||||
|
onChangeTheme();
|
||||||
|
},
|
||||||
|
getHideMenuBar: () => storage.get('hide-menu-bar'),
|
||||||
|
setHideMenuBar: value => {
|
||||||
|
storage.get('hide-menu-bar', value);
|
||||||
|
window.setAutoHideMenuBar(value);
|
||||||
|
window.setMenuBarVisibility(!value);
|
||||||
|
},
|
||||||
|
|
||||||
|
getNotificationSetting: () =>
|
||||||
|
storage.get('notification-setting', 'message'),
|
||||||
|
setNotificationSetting: value =>
|
||||||
|
storage.get('notification-setting', value),
|
||||||
|
getAudioNotification: () => storage.get('audio-notification'),
|
||||||
|
setAudioNotification: value => storage.put('audio-notification', value),
|
||||||
|
|
||||||
|
// eslint-disable-next-line eqeqeq
|
||||||
|
isPrimary: () => textsecure.storage.user.getDeviceId() == '1',
|
||||||
|
getSyncRequest: () =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const syncRequest = window.getSyncRequest();
|
||||||
|
syncRequest.addEventListener('success', resolve);
|
||||||
|
syncRequest.addEventListener('timeout', reject);
|
||||||
|
}),
|
||||||
|
getLastSyncTime: () => storage.get('synced_at'),
|
||||||
|
setLastSyncTime: value => storage.put('synced_at', value),
|
||||||
|
|
||||||
|
addDarkOverlay: () => {
|
||||||
|
if ($('.dark-overlay').length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$(document.body).prepend('<div class="dark-overlay"></div>');
|
||||||
|
$('.dark-overlay').on('click', () => $('.dark-overlay').remove());
|
||||||
|
},
|
||||||
|
removeDarkOverlay: () => $('.dark-overlay').remove(),
|
||||||
|
deleteAllData: () => {
|
||||||
|
const clearDataView = new window.Whisper.ClearDataView().render();
|
||||||
|
$('body').append(clearDataView.el);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ConversationController.load();
|
await ConversationController.load();
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -208,16 +256,6 @@
|
||||||
Whisper.events.on('showDebugLog', () => {
|
Whisper.events.on('showDebugLog', () => {
|
||||||
appView.openDebugLog();
|
appView.openDebugLog();
|
||||||
});
|
});
|
||||||
Whisper.events.on('showSettings', () => {
|
|
||||||
if (!appView || !appView.inboxView) {
|
|
||||||
console.log(
|
|
||||||
"background: Event: 'showSettings':" +
|
|
||||||
' Expected `appView.inboxView` to exist.'
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
appView.inboxView.showSettings();
|
|
||||||
});
|
|
||||||
Whisper.events.on('unauthorized', () => {
|
Whisper.events.on('unauthorized', () => {
|
||||||
appView.inboxView.networkStatusView.update();
|
appView.inboxView.networkStatusView.update();
|
||||||
});
|
});
|
||||||
|
|
13
js/debug_log_start.js
Normal file
13
js/debug_log_start.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
$(document).on('keyup', function(e) {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
window.closeDebugLog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const $body = $(document.body);
|
||||||
|
|
||||||
|
// got.js appears to need this to successfully submit debug logs to the cloud
|
||||||
|
window.setImmediate = window.nodeSetImmediate;
|
||||||
|
|
||||||
|
window.view = new Whisper.DebugLogView();
|
||||||
|
window.view.$el.appendTo($body);
|
|
@ -55,32 +55,23 @@ const Initialization = require('./views/initialization');
|
||||||
const { IdleDetector } = require('./idle_detector');
|
const { IdleDetector } = require('./idle_detector');
|
||||||
const MessageDataMigrator = require('./messages_data_migrator');
|
const MessageDataMigrator = require('./messages_data_migrator');
|
||||||
|
|
||||||
exports.setup = (options = {}) => {
|
function initializeMigrations({
|
||||||
const { Attachments, userDataPath, getRegionCode } = options;
|
Attachments,
|
||||||
|
userDataPath,
|
||||||
const Components = {
|
Type,
|
||||||
ContactDetail,
|
getRegionCode,
|
||||||
ContactName,
|
}) {
|
||||||
ConversationTitle,
|
if (!Attachments) {
|
||||||
EmbeddedContact,
|
return null;
|
||||||
Emojify,
|
}
|
||||||
Lightbox,
|
|
||||||
LightboxGallery,
|
|
||||||
MediaGallery,
|
|
||||||
MessageBody,
|
|
||||||
Types: {
|
|
||||||
Message: MediaGalleryMessage,
|
|
||||||
},
|
|
||||||
Quote,
|
|
||||||
};
|
|
||||||
|
|
||||||
const attachmentsPath = Attachments.getPath(userDataPath);
|
const attachmentsPath = Attachments.getPath(userDataPath);
|
||||||
const readAttachmentData = Attachments.createReader(attachmentsPath);
|
const readAttachmentData = Attachments.createReader(attachmentsPath);
|
||||||
const loadAttachmentData = AttachmentType.loadData(readAttachmentData);
|
const loadAttachmentData = Type.loadData(readAttachmentData);
|
||||||
|
|
||||||
const Migrations = {
|
return {
|
||||||
attachmentsPath,
|
attachmentsPath,
|
||||||
deleteAttachmentData: AttachmentType.deleteData(
|
deleteAttachmentData: Type.deleteData(
|
||||||
Attachments.createDeleter(attachmentsPath)
|
Attachments.createDeleter(attachmentsPath)
|
||||||
),
|
),
|
||||||
getAbsoluteAttachmentPath: Attachments.createAbsolutePathGetter(
|
getAbsoluteAttachmentPath: Attachments.createAbsolutePathGetter(
|
||||||
|
@ -100,6 +91,33 @@ exports.setup = (options = {}) => {
|
||||||
Attachments.createWriterForExisting(attachmentsPath)
|
Attachments.createWriterForExisting(attachmentsPath)
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.setup = (options = {}) => {
|
||||||
|
const { Attachments, userDataPath, getRegionCode } = options;
|
||||||
|
|
||||||
|
const Migrations = initializeMigrations({
|
||||||
|
Attachments,
|
||||||
|
userDataPath,
|
||||||
|
Type: AttachmentType,
|
||||||
|
getRegionCode,
|
||||||
|
});
|
||||||
|
|
||||||
|
const Components = {
|
||||||
|
ContactDetail,
|
||||||
|
ContactName,
|
||||||
|
ConversationTitle,
|
||||||
|
EmbeddedContact,
|
||||||
|
Emojify,
|
||||||
|
Lightbox,
|
||||||
|
LightboxGallery,
|
||||||
|
MediaGallery,
|
||||||
|
MessageBody,
|
||||||
|
Types: {
|
||||||
|
Message: MediaGalleryMessage,
|
||||||
|
},
|
||||||
|
Quote,
|
||||||
|
};
|
||||||
|
|
||||||
const Types = {
|
const Types = {
|
||||||
Attachment: AttachmentType,
|
Attachment: AttachmentType,
|
||||||
|
|
|
@ -134,6 +134,8 @@
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log({ title, message, iconUrl });
|
||||||
|
|
||||||
const shouldHideExpiringMessageBody =
|
const shouldHideExpiringMessageBody =
|
||||||
last.isExpiringMessage && Signal.OS.isMacOS();
|
last.isExpiringMessage && Signal.OS.isMacOS();
|
||||||
if (shouldHideExpiringMessageBody) {
|
if (shouldHideExpiringMessageBody) {
|
||||||
|
|
19
js/permissions_popup_start.js
Normal file
19
js/permissions_popup_start.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
$(document).on('keyup', function(e) {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
window.closePermissionsPopup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const $body = $(document.body);
|
||||||
|
|
||||||
|
window.view = new Whisper.ConfirmationDialogView({
|
||||||
|
message: i18n('audioPermissionNeeded'),
|
||||||
|
okText: i18n('allowAccess'),
|
||||||
|
resolve: () => {
|
||||||
|
window.setMediaPermissions(true);
|
||||||
|
window.closePermissionsPopup();
|
||||||
|
},
|
||||||
|
reject: window.closePermissionsPopup,
|
||||||
|
});
|
||||||
|
|
||||||
|
window.view.$el.appendTo($body);
|
29
js/settings_start.js
Normal file
29
js/settings_start.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
$(document).on('keyup', function(e) {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
window.closeSettings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const $body = $(document.body);
|
||||||
|
|
||||||
|
const getInitialData = async () => ({
|
||||||
|
deviceName: await window.getDeviceName(),
|
||||||
|
|
||||||
|
themeSetting: await window.getThemeSetting(),
|
||||||
|
hideMenuBar: await window.getHideMenuBar(),
|
||||||
|
|
||||||
|
notificationSetting: await window.getNotificationSetting(),
|
||||||
|
audioNotification: await window.getAudioNotification(),
|
||||||
|
|
||||||
|
mediaPermissions: await window.getMediaPermissions(),
|
||||||
|
|
||||||
|
isPrimary: await window.isPrimary(),
|
||||||
|
lastSyncTime: await window.getLastSyncTime(),
|
||||||
|
});
|
||||||
|
|
||||||
|
window.initialRequest = getInitialData();
|
||||||
|
window.initialRequest.then(data => {
|
||||||
|
window.initialData = data;
|
||||||
|
window.view = new Whisper.SettingsView();
|
||||||
|
window.view.$el.appendTo($body);
|
||||||
|
});
|
|
@ -14,8 +14,6 @@
|
||||||
events: {
|
events: {
|
||||||
'click .openInstaller': 'openInstaller', // NetworkStatusView has this button
|
'click .openInstaller': 'openInstaller', // NetworkStatusView has this button
|
||||||
openInbox: 'openInbox',
|
openInbox: 'openInbox',
|
||||||
'change-theme': 'applyTheme',
|
|
||||||
'change-hide-menu': 'applyHideMenu',
|
|
||||||
},
|
},
|
||||||
applyTheme: function() {
|
applyTheme: function() {
|
||||||
var theme = storage.get('theme-setting') || 'android';
|
var theme = storage.get('theme-setting') || 'android';
|
||||||
|
|
69
js/views/clear_data_view.js
Normal file
69
js/views/clear_data_view.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/* global i18n: false */
|
||||||
|
/* global Whisper: false */
|
||||||
|
|
||||||
|
/* eslint-disable no-new */
|
||||||
|
|
||||||
|
// eslint-disable-next-line func-names
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.Whisper = window.Whisper || {};
|
||||||
|
const { Database } = window.Whisper;
|
||||||
|
const { Logs } = window.Signal;
|
||||||
|
|
||||||
|
const CLEAR_DATA_STEPS = {
|
||||||
|
CHOICE: 1,
|
||||||
|
DELETING: 2,
|
||||||
|
};
|
||||||
|
window.Whisper.ClearDataView = Whisper.View.extend({
|
||||||
|
templateName: 'clear-data',
|
||||||
|
className: 'full-screen-flow overlay',
|
||||||
|
events: {
|
||||||
|
'click .cancel': 'onCancel',
|
||||||
|
'click .delete-all-data': 'onDeleteAllData',
|
||||||
|
},
|
||||||
|
initialize() {
|
||||||
|
this.step = CLEAR_DATA_STEPS.CHOICE;
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
this.remove();
|
||||||
|
},
|
||||||
|
async onDeleteAllData() {
|
||||||
|
console.log('Deleting everything!');
|
||||||
|
this.step = CLEAR_DATA_STEPS.DELETING;
|
||||||
|
this.render();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Database.close();
|
||||||
|
console.log('All database connections closed. Starting delete.');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Something went wrong closing all database connections.');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clearAllData();
|
||||||
|
},
|
||||||
|
async clearAllData() {
|
||||||
|
try {
|
||||||
|
await Promise.all([Logs.deleteAll(), Database.drop()]);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(
|
||||||
|
'Something went wrong deleting all data:',
|
||||||
|
error && error.stack ? error.stack : error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
window.restart();
|
||||||
|
},
|
||||||
|
render_attributes() {
|
||||||
|
return {
|
||||||
|
isStep1: this.step === CLEAR_DATA_STEPS.CHOICE,
|
||||||
|
header: i18n('deleteAllDataHeader'),
|
||||||
|
body: i18n('deleteAllDataBody'),
|
||||||
|
cancelButton: i18n('cancel'),
|
||||||
|
deleteButton: i18n('deleteAllDataButton'),
|
||||||
|
|
||||||
|
isStep2: this.step === CLEAR_DATA_STEPS.DELETING,
|
||||||
|
deleting: i18n('deleteAllDataProgress'),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
|
@ -42,9 +42,8 @@
|
||||||
close: i18n('gotIt'),
|
close: i18n('gotIt'),
|
||||||
debugLogExplanation: i18n('debugLogExplanation'),
|
debugLogExplanation: i18n('debugLogExplanation'),
|
||||||
},
|
},
|
||||||
close(e) {
|
close() {
|
||||||
e.preventDefault();
|
window.closeDebugLog();
|
||||||
this.remove();
|
|
||||||
},
|
},
|
||||||
async submit(e) {
|
async submit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
@ -233,13 +233,6 @@
|
||||||
reloadBackgroundPage() {
|
reloadBackgroundPage() {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
},
|
},
|
||||||
showSettings() {
|
|
||||||
if (this.$el.find('.settings').length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const view = new Whisper.SettingsView();
|
|
||||||
view.$el.appendTo(this.el);
|
|
||||||
},
|
|
||||||
filterContacts(e) {
|
filterContacts(e) {
|
||||||
this.searchView.filterContacts(e);
|
this.searchView.filterContacts(e);
|
||||||
const input = this.$('input.search');
|
const input = this.$('input.search');
|
||||||
|
|
|
@ -31,17 +31,25 @@
|
||||||
if (this.recorder.isRecording()) {
|
if (this.recorder.isRecording()) {
|
||||||
this.recorder.cancelRecording();
|
this.recorder.cancelRecording();
|
||||||
}
|
}
|
||||||
|
this.recorder = null;
|
||||||
|
|
||||||
if (this.interval) {
|
if (this.interval) {
|
||||||
clearInterval(this.interval);
|
clearInterval(this.interval);
|
||||||
}
|
}
|
||||||
|
this.interval = null;
|
||||||
|
|
||||||
if (this.source) {
|
if (this.source) {
|
||||||
this.source.disconnect();
|
this.source.disconnect();
|
||||||
}
|
}
|
||||||
|
this.source = null;
|
||||||
|
|
||||||
if (this.context) {
|
if (this.context) {
|
||||||
this.context.close().then(function() {
|
this.context.close().then(function() {
|
||||||
console.log('audio context closed');
|
console.log('audio context closed');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
this.context = null;
|
||||||
|
|
||||||
this.remove();
|
this.remove();
|
||||||
this.trigger('closed');
|
this.trigger('closed');
|
||||||
},
|
},
|
||||||
|
@ -74,8 +82,23 @@
|
||||||
this.recorder.startRecording();
|
this.recorder.startRecording();
|
||||||
},
|
},
|
||||||
onError: function(error) {
|
onError: function(error) {
|
||||||
console.log(error.stack);
|
// Protect against out-of-band errors, which can happen if the user revokes media
|
||||||
|
// permissions after successfully accessing the microphone.
|
||||||
|
if (!this.recorder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.close();
|
this.close();
|
||||||
|
|
||||||
|
if (error && error.name === 'PermissionDeniedError') {
|
||||||
|
console.log('RecorderView.onError: Microphone access is not allowed!');
|
||||||
|
window.showPermissionsPopup();
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
'RecorderView.onError:',
|
||||||
|
error && error.stack ? error.stack : error
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,106 +1,119 @@
|
||||||
/* global storage: false */
|
|
||||||
/* global textsecure: false */
|
|
||||||
/* global i18n: false */
|
/* global i18n: false */
|
||||||
/* global Whisper: false */
|
/* global Whisper: false */
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable no-new */
|
||||||
|
|
||||||
|
// eslint-disable-next-line func-names
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
window.Whisper = window.Whisper || {};
|
window.Whisper = window.Whisper || {};
|
||||||
const { Database } = window.Whisper;
|
|
||||||
const { OS, Logs } = window.Signal;
|
|
||||||
const { Settings } = window.Signal.Types;
|
const { Settings } = window.Signal.Types;
|
||||||
|
|
||||||
var CheckboxView = Whisper.View.extend({
|
const CheckboxView = Whisper.View.extend({
|
||||||
initialize: function(options) {
|
initialize(options) {
|
||||||
this.name = options.name;
|
this.setFn = options.setFn;
|
||||||
this.defaultValue = options.defaultValue;
|
this.value = options.value;
|
||||||
this.event = options.event;
|
|
||||||
this.populate();
|
this.populate();
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
change: 'change',
|
change: 'change',
|
||||||
},
|
},
|
||||||
change: function(e) {
|
change(e) {
|
||||||
var value = e.target.checked;
|
const value = e.target.checked;
|
||||||
storage.put(this.name, value);
|
this.setFn(value);
|
||||||
console.log(this.name, 'changed to', value);
|
console.log(this.name, 'changed to', value);
|
||||||
if (this.event) {
|
|
||||||
this.$el.trigger(this.event);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
populate: function() {
|
populate() {
|
||||||
var value = storage.get(this.name, this.defaultValue);
|
this.$('input').prop('checked', !!this.value);
|
||||||
this.$('input').prop('checked', !!value);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
var RadioButtonGroupView = Whisper.View.extend({
|
|
||||||
initialize: function(options) {
|
const MediaPermissionsSettingView = Whisper.View.extend({
|
||||||
this.name = options.name;
|
initialize(options) {
|
||||||
this.defaultValue = options.defaultValue;
|
this.value = options.value;
|
||||||
this.event = options.event;
|
this.setFn = options.setFn;
|
||||||
this.populate();
|
this.populate();
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
change: 'change',
|
change: 'change',
|
||||||
},
|
},
|
||||||
change: function(e) {
|
change(e) {
|
||||||
var value = this.$(e.target).val();
|
this.value = e.target.checked;
|
||||||
storage.put(this.name, value);
|
this.setFn(this.value);
|
||||||
console.log(this.name, 'changed to', value);
|
console.log('media-permissions changed to', this.value);
|
||||||
if (this.event) {
|
|
||||||
this.$el.trigger(this.event);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
populate: function() {
|
populate() {
|
||||||
var value = storage.get(this.name, this.defaultValue);
|
this.$('input').prop('checked', Boolean(this.value));
|
||||||
this.$('#' + this.name + '-' + value).attr('checked', 'checked');
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RadioButtonGroupView = Whisper.View.extend({
|
||||||
|
initialize(options) {
|
||||||
|
this.name = options.name;
|
||||||
|
this.setFn = options.setFn;
|
||||||
|
this.value = options.value;
|
||||||
|
this.populate();
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
change: 'change',
|
||||||
|
},
|
||||||
|
change(e) {
|
||||||
|
const value = this.$(e.target).val();
|
||||||
|
this.setFn(value);
|
||||||
|
console.log(this.name, 'changed to', value);
|
||||||
|
},
|
||||||
|
populate() {
|
||||||
|
this.$(`#${this.name}-${this.value}`).attr('checked', 'checked');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
Whisper.SettingsView = Whisper.View.extend({
|
Whisper.SettingsView = Whisper.View.extend({
|
||||||
className: 'settings modal expand',
|
className: 'settings modal expand',
|
||||||
templateName: 'settings',
|
templateName: 'settings',
|
||||||
initialize: function() {
|
initialize() {
|
||||||
this.deviceName = textsecure.storage.user.getDeviceName();
|
|
||||||
this.render();
|
this.render();
|
||||||
new RadioButtonGroupView({
|
new RadioButtonGroupView({
|
||||||
el: this.$('.notification-settings'),
|
el: this.$('.notification-settings'),
|
||||||
defaultValue: 'message',
|
|
||||||
name: 'notification-setting',
|
name: 'notification-setting',
|
||||||
|
value: window.initialData.notificationSetting,
|
||||||
|
setFn: window.setNotificationSetting,
|
||||||
});
|
});
|
||||||
new RadioButtonGroupView({
|
new RadioButtonGroupView({
|
||||||
el: this.$('.theme-settings'),
|
el: this.$('.theme-settings'),
|
||||||
defaultValue: 'android',
|
|
||||||
name: 'theme-setting',
|
name: 'theme-setting',
|
||||||
event: 'change-theme',
|
value: window.initialData.themeSetting,
|
||||||
|
setFn: window.setThemeSetting,
|
||||||
});
|
});
|
||||||
if (Settings.isAudioNotificationSupported()) {
|
if (Settings.isAudioNotificationSupported()) {
|
||||||
new CheckboxView({
|
new CheckboxView({
|
||||||
el: this.$('.audio-notification-setting'),
|
el: this.$('.audio-notification-setting'),
|
||||||
defaultValue: false,
|
value: window.initialData.audioNotification,
|
||||||
name: 'audio-notification',
|
setFn: window.setAudioNotification,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
new CheckboxView({
|
new CheckboxView({
|
||||||
el: this.$('.menu-bar-setting'),
|
el: this.$('.menu-bar-setting'),
|
||||||
defaultValue: false,
|
value: window.initialData.hideMenuBar,
|
||||||
name: 'hide-menu-bar',
|
setFn: window.setHideMenuBar,
|
||||||
event: 'change-hide-menu',
|
|
||||||
});
|
});
|
||||||
if (textsecure.storage.user.getDeviceId() != '1') {
|
new MediaPermissionsSettingView({
|
||||||
var syncView = new SyncView().render();
|
el: this.$('.media-permissions'),
|
||||||
|
value: window.initialData.mediaPermissions,
|
||||||
|
setFn: window.setMediaPermissions,
|
||||||
|
});
|
||||||
|
if (!window.initialData.isPrimary) {
|
||||||
|
const syncView = new SyncView().render();
|
||||||
this.$('.sync-setting').append(syncView.el);
|
this.$('.sync-setting').append(syncView.el);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
'click .close': 'remove',
|
'click .close': 'onClose',
|
||||||
'click .clear-data': 'onClearData',
|
'click .clear-data': 'onClearData',
|
||||||
},
|
},
|
||||||
render_attributes: function() {
|
render_attributes() {
|
||||||
return {
|
return {
|
||||||
deviceNameLabel: i18n('deviceName'),
|
deviceNameLabel: i18n('deviceName'),
|
||||||
deviceName: this.deviceName,
|
deviceName: window.initialData.deviceName,
|
||||||
theme: i18n('theme'),
|
theme: i18n('theme'),
|
||||||
notifications: i18n('notifications'),
|
notifications: i18n('notifications'),
|
||||||
notificationSettingsDialog: i18n('notificationSettingsDialog'),
|
notificationSettingsDialog: i18n('notificationSettingsDialog'),
|
||||||
|
@ -116,121 +129,72 @@
|
||||||
clearDataHeader: i18n('clearDataHeader'),
|
clearDataHeader: i18n('clearDataHeader'),
|
||||||
clearDataButton: i18n('clearDataButton'),
|
clearDataButton: i18n('clearDataButton'),
|
||||||
clearDataExplanation: i18n('clearDataExplanation'),
|
clearDataExplanation: i18n('clearDataExplanation'),
|
||||||
|
permissions: i18n('permissions'),
|
||||||
|
mediaPermissionsDescription: i18n('mediaPermissionsDescription'),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onClearData: function() {
|
onClose() {
|
||||||
var clearDataView = new ClearDataView().render();
|
window.closeSettings();
|
||||||
$('body').append(clearDataView.el);
|
},
|
||||||
|
onClearData() {
|
||||||
|
window.deleteAllData();
|
||||||
|
window.closeSettings();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/* jshint ignore:start */
|
const SyncView = Whisper.View.extend({
|
||||||
/* eslint-enable */
|
|
||||||
|
|
||||||
const CLEAR_DATA_STEPS = {
|
|
||||||
CHOICE: 1,
|
|
||||||
DELETING: 2,
|
|
||||||
};
|
|
||||||
const ClearDataView = Whisper.View.extend({
|
|
||||||
templateName: 'clear-data',
|
|
||||||
className: 'full-screen-flow overlay',
|
|
||||||
events: {
|
|
||||||
'click .cancel': 'onCancel',
|
|
||||||
'click .delete-all-data': 'onDeleteAllData',
|
|
||||||
},
|
|
||||||
initialize() {
|
|
||||||
this.step = CLEAR_DATA_STEPS.CHOICE;
|
|
||||||
},
|
|
||||||
onCancel() {
|
|
||||||
this.remove();
|
|
||||||
},
|
|
||||||
async onDeleteAllData() {
|
|
||||||
console.log('Deleting everything!');
|
|
||||||
this.step = CLEAR_DATA_STEPS.DELETING;
|
|
||||||
this.render();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Database.close();
|
|
||||||
console.log('All database connections closed. Starting delete.');
|
|
||||||
} catch (error) {
|
|
||||||
console.log('Something went wrong closing all database connections.');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.clearAllData();
|
|
||||||
},
|
|
||||||
async clearAllData() {
|
|
||||||
try {
|
|
||||||
await Promise.all([Logs.deleteAll(), Database.drop()]);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(
|
|
||||||
'Something went wrong deleting all data:',
|
|
||||||
error && error.stack ? error.stack : error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
window.restart();
|
|
||||||
},
|
|
||||||
render_attributes() {
|
|
||||||
return {
|
|
||||||
isStep1: this.step === CLEAR_DATA_STEPS.CHOICE,
|
|
||||||
header: i18n('deleteAllDataHeader'),
|
|
||||||
body: i18n('deleteAllDataBody'),
|
|
||||||
cancelButton: i18n('cancel'),
|
|
||||||
deleteButton: i18n('deleteAllDataButton'),
|
|
||||||
|
|
||||||
isStep2: this.step === CLEAR_DATA_STEPS.DELETING,
|
|
||||||
deleting: i18n('deleteAllDataProgress'),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/* eslint-disable */
|
|
||||||
/* jshint ignore:end */
|
|
||||||
|
|
||||||
var SyncView = Whisper.View.extend({
|
|
||||||
templateName: 'syncSettings',
|
templateName: 'syncSettings',
|
||||||
className: 'syncSettings',
|
className: 'syncSettings',
|
||||||
events: {
|
events: {
|
||||||
'click .sync': 'sync',
|
'click .sync': 'sync',
|
||||||
},
|
},
|
||||||
enable: function() {
|
initialize() {
|
||||||
|
this.lastSyncTime = window.initialData.lastSyncTime;
|
||||||
|
},
|
||||||
|
enable() {
|
||||||
this.$('.sync').text(i18n('syncNow'));
|
this.$('.sync').text(i18n('syncNow'));
|
||||||
this.$('.sync').removeAttr('disabled');
|
this.$('.sync').removeAttr('disabled');
|
||||||
},
|
},
|
||||||
disable: function() {
|
disable() {
|
||||||
this.$('.sync').attr('disabled', 'disabled');
|
this.$('.sync').attr('disabled', 'disabled');
|
||||||
this.$('.sync').text(i18n('syncing'));
|
this.$('.sync').text(i18n('syncing'));
|
||||||
},
|
},
|
||||||
onsuccess: function() {
|
onsuccess() {
|
||||||
storage.put('synced_at', Date.now());
|
window.setLastSyncTime(Date.now());
|
||||||
|
this.lastSyncTime = Date.now();
|
||||||
console.log('sync successful');
|
console.log('sync successful');
|
||||||
this.enable();
|
this.enable();
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
ontimeout: function() {
|
ontimeout() {
|
||||||
console.log('sync timed out');
|
console.log('sync timed out');
|
||||||
this.$('.synced_at').hide();
|
this.$('.synced_at').hide();
|
||||||
this.$('.sync_failed').show();
|
this.$('.sync_failed').show();
|
||||||
this.enable();
|
this.enable();
|
||||||
},
|
},
|
||||||
sync: function() {
|
async sync() {
|
||||||
this.$('.sync_failed').hide();
|
this.$('.sync_failed').hide();
|
||||||
if (textsecure.storage.user.getDeviceId() != '1') {
|
if (window.initialData.isPrimary) {
|
||||||
this.disable();
|
|
||||||
var syncRequest = window.getSyncRequest();
|
|
||||||
syncRequest.addEventListener('success', this.onsuccess.bind(this));
|
|
||||||
syncRequest.addEventListener('timeout', this.ontimeout.bind(this));
|
|
||||||
} else {
|
|
||||||
console.log('Tried to sync from device 1');
|
console.log('Tried to sync from device 1');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.disable();
|
||||||
|
try {
|
||||||
|
await window.makeSyncRequest();
|
||||||
|
this.onsuccess();
|
||||||
|
} catch (error) {
|
||||||
|
this.ontimeout();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render_attributes: function() {
|
render_attributes() {
|
||||||
var attrs = {
|
const attrs = {
|
||||||
sync: i18n('sync'),
|
sync: i18n('sync'),
|
||||||
syncNow: i18n('syncNow'),
|
syncNow: i18n('syncNow'),
|
||||||
syncExplanation: i18n('syncExplanation'),
|
syncExplanation: i18n('syncExplanation'),
|
||||||
syncFailed: i18n('syncFailed'),
|
syncFailed: i18n('syncFailed'),
|
||||||
};
|
};
|
||||||
var date = storage.get('synced_at');
|
let date = this.lastSyncTime;
|
||||||
if (date) {
|
if (date) {
|
||||||
date = new Date(date);
|
date = new Date(date);
|
||||||
attrs.lastSynced = i18n('lastSynced');
|
attrs.lastSynced = i18n('lastSynced');
|
||||||
|
|
268
main.js
268
main.js
|
@ -349,20 +349,6 @@ function createWindow() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showDebugLog() {
|
|
||||||
if (mainWindow) {
|
|
||||||
mainWindow.webContents.send('debug-log');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showSettings() {
|
|
||||||
if (!mainWindow) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mainWindow.webContents.send('show-settings');
|
|
||||||
}
|
|
||||||
|
|
||||||
function openReleaseNotes() {
|
function openReleaseNotes() {
|
||||||
shell.openExternal(
|
shell.openExternal(
|
||||||
`https://github.com/signalapp/Signal-Desktop/releases/tag/v${app.getVersion()}`
|
`https://github.com/signalapp/Signal-Desktop/releases/tag/v${app.getVersion()}`
|
||||||
|
@ -441,6 +427,146 @@ function showAbout() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let settingsWindow;
|
||||||
|
function showSettingsWindow() {
|
||||||
|
if (settingsWindow) {
|
||||||
|
settingsWindow.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!mainWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = mainWindow.getSize();
|
||||||
|
const options = {
|
||||||
|
width: Math.min(500, size[0]),
|
||||||
|
height: Math.max(size[1] - 100, MIN_HEIGHT),
|
||||||
|
resizable: false,
|
||||||
|
title: locale.messages.signalDesktopPreferences.message,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
show: false,
|
||||||
|
modal: true,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
nodeIntegrationInWorker: false,
|
||||||
|
preload: path.join(__dirname, 'settings_preload.js'),
|
||||||
|
// sandbox: true,
|
||||||
|
nativeWindowOpen: true,
|
||||||
|
},
|
||||||
|
parent: mainWindow,
|
||||||
|
};
|
||||||
|
|
||||||
|
settingsWindow = new BrowserWindow(options);
|
||||||
|
|
||||||
|
captureClicks(settingsWindow);
|
||||||
|
|
||||||
|
settingsWindow.loadURL(prepareURL([__dirname, 'settings.html']));
|
||||||
|
|
||||||
|
settingsWindow.on('closed', () => {
|
||||||
|
removeDarkOverlay();
|
||||||
|
settingsWindow = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
settingsWindow.once('ready-to-show', () => {
|
||||||
|
addDarkOverlay();
|
||||||
|
settingsWindow.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let debugLogWindow;
|
||||||
|
function showDebugLogWindow() {
|
||||||
|
if (debugLogWindow) {
|
||||||
|
debugLogWindow.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = mainWindow.getSize();
|
||||||
|
const options = {
|
||||||
|
width: Math.max(size[0] - 100, MIN_WIDTH),
|
||||||
|
height: Math.max(size[1] - 100, MIN_HEIGHT),
|
||||||
|
resizable: false,
|
||||||
|
title: locale.messages.signalDesktopPreferences.message,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
show: false,
|
||||||
|
modal: true,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
nodeIntegrationInWorker: false,
|
||||||
|
preload: path.join(__dirname, 'debug_log_preload.js'),
|
||||||
|
// sandbox: true,
|
||||||
|
nativeWindowOpen: true,
|
||||||
|
},
|
||||||
|
parent: mainWindow,
|
||||||
|
};
|
||||||
|
|
||||||
|
debugLogWindow = new BrowserWindow(options);
|
||||||
|
|
||||||
|
captureClicks(debugLogWindow);
|
||||||
|
|
||||||
|
debugLogWindow.loadURL(prepareURL([__dirname, 'debug_log.html']));
|
||||||
|
|
||||||
|
debugLogWindow.on('closed', () => {
|
||||||
|
removeDarkOverlay();
|
||||||
|
debugLogWindow = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
debugLogWindow.once('ready-to-show', () => {
|
||||||
|
addDarkOverlay();
|
||||||
|
debugLogWindow.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let permissionsPopupWindow;
|
||||||
|
function showPermissionsPopupWindow() {
|
||||||
|
if (permissionsPopupWindow) {
|
||||||
|
permissionsPopupWindow.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!mainWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = mainWindow.getSize();
|
||||||
|
const options = {
|
||||||
|
width: Math.min(400, size[0]),
|
||||||
|
height: Math.min(150, size[1]),
|
||||||
|
resizable: false,
|
||||||
|
title: locale.messages.signalDesktopPreferences.message,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
show: false,
|
||||||
|
modal: true,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
nodeIntegrationInWorker: false,
|
||||||
|
preload: path.join(__dirname, 'permissions_popup_preload.js'),
|
||||||
|
// sandbox: true,
|
||||||
|
nativeWindowOpen: true,
|
||||||
|
},
|
||||||
|
parent: mainWindow,
|
||||||
|
};
|
||||||
|
|
||||||
|
permissionsPopupWindow = new BrowserWindow(options);
|
||||||
|
|
||||||
|
captureClicks(permissionsPopupWindow);
|
||||||
|
|
||||||
|
permissionsPopupWindow.loadURL(
|
||||||
|
prepareURL([__dirname, 'permissions_popup.html'])
|
||||||
|
);
|
||||||
|
|
||||||
|
permissionsPopupWindow.on('closed', () => {
|
||||||
|
removeDarkOverlay();
|
||||||
|
permissionsPopupWindow = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
permissionsPopupWindow.once('ready-to-show', () => {
|
||||||
|
addDarkOverlay();
|
||||||
|
permissionsPopupWindow.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// This method will be called when Electron has finished
|
// This method will be called when Electron has finished
|
||||||
// initialization and is ready to create browser windows.
|
// initialization and is ready to create browser windows.
|
||||||
// Some APIs can only be used after this event occurs.
|
// Some APIs can only be used after this event occurs.
|
||||||
|
@ -462,7 +588,7 @@ app.on('ready', async () => {
|
||||||
protocol: electronProtocol,
|
protocol: electronProtocol,
|
||||||
});
|
});
|
||||||
|
|
||||||
installPermissionsHandler({ session });
|
installPermissionsHandler({ session, userConfig });
|
||||||
|
|
||||||
// NOTE: Temporarily allow `then` until we convert the entire file to `async` / `await`:
|
// NOTE: Temporarily allow `then` until we convert the entire file to `async` / `await`:
|
||||||
/* eslint-disable more/no-then */
|
/* eslint-disable more/no-then */
|
||||||
|
@ -508,9 +634,10 @@ function setupMenu(options) {
|
||||||
const { platform } = process;
|
const { platform } = process;
|
||||||
const menuOptions = Object.assign({}, options, {
|
const menuOptions = Object.assign({}, options, {
|
||||||
development,
|
development,
|
||||||
showDebugLog,
|
showDebugLog: showDebugLogWindow,
|
||||||
showWindow,
|
showWindow,
|
||||||
showAbout,
|
showAbout,
|
||||||
|
showSettings: showSettingsWindow,
|
||||||
openReleaseNotes,
|
openReleaseNotes,
|
||||||
openNewBugForm,
|
openNewBugForm,
|
||||||
openSupportPage,
|
openSupportPage,
|
||||||
|
@ -519,7 +646,6 @@ function setupMenu(options) {
|
||||||
setupWithImport,
|
setupWithImport,
|
||||||
setupAsNewDevice,
|
setupAsNewDevice,
|
||||||
setupAsStandalone,
|
setupAsStandalone,
|
||||||
showSettings,
|
|
||||||
});
|
});
|
||||||
const template = createTemplate(menuOptions, locale.messages);
|
const template = createTemplate(menuOptions, locale.messages);
|
||||||
const menu = Menu.buildFromTemplate(template);
|
const menu = Menu.buildFromTemplate(template);
|
||||||
|
@ -591,6 +717,14 @@ ipc.on('draw-attention', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipc.on('set-media-permissions', (event, enabled) => {
|
||||||
|
userConfig.set('mediaPermissions', enabled);
|
||||||
|
});
|
||||||
|
ipc.on('get-media-permissions', event => {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
event.returnValue = userConfig.get('mediaPermissions') || false;
|
||||||
|
});
|
||||||
|
|
||||||
ipc.on('restart', () => {
|
ipc.on('restart', () => {
|
||||||
app.relaunch();
|
app.relaunch();
|
||||||
app.quit();
|
app.quit();
|
||||||
|
@ -619,3 +753,103 @@ ipc.on('update-tray-icon', (event, unreadCount) => {
|
||||||
tray.updateIcon(unreadCount);
|
tray.updateIcon(unreadCount);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Debug Log-related IPC calls
|
||||||
|
|
||||||
|
ipc.on('show-debug-log', showDebugLogWindow);
|
||||||
|
ipc.on('close-debug-log', () => {
|
||||||
|
if (debugLogWindow) {
|
||||||
|
debugLogWindow.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Permissions Popup-related IPC calls
|
||||||
|
|
||||||
|
ipc.on('show-permissions-popup', showPermissionsPopupWindow);
|
||||||
|
ipc.on('close-permissions-popup', () => {
|
||||||
|
if (permissionsPopupWindow) {
|
||||||
|
permissionsPopupWindow.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Settings-related IPC calls
|
||||||
|
|
||||||
|
function addDarkOverlay() {
|
||||||
|
if (mainWindow && mainWindow.webContents) {
|
||||||
|
mainWindow.webContents.send('add-dark-overlay');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function removeDarkOverlay() {
|
||||||
|
if (mainWindow && mainWindow.webContents) {
|
||||||
|
mainWindow.webContents.send('remove-dark-overlay');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipc.on('show-settings', showSettingsWindow);
|
||||||
|
ipc.on('close-settings', () => {
|
||||||
|
if (settingsWindow) {
|
||||||
|
settingsWindow.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
installSettingsGetter('device-name');
|
||||||
|
|
||||||
|
installSettingsGetter('theme-setting');
|
||||||
|
installSettingsSetter('theme-setting');
|
||||||
|
installSettingsGetter('hide-menu-bar');
|
||||||
|
installSettingsSetter('hide-menu-bar');
|
||||||
|
|
||||||
|
installSettingsGetter('notification-setting');
|
||||||
|
installSettingsSetter('notification-setting');
|
||||||
|
installSettingsGetter('audio-notification');
|
||||||
|
installSettingsSetter('audio-notification');
|
||||||
|
|
||||||
|
// This one is different because its single source of truth is userConfig, not IndexedDB
|
||||||
|
ipc.on('get-media-permissions', event => {
|
||||||
|
event.sender.send(
|
||||||
|
'get-success-media-permissions',
|
||||||
|
null,
|
||||||
|
userConfig.get('mediaPermissions') || false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
ipc.on('set-media-permissions', (event, value) => {
|
||||||
|
userConfig.set('mediaPermissions', value);
|
||||||
|
|
||||||
|
// We reinstall permissions handler to ensure that a revoked permission takes effect
|
||||||
|
installPermissionsHandler({ session, userConfig });
|
||||||
|
|
||||||
|
event.sender.send('set-success-media-permissions', null);
|
||||||
|
});
|
||||||
|
|
||||||
|
installSettingsGetter('is-primary');
|
||||||
|
installSettingsGetter('sync-request');
|
||||||
|
installSettingsGetter('sync-time');
|
||||||
|
installSettingsSetter('sync-time');
|
||||||
|
|
||||||
|
ipc.on('delete-all-data', () => {
|
||||||
|
if (mainWindow && mainWindow.webContents) {
|
||||||
|
mainWindow.webContents.send('delete-all-data');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function installSettingsGetter(name) {
|
||||||
|
ipc.on(`get-${name}`, event => {
|
||||||
|
if (mainWindow && mainWindow.webContents) {
|
||||||
|
ipc.once(`get-success-${name}`, (_event, error, value) =>
|
||||||
|
event.sender.send(`get-success-${name}`, error, value)
|
||||||
|
);
|
||||||
|
mainWindow.webContents.send(`get-${name}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function installSettingsSetter(name) {
|
||||||
|
ipc.on(`set-${name}`, (event, value) => {
|
||||||
|
if (mainWindow && mainWindow.webContents) {
|
||||||
|
ipc.once(`set-success-${name}`, (_event, error) =>
|
||||||
|
event.sender.send(`set-success-${name}`, error)
|
||||||
|
);
|
||||||
|
mainWindow.webContents.send(`set-${name}`, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -214,6 +214,9 @@
|
||||||
"config/local-${env.SIGNAL_ENV}.json",
|
"config/local-${env.SIGNAL_ENV}.json",
|
||||||
"background.html",
|
"background.html",
|
||||||
"about.html",
|
"about.html",
|
||||||
|
"settings.html",
|
||||||
|
"permissions_popup.html",
|
||||||
|
"debug_log.html",
|
||||||
"_locales/**",
|
"_locales/**",
|
||||||
"protos/*",
|
"protos/*",
|
||||||
"js/**",
|
"js/**",
|
||||||
|
@ -225,6 +228,9 @@
|
||||||
"app/*",
|
"app/*",
|
||||||
"preload.js",
|
"preload.js",
|
||||||
"about_preload.js",
|
"about_preload.js",
|
||||||
|
"settings_preload.js",
|
||||||
|
"permissions_preload.js",
|
||||||
|
"debug_log_preload.js",
|
||||||
"main.js",
|
"main.js",
|
||||||
"images/**",
|
"images/**",
|
||||||
"fonts/*",
|
"fonts/*",
|
||||||
|
|
38
permissions_popup.html
Normal file
38
permissions_popup.html
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'none';
|
||||||
|
child-src 'self';
|
||||||
|
connect-src 'self' https: wss:;
|
||||||
|
font-src 'self';
|
||||||
|
form-action 'self';
|
||||||
|
frame-src 'none';
|
||||||
|
img-src 'self' blob: data:;
|
||||||
|
media-src 'self' blob:;
|
||||||
|
object-src 'none';
|
||||||
|
script-src 'self';
|
||||||
|
style-src 'self' 'unsafe-inline';"
|
||||||
|
>
|
||||||
|
<link href="stylesheets/manifest.css" rel="stylesheet" type="text/css" />
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class='permissions-popup'>
|
||||||
|
</body>
|
||||||
|
<script type='text/x-tmpl-mustache' id='confirmation-dialog'>
|
||||||
|
<div class="content">
|
||||||
|
<div class='message'>{{ message }}</div>
|
||||||
|
<div class='buttons'>
|
||||||
|
{{ #showCancel }}
|
||||||
|
<button class='cancel' tabindex='2'>{{ cancel }}</button>
|
||||||
|
{{ /showCancel }}
|
||||||
|
<button class='ok' tabindex='1'>{{ ok }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
</script>
|
||||||
|
<script type='text/javascript' src='js/components.js'></script>
|
||||||
|
<script type='text/javascript' src='js/views/whisper_view.js'></script>
|
||||||
|
<script type='text/javascript' src='js/views/confirmation_dialog_view.js'></script>
|
||||||
|
<script type='text/javascript' src='js/permissions_popup_start.js'></script>
|
||||||
|
</html>
|
45
permissions_popup_preload.js
Normal file
45
permissions_popup_preload.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
const { ipcRenderer } = require('electron');
|
||||||
|
const url = require('url');
|
||||||
|
const i18n = require('./js/modules/i18n');
|
||||||
|
|
||||||
|
const config = url.parse(window.location.toString(), true).query;
|
||||||
|
const { locale } = config;
|
||||||
|
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||||
|
|
||||||
|
window.i18n = i18n.setup(locale, localeMessages);
|
||||||
|
|
||||||
|
require('./js/logging');
|
||||||
|
|
||||||
|
window.closePermissionsPopup = () =>
|
||||||
|
ipcRenderer.send('close-permissions-popup');
|
||||||
|
|
||||||
|
window.getMediaPermissions = makeGetter('media-permissions');
|
||||||
|
window.setMediaPermissions = makeSetter('media-permissions');
|
||||||
|
|
||||||
|
function makeGetter(name) {
|
||||||
|
return () =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
ipcRenderer.once(`get-success-${name}`, (event, error, value) => {
|
||||||
|
if (error) {
|
||||||
|
return reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve(value);
|
||||||
|
});
|
||||||
|
ipcRenderer.send(`get-${name}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSetter(name) {
|
||||||
|
return value =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
ipcRenderer.once(`set-success-${name}`, (event, error) => {
|
||||||
|
if (error) {
|
||||||
|
return reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve();
|
||||||
|
});
|
||||||
|
ipcRenderer.send(`set-${name}`, value);
|
||||||
|
});
|
||||||
|
}
|
87
preload.js
87
preload.js
|
@ -61,6 +61,10 @@ window.restart = () => {
|
||||||
ipc.send('restart');
|
ipc.send('restart');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.setMediaPermissions = enabled =>
|
||||||
|
ipc.send('set-media-permissions', enabled);
|
||||||
|
window.getMediaPermissions = () => ipc.sendSync('get-media-permissions');
|
||||||
|
|
||||||
window.closeAbout = () => ipc.send('close-about');
|
window.closeAbout = () => ipc.send('close-about');
|
||||||
|
|
||||||
window.updateTrayIcon = unreadCount =>
|
window.updateTrayIcon = unreadCount =>
|
||||||
|
@ -82,12 +86,89 @@ ipc.on('set-up-as-standalone', () => {
|
||||||
Whisper.events.trigger('setupAsStandalone');
|
Whisper.events.trigger('setupAsStandalone');
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc.on('show-settings', () => {
|
// Settings-related events
|
||||||
Whisper.events.trigger('showSettings');
|
|
||||||
|
window.showSettings = () => ipc.send('show-settings');
|
||||||
|
window.showPermissionsPopup = () => ipc.send('show-permissions-popup');
|
||||||
|
|
||||||
|
ipc.on('add-dark-overlay', () => {
|
||||||
|
const { addDarkOverlay } = window.Events;
|
||||||
|
if (addDarkOverlay) {
|
||||||
|
addDarkOverlay();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ipc.on('remove-dark-overlay', () => {
|
||||||
|
const { removeDarkOverlay } = window.Events;
|
||||||
|
if (removeDarkOverlay) {
|
||||||
|
removeDarkOverlay();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addSetupMenuItems = () => ipc.send('add-setup-menu-items');
|
installGetter('device-name', 'getDeviceName');
|
||||||
|
|
||||||
|
installGetter('theme-setting', 'getThemeSetting');
|
||||||
|
installSetter('theme-setting', 'setThemeSetting');
|
||||||
|
installGetter('hide-menu-bar', 'getHideMenuBar');
|
||||||
|
installSetter('hide-menu-bar', 'setHideMenuBar');
|
||||||
|
|
||||||
|
installGetter('notification-setting', 'getNotificationSetting');
|
||||||
|
installSetter('notification-setting', 'setNotificationSetting');
|
||||||
|
installGetter('audio-notification', 'getAudioNotification');
|
||||||
|
installSetter('audio-notification', 'setAudioNotification');
|
||||||
|
|
||||||
|
window.getMediaPermissions = () =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
ipc.once('get-success-media-permissions', (_event, error, value) => {
|
||||||
|
if (error) {
|
||||||
|
return reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve(value);
|
||||||
|
});
|
||||||
|
ipc.send('get-media-permissions');
|
||||||
|
});
|
||||||
|
|
||||||
|
installGetter('is-primary', 'isPrimary');
|
||||||
|
installGetter('sync-request', 'getSyncRequest');
|
||||||
|
installGetter('sync-time', 'getLastSyncTime');
|
||||||
|
installSetter('sync-time', 'setLastSyncTime');
|
||||||
|
|
||||||
|
ipc.on('delete-all-data', () => {
|
||||||
|
const { deleteAllData } = window.Events;
|
||||||
|
if (deleteAllData) {
|
||||||
|
deleteAllData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function installGetter(name, functionName) {
|
||||||
|
ipc.on(`get-${name}`, async () => {
|
||||||
|
const getFn = window.Events[functionName];
|
||||||
|
if (getFn) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
try {
|
||||||
|
ipc.send(`get-success-${name}`, null, await getFn());
|
||||||
|
} catch (error) {
|
||||||
|
ipc.send(`get-success-${name}`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function installSetter(name, functionName) {
|
||||||
|
ipc.on(`set-${name}`, async (_event, value) => {
|
||||||
|
const setFn = window.Events[functionName];
|
||||||
|
if (setFn) {
|
||||||
|
try {
|
||||||
|
await setFn(value);
|
||||||
|
ipc.send(`set-success-${name}`);
|
||||||
|
} catch (error) {
|
||||||
|
ipc.send(`set-success-${name}`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addSetupMenuItems = () => ipc.send('add-setup-menu-items');
|
||||||
window.removeSetupMenuItems = () => ipc.send('remove-setup-menu-items');
|
window.removeSetupMenuItems = () => ipc.send('remove-setup-menu-items');
|
||||||
|
|
||||||
// We pull these dependencies in now, from here, because they have Node.js dependencies
|
// We pull these dependencies in now, from here, because they have Node.js dependencies
|
||||||
|
|
116
settings.html
Normal file
116
settings.html
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'none';
|
||||||
|
child-src 'self';
|
||||||
|
connect-src 'self' https: wss:;
|
||||||
|
font-src 'self';
|
||||||
|
form-action 'self';
|
||||||
|
frame-src 'none';
|
||||||
|
img-src 'self' blob: data:;
|
||||||
|
media-src 'self' blob:;
|
||||||
|
object-src 'none';
|
||||||
|
script-src 'self';
|
||||||
|
style-src 'self' 'unsafe-inline';"
|
||||||
|
>
|
||||||
|
<link href="stylesheets/manifest.css" rel="stylesheet" type="text/css" />
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
<script type='text/x-tmpl-mustache' id='syncSettings'>
|
||||||
|
<hr>
|
||||||
|
<h3>{{ sync }}</h3>
|
||||||
|
<div>
|
||||||
|
<button class='grey sync'>{{ syncNow }}</button>
|
||||||
|
<p>
|
||||||
|
{{ syncExplanation }}
|
||||||
|
<div class='synced_at'>
|
||||||
|
{{ lastSynced }} {{ syncDate }} {{ syncTime }}
|
||||||
|
</div>
|
||||||
|
<div class='sync_failed'>{{ syncFailed }}</div>
|
||||||
|
<div class='clearfix'></div>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
<script type='text/x-tmpl-mustache' id='settings'>
|
||||||
|
<div class='content'>
|
||||||
|
<a class='x close' alt='close settings' href='#'></a>
|
||||||
|
<h2>{{ settings }}</h2>
|
||||||
|
<div class='device-name-settings'>
|
||||||
|
<b>{{ deviceNameLabel }}:</b> {{ deviceName }}
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class='theme-settings'>
|
||||||
|
<h3>{{ theme }}</h3>
|
||||||
|
<div>
|
||||||
|
<input type='radio' name='theme' id='theme-setting-android' value='android'>
|
||||||
|
<label for='theme-setting-android'>Android</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type='radio' name='theme' id='theme-setting-android-dark' value='android-dark'>
|
||||||
|
<label for='theme-setting-android-dark'>{{ themeAndroidDark }}</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type='radio' name='theme' id='theme-setting-ios' value='ios'/>
|
||||||
|
<label for='theme-setting-ios'>iOS</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class='menu-bar-setting'>
|
||||||
|
<input type='checkbox' name='hide-menu-bar' id='hide-menu-bar'/>
|
||||||
|
<label for='hide-menu-bar'>{{ hideMenuBar }}</label>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class='notification-settings'>
|
||||||
|
<h3>{{ notifications }}</h3>
|
||||||
|
<p>{{ notificationSettingsDialog }}</p>
|
||||||
|
<div>
|
||||||
|
<input type='radio' name='notifications' id='notification-setting-message' value='message'>
|
||||||
|
<label for='notification-setting-message'>{{ nameAndMessage }} </label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type='radio' name='notifications' id='notification-setting-name' value='name'/>
|
||||||
|
<label for='notification-setting-name'>{{ nameOnly }} </label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type='radio' name='notifications' id='notification-setting-count' value='count'/>
|
||||||
|
<label for='notification-setting-count'>{{ noNameOrMessage }} </label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type='radio' name='notifications' id='notification-setting-off' value='off'/>
|
||||||
|
<label for='notification-setting-off'>{{ disableNotifications }} </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
{{ #isAudioNotificationSupported }}
|
||||||
|
<div class='audio-notification-setting'>
|
||||||
|
<input type='checkbox' name='audio-notification' id='audio-notification'/>
|
||||||
|
<label for='audio-notification'>{{ audioNotificationDescription }}</label>
|
||||||
|
</div>
|
||||||
|
{{ /isAudioNotificationSupported }}
|
||||||
|
<hr>
|
||||||
|
<div class='permissions-setting'>
|
||||||
|
<h3>{{ permissions }}</h3>
|
||||||
|
<div class='media-permissions'>
|
||||||
|
<input type='checkbox' name='media-permissions' />
|
||||||
|
<label for='media-permissions'>{{ mediaPermissionsDescription }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='sync-setting'></div>
|
||||||
|
<hr>
|
||||||
|
<div class='clear-data-settings'>
|
||||||
|
<h3>{{ clearDataHeader }}</h3>
|
||||||
|
<div>
|
||||||
|
<button class='grey destructive clear-data'>{{ clearDataButton }}</button>
|
||||||
|
<p>{{ clearDataExplanation }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
<script type='text/javascript' src='js/components.js'></script>
|
||||||
|
<script type='text/javascript' src='js/views/whisper_view.js'></script>
|
||||||
|
<script type='text/javascript' src='js/views/settings_view.js'></script>
|
||||||
|
<script type='text/javascript' src='js/settings_start.js'></script>
|
||||||
|
</html>
|
76
settings_preload.js
Normal file
76
settings_preload.js
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
const { ipcRenderer } = require('electron');
|
||||||
|
const url = require('url');
|
||||||
|
const i18n = require('./js/modules/i18n');
|
||||||
|
|
||||||
|
const config = url.parse(window.location.toString(), true).query;
|
||||||
|
const { locale } = config;
|
||||||
|
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||||
|
|
||||||
|
window.i18n = i18n.setup(locale, localeMessages);
|
||||||
|
|
||||||
|
require('./js/logging');
|
||||||
|
|
||||||
|
// So far we're only using this for Signal.Types
|
||||||
|
const Signal = require('./js/modules/signal');
|
||||||
|
|
||||||
|
window.Signal = Signal.setup({
|
||||||
|
Attachments: null,
|
||||||
|
userDataPath: null,
|
||||||
|
getRegionCode: () => null,
|
||||||
|
});
|
||||||
|
|
||||||
|
window.getEnvironment = () => config.environment;
|
||||||
|
window.getVersion = () => config.version;
|
||||||
|
window.getAppInstance = () => config.appInstance;
|
||||||
|
|
||||||
|
window.closeSettings = () => ipcRenderer.send('close-settings');
|
||||||
|
|
||||||
|
window.getDeviceName = makeGetter('device-name');
|
||||||
|
|
||||||
|
window.getThemeSetting = makeGetter('theme-setting');
|
||||||
|
window.setThemeSetting = makeSetter('theme-setting');
|
||||||
|
window.getHideMenuBar = makeGetter('hide-menu-bar');
|
||||||
|
window.setHideMenuBar = makeSetter('hide-menu-bar');
|
||||||
|
|
||||||
|
window.getNotificationSetting = makeGetter('notification-setting');
|
||||||
|
window.setNotificationSetting = makeSetter('notification-setting');
|
||||||
|
window.getAudioNotification = makeGetter('audio-notification');
|
||||||
|
window.setAudioNotification = makeSetter('audio-notification');
|
||||||
|
|
||||||
|
window.getMediaPermissions = makeGetter('media-permissions');
|
||||||
|
window.setMediaPermissions = makeSetter('media-permissions');
|
||||||
|
|
||||||
|
window.isPrimary = makeGetter('is-primary');
|
||||||
|
window.makeSyncRequest = makeGetter('sync-request');
|
||||||
|
window.getLastSyncTime = makeGetter('sync-time');
|
||||||
|
window.setLastSyncTime = makeSetter('sync-time');
|
||||||
|
|
||||||
|
window.deleteAllData = () => ipcRenderer.send('delete-all-data');
|
||||||
|
|
||||||
|
function makeGetter(name) {
|
||||||
|
return () =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
ipcRenderer.once(`get-success-${name}`, (event, error, value) => {
|
||||||
|
if (error) {
|
||||||
|
return reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve(value);
|
||||||
|
});
|
||||||
|
ipcRenderer.send(`get-${name}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSetter(name) {
|
||||||
|
return value =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
ipcRenderer.once(`set-success-${name}`, (event, error) => {
|
||||||
|
if (error) {
|
||||||
|
return reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve();
|
||||||
|
});
|
||||||
|
ipcRenderer.send(`set-${name}`, value);
|
||||||
|
});
|
||||||
|
}
|
|
@ -1320,6 +1320,24 @@ span.status {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.permissions-popup,
|
||||||
|
.debug-log-window {
|
||||||
|
.modal {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmation-dialog .content {
|
||||||
|
box-shadow: 0px 0px 0px 0px;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.advisory .icon {
|
.advisory .icon {
|
||||||
height: 1.25em;
|
height: 1.25em;
|
||||||
width: 1.25em;
|
width: 1.25em;
|
||||||
|
|
|
@ -16,6 +16,17 @@ body {
|
||||||
color: $grey_d;
|
color: $grey_d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dark-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: black;
|
||||||
|
opacity: 0.25;
|
||||||
|
z-index: 200;
|
||||||
|
}
|
||||||
|
|
||||||
.clearfix:before,
|
.clearfix:before,
|
||||||
.clearfix:after {
|
.clearfix:after {
|
||||||
display: table;
|
display: table;
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
.settings {
|
.settings {
|
||||||
&.modal {
|
&.modal {
|
||||||
padding: 50px;
|
padding: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
margin: 0 auto;
|
margin: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 500px;
|
max-width: 450px;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: 0px 0px 0px 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hr {
|
hr {
|
||||||
|
@ -32,6 +38,10 @@
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.restart-needed {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.clear-data-settings {
|
.clear-data-settings {
|
||||||
button {
|
button {
|
||||||
float: right;
|
float: right;
|
||||||
|
|
|
@ -618,11 +618,11 @@
|
||||||
<script type='text/javascript' src='../js/views/network_status_view.js'></script>
|
<script type='text/javascript' src='../js/views/network_status_view.js'></script>
|
||||||
<script type='text/javascript' src='../js/views/confirmation_dialog_view.js' data-cover></script>
|
<script type='text/javascript' src='../js/views/confirmation_dialog_view.js' data-cover></script>
|
||||||
<script type='text/javascript' src='../js/views/identicon_svg_view.js' data-cover></script>
|
<script type='text/javascript' src='../js/views/identicon_svg_view.js' data-cover></script>
|
||||||
<script type='text/javascript' src='../js/views/settings_view.js' data-cover></script>
|
|
||||||
<script type='text/javascript' src='../js/views/last_seen_indicator_view.js' data-cover></script>
|
<script type='text/javascript' src='../js/views/last_seen_indicator_view.js' data-cover></script>
|
||||||
<script type='text/javascript' src='../js/views/scroll_down_button_view.js' data-cover></script>
|
<script type='text/javascript' src='../js/views/scroll_down_button_view.js' data-cover></script>
|
||||||
<script type='text/javascript' src='../js/views/banner_view.js' data-cover></script>
|
<script type='text/javascript' src='../js/views/banner_view.js' data-cover></script>
|
||||||
<script type="text/javascript" src='../js/views/identity_key_send_error_view.js' data-cover></script>
|
<script type="text/javascript" src='../js/views/identity_key_send_error_view.js' data-cover></script>
|
||||||
|
<script type='text/javascript' src='../js/views/clear_data_view.js'></script>
|
||||||
|
|
||||||
<script type="text/javascript" src="views/whisper_view_test.js"></script>
|
<script type="text/javascript" src="views/whisper_view_test.js"></script>
|
||||||
<script type="text/javascript" src="views/group_update_view_test.js"></script>
|
<script type="text/javascript" src="views/group_update_view_test.js"></script>
|
||||||
|
@ -636,7 +636,6 @@
|
||||||
<script type="text/javascript" src="views/last_seen_indicator_view_test.js"></script>
|
<script type="text/javascript" src="views/last_seen_indicator_view_test.js"></script>
|
||||||
<script type='text/javascript' src='views/scroll_down_button_view_test.js'></script>
|
<script type='text/javascript' src='views/scroll_down_button_view_test.js'></script>
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript" src="models/conversations_test.js"></script>
|
<script type="text/javascript" src="models/conversations_test.js"></script>
|
||||||
<script type="text/javascript" src="models/messages_test.js"></script>
|
<script type="text/javascript" src="models/messages_test.js"></script>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue