/**
* This file is part of Threema Web.
*
* Threema Web is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Threema Web. If not, see .
*/
import {Logger} from 'ts-log';
import {arrayToBuffer, hasFeature, sleep} from '../helpers';
import * as clipboard from '../helpers/clipboard';
import {BrowserService} from '../services/browser';
import {LogService} from '../services/log';
import {WebClientService} from '../services/webclient';
import {DialogController} from './dialog';
export class TroubleshootingController extends DialogController {
public static readonly $inject = [
'$scope', '$mdDialog', '$mdToast', '$translate',
'LogService', 'BrowserService', 'WebClientService',
];
private readonly $scope: ng.IScope;
private readonly $mdToast: ng.material.IToastService;
private readonly $translate: ng.translate.ITranslateService;
private readonly logService: LogService;
private readonly browserService: BrowserService;
private readonly webClientService: WebClientService;
private readonly log: Logger;
public isSending: boolean = false;
public sendingFailed: boolean = false;
public description: string = '';
constructor(
$scope: ng.IScope,
$mdDialog: ng.material.IDialogService,
$mdToast: ng.material.IToastService,
$translate: ng.translate.ITranslateService,
logService: LogService,
browserService: BrowserService,
webClientService: WebClientService,
) {
super($mdDialog);
this.$scope = $scope;
this.$mdToast = $mdToast;
this.$translate = $translate;
this.logService = logService;
this.browserService = browserService;
this.webClientService = webClientService;
this.log = logService.getLogger('Troubleshooting-C');
}
/**
* Return whether the web client is currently connected (or able to
* reconnect on its own).
*/
public get isConnected(): boolean {
return this.webClientService.readyToSubmit;
}
/**
* Return whether the log is ready to be sent.
*
* This requires...
*
* - the web client to be connected (or able to reconnect on its own),
* - a description of the problem to be populated, and
* - sending to be not in progress already.
*/
public get canSend(): boolean {
return this.isConnected && this.description.length > 0 && !this.isSending;
}
/**
* Send the log to *SUPPORT.
*/
public async send(): Promise {
this.isSending = true;
this.sendingFailed = false;
// Serialise the log
const log = new TextEncoder().encode(this.logService.memory.serialize());
// Error handler
const fail = () => {
this.$scope.$apply(() => {
this.isSending = false;
this.sendingFailed = true;
// Show toast
this.$mdToast.show(this.$mdToast.simple()
.textContent(this.$translate.instant('troubleshooting.REPORT_VIA_THREEMA_FAILED'))
.position('bottom center'));
});
};
// Add contact *SUPPORT (if needed)
const support: threema.BaseReceiver = {
id: '*SUPPORT',
type: 'contact',
};
if (!this.webClientService.contacts.has(support.id)) {
try {
await this.webClientService.addContact(support.id);
} catch (error) {
this.log.error('Unable to add contact *SUPPORT:', error);
return fail();
}
}
// Workaround for iOS which does not fetch the feature mask immediately
// TODO: Remove once IOS-809 has been resolved
for (let i = 0; i < 50; ++i) {
const contact = this.webClientService.contacts.get(support.id);
if (hasFeature(contact, threema.ContactReceiverFeature.FILE, this.log)) {
break;
}
await sleep(100);
}
// Send as file to *SUPPORT
const browser = this.browserService.getBrowser();
let browserShortInfo = 'unknown';
if (browser.wasDetermined()) {
browserShortInfo = `${browser.name}-${browser.version}`;
if (browser.mobile) {
browserShortInfo += '-mobile';
}
}
const message: threema.FileMessageData = {
name: `webclient-[[VERSION]]-${browserShortInfo}.log`,
fileType: 'text/plain',
size: log.byteLength,
data: arrayToBuffer(log),
caption: this.description,
sendAsFile: true,
};
try {
await this.webClientService.sendMessage(support, 'file', message, { waitUntilAcknowledged: true });
} catch (error) {
this.log.error('Unable to send log report to *SUPPORT:', error);
return fail();
}
// Done
this.isSending = false;
this.$mdToast.show(this.$mdToast.simple()
.textContent(this.$translate.instant('troubleshooting.REPORT_VIA_THREEMA_SUCCESS'))
.position('bottom center'));
// Hide dialog
this.hide();
}
/**
* Copy the log into the clipboard.
*/
public copyToClipboard(): void {
// Get the log
const log = this.getLog();
// Copy to clipboard
let toastString = 'messenger.COPIED';
try {
clipboard.copyString(log, this.browserService.getBrowser().isSafari());
} catch (error) {
this.log.warn('Could not copy text to clipboard:', error);
toastString = 'messenger.COPY_ERROR';
}
// Show toast
this.$mdToast.show(this.$mdToast.simple()
.textContent(this.$translate.instant(toastString))
.position('bottom center'));
}
/**
* Serialise the memory log.
*/
private getLog(): string {
// TODO: Add metadata
return this.logService.memory.serialize();
}
}