Turn utils.js into a Utils.qml QtObject
When a .js module is used, every single file that imports it creates its own duplicated environment in memory for that module. Instead, use a simple QtObject with all the functions, and declare it in Window.qml so that it is available to all children everywhere in the app.
This commit is contained in:
parent
c16731b239
commit
2cb64c5346
|
@ -58,11 +58,10 @@ def convert_to_qml(theme_content: str) -> str:
|
||||||
lines = [
|
lines = [
|
||||||
"import QtQuick 2.12",
|
"import QtQuick 2.12",
|
||||||
'import "Base"',
|
'import "Base"',
|
||||||
'import "utils.js" as Utils',
|
|
||||||
"QtObject {",
|
"QtObject {",
|
||||||
" function hsluv(h, s, l, a) { return Utils.hsluv(h, s, l, a) }",
|
" function hsluv(h, s, l, a) { return utils.hsluv(h, s, l, a) }",
|
||||||
" function hsl(h, s, l) { return Utils.hsl(h, s, l) }",
|
" function hsl(h, s, l) { return utils.hsl(h, s, l) }",
|
||||||
" function hsla(h, s, l, a) { return Utils.hsla(h, s, l, a) }",
|
" function hsla(h, s, l, a) { return utils.hsla(h, s, l, a) }",
|
||||||
" id: theme",
|
" id: theme",
|
||||||
]
|
]
|
||||||
lines += [f" {line}" for line in _process_lines(theme_content)]
|
lines += [f" {line}" for line in _process_lines(theme_content)]
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import "../Base"
|
import "../Base"
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: avatar
|
id: avatar
|
||||||
implicitWidth: theme.controls.avatar.size
|
implicitWidth: theme.controls.avatar.size
|
||||||
implicitHeight: theme.controls.avatar.size
|
implicitHeight: theme.controls.avatar.size
|
||||||
|
|
||||||
color: avatarImage.visible ? "transparent" : Utils.hsluv(
|
color: avatarImage.visible ? "transparent" : utils.hsluv(
|
||||||
name ? Utils.hueFrom(name) : 0,
|
name ? utils.hueFrom(name) : 0,
|
||||||
name ? theme.controls.avatar.background.saturation : 0,
|
name ? theme.controls.avatar.background.saturation : 0,
|
||||||
theme.controls.avatar.background.lightness,
|
theme.controls.avatar.background.lightness,
|
||||||
theme.controls.avatar.background.opacity
|
theme.controls.avatar.background.opacity
|
||||||
|
@ -34,8 +33,8 @@ Rectangle {
|
||||||
text: name ? name.charAt(0) : "?"
|
text: name ? name.charAt(0) : "?"
|
||||||
font.pixelSize: parent.height / 1.4
|
font.pixelSize: parent.height / 1.4
|
||||||
|
|
||||||
color: Utils.hsluv(
|
color: utils.hsluv(
|
||||||
name ? Utils.hueFrom(name) : 0,
|
name ? utils.hueFrom(name) : 0,
|
||||||
name ? theme.controls.avatar.letter.saturation : 0,
|
name ? theme.controls.avatar.letter.saturation : 0,
|
||||||
theme.controls.avatar.letter.lightness,
|
theme.controls.avatar.letter.lightness,
|
||||||
theme.controls.avatar.letter.opacity
|
theme.controls.avatar.letter.opacity
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: box
|
id: box
|
||||||
|
@ -114,10 +113,10 @@ Rectangle {
|
||||||
property string name: modelData.name
|
property string name: modelData.name
|
||||||
|
|
||||||
property Item next: buttonRepeater.itemAt(
|
property Item next: buttonRepeater.itemAt(
|
||||||
Utils.numberWrapAt(index + 1, buttonRepeater.count),
|
utils.numberWrapAt(index + 1, buttonRepeater.count),
|
||||||
)
|
)
|
||||||
property Item previous: buttonRepeater.itemAt(
|
property Item previous: buttonRepeater.itemAt(
|
||||||
Utils.numberWrapAt(index - 1, buttonRepeater.count),
|
utils.numberWrapAt(index - 1, buttonRepeater.count),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
CheckBox {
|
CheckBox {
|
||||||
id: box
|
id: box
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.13
|
import QtQuick 2.13
|
||||||
import QtQuick.Controls 2.13
|
import QtQuick.Controls 2.13
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
Drawer {
|
Drawer {
|
||||||
id: drawer
|
id: drawer
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: image
|
id: image
|
||||||
|
@ -13,7 +12,7 @@ Image {
|
||||||
|
|
||||||
property bool broken: false
|
property bool broken: false
|
||||||
property bool animate: true
|
property bool animate: true
|
||||||
property bool animated: Utils.urlExtension(image.source) === "gif"
|
property bool animated: utils.urlExtension(image.source) === "gif"
|
||||||
|
|
||||||
property alias showProgressBar: progressBarLoader.active
|
property alias showProgressBar: progressBarLoader.active
|
||||||
property bool inderterminateProgressBar: false
|
property bool inderterminateProgressBar: false
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: label
|
id: label
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
signal deselectAll()
|
signal deselectAll()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
HButton {
|
HButton {
|
||||||
id: tile
|
id: tile
|
||||||
|
|
|
@ -2,7 +2,6 @@ import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import QtAV 1.7
|
import QtAV 1.7
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HColumnLayout {
|
HColumnLayout {
|
||||||
id: osd
|
id: osd
|
||||||
|
@ -118,7 +117,7 @@ HColumnLayout {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.margins: padding / 4
|
anchors.margins: padding / 4
|
||||||
text: Utils.formatDuration(previewToolTip.wantTimestamp)
|
text: utils.formatDuration(previewToolTip.wantTimestamp)
|
||||||
padding: theme.spacing / 2
|
padding: theme.spacing / 2
|
||||||
opacity: previewToolTip.wantTimestamp === -1 ? 0 : 1
|
opacity: previewToolTip.wantTimestamp === -1 ? 0 : 1
|
||||||
|
|
||||||
|
@ -197,7 +196,7 @@ HColumnLayout {
|
||||||
OSDButton {
|
OSDButton {
|
||||||
id: speedButton
|
id: speedButton
|
||||||
icon.name: "player-speed"
|
icon.name: "player-speed"
|
||||||
text: qsTr("%1x").arg(Utils.round(media.playbackRate))
|
text: qsTr("%1x").arg(utils.round(media.playbackRate))
|
||||||
toolTip.text: qsTr("Reset speed")
|
toolTip.text: qsTr("Reset speed")
|
||||||
onClicked: media.playbackRate = 1
|
onClicked: media.playbackRate = 1
|
||||||
}
|
}
|
||||||
|
@ -225,11 +224,11 @@ HColumnLayout {
|
||||||
text: boundPosition && savedDuration ?
|
text: boundPosition && savedDuration ?
|
||||||
|
|
||||||
qsTr("%1 / %2")
|
qsTr("%1 / %2")
|
||||||
.arg(Utils.formatDuration(boundPosition))
|
.arg(utils.formatDuration(boundPosition))
|
||||||
.arg(Utils.formatDuration(savedDuration)) :
|
.arg(utils.formatDuration(savedDuration)) :
|
||||||
|
|
||||||
boundPosition || savedDuration ?
|
boundPosition || savedDuration ?
|
||||||
Utils.formatDuration(boundPosition || savedDuration) :
|
utils.formatDuration(boundPosition || savedDuration) :
|
||||||
|
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
@ -239,7 +238,7 @@ HColumnLayout {
|
||||||
OSDLabel {
|
OSDLabel {
|
||||||
text: boundPosition && savedDuration ?
|
text: boundPosition && savedDuration ?
|
||||||
qsTr("-%1").arg(
|
qsTr("-%1").arg(
|
||||||
Utils.formatDuration(savedDuration - boundPosition)
|
utils.formatDuration(savedDuration - boundPosition)
|
||||||
) : ""
|
) : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
Banner {
|
Banner {
|
||||||
property string inviterId: chat.roomInfo.inviter
|
property string inviterId: chat.roomInfo.inviter
|
||||||
|
@ -14,7 +13,7 @@ Banner {
|
||||||
avatar.mxc: inviterAvatar
|
avatar.mxc: inviterAvatar
|
||||||
|
|
||||||
labelText: qsTr("%1 invited you to this room").arg(
|
labelText: qsTr("%1 invited you to this room").arg(
|
||||||
Utils.coloredNameHtml(inviterName, inviterId)
|
utils.coloredNameHtml(inviterName, inviterId)
|
||||||
)
|
)
|
||||||
|
|
||||||
buttonModel: [
|
buttonModel: [
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
Banner {
|
Banner {
|
||||||
color: theme.chat.leftBanner.background
|
color: theme.chat.leftBanner.background
|
||||||
|
@ -22,7 +21,7 @@ Banner {
|
||||||
|
|
||||||
buttonCallbacks: ({
|
buttonCallbacks: ({
|
||||||
forget: button => {
|
forget: button => {
|
||||||
Utils.makePopup(
|
utils.makePopup(
|
||||||
"Popups/ForgetRoomPopup.qml",
|
"Popups/ForgetRoomPopup.qml",
|
||||||
mainUI, // Must not be destroyed with chat
|
mainUI, // Must not be destroyed with chat
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../utils.js" as ChatJS
|
|
||||||
|
|
||||||
Banner {
|
Banner {
|
||||||
color: theme.chat.unknownDevices.background
|
color: theme.chat.unknownDevices.background
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../Base"
|
import "../Base"
|
||||||
import "../utils.js" as Utils
|
|
||||||
import "RoomPane"
|
import "RoomPane"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -16,10 +15,10 @@ Item {
|
||||||
property bool ready: userInfo !== "waiting" && roomInfo !== "waiting"
|
property bool ready: userInfo !== "waiting" && roomInfo !== "waiting"
|
||||||
|
|
||||||
readonly property var userInfo:
|
readonly property var userInfo:
|
||||||
Utils.getItem(modelSources["Account"] || [], "user_id", userId) ||
|
utils.getItem(modelSources["Account"] || [], "user_id", userId) ||
|
||||||
"waiting"
|
"waiting"
|
||||||
|
|
||||||
readonly property var roomInfo: Utils.getItem(
|
readonly property var roomInfo: utils.getItem(
|
||||||
modelSources[["Room", userId]] || [], "room_id", roomId
|
modelSources[["Room", userId]] || [], "room_id", roomId
|
||||||
) || "waiting"
|
) || "waiting"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../Base"
|
import "../Base"
|
||||||
import "../utils.js" as Utils
|
|
||||||
import "Banners"
|
import "Banners"
|
||||||
import "Timeline"
|
import "Timeline"
|
||||||
import "FileTransfer"
|
import "FileTransfer"
|
||||||
|
|
|
@ -2,7 +2,6 @@ import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../Base"
|
import "../Base"
|
||||||
import "../Dialogs"
|
import "../Dialogs"
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property string indent: " "
|
property string indent: " "
|
||||||
|
@ -12,7 +11,7 @@ Rectangle {
|
||||||
|
|
||||||
property string writingUserId: chat.userId
|
property string writingUserId: chat.userId
|
||||||
readonly property var writingUserInfo:
|
readonly property var writingUserInfo:
|
||||||
Utils.getItem(modelSources["Account"] || [], "user_id", writingUserId)
|
utils.getItem(modelSources["Account"] || [], "user_id", writingUserId)
|
||||||
|
|
||||||
property bool textChangedSinceLostFocus: false
|
property bool textChangedSinceLostFocus: false
|
||||||
|
|
||||||
|
@ -90,7 +89,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
if (Utils.isEmptyObject(aliases)) {
|
if (utils.isEmptyObject(aliases)) {
|
||||||
writingUserId = Qt.binding(() => chat.userId)
|
writingUserId = Qt.binding(() => chat.userId)
|
||||||
toSend = text
|
toSend = text
|
||||||
setTyping(Boolean(text))
|
setTyping(Boolean(text))
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HColumnLayout {
|
HColumnLayout {
|
||||||
id: transfer
|
id: transfer
|
||||||
|
@ -102,7 +101,7 @@ HColumnLayout {
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: [
|
model: [
|
||||||
msLeft ? qsTr("-%1").arg(Utils.formatDuration(msLeft)) : "",
|
msLeft ? qsTr("-%1").arg(utils.formatDuration(msLeft)) : "",
|
||||||
|
|
||||||
speed ? qsTr("%1/s").arg(CppUtils.formattedBytes(speed)) : "",
|
speed ? qsTr("%1/s").arg(CppUtils.formattedBytes(speed)) : "",
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HTileDelegate {
|
HTileDelegate {
|
||||||
id: memberDelegate
|
id: memberDelegate
|
||||||
|
@ -20,7 +19,7 @@ HTileDelegate {
|
||||||
title.text: model.display_name || model.user_id
|
title.text: model.display_name || model.user_id
|
||||||
title.color:
|
title.color:
|
||||||
memberDelegate.hovered ?
|
memberDelegate.hovered ?
|
||||||
Utils.nameColor(model.display_name || model.user_id.substring(1)) :
|
utils.nameColor(model.display_name || model.user_id.substring(1)) :
|
||||||
theme.chat.roomPane.member.name
|
theme.chat.roomPane.member.name
|
||||||
|
|
||||||
subtitle.text: model.display_name ? model.user_id : ""
|
subtitle.text: model.display_name ? model.user_id : ""
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HColumnLayout {
|
HColumnLayout {
|
||||||
HListView {
|
HListView {
|
||||||
|
@ -21,7 +20,7 @@ HColumnLayout {
|
||||||
|
|
||||||
function filterSource() {
|
function filterSource() {
|
||||||
model.source =
|
model.source =
|
||||||
Utils.filterModelSource(originSource, filterField.text)
|
utils.filterModelSource(originSource, filterField.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,7 +76,7 @@ HColumnLayout {
|
||||||
topPadding: 0 // XXX
|
topPadding: 0 // XXX
|
||||||
bottomPadding: 0
|
bottomPadding: 0
|
||||||
|
|
||||||
onClicked: Utils.makePopup(
|
onClicked: utils.makePopup(
|
||||||
"Popups/InviteToRoomPopup.qml",
|
"Popups/InviteToRoomPopup.qml",
|
||||||
chat,
|
chat,
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HBox {
|
HBox {
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
|
@ -3,7 +3,6 @@ import QtQuick.Layouts 1.12
|
||||||
import QtAV 1.7
|
import QtAV 1.7
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../Base/MediaPlayer"
|
import "../../Base/MediaPlayer"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
AudioPlayer {
|
AudioPlayer {
|
||||||
id: audio
|
id: audio
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HRowLayout {
|
HRowLayout {
|
||||||
id: eventContent
|
id: eventContent
|
||||||
|
@ -12,11 +11,11 @@ HRowLayout {
|
||||||
readonly property string senderText:
|
readonly property string senderText:
|
||||||
hideNameLine ? "" : (
|
hideNameLine ? "" : (
|
||||||
"<div class='sender'>" +
|
"<div class='sender'>" +
|
||||||
Utils.coloredNameHtml(model.sender_name, model.sender_id) +
|
utils.coloredNameHtml(model.sender_name, model.sender_id) +
|
||||||
"</div>"
|
"</div>"
|
||||||
)
|
)
|
||||||
readonly property string contentText: Utils.processedEventText(model)
|
readonly property string contentText: utils.processedEventText(model)
|
||||||
readonly property string timeText: Utils.formatTime(model.date, false)
|
readonly property string timeText: utils.formatTime(model.date, false)
|
||||||
readonly property string localEchoText:
|
readonly property string localEchoText:
|
||||||
model.is_local_echo ?
|
model.is_local_echo ?
|
||||||
` <font size=${theme.fontSize.small}px>⏳</font>` :
|
` <font size=${theme.fontSize.small}px>⏳</font>` :
|
||||||
|
@ -40,7 +39,7 @@ HRowLayout {
|
||||||
TapHandler {
|
TapHandler {
|
||||||
enabled: debugMode
|
enabled: debugMode
|
||||||
onDoubleTapped:
|
onDoubleTapped:
|
||||||
Utils.debug(eventContent, null, con => { con.runJS("json()") })
|
utils.debug(eventContent, null, con => { con.runJS("json()") })
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -149,7 +148,7 @@ HRowLayout {
|
||||||
visible: model.event_type === "RoomMessageNotice"
|
visible: model.event_type === "RoomMessageNotice"
|
||||||
width: theme.chat.message.noticeLineWidth
|
width: theme.chat.message.noticeLineWidth
|
||||||
height: parent.height
|
height: parent.height
|
||||||
color: Utils.nameColor(
|
color: utils.nameColor(
|
||||||
model.sender_name || model.sender_id.substring(1),
|
model.sender_name || model.sender_id.substring(1),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HColumnLayout {
|
HColumnLayout {
|
||||||
id: eventDelegate
|
id: eventDelegate
|
||||||
|
@ -63,7 +62,7 @@ HColumnLayout {
|
||||||
function json() {
|
function json() {
|
||||||
return JSON.stringify(
|
return JSON.stringify(
|
||||||
{
|
{
|
||||||
"model": Utils.getItem(
|
"model": utils.getItem(
|
||||||
modelSources[[
|
modelSources[[
|
||||||
"Event", chat.userId, chat.roomId
|
"Event", chat.userId, chat.roomId
|
||||||
]],
|
]],
|
||||||
|
@ -172,7 +171,7 @@ HColumnLayout {
|
||||||
HMenuItem {
|
HMenuItem {
|
||||||
icon.name: "clear-messages"
|
icon.name: "clear-messages"
|
||||||
text: qsTr("Clear messages")
|
text: qsTr("Clear messages")
|
||||||
onTriggered: Utils.makePopup(
|
onTriggered: utils.makePopup(
|
||||||
"Popups/ClearMessagesPopup.qml",
|
"Popups/ClearMessagesPopup.qml",
|
||||||
chat,
|
chat,
|
||||||
{userId: chat.userId, roomId: chat.roomId},
|
{userId: chat.userId, roomId: chat.roomId},
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HTile {
|
HTile {
|
||||||
id: file
|
id: file
|
||||||
|
@ -39,5 +38,5 @@ HTile {
|
||||||
property EventMediaLoader loader
|
property EventMediaLoader loader
|
||||||
|
|
||||||
readonly property bool cryptDict: loader.singleMediaInfo.media_crypt_dict
|
readonly property bool cryptDict: loader.singleMediaInfo.media_crypt_dict
|
||||||
readonly property bool isEncrypted: ! Utils.isEmptyObject(cryptDict)
|
readonly property bool isEncrypted: ! utils.isEmptyObject(cryptDict)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HMxcImage {
|
HMxcImage {
|
||||||
id: image
|
id: image
|
||||||
|
@ -9,7 +8,7 @@ HMxcImage {
|
||||||
horizontalAlignment: Image.AlignLeft
|
horizontalAlignment: Image.AlignLeft
|
||||||
|
|
||||||
animated: loader.singleMediaInfo.media_mime === "image/gif" ||
|
animated: loader.singleMediaInfo.media_mime === "image/gif" ||
|
||||||
Utils.urlExtension(loader.mediaUrl) === "gif"
|
utils.urlExtension(loader.mediaUrl) === "gif"
|
||||||
thumbnail: ! animated && loader.thumbnailMxc
|
thumbnail: ! animated && loader.thumbnailMxc
|
||||||
mxc: thumbnail ?
|
mxc: thumbnail ?
|
||||||
(loader.thumbnailMxc || loader.mediaUrl) :
|
(loader.thumbnailMxc || loader.mediaUrl) :
|
||||||
|
@ -21,12 +20,12 @@ HMxcImage {
|
||||||
|
|
||||||
property EventMediaLoader loader
|
property EventMediaLoader loader
|
||||||
|
|
||||||
readonly property bool isEncrypted: ! Utils.isEmptyObject(cryptDict)
|
readonly property bool isEncrypted: ! utils.isEmptyObject(cryptDict)
|
||||||
|
|
||||||
readonly property real maxHeight:
|
readonly property real maxHeight:
|
||||||
theme.chat.message.thumbnailMaxHeightRatio
|
theme.chat.message.thumbnailMaxHeightRatio
|
||||||
|
|
||||||
readonly property size fitSize: Utils.fitSize(
|
readonly property size fitSize: utils.fitSize(
|
||||||
// Minimum display size
|
// Minimum display size
|
||||||
theme.chat.message.thumbnailMinSize.width,
|
theme.chat.message.thumbnailMinSize.width,
|
||||||
theme.chat.message.thumbnailMinSize.height,
|
theme.chat.message.thumbnailMinSize.height,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property alias selectableLabelContainer: selectableLabelContainer
|
property alias selectableLabelContainer: selectableLabelContainer
|
||||||
|
@ -100,7 +99,7 @@ Rectangle {
|
||||||
! canTalkBreak(item, itemAfter) &&
|
! canTalkBreak(item, itemAfter) &&
|
||||||
! canDayBreak(item, itemAfter) &&
|
! canDayBreak(item, itemAfter) &&
|
||||||
item.sender_id === itemAfter.sender_id &&
|
item.sender_id === itemAfter.sender_id &&
|
||||||
Utils.minutesBetween(item.date, itemAfter.date) <= 5
|
utils.minutesBetween(item.date, itemAfter.date) <= 5
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +108,7 @@ Rectangle {
|
||||||
|
|
||||||
return Boolean(
|
return Boolean(
|
||||||
! canDayBreak(item, itemAfter) &&
|
! canDayBreak(item, itemAfter) &&
|
||||||
Utils.minutesBetween(item.date, itemAfter.date) >= 20
|
utils.minutesBetween(item.date, itemAfter.date) >= 20
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HLoader {
|
HLoader {
|
||||||
id: loader
|
id: loader
|
||||||
|
@ -58,7 +57,7 @@ HLoader {
|
||||||
return EventDelegate.Media.File
|
return EventDelegate.Media.File
|
||||||
|
|
||||||
// If this is a preview for a link in a normal message
|
// If this is a preview for a link in a normal message
|
||||||
let ext = Utils.urlExtension(mediaUrl)
|
let ext = utils.urlExtension(mediaUrl)
|
||||||
|
|
||||||
if (imageExtensions.includes(ext)) return EventDelegate.Media.Image
|
if (imageExtensions.includes(ext)) return EventDelegate.Media.Image
|
||||||
if (videoExtensions.includes(ext)) return EventDelegate.Media.Video
|
if (videoExtensions.includes(ext)) return EventDelegate.Media.Video
|
||||||
|
|
|
@ -3,7 +3,6 @@ import QtQuick.Layouts 1.12
|
||||||
import QtAV 1.7
|
import QtAV 1.7
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../Base/MediaPlayer"
|
import "../../Base/MediaPlayer"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
VideoPlayer {
|
VideoPlayer {
|
||||||
id: video
|
id: video
|
||||||
|
|
|
@ -2,8 +2,6 @@ import QtQuick 2.12
|
||||||
import QtQuick.Window 2.12
|
import QtQuick.Window 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "Base"
|
import "Base"
|
||||||
import "utils.js" as Utils
|
|
||||||
import "utils.js" as U
|
|
||||||
|
|
||||||
HDrawer {
|
HDrawer {
|
||||||
id: debugConsole
|
id: debugConsole
|
||||||
|
@ -31,9 +29,8 @@ HDrawer {
|
||||||
`Javascript debugging console
|
`Javascript debugging console
|
||||||
|
|
||||||
Useful variables:
|
Useful variables:
|
||||||
window, theme, settings, shortcuts, mainUI, pageLoader
|
window, theme, settings, shortcuts, utils, mainUI, pageLoader
|
||||||
py Python interpreter
|
py Python interpreter
|
||||||
U Utils/utils.js module
|
|
||||||
this The console itself
|
this The console itself
|
||||||
t Target item to debug for which this console was opened
|
t Target item to debug for which this console was opened
|
||||||
his History, list of commands entered
|
his History, list of commands entered
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import Qt.labs.platform 1.1
|
import Qt.labs.platform 1.1
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
HFileDialogOpener {
|
HFileDialogOpener {
|
||||||
fill: false
|
fill: false
|
||||||
|
@ -11,7 +10,7 @@ HFileDialogOpener {
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
let path = Qt.resolvedUrl(file).replace(/^file:/, "")
|
let path = Qt.resolvedUrl(file).replace(/^file:/, "")
|
||||||
|
|
||||||
Utils.sendFile(userId, roomId, path, () => {
|
utils.sendFile(userId, roomId, path, () => {
|
||||||
if (destroyWhenDone) destroy()
|
if (destroyWhenDone) destroy()
|
||||||
},
|
},
|
||||||
(type, args, error, traceback) => {
|
(type, args, error, traceback) => {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../Base"
|
import "../Base"
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
HTileDelegate {
|
HTileDelegate {
|
||||||
id: accountDelegate
|
id: accountDelegate
|
||||||
|
@ -113,7 +112,7 @@ HTileDelegate {
|
||||||
icon.name: "sign-out"
|
icon.name: "sign-out"
|
||||||
icon.color: theme.colors.negativeBackground
|
icon.color: theme.colors.negativeBackground
|
||||||
text: qsTr("Sign out")
|
text: qsTr("Sign out")
|
||||||
onTriggered: Utils.makePopup(
|
onTriggered: utils.makePopup(
|
||||||
"Popups/SignOutPopup.qml",
|
"Popups/SignOutPopup.qml",
|
||||||
window,
|
window,
|
||||||
{ "userId": model.data.user_id },
|
{ "userId": model.data.user_id },
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../Base"
|
import "../Base"
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
HListView {
|
HListView {
|
||||||
id: mainPaneList
|
id: mainPaneList
|
||||||
|
@ -28,12 +27,12 @@ HListView {
|
||||||
|
|
||||||
if (item.type === "Account" ||
|
if (item.type === "Account" ||
|
||||||
(filter ?
|
(filter ?
|
||||||
Utils.filterMatches(filter, item.data.filter_string) :
|
utils.filterMatches(filter, item.data.filter_string) :
|
||||||
! window.uiState.collapseAccounts[item.user_id]))
|
! window.uiState.collapseAccounts[item.user_id]))
|
||||||
{
|
{
|
||||||
if (filter && show.length && item.type === "Account" &&
|
if (filter && show.length && item.type === "Account" &&
|
||||||
show[show.length - 1].type === "Account" &&
|
show[show.length - 1].type === "Account" &&
|
||||||
! Utils.filterMatches(
|
! utils.filterMatches(
|
||||||
filter, show[show.length - 1].data.filter_string)
|
filter, show[show.length - 1].data.filter_string)
|
||||||
) {
|
) {
|
||||||
// If filter active, current and previous items are
|
// If filter active, current and previous items are
|
||||||
|
@ -48,7 +47,7 @@ HListView {
|
||||||
|
|
||||||
let last = show[show.length - 1]
|
let last = show[show.length - 1]
|
||||||
if (show.length && filter && last.type === "Account" &&
|
if (show.length && filter && last.type === "Account" &&
|
||||||
! Utils.filterMatches(filter, last.data.filter_string))
|
! utils.filterMatches(filter, last.data.filter_string))
|
||||||
{
|
{
|
||||||
// If filter active, last item is an account and last item
|
// If filter active, last item is an account and last item
|
||||||
// doesn't match filter, that account had no matching rooms.
|
// doesn't match filter, that account had no matching rooms.
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../Base"
|
import "../Base"
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
HDrawer {
|
HDrawer {
|
||||||
id: mainPane
|
id: mainPane
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../Base"
|
import "../Base"
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
HTileDelegate {
|
HTileDelegate {
|
||||||
id: roomDelegate
|
id: roomDelegate
|
||||||
|
@ -53,8 +52,8 @@ HTileDelegate {
|
||||||
! lastEvent || ! lastEvent.date ?
|
! lastEvent || ! lastEvent.date ?
|
||||||
"" :
|
"" :
|
||||||
|
|
||||||
Utils.dateIsToday(lastEvent.date) ?
|
utils.dateIsToday(lastEvent.date) ?
|
||||||
Utils.formatTime(lastEvent.date, false) : // no seconds
|
utils.formatTime(lastEvent.date, false) : // no seconds
|
||||||
|
|
||||||
lastEvent.date.getFullYear() === new Date().getFullYear() ?
|
lastEvent.date.getFullYear() === new Date().getFullYear() ?
|
||||||
Qt.formatDate(lastEvent.date, "d MMM") : // e.g. "5 Dec"
|
Qt.formatDate(lastEvent.date, "d MMM") : // e.g. "5 Dec"
|
||||||
|
@ -76,10 +75,10 @@ HTileDelegate {
|
||||||
|
|
||||||
// If it's a general event
|
// If it's a general event
|
||||||
if (isEmote || isUnknownMsg || (! isMsg && ! isCryptMedia)) {
|
if (isEmote || isUnknownMsg || (! isMsg && ! isCryptMedia)) {
|
||||||
return Utils.processedEventText(lastEvent)
|
return utils.processedEventText(lastEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
let text = Utils.coloredNameHtml(
|
let text = utils.coloredNameHtml(
|
||||||
lastEvent.sender_name, lastEvent.sender_id
|
lastEvent.sender_name, lastEvent.sender_id
|
||||||
) + ": " + lastEvent.inline_content
|
) + ": " + lastEvent.inline_content
|
||||||
|
|
||||||
|
@ -96,7 +95,7 @@ HTileDelegate {
|
||||||
icon.name: "room-send-invite"
|
icon.name: "room-send-invite"
|
||||||
text: qsTr("Invite members")
|
text: qsTr("Invite members")
|
||||||
|
|
||||||
onTriggered: Utils.makePopup(
|
onTriggered: utils.makePopup(
|
||||||
"Popups/InviteToRoomPopup.qml",
|
"Popups/InviteToRoomPopup.qml",
|
||||||
window,
|
window,
|
||||||
{
|
{
|
||||||
|
@ -118,7 +117,7 @@ HTileDelegate {
|
||||||
visible: invited
|
visible: invited
|
||||||
icon.name: "invite-accept"
|
icon.name: "invite-accept"
|
||||||
icon.color: theme.colors.positiveBackground
|
icon.color: theme.colors.positiveBackground
|
||||||
text: qsTr("Accept %1's invite").arg(Utils.coloredNameHtml(
|
text: qsTr("Accept %1's invite").arg(utils.coloredNameHtml(
|
||||||
model.data.inviter_name, model.data.inviter_id
|
model.data.inviter_name, model.data.inviter_id
|
||||||
))
|
))
|
||||||
label.textFormat: Text.StyledText
|
label.textFormat: Text.StyledText
|
||||||
|
@ -134,7 +133,7 @@ HTileDelegate {
|
||||||
icon.color: theme.colors.negativeBackground
|
icon.color: theme.colors.negativeBackground
|
||||||
text: invited ? qsTr("Decline invite") : qsTr("Leave")
|
text: invited ? qsTr("Decline invite") : qsTr("Leave")
|
||||||
|
|
||||||
onTriggered: Utils.makePopup(
|
onTriggered: utils.makePopup(
|
||||||
"Popups/LeaveRoomPopup.qml",
|
"Popups/LeaveRoomPopup.qml",
|
||||||
window,
|
window,
|
||||||
{
|
{
|
||||||
|
@ -150,7 +149,7 @@ HTileDelegate {
|
||||||
icon.color: theme.colors.negativeBackground
|
icon.color: theme.colors.negativeBackground
|
||||||
text: qsTr("Forget")
|
text: qsTr("Forget")
|
||||||
|
|
||||||
onTriggered: Utils.makePopup(
|
onTriggered: utils.makePopup(
|
||||||
"Popups/ForgetRoomPopup.qml",
|
"Popups/ForgetRoomPopup.qml",
|
||||||
window,
|
window,
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,6 @@ import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HPage {
|
HPage {
|
||||||
id: accountSettings
|
id: accountSettings
|
||||||
|
@ -14,7 +13,7 @@ HPage {
|
||||||
readonly property bool ready:
|
readonly property bool ready:
|
||||||
accountInfo !== "waiting" && Boolean(accountInfo.profile_updated)
|
accountInfo !== "waiting" && Boolean(accountInfo.profile_updated)
|
||||||
|
|
||||||
readonly property var accountInfo: Utils.getItem(
|
readonly property var accountInfo: utils.getItem(
|
||||||
modelSources["Account"] || [], "user_id", userId
|
modelSources["Account"] || [], "user_id", userId
|
||||||
) || "waiting"
|
) || "waiting"
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ HPage {
|
||||||
|
|
||||||
hideHeaderUnderHeight: avatarPreferredSize
|
hideHeaderUnderHeight: avatarPreferredSize
|
||||||
headerLabel.text: qsTr("Account settings for %1").arg(
|
headerLabel.text: qsTr("Account settings for %1").arg(
|
||||||
Utils.coloredNameHtml(headerName, userId)
|
utils.coloredNameHtml(headerName, userId)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HBox {
|
HBox {
|
||||||
buttonModel: [
|
buttonModel: [
|
||||||
|
@ -11,7 +10,7 @@ HBox {
|
||||||
|
|
||||||
buttonCallbacks: ({
|
buttonCallbacks: ({
|
||||||
export: button => {
|
export: button => {
|
||||||
Utils.makeObject(
|
utils.makeObject(
|
||||||
"Dialogs/ExportKeys.qml",
|
"Dialogs/ExportKeys.qml",
|
||||||
accountSettings,
|
accountSettings,
|
||||||
{ userId: accountSettings.userId },
|
{ userId: accountSettings.userId },
|
||||||
|
@ -22,7 +21,7 @@ HBox {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
import: button => {
|
import: button => {
|
||||||
Utils.makeObject(
|
utils.makeObject(
|
||||||
"Dialogs/ImportKeys.qml",
|
"Dialogs/ImportKeys.qml",
|
||||||
accountSettings,
|
accountSettings,
|
||||||
{ userId: accountSettings.userId },
|
{ userId: accountSettings.userId },
|
||||||
|
|
|
@ -3,7 +3,6 @@ import QtQuick.Controls 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../Dialogs"
|
import "../../Dialogs"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HGridLayout {
|
HGridLayout {
|
||||||
function applyChanges() {
|
function applyChanges() {
|
||||||
|
@ -77,7 +76,7 @@ HGridLayout {
|
||||||
1 : 0
|
1 : 0
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Utils.hsluv(0, 0, 0,
|
color: utils.hsluv(0, 0, 0,
|
||||||
(! avatar.mxc && overlayHover.hovered) ? 0.8 : 0.7
|
(! avatar.mxc && overlayHover.hovered) ? 0.8 : 0.7
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -139,7 +138,7 @@ HGridLayout {
|
||||||
|
|
||||||
HLabel {
|
HLabel {
|
||||||
text: qsTr("User ID:<br>%1")
|
text: qsTr("User ID:<br>%1")
|
||||||
.arg(Utils.coloredNameHtml(userId, userId, userId))
|
.arg(utils.coloredNameHtml(userId, userId, userId))
|
||||||
textFormat: Text.StyledText
|
textFormat: Text.StyledText
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HPage {
|
HPage {
|
||||||
id: addChatPage
|
id: addChatPage
|
||||||
|
@ -10,7 +9,7 @@ HPage {
|
||||||
property string userId
|
property string userId
|
||||||
|
|
||||||
readonly property var account:
|
readonly property var account:
|
||||||
Utils.getItem(modelSources["Account"] || [], "user_id", userId)
|
utils.getItem(modelSources["Account"] || [], "user_id", userId)
|
||||||
|
|
||||||
|
|
||||||
HTabContainer {
|
HTabContainer {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HBox {
|
HBox {
|
||||||
id: addChatBox
|
id: addChatBox
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HBox {
|
HBox {
|
||||||
id: addChatBox
|
id: addChatBox
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../utils.js" as Utils
|
|
||||||
|
|
||||||
HBox {
|
HBox {
|
||||||
id: addChatBox
|
id: addChatBox
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../Base"
|
import "../Base"
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
HPopup {
|
HPopup {
|
||||||
id: popup
|
id: popup
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import "../utils.js" as Utils
|
|
||||||
|
|
||||||
BoxPopup {
|
BoxPopup {
|
||||||
id: popup
|
id: popup
|
||||||
|
@ -25,7 +24,7 @@ BoxPopup {
|
||||||
|
|
||||||
box.buttonCallbacks: ({
|
box.buttonCallbacks: ({
|
||||||
ok: button => {
|
ok: button => {
|
||||||
Utils.makeObject(
|
utils.makeObject(
|
||||||
"Dialogs/ExportKeys.qml",
|
"Dialogs/ExportKeys.qml",
|
||||||
mainUI,
|
mainUI,
|
||||||
{ userId },
|
{ userId },
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import "Base"
|
import "Base"
|
||||||
import "utils.js" as Utils
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: false
|
visible: false
|
||||||
|
@ -35,7 +34,7 @@ Item {
|
||||||
if (debugConsole) {
|
if (debugConsole) {
|
||||||
debugConsole.visible = ! debugConsole.visible
|
debugConsole.visible = ! debugConsole.visible
|
||||||
} else {
|
} else {
|
||||||
Utils.debug(mainUI || window)
|
utils.debug(mainUI || window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,37 +71,37 @@ Item {
|
||||||
HShortcut {
|
HShortcut {
|
||||||
enabled: toFlick
|
enabled: toFlick
|
||||||
sequences: settings.keys.scrollUp
|
sequences: settings.keys.scrollUp
|
||||||
onActivated: Utils.flickPages(toFlick, -1 / 10)
|
onActivated: utils.flickPages(toFlick, -1 / 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
HShortcut {
|
HShortcut {
|
||||||
enabled: toFlick
|
enabled: toFlick
|
||||||
sequences: settings.keys.scrollDown
|
sequences: settings.keys.scrollDown
|
||||||
onActivated: Utils.flickPages(toFlick, 1 / 10)
|
onActivated: utils.flickPages(toFlick, 1 / 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
HShortcut {
|
HShortcut {
|
||||||
enabled: toFlick
|
enabled: toFlick
|
||||||
sequences: settings.keys.scrollPageUp
|
sequences: settings.keys.scrollPageUp
|
||||||
onActivated: Utils.flickPages(toFlick, -1)
|
onActivated: utils.flickPages(toFlick, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
HShortcut {
|
HShortcut {
|
||||||
enabled: toFlick
|
enabled: toFlick
|
||||||
sequences: settings.keys.scrollPageDown
|
sequences: settings.keys.scrollPageDown
|
||||||
onActivated: Utils.flickPages(toFlick, 1)
|
onActivated: utils.flickPages(toFlick, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
HShortcut {
|
HShortcut {
|
||||||
enabled: toFlick
|
enabled: toFlick
|
||||||
sequences: settings.keys.scrollToTop
|
sequences: settings.keys.scrollToTop
|
||||||
onActivated: Utils.flickToTop(toFlick)
|
onActivated: utils.flickToTop(toFlick)
|
||||||
}
|
}
|
||||||
|
|
||||||
HShortcut {
|
HShortcut {
|
||||||
enabled: toFlick
|
enabled: toFlick
|
||||||
sequences: settings.keys.scrollToBottom
|
sequences: settings.keys.scrollToBottom
|
||||||
onActivated: Utils.flickToBottom(toFlick)
|
onActivated: utils.flickToBottom(toFlick)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,7 +111,7 @@ Item {
|
||||||
enabled: tabsTarget
|
enabled: tabsTarget
|
||||||
sequences: settings.keys.previousTab
|
sequences: settings.keys.previousTab
|
||||||
onActivated: tabsTarget.setCurrentIndex(
|
onActivated: tabsTarget.setCurrentIndex(
|
||||||
Utils.numberWrapAt(tabsTarget.currentIndex - 1, tabsTarget.count),
|
utils.numberWrapAt(tabsTarget.currentIndex - 1, tabsTarget.count),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +119,7 @@ Item {
|
||||||
enabled: tabsTarget
|
enabled: tabsTarget
|
||||||
sequences: settings.keys.nextTab
|
sequences: settings.keys.nextTab
|
||||||
onActivated: tabsTarget.setCurrentIndex(
|
onActivated: tabsTarget.setCurrentIndex(
|
||||||
Utils.numberWrapAt(tabsTarget.currentIndex + 1, tabsTarget.count),
|
utils.numberWrapAt(tabsTarget.currentIndex + 1, tabsTarget.count),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +184,7 @@ Item {
|
||||||
HShortcut {
|
HShortcut {
|
||||||
enabled: window.uiState.page === "Chat/Chat.qml"
|
enabled: window.uiState.page === "Chat/Chat.qml"
|
||||||
sequences: settings.keys.clearRoomMessages
|
sequences: settings.keys.clearRoomMessages
|
||||||
onActivated: Utils.makePopup(
|
onActivated: utils.makePopup(
|
||||||
"Popups/ClearMessagesPopup.qml",
|
"Popups/ClearMessagesPopup.qml",
|
||||||
mainUI,
|
mainUI,
|
||||||
{
|
{
|
||||||
|
@ -198,7 +197,7 @@ Item {
|
||||||
HShortcut {
|
HShortcut {
|
||||||
enabled: window.uiState.page === "Chat/Chat.qml"
|
enabled: window.uiState.page === "Chat/Chat.qml"
|
||||||
sequences: settings.keys.sendFile
|
sequences: settings.keys.sendFile
|
||||||
onActivated: Utils.makeObject(
|
onActivated: utils.makeObject(
|
||||||
"Dialogs/SendFilePicker.qml",
|
"Dialogs/SendFilePicker.qml",
|
||||||
mainUI,
|
mainUI,
|
||||||
{
|
{
|
||||||
|
@ -213,7 +212,7 @@ Item {
|
||||||
HShortcut {
|
HShortcut {
|
||||||
enabled: window.uiState.page === "Chat/Chat.qml"
|
enabled: window.uiState.page === "Chat/Chat.qml"
|
||||||
sequences: settings.keys.sendFileFromPathInClipboard
|
sequences: settings.keys.sendFileFromPathInClipboard
|
||||||
onActivated: Utils.sendFile(
|
onActivated: utils.sendFile(
|
||||||
window.uiState.pageProperties.userId,
|
window.uiState.pageProperties.userId,
|
||||||
window.uiState.pageProperties.roomId,
|
window.uiState.pageProperties.roomId,
|
||||||
Clipboard.text.trim(),
|
Clipboard.text.trim(),
|
||||||
|
|
|
@ -5,7 +5,6 @@ import QtQuick.Window 2.7
|
||||||
import QtGraphicalEffects 1.12
|
import QtGraphicalEffects 1.12
|
||||||
import "Base"
|
import "Base"
|
||||||
import "MainPane"
|
import "MainPane"
|
||||||
import "utils.js" as Utils
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: mainUI
|
id: mainUI
|
||||||
|
|
323
src/qml/Utils.qml
Normal file
323
src/qml/Utils.qml
Normal file
|
@ -0,0 +1,323 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
function makeObject(urlComponent, parent=null, properties={},
|
||||||
|
callback=null) {
|
||||||
|
let comp = urlComponent
|
||||||
|
|
||||||
|
if (! Qt.isQtObject(urlComponent)) {
|
||||||
|
// It's an url or path string to a component
|
||||||
|
comp = Qt.createComponent(urlComponent, Component.Asynchronous)
|
||||||
|
}
|
||||||
|
|
||||||
|
let ready = false
|
||||||
|
|
||||||
|
comp.statusChanged.connect(status => {
|
||||||
|
if ([Component.Null, Component.Error].includes(status)) {
|
||||||
|
console.error("Failed creating component: ", comp.errorString())
|
||||||
|
|
||||||
|
} else if (! ready && status === Component.Ready) {
|
||||||
|
let incu = comp.incubateObject(
|
||||||
|
parent, properties, Qt.Asynchronous,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (incu.status === Component.Ready) {
|
||||||
|
if (callback) callback(incu.object)
|
||||||
|
ready = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
incu.onStatusChanged = (istatus) => {
|
||||||
|
if (incu.status === Component.Error) {
|
||||||
|
console.error("Failed incubating object: ",
|
||||||
|
incu.errorString())
|
||||||
|
|
||||||
|
} else if (istatus === Component.Ready &&
|
||||||
|
callback && ! ready) {
|
||||||
|
if (callback) callback(incu.object)
|
||||||
|
ready = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (comp.status === Component.Ready) comp.statusChanged(comp.status)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function makePopup(urlComponent, parent=null, properties={}, callback=null,
|
||||||
|
autoDestruct=true) {
|
||||||
|
makeObject(urlComponent, parent, properties, (popup) => {
|
||||||
|
popup.open()
|
||||||
|
if (autoDestruct) popup.closed.connect(() => { popup.destroy() })
|
||||||
|
if (callback) callback(popup)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function debug(target, parent=null, callback=null) {
|
||||||
|
parent = parent || target
|
||||||
|
return utils.makeObject(
|
||||||
|
"DebugConsole.qml", parent, { target }, callback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function isEmptyObject(obj) {
|
||||||
|
return Object.entries(obj).length === 0 && obj.constructor === Object
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function objectUpdateRecursive(current, update) {
|
||||||
|
for (const key of Object.keys(update)) {
|
||||||
|
if ((key in current) && typeof(current[key]) === "object" &&
|
||||||
|
typeof(update[key]) === "object") {
|
||||||
|
objectUpdateRecursive(current[key], update[key])
|
||||||
|
} else {
|
||||||
|
current[key] = update[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function numberWrapAt(num, max) {
|
||||||
|
return num < 0 ? max + (num % max) : (num % max)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function hsluv(hue, saturation, lightness, alpha=1.0) {
|
||||||
|
hue = numberWrapAt(hue, 360)
|
||||||
|
let rgb = py.callSync("hsluv", [hue, saturation, lightness])
|
||||||
|
return Qt.rgba(rgb[0], rgb[1], rgb[2], alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function hueFrom(string) {
|
||||||
|
// Calculate and return a unique hue between 0 and 360 for the string
|
||||||
|
let hue = 0
|
||||||
|
for (let i = 0; i < string.length; i++) {
|
||||||
|
hue += string.charCodeAt(i) * 99
|
||||||
|
}
|
||||||
|
return hue % 360
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function nameColor(name) {
|
||||||
|
return hsluv(
|
||||||
|
hueFrom(name),
|
||||||
|
theme.controls.displayName.saturation,
|
||||||
|
theme.controls.displayName.lightness,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function coloredNameHtml(name, userId, displayText=null,
|
||||||
|
disambiguate=false) {
|
||||||
|
// substring: remove leading @
|
||||||
|
return `<font color="${nameColor(name || userId.substring(1))}">` +
|
||||||
|
escapeHtml(displayText || name || userId) +
|
||||||
|
"</font>"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function escapeHtml(string) {
|
||||||
|
// Replace special HTML characters by encoded alternatives
|
||||||
|
return string.replace("&", "&")
|
||||||
|
.replace("<", "<")
|
||||||
|
.replace(">", ">")
|
||||||
|
.replace('"', """)
|
||||||
|
.replace("'", "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function processedEventText(ev) {
|
||||||
|
if (ev.event_type === "RoomMessageEmote")
|
||||||
|
return coloredNameHtml(ev.sender_name, ev.sender_id) + " " +
|
||||||
|
ev.content
|
||||||
|
|
||||||
|
let unknown = ev.event_type === "RoomMessageUnknown"
|
||||||
|
|
||||||
|
if (ev.event_type.startsWith("RoomMessage") && ! unknown)
|
||||||
|
return ev.content
|
||||||
|
|
||||||
|
if (ev.event_type.startsWith("RoomEncrypted")) return ev.content
|
||||||
|
|
||||||
|
let text = qsTr(ev.content).arg(
|
||||||
|
coloredNameHtml(ev.sender_name, ev.sender_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (text.includes("%2") && ev.target_id)
|
||||||
|
text = text.arg(coloredNameHtml(ev.target_name, ev.target_id))
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function filterMatches(filter, text) {
|
||||||
|
let filter_lower = filter.toLowerCase()
|
||||||
|
|
||||||
|
if (filter_lower === filter) {
|
||||||
|
// Consider case only if filter isn't all lowercase (smart case)
|
||||||
|
filter = filter_lower
|
||||||
|
text = text.toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let word of filter.split(" ")) {
|
||||||
|
if (word && ! text.includes(word)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function filterModelSource(source, filter_text, property="filter_string") {
|
||||||
|
if (! filter_text) return source
|
||||||
|
let results = []
|
||||||
|
|
||||||
|
for (let i = 0; i < source.length; i++) {
|
||||||
|
if (filterMatches(filter_text, source[i][property])) {
|
||||||
|
results.push(source[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function fitSize(minWidth, minHeight, width, height, maxWidth, maxHeight) {
|
||||||
|
if (width >= height) {
|
||||||
|
let new_width = Math.max(Math.min(width, maxWidth), minWidth)
|
||||||
|
return Qt.size(new_width, height / (width / new_width))
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_height = Math.max(Math.min(height, maxHeight), minHeight)
|
||||||
|
return Qt.size(width / (height / new_height), new_height)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function minutesBetween(date1, date2) {
|
||||||
|
return ((date2 - date1) / 1000) / 60
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function dateIsDay(date, dayDate) {
|
||||||
|
return date.getDate() === dayDate.getDate() &&
|
||||||
|
date.getMonth() === dayDate.getMonth() &&
|
||||||
|
date.getFullYear() === dayDate.getFullYear()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function dateIsToday(date) {
|
||||||
|
return dateIsDay(date, new Date())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function dateIsYesterday(date) {
|
||||||
|
const yesterday = new Date()
|
||||||
|
yesterday.setDate(yesterday.getDate() - 1)
|
||||||
|
return dateIsDay(date, yesterday)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function formatTime(time, seconds=true) {
|
||||||
|
return Qt.formatTime(
|
||||||
|
time,
|
||||||
|
|
||||||
|
Qt.locale().timeFormat(
|
||||||
|
seconds ? Locale.LongFormat : Locale.NarrowFormat
|
||||||
|
).replace(/\./g, ":").replace(/ t$/, "")
|
||||||
|
// en_DK.UTF-8 locale wrongfully gives "." separators;
|
||||||
|
// also remove the timezone at the end
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function formatDuration(milliseconds) {
|
||||||
|
let totalSeconds = milliseconds / 1000
|
||||||
|
|
||||||
|
let hours = Math.floor(totalSeconds / 3600)
|
||||||
|
let minutes = Math.floor((totalSeconds % 3600) / 60)
|
||||||
|
let seconds = Math.floor(totalSeconds % 60)
|
||||||
|
|
||||||
|
if (seconds < 10) seconds = `0${seconds}`
|
||||||
|
if (hours < 1) return `${minutes}:${seconds}`
|
||||||
|
|
||||||
|
if (minutes < 10) minutes = `0${minutes}`
|
||||||
|
return `${hours}:${minutes}:${seconds}`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function round(floatNumber) {
|
||||||
|
return parseFloat(floatNumber.toFixed(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getItem(array, mainKey, value) {
|
||||||
|
for (let i = 0; i < array.length; i++) {
|
||||||
|
if (array[i][mainKey] === value) { return array[i] }
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function flickPages(flickable, pages) {
|
||||||
|
// Adapt velocity and deceleration for the number of pages to flick.
|
||||||
|
// If this is a repeated flicking, flick faster than a single flick.
|
||||||
|
if (! flickable.interactive && flickable.allowDragging) return
|
||||||
|
|
||||||
|
const futureVelocity = -flickable.height * pages
|
||||||
|
const currentVelocity = -flickable.verticalVelocity
|
||||||
|
const goFaster =
|
||||||
|
(futureVelocity < 0 && currentVelocity < futureVelocity / 2) ||
|
||||||
|
(futureVelocity > 0 && currentVelocity > futureVelocity / 2)
|
||||||
|
|
||||||
|
const normalDecel = flickable.flickDeceleration
|
||||||
|
const fastMultiply = pages && 8 / (1 - Math.log10(Math.abs(pages)))
|
||||||
|
const magicNumber = 2.5
|
||||||
|
|
||||||
|
flickable.flickDeceleration = Math.max(
|
||||||
|
goFaster ? normalDecel : -Infinity,
|
||||||
|
Math.abs(normalDecel * magicNumber * pages),
|
||||||
|
)
|
||||||
|
|
||||||
|
flickable.flick(
|
||||||
|
0, futureVelocity * magicNumber * (goFaster ? fastMultiply : 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
flickable.flickDeceleration = normalDecel
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function flickToTop(flickable) {
|
||||||
|
if (! flickable.interactive && flickable.allowDragging) return
|
||||||
|
if (flickable.visibleArea.yPosition < 0) return
|
||||||
|
|
||||||
|
flickable.contentY -= flickable.contentHeight
|
||||||
|
flickable.returnToBounds()
|
||||||
|
flickable.flick(0, -100) // Force the delegates to load
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function flickToBottom(flickable) {
|
||||||
|
if (! flickable.interactive && flickable.allowDragging) return
|
||||||
|
if (flickable.visibleArea.yPosition < 0) return
|
||||||
|
|
||||||
|
flickable.contentY = flickTarget.contentHeight - flickTarget.height
|
||||||
|
flickable.returnToBounds()
|
||||||
|
flickable.flick(0, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function urlExtension(url) {
|
||||||
|
return url.toString().split("/").slice(-1)[0].split("?")[0].split(".")
|
||||||
|
.slice(-1)[0].toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function sendFile(userId, roomId, path, onSuccess, onError) {
|
||||||
|
py.callClientCoro(
|
||||||
|
userId, "send_file", [roomId, path], onSuccess, onError,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import "Base"
|
import "Base"
|
||||||
import "utils.js" as Utils
|
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: window
|
id: window
|
||||||
|
@ -55,7 +54,7 @@ ApplicationWindow {
|
||||||
propertyValues[prop] = obj[prop]
|
propertyValues[prop] = obj[prop]
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils.objectUpdateRecursive(uiState, {
|
utils.objectUpdateRecursive(uiState, {
|
||||||
[obj.saveName]: { [obj.saveId || "ALL"]: propertyValues },
|
[obj.saveName]: { [obj.saveId || "ALL"]: propertyValues },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -73,6 +72,8 @@ ApplicationWindow {
|
||||||
|
|
||||||
Python { id: py }
|
Python { id: py }
|
||||||
|
|
||||||
|
Utils { id: utils }
|
||||||
|
|
||||||
HoverHandler { id: windowHover }
|
HoverHandler { id: windowHover }
|
||||||
|
|
||||||
HLoader {
|
HLoader {
|
||||||
|
|
307
src/qml/utils.js
307
src/qml/utils.js
|
@ -1,307 +0,0 @@
|
||||||
function makeObject(urlComponent, parent=null, properties={}, callback=null) {
|
|
||||||
let comp = urlComponent
|
|
||||||
|
|
||||||
if (! Qt.isQtObject(urlComponent)) {
|
|
||||||
// It's an url or path string to a component
|
|
||||||
comp = Qt.createComponent(urlComponent, Component.Asynchronous)
|
|
||||||
}
|
|
||||||
|
|
||||||
let ready = false
|
|
||||||
|
|
||||||
comp.statusChanged.connect(status => {
|
|
||||||
if ([Component.Null, Component.Error].includes(status)) {
|
|
||||||
console.error("Failed creating component: ", comp.errorString())
|
|
||||||
|
|
||||||
} else if (! ready && status === Component.Ready) {
|
|
||||||
let incu = comp.incubateObject(parent, properties, Qt.Asynchronous)
|
|
||||||
|
|
||||||
if (incu.status === Component.Ready) {
|
|
||||||
if (callback) callback(incu.object)
|
|
||||||
ready = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
incu.onStatusChanged = (istatus) => {
|
|
||||||
if (incu.status === Component.Error) {
|
|
||||||
console.error("Failed incubating object: ",
|
|
||||||
incu.errorString())
|
|
||||||
|
|
||||||
} else if (istatus === Component.Ready && callback && ! ready) {
|
|
||||||
if (callback) callback(incu.object)
|
|
||||||
ready = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (comp.status === Component.Ready) comp.statusChanged(comp.status)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function makePopup(urlComponent, parent=null, properties={}, callback=null,
|
|
||||||
autoDestruct=true) {
|
|
||||||
makeObject(urlComponent, parent, properties, (popup) => {
|
|
||||||
popup.open()
|
|
||||||
if (autoDestruct) popup.closed.connect(() => { popup.destroy() })
|
|
||||||
if (callback) callback(popup)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function debug(target, parent=null, callback=null) {
|
|
||||||
parent = parent || target
|
|
||||||
return Utils.makeObject("DebugConsole.qml", parent, { target }, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function isEmptyObject(obj) {
|
|
||||||
return Object.entries(obj).length === 0 && obj.constructor === Object
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function objectUpdateRecursive(current, update) {
|
|
||||||
for (const key of Object.keys(update)) {
|
|
||||||
if ((key in current) && typeof(current[key]) === "object" &&
|
|
||||||
typeof(update[key]) === "object") {
|
|
||||||
objectUpdateRecursive(current[key], update[key])
|
|
||||||
} else {
|
|
||||||
current[key] = update[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function numberWrapAt(num, max) {
|
|
||||||
return num < 0 ? max + (num % max) : (num % max)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function hsluv(hue, saturation, lightness, alpha=1.0) {
|
|
||||||
hue = numberWrapAt(hue, 360)
|
|
||||||
let rgb = py.callSync("hsluv", [hue, saturation, lightness])
|
|
||||||
return Qt.rgba(rgb[0], rgb[1], rgb[2], alpha)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function hueFrom(string) {
|
|
||||||
// Calculate and return a unique hue between 0 and 360 for the string
|
|
||||||
let hue = 0
|
|
||||||
for (let i = 0; i < string.length; i++) {
|
|
||||||
hue += string.charCodeAt(i) * 99
|
|
||||||
}
|
|
||||||
return hue % 360
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function nameColor(name) {
|
|
||||||
return hsluv(
|
|
||||||
hueFrom(name),
|
|
||||||
theme.controls.displayName.saturation,
|
|
||||||
theme.controls.displayName.lightness,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function coloredNameHtml(name, userId, displayText=null, disambiguate=false) {
|
|
||||||
// substring: remove leading @
|
|
||||||
return `<font color="${nameColor(name || userId.substring(1))}">` +
|
|
||||||
escapeHtml(displayText || name || userId) +
|
|
||||||
"</font>"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function escapeHtml(string) {
|
|
||||||
// Replace special HTML characters by encoded alternatives
|
|
||||||
return string.replace("&", "&")
|
|
||||||
.replace("<", "<")
|
|
||||||
.replace(">", ">")
|
|
||||||
.replace('"', """)
|
|
||||||
.replace("'", "'")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function processedEventText(ev) {
|
|
||||||
if (ev.event_type === "RoomMessageEmote")
|
|
||||||
return coloredNameHtml(ev.sender_name, ev.sender_id) + " " + ev.content
|
|
||||||
|
|
||||||
let unknown = ev.event_type === "RoomMessageUnknown"
|
|
||||||
|
|
||||||
if (ev.event_type.startsWith("RoomMessage") && ! unknown) return ev.content
|
|
||||||
if (ev.event_type.startsWith("RoomEncrypted")) return ev.content
|
|
||||||
|
|
||||||
let text = qsTr(ev.content).arg(
|
|
||||||
coloredNameHtml(ev.sender_name, ev.sender_id)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (text.includes("%2") && ev.target_id)
|
|
||||||
text = text.arg(coloredNameHtml(ev.target_name, ev.target_id))
|
|
||||||
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function filterMatches(filter, text) {
|
|
||||||
let filter_lower = filter.toLowerCase()
|
|
||||||
|
|
||||||
if (filter_lower === filter) {
|
|
||||||
// Consider case only if filter isn't all lowercase (smart case)
|
|
||||||
filter = filter_lower
|
|
||||||
text = text.toLowerCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let word of filter.split(" ")) {
|
|
||||||
if (word && ! text.includes(word)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function filterModelSource(source, filter_text, property="filter_string") {
|
|
||||||
if (! filter_text) return source
|
|
||||||
let results = []
|
|
||||||
|
|
||||||
for (let i = 0; i < source.length; i++) {
|
|
||||||
if (filterMatches(filter_text, source[i][property])) {
|
|
||||||
results.push(source[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function fitSize(minWidth, minHeight, width, height, maxWidth, maxHeight) {
|
|
||||||
if (width >= height) {
|
|
||||||
let new_width = Math.max(Math.min(width, maxWidth), minWidth)
|
|
||||||
return Qt.size(new_width, height / (width / new_width))
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_height = Math.max(Math.min(height, maxHeight), minHeight)
|
|
||||||
return Qt.size(width / (height / new_height), new_height)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function minutesBetween(date1, date2) {
|
|
||||||
return ((date2 - date1) / 1000) / 60
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function dateIsDay(date, dayDate) {
|
|
||||||
return date.getDate() === dayDate.getDate() &&
|
|
||||||
date.getMonth() === dayDate.getMonth() &&
|
|
||||||
date.getFullYear() === dayDate.getFullYear()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function dateIsToday(date) {
|
|
||||||
return dateIsDay(date, new Date())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function dateIsYesterday(date) {
|
|
||||||
const yesterday = new Date()
|
|
||||||
yesterday.setDate(yesterday.getDate() - 1)
|
|
||||||
return dateIsDay(date, yesterday)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function formatTime(time, seconds=true) {
|
|
||||||
return Qt.formatTime(
|
|
||||||
time,
|
|
||||||
|
|
||||||
Qt.locale().timeFormat(
|
|
||||||
seconds ? Locale.LongFormat : Locale.NarrowFormat
|
|
||||||
).replace(/\./g, ":").replace(/ t$/, "")
|
|
||||||
// en_DK.UTF-8 locale wrongfully gives "." separators;
|
|
||||||
// also remove the timezone at the end
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function formatDuration(milliseconds) {
|
|
||||||
let totalSeconds = milliseconds / 1000
|
|
||||||
|
|
||||||
let hours = Math.floor(totalSeconds / 3600)
|
|
||||||
let minutes = Math.floor((totalSeconds % 3600) / 60)
|
|
||||||
let seconds = Math.floor(totalSeconds % 60)
|
|
||||||
|
|
||||||
if (seconds < 10) seconds = `0${seconds}`
|
|
||||||
if (hours < 1) return `${minutes}:${seconds}`
|
|
||||||
|
|
||||||
if (minutes < 10) minutes = `0${minutes}`
|
|
||||||
return `${hours}:${minutes}:${seconds}`
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function round(float) {
|
|
||||||
return parseFloat(float.toFixed(2))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getItem(array, mainKey, value) {
|
|
||||||
for (let i = 0; i < array.length; i++) {
|
|
||||||
if (array[i][mainKey] === value) { return array[i] }
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function flickPages(flickable, pages) {
|
|
||||||
// Adapt velocity and deceleration for the number of pages to flick.
|
|
||||||
// If this is a repeated flicking, flick faster than a single flick.
|
|
||||||
if (! flickable.interactive && flickable.allowDragging) return
|
|
||||||
|
|
||||||
const futureVelocity = -flickable.height * pages
|
|
||||||
const currentVelocity = -flickable.verticalVelocity
|
|
||||||
const goFaster =
|
|
||||||
(futureVelocity < 0 && currentVelocity < futureVelocity / 2) ||
|
|
||||||
(futureVelocity > 0 && currentVelocity > futureVelocity / 2)
|
|
||||||
|
|
||||||
const normalDecel = flickable.flickDeceleration
|
|
||||||
const fastMultiply = pages && 8 / (1 - Math.log10(Math.abs(pages)))
|
|
||||||
const magicNumber = 2.5
|
|
||||||
|
|
||||||
flickable.flickDeceleration = Math.max(
|
|
||||||
goFaster ? normalDecel : -Infinity,
|
|
||||||
Math.abs(normalDecel * magicNumber * pages),
|
|
||||||
)
|
|
||||||
|
|
||||||
flickable.flick(
|
|
||||||
0, futureVelocity * magicNumber * (goFaster ? fastMultiply : 1),
|
|
||||||
)
|
|
||||||
|
|
||||||
flickable.flickDeceleration = normalDecel
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function flickToTop(flickable) {
|
|
||||||
if (! flickable.interactive && flickable.allowDragging) return
|
|
||||||
if (flickable.visibleArea.yPosition < 0) return
|
|
||||||
|
|
||||||
flickable.contentY -= flickable.contentHeight
|
|
||||||
flickable.returnToBounds()
|
|
||||||
flickable.flick(0, -100) // Force the delegates to load
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function flickToBottom(flickable) {
|
|
||||||
if (! flickable.interactive && flickable.allowDragging) return
|
|
||||||
if (flickable.visibleArea.yPosition < 0) return
|
|
||||||
|
|
||||||
flickable.contentY = flickTarget.contentHeight - flickTarget.height
|
|
||||||
flickable.returnToBounds()
|
|
||||||
flickable.flick(0, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function urlExtension(url) {
|
|
||||||
return url.toString().split("/").slice(-1)[0].split("?")[0].split(".")
|
|
||||||
.slice(-1)[0].toLowerCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function sendFile(userId, roomId, path, onSuccess, onError) {
|
|
||||||
py.callClientCoro(userId, "send_file", [roomId, path], onSuccess, onError)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user