Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
Konheko
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Nheko Reborn
Konheko
Commits
b684bb5c
Commit
b684bb5c
authored
4 years ago
by
Nicolas Werner
Browse files
Options
Downloads
Patches
Plain Diff
Rough image sending
parent
c6c128e9
No related branches found
No related tags found
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
qml/pages/ChatPage.qml
+197
-150
197 additions, 150 deletions
qml/pages/ChatPage.qml
src/models/roommodel.cpp
+46
-0
46 additions, 0 deletions
src/models/roommodel.cpp
src/models/roommodel.h
+3
-0
3 additions, 0 deletions
src/models/roommodel.h
with
246 additions
and
150 deletions
qml/pages/ChatPage.qml
+
197
−
150
View file @
b684bb5c
...
...
@@ -2,6 +2,7 @@ import QtQuick 2.0
import
QtQuick
.
Layouts
1.0
import
Sailfish
.
Silica
1.0
import
Sailfish
.
Silica
.
private
1.0
import
Sailfish
.
Pickers
1.0
import
dev
.
neko
.
spoon
1.0
Page
{
...
...
@@ -12,206 +13,252 @@ Page {
// The effective value will be restricted by ApplicationWindow.allowedOrientations
allowedOrientations
:
Orientation
.
All
Column
{
SilicaFlickable
{
anchors.fill
:
parent
PageHeader
{
id
:
header
Column
{
anchors.fill
:
parent
title
:
room
.
roomName
()
PageHeader
{
id
:
header
anchors
{
left
:
parent
.
left
right
:
parent
.
right
}
title
:
room
.
roomName
()
Connection
s
{
target
:
room
onRoomNameChanged
:
header
.
title
=
name
}
anchor
s
{
left
:
parent
.
left
right
:
parent
.
right
}
}
Connections
{
target
:
room
onRoomNameChanged
:
header
.
title
=
name
}
SilicaListView
{
id
:
chatView
property
bool
atEnd
:
false
property
int
maxIndex
:
-
1
height
:
parent
.
height
-
bot
.
height
-
header
.
height
contentHeight
:
height
cacheBuffer
:
1000
clip
:
true
boundsBehavior
:
Flickable
.
DragOverBounds
anchors.left
:
parent
.
left
anchors.right
:
parent
.
right
ListView.onAdd
:
{
console
.
log
(
"
atYEnd::
"
+
atYEnd
);
atEnd
=
atYEnd
;
}
onCountChanged
:
{
console
.
log
(
"
atEnd::
"
+
atEnd
);
if
(
atEnd
)
{
scrollToBottom
();
currentIndex
=
count
-
1
;
SilicaListView
{
id
:
chatView
property
bool
atEnd
:
false
property
int
maxIndex
:
-
1
height
:
parent
.
height
-
bot
.
height
-
header
.
height
contentHeight
:
height
cacheBuffer
:
1000
clip
:
true
boundsBehavior
:
Flickable
.
StopAtBounds
anchors.left
:
parent
.
left
anchors.right
:
parent
.
right
ListView.onAdd
:
{
console
.
log
(
"
atYEnd::
"
+
atYEnd
);
atEnd
=
atYEnd
;
}
}
onMovementEnded
:
{
atEnd
=
false
;
console
.
log
(
"
movement ended
"
);
if
(
chatView
.
atYBeginning
)
{
console
.
log
(
"
load older messages
"
);
room
.
fetchOlderMessages
();
onCountChanged
:
{
console
.
log
(
"
atEnd::
"
+
atEnd
);
if
(
atEnd
)
{
scrollToBottom
();
currentIndex
=
count
-
1
;
}
}
if
(
chatView
.
atYEnd
)
{
console
.
log
(
"
at end
"
);
atEnd
=
true
;
}
else
{
console
.
log
(
"
not at end
"
);
onMovementEnded
:
{
atEnd
=
false
;
}
var
lastIndex
=
maxIndex
;
for
(
var
child
in
chatView
.
contentItem
.
children
)
{
if
(
chatView
.
contentItem
.
children
[
child
].
isFullyVisible
&&
chatView
.
contentItem
.
children
[
child
].
index
>
maxIndex
)
console
.
log
(
"
movement ended
"
);
if
(
chatView
.
atYBeginning
)
{
console
.
log
(
"
load older messages
"
);
room
.
fetchOlderMessages
();
}
if
(
chatView
.
atYEnd
)
{
console
.
log
(
"
at end
"
);
atEnd
=
true
;
}
else
{
console
.
log
(
"
not at end
"
);
atEnd
=
false
;
}
var
lastIndex
=
maxIndex
;
for
(
var
child
in
chatView
.
contentItem
.
children
)
{
if
(
chatView
.
contentItem
.
children
[
child
].
isFullyVisible
&&
chatView
.
contentItem
.
children
[
child
].
index
>
maxIndex
)
lastIndex
=
chatView
.
contentItem
.
children
[
child
].
index
;
}
if
(
lastIndex
>
maxIndex
)
}
if
(
lastIndex
>
maxIndex
)
maxIndex
=
lastIndex
;
}
onMaxIndexChanged
:
{
console
.
log
(
"
max index:
"
+
maxIndex
);
room
.
markRead
(
maxIndex
);
}
Component.onCompleted
:
{
maxIndex
=
room
.
lastRead
();
if
(
maxIndex
=
count
-
1
)
}
onMaxIndexChanged
:
{
console
.
log
(
"
max index:
"
+
maxIndex
);
room
.
markRead
(
maxIndex
);
}
Component.onCompleted
:
{
maxIndex
=
room
.
lastRead
();
if
(
maxIndex
=
count
-
1
)
atEnd
=
true
;
positionViewAtIndex
(
maxIndex
,
ListView
.
Contain
);
}
positionViewAtIndex
(
maxIndex
,
ListView
.
Contain
);
}
ScrollDecorator
{
flickable
:
chatView
}
ScrollDecorator
{
flickable
:
chatView
}
delegate
:
ListItem
{
id
:
messageItem
contentHeight
:
content
.
height
+
2
*
Theme
.
paddingMedium
width
:
chatView
.
width
BubbleBackground
{
id
:
bubble
property
int
fullMessageWidth
:
parent
.
width
-
2
*
Theme
.
paddingLarge
property
int
yoff
:
Math
.
round
(
messageItem
.
y
-
chatView
.
contentY
-
1
)
property
bool
isFullyVisible
:
yoff
+
messageItem
.
height
<
chatView
.
height
property
int
index
:
model
.
index
color
:
Theme
.
rgba
(
Theme
.
primaryColor
,
Theme
.
opacityFaint
)
opacity
:
model
.
IsSender
?
Theme
.
opacityFaint
:
Theme
.
opacityHigh
radius
:
Theme
.
paddingLarge
height
:
content
.
implicitHeight
+
2
*
Theme
.
paddingSmall
width
:
content
.
implicitWidth
+
2
*
Theme
.
paddingSmall
roundedCorners
:
BubbleBackground
.
NoCorners
|
(
model
.
IsSender
?
BubbleBackground
.
BottomLeft
:
BubbleBackground
.
BottomRight
)
anchors
{
topMargin
:
Theme
.
paddingSmall
bottomMargin
:
Theme
.
paddingSmall
leftMargin
:
Theme
.
paddingMedium
rightMargin
:
Theme
.
paddingMedium
right
:
model
.
IsSender
?
parent
.
right
:
undefined
left
:
!
model
.
IsSender
?
parent
.
left
:
undefined
top
:
parent
.
top
bottom
:
parent
.
bottom
}
Behavior
on
width
{
NumberAnimation
{
duration
:
100
easing.type
:
Easing
.
InOutQuad
}
}
delegate
:
ListItem
{
id
:
messageItem
contentHeight
:
content
.
height
+
2
*
Theme
.
paddingMedium
width
:
chatView
.
width
BubbleBackground
{
id
:
bubble
property
int
fullMessageWidth
:
parent
.
width
-
2
*
Theme
.
paddingLarge
property
int
yoff
:
Math
.
round
(
messageItem
.
y
-
chatView
.
contentY
-
1
)
property
bool
isFullyVisible
:
yoff
+
messageItem
.
height
<
chatView
.
height
property
int
index
:
model
.
index
color
:
Theme
.
rgba
(
Theme
.
primaryColor
,
Theme
.
opacityFaint
)
opacity
:
model
.
IsSender
?
Theme
.
opacityFaint
:
Theme
.
opacityHigh
radius
:
Theme
.
paddingLarge
height
:
content
.
implicitHeight
+
2
*
Theme
.
paddingSmall
width
:
content
.
implicitWidth
+
2
*
Theme
.
paddingSmall
roundedCorners
:
BubbleBackground
.
NoCorners
|
(
model
.
IsSender
?
BubbleBackground
.
BottomLeft
:
BubbleBackground
.
BottomRight
)
anchors
{
topMargin
:
Theme
.
paddingSmall
bottomMargin
:
Theme
.
paddingSmall
leftMargin
:
Theme
.
paddingMedium
rightMargin
:
Theme
.
paddingMedium
right
:
model
.
IsSender
?
parent
.
right
:
undefined
left
:
!
model
.
IsSender
?
parent
.
left
:
undefined
top
:
parent
.
top
bottom
:
parent
.
bottom
}
Behavior
on
width
{
NumberAnimation
{
duration
:
100
easing.type
:
Easing
.
InOutQuad
ColumnLayout
{
id
:
content
anchors
{
margins
:
Theme
.
paddingSmall
right
:
model
.
IsSender
?
bubble
.
right
:
undefined
left
:
!
model
.
IsSender
?
bubble
.
left
:
undefined
top
:
bubble
.
top
bottom
:
bubble
.
bottom
}
Text
{
text
:
room
.
userIdToUserName
(
model
.
UserId
)
color
:
room
.
userColor
(
model
.
UserId
,
"
#00000000
"
)
Layout.alignment
:
model
.
IsSender
?
Qt
.
AlignRight
:
Qt
.
AlignLeft
horizontalAlignment
:
Text
.
AlignRight
}
Message
{
id
:
loader
Layout.alignment
:
model
.
IsSender
?
Qt
.
AlignRight
:
Qt
.
AlignLeft
modelData
:
model
opacity
:
1
}
Text
{
id
:
ts
Layout.alignment
:
model
.
IsSender
?
Qt
.
AlignRight
:
Qt
.
AlignLeft
text
:
model
.
Timestamp
.
toLocaleString
()
color
:
Theme
.
highlightColor
font.pixelSize
:
Theme
.
fontSizeTiny
horizontalAlignment
:
Text
.
AlignRight
}
}
}
ColumnLayout
{
id
:
content
}
anchors
{
margins
:
Theme
.
paddingSmall
right
:
model
.
IsSender
?
bubble
.
right
:
undefined
left
:
!
model
.
IsSender
?
bubble
.
left
:
undefined
top
:
bubble
.
top
bottom
:
bubble
.
bottom
}
Row
{
id
:
bot
Text
{
text
:
room
.
userIdToUserName
(
model
.
UserId
)
color
:
room
.
userColor
(
model
.
UserId
,
"
#00000000
"
)
Layout.alignment
:
model
.
IsSender
?
Qt
.
AlignRight
:
Qt
.
AlignLeft
horizontalAlignment
:
Text
.
AlignRight
}
anchors.left
:
parent
.
left
anchors.right
:
parent
.
right
height
:
messageText
.
height
Message
{
id
:
loader
TextArea
{
id
:
messageText
Layout.alignment
:
model
.
IsSender
?
Qt
.
AlignRight
:
Qt
.
AlignLeft
modelData
:
model
opacity
:
1
}
anchors.bottom
:
parent
.
bottom
placeholderText
:
qsTr
(
"
Enter your message
"
)
label
:
qsTr
(
"
Matrix
"
)
labelVisible
:
true
wrapMode
:
Text
.
Wrap
width
:
parent
.
width
-
sendButton
.
width
}
Text
{
id
:
ts
IconButton
{
id
:
sendButton
Layout.alignment
:
model
.
IsSender
?
Qt
.
AlignRight
:
Qt
.
AlignLeft
text
:
model
.
Timestamp
.
toLocaleString
()
color
:
Theme
.
highlightColor
font.pixelSize
:
Theme
.
fontSizeTiny
horizontalAlignment
:
Text
.
AlignRight
}
anchors.top
:
parent
.
top
icon.source
:
"
image://theme/icon-m-send
"
onClicked
:
{
if
(
messageText
.
text
!=
""
)
room
.
sendTextMessage
(
messageText
.
text
);
messageText
.
text
=
""
;
}
}
}
}
Row
{
id
:
bot
PushUpMenu
{
MenuItem
{
text
:
qsTr
(
"
Send image
"
)
onClicked
:
pageStack
.
push
(
multiImagePickerDialog
)
}
anchors.left
:
parent
.
left
anchors.right
:
parent
.
right
height
:
messageText
.
height
MenuItem
{
text
:
qsTr
(
"
Send video
"
)
onClicked
:
console
.
log
(
"
Clicked option 1
"
)
}
TextArea
{
id
:
messageText
MenuItem
{
text
:
qsTr
(
"
Send audio
"
)
onClicked
:
console
.
log
(
"
Clicked option 1
"
)
}
anchors.bottom
:
parent
.
bottom
placeholderText
:
qsTr
(
"
Enter your message
"
)
label
:
qsTr
(
"
Matrix
"
)
labelVisible
:
true
wrapMode
:
Text
.
Wrap
width
:
parent
.
width
-
sendButton
.
width
MenuItem
{
text
:
qsTr
(
"
Send file
"
)
onClicked
:
console
.
log
(
"
Clicked option 1
"
)
}
IconButton
{
id
:
sendButton
}
anchors.top
:
parent
.
top
icon.source
:
"
image://theme/icon-m-send
"
onClicked
:
{
if
(
messageText
.
text
!=
""
)
room
.
sendTextMessage
(
messageText
.
text
);
}
messageText
.
text
=
""
;
Component
{
id
:
multiImagePickerDialog
MultiImagePickerDialog
{
onAccepted
:
{
var
urls
=
[]
var
mimetypes
=
[]
for
(
var
i
=
0
;
i
<
selectedContent
.
count
;
++
i
)
{
urls
.
push
(
selectedContent
.
get
(
i
).
url
)
mimetypes
.
push
(
selectedContent
.
get
(
i
).
mimeType
)
}
room
.
sendImages
(
urls
,
mimetypes
)
}
onRejected
:
selectedFiles
=
""
}
}
}
This diff is collapsed.
Click to expand it.
src/models/roommodel.cpp
+
46
−
0
View file @
b684bb5c
...
...
@@ -2,6 +2,8 @@
#include
<QGuiApplication>
#include
<QFile>
#include
<QImageReader>
#include
<QMetaType>
#include
<QSharedPointer>
#include
<QtMath>
...
...
@@ -418,6 +420,50 @@ void Room::sendTextMessage(QString message) {
});
}
void
Room
::
sendImages
(
QList
<
QUrl
>
images
,
QList
<
QString
>
mimetypes
)
{
for
(
int
i
=
0
;
i
<
images
.
size
();
i
++
)
{
auto
file
=
QFile
(
images
.
at
(
i
).
toLocalFile
());
if
(
!
file
.
open
(
QIODevice
::
ReadOnly
))
{
qCritical
()
<<
"Failed to open image"
<<
images
.
at
(
i
).
fileName
();
return
;
}
auto
temp
=
file
.
readAll
();
std
::
string
data
(
temp
.
data
(),
temp
.
size
());
auto
size
=
QImageReader
(
images
.
at
(
i
).
toLocalFile
()).
size
();
qDebug
()
<<
"Sending image "
<<
images
.
at
(
i
).
fileName
()
<<
" size "
<<
size
;
http
::
client
().
upload
(
data
,
mimetypes
.
at
(
i
).
toStdString
(),
images
.
at
(
i
).
fileName
().
toStdString
(),
[
this
,
size
,
filesize
=
temp
.
size
(),
filename
=
images
.
at
(
i
).
fileName
().
toStdString
()](
const
mtx
::
responses
::
ContentURI
&
uri
,
mtx
::
http
::
RequestErr
err
)
{
if
(
err
)
{
qDebug
()
<<
"failed to upload image: "
<<
*
err
;
return
;
}
mtx
::
events
::
msg
::
Image
img
=
{};
img
.
msgtype
=
"m.image"
;
img
.
body
=
filename
;
img
.
url
=
uri
.
content_uri
;
img
.
info
.
h
=
size
.
height
();
img
.
info
.
w
=
size
.
width
();
http
::
client
().
send_room_message
<
msg
::
Image
,
mtx
::
events
::
EventType
::
RoomMessage
>
(
this
->
id
,
http
::
client
().
generate_txn_id
(),
img
,
[
this
](
const
mtx
::
responses
::
EventId
&
res
,
mtx
::
http
::
RequestErr
err
)
{
if
(
err
)
{
qDebug
()
<<
"failed to send message: "
<<
*
err
;
return
;
}
qDebug
()
<<
"send event as: $"
<<
QString
::
fromStdString
(
res
.
event_id
.
to_string
());
});
});
}
}
QHash
<
int
,
QByteArray
>
Room
::
roleNames
()
const
{
QHash
<
int
,
QByteArray
>
roles
;
roles
[
Type
]
=
"Type"
;
...
...
This diff is collapsed.
Click to expand it.
src/models/roommodel.h
+
3
−
0
View file @
b684bb5c
...
...
@@ -10,6 +10,8 @@
#include
<QColor>
#include
<QDateTime>
#include
<QString>
#include
<QUrl>
#include
<QVector>
#include
<mtx/events/collections.hpp>
...
...
@@ -168,6 +170,7 @@ struct Room : public QAbstractListModel {
void
setUnread
(
int
unread
)
{
this
->
unreadNotifications
=
unread
;
}
void
fetchOlderMessages
();
void
sendTextMessage
(
QString
message
);
void
sendImages
(
QList
<
QUrl
>
images
,
QList
<
QString
>
mimetypes
);
void
markRead
(
int
index
);
int
lastRead
();
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment