Implement suggested changes

- (rebase to current master)
 - put compareAndRepeat in debug.js
 - create a new DebugPage for tests instead of AboutPage; only visible in debug mode
 - finalize PR state (remove functionsOLD left for comparisons)
This commit is contained in:
John Gibbon 2020-12-04 21:21:53 +01:00
parent a2118edc3b
commit 214e6d335b
7 changed files with 180 additions and 664 deletions

View file

@ -73,6 +73,7 @@ DISTFILES += qml/harbour-fernschreiber.qml \
qml/pages/ChatPage.qml \ qml/pages/ChatPage.qml \
qml/pages/ChatSelectionPage.qml \ qml/pages/ChatSelectionPage.qml \
qml/pages/CoverPage.qml \ qml/pages/CoverPage.qml \
qml/pages/DebugPage.qml \
qml/pages/InitializationPage.qml \ qml/pages/InitializationPage.qml \
qml/pages/NewChatPage.qml \ qml/pages/NewChatPage.qml \
qml/pages/OverviewPage.qml \ qml/pages/OverviewPage.qml \

View file

@ -50,3 +50,81 @@ Fernschreiber.DebugLog.enabledChanged.connect(function() {
} }
}); });
/**
* @function compareAndRepeat
* This function compares results of two functions for multiple sets of arguments and then repeats those calls to compare them with profiling.
* Testing showed that results are slightly skewed in favor of the secondaryProfileFunction, which (together with external factors)
* is almost compensated by running the calls often.
*
* @param {string} title - used for Debug output
* @param {function} profileFunction - function to compare
* @param {function} [secondaryProfileFunction] - secondary function to compare. Use falsy value to skip comparison.
* @param {Array.<Array.<*>>} [functionArgumentsArray = []] - argument sets to run both functions with
* @param {number} [testCount = 10000] - times to run each function in addition to result comparison
* @param {Object} [testContext = null] - If the functions use `this` or otherwise depend on a Closure/Execution context, it can be set here. If they, for example, use properties of a QML `Item`, you can use that `Item` as `testContext`.
*
* @example
* // to compare the results of Debug.log("first", "second", "third")
* // and Debug.log("some value")
* // with the same calls of console.log and run 100 times each:
* Debug.compareAndRepeat("Debuglog",
* Debug.log, console.log,
* [["first", "second", "third"], ["some value"]],
* 100
* );
*
*/
function compareAndRepeat(title, profileFunction, secondaryProfileFunction, functionArgumentsArray, testCount, testContext) {
if(!enabled) {
log("Debugging disabled, compareAndRepeat ran uselessly:", title);
return;
}
var numberOfTests = testCount || 10000,
context = testContext || null,
args = functionArgumentsArray || [],
argumentIterator = args.length || 1,
results,
resultsComparison,
functionIterator = numberOfTests;
if(secondaryProfileFunction) {
while(argumentIterator--) {
results = JSON.stringify(profileFunction.apply(context, args[argumentIterator] || null));
resultsComparison = JSON.stringify(secondaryProfileFunction.apply(context, args[argumentIterator] || null));
if(resultsComparison !== results) {
warn("\x1b[1m✘ Different return values!\x1b[0m", title)
warn("profileFunction result:", results);
warn("secondaryProfileFunction result:", resultsComparison);
return;
}
}
log("\x1b[1m✔ Comparison of", title, "return values successful!\x1b[0m")
}
log("Running", title, "with", args.length, "different argument sets", numberOfTests, "times each")
if(secondaryProfileFunction) {
// "secondaryProfileFunction"
functionIterator = numberOfTests;
while(functionIterator--) {
argumentIterator = args.length || 1;
while(argumentIterator--) {
secondaryProfileFunction.apply(context, args[argumentIterator] || null);
}
}
}
// "profileFunction"
functionIterator = numberOfTests;
while(functionIterator--) {
argumentIterator = args.length || 1;
while(argumentIterator--) {
profileFunction.apply(context, args[argumentIterator] || null);
}
}
log("Ran", title, args.length * numberOfTests, "times", secondaryProfileFunction ? "and compared results successfully" : "");
}

View file

@ -213,7 +213,6 @@ function enhanceHtmlEntities(simpleText) {
return simpleText.replace(ampRegExp, "&amp;").replace(ltRegExp, "&lt;").replace(gtRegExp, "&gt;");//.replace(rawNewLineRegExp, "<br>"); return simpleText.replace(ampRegExp, "&amp;").replace(ltRegExp, "&lt;").replace(gtRegExp, "&gt;");//.replace(rawNewLineRegExp, "<br>");
} }
function messageInsertionSorter(a, b) { return (b.offset+b.removeLength) - (a.offset+a.removeLength) } function messageInsertionSorter(a, b) { return (b.offset+b.removeLength) - (a.offset+a.removeLength) }
function enhanceMessageText(formattedText, ignoreEntities) { function enhanceMessageText(formattedText, ignoreEntities) {
@ -381,6 +380,7 @@ function getVideoHeight(videoWidth, videoData) {
return 1; return 1;
} }
} }
function replaceUrlsWithLinks(string) { function replaceUrlsWithLinks(string) {
return string.replace(/((\w+):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g, "<a href=\"$1\">$1</a>"); return string.replace(/((\w+):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g, "<a href=\"$1\">$1</a>");
} }
@ -448,81 +448,3 @@ function getMessagesNeededForwardPermissions(messages) {
} }
return neededPermissions return neededPermissions
} }
/**
* @function compareAndRepeat
* This function compares results of two functions for multiple sets of arguments and then repeats those calls to compare them with profiling.
* Testing showed that results are slightly skewed in favor of the secondaryProfileFunction, which (together with external factors)
* is almost compensated by running the calls often.
*
* @param {string} title - used for Debug output
* @param {function} profileFunction - function to compare
* @param {function} [secondaryProfileFunction] - secondary function to compare. Use falsy value to skip comparison.
* @param {Array.<Array.<*>>} [functionArgumentsArray = []] - argument sets to run both functions with
* @param {number} [testCount = 10000] - times to run each function in addition to result comparison
* @param {Object} [testContext = null] - If the functions use `this` or otherwise depend on a Closure/Execution context, it can be set here. If they, for example, use properties of a QML `Item`, you can use that `Item` as `testContext`.
*
* @example
* // to compare the results of Debug.log("first", "second", "third")
* // and Debug.log("some value")
* // with the same calls of console.log and run 100 times each:
* Functions.compareBenchmark("Debuglog",
* Debug.log, console.log,
* [["first", "second", "third"], ["some value"]],
* 100
* );
*
*/
function compareAndRepeat(title, profileFunction, secondaryProfileFunction, functionArgumentsArray, testCount, testContext) {
if(!Debug.enabled) {
Debug.log("Debugging disabled, compareAndRepeat ran uselessly:", title);
return;
}
var numberOfTests = testCount || 10000,
context = testContext || null,
args = functionArgumentsArray || [],
argumentIterator = args.length || 1,
results,
resultsComparison,
functionIterator = numberOfTests;
if(secondaryProfileFunction) {
while(argumentIterator--) {
results = JSON.stringify(profileFunction.apply(context, args[argumentIterator] || null));
resultsComparison = JSON.stringify(secondaryProfileFunction.apply(context, args[argumentIterator] || null));
if(resultsComparison !== results) {
Debug.warn("\x1b[1m✘ Different return values!\x1b[0m", title)
Debug.warn("profileFunction result:", results);
Debug.warn("secondaryProfileFunction result:", resultsComparison);
return;
}
}
Debug.log("\x1b[1m✔ Comparison of", title, "return values successful!\x1b[0m")
}
Debug.log("Running", title, "with", args.length, "different argument sets", numberOfTests, "times each")
if(secondaryProfileFunction) {
// "secondaryProfileFunction"
functionIterator = numberOfTests;
while(functionIterator--) {
argumentIterator = args.length || 1;
while(argumentIterator--) {
secondaryProfileFunction.apply(context, args[argumentIterator] || null);
}
}
}
// "profileFunction"
functionIterator = numberOfTests;
while(functionIterator--) {
argumentIterator = args.length || 1;
while(argumentIterator--) {
profileFunction.apply(context, args[argumentIterator] || null);
}
}
Debug.log("Ran", title, args.length * numberOfTests, "times", secondaryProfileFunction ? "and compared results successfully" : "");
}

View file

@ -1,463 +0,0 @@
/*
Copyright (C) 2020 Sebastian J. Wolf and other contributors
This file is part of Fernschreiber.
Fernschreiber is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Fernschreiber 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
.pragma library
.import "debug.js" as Debug
.import Sailfish.Silica 1.0 as Silica
var tdLibWrapper;
var appNotification;
function setGlobals(globals) {
tdLibWrapper = globals.tdLibWrapper;
appNotification = globals.appNotification;
}
function getUserName(userInformation) {
var firstName = typeof userInformation.first_name !== "undefined" ? userInformation.first_name : "";
var lastName = typeof userInformation.last_name !== "undefined" ? userInformation.last_name : "";
return (firstName + " " + lastName).trim();
}
function getMessageText(message, simple, myself) {
if (message.content['@type'] === 'messageText') {
if (simple) {
return message.content.text.text;
} else {
return enhanceMessageText(message.content.text);
}
}
if (message.content['@type'] === 'messageSticker') {
return simple ? qsTr("Sticker: %1").arg(message.content.sticker.emoji) : "";
}
if (message.content['@type'] === 'messagePhoto') {
if (message.content.caption.text !== "") {
return simple ? qsTr("Picture: %1").arg(message.content.caption.text) : enhanceMessageText(message.content.caption)
} else {
return simple ? (myself ? qsTr("sent a picture", "myself") : qsTr("sent a picture")) : "";
}
}
if (message.content['@type'] === 'messageVideo') {
if (message.content.caption.text !== "") {
return simple ? qsTr("Video: %1").arg(message.content.caption.text) : enhanceMessageText(message.content.caption)
} else {
return simple ? (myself ? qsTr("sent a video", "myself") : qsTr("sent a video")) : "";
}
}
if (message.content['@type'] === 'messageVideoNote') {
return simple ? (myself ? qsTr("sent a video note", "myself") : qsTr("sent a video note")) : "";
}
if (message.content['@type'] === 'messageAnimation') {
if (message.content.caption.text !== "") {
return simple ? qsTr("Animation: %1").arg(message.content.caption.text) : enhanceMessageText(message.content.caption)
} else {
return simple ? (myself ? qsTr("sent an animation", "myself") : qsTr("sent an animation")) : "";
}
}
if (message.content['@type'] === 'messageAudio') {
if (message.content.caption.text !== "") {
return simple ? qsTr("Audio: %1").arg(message.content.caption.text) : enhanceMessageText(message.content.caption)
} else {
return simple ? (myself ? qsTr("sent an audio", "myself") : qsTr("sent an audio")) : "";
}
}
if (message.content['@type'] === 'messageVoiceNote') {
if (message.content.caption.text !== "") {
return simple ? qsTr("Voice Note: %1").arg(message.content.caption.text) : enhanceMessageText(message.content.caption)
} else {
return simple ? (myself ? qsTr("sent a voice note", "myself") : qsTr("sent a voice note")) : "";
}
}
if (message.content['@type'] === 'messageDocument') {
if (message.content.document.file_name !== "") {
return simple ? qsTr("Document: %1").arg(message.content.document.file_name) : (message.content.document.file_name + ( message.content.caption.text !== "" ? ("<br />" + enhanceMessageText(message.content.caption) ) : "")).trim();
} else {
return simple ? (myself ? qsTr("sent a document", "myself") : qsTr("sent a document")) : "";
}
}
if (message.content['@type'] === 'messageLocation') {
return simple ? (myself ? qsTr("sent a location", "myself") : qsTr("sent a location")) : "";
}
if (message.content['@type'] === 'messageVenue') {
return simple ? (myself ? qsTr("sent a venue", "myself") : qsTr("sent a venue")) : ( "<b>" + message.content.venue.title + "</b>, " + message.content.venue.address );
}
if (message.content['@type'] === 'messageContactRegistered') {
return myself ? qsTr("have registered with Telegram") : qsTr("has registered with Telegram");
}
if (message.content['@type'] === 'messageChatJoinByLink') {
return myself ? qsTr("joined this chat", "myself") : qsTr("joined this chat");
}
if (message.content['@type'] === 'messageChatAddMembers') {
return myself ? qsTr("were added to this chat", "myself") : qsTr("was added to this chat");
}
if (message.content['@type'] === 'messageChatDeleteMember') {
return myself ? qsTr("left this chat", "myself") : qsTr("left this chat");
}
if (message.content['@type'] === 'messageChatChangeTitle') {
return myself ? qsTr("changed the chat title to %1", "myself").arg(message.content.title) : qsTr("changed the chat title to %1").arg(message.content.title);
}
if (message.content['@type'] === 'messagePoll') {
if (message.content.poll.type['@type'] === "pollTypeQuiz") {
if (message.content.poll.is_anonymous) {
return simple ? (myself ? qsTr("sent an anonymous quiz", "myself") : qsTr("sent an anonymous quiz")) : ("<b>" + qsTr("Anonymous Quiz") + "</b>");
}
return simple ? (myself ? qsTr("sent a quiz", "myself") : qsTr("sent a quiz")) : ("<b>" + qsTr("Quiz") + "</b>");
}
if (message.content.poll.is_anonymous) {
return simple ? (myself ? qsTr("sent an anonymous poll", "myself") : qsTr("sent an anonymous poll")) : ("<b>" + qsTr("Anonymous Poll") + "</b>");
}
return simple ? (myself ? qsTr("sent a poll", "myself") : qsTr("sent a poll")) : ("<b>" + qsTr("Poll") + "</b>");
}
if (message.content['@type'] === 'messageBasicGroupChatCreate' || message.content['@type'] === 'messageSupergroupChatCreate') {
return myself ? qsTr("created this group", "myself") : qsTr("created this group");
}
if (message.content['@type'] === 'messageChatChangePhoto') {
return myself ? qsTr("changed the chat photo", "myself") : qsTr("changed the chat photo");
}
if (message.content['@type'] === 'messageChatDeletePhoto') {
return myself ? qsTr("deleted the chat photo", "myself") : qsTr("deleted the chat photo");
}
if (message.content['@type'] === 'messageChatSetTtl') {
return myself ? qsTr("changed the secret chat TTL setting", "myself; TTL = Time To Live") : qsTr("changed the secret chat TTL setting", "TTL = Time To Live");
}
if (message.content['@type'] === 'messageChatUpgradeFrom' || message.content['@type'] === 'messageChatUpgradeTo' ) {
return myself ? qsTr("upgraded this group to a supergroup", "myself") : qsTr("upgraded this group to a supergroup");
}
if (message.content['@type'] === 'messageCustomServiceAction') {
return message.content.text;
}
if (message.content['@type'] === 'messagePinMessage') {
return myself ? qsTr("changed the pinned message", "myself") : qsTr("changed the pinned message");
}
if (message.content['@type'] === 'messageExpiredPhoto') {
return myself ? qsTr("sent a self-destructing photo that is expired", "myself") : qsTr("sent a self-destructing photo that is expired");
}
if (message.content['@type'] === 'messageExpiredVideo') {
return myself ? qsTr("sent a self-destructing video that is expired", "myself") : qsTr("sent a self-destructing video that is expired");
}
if (message.content['@type'] === 'messageScreenshotTaken') {
return myself ? qsTr("created a screenshot in this chat", "myself") : qsTr("created a screenshot in this chat");
}
if (message.content['@type'] === 'messageUnsupported') {
return myself ? qsTr("sent an unsupported message", "myself") : qsTr("sent an unsupported message");
}
return myself ? qsTr("sent an unsupported message: %1", "myself; %1 is message type").arg(message.content['@type'].substring(7)) : qsTr("sent an unsupported message: %1", "%1 is message type").arg(message.content['@type'].substring(7));
}
function getChatPartnerStatusText(statusType, was_online) {
switch(statusType) {
case "userStatusEmpty":
return qsTr("was never online");
case "userStatusLastMonth":
return qsTr("offline, last online: last month");
case "userStatusLastWeek":
return qsTr("offline, last online: last week");
case "userStatusOffline":
return qsTr("offline, last online: %1").arg(getDateTimeElapsed(was_online));
case "userStatusOnline":
return qsTr("online");
case "userStatusRecently":
return qsTr("offline, was recently online");
}
}
function getChatMemberStatusText(statusType) {
// chatMemberStatusAdministrator, chatMemberStatusBanned, chatMemberStatusCreator, chatMemberStatusLeft, chatMemberStatusMember, and chatMemberStatusRestricted.
switch(statusType) {
case "chatMemberStatusAdministrator":
return qsTr("Admin", "channel user role");
case "chatMemberStatusBanned":
return qsTr("Banned", "channel user role");
case "chatMemberStatusCreator":
return qsTr("Creator", "channel user role");
case "chatMemberStatusRestricted":
return qsTr("Restricted", "channel user role");
case "chatMemberStatusLeft":
case "chatMemberStatusMember":
return ""
}
return statusType || "";
}
function getShortenedCount(count) {
if (count >= 1000000) {
return qsTr("%1M").arg((count / 1000000).toLocaleString(Qt.locale(), 'f', 0));
} else if (count >= 1000 ) {
return qsTr("%1K").arg((count / 1000).toLocaleString(Qt.locale(), 'f', 0));
} else {
return count;
}
}
function getDateTimeElapsed(timestamp) {
return Silica.Format.formatDate(new Date(timestamp * 1000), Silica.Formatter.DurationElapsed);
}
function getDateTimeTranslated(timestamp) {
return new Date(timestamp * 1000).toLocaleString();
}
function MessageInsertion(offset, insertionString, removeLength) {
this.offset = offset;
this.insertionString = insertionString;
this.removeLength = removeLength;
}
MessageInsertion.prototype.toString = function insertionToString() {
return "Offset: " + this.offset + ", Insertion String: " + this.insertionString + ", Remove Length: " + this.removeLength;
}
function handleHtmlEntity(messageText, messageInsertions, originalString, replacementString) {
var continueSearch = true;
var fromIndex = 0;
var nextIndex = 0;
while (continueSearch) {
nextIndex = messageText.indexOf(originalString, fromIndex);
if (nextIndex < 0) {
continueSearch = false;
} else {
messageInsertions.push(new MessageInsertion(nextIndex, replacementString, originalString.length));
fromIndex = nextIndex + 1;
}
}
}
function enhanceHtmlEntities(simpleText) {
var messageInsertions = [];
var messageText = simpleText;
handleHtmlEntity(messageText, messageInsertions, "&", "&amp;");
handleHtmlEntity(messageText, messageInsertions, "<", "&lt;");
handleHtmlEntity(messageText, messageInsertions, ">", "&gt;");
messageInsertions.sort( function(a, b) { return (b.offset+b.removeLength) - (a.offset+a.removeLength) } );
for (var z = 0; z < messageInsertions.length; z++) {
messageText = messageText.substring(0, messageInsertions[z].offset) + messageInsertions[z].insertionString + messageText.substring(messageInsertions[z].offset + messageInsertions[z].removeLength);
}
return messageText;
}
function enhanceMessageText(formattedText) {
var messageInsertions = [];
var messageText = formattedText.text;
handleHtmlEntity(messageText, messageInsertions, "&", "&amp;");
handleHtmlEntity(messageText, messageInsertions, "<", "&lt;");
handleHtmlEntity(messageText, messageInsertions, ">", "&gt;");
for (var i = 0; i < formattedText.entities.length; i++) {
if (formattedText.entities[i]['@type'] !== "textEntity") {
continue;
}
var entityType = formattedText.entities[i].type['@type'];
if (entityType === "textEntityTypeBold") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<b>", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</b>", 0 ));
}
if (entityType === "textEntityTypeUrl") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<a href=\"" + messageText.substring(formattedText.entities[i].offset, ( formattedText.entities[i].offset + formattedText.entities[i].length )) + "\">", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</a>", 0 ));
}
if (entityType === "textEntityTypeCode") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<pre>", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</pre>", 0 ));
}
if (entityType === "textEntityTypeEmailAddress") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<a href=\"mailto:" + messageText.substring(formattedText.entities[i].offset, ( formattedText.entities[i].offset + formattedText.entities[i].length )) + "\">", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</a>", 0 ));
}
if (entityType === "textEntityTypeItalic") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<i>", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</i>", 0 ));
}
if (entityType === "textEntityTypeMention") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<a href=\"user://" + messageText.substring(formattedText.entities[i].offset, ( formattedText.entities[i].offset + formattedText.entities[i].length )) + "\">", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</a>", 0 ));
}
if (entityType === "textEntityTypeMentionName") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<a href=\"userId://" + formattedText.entities[i].type.user_id + "\">", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</a>", 0 ));
}
if (entityType === "textEntityTypePhoneNumber") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<a href=\"tel:" + messageText.substring(formattedText.entities[i].offset, ( formattedText.entities[i].offset + formattedText.entities[i].length )) + "\">", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</a>", 0 ));
}
if (entityType === "textEntityTypePre") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<pre>", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</pre>", 0 ));
}
if (entityType === "textEntityTypePreCode") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<pre>", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</pre>", 0 ));
}
if (entityType === "textEntityTypeTextUrl") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<a href=\"" + formattedText.entities[i].type.url + "\">", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</a>", 0 ));
}
if (entityType === "textEntityTypeUnderline") {
messageInsertions.push(new MessageInsertion(formattedText.entities[i].offset, "<u>", 0 ));
messageInsertions.push(new MessageInsertion((formattedText.entities[i].offset + formattedText.entities[i].length), "</u>", 0 ));
}
}
messageInsertions.sort( function(a, b) { return (b.offset+b.removeLength) - (a.offset+a.removeLength) } );
for (var z = 0; z < messageInsertions.length; z++) {
messageText = messageText.substring(0, messageInsertions[z].offset) + messageInsertions[z].insertionString + messageText.substring(messageInsertions[z].offset + messageInsertions[z].removeLength);
}
messageText = messageText.replace(new RegExp("\r?\n", "g"), "<br>");
// var spaceRegex = /\s{2,}/g;
// function spaceReplacer(match, p1, offset, string) {
// var replaceString = "";
// for (var i = 0; i < match.length; i++) {
// replaceString += "&nbsp;";
// }
// return replaceString;
// }
// messageText = messageText.replace(spaceRegex, spaceReplacer);
return messageText;
}
function handleLink(link) {
var tMePrefix = tdLibWrapper.getOptionString("t_me_url");
if (link.indexOf("user://") === 0) {
var userName = link.substring(8);
var userInformation = tdLibWrapper.getUserInformationByName(userName);
if (typeof userInformation.id === "undefined") {
appNotification.show(qsTr("Unable to find user %1").arg(userName));
} else {
tdLibWrapper.createPrivateChat(userInformation.id);
}
} else if (link.indexOf("userId://") === 0) {
tdLibWrapper.createPrivateChat(link.substring(9));
} else if (link.indexOf("tg://") === 0) {
Debug.log("Special TG link: ", link);
if (link.indexOf("tg://join?invite=") === 0) {
tdLibWrapper.joinChatByInviteLink(tMePrefix + "joinchat/" + link.substring(17));
} else if (link.indexOf("tg://resolve?domain=") === 0) {
tdLibWrapper.searchPublicChat(link.substring(20));
}
} else {
if (link.indexOf(tMePrefix) === 0) {
if (link.indexOf("joinchat") !== -1) {
Debug.log("Joining Chat: ", link);
tdLibWrapper.joinChatByInviteLink(link);
// Do the necessary stuff to open the chat if successful
// Fail with nice error message if it doesn't work
} else {
Debug.log("Search public chat: ", link.substring(tMePrefix.length));
tdLibWrapper.searchPublicChat(link.substring(tMePrefix.length));
// Check responses for updateBasicGroup or updateSupergroup
// Fire createBasicGroupChat or createSupergroupChat
// Do the necessary stuff to open the chat
// Fail with nice error message if chat can't be found
}
} else {
Qt.openUrlExternally(link);
}
}
}
function getVideoHeight(videoWidth, videoData) {
if (typeof videoData !== "undefined") {
if (videoData.height === 0) {
return videoWidth;
} else {
var aspectRatio = videoData.height / videoData.width;
return Math.round(videoWidth * aspectRatio);
}
} else {
return 1;
}
}
function replaceUrlsWithLinks(string) {
return string.replace(/((\w+):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g, "<a href=\"$1\">$1</a>");
}
function sortMessagesArrayByDate(messages) {
messages.sort(function(a, b) {
return a.date - b.date;
});
}
function getMessagesArrayIds(messages) {
sortMessagesArrayByDate(messages);
return messages.map(function(message){return message.id.toString()});
}
function getMessagesArrayText(messages) {
sortMessagesArrayByDate(messages);
var lastSenderName = "";
var lines = [];
for(var i = 0; i < messages.length; i += 1) {
var senderName = getUserName(tdLibWrapper.getUserInformation(messages[i].sender_user_id));
if(senderName !== lastSenderName) {
lines.push(senderName);
}
lastSenderName = senderName;
lines.push(getMessageText(messages[i], true, false));
lines.push("");
}
return lines.join("\n");
}
function handleErrorMessage(code, message) {
if (code === 404) {
// Silently ignore 404 Not Found messages (occur sometimes, without clear context...)
return;
}
if (message === "USER_ALREADY_PARTICIPANT") {
appNotification.show(qsTr("You are already a member of this chat."));
} else {
appNotification.show(message);
}
}
function getMessagesNeededForwardPermissions(messages) {
var neededPermissions = ["can_send_messages"]
var mediaMessageTypes = ["messageAudio", "messageDocument", "messagePhoto", "messageVideo", "messageVideoNote", "messageVoiceNote"]
var otherMessageTypes = ["messageAnimation", "messageGame", "messageSticker"]
for(var i = 0; i < messages.length && neededPermissions.length < 3; i += 1) {
var type = messages[i]["content"]["@type"]
var permission = ""
if(type === "messageText") {
continue
} else if(type === "messagePoll") {
permission = "can_send_polls"
} else if(mediaMessageTypes.indexOf(type) > -1) {
permission = "can_send_media_messages"
} else if(otherMessageTypes.indexOf(type) > -1) {
permission = "can_send_other_messages"
}
if(permission !== "" && neededPermissions.indexOf(permission) === -1) {
neededPermissions.push(permission)
}
}
return neededPermissions
}

View file

@ -21,7 +21,6 @@ import Sailfish.Silica 1.0
import "../components" import "../components"
import "../js/twemoji.js" as Emoji import "../js/twemoji.js" as Emoji
import "../js/functions.js" as Functions import "../js/functions.js" as Functions
import "../js/functionsOld.js" as FunctionsOld
import "../js/debug.js" as Debug import "../js/debug.js" as Debug
Page { Page {
@ -310,125 +309,4 @@ Page {
} }
} }
Timer {
id: profileTimer
interval: 1000
property bool hasRun
property var cases: []
onTriggered: {
if(cases.length === 0) {
return;
}
if(!hasRun) {
hasRun = true;
Debug.profile();
}
cases.pop()();
if(cases.length > 0) {
restart();
} else {
Debug.profileEnd();
}
}
}
Component.onCompleted: {
profileTimer.cases.push(function(){
Functions.compareAndRepeat(
"getUserName",
Functions.getUserName,
FunctionsOld.getUserName,
[
[{first_name: "Test", last_name: "User"}],
[{first_name: "Test", last_name: ""}],
[{first_name: "Test"}],
[{first_name: "", last_name: "User"}],
[{last_name: "User"}]
],
800
)
});
var testMessages = [
{
"content": {
"@type": "messageText",
"text": {"@type":"formattedText","entities":[{"@type":"textEntity","length":15,"offset":0,"type":{"@type":"textEntityTypeBold"}},{"@type":"textEntity","length":14,"offset":194,"type":{"@type":"textEntityTypeUrl"}},{"@type":"textEntity","length":31,"offset":303,"type":{"@type":"textEntityTypeBold"}},{"@type":"textEntity","length":46,"offset":341,"type":{"@type":"textEntityTypeBold"}},{"@type":"textEntity","length":23,"offset":460,"type":{"@type":"textEntityTypeBold"}}],"text":"Neue Anmeldung. Liebe(r) xxxxx, wir haben eine Anmeldung von einem neuen Gerät bei deinem Konto am 99/99/9999 um 99:99:99 UTC festgestellt.\n\nGerät: Unknown device\nStandort: xxxxxxxxxxxxxx (IP = xxx.xxx.xx.xxx)\n\nWenn du das nicht selbst gewesen bist, so kannst du die die entsprechende Sitzung abmelden: Telegram Einstellungen > Geräte (oder Privatsphäre und Sicherheit > Aktive Sitzungen.\n\nHat sich jemand ohne dein Einverständnis angemeldet, so kannst du die zweistufige Bestätigung in den Einstellungen unter Privatsphäre und Sicherheit aktivieren."}
}
},
{
"content": {
"@type": "messageText",
"text": {"@type":"formattedText","entities":[{"@type":"textEntity","length":12,"offset":0,"type":{"@type":"textEntityTypeBold"}},{"@type":"textEntity","length":15,"offset":20,"type":{"@type":"textEntityTypeBold"}}],"text":"Anmeldecode: 12345. Auf keinen Fall diesen Code anderen geben, selbst wenn sie behaupten zum Telegram-Team zu gehören!\n\nDieser Code kann dazu benutzt werden, um sich mit deinem Konto zu verbinden. Wir fragen den Code niemals für einen anderen Zweck ab.\n\nWenn du den Code nicht durch die Anmeldung eines anderen Gerätes angefordert hast, so kannst du diese Nachricht einfach ignorieren."}
}
},
{
"content": {
"@type": "messageText",
"text": {"@type":"formattedText","entities":[{"@type":"textEntity","length":10,"offset":0,"type":{"@type":"textEntityTypeBold"}},{"@type":"textEntity","length":14,"offset":169,"type":{"@type":"textEntityTypeUrl"}},{"@type":"textEntity","length":18,"offset":240,"type":{"@type":"textEntityTypeBold"}},{"@type":"textEntity","length":36,"offset":263,"type":{"@type":"textEntityTypeBold"}},{"@type":"textEntity","length":21,"offset":390,"type":{"@type":"textEntityTypeBold"}}],"text":"New login. Dear xxxxx, we detected a login into your account from a new device on 99/99/9999 at 99:99:99 UTC.\n\nDevice: Unknown device\nLocation: xxxxxxxxxxxxxxxxxx (IP = xxx.xxx.xx.xxx)\n\nIf this wasn't you, you can terminate that session in Settings > Devices (or Privacy & Security > Active Sessions).\n\nIf you think that somebody logged in to your account against your will, you can enable Two-Step Verification in Privacy and Security settings."}
}
},
{"@type":"message","content":{"@type":"messageText","text":{"@type":"formattedText","entities":[],"text":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam faucibus erat libero, vitae dictum purus tristique vitae. Praesent lacinia eu augue vitae convallis. Praesent leo & ante, lacinia et efficitur ac, <volutpat in purus. <3 Curabitur nec diam odio. Curabitur interdum mollis libero, nec tincidunt leo vestibulum sit amet. In convallis nisl nibh, a venenatis mi efficitur eu. Integer posuere turpis est, eget congue sapien elementum eget. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi et eleifend."}},"forward_info":{"@type":"messageForwardInfo","date":12323545345,"from_chat_id":0,"from_message_id":0,"origin":{"@type":"messageForwardOriginHiddenUser","sender_name":"USER"}}},
{"@type":"message","content":{"@type":"messageSticker","sticker":{"@type":"sticker","emoji":"👍","height":512,"is_animated":true,"is_mask":false,"set_id":"12323545345","sticker":{"@type":"file"},"thumbnail":{},"width":512}}},
{"@type":"message","content":{"@type":"messagePoll","poll":{"@type":"poll","id":"12323545345","is_anonymous":false,"is_closed":false,"options":[{"@type":"pollOption","is_being_chosen":false,"is_chosen":true,"text":"Auch ","vote_percentage":100,"voter_count":1},{"@type":"pollOption","is_being_chosen":false,"is_chosen":false,"text":"Januar ","vote_percentage":0,"voter_count":0}],"question":"New","recent_voter_user_ids":[12323545345],"total_voter_count":1,"type":{"@type":"pollTypeRegular","allow_multiple_answers":false}}}},
{"@type":"message","content":{"@type":"messageText","text":{"@type":"formattedText","entities":[],"text":"This is not a message, this is just a tribute"}},"date":12323545345,"edit_date":0,"forward_info":{"@type":"messageForwardInfo","date":12323545345,"from_chat_id":0,"from_message_id":0,"origin":{"@type":"messageForwardOriginUser","sender_user_id":12323545345}}}
];
profileTimer.cases.push(function(){
Functions.compareAndRepeat(
"getMessageText",
Functions.getMessageText,
FunctionsOld.getMessageText,
[
[testMessages[0], true, true],
[testMessages[1], true, true],
[testMessages[2], true, true],
[testMessages[3], true, true],
[testMessages[4], true, true],
[testMessages[5], true, true],
[testMessages[0], true, false],
[testMessages[1], true, false],
[testMessages[2], true, false],
[testMessages[3], true, false],
[testMessages[4], true, false],
[testMessages[5], true, false],
[testMessages[0], false, false],
[testMessages[1], false, false],
[testMessages[2], false, false],
[testMessages[3], false, false],
[testMessages[4], false, false],
[testMessages[5], false, false],
],
100
);
});
var messageContentPayload = testMessages.filter(function(message) {return message.content["@type"] === "messageText"}).map(function(message) {return [message.content.text]});
profileTimer.cases.push(function(){
Functions.compareAndRepeat(
"enhanceMessageText",
Functions.enhanceMessageText,
FunctionsOld.enhanceMessageText,
messageContentPayload,
500
)
});
var enhanceHtmlEntitiesPayload = testMessages.filter(function(message) {return message.content["@type"] === "messageText"}).map(function(message) {return [message.content.text.text]});
profileTimer.cases.push(function(){
Functions.compareAndRepeat(
"enhanceHtmlEntities",
Functions.enhanceHtmlEntities,
FunctionsOld.enhanceHtmlEntities,
enhanceHtmlEntitiesPayload,
500
)
});
profileTimer.start();
}
} }

95
qml/pages/DebugPage.qml Normal file
View file

@ -0,0 +1,95 @@
/*
Copyright (C) 2020 Sebastian J. Wolf and other contributors
This file is part of Fernschreiber.
Fernschreiber is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Fernschreiber 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.6
import Sailfish.Silica 1.0
import "../components"
import "../js/twemoji.js" as Emoji
import "../js/functions.js" as Functions
import "../js/debug.js" as Debug
Page {
id: debugPage
allowedOrientations: Orientation.All
SilicaFlickable {
id: aboutContainer
contentHeight: column.height
anchors.fill: parent
Column {
id: column
width: parent.width
spacing: Theme.paddingLarge
PageHeader {
title: "Debug Page"
description: "here be dragons"
}
}
VerticalScrollDecorator {}
}
Timer {
id: profileTimer
interval: 1000
property bool hasRun
property var cases: []
onTriggered: {
if(cases.length === 0) {
return;
}
if(!hasRun) {
hasRun = true;
Debug.profile();
}
cases.pop()();
if(cases.length > 0) {
restart();
} else {
Debug.profileEnd();
}
}
}
onStatusChanged: {
if (status === PageStatus.Active) {
// example runner for comparing function calls
// profileTimer.cases.push(function(){
// Debug.compareAndRepeat(
// "getUserName",
// Functions.getUserName,
// Functions.getUserName,
// [
// [{first_name: "Test", last_name: "User"}],
// [{first_name: "Test", last_name: ""}],
// [{first_name: "Test"}],
// [{first_name: "", last_name: "User"}],
// [{last_name: "User"}]
// ],
// 800
// )
// });
// profileTimer.start();
}
}
}

View file

@ -190,6 +190,11 @@ Page {
visible: !overviewPage.loading visible: !overviewPage.loading
PullDownMenu { PullDownMenu {
MenuItem {
text: "Debug"
visible: Debug.enabled
onClicked: pageStack.push(Qt.resolvedUrl("../pages/DebugPage.qml"))
}
MenuItem { MenuItem {
text: qsTr("About Fernschreiber") text: qsTr("About Fernschreiber")
onClicked: pageStack.push(Qt.resolvedUrl("../pages/AboutPage.qml")) onClicked: pageStack.push(Qt.resolvedUrl("../pages/AboutPage.qml"))