Compare commits

..

8 Commits

Author SHA1 Message Date
expectocode dfdad76d97 merge upstream 2019-05-19 19:03:37 +01:00
Black Hat 6bf7e7e0c9 Fix imageclipboard saveImage(). 2019-05-19 22:35:08 +08:00
Black Hat 603cb33042 New attachment mechanism. Also add image from clipboard. 2019-05-19 21:58:54 +08:00
Black Hat ae5154fd35 Fix link color in reply delegate. 2019-05-18 16:32:36 +08:00
Black Hat 5fd5d0d9ea Sort room list based on notification count and highlight count. 2019-05-18 16:27:10 +08:00
Black Hat b4281896ba Add drag and drop support. 2019-05-17 19:46:59 +08:00
Black Hat 75c5c71855 Make reply delegate's border round. 2019-05-17 19:29:41 +08:00
Black Hat b7d8f70a5f More changes in MessageDelegate. 2019-05-17 15:01:01 +08:00
30 changed files with 906 additions and 190 deletions

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/spectral.iml" filepath="$PROJECT_DIR$/.idea/spectral.iml" />
</modules>
</component>
</project>

8
.idea/spectral.iml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/vcs.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/include/SortFilterProxyModel" vcs="Git" />
<mapping directory="$PROJECT_DIR$/include/libqmatrixclient" vcs="Git" />
</component>
</project>

153
.idea/workspace.xml Normal file
View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeRunConfigurationManager" shouldGenerate="true" shouldDeleteObsolete="true">
<generated />
</component>
<component name="CMakeSettings">
<configurations>
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
</configurations>
</component>
<component name="ChangeListManager">
<list default="true" id="1dc08437-54b4-4b19-9a54-6472ca11c95f" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/include/libqmatrixclient" beforeDir="false" afterPath="$PROJECT_DIR$/include/libqmatrixclient" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="FileEditorManager">
<leaf>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/spectraluser.cpp">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="75">
<caret line="5" lean-forward="true" selection-start-line="5" selection-end-line="5" />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/src/spectraluser.cpp" />
</list>
</option>
</component>
<component name="MacroExpansionManager">
<option name="directoryName" value="KjZzvpQi" />
</component>
<component name="ProjectConfigurationFiles">
<option name="files">
<list>
<option value="$PROJECT_DIR$/.idea/spectral.iml" />
<option value="$PROJECT_DIR$/.idea/vcs.xml" />
<option value="$PROJECT_DIR$/.idea/modules.xml" />
</list>
</option>
</component>
<component name="ProjectFrameBounds">
<option name="x" value="1928" />
<option name="y" value="8" />
<option name="width" value="539" />
<option name="height" value="687" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="spectral" type="b2602c69:ProjectViewProjectNode" />
<item name="spectral" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="spectral" type="b2602c69:ProjectViewProjectNode" />
<item name="spectral" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="1dc08437-54b4-4b19-9a54-6472ca11c95f" name="Default Changelist" comment="" />
<created>1558287890866</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1558287890866</updated>
<workItem from="1558287893419" duration="176000" />
</task>
<servers />
</component>
<component name="TimeTrackingManager">
<option name="totallyTimeSpent" value="176000" />
</component>
<component name="ToolWindowManager">
<frame x="1928" y="8" width="539" height="687" extended-state="0" />
<layout>
<window_info id="Favorites" side_tool="true" />
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.6136821" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info anchor="bottom" id="Database Changes" />
<window_info anchor="bottom" id="Version Control" />
<window_info anchor="bottom" id="Terminal" />
<window_info anchor="bottom" id="Event Log" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info anchor="bottom" id="Run" order="2" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="right" id="Cargo" />
<window_info anchor="right" id="Database" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
</layout>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/src/spectraluser.cpp">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="75">
<caret line="5" lean-forward="true" selection-start-line="5" selection-end-line="5" />
</state>
</provider>
</entry>
</component>
</project>

View File

@ -0,0 +1,71 @@
import QtQuick 2.12
Rectangle {
property alias topLeftRadius: topLeftRect.radius
property alias topRightRadius: topRightRect.radius
property alias bottomLeftRadius: bottomLeftRect.radius
property alias bottomRightRadius: bottomRightRect.radius
property alias topLeftVisible: topLeftRect.visible
property alias topRightVisible: topRightRect.visible
property alias bottomLeftVisible: bottomLeftRect.visible
property alias bottomRightVisible: bottomRightRect.visible
antialiasing: true
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
width: parent.width / 2
height: parent.height / 2
id: topLeftRect
antialiasing: true
color: parent.color
}
Rectangle {
anchors.top: parent.top
anchors.right: parent.right
width: parent.width / 2
height: parent.height / 2
id: topRightRect
antialiasing: true
color: parent.color
}
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
width: parent.width / 2
height: parent.height / 2
id: bottomLeftRect
antialiasing: true
color: parent.color
}
Rectangle {
anchors.bottom: parent.bottom
anchors.right: parent.right
width: parent.width / 2
height: parent.height / 2
id: bottomRightRect
antialiasing: true
color: parent.color
}
}

View File

@ -2,6 +2,8 @@ import QtQuick 2.12
import QtQuick.Controls 2.12 import QtQuick.Controls 2.12
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
import Spectral.Setting 0.1
Item { Item {
property string hint: "H" property string hint: "H"
property string source: "" property string source: ""
@ -35,7 +37,7 @@ Item {
visible: !realSource || image.status != Image.Ready visible: !realSource || image.status != Image.Ready
radius: height / 2 radius: height / 2
color: stringToColor(hint) color: MPalette.accent
antialiasing: true antialiasing: true
Label { Label {
@ -50,16 +52,16 @@ Item {
} }
} }
function stringToColor(str) { // function stringToColor(str) {
var hash = 0; // var hash = 0;
for (var i = 0; i < str.length; i++) { // for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash); // hash = str.charCodeAt(i) + ((hash << 5) - hash);
} // }
var colour = '#'; // var colour = '#';
for (var j = 0; j < 3; j++) { // for (var j = 0; j < 3; j++) {
var value = (hash >> (j * 8)) & 0xFF; // var value = (hash >> (j * 8)) & 0xFF;
colour += ('00' + value.toString(16)).substr(-2); // colour += ('00' + value.toString(16)).substr(-2);
} // }
return colour; // return colour;
} // }
} }

View File

@ -67,65 +67,25 @@ ColumnLayout {
Control { Control {
Layout.maximumWidth: messageListView.width - (!sentByMe ? 36 + messageRow.spacing : 0) - 48 Layout.maximumWidth: messageListView.width - (!sentByMe ? 36 + messageRow.spacing : 0) - 48
verticalPadding: 8 padding: 0
horizontalPadding: 16
background: AutoRectangle {
readonly property int minorRadius: 2
id: bubbleBackground
background: Rectangle {
color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
radius: 18 radius: 18
antialiasing: true
Rectangle { topLeftVisible: true
anchors.top: parent.top topRightVisible: true
anchors.left: parent.left bottomLeftVisible: true
bottomRightVisible: true
width: parent.width / 2 topLeftRadius: minorRadius
height: parent.height / 2 topRightRadius: minorRadius
bottomLeftRadius: minorRadius
visible: true bottomRightRadius: minorRadius
color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
radius: 5
}
Rectangle {
anchors.top: parent.top
anchors.right: parent.right
width: parent.width / 2
height: parent.height / 2
visible: true
color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
radius: 5
}
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
width: parent.width / 2
height: parent.height / 2
visible: true
color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
radius: 5
}
Rectangle {
anchors.bottom: parent.bottom
anchors.right: parent.right
width: parent.width / 2
height: parent.height / 2
visible: true
color: sentByMe ? MPalette.background : eventType === "notice" ? MPalette.primary : MPalette.accent
radius: 5
}
AutoMouseArea { AutoMouseArea {
anchors.fill: parent anchors.fill: parent
@ -166,63 +126,72 @@ ColumnLayout {
} }
contentItem: ColumnLayout { contentItem: ColumnLayout {
RowLayout { spacing: 0
Control {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 8
Layout.rightMargin: 8
padding: 4
rightPadding: 12
visible: replyVisible visible: replyVisible
Avatar { contentItem: RowLayout {
Layout.preferredWidth: 28 Avatar {
Layout.preferredHeight: 28 Layout.preferredWidth: 28
Layout.alignment: Qt.AlignTop Layout.preferredHeight: 28
Layout.alignment: Qt.AlignTop
source: replyVisible ? replyAuthor.avatarMediaId : "" source: replyVisible ? replyAuthor.avatarMediaId : ""
hint: replyVisible ? replyAuthor.displayName : "H" hint: replyVisible ? replyAuthor.displayName : "H"
RippleEffect { RippleEffect {
anchors.fill: parent anchors.fill: parent
circular: true circular: true
onClicked: userDetailDialog.createObject(ApplicationWindow.overlay, {"room": currentRoom, "user": replyAuthor}).open() onClicked: userDetailDialog.createObject(ApplicationWindow.overlay, {"room": currentRoom, "user": replyAuthor}).open()
} }
}
Control {
Layout.fillWidth: true
visible: replyVisible
padding: 0
background: RippleEffect {
onClicked: goToEvent(replyEventId)
} }
contentItem: Label { Label {
color: darkBackground ? "white" : MPalette.lighter Layout.fillWidth: true
text: "<style>a{color: " + (darkBackground ? "white" : MPalette.foreground) + ";} .user-pill{}</style>" + (replyDisplay || "")
color: !sentByMe ? MPalette.foreground : "white"
text: "<style>a{color: " + color + ";} .user-pill{}</style>" + (replyDisplay || "")
wrapMode: Label.Wrap wrapMode: Label.Wrap
textFormat: Label.RichText textFormat: Label.RichText
} }
} }
}
Rectangle { background: Rectangle {
Layout.fillWidth: true color: sentByMe ? MPalette.accent : MPalette.background
Layout.preferredHeight: 1 radius: 18
visible: replyVisible AutoMouseArea {
color: darkBackground ? "white" : MPalette.lighter anchors.fill: parent
onClicked: goToEvent(replyEventId)
}
}
} }
TextEdit { TextEdit {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
Layout.bottomMargin: 8
id: contentLabel id: contentLabel
text: "<style>a{color: " + (darkBackground ? "white" : MPalette.foreground) + ";} .user-pill{}</style>" + display text: "<style>a{color: " + color + ";} .user-pill{}</style>" + display
color: darkBackground ? "white" : MPalette.foreground color: darkBackground ? "white" : MPalette.foreground

View File

@ -7,3 +7,4 @@ AutoListView 2.0 AutoListView.qml
AutoTextField 2.0 AutoTextField.qml AutoTextField 2.0 AutoTextField.qml
Avatar 2.0 Avatar.qml Avatar 2.0 Avatar.qml
FullScreenImage 2.0 FullScreenImage.qml FullScreenImage 2.0 FullScreenImage.qml
AutoRectangle 2.0 AutoRectangle.qml

View File

@ -27,8 +27,8 @@ Dialog {
Layout.preferredWidth: 72 Layout.preferredWidth: 72
Layout.preferredHeight: 72 Layout.preferredHeight: 72
hint: user ? user.displayName : "No name" hint: user.displayName
source: user ? user.avatarMediaId : null source: user.avatarMediaId
RippleEffect { RippleEffect {
anchors.fill: parent anchors.fill: parent
@ -50,10 +50,19 @@ Dialog {
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.NoWrap wrapMode: Text.NoWrap
text: user ? user.displayName : "No Name" text: user.displayName
color: MPalette.foreground color: MPalette.foreground
} }
Label {
Layout.fillWidth: true
visible: user.bridgeName
text: user.bridgeName
color: MPalette.lighter
}
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
@ -89,7 +98,7 @@ Dialog {
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.NoWrap wrapMode: Text.NoWrap
text: user ? user.id : "No ID" text: user.id
color: MPalette.accent color: MPalette.accent
} }

View File

@ -11,3 +11,4 @@ FontFamilyDialog 2.0 FontFamilyDialog.qml
AccountDetailDialog 2.0 AccountDetailDialog.qml AccountDetailDialog 2.0 AccountDetailDialog.qml
OpenFileDialog 2.0 OpenFileDialog.qml OpenFileDialog 2.0 OpenFileDialog.qml
OpenFolderDialog 2.0 OpenFolderDialog.qml OpenFolderDialog 2.0 OpenFolderDialog.qml
ImageClipboardDialog 2.0 ImageClipboardDialog.qml

View File

@ -31,7 +31,7 @@ Item {
connection: root.connection connection: root.connection
onNewMessage: if (!window.active && MSettings.showNotification) spectralController.postNotification(roomId, eventId, roomName, senderName, text, icon) onNewMessage: if (!window.active && MSettings.showNotification) notificationsManager.postNotification(roomId, eventId, roomName, senderName, text, icon)
} }
SortFilterProxyModel { SortFilterProxyModel {
@ -54,6 +54,16 @@ Item {
sorters: [ sorters: [
RoleSorter { roleName: "category" }, RoleSorter { roleName: "category" },
ExpressionSorter {
expression: {
return modelLeft.highlightCount > 0;
}
},
ExpressionSorter {
expression: {
return modelLeft.notificationCount > 0;
}
},
RoleSorter { RoleSorter {
roleName: "lastActiveTime" roleName: "lastActiveTime"
sortOrder: Qt.DescendingOrder sortOrder: Qt.DescendingOrder

View File

@ -3,10 +3,12 @@ import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import QtQuick.Controls.Material 2.12 import QtQuick.Controls.Material 2.12
import Qt.labs.qmlmodels 1.0 import Qt.labs.qmlmodels 1.0
import Qt.labs.platform 1.0
import Spectral.Component 2.0 import Spectral.Component 2.0
import Spectral.Component.Emoji 2.0 import Spectral.Component.Emoji 2.0
import Spectral.Component.Timeline 2.0 import Spectral.Component.Timeline 2.0
import Spectral.Dialog 2.0
import Spectral.Effect 2.0 import Spectral.Effect 2.0
import Spectral 0.1 import Spectral 0.1
@ -23,6 +25,121 @@ Item {
room: currentRoom room: currentRoom
} }
DropArea {
anchors.fill: parent
enabled: currentRoom
onDropped: {
if (!drop.hasUrls) return
roomPanelInput.attach(drop.urls[0])
}
}
ImageClipboard {
id: imageClipboard
}
Popup {
anchors.centerIn: parent
id: attachDialog
padding: 16
contentItem: RowLayout {
Control {
Layout.preferredWidth: 160
Layout.fillHeight: true
padding: 16
contentItem: ColumnLayout {
spacing: 16
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
icon: "\ue2c8"
font.pixelSize: 64
color: MPalette.lighter
}
Label {
Layout.alignment: Qt.AlignHCenter
text: "Choose local file"
}
}
background: RippleEffect {
onClicked: {
attachDialog.close()
var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
fileDialog.chosen.connect(function(path) {
if (!path) return
roomPanelInput.attach(path)
})
fileDialog.open()
}
}
}
Rectangle {
Layout.preferredWidth: 1
Layout.fillHeight: true
color: MPalette.banner
}
Control {
Layout.preferredWidth: 160
Layout.fillHeight: true
padding: 16
contentItem: ColumnLayout {
spacing: 16
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
icon: "\ue410"
font.pixelSize: 64
color: MPalette.lighter
}
Label {
Layout.alignment: Qt.AlignHCenter
text: "Clipboard image"
color: MPalette.foreground
}
}
background: RippleEffect {
onClicked: {
var localPath = StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/screenshots/" + (new Date()).getTime() + ".png"
if (!imageClipboard.saveImage(localPath)) return
roomPanelInput.attach(localPath)
attachDialog.close()
}
}
}
}
}
Component {
id: openFileDialog
OpenFileDialog {}
}
Column { Column {
anchors.centerIn: parent anchors.centerIn: parent

View File

@ -5,6 +5,7 @@ import QtQuick.Controls.Material 2.12
import Spectral.Component 2.0 import Spectral.Component 2.0
import Spectral.Component.Emoji 2.0 import Spectral.Component.Emoji 2.0
import Spectral.Dialog 2.0
import Spectral.Effect 2.0 import Spectral.Effect 2.0
import Spectral.Setting 0.1 import Spectral.Setting 0.1
@ -21,6 +22,9 @@ Control {
property int autoCompleteBeginPosition property int autoCompleteBeginPosition
property int autoCompleteEndPosition property int autoCompleteEndPosition
property bool hasAttachment: false
property url attachmentPath
id: root id: root
padding: 0 padding: 0
@ -171,13 +175,13 @@ Control {
Layout.alignment: Qt.AlignBottom Layout.alignment: Qt.AlignBottom
id: uploadButton id: uploadButton
visible: !isReply visible: !isReply && !hasAttachment
contentItem: MaterialIcon { contentItem: MaterialIcon {
icon: "\ue226" icon: "\ue226"
} }
onClicked: currentRoom.chooseAndUploadFile() onClicked: attachDialog.open()
BusyIndicator { BusyIndicator {
anchors.fill: parent anchors.fill: parent
@ -202,6 +206,51 @@ Control {
onClicked: clearReply() onClicked: clearReply()
} }
Control {
Layout.margins: 6
Layout.preferredHeight: 36
Layout.alignment: Qt.AlignVCenter
visible: hasAttachment
rightPadding: 8
background: Rectangle {
color: MPalette.accent
radius: height / 2
antialiasing: true
}
contentItem: RowLayout {
spacing: 0
ToolButton {
Layout.preferredWidth: height
Layout.fillHeight: true
id: cancelAttachmentButton
contentItem: MaterialIcon {
icon: "\ue5cd"
color: "white"
font.pixelSize: 18
}
onClicked: {
hasAttachment = false
attachmentPath = ""
}
}
Label {
Layout.alignment: Qt.AlignVCenter
text: attachmentPath != "" ? attachmentPath.toString().substring(attachmentPath.toString().lastIndexOf('/') + 1, attachmentPath.length) : ""
color: "white"
}
}
}
TextArea { TextArea {
property real progress: 0 property real progress: 0
@ -252,7 +301,7 @@ Control {
Keys.onReturnPressed: { Keys.onReturnPressed: {
if (event.modifiers & Qt.ShiftModifier) { if (event.modifiers & Qt.ShiftModifier) {
insert(cursorPosition, "\n") insert(cursorPosition, "\n")
} else if (text) { } else {
postMessage(text) postMessage(text)
text = "" text = ""
closeAll() closeAll()
@ -304,9 +353,16 @@ Control {
} }
function postMessage(text) { function postMessage(text) {
if (text.trim().length === 0) { return }
if(!currentRoom) { return } if(!currentRoom) { return }
if (hasAttachment) {
currentRoom.uploadFile(attachmentPath, text)
clearAttachment()
return
}
if (text.trim().length === 0) { return }
var PREFIX_ME = '/me ' var PREFIX_ME = '/me '
var PREFIX_NOTICE = '/notice ' var PREFIX_NOTICE = '/notice '
var PREFIX_RAINBOW = '/rainbow ' var PREFIX_RAINBOW = '/rainbow '
@ -372,6 +428,10 @@ Control {
} }
} }
ImageClipboard {
id: imageClipboard
}
function insert(str) { function insert(str) {
inputField.insert(inputField.cursorPosition, str) inputField.insert(inputField.cursorPosition, str)
} }
@ -396,4 +456,14 @@ Control {
autoCompleteListView.visible = false autoCompleteListView.visible = false
emojiPicker.visible = false emojiPicker.visible = false
} }
function attach(localPath) {
hasAttachment = true
attachmentPath = localPath
}
function clearAttachment() {
hasAttachment = false
attachmentPath = ""
}
} }

@ -1 +1 @@
Subproject commit 52a81dfa8a5415be369d819837f445479b833cde Subproject commit d6f39dcb0de69322479f287514a8c36afcb3fe7b

View File

@ -56,12 +56,17 @@ ApplicationWindow {
quitOnLastWindowClosed: !MSettings.showTray quitOnLastWindowClosed: !MSettings.showTray
onErrorOccured: errorControl.show(error + ": " + detail, 3000)
}
NotificationsManager {
id: notificationsManager
onNotificationClicked: { onNotificationClicked: {
roomListForm.enteredRoom = spectralController.connection.room(roomId) roomListForm.enteredRoom = spectralController.connection.room(roomId)
roomForm.goToEvent(eventId) roomForm.goToEvent(eventId)
showWindow() showWindow()
} }
onErrorOccured: errorControl.show(error + ": " + detail, 3000)
} }
Shortcut { Shortcut {

View File

@ -58,5 +58,6 @@
<file>imports/Spectral/Dialog/OpenFileDialog.qml</file> <file>imports/Spectral/Dialog/OpenFileDialog.qml</file>
<file>imports/Spectral/Dialog/OpenFolderDialog.qml</file> <file>imports/Spectral/Dialog/OpenFolderDialog.qml</file>
<file>imports/Spectral/Component/Timeline/VideoDelegate.qml</file> <file>imports/Spectral/Component/Timeline/VideoDelegate.qml</file>
<file>imports/Spectral/Component/AutoRectangle.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -117,7 +117,6 @@ mac {
HEADERS += \ HEADERS += \
src/controller.h \ src/controller.h \
src/roomlistmodel.h \ src/roomlistmodel.h \
src/imageprovider.h \
src/messageeventmodel.h \ src/messageeventmodel.h \
src/emojimodel.h \ src/emojimodel.h \
src/spectralroom.h \ src/spectralroom.h \
@ -125,19 +124,22 @@ HEADERS += \
src/accountlistmodel.h \ src/accountlistmodel.h \
src/spectraluser.h \ src/spectraluser.h \
src/notifications/manager.h \ src/notifications/manager.h \
src/utils.h src/utils.h \
src/imageclipboard.h \
src/matriximageprovider.h
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/controller.cpp \ src/controller.cpp \
src/roomlistmodel.cpp \ src/roomlistmodel.cpp \
src/imageprovider.cpp \
src/messageeventmodel.cpp \ src/messageeventmodel.cpp \
src/emojimodel.cpp \ src/emojimodel.cpp \
src/spectralroom.cpp \ src/spectralroom.cpp \
src/userlistmodel.cpp \ src/userlistmodel.cpp \
src/accountlistmodel.cpp \ src/accountlistmodel.cpp \
src/spectraluser.cpp \ src/spectraluser.cpp \
src/utils.cpp src/utils.cpp \
src/imageclipboard.cpp \
src/matriximageprovider.cpp
unix:!mac { unix:!mac {
SOURCES += src/notifications/managerlinux.cpp SOURCES += src/notifications/managerlinux.cpp

223
spectral.pro.user Normal file
View File

@ -0,0 +1,223 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.9.0, 2019-05-19T18:51:54. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{7614c50b-52db-49bf-a585-31417a3c0f62}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Qt 5.12.1 (System)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Qt 5.12.1 (System)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{97a6fc6e-df2b-4272-9664-99a23fd81def}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/tanuj/code/spectral/build</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy Configuration</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">spectral</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/home/tanuj/code/spectral/spectral.pro</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/tanuj/code/spectral/build</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">21</value>
</data>
<data>
<variable>Version</variable>
<value type="int">21</value>
</data>
</qtcreator>

View File

@ -31,13 +31,9 @@
#include <QtNetwork/QAuthenticator> #include <QtNetwork/QAuthenticator>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
Controller::Controller(QObject* parent) Controller::Controller(QObject* parent) : QObject(parent) {
: QObject(parent), notificationsManager(this) {
QApplication::setQuitOnLastWindowClosed(false); QApplication::setQuitOnLastWindowClosed(false);
connect(&notificationsManager, &NotificationsManager::notificationClicked,
this, &Controller::notificationClicked);
Connection::setRoomType<SpectralRoom>(); Connection::setRoomType<SpectralRoom>();
Connection::setUserType<SpectralUser>(); Connection::setUserType<SpectralUser>();
@ -232,10 +228,6 @@ void Controller::createDirectChat(Connection* c, const QString& userID) {
}); });
} }
void Controller::copyToClipboard(const QString& text) {
m_clipboard->setText(text);
}
void Controller::playAudio(QUrl localFile) { void Controller::playAudio(QUrl localFile) {
QMediaPlayer* player = new QMediaPlayer; QMediaPlayer* player = new QMediaPlayer;
player->setMedia(localFile); player->setMedia(localFile);
@ -243,16 +235,6 @@ void Controller::playAudio(QUrl localFile) {
connect(player, &QMediaPlayer::stateChanged, [=] { player->deleteLater(); }); connect(player, &QMediaPlayer::stateChanged, [=] { player->deleteLater(); });
} }
void Controller::postNotification(const QString& roomId,
const QString& eventId,
const QString& roomName,
const QString& senderName,
const QString& text,
const QImage& icon) {
notificationsManager.postNotification(roomId, eventId, roomName, senderName,
text, icon);
}
int Controller::dpi() { int Controller::dpi() {
return SettingsGroup("Interface").value("dpi", 100).toInt(); return SettingsGroup("Interface").value("dpi", 100).toInt();
} }

View File

@ -67,8 +67,6 @@ class Controller : public QObject {
} }
private: private:
QClipboard* m_clipboard = QApplication::clipboard();
NotificationsManager notificationsManager;
QVector<Connection*> m_connections; QVector<Connection*> m_connections;
QPointer<Connection> m_connection; QPointer<Connection> m_connection;
@ -99,14 +97,7 @@ class Controller : public QObject {
void joinRoom(Connection* c, const QString& alias); void joinRoom(Connection* c, const QString& alias);
void createRoom(Connection* c, const QString& name, const QString& topic); void createRoom(Connection* c, const QString& name, const QString& topic);
void createDirectChat(Connection* c, const QString& userID); void createDirectChat(Connection* c, const QString& userID);
void copyToClipboard(const QString& text);
void playAudio(QUrl localFile); void playAudio(QUrl localFile);
void postNotification(const QString& roomId,
const QString& eventId,
const QString& roomName,
const QString& senderName,
const QString& text,
const QImage& icon);
}; };
#endif // CONTROLLER_H #endif // CONTROLLER_H

40
src/imageclipboard.cpp Normal file
View File

@ -0,0 +1,40 @@
#include "imageclipboard.h"
#include <QDir>
#include <QFileInfo>
#include <QGuiApplication>
#include <QUrl>
#include <QtDebug>
ImageClipboard::ImageClipboard(QObject* parent)
: QObject(parent), m_clipboard(QGuiApplication::clipboard()) {
connect(m_clipboard, &QClipboard::changed, this,
&ImageClipboard::imageChanged);
}
bool ImageClipboard::hasImage() {
return !image().isNull();
}
QImage ImageClipboard::image() {
return m_clipboard->image();
}
bool ImageClipboard::saveImage(const QUrl& localPath) {
if (!localPath.isLocalFile())
return false;
auto i = image();
if (i.isNull())
return false;
QString path = QFileInfo(localPath.toString()).absolutePath();
QDir dir;
if (!dir.exists(path))
dir.mkpath(path);
i.save(localPath.toLocalFile());
return true;
}

28
src/imageclipboard.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef IMAGECLIPBOARD_H
#define IMAGECLIPBOARD_H
#include <QClipboard>
#include <QImage>
#include <QObject>
class ImageClipboard : public QObject {
Q_OBJECT
Q_PROPERTY(bool hasImage READ hasImage NOTIFY imageChanged)
Q_PROPERTY(QImage image READ image NOTIFY imageChanged)
public:
explicit ImageClipboard(QObject* parent = nullptr);
bool hasImage();
QImage image();
Q_INVOKABLE bool saveImage(const QUrl& localPath);
private:
QClipboard* m_clipboard;
signals:
void imageChanged();
};
#endif // IMAGECLIPBOARD_H

View File

@ -8,8 +8,10 @@
#include "accountlistmodel.h" #include "accountlistmodel.h"
#include "controller.h" #include "controller.h"
#include "emojimodel.h" #include "emojimodel.h"
#include "imageprovider.h" #include "imageclipboard.h"
#include "matriximageprovider.h"
#include "messageeventmodel.h" #include "messageeventmodel.h"
#include "notifications/manager.h"
#include "room.h" #include "room.h"
#include "roomlistmodel.h" #include "roomlistmodel.h"
#include "spectralroom.h" #include "spectralroom.h"
@ -55,6 +57,9 @@ int main(int argc, char* argv[]) {
qmlRegisterType<UserListModel>("Spectral", 0, 1, "UserListModel"); qmlRegisterType<UserListModel>("Spectral", 0, 1, "UserListModel");
qmlRegisterType<MessageEventModel>("Spectral", 0, 1, "MessageEventModel"); qmlRegisterType<MessageEventModel>("Spectral", 0, 1, "MessageEventModel");
qmlRegisterType<EmojiModel>("Spectral", 0, 1, "EmojiModel"); qmlRegisterType<EmojiModel>("Spectral", 0, 1, "EmojiModel");
qmlRegisterType<NotificationsManager>("Spectral", 0, 1,
"NotificationsManager");
qmlRegisterType<ImageClipboard>("Spectral", 0, 1, "ImageClipboard");
qmlRegisterUncreatableType<RoomMessageEvent>("Spectral", 0, 1, qmlRegisterUncreatableType<RoomMessageEvent>("Spectral", 0, 1,
"RoomMessageEvent", "ENUM"); "RoomMessageEvent", "ENUM");
qmlRegisterUncreatableType<RoomType>("Spectral", 0, 1, "RoomType", "ENUM"); qmlRegisterUncreatableType<RoomType>("Spectral", 0, 1, "RoomType", "ENUM");
@ -72,9 +77,10 @@ int main(int argc, char* argv[]) {
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
engine.addImportPath("qrc:/imports"); engine.addImportPath("qrc:/imports");
ImageProvider* m_provider = new ImageProvider(); MatrixImageProvider* matrixImageProvider = new MatrixImageProvider();
engine.rootContext()->setContextProperty("imageProvider", m_provider); engine.rootContext()->setContextProperty("imageProvider",
engine.addImageProvider(QLatin1String("mxc"), m_provider); matrixImageProvider);
engine.addImageProvider(QLatin1String("mxc"), matrixImageProvider);
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
if (engine.rootObjects().isEmpty()) if (engine.rootObjects().isEmpty())

View File

@ -1,4 +1,4 @@
#include "imageprovider.h" #include "matriximageprovider.h"
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
@ -108,7 +108,7 @@ void ThumbnailResponse::cancel() {
Qt::QueuedConnection); Qt::QueuedConnection);
} }
QQuickImageResponse* ImageProvider::requestImageResponse( QQuickImageResponse* MatrixImageProvider::requestImageResponse(
const QString& id, const QString& id,
const QSize& requestedSize) { const QSize& requestedSize) {
return new ThumbnailResponse(m_connection.load(), id, requestedSize); return new ThumbnailResponse(m_connection.load(), id, requestedSize);

View File

@ -1,5 +1,5 @@
#ifndef IMAGEPROVIDER_H #ifndef MatrixImageProvider_H
#define IMAGEPROVIDER_H #define MatrixImageProvider_H
#pragma once #pragma once
#include <QtQuick/QQuickAsyncImageProvider> #include <QtQuick/QQuickAsyncImageProvider>
@ -43,12 +43,12 @@ class ThumbnailResponse : public QQuickImageResponse {
void cancel() override; void cancel() override;
}; };
class ImageProvider : public QObject, public QQuickAsyncImageProvider { class MatrixImageProvider : public QObject, public QQuickAsyncImageProvider {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QMatrixClient::Connection* connection READ connection WRITE Q_PROPERTY(QMatrixClient::Connection* connection READ connection WRITE
setConnection NOTIFY connectionChanged) setConnection NOTIFY connectionChanged)
public: public:
explicit ImageProvider() = default; explicit MatrixImageProvider() = default;
QQuickImageResponse* requestImageResponse( QQuickImageResponse* requestImageResponse(
const QString& id, const QString& id,
@ -67,4 +67,4 @@ class ImageProvider : public QObject, public QQuickAsyncImageProvider {
QAtomicPointer<QMatrixClient::Connection> m_connection; QAtomicPointer<QMatrixClient::Connection> m_connection;
}; };
#endif // IMAGEPROVIDER_H #endif // MatrixImageProvider_H

View File

@ -19,11 +19,7 @@ struct roomEventId {
class NotificationsManager : public QObject { class NotificationsManager : public QObject {
Q_OBJECT Q_OBJECT
public: public:
NotificationsManager(QObject *parent = nullptr); NotificationsManager(QObject* parent = nullptr);
void postNotification(const QString &roomId, const QString &eventId,
const QString &roomName, const QString &senderName,
const QString &text, const QImage &icon);
signals: signals:
void notificationClicked(const QString roomId, const QString eventId); void notificationClicked(const QString roomId, const QString eventId);
@ -31,7 +27,8 @@ class NotificationsManager : public QObject {
private: private:
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
QDBusInterface dbus; QDBusInterface dbus;
uint showNotification(const QString summary, const QString text, uint showNotification(const QString summary,
const QString text,
const QImage image); const QImage image);
#endif #endif
@ -43,9 +40,16 @@ class NotificationsManager : public QObject {
public slots: public slots:
void actionInvoked(uint id, QString action); void actionInvoked(uint id, QString action);
void notificationClosed(uint id, uint reason); void notificationClosed(uint id, uint reason);
void postNotification(const QString& roomId,
const QString& eventId,
const QString& roomName,
const QString& senderName,
const QString& text,
const QImage& icon);
}; };
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
QDBusArgument &operator<<(QDBusArgument &arg, const QImage &image); QDBusArgument& operator<<(QDBusArgument& arg, const QImage& image);
const QDBusArgument &operator>>(const QDBusArgument &arg, QImage &); const QDBusArgument& operator>>(const QDBusArgument& arg, QImage&);
#endif #endif

View File

@ -44,33 +44,33 @@ inline QSize getImageSize(const QUrl& imageUrl) {
return reader.size(); return reader.size();
} }
void SpectralRoom::chooseAndUploadFile() { void SpectralRoom::uploadFile(const QUrl& url, const QString& body) {
auto localFile = QFileDialog::getOpenFileUrl(Q_NULLPTR, tr("Save File as")); if (url.isEmpty())
if (!localFile.isEmpty()) { return;
QString txnID = postFile(localFile.fileName(), localFile, false);
setHasFileUploading(true); QString txnID = postFile(body.isEmpty() ? url.fileName() : body, url, false);
connect(this, &Room::fileTransferCompleted, setHasFileUploading(true);
[=](QString id, QUrl localFile, QUrl mxcUrl) { connect(this, &Room::fileTransferCompleted,
if (id == txnID) { [=](QString id, QUrl localFile, QUrl mxcUrl) {
setFileUploadingProgress(0); if (id == txnID) {
setHasFileUploading(false); setFileUploadingProgress(0);
} setHasFileUploading(false);
}); }
connect(this, &Room::fileTransferFailed, [=](QString id, QString error) { });
if (id == txnID) { connect(this, &Room::fileTransferFailed, [=](QString id, QString error) {
setFileUploadingProgress(0); if (id == txnID) {
setHasFileUploading(false); setFileUploadingProgress(0);
} setHasFileUploading(false);
}); }
connect( });
this, &Room::fileTransferProgress, connect(
[=](QString id, qint64 progress, qint64 total) { this, &Room::fileTransferProgress,
if (id == txnID) { [=](QString id, qint64 progress, qint64 total) {
qDebug() << "Progress:" << progress << total; if (id == txnID) {
setFileUploadingProgress(int(float(progress) / float(total) * 100)); qDebug() << "Progress:" << progress << total;
} setFileUploadingProgress(int(float(progress) / float(total) * 100));
}); }
} });
} }
void SpectralRoom::acceptInvitation() { void SpectralRoom::acceptInvitation() {

View File

@ -254,7 +254,7 @@ class SpectralRoom : public Room {
void fileUploadingProgressChanged(); void fileUploadingProgressChanged();
public slots: public slots:
void chooseAndUploadFile(); void uploadFile(const QUrl& url, const QString& body = "");
void acceptInvitation(); void acceptInvitation();
void forget(); void forget();
void sendTypingNotification(bool isTyping); void sendTypingNotification(bool isTyping);

View File

@ -1 +1,5 @@
#include "spectraluser.h" #include "spectraluser.h"
QColor SpectralUser::color() {
return QColor::fromHslF(hueF(), 0.7, 0.5, 1);
}

View File

@ -10,9 +10,12 @@ using namespace QMatrixClient;
class SpectralUser : public User { class SpectralUser : public User {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QColor color READ color CONSTANT)
public: public:
SpectralUser(QString userId, Connection* connection) SpectralUser(QString userId, Connection* connection)
: User(userId, connection) {} : User(userId, connection) {}
QColor color();
}; };
#endif // SpectralUser_H #endif // SpectralUser_H