Merge branch 'clearnet' of https://github.com/loki-project/loki-messenger into patchopensnapps

pull/988/head
Ryan Tharp 5 years ago
commit e4d762a358

@ -1958,6 +1958,12 @@
"relink": {
"message": "Relink"
},
"autoUpdateSettingTitle": {
"message": "Auto Update"
},
"autoUpdateSettingDescription": {
"message": "Automatically check for updates on launch"
},
"autoUpdateNewVersionTitle": {
"message": "Session update available"
},

@ -0,0 +1,15 @@
export interface BaseConfig {
set(keyPath: string, value: any): void;
get(keyPath: string): any | undefined;
remove(): void;
}
interface Options {
allowMalformedOnStartup: boolean;
}
export function start(
name: string,
targetPath: string,
options: Options
): BaseConfig;

@ -0,0 +1,3 @@
import { BaseConfig } from './base_config';
type UserConfig = BaseConfig;

@ -1946,6 +1946,10 @@
}) {
return async event => {
const { data, confirm } = event;
if (!data) {
window.log.warn('Invalid data passed to createMessageHandler.', event);
return confirm();
}
const messageDescriptor = getMessageDescriptor(data);
@ -1968,36 +1972,38 @@
messageDescriptor.id
);
let message;
if (
messageDescriptor.type === 'group' &&
descriptorId.match(/^publicChat:/)
) {
const { source } = data;
// Note: This only works currently because we have a 1 device limit
// When we change that, the check below needs to change too
const ourNumber = textsecure.storage.user.getNumber();
const primaryDevice = window.storage.get('primaryDevicePubKey');
const { source } = data;
if (source && (source === ourNumber || source === primaryDevice)) {
const isOurDevice =
source && (source === ourNumber || source === primaryDevice);
const isPublicChatMessage =
messageDescriptor.type === 'group' &&
descriptorId.match(/^publicChat:/);
if (isPublicChatMessage && isOurDevice) {
// Public chat messages from ourselves should be outgoing
message = await createSentMessage(data);
}
} else {
message = await createMessage(data);
}
const isDuplicate = await isMessageDuplicate(message);
if (isDuplicate) {
// RSS expects duplciates, so squelch log
if (!descriptorId.match(/^rss:/)) {
window.log.warn('Received duplicate message', message.idForLogging());
}
return event.confirm();
return confirm();
}
await ConversationController.getOrCreateAndWait(
messageDescriptor.id,
messageDescriptor.type
);
return message.handleDataMessage(data.message, event.confirm, {
return message.handleDataMessage(data.message, confirm, {
initialLoadComplete,
});
};

@ -330,9 +330,13 @@
resetMessageSelection() {
this.selectedMessages.clear();
this.messageCollection.forEach(m => {
// on change for ALL messages without real changes is a really costly operation
// -> cause refresh of the whole conversation view even if not a single message was selected
if (m.selected) {
// eslint-disable-next-line no-param-reassign
m.selected = false;
m.trigger('change');
}
});
this.trigger('message-selection-changed');

@ -103,8 +103,11 @@ class LokiFileServerInstance {
if (!Array.isArray(authorisations)) {
return;
}
const validAuthorisations = authorisations.filter(
a => a && typeof auth === 'object'
);
await Promise.all(
authorisations.map(async auth => {
validAuthorisations.map(async auth => {
// only skip, if in secondary search mode
if (isRequest && auth.secondaryDevicePubKey !== user.username) {
// this is not the authorization we're looking for

@ -147,7 +147,9 @@
({ authorisations } = primaryDeviceMapping);
}
}
return authorisations || [];
// filter out any invalid authorisations
return authorisations.filter(a => a && typeof a === 'object') || [];
}
// if the device is a secondary device,
@ -168,6 +170,10 @@
}
async function savePairingAuthorisation(authorisation) {
if (!authorisation) {
return;
}
// Ensure that we have a conversation for all the devices
const conversation = await ConversationController.getOrCreateAndWait(
authorisation.secondaryDevicePubKey,

@ -427,7 +427,7 @@ async function readyForUpdates() {
// Second, start checking for app updates
try {
await updater.start(getMainWindow, locale.messages, logger);
await updater.start(getMainWindow, userConfig, locale.messages, logger);
} catch (error) {
const log = logger || console;
log.error(
@ -1090,6 +1090,24 @@ ipc.on('set-media-permissions', (event, value) => {
}
});
// Loki - Auto updating
ipc.on('get-auto-update-setting', event => {
const configValue = userConfig.get('autoUpdate');
// eslint-disable-next-line no-param-reassign
event.returnValue = typeof configValue !== 'boolean' ? true : configValue;
});
ipc.on('set-auto-update-setting', (event, enabled) => {
userConfig.set('autoUpdate', !!enabled);
if (enabled) {
readyForUpdates();
} else {
updater.stop();
isReadyForUpdates = false;
}
});
function getDataFromMainWindow(name, callback) {
ipc.once(`get-success-${name}`, (_event, error, value) =>
callback(error, value)

@ -211,8 +211,11 @@ window.getSettingValue = (settingID, comparisonValue = null) => {
// Eg. window.getSettingValue('theme', 'light')
// returns 'false' when the value is 'dark'.
// We need to get specific settings from the main process
if (settingID === 'media-permissions') {
return window.getMediaPermissions();
} else if (settingID === 'auto-update') {
return window.getAutoUpdateEnabled();
}
const settingVal = window.storage.get(settingID);
@ -220,6 +223,12 @@ window.getSettingValue = (settingID, comparisonValue = null) => {
};
window.setSettingValue = (settingID, value) => {
// For auto updating we need to pass the value to the main process
if (settingID === 'auto-update') {
window.setAutoUpdateEnabled(value);
return;
}
window.storage.put(settingID, value);
if (settingID === 'zoom-factor-setting') {
@ -231,6 +240,11 @@ window.setSettingValue = (settingID, value) => {
window.getMessageTTL = () => window.storage.get('message-ttl', 24);
window.getMediaPermissions = () => ipc.sendSync('get-media-permissions');
// Auto update setting
window.getAutoUpdateEnabled = () => ipc.sendSync('get-auto-update-setting');
window.setAutoUpdateEnabled = value =>
ipc.send('set-auto-update-setting', !!value);
ipc.on('get-ready-for-shutdown', async () => {
const { shutdown } = window.Events || {};
if (!shutdown) {

@ -1457,6 +1457,7 @@ label {
resize: none;
overflow: hidden;
user-select: all;
overflow-y: auto;
}
input {

@ -213,7 +213,7 @@ export class Message extends React.PureComponent<Props, State> {
}
public renderMetadataBadges() {
const { direction, isPublic, senderIsModerator } = this.props;
const { direction, isPublic, senderIsModerator, id } = this.props;
const badges = [isPublic && 'Public', senderIsModerator && 'Mod'];
@ -224,7 +224,7 @@ export class Message extends React.PureComponent<Props, State> {
}
return (
<>
<div key={`${id}-${badgeText}`}>
<span className="module-message__metadata__badge--separator">
&nbsp;&nbsp;
</span>
@ -239,7 +239,7 @@ export class Message extends React.PureComponent<Props, State> {
>
{badgeText}
</span>
</>
</div>
);
})
.filter(i => !!i);
@ -1064,9 +1064,13 @@ export class Message extends React.PureComponent<Props, State> {
// This id is what connects our triple-dot click with our associated pop-up menu.
// It needs to be unique.
const triggerId = String(id || `${authorPhoneNumber}-${timestamp}`);
const rightClickTriggerId = `${authorPhoneNumber}-ctx-${timestamp}`;
// The Date.now() is a workaround to be sure a single triggerID with this id exists
const triggerId = id
? String(`${id}-${Date.now()}`)
: String(`${authorPhoneNumber}-${timestamp}`);
const rightClickTriggerId = id
? String(`${id}-ctx-${Date.now()}`)
: String(`${authorPhoneNumber}-ctx-${timestamp}`);
if (expired) {
return null;
}

@ -1,5 +1,6 @@
import React from 'react';
import { SessionIconButton, SessionIconSize, SessionIconType } from './icon';
import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu';
interface Props {
searchString: string;
@ -16,8 +17,11 @@ export class SessionSearchInput extends React.Component<Props> {
public render() {
const { searchString } = this.props;
const triggerId = 'session-search-input-context';
return (
<>
<ContextMenuTrigger id={triggerId}>
<div className="session-search-input">
<SessionIconButton
iconSize={SessionIconSize.Medium}
@ -30,6 +34,29 @@ export class SessionSearchInput extends React.Component<Props> {
placeholder={this.props.placeholder}
/>
</div>
</ContextMenuTrigger>
<ContextMenu id={triggerId}>
<MenuItem onClick={() => document.execCommand('undo')}>
{window.i18n('editMenuUndo')}
</MenuItem>
<MenuItem onClick={() => document.execCommand('redo')}>
{window.i18n('editMenuRedo')}
</MenuItem>
<hr />
<MenuItem onClick={() => document.execCommand('cut')}>
{window.i18n('editMenuCut')}
</MenuItem>
<MenuItem onClick={() => document.execCommand('copy')}>
{window.i18n('editMenuCopy')}
</MenuItem>
<MenuItem onClick={() => document.execCommand('paste')}>
{window.i18n('editMenuPaste')}
</MenuItem>
<MenuItem onClick={() => document.execCommand('selectAll')}>
{window.i18n('editMenuSelectAll')}
</MenuItem>
</ContextMenu>
</>
);
}

@ -496,6 +496,19 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
content: {},
confirmationDialogParams: undefined,
},
{
id: 'auto-update',
title: window.i18n('autoUpdateSettingTitle'),
description: window.i18n('autoUpdateSettingDescription'),
hidden: false,
type: SessionSettingType.Toggle,
category: SessionSettingCategory.Privacy,
setFn: undefined,
comparisonValue: undefined,
onClick: undefined,
content: {},
confirmationDialogParams: undefined,
},
{
id: 'set-password',
title: window.i18n('setAccountPasswordTitle'),

@ -1,12 +1,15 @@
import { get as getFromConfig } from 'config';
import { BrowserWindow } from 'electron';
import { start as startUpdater } from './updater';
import { start as startUpdater, stop as stopUpdater } from './updater';
import { LoggerType, MessagesType } from './common';
import { UserConfig } from '../../app/user_config';
let initialized = false;
let config: UserConfig;
export async function start(
getMainWindow: () => BrowserWindow,
userConfig: UserConfig,
messages?: MessagesType,
logger?: LoggerType
) {
@ -14,6 +17,7 @@ export async function start(
throw new Error('updater/start: Updates have already been initialized!');
}
initialized = true;
config = userConfig;
if (!messages) {
throw new Error('updater/start: Must provide messages!');
@ -40,8 +44,18 @@ export async function start(
await startUpdater(getMainWindow, messages, logger);
}
export function stop() {
if (initialized) {
stopUpdater();
initialized = false;
}
}
function autoUpdateDisabled() {
return (
process.mas || !getFromConfig('updatesEnabled') // From Electron: Mac App Store build
process.mas || // From Electron: Mac App Store build
!getFromConfig('updatesEnabled') || // Hard coded config
// tslint:disable-next-line: no-backbone-get-set-outside-model
!config.get('autoUpdate') // User setting
);
}

@ -3,6 +3,7 @@ import * as fs from 'fs-extra';
import { autoUpdater, UpdateInfo } from 'electron-updater';
import { app, BrowserWindow } from 'electron';
import { markShouldQuit } from '../../app/window_state';
import {
getPrintableError,
LoggerType,
@ -15,6 +16,8 @@ import { gt as isVersionGreaterThan, parse as parseVersion } from 'semver';
let isUpdating = false;
let downloadIgnored = false;
let interval: NodeJS.Timeout | undefined;
let stopped = false;
const SECOND = 1000;
const MINUTE = SECOND * 60;
@ -25,28 +28,43 @@ export async function start(
messages: MessagesType,
logger: LoggerType
) {
if (interval) {
logger.info('auto-update: Already running');
return;
}
logger.info('auto-update: starting checks...');
autoUpdater.logger = logger;
autoUpdater.autoDownload = false;
setInterval(async () => {
interval = setInterval(async () => {
try {
await checkForUpdates(getMainWindow, messages, logger);
} catch (error) {
logger.error('auto-update: error:', getPrintableError(error));
}
}, INTERVAL);
stopped = false;
await checkForUpdates(getMainWindow, messages, logger);
}
export function stop() {
if (interval) {
clearInterval(interval);
interval = undefined;
stopped = true;
}
}
async function checkForUpdates(
getMainWindow: () => BrowserWindow,
messages: MessagesType,
logger: LoggerType
) {
if (isUpdating || downloadIgnored) {
if (stopped || isUpdating || downloadIgnored) {
return;
}

Loading…
Cancel
Save