Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linphone-desktop
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
linphone-desktop
Commits
2b5cb6bc
Commit
2b5cb6bc
authored
Feb 09, 2017
by
Ronan Abhamon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(src/components/notifier/Notifier): supports messages & calls notifications
parent
40ee4028
Changes
36
Show whitespace changes
Inline
Side-by-side
Showing
36 changed files
with
616 additions
and
129 deletions
+616
-129
file_sign.svg
linphone-desktop/assets/images/file_sign.svg
+13
-0
message_sign.svg
linphone-desktop/assets/images/message_sign.svg
+13
-0
resources.qrc
linphone-desktop/resources.qrc
+9
-3
App.cpp
linphone-desktop/src/app/App.cpp
+11
-8
App.hpp
linphone-desktop/src/app/App.hpp
+1
-0
CallModel.cpp
linphone-desktop/src/components/call/CallModel.cpp
+5
-8
CallModel.hpp
linphone-desktop/src/components/call/CallModel.hpp
+0
-1
CallsListModel.cpp
linphone-desktop/src/components/calls/CallsListModel.cpp
+10
-0
CallsListModel.hpp
linphone-desktop/src/components/calls/CallsListModel.hpp
+2
-0
MSFunctions.cpp
linphone-desktop/src/components/camera/MSFunctions.cpp
+23
-3
MSFunctions.hpp
linphone-desktop/src/components/camera/MSFunctions.hpp
+22
-0
ChatModel.cpp
linphone-desktop/src/components/chat/ChatModel.cpp
+3
-0
CoreHandlers.cpp
linphone-desktop/src/components/core/CoreHandlers.cpp
+5
-2
CoreManager.cpp
linphone-desktop/src/components/core/CoreManager.cpp
+1
-0
CoreManager.hpp
linphone-desktop/src/components/core/CoreManager.hpp
+6
-0
Notifier.cpp
linphone-desktop/src/components/notifier/Notifier.cpp
+57
-28
Notifier.hpp
linphone-desktop/src/components/notifier/Notifier.hpp
+5
-9
main.cpp
linphone-desktop/src/main.cpp
+2
-0
DesktopPopup.qml
linphone-desktop/ui/modules/Common/Popup/DesktopPopup.qml
+4
-1
MessagesCounter.qml
...e-desktop/ui/modules/Linphone/Contact/MessagesCounter.qml
+4
-2
CallNotification.qml
...op/ui/modules/Linphone/Notifications/CallNotification.qml
+0
-12
Notification.qml
...esktop/ui/modules/Linphone/Notifications/Notification.qml
+7
-3
Notification.spec.qml
...p/ui/modules/Linphone/Notifications/Notification.spec.qml
+4
-0
NotificationReceivedCall.qml
...dules/Linphone/Notifications/NotificationReceivedCall.qml
+108
-0
NotificationReceivedFileMessage.qml
...inphone/Notifications/NotificationReceivedFileMessage.qml
+80
-0
NotificationReceivedMessage.qml
...es/Linphone/Notifications/NotificationReceivedMessage.qml
+100
-0
ReceivedMessageNotification.qml
...es/Linphone/Notifications/ReceivedMessageNotification.qml
+0
-12
NotificationReceivedCallStyle.qml
...ne/Styles/Notifications/NotificationReceivedCallStyle.qml
+22
-0
NotificationReceivedFileMessageStyle.qml
...es/Notifications/NotificationReceivedFileMessageStyle.qml
+27
-0
NotificationReceivedMessageStyle.qml
...Styles/Notifications/NotificationReceivedMessageStyle.qml
+28
-0
NotificationStyle.qml
...dules/Linphone/Styles/Notifications/NotificationStyle.qml
+1
-1
qmldir
linphone-desktop/ui/modules/Linphone/Styles/qmldir
+15
-12
utils.js
linphone-desktop/ui/scripts/Utils/utils.js
+12
-0
MainWindow.qml
linphone-desktop/ui/views/App/MainWindow/MainWindow.qml
+14
-22
cmake-builder
submodules/cmake-builder
+1
-1
linphone
submodules/linphone
+1
-1
No files found.
linphone-desktop/assets/images/file_sign.svg
0 → 100644
View file @
2b5cb6bc
<?xml version="1.0" encoding="UTF-8"?>
<svg
width=
"40px"
height=
"40px"
viewBox=
"0 0 40 40"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<!-- Generator: Sketch 41 (35326) - http://www.bohemiancoding.com/sketch -->
<title>
call_in_sign
</title>
<desc>
Created with Sketch.
</desc>
<defs></defs>
<g
id=
"Symbols"
stroke=
"none"
stroke-width=
"1"
fill=
"none"
fill-rule=
"evenodd"
>
<g
id=
"call_in_sign"
>
<polygon
fill=
"#FF5E00"
points=
"0 0 40 0 0 40"
></polygon>
<path
d=
"M11.243716,8.71501414 L11.1871835,8.71501414 C8.47215269,8.71501414 5.21235367,10.3895588 4.11804501,12.8152126 L4.51732144,15.956733 L8.01512006,15.956733 L8.2626641,12.830113 L14.1683578,12.830113 L14.41529,15.956733 L17.9130886,15.956733 L18.3123651,12.8149535 C17.2174446,10.3886518 13.9583798,8.71501414 11.243716,8.71501414 Z"
fill=
"#FFFFFF"
transform=
"translate(11.215205, 12.335874) rotate(-135.000000) translate(-11.215205, -12.335874) "
></path>
</g>
</g>
</svg>
\ No newline at end of file
linphone-desktop/assets/images/message_sign.svg
0 → 100644
View file @
2b5cb6bc
<?xml version="1.0" encoding="UTF-8"?>
<svg
width=
"40px"
height=
"40px"
viewBox=
"0 0 40 40"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<!-- Generator: Sketch 41 (35326) - http://www.bohemiancoding.com/sketch -->
<title>
call_in_sign
</title>
<desc>
Created with Sketch.
</desc>
<defs></defs>
<g
id=
"Symbols"
stroke=
"none"
stroke-width=
"1"
fill=
"none"
fill-rule=
"evenodd"
>
<g
id=
"call_in_sign"
>
<polygon
fill=
"#FF5E00"
points=
"0 0 40 0 0 40"
></polygon>
<path
d=
"M11.243716,8.71501414 L11.1871835,8.71501414 C8.47215269,8.71501414 5.21235367,10.3895588 4.11804501,12.8152126 L4.51732144,15.956733 L8.01512006,15.956733 L8.2626641,12.830113 L14.1683578,12.830113 L14.41529,15.956733 L17.9130886,15.956733 L18.3123651,12.8149535 C17.2174446,10.3886518 13.9583798,8.71501414 11.243716,8.71501414 Z"
fill=
"#FFFFFF"
transform=
"translate(11.215205, 12.335874) rotate(-135.000000) translate(-11.215205, -12.335874) "
></path>
</g>
</g>
</svg>
\ No newline at end of file
linphone-desktop/resources.qrc
View file @
2b5cb6bc
...
...
@@ -78,6 +78,7 @@
<file>
assets/images/edit_normal.svg
</file>
<file>
assets/images/edit_pressed.svg
</file>
<file>
assets/images/ended_call.svg
</file>
<file>
assets/images/file_sign.svg
</file>
<file>
assets/images/filter.svg
</file>
<file>
assets/images/fullscreen_hovered.svg
</file>
<file>
assets/images/fullscreen_normal.svg
</file>
...
...
@@ -101,6 +102,7 @@
<file>
assets/images/led_red.svg
</file>
<file>
assets/images/led_white.svg
</file>
<file>
assets/images/linphone.png
</file>
<file>
assets/images/message_sign.svg
</file>
<file>
assets/images/micro_off_hovered.svg
</file>
<file>
assets/images/micro_off_normal.svg
</file>
<file>
assets/images/micro_off_pressed.svg
</file>
...
...
@@ -228,9 +230,10 @@
<file>
ui/modules/Linphone/Contact/ContactDescription.qml
</file>
<file>
ui/modules/Linphone/Contact/Contact.qml
</file>
<file>
ui/modules/Linphone/Contact/MessagesCounter.qml
</file>
<file>
ui/modules/Linphone/Notifications/CallNotification.qml
</file>
<file>
ui/modules/Linphone/Notifications/Notification.qml
</file>
<file>
ui/modules/Linphone/Notifications/ReceivedMessageNotification.qml
</file>
<file>
ui/modules/Linphone/Notifications/NotificationReceivedCall.qml
</file>
<file>
ui/modules/Linphone/Notifications/NotificationReceivedFileMessage.qml
</file>
<file>
ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml
</file>
<file>
ui/modules/Linphone/Presence/PresenceLevel.qml
</file>
<file>
ui/modules/Linphone/Presence/PresenceString.qml
</file>
<file>
ui/modules/Linphone/qmldir
</file>
...
...
@@ -243,7 +246,10 @@
<file>
ui/modules/Linphone/Styles/Contact/ContactDescriptionStyle.qml
</file>
<file>
ui/modules/Linphone/Styles/Contact/ContactStyle.qml
</file>
<file>
ui/modules/Linphone/Styles/Contact/MessagesCounterStyle.qml
</file>
<file>
ui/modules/Linphone/Styles/NotificationStyle.qml
</file>
<file>
ui/modules/Linphone/Styles/Notifications/NotificationReceivedCallStyle.qml
</file>
<file>
ui/modules/Linphone/Styles/Notifications/NotificationReceivedFileMessageStyle.qml
</file>
<file>
ui/modules/Linphone/Styles/Notifications/NotificationReceivedMessageStyle.qml
</file>
<file>
ui/modules/Linphone/Styles/Notifications/NotificationStyle.qml
</file>
<file>
ui/modules/Linphone/Styles/Presence/PresenceStringStyle.qml
</file>
<file>
ui/modules/Linphone/Styles/qmldir
</file>
<file>
ui/modules/Linphone/Styles/SmartSearchBarStyle.qml
</file>
...
...
linphone-desktop/src/app/App.cpp
View file @
2b5cb6bc
...
...
@@ -92,10 +92,13 @@ QQuickWindow *App::getCallsWindow () const {
return
window
;
}
bool
App
::
hasFocus
()
const
{
QQuickWindow
*
App
::
getMainWindow
()
const
{
QQmlApplicationEngine
&
engine
=
const_cast
<
QQmlApplicationEngine
&>
(
m_engine
);
const
QQuickWindow
*
root
=
qobject_cast
<
QQuickWindow
*>
(
engine
.
rootObjects
().
at
(
0
));
return
!!
root
->
activeFocusItem
();
return
qobject_cast
<
QQuickWindow
*>
(
engine
.
rootObjects
().
at
(
0
));
}
bool
App
::
hasFocus
()
const
{
return
getMainWindow
()
->
isActive
()
||
getCallsWindow
()
->
isActive
();
}
// -----------------------------------------------------------------------------
...
...
@@ -109,6 +112,9 @@ void App::initContentApp () {
registerTypes
();
addContextProperties
();
// Enable notifications.
m_notifier
=
new
Notifier
();
CoreManager
::
getInstance
()
->
enableHandlers
();
// Load main view.
...
...
@@ -167,7 +173,7 @@ void App::registerTypes () {
qmlRegisterSingletonType
<
CallsListModel
>
(
"Linphone"
,
1
,
0
,
"CallsListModel"
,
[](
QQmlEngine
*
,
QJSEngine
*
)
->
QObject
*
{
return
new
CallsListModel
();
return
CoreManager
::
getInstance
()
->
get
CallsListModel
();
}
);
...
...
@@ -219,13 +225,10 @@ void App::addContextProperties () {
}
context
->
setContextProperty
(
"CallsWindow"
,
component
.
create
());
m_notifier
=
new
Notifier
();
context
->
setContextProperty
(
"Notifier"
,
m_notifier
);
}
void
App
::
setTrayIcon
()
{
QQuickWindow
*
root
=
qobject_cast
<
QQuickWindow
*>
(
m_engine
.
rootObjects
().
at
(
0
)
);
QQuickWindow
*
root
=
getMainWindow
(
);
QMenu
*
menu
=
new
QMenu
();
m_system_tray_icon
=
new
QSystemTrayIcon
(
root
);
...
...
linphone-desktop/src/app/App.hpp
View file @
2b5cb6bc
...
...
@@ -49,6 +49,7 @@ public:
}
QQuickWindow
*
getCallsWindow
()
const
;
QQuickWindow
*
getMainWindow
()
const
;
bool
hasFocus
()
const
;
...
...
linphone-desktop/src/components/call/CallModel.cpp
View file @
2b5cb6bc
...
...
@@ -226,18 +226,15 @@ float CallModel::getQuality () const {
}
bool
CallModel
::
getMicroMuted
()
const
{
return
m_micro_muted
;
return
!
CoreManager
::
getInstance
()
->
getCore
()
->
micEnabled
()
;
}
void
CallModel
::
setMicroMuted
(
bool
status
)
{
if
(
m_micro_muted
!=
status
)
{
m_micro_muted
=
status
;
shared_ptr
<
linphone
::
Core
>
core
=
CoreManager
::
getInstance
()
->
getCore
();
if
(
m_micro_muted
==
core
->
micEnabled
())
core
->
enableMic
(
!
m_micro_muted
);
emit
microMutedChanged
(
m_micro_muted
);
if
(
status
!=
core
->
micEnabled
())
{
core
->
enableMic
(
!
status
);
emit
microMutedChanged
(
status
);
}
}
...
...
linphone-desktop/src/components/call/CallModel.hpp
View file @
2b5cb6bc
...
...
@@ -109,7 +109,6 @@ private:
bool
getRecording
()
const
;
bool
m_micro_muted
=
false
;
bool
m_paused_by_remote
=
false
;
bool
m_paused_by_user
=
false
;
bool
m_recording
=
false
;
...
...
linphone-desktop/src/components/calls/CallsListModel.cpp
View file @
2b5cb6bc
...
...
@@ -81,6 +81,16 @@ QVariant CallsListModel::data (const QModelIndex &index, int role) const {
return
QVariant
();
}
CallModel
*
CallsListModel
::
getCall
(
const
shared_ptr
<
linphone
::
Call
>
&
linphone_call
)
const
{
auto
it
=
find_if
(
m_list
.
begin
(),
m_list
.
end
(),
[
linphone_call
](
CallModel
*
call
)
{
return
linphone_call
==
call
->
getLinphoneCall
();
}
);
return
it
!=
m_list
.
end
()
?
*
it
:
nullptr
;
}
// -----------------------------------------------------------------------------
void
CallsListModel
::
launchAudioCall
(
const
QString
&
sip_uri
)
const
{
...
...
linphone-desktop/src/components/calls/CallsListModel.hpp
View file @
2b5cb6bc
...
...
@@ -43,6 +43,8 @@ public:
QHash
<
int
,
QByteArray
>
roleNames
()
const
override
;
QVariant
data
(
const
QModelIndex
&
index
,
int
role
=
Qt
::
DisplayRole
)
const
override
;
CallModel
*
getCall
(
const
std
::
shared_ptr
<
linphone
::
Call
>
&
linphone_call
)
const
;
Q_INVOKABLE
void
launchAudioCall
(
const
QString
&
sip_uri
)
const
;
Q_INVOKABLE
void
launchVideoCall
(
const
QString
&
sip_uri
)
const
;
...
...
linphone-desktop/src/components/camera/MSFunctions.cpp
View file @
2b5cb6bc
#include <QOpenGLFunctions>
/*
* MSFunctions.cpp
* Copyright (C) 2017 Belledonne Communications, Grenoble, France
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Created on: February 9, 2017
* Author: Ronan Abhamon
*/
#include "MSFunctions.hpp"
// Do not include this header before `QOpenGLFunctions`!!!
#include <mediastreamer2/msogl.h>
#include "MSFunctions.hpp"
// =============================================================================
MSFunctions
*
MSFunctions
::
m_instance
=
nullptr
;
...
...
linphone-desktop/src/components/camera/MSFunctions.hpp
View file @
2b5cb6bc
/*
* MSFunctions.hpp
* Copyright (C) 2017 Belledonne Communications, Grenoble, France
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Created on: February 9, 2017
* Author: Ronan Abhamon
*/
#ifndef MS_FUNCTIONS_H_
#define MS_FUNCTIONS_H_
...
...
linphone-desktop/src/components/chat/ChatModel.cpp
View file @
2b5cb6bc
...
...
@@ -30,6 +30,7 @@
#include <QTimer>
#include <QUuid>
#include "../../app/App.hpp"
#include "../../app/Paths.hpp"
#include "../../app/ThumbnailProvider.hpp"
#include "../../utils.hpp"
...
...
@@ -150,9 +151,11 @@ private:
if
(
it
==
m_chat_model
->
m_entries
.
end
())
return
;
// File message downloaded.
if
(
state
==
linphone
::
ChatMessageStateFileTransferDone
&&
!
message
->
isOutgoing
())
{
createThumbnail
(
message
);
fillThumbnailProperty
((
*
it
).
first
,
message
);
App
::
getInstance
()
->
getNotifier
()
->
notifyReceivedFileMessage
(
message
);
}
(
*
it
).
first
[
"status"
]
=
state
;
...
...
linphone-desktop/src/components/core/CoreHandlers.cpp
View file @
2b5cb6bc
...
...
@@ -46,16 +46,19 @@ void CoreHandlers::onCallStateChanged (
const
string
&
)
{
emit
callStateChanged
(
call
,
state
);
if
(
call
->
getState
()
==
linphone
::
CallStateIncomingReceived
)
App
::
getInstance
()
->
getNotifier
()
->
notifyReceivedCall
(
call
);
}
void
CoreHandlers
::
onMessageReceived
(
const
shared_ptr
<
linphone
::
Core
>
&
,
const
shared_ptr
<
linphone
::
ChatRoom
>
&
room
,
const
shared_ptr
<
linphone
::
ChatRoom
>
&
,
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
)
{
emit
messageReceived
(
message
);
const
App
*
app
=
App
::
getInstance
();
if
(
!
app
->
hasFocus
())
app
->
getNotifier
()
->
notifyReceivedMessage
(
10000
,
room
,
message
);
app
->
getNotifier
()
->
notifyReceivedMessage
(
message
);
}
linphone-desktop/src/components/core/CoreManager.cpp
View file @
2b5cb6bc
...
...
@@ -49,6 +49,7 @@ void CoreManager::init () {
if
(
!
m_instance
)
{
m_instance
=
new
CoreManager
();
m_instance
->
m_calls_list_model
=
new
CallsListModel
(
m_instance
);
m_instance
->
m_contacts_list_model
=
new
ContactsListModel
(
m_instance
);
m_instance
->
m_sip_addresses_model
=
new
SipAddressesModel
(
m_instance
);
...
...
linphone-desktop/src/components/core/CoreManager.hpp
View file @
2b5cb6bc
...
...
@@ -23,6 +23,7 @@
#ifndef CORE_MANAGER_H_
#define CORE_MANAGER_H_
#include "../calls/CallsListModel.hpp"
#include "../contacts/ContactsListModel.hpp"
#include "../sip-addresses/SipAddressesModel.hpp"
#include "CoreHandlers.hpp"
...
...
@@ -51,6 +52,10 @@ public:
// Singleton models.
// ---------------------------------------------------------------------------
CallsListModel
*
getCallsListModel
()
const
{
return
m_calls_list_model
;
}
ContactsListModel
*
getContactsListModel
()
const
{
return
m_contacts_list_model
;
}
...
...
@@ -83,6 +88,7 @@ private:
std
::
shared_ptr
<
linphone
::
Core
>
m_core
;
std
::
shared_ptr
<
CoreHandlers
>
m_handlers
;
CallsListModel
*
m_calls_list_model
;
ContactsListModel
*
m_contacts_list_model
;
SipAddressesModel
*
m_sip_addresses_model
;
...
...
linphone-desktop/src/components/notifier/Notifier.cpp
View file @
2b5cb6bc
...
...
@@ -20,23 +20,31 @@
* Author: Ronan Abhamon
*/
#include "../../app/App.hpp"
#include "Notifier.hpp"
#include <QQmlComponent>
#include <QQuickWindow>
#include <QtDebug>
#include <QTimer>
#include "../../app/App.hpp"
#include "../../utils.hpp"
#include "../core/CoreManager.hpp"
#include "Notifier.hpp"
// Notifications QML properties/methods.
#define NOTIFICATION_SHOW_METHOD_NAME "show"
#define NOTIFICATION_HEIGHT_PROPERTY "notificationHeight"
#define NOTIFICATION_OFFSET_PROPERTY_NAME "notificationOffset"
#define NOTIFICATION_PROPERTY_DATA "notificationData"
#define NOTIFICATION_PROPERTY_HEIGHT "notificationHeight"
#define NOTIFICATION_PROPERTY_OFFSET "notificationOffset"
#define QML_CALL_NOTIFICATION_PATH "qrc:/ui/modules/Linphone/Notifications/CallNotification.qml"
#define QML_MESSAGE_RECEIVED_NOTIFICATION_PATH "qrc:/ui/modules/Linphone/Notifications/ReceivedMessageNotification.qml"
#define QML_NOTIFICATION_PATH_RECEIVED_MESSAGE "qrc:/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml"
#define QML_NOTIFICATION_PATH_RECEIVED_FILE_MESSAGE "qrc:/ui/modules/Linphone/Notifications/NotificationReceivedFileMessage.qml"
#define QML_NOTIFICATION_PATH_RECEIVED_CALL "qrc:/ui/modules/Linphone/Notifications/NotificationReceivedCall.qml"
#define NOTIFICATION_TIMEOUT_RECEIVED_MESSAGE 10000
#define NOTIFICATION_TIMEOUT_RECEIVED_FILE_MESSAGE 10000
#define NOTIFICATION_TIMEOUT_RECEIVED_CALL 10000
// Arbitrary hardcoded values.
#define NOTIFICATION_SPACING 10
...
...
@@ -77,22 +85,22 @@ Notifier::Notifier (QObject *parent) :
QQmlEngine
*
engine
=
App
::
getInstance
()
->
getEngine
();
// Build components.
m_components
[
Notifier
::
Call
]
=
new
QQmlComponent
(
engine
,
QUrl
(
QML_CALL_NOTIFICATION_PATH
));
m_components
[
Notifier
::
MessageReceived
]
=
new
QQmlComponent
(
engine
,
QUrl
(
QML_MESSAGE_RECEIVED_NOTIFICATION_PATH
));
m_components
[
Notifier
::
MessageReceived
]
=
new
QQmlComponent
(
engine
,
QUrl
(
QML_NOTIFICATION_PATH_RECEIVED_MESSAGE
));
m_components
[
Notifier
::
FileMessageReceived
]
=
new
QQmlComponent
(
engine
,
QUrl
(
QML_NOTIFICATION_PATH_RECEIVED_FILE_MESSAGE
));
m_components
[
Notifier
::
CallReceived
]
=
new
QQmlComponent
(
engine
,
QUrl
(
QML_NOTIFICATION_PATH_RECEIVED_CALL
));
// Check errors.
for
(
int
i
=
0
;
i
<
Notifier
::
MaxNbTypes
;
++
i
)
{
QQmlComponent
*
component
=
m_components
[
i
];
if
(
component
->
isError
())
{
qWarning
()
<<
QStringLiteral
(
"Errors found in `Notification` component %1:"
).
arg
(
i
)
<<
component
->
errors
();
qWarning
()
<<
QStringLiteral
(
"Errors found in `Notification` component %1:"
).
arg
(
i
)
<<
component
->
errors
();
abort
();
}
}
}
Notifier
::~
Notifier
()
{
for
(
int
i
=
0
;
i
<
Notifier
::
MaxNbTypes
;
i
++
)
for
(
int
i
=
0
;
i
<
Notifier
::
MaxNbTypes
;
++
i
)
delete
m_components
[
i
];
}
...
...
@@ -110,9 +118,9 @@ QObject *Notifier::createNotification (Notifier::NotificationType type) {
// Create instance and set attributes.
QObject
*
object
=
m_components
[
type
]
->
create
();
int
offset
=
getNotificationSize
(
*
object
,
NOTIFICATION_
HEIGHT_PROPERTY
);
int
offset
=
getNotificationSize
(
*
object
,
NOTIFICATION_
PROPERTY_HEIGHT
);
if
(
offset
==
-
1
||
!::
setProperty
(
*
object
,
NOTIFICATION_
OFFSET_PROPERTY_NAME
,
m_offset
))
{
if
(
offset
==
-
1
||
!::
setProperty
(
*
object
,
NOTIFICATION_
PROPERTY_OFFSET
,
m_offset
))
{
delete
object
;
m_mutex
.
unlock
();
return
nullptr
;
...
...
@@ -149,7 +157,7 @@ void Notifier::showNotification (QObject *notification, int timeout) {
qInfo
()
<<
"Update notifications counter, hidden notification detected."
;
if
(
visible
)
q
Fatal
(
"A notification cannot be visible twice!"
);
q
Warning
(
"A notification cannot be visible twice!"
);
m_mutex
.
lock
();
...
...
@@ -172,20 +180,41 @@ void Notifier::showNotification (QObject *notification, int timeout) {
// -----------------------------------------------------------------------------
void
Notifier
::
notifyReceivedMessage
(
int
timeout
,
const
shared_ptr
<
linphone
::
ChatRoom
>
&
room
,
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
)
{
QObject
*
object
=
createNotification
(
Notifier
::
MessageReceived
);
void
Notifier
::
notifyReceivedMessage
(
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
)
{
QObject
*
notification
=
createNotification
(
Notifier
::
MessageReceived
);
if
(
!
notification
)
return
;
QVariantMap
map
;
map
[
"message"
]
=
::
Utils
::
linphoneStringToQString
(
message
->
getText
());
map
[
"sipAddress"
]
=
::
Utils
::
linphoneStringToQString
(
message
->
getFromAddress
()
->
asStringUriOnly
());
map
[
"window"
].
setValue
(
App
::
getInstance
()
->
getMainWindow
());
if
(
object
)
showNotification
(
object
,
timeout
);
::
setProperty
(
*
notification
,
NOTIFICATION_PROPERTY_DATA
,
map
);
showNotification
(
notification
,
NOTIFICATION_TIMEOUT_RECEIVED_MESSAGE
);
}
void
Notifier
::
showCallMessage
(
int
timeout
,
const
QString
&
)
{
QObject
*
object
=
createNotification
(
Notifier
::
Call
);
void
Notifier
::
notifyReceivedFileMessage
(
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
)
{
QObject
*
notification
=
createNotification
(
Notifier
::
FileMessageReceived
);
if
(
!
notification
)
return
;
QVariantMap
map
;
map
[
"fileUri"
]
=
::
Utils
::
linphoneStringToQString
(
message
->
getFileTransferFilepath
());
map
[
"fileSize"
]
=
static_cast
<
quint64
>
(
message
->
getFileTransferInformation
()
->
getSize
());
::
setProperty
(
*
notification
,
NOTIFICATION_PROPERTY_DATA
,
map
);
showNotification
(
notification
,
NOTIFICATION_TIMEOUT_RECEIVED_FILE_MESSAGE
);
}
void
Notifier
::
notifyReceivedCall
(
const
shared_ptr
<
linphone
::
Call
>
&
call
)
{
QObject
*
notification
=
createNotification
(
Notifier
::
CallReceived
);
if
(
!
notification
)
return
;
QVariantMap
map
;
map
[
"call"
].
setValue
(
CoreManager
::
getInstance
()
->
getCallsListModel
()
->
getCall
(
call
));
if
(
object
)
showNotification
(
object
,
timeout
);
::
setProperty
(
*
notification
,
NOTIFICATION_PROPERTY_DATA
,
map
);
showNotification
(
notification
,
NOTIFICATION_TIMEOUT_RECEIVED_CALL
);
}
linphone-desktop/src/components/notifier/Notifier.hpp
View file @
2b5cb6bc
...
...
@@ -40,19 +40,15 @@ public:
~
Notifier
();
enum
NotificationType
{
Call
,
MessageReceived
,
FileMessageReceived
,
CallReceived
,
MaxNbTypes
};
void
notifyReceivedMessage
(
int
timeout
,
const
std
::
shared_ptr
<
linphone
::
ChatRoom
>
&
room
,
const
std
::
shared_ptr
<
linphone
::
ChatMessage
>
&
message
);
// TODO
void
showCallMessage
(
int
timeout
,
const
QString
&
);
void
notifyReceivedMessage
(
const
std
::
shared_ptr
<
linphone
::
ChatMessage
>
&
message
);
void
notifyReceivedFileMessage
(
const
std
::
shared_ptr
<
linphone
::
ChatMessage
>
&
message
);
void
notifyReceivedCall
(
const
std
::
shared_ptr
<
linphone
::
Call
>
&
call
);
private:
QObject
*
createNotification
(
NotificationType
type
);
...
...
linphone-desktop/src/main.cpp
View file @
2b5cb6bc
...
...
@@ -28,6 +28,8 @@
int
main
(
int
argc
,
char
*
argv
[])
{
Logger
::
init
();
QCoreApplication
::
setAttribute
(
Qt
::
AA_UseOpenGLES
,
true
);
// Force shader version 2.0.
QSurfaceFormat
fmt
;
fmt
.
setVersion
(
2
,
0
);
...
...
linphone-desktop/ui/modules/Common/Popup/DesktopPopup.qml
View file @
2b5cb6bc
...
...
@@ -77,7 +77,10 @@ Item {
to
:
'
opened
'
ScriptAction
{
script
:
popup
.
showNormal
()
script
:
{
popup
.
showNormal
()
popup
.
requestActivate
()
}
}
NumberAnimation
{
...
...
linphone-desktop/ui/modules/Linphone/Contact/MessagesCounter.qml
View file @
2b5cb6bc
...
...
@@ -23,8 +23,10 @@ Item {
iconSize
:
MessagesCounterStyle
.
iconSize
.
message
Icon
{
anchors.horizontalCenter
:
parent
.
right
anchors.verticalCenter
:
parent
.
bottom
anchors
{
horizontalCenter
:
parent
.
right
verticalCenter
:
parent
.
bottom
}
icon
:
'
chat_amount
'
iconSize
:
MessagesCounterStyle
.
iconSize
.
amount
...
...
linphone-desktop/ui/modules/Linphone/Notifications/CallNotification.qml
deleted
100644 → 0
View file @
40ee4028
import
QtQuick
2.7
// =============================================================================
Notification
{
Rectangle
{
color
:
'
red
'
width
:
200
height
:
100
}
}
linphone-desktop/ui/modules/Linphone/Notifications/Notification.qml
View file @
2b5cb6bc
...
...
@@ -15,15 +15,19 @@ DesktopPopup {
// ---------------------------------------------------------------------------
property
int
notificationOffset
:
0
property
alias
notificationHeight
:
notification
.
popupHeight
property
int
notificationOffset
:
0
property
var
notificationData
:
null
readonly
property
var
window
:
_window
property
var
_window
// ---------------------------------------------------------------------------
flags
:
Qt
.
Popup
Component.onCompleted
:
{
var
window
=
data
[
0
]
var
window
=
_window
=
data
[
0
]
Utils
.
assert
(
Utils
.
qmlTypeof
(
window
,
'
QQuickWindowQmlImpl
'
),
true
,
...
...
@@ -45,7 +49,7 @@ DesktopPopup {
}
var
height
=
screen
.
desktopAvailableHeight
-
window
.
height
return
height
-
notificationOffset
%
height
return
height
-
(
notificationOffset
%
height
)
})
}
}
linphone-desktop/ui/modules/Linphone/Notifications/Notification.spec.qml
View file @
2b5cb6bc
...
...
@@ -11,6 +11,10 @@ TestCase {
id
:
notification
}
function
test_notificationDataProperty
()
{
compare
(
Utils
.
isObject
(
notification
.
notificationData
),
true
)
}
function
test_notificationHeightProperty
()
{
compare
(
Utils
.
isInteger
(
notification
.
notificationHeight
),
true
)
}
...
...
linphone-desktop/ui/modules/Linphone/Notifications/NotificationReceivedCall.qml
0 → 100644
View file @
2b5cb6bc
import
QtQuick
2.7
import
QtQuick
.
Layouts
1.3
import
Common
1.0
import
Linphone
1.0
import
Linphone
.
Styles
1.0
// =============================================================================
Notification
{
id
:
notification
// ---------------------------------------------------------------------------
property
var
_call
:
notificationData
&&
notificationData
.
call
property
var
_contact
:
_contactObserver
.
contact
property
var
_contactObserver
:
SipAddressesModel
.
getContactObserver
(
_call
?
_call
.
sipAddress
:
''
)
// ---------------------------------------------------------------------------
function
_close
(
cb
)
{
notification
.
window
.
setVisible
(
false
)
cb
()
}
// ---------------------------------------------------------------------------
Rectangle
{
color
:
NotificationReceivedCallStyle
.
color
height
:
NotificationReceivedCallStyle
.
height
width
:
NotificationReceivedCallStyle
.
width
Icon
{
anchors
{
left
:
parent
.
left
top
:
parent
.
top
}
icon
:
'
call_sign_incoming
'
iconSize
:
NotificationReceivedCallStyle
.
iconSize
}
Loader
{
active
:
notification
.
_call
anchors
{
fill
:
parent
leftMargin
:
NotificationReceivedCallStyle
.
leftMargin
rightMargin
:
NotificationReceivedCallStyle
.
rightMargin
bottomMargin
:
NotificationReceivedCallStyle
.
bottomMargin
}
sourceComponent
:
ColumnLayout
{
spacing
:
NotificationReceivedCallStyle
.
spacing
Contact
{
Layout.fillWidth
:
true
entry
:
({
contact
:
notification
.
_contact
,
sipAddress
:
notification
.
_contactObserver
.
sipAddress
})
}
// ---------------------------------------------------------------------
// Action buttons.
// ---------------------------------------------------------------------
Item
{
Layout.fillHeight
:
true
Layout.fillWidth
:
true
ActionBar
{
anchors.centerIn
:
parent
iconSize
:
NotificationReceivedCallStyle
.
actionArea
.
iconSize
ActionButton
{
icon
:
'
video_call_accept
'
onClicked
:
notification
.
_close
(
notification
.
_call
.
acceptWithVideo
)
}
ActionButton
{
icon
:
'
call_accept
'
onClicked
:
notification
.
_close
(
notification
.
_call
.
accept
)
}
}
ActionBar
{
anchors
{
right
:
parent
.
right
rightMargin
:
NotificationReceivedCallStyle
.
actionArea
.
rightButtonsGroupMargin
verticalCenter
:
parent
.
verticalCenter
}
iconSize
:
NotificationReceivedCallStyle
.
actionArea
.
iconSize
ActionButton
{
icon
:
'
hangup
'
onClicked
:
notification
.
_close
(
notification
.
_call
.
terminate
)
}
}
}
}
}
}
}
linphone-desktop/ui/modules/Linphone/Notifications/NotificationReceivedFileMessage.qml
0 → 100644
View file @
2b5cb6bc
import
QtQuick
2.7
import
QtQuick
.
Layouts
1.3
import
Common
1.0
import
Linphone
1.0
import
Linphone
.
Styles
1.0
import
Utils
1.0
// =============================================================================
Notification
{
id
:
notification
// ---------------------------------------------------------------------------
property
string
_fileUri
:
notificationData
?
notificationData
.
fileUri
:
''
// ---------------------------------------------------------------------------
Rectangle
{
color
:
NotificationReceivedFileMessageStyle
.
color
height
:
NotificationReceivedFileMessageStyle
.
height
width
:
NotificationReceivedFileMessageStyle
.
width
Icon
{
anchors
{
left
:
parent
.
left
top
:
parent
.
top
}
icon
:
'
file_sign
'
iconSize
:
NotificationReceivedFileMessageStyle
.
iconSize
}
Loader
{
active
:
notification
.
_fileUri
.
length
>
0
anchors
{
fill
:
parent
leftMargin
:
NotificationReceivedFileMessageStyle
.
leftMargin
rightMargin
:
NotificationReceivedFileMessageStyle
.
rightMargin
}
sourceComponent
:
RowLayout
{
anchors.fill
:
parent
spacing
:
NotificationReceivedFileMessageStyle
.
spacing
Text
{
Layout.fillWidth
:
true
color
:
NotificationReceivedFileMessageStyle
.
fileName
.
color
elide
:
Text
.
ElideRight
font.pointSize
:
NotificationReceivedFileMessageStyle
.
fileName
.
fontSize
text
:
Utils
.
basename
(
notification
.
_fileUri
)
}
Text
{
Layout.preferredWidth
:
NotificationReceivedFileMessageStyle
.
fileSize
.
width
color
:
NotificationReceivedFileMessageStyle
.
fileSize
.
color
elide
:
Text
.
ElideRight
font.pointSize
:
NotificationReceivedFileMessageStyle
.
fileSize
.
fontSize
horizontalAlignment
:
Text
.
AlignRight
text
:
Utils
.
formatSize
(
notification
.
notificationData
.
fileSize
)
}
}
MouseArea
{
anchors.fill
:
parent
cursorShape
:
containsMouse
?
Qt
.
PointingHandCursor
:
Qt
.
ArrowCursor
hoverEnabled
:
true
onClicked
:
{
notification
.
window
.
setVisible
(
false
)
Qt
.
openUrlExternally
(
'
file://
'
+
Utils
.
dirname
(
notification
.
_fileUri
))
}
}
}
}
}
linphone-desktop/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml
0 → 100644
View file @
2b5cb6bc
import
QtQuick
2.7
import
QtQuick
.
Layouts
1.3
import
Common
1.0
import
Linphone
1.0
import
Linphone
.
Styles
1.0
// =============================================================================
Notification
{
id
:
notification
// ---------------------------------------------------------------------------
property
string
_sipAddress
:
notificationData
?
notificationData
.
sipAddress
:
''
property
var
_contact
:
_contactObserver
.
contact
property
var
_contactObserver
:
SipAddressesModel
.
getContactObserver
(
_sipAddress
)
// ---------------------------------------------------------------------------
Rectangle
{
color
:
NotificationReceivedMessageStyle
.
color
height
:
NotificationReceivedMessageStyle
.
height
width
:
NotificationReceivedMessageStyle
.
width
Icon
{
anchors
{
left
:
parent
.
left
top
:
parent
.
top
}
icon
:
'
message_sign
'
iconSize
:
NotificationReceivedMessageStyle
.
iconSize
}
Loader
{
active
:
notification
.
_sipAddress
.
length
>
0
anchors
{
fill
:
parent
leftMargin
:
NotificationReceivedMessageStyle
.
leftMargin
rightMargin
:
NotificationReceivedMessageStyle
.
rightMargin
bottomMargin
:
NotificationReceivedMessageStyle
.
bottomMargin
}
sourceComponent
:
ColumnLayout
{
spacing
:
NotificationReceivedMessageStyle
.
spacing
Contact
{
Layout.fillWidth
:
true
entry
:
({
contact
:
notification
.
_contact
,
sipAddress
:
notification
.
_sipAddress
})
}
Rectangle
{
Layout.fillHeight
:
true
Layout.fillWidth
:
true
color
:
NotificationReceivedMessageStyle
.
messageContainer
.
color
radius
:
NotificationReceivedMessageStyle
.
messageContainer
.
radius
Text
{
anchors
{
fill
:
parent
margins
:
NotificationReceivedMessageStyle
.
messageContainer
.
margins
}
color
:
NotificationReceivedMessageStyle
.
messageContainer
.
text
.
color
elide
:
Text
.
ElideRight
font
{
italic
:
true
pointSize
:
NotificationReceivedMessageStyle
.
messageContainer
.
text
.
fontSize
}
verticalAlignment
:
Text
.
AlignVCenter
text
:
notification
.
notificationData
.
message
wrapMode
:
Text
.
Wrap
}
}
}
}
MouseArea
{
anchors.fill
:
parent
cursorShape
:
containsMouse
?
Qt
.
PointingHandCursor
:
Qt
.
ArrowCursor
hoverEnabled
:
true
onClicked
:
{
notification
.
window
.
setVisible
(
false
)
notification
.
notificationData
.
window
.
setView
(
'
Conversation
'
,
{
sipAddress
:
notification
.
_sipAddress
})
}
}
}
}
linphone-desktop/ui/modules/Linphone/Notifications/ReceivedMessageNotification.qml
deleted
100644 → 0
View file @
40ee4028
import
QtQuick
2.7
// =============================================================================
Notification
{
Rectangle
{
color
:
'
red
'
width
:
200
height
:
100
}
}
linphone-desktop/ui/modules/Linphone/Styles/Notifications/NotificationReceivedCallStyle.qml
0 → 100644
View file @
2b5cb6bc
pragma
Singleton
import
QtQuick
2.7
import
Common
1.0
// =============================================================================
QtObject
{
property
color
color
:
Colors
.
k
property
int
height
:
120
property
int
iconSize
:
40
property
int
spacing
:
0
property
int
width
:
300
property
int
bottomMargin
:
15
property
int
leftMargin
:
15
property
int
rightMargin
:
15
property
QtObject
actionArea
:
QtObject
{
property
int
iconSize
:
40
property
int
rightButtonsGroupMargin
:
15
}
}
linphone-desktop/ui/modules/Linphone/Styles/Notifications/NotificationReceivedFileMessageStyle.qml
0 → 100644
View file @
2b5cb6bc
pragma
Singleton
import
QtQuick
2.7
import
Common
1.0
// =============================================================================
QtObject
{
property
color
color
:
Colors
.
k
property
int
height
:
55
property
int
iconSize
:
40
property
int
leftMargin
:
25
property
int
rightMargin
:
15
property
int
spacing
:
10
property
int
width
:
300
property
QtObject
fileName
:
QtObject
{
property
color
color
:
Colors
.
h
property
int
fontSize
:
10
}
property
QtObject
fileSize
:
QtObject
{
property
color
color
:
Colors
.
h
property
int
fontSize
:
9
property
int
width
:
100
}
}
linphone-desktop/ui/modules/Linphone/Styles/Notifications/NotificationReceivedMessageStyle.qml
0 → 100644
View file @
2b5cb6bc
pragma
Singleton
import
QtQuick
2.7
import
Common
1.0
// =============================================================================
QtObject
{
property
color
color
:
Colors
.
k
property
int
bottomMargin
:
15
property
int
iconSize
:
40
property
int
leftMargin
:
15
property
int
rightMargin
:
15
property
int
spacing
:
0
property
int
width
:
300
property
int
height
:
120
property
QtObject
messageContainer
:
QtObject
{
property
color
color
:
Colors
.
m
property
int
radius
:
6
property
int
margins
:
10
property
QtObject
text
:
QtObject
{
property
color
color
:
Colors
.
l
property
int
fontSize
:
9
}
}
}
linphone-desktop/ui/modules/Linphone/Styles/NotificationStyle.qml
→
linphone-desktop/ui/modules/Linphone/Styles/Notification
s/Notification
Style.qml
View file @
2b5cb6bc
...
...
@@ -4,5 +4,5 @@ import QtQuick 2.7
// =============================================================================
QtObject
{
property
int
margin
:
1
0
property
int
margin
:
0
}
linphone-desktop/ui/modules/Linphone/Styles/qmldir
View file @
2b5cb6bc
...
...
@@ -16,7 +16,10 @@ singleton ContactDescriptionStyle 1.0 Contact/ContactDescriptionStyle.qml
singleton ContactStyle 1.0 Contact/ContactStyle.qml
singleton MessagesCounterStyle 1.0 Contact/MessagesCounterStyle.qml
singleton NotificationStyle 1.0 NotificationStyle.qml
singleton NotificationStyle 1.0 Notifications/NotificationStyle.qml
singleton NotificationReceivedCallStyle 1.0 Notifications/NotificationReceivedCallStyle.qml
singleton NotificationReceivedMessageStyle 1.0 Notifications/NotificationReceivedMessageStyle.qml
singleton NotificationReceivedFileMessageStyle 1.0 Notifications/NotificationReceivedFileMessageStyle.qml
singleton PresenceStringStyle 1.0 Presence/PresenceStringStyle.qml
...
...
linphone-desktop/ui/scripts/Utils/utils.js
View file @
2b5cb6bc
...
...
@@ -261,6 +261,18 @@ function assert (condition, message) {
// -----------------------------------------------------------------------------
function
basename
(
str
)
{
return
str
.
slice
(
str
.
lastIndexOf
(
'
/
'
)
+
1
)
}
// -----------------------------------------------------------------------------
function
dirname
(
str
)
{
return
str
.
slice
(
0
,
str
.
lastIndexOf
(
'
/
'
)
+
1
)
}
// -----------------------------------------------------------------------------
function
extractProperties
(
obj
,
pattern
)
{
if
(
!
pattern
)
{
return
{}
...
...
linphone-desktop/ui/views/App/MainWindow/MainWindow.qml
View file @
2b5cb6bc
...
...
@@ -46,10 +46,6 @@ ApplicationWindow {
})
}
function
ensureCollapsed
()
{
collapse
.
setCollapsed
(
true
)
}
// ---------------------------------------------------------------------------
function
_updateSelectedEntry
(
view
,
props
)
{
...
...
@@ -66,6 +62,10 @@ ApplicationWindow {
}
function
_setView
(
view
,
props
)
{
window
.
showNormal
()
window
.
requestActivate
()
collapse
.
setCollapsed
(
true
)
_updateSelectedEntry
(
view
,
props
)
_currentView
=
view
contentLoader
.
setSource
(
view
+
'
.qml
'
,
props
||
{})
...
...
@@ -153,31 +153,23 @@ ApplicationWindow {
model
:
SmartSearchBarModel
{}
onAddContact
:
{
window
.
ensureCollapsed
()
window
.
setView
(
'
ContactEdit
'
,
{
onAddContact
:
window
.
setView
(
'
ContactEdit
'
,
{
sipAddress
:
sipAddress
})
}
onLaunchCall
:
CallsListModel
.
launchAudioCall
(
sipAddress
)
onLaunchChat
:
{
window
.
ensureCollapsed
()
window
.
setView
(
'
Conversation
'
,
{
onLaunchChat
:
window
.
setView
(
'
Conversation
'
,
{
sipAddress
:
sipAddress
})
}
onLaunchVideoCall
:
CallsListModel
.
launchVideoCall
(
sipAddress
)
onEntryClicked
:
{
window
.
ensureCollapsed
()
window
.
setView
(
entry
.
contact
?
'
ContactEdit
'
:
'
Conversation
'
,
{
onEntryClicked
:
window
.
setView
(
entry
.
contact
?
'
ContactEdit
'
:
'
Conversation
'
,
{
sipAddress
:
entry
.
sipAddress
})
}
}
}
}
// ---------------------------------------------------------------------------
// Content.
...
...
cmake-builder
@
326a0976
Subproject commit
a79e2826e056cc48b3b4d0eda4f38d82f124bb56
Subproject commit
326a0976c0b42927056d39d99a396a2d0a8fc3da
linphone
@
2c3f66ef
Subproject commit
87bc9f7ea55f8e449cf1d6b83fefacd2d9e596b0
Subproject commit
2c3f66ef6f160f4c23efc2dea9485ddae8194af1
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment