Syncing my fork to the original repository
This commit is contained in:
commit
1a0b6e0599
30 changed files with 1093 additions and 575 deletions
BIN
db/emojis.db
Normal file
BIN
db/emojis.db
Normal file
Binary file not shown.
|
@ -16,7 +16,7 @@ CONFIG += sailfishapp sailfishapp_i18n
|
||||||
|
|
||||||
PKGCONFIG += nemonotifications-qt5 ngf-qt5
|
PKGCONFIG += nemonotifications-qt5 ngf-qt5
|
||||||
|
|
||||||
QT += core dbus
|
QT += core dbus sql
|
||||||
|
|
||||||
SOURCES += src/harbour-fernschreiber.cpp \
|
SOURCES += src/harbour-fernschreiber.cpp \
|
||||||
src/appsettings.cpp \
|
src/appsettings.cpp \
|
||||||
|
@ -24,6 +24,7 @@ SOURCES += src/harbour-fernschreiber.cpp \
|
||||||
src/chatmodel.cpp \
|
src/chatmodel.cpp \
|
||||||
src/dbusadaptor.cpp \
|
src/dbusadaptor.cpp \
|
||||||
src/dbusinterface.cpp \
|
src/dbusinterface.cpp \
|
||||||
|
src/emojisearchworker.cpp \
|
||||||
src/fernschreiberutils.cpp \
|
src/fernschreiberutils.cpp \
|
||||||
src/notificationmanager.cpp \
|
src/notificationmanager.cpp \
|
||||||
src/processlauncher.cpp \
|
src/processlauncher.cpp \
|
||||||
|
@ -102,8 +103,11 @@ ICONPATH = /usr/share/icons/hicolor
|
||||||
fernschreiber.desktop.path = /usr/share/applications/
|
fernschreiber.desktop.path = /usr/share/applications/
|
||||||
fernschreiber.desktop.files = harbour-fernschreiber.desktop
|
fernschreiber.desktop.files = harbour-fernschreiber.desktop
|
||||||
|
|
||||||
|
database.files = db
|
||||||
|
database.path = /usr/share/$${TARGET}
|
||||||
|
|
||||||
INSTALLS += telegram 86.png 108.png 128.png 172.png 256.png \
|
INSTALLS += telegram 86.png 108.png 128.png 172.png 256.png \
|
||||||
fernschreiber.desktop gui images
|
fernschreiber.desktop gui images database
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
src/appsettings.h \
|
src/appsettings.h \
|
||||||
|
@ -111,6 +115,7 @@ HEADERS += \
|
||||||
src/chatmodel.h \
|
src/chatmodel.h \
|
||||||
src/dbusadaptor.h \
|
src/dbusadaptor.h \
|
||||||
src/dbusinterface.h \
|
src/dbusinterface.h \
|
||||||
|
src/emojisearchworker.h \
|
||||||
src/fernschreiberutils.h \
|
src/fernschreiberutils.h \
|
||||||
src/notificationmanager.h \
|
src/notificationmanager.h \
|
||||||
src/processlauncher.h \
|
src/processlauncher.h \
|
||||||
|
|
|
@ -31,6 +31,9 @@ Row {
|
||||||
|
|
||||||
property string myUserId;
|
property string myUserId;
|
||||||
property variant inReplyToMessage;
|
property variant inReplyToMessage;
|
||||||
|
property bool editable: false;
|
||||||
|
|
||||||
|
signal clearRequested()
|
||||||
|
|
||||||
onInReplyToMessageChanged: {
|
onInReplyToMessageChanged: {
|
||||||
if (inReplyToMessage) {
|
if (inReplyToMessage) {
|
||||||
|
@ -47,37 +50,51 @@ Row {
|
||||||
border.width: 0
|
border.width: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Row {
|
||||||
id: inReplyToMessageColumn
|
|
||||||
spacing: Theme.paddingSmall
|
|
||||||
width: parent.width - Theme.paddingSmall - inReplyToMessageRectangle.width
|
width: parent.width - Theme.paddingSmall - inReplyToMessageRectangle.width
|
||||||
|
spacing: Theme.paddingSmall
|
||||||
|
|
||||||
Text {
|
Column {
|
||||||
id: inReplyToUserText
|
id: inReplyToMessageColumn
|
||||||
|
spacing: Theme.paddingSmall
|
||||||
|
width: parent.width - ( inReplyToRow.editable ? ( Theme.paddingSmall + removeInReplyToIconButton.width ) : 0 )
|
||||||
|
|
||||||
width: parent.width
|
Text {
|
||||||
font.pixelSize: Theme.fontSizeExtraSmall
|
id: inReplyToUserText
|
||||||
font.weight: Font.ExtraBold
|
|
||||||
color: Theme.primaryColor
|
width: parent.width
|
||||||
maximumLineCount: 1
|
font.pixelSize: Theme.fontSizeExtraSmall
|
||||||
elide: Text.ElideRight
|
font.weight: Font.ExtraBold
|
||||||
textFormat: Text.StyledText
|
color: Theme.primaryColor
|
||||||
horizontalAlignment: Text.AlignLeft
|
maximumLineCount: 1
|
||||||
|
elide: Text.ElideRight
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: inReplyToMessageText
|
||||||
|
font.pixelSize: Theme.fontSizeExtraSmall
|
||||||
|
color: Theme.primaryColor
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
onTruncatedChanged: {
|
||||||
|
// There is obviously a bug in QML in truncating text with images.
|
||||||
|
// We simply remove Emojis then...
|
||||||
|
if (truncated) {
|
||||||
|
text = text.replace(/\<img [^>]+\/\>/g, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
IconButton {
|
||||||
id: inReplyToMessageText
|
id: removeInReplyToIconButton
|
||||||
font.pixelSize: Theme.fontSizeExtraSmall
|
icon.source: "image://theme/icon-m-clear"
|
||||||
color: Theme.primaryColor
|
visible: inReplyToRow.editable
|
||||||
width: parent.width
|
onClicked: {
|
||||||
elide: Text.ElideRight
|
inReplyToRow.clearRequested();
|
||||||
textFormat: Text.StyledText
|
|
||||||
onTruncatedChanged: {
|
|
||||||
// There is obviously a bug in QML in truncating text with images.
|
|
||||||
// We simply remove Emojis then...
|
|
||||||
if (truncated) {
|
|
||||||
text = text.replace(/\<img [^>]+\/\>/g, "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,53 +74,50 @@ Item {
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
text: qsTr("Recently used")
|
text: qsTr("Recently used")
|
||||||
}
|
}
|
||||||
Flickable {
|
|
||||||
|
SilicaGridView {
|
||||||
|
id: recentStickersGridView
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: recentStickersRow.height + Theme.paddingSmall
|
height: Theme.itemSizeExtraLarge
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
cellWidth: Theme.itemSizeExtraLarge;
|
||||||
contentWidth: recentStickersRow.width
|
cellHeight: Theme.itemSizeExtraLarge;
|
||||||
|
visible: count > 0
|
||||||
clip: true
|
clip: true
|
||||||
|
flow: GridView.FlowTopToBottom
|
||||||
|
|
||||||
Row {
|
model: stickerPickerOverlayItem.recentStickers
|
||||||
id: recentStickersRow
|
|
||||||
spacing: Theme.paddingMedium
|
|
||||||
Repeater {
|
|
||||||
model: stickerPickerOverlayItem.recentStickers
|
|
||||||
|
|
||||||
Item {
|
delegate: Item {
|
||||||
height: singleRecentStickerRow.height
|
width: recentStickersGridView.cellWidth
|
||||||
width: singleRecentStickerRow.width
|
height: recentStickersGridView.cellHeight
|
||||||
|
|
||||||
Row {
|
Image {
|
||||||
id: singleRecentStickerRow
|
source: modelData.thumbnail.photo.local.path
|
||||||
spacing: Theme.paddingSmall
|
anchors.fill: parent
|
||||||
Image {
|
asynchronous: true
|
||||||
source: modelData.thumbnail.photo.local.path
|
onStatusChanged: {
|
||||||
width: Theme.itemSizeExtraLarge
|
if (status === Image.Ready) {
|
||||||
height: Theme.itemSizeExtraLarge
|
stickerPickerLoadedTimer.restart();
|
||||||
asynchronous: true
|
|
||||||
onStatusChanged: {
|
|
||||||
if (status === Image.Ready) {
|
|
||||||
stickerPickerLoadedTimer.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
tdLibWrapper.sendStickerMessage(chatInformation.id, modelData.sticker.remote.id);
|
|
||||||
stickerPickerOverlayItem.visible = false;
|
|
||||||
attachmentOptionsRow.visible = false;
|
|
||||||
stickerPickerLoader.active = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
tdLibWrapper.sendStickerMessage(chatInformation.id, modelData.sticker.remote.id);
|
||||||
|
stickerPickerOverlayItem.visible = false;
|
||||||
|
attachmentOptionsRow.visible = false;
|
||||||
|
stickerPickerLoader.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HorizontalScrollDecorator {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: stickerPickerOverlayItem.installedStickerSets
|
model: stickerPickerOverlayItem.installedStickerSets
|
||||||
width: stickerPickerFlickable.width
|
width: stickerPickerFlickable.width
|
||||||
|
@ -136,57 +133,58 @@ Item {
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
text: modelData.title
|
text: modelData.title
|
||||||
}
|
}
|
||||||
Flickable {
|
|
||||||
|
SilicaGridView {
|
||||||
|
id: installedStickerSetGridView
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: installedStickerSetRow.height + Theme.paddingSmall
|
height: Theme.itemSizeExtraLarge
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
cellWidth: Theme.itemSizeExtraLarge;
|
||||||
contentWidth: installedStickerSetRow.width
|
cellHeight: Theme.itemSizeExtraLarge;
|
||||||
|
visible: count > 0
|
||||||
clip: true
|
clip: true
|
||||||
Row {
|
flow: GridView.FlowTopToBottom
|
||||||
id: installedStickerSetRow
|
|
||||||
spacing: Theme.paddingMedium
|
|
||||||
|
|
||||||
Repeater {
|
model: modelData.stickers
|
||||||
model: modelData.stickers
|
delegate: Item {
|
||||||
|
width: installedStickerSetGridView.cellWidth
|
||||||
|
height: installedStickerSetGridView.cellHeight
|
||||||
|
|
||||||
Item {
|
Image {
|
||||||
width: Theme.itemSizeExtraLarge
|
id: singleStickerImage
|
||||||
height: Theme.itemSizeExtraLarge
|
source: modelData.thumbnail.photo.local.is_downloading_completed ? modelData.thumbnail.photo.local.path : ""
|
||||||
Image {
|
anchors.fill: parent
|
||||||
id: singleStickerImage
|
visible: modelData.thumbnail.photo.local.is_downloading_completed
|
||||||
source: modelData.thumbnail.photo.local.is_downloading_completed ? modelData.thumbnail.photo.local.path : ""
|
asynchronous: true
|
||||||
anchors.fill: parent
|
onStatusChanged: {
|
||||||
visible: modelData.thumbnail.photo.local.is_downloading_completed
|
if (status === Image.Ready) {
|
||||||
asynchronous: true
|
stickerPickerLoadedTimer.restart();
|
||||||
onStatusChanged: {
|
|
||||||
if (status === Image.Ready) {
|
|
||||||
stickerPickerLoadedTimer.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
font.pixelSize: Theme.fontSizeHuge
|
|
||||||
color: Theme.primaryColor
|
|
||||||
anchors.fill: parent
|
|
||||||
maximumLineCount: 1
|
|
||||||
elide: Text.ElideRight
|
|
||||||
text: Emoji.emojify(modelData.emoji, font.pixelSize)
|
|
||||||
visible: !modelData.thumbnail.photo.local.is_downloading_completed
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
tdLibWrapper.sendStickerMessage(chatInformation.id, modelData.sticker.remote.id);
|
|
||||||
stickerPickerOverlayItem.visible = false;
|
|
||||||
attachmentOptionsRow.visible = false;
|
|
||||||
stickerPickerLoader.active = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Text {
|
||||||
|
font.pixelSize: Theme.fontSizeHuge
|
||||||
|
color: Theme.primaryColor
|
||||||
|
anchors.fill: parent
|
||||||
|
maximumLineCount: 1
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: Emoji.emojify(modelData.emoji, font.pixelSize)
|
||||||
|
visible: !modelData.thumbnail.photo.local.is_downloading_completed
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
tdLibWrapper.sendStickerMessage(chatInformation.id, modelData.sticker.remote.id);
|
||||||
|
stickerPickerOverlayItem.visible = false;
|
||||||
|
attachmentOptionsRow.visible = false;
|
||||||
|
stickerPickerLoader.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HorizontalScrollDecorator {}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,37 +17,29 @@
|
||||||
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
import Sailfish.Silica 1.0
|
import Sailfish.Silica 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|
||||||
id: stickerPreviewItem
|
|
||||||
|
|
||||||
property variant stickerData;
|
property variant stickerData;
|
||||||
property int usedFileId;
|
property int usedFileId;
|
||||||
|
|
||||||
width: stickerData.width + Theme.paddingSmall
|
width: stickerData.width
|
||||||
height: stickerData.height + Theme.paddingSmall
|
height: stickerData.height
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
updateSticker();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSticker() {
|
|
||||||
if (stickerData) {
|
if (stickerData) {
|
||||||
if (stickerData.is_animated) {
|
if (stickerData.is_animated) {
|
||||||
// Use thumbnail until we can decode TGS files
|
// Use thumbnail until we can decode TGS files
|
||||||
usedFileId = stickerData.thumbnail.photo.id;
|
usedFileId = stickerData.thumbnail.photo.id;
|
||||||
if (stickerData.thumbnail.photo.local.is_downloading_completed) {
|
if (stickerData.thumbnail.photo.local.is_downloading_completed) {
|
||||||
singleImage.source = stickerData.thumbnail.photo.local.path;
|
stickerImage.source = stickerData.thumbnail.photo.local.path;
|
||||||
} else {
|
} else {
|
||||||
tdLibWrapper.downloadFile(usedFileId);
|
tdLibWrapper.downloadFile(usedFileId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
usedFileId = stickerData.sticker.id;
|
usedFileId = stickerData.sticker.id;
|
||||||
if (stickerData.sticker.local.is_downloading_completed) {
|
if (stickerData.sticker.local.is_downloading_completed) {
|
||||||
singleImage.source = stickerData.sticker.local.path;
|
stickerImage.source = stickerData.sticker.local.path;
|
||||||
} else {
|
} else {
|
||||||
tdLibWrapper.downloadFile(usedFileId);
|
tdLibWrapper.downloadFile(usedFileId);
|
||||||
}
|
}
|
||||||
|
@ -65,45 +57,43 @@ Item {
|
||||||
} else {
|
} else {
|
||||||
stickerData.sticker = fileInformation;
|
stickerData.sticker = fileInformation;
|
||||||
}
|
}
|
||||||
singleImage.source = fileInformation.local.path;
|
stickerImage.source = fileInformation.local.path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: singleImage
|
id: stickerImage
|
||||||
width: ( ( parent.width - Theme.paddingSmall ) >= stickerData.width ) ? stickerData.width : ( parent.width - Theme.paddingSmall )
|
anchors.fill: parent
|
||||||
height: ( ( parent.height - Theme.paddingSmall ) >= stickerData.height ) ? stickerData.height : ( parent.height - Theme.paddingSmall )
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectCrop
|
|
||||||
autoTransform: true
|
|
||||||
asynchronous: true
|
|
||||||
visible: status === Image.Ready
|
|
||||||
opacity: status === Image.Ready ? 1 : 0
|
|
||||||
Behavior on opacity { NumberAnimation {} }
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
//pageStack.push(Qt.resolvedUrl("../pages/ImagePage.qml"), { "photoData" : imagePreviewItem.photoData, "pictureFileInformation" : imagePreviewItem.pictureFileInformation });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: imageLoadingBackgroundImage
|
|
||||||
source: "../../images/background-" + ( Theme.colorScheme ? "black" : "white" ) + "-small.png"
|
|
||||||
anchors {
|
|
||||||
centerIn: parent
|
|
||||||
}
|
|
||||||
width: ( ( parent.width - Theme.paddingSmall ) >= stickerData.width ) ? stickerData.width : ( parent.width - Theme.paddingSmall )
|
|
||||||
height: ( ( parent.height - Theme.paddingSmall ) >= stickerData.height ) ? stickerData.height : ( parent.height - Theme.paddingSmall )
|
|
||||||
visible: singleImage.status !== Image.Ready
|
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
opacity: 0.15
|
autoTransform: true
|
||||||
|
asynchronous: true
|
||||||
|
visible: opacity > 0
|
||||||
|
opacity: status === Image.Ready ? 1 : 0
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
sourceComponent: Component {
|
||||||
|
Image {
|
||||||
|
source: "../../images/background-" + ( Theme.colorScheme ? "black" : "white" ) + "-small.png"
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
active: opacity > 0
|
||||||
|
opacity: !stickerImage.visible && !placeHolderDelayTimer.running ? 0.15 : 0
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: placeHolderDelayTimer
|
||||||
|
interval: 1000
|
||||||
|
running: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ Page {
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
width: (aboutPage.isPortrait ? aboutPage.width : aboutPage.height) / 2
|
width: Math.min(2 * Theme.itemSizeHuge, Math.min(aboutPage.width, aboutPage.height) / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
|
|
@ -43,6 +43,7 @@ Page {
|
||||||
property variant chatPartnerInformation;
|
property variant chatPartnerInformation;
|
||||||
property variant chatGroupInformation;
|
property variant chatGroupInformation;
|
||||||
property int chatOnlineMemberCount: 0;
|
property int chatOnlineMemberCount: 0;
|
||||||
|
property variant emojiProposals;
|
||||||
|
|
||||||
function updateChatPartnerStatusText() {
|
function updateChatPartnerStatusText() {
|
||||||
if (chatPartnerInformation.status['@type'] === "userStatusEmpty" ) {
|
if (chatPartnerInformation.status['@type'] === "userStatusEmpty" ) {
|
||||||
|
@ -174,6 +175,46 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
controlSendButton();
|
controlSendButton();
|
||||||
|
newMessageInReplyToRow.inReplyToMessage = null;
|
||||||
|
newMessageColumn.editMessageId = "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWordBoundaries(text, cursorPosition) {
|
||||||
|
var wordBoundaries = { beginIndex : 0, endIndex : text.length};
|
||||||
|
var currentIndex = 0;
|
||||||
|
for (currentIndex = (cursorPosition - 1); currentIndex > 0; currentIndex--) {
|
||||||
|
if (text.charAt(currentIndex) === ' ') {
|
||||||
|
wordBoundaries.beginIndex = currentIndex + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (currentIndex = cursorPosition; currentIndex < text.length; currentIndex++) {
|
||||||
|
if (text.charAt(currentIndex) === ' ') {
|
||||||
|
wordBoundaries.endIndex = currentIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wordBoundaries;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMessageTextReplacement(text, cursorPosition) {
|
||||||
|
var wordBoundaries = getWordBoundaries(text, cursorPosition);
|
||||||
|
|
||||||
|
var currentWord = text.substring(wordBoundaries.beginIndex, wordBoundaries.endIndex);
|
||||||
|
if (currentWord.length > 1 && currentWord.charAt(0) === ':') {
|
||||||
|
tdLibWrapper.searchEmoji(currentWord.substring(1));
|
||||||
|
} else {
|
||||||
|
chatPage.emojiProposals = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceMessageText(text, cursorPosition, newText) {
|
||||||
|
var wordBoundaries = getWordBoundaries(text, cursorPosition);
|
||||||
|
var newCompleteText = text.substring(0, wordBoundaries.beginIndex) + newText + " " + text.substring(wordBoundaries.endIndex);
|
||||||
|
var newIndex = wordBoundaries.beginIndex + newText.length + 1;
|
||||||
|
newMessageTextField.text = newCompleteText;
|
||||||
|
newMessageTextField.cursorPosition = newIndex;
|
||||||
|
lostFocusTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
@ -227,6 +268,9 @@ Page {
|
||||||
uploadingProgressBar.maximumValue = fileInformation.size;
|
uploadingProgressBar.maximumValue = fileInformation.size;
|
||||||
uploadingProgressBar.value = fileInformation.remote.uploaded_size;
|
uploadingProgressBar.value = fileInformation.remote.uploaded_size;
|
||||||
}
|
}
|
||||||
|
onEmojiSearchSuccessful: {
|
||||||
|
chatPage.emojiProposals = result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
@ -269,6 +313,26 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: lostFocusTimer
|
||||||
|
interval: 200
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
newMessageTextField.forceActiveFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: textReplacementTimer
|
||||||
|
interval: 600
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
handleMessageTextReplacement(newMessageTextField.text, newMessageTextField.cursorPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: chatContactTimeUpdater
|
id: chatContactTimeUpdater
|
||||||
interval: 60000
|
interval: 60000
|
||||||
|
@ -1026,6 +1090,12 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editable: true
|
||||||
|
|
||||||
|
onClearRequested: {
|
||||||
|
newMessageInReplyToRow.inReplyToMessage = null;
|
||||||
|
}
|
||||||
|
|
||||||
id: newMessageInReplyToRow
|
id: newMessageInReplyToRow
|
||||||
myUserId: chatPage.myUserId
|
myUserId: chatPage.myUserId
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
@ -1151,15 +1221,83 @@ Page {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Column {
|
||||||
|
id: emojiColumn
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: emojiProposals ? ( emojiProposals.length > 0 ? true : false ) : false
|
||||||
|
opacity: emojiProposals ? ( emojiProposals.length > 0 ? 1 : 0 ) : 0
|
||||||
|
Behavior on opacity { NumberAnimation {} }
|
||||||
|
spacing: Theme.paddingMedium
|
||||||
|
|
||||||
id: editMessageText
|
Flickable {
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
width: parent.width
|
||||||
font.bold: true
|
height: emojiResultRow.height + Theme.paddingSmall
|
||||||
text: qsTr("Edit Message")
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
color: Theme.secondaryColor
|
contentWidth: emojiResultRow.width
|
||||||
|
clip: true
|
||||||
|
Row {
|
||||||
|
id: emojiResultRow
|
||||||
|
spacing: Theme.paddingMedium
|
||||||
|
Repeater {
|
||||||
|
model: emojiProposals
|
||||||
|
|
||||||
|
Item {
|
||||||
|
height: singleEmojiRow.height
|
||||||
|
width: singleEmojiRow.width
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: singleEmojiRow
|
||||||
|
spacing: Theme.paddingSmall
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: emojiPicture
|
||||||
|
source: "../js/emoji/" + modelData.file_name
|
||||||
|
width: Theme.fontSizeLarge
|
||||||
|
height: Theme.fontSizeLarge
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
replaceMessageText(newMessageTextField.text, newMessageTextField.cursorPosition, modelData.emoji);
|
||||||
|
emojiProposals = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.paddingSmall
|
||||||
visible: newMessageColumn.editMessageId !== "0"
|
visible: newMessageColumn.editMessageId !== "0"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
width: parent.width - Theme.paddingSmall - removeEditMessageIconButton.width
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
id: editMessageText
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.bold: true
|
||||||
|
text: qsTr("Edit Message")
|
||||||
|
color: Theme.secondaryColor
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
id: removeEditMessageIconButton
|
||||||
|
icon.source: "image://theme/icon-m-clear"
|
||||||
|
onClicked: {
|
||||||
|
newMessageColumn.editMessageId = "0";
|
||||||
|
newMessageTextField.text = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
@ -1177,15 +1315,6 @@ Page {
|
||||||
labelVisible: false
|
labelVisible: false
|
||||||
textLeftMargin: 0
|
textLeftMargin: 0
|
||||||
textTopMargin: 0
|
textTopMargin: 0
|
||||||
onFocusChanged: {
|
|
||||||
if (!focus) {
|
|
||||||
newMessageInReplyToRow.inReplyToMessage = null;
|
|
||||||
if (newMessageColumn.editMessageId !== "0") {
|
|
||||||
newMessageColumn.editMessageId = "0";
|
|
||||||
newMessageTextField.text = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EnterKey.onClicked: {
|
EnterKey.onClicked: {
|
||||||
if (appSettings.sendByEnter) {
|
if (appSettings.sendByEnter) {
|
||||||
sendMessage();
|
sendMessage();
|
||||||
|
@ -1199,6 +1328,7 @@ Page {
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
controlSendButton();
|
controlSendButton();
|
||||||
|
textReplacementTimer.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import QtQuick 2.6
|
import QtQuick 2.0
|
||||||
import Sailfish.Silica 1.0
|
import Sailfish.Silica 1.0
|
||||||
import WerkWolf.Fernschreiber 1.0
|
import WerkWolf.Fernschreiber 1.0
|
||||||
|
|
||||||
|
@ -29,11 +29,6 @@ Page {
|
||||||
property int authorizationState: TelegramAPI.Closed
|
property int authorizationState: TelegramAPI.Closed
|
||||||
property var authorizationStateData: null
|
property var authorizationStateData: null
|
||||||
|
|
||||||
BusyLabel {
|
|
||||||
text: qsTr("Loading...")
|
|
||||||
running: initializationPage.loading
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
initializationPage.authorizationState = tdLibWrapper.getAuthorizationState();
|
initializationPage.authorizationState = tdLibWrapper.getAuthorizationState();
|
||||||
initializationPage.authorizationStateData = tdLibWrapper.getAuthorizationStateData();
|
initializationPage.authorizationStateData = tdLibWrapper.getAuthorizationStateData();
|
||||||
|
@ -44,6 +39,7 @@ Page {
|
||||||
initializationPage.loading = false;
|
initializationPage.loading = false;
|
||||||
welcomeColumn.visible = false;
|
welcomeColumn.visible = false;
|
||||||
enterPinColumn.visible = true;
|
enterPinColumn.visible = true;
|
||||||
|
enterPinField.focus = true
|
||||||
enterPasswordColumn.visible = false;
|
enterPasswordColumn.visible = false;
|
||||||
waitRegistrationColumn.visible = false;
|
waitRegistrationColumn.visible = false;
|
||||||
break;
|
break;
|
||||||
|
@ -60,6 +56,7 @@ Page {
|
||||||
enterPinColumn.visible = false;
|
enterPinColumn.visible = false;
|
||||||
enterPasswordColumn.visible = false;
|
enterPasswordColumn.visible = false;
|
||||||
waitRegistrationColumn.visible = true;
|
waitRegistrationColumn.visible = true;
|
||||||
|
pageHeader.title = qsTr("User Registration")
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Nothing ;)
|
// Nothing ;)
|
||||||
|
@ -73,6 +70,7 @@ Page {
|
||||||
case TelegramAPI.WaitCode:
|
case TelegramAPI.WaitCode:
|
||||||
initializationPage.loading = false;
|
initializationPage.loading = false;
|
||||||
enterPinColumn.visible = true;
|
enterPinColumn.visible = true;
|
||||||
|
enterPinField.focus = true
|
||||||
enterPasswordColumn.visible = false;
|
enterPasswordColumn.visible = false;
|
||||||
waitRegistrationColumn.visible = false;
|
waitRegistrationColumn.visible = false;
|
||||||
break;
|
break;
|
||||||
|
@ -103,21 +101,21 @@ Page {
|
||||||
|
|
||||||
SilicaFlickable {
|
SilicaFlickable {
|
||||||
id: welcomeFlickable
|
id: welcomeFlickable
|
||||||
contentHeight: contentItem.childrenRect.height
|
contentHeight: content.height
|
||||||
Behavior on contentHeight { NumberAnimation {} }
|
Behavior on contentHeight { NumberAnimation {} }
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: welcomeColumn
|
id: content
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.paddingSmall
|
spacing: Theme.paddingLarge
|
||||||
|
|
||||||
PageHeader {
|
PageHeader {
|
||||||
|
id: pageHeader
|
||||||
title: qsTr("Welcome to Fernschreiber!")
|
title: qsTr("Welcome to Fernschreiber!")
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: fernschreiberImage
|
|
||||||
source: "../../images/fernschreiber.png"
|
source: "../../images/fernschreiber.png"
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
@ -125,374 +123,315 @@ Page {
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
width: 1/2 * parent.width
|
width: Math.min(2 * Theme.itemSizeHuge, Math.min(Screen.width, Screen.height) / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
BusyLabel {
|
||||||
id: enterPhoneNumberLabel
|
text: qsTr("Loading...")
|
||||||
wrapMode: Text.Wrap
|
running: initializationPage.loading
|
||||||
x: Theme.horizontalPageMargin
|
Behavior on opacity { FadeAnimation {} }
|
||||||
width: parent.width - ( 2 * Theme.horizontalPageMargin )
|
visible: opacity > 0
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: qsTr("Please enter your phone number to continue.")
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
Column {
|
||||||
id: phoneNumberTextField
|
id: welcomeColumn
|
||||||
placeholderText: "Use the international format, e.g. +4912342424242"
|
|
||||||
inputMethodHints: Qt.ImhDialableCharactersOnly
|
|
||||||
labelVisible: false
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
spacing: Theme.paddingLarge
|
||||||
|
|
||||||
Button {
|
Behavior on opacity { FadeAnimation {} }
|
||||||
id: continueWithPhoneNumberButton
|
opacity: visible ? 1 : 0
|
||||||
text: qsTr("Continue")
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
enabled: phoneNumberTextField.text.match(/\+[1-9][0-9]{4,}/g)
|
|
||||||
onClicked: {
|
|
||||||
initializationPage.loading = true;
|
|
||||||
welcomeColumn.visible = false;
|
|
||||||
tdLibWrapper.setAuthenticationPhoneNumber(phoneNumberTextField.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
InfoLabel {
|
||||||
|
text: qsTr("Please enter your phone number to continue.")
|
||||||
Column {
|
|
||||||
id: pinErrorColumn
|
|
||||||
// y: ( parent.height - ( errorInfoLabel.height + fernschreiberErrorImage.height + errorOkButton.height + ( 3 * Theme.paddingSmall ) ) ) / 2
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.paddingSmall
|
|
||||||
|
|
||||||
Behavior on opacity { NumberAnimation {} }
|
|
||||||
opacity: visible ? 1 : 0
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: fernschreiberErrorImage
|
|
||||||
source: "../../images/fernschreiber.png"
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
TextField {
|
||||||
asynchronous: true
|
id: phoneNumberTextField
|
||||||
width: 1/2 * parent.width
|
placeholderText: "Use the international format, e.g. +4912342424242"
|
||||||
}
|
inputMethodHints: Qt.ImhDialableCharactersOnly
|
||||||
|
labelVisible: false
|
||||||
InfoLabel {
|
width: parent.width
|
||||||
id: errorInfoLabel
|
readonly property bool validInput: text.match(/\+[1-9][0-9]{4,}/g)
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
EnterKey.iconSource: "image://theme/icon-m-enter-next"
|
||||||
text: ""
|
EnterKey.enabled: validInput
|
||||||
}
|
EnterKey.onClicked: {
|
||||||
|
validator = filledValidator
|
||||||
Button {
|
if(acceptableInput) {
|
||||||
id: errorOkButton
|
continueWithPhoneNumberButton.focus = true
|
||||||
text: qsTr("OK")
|
continueWithPhoneNumberButton.enter()
|
||||||
anchors {
|
}
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
pinErrorColumn.visible = false;
|
|
||||||
welcomeColumn.visible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: enterPasswordColumn
|
|
||||||
// y: ( parent.height - ( fernschreiberPasswordImage.height + enterPasswordLabel.height + enterPasswordField.height + enterPasswordButton.height + ( 3 * Theme.paddingSmall ) ) ) / 2
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.paddingSmall
|
|
||||||
|
|
||||||
Behavior on opacity { NumberAnimation {} }
|
|
||||||
opacity: visible ? 1.0 : 0.0
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: fernschreiberPasswordImage
|
|
||||||
source: "../../images/fernschreiber.png"
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
asynchronous: true
|
|
||||||
width: 1/2 * parent.width
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoLabel {
|
|
||||||
id: enterPasswordLabel
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
text: qsTr("Please enter your password:")
|
|
||||||
}
|
|
||||||
|
|
||||||
PasswordField {
|
|
||||||
id: enterPasswordField
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
|
||||||
horizontalAlignment: TextInput.AlignHCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: enterPasswordButton
|
|
||||||
text: qsTr("OK")
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
initializationPage.loading = true;
|
|
||||||
enterPasswordColumn.visible = false;
|
|
||||||
tdLibWrapper.setAuthenticationPassword(enterPasswordField.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: enterPinColumn
|
|
||||||
topPadding: Theme.paddingLarge
|
|
||||||
bottomPadding: Theme.paddingLarge
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.paddingSmall
|
|
||||||
|
|
||||||
Behavior on opacity { NumberAnimation {} }
|
|
||||||
opacity: visible ? 1.0 : 0.0
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: fernschreiberPinImage
|
|
||||||
source: "../../images/fernschreiber.png"
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
asynchronous: true
|
|
||||||
width: 1/2 * parent.width
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoLabel {
|
|
||||||
id: enterPinLabel
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
text: qsTr("Please enter the code that you received:")
|
|
||||||
}
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
id: enterPinField
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
inputMethodHints: Qt.ImhDigitsOnly
|
|
||||||
font.pixelSize: Theme.fontSizeExtraLarge
|
|
||||||
width: parent.width - 4 * Theme.paddingLarge
|
|
||||||
horizontalAlignment: TextInput.AlignHCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: enterPinButton
|
|
||||||
text: qsTr("OK")
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
initializationPage.loading = true;
|
|
||||||
enterPinColumn.visible = false;
|
|
||||||
tdLibWrapper.setAuthenticationCode(enterPinField.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: linkingErrorColumn
|
|
||||||
topPadding: Theme.paddingLarge
|
|
||||||
bottomPadding: Theme.paddingLarge
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.paddingSmall
|
|
||||||
|
|
||||||
Behavior on opacity { NumberAnimation {} }
|
|
||||||
opacity: visible ? 1.0 : 0.0
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: fernschreiberLinkingErrorImage
|
|
||||||
source: "../../images/fernschreiber.png"
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
asynchronous: true
|
|
||||||
width: 1/2 * parent.width
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoLabel {
|
|
||||||
id: linkingErrorInfoLabel
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
text: qsTr("Unable to authenticate you with the entered code.")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: enterPinAgainButton
|
|
||||||
text: qsTr("Enter code again")
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
linkingErrorColumn.visible = false;
|
|
||||||
enterPinColumn.visible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: restartAuthenticationButton
|
|
||||||
text: qsTr("Restart authentication")
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
linkingErrorColumn.visible = false;
|
|
||||||
welcomeColumn.visible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: waitRegistrationColumn
|
|
||||||
topPadding: Theme.paddingLarge
|
|
||||||
bottomPadding: Theme.paddingLarge
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.paddingLarge
|
|
||||||
|
|
||||||
Behavior on opacity { NumberAnimation {} }
|
|
||||||
opacity: visible ? 1.0 : 0.0
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
PageHeader {
|
|
||||||
title: qsTr("User Registration")
|
|
||||||
}
|
|
||||||
Image {
|
|
||||||
id: waitRegistrationImage
|
|
||||||
source: "../../images/fernschreiber.png"
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
asynchronous: true
|
|
||||||
width: 1/2 * Screen.width
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoLabel {
|
|
||||||
id: waitRegistrationInfoLabel
|
|
||||||
property bool acknowledged
|
|
||||||
font.pixelSize: Theme.fontSizeExtraSmall
|
|
||||||
textFormat: Text.StyledText
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
linkColor: Theme.primaryColor
|
|
||||||
property var stateText: initializationPage.authorizationStateData.authorization_state && initializationPage.authorizationStateData.authorization_state.terms_of_service ?
|
|
||||||
initializationPage.authorizationStateData.authorization_state.terms_of_service.text :
|
|
||||||
null
|
|
||||||
visible: !!stateText && !acknowledged
|
|
||||||
text: {
|
|
||||||
if(!stateText) {
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
var entities = stateText.entities;
|
}
|
||||||
if(entities && entities.length > 0 && entities[0]["type"]["@type"] === "textEntityTypeTextUrl") { //we just use the first entity for now.
|
|
||||||
var offset = entities[0].offset;
|
Button {
|
||||||
var length = entities[0].length;
|
id: continueWithPhoneNumberButton
|
||||||
return (stateText.text.slice(0,entities[0].offset)
|
text: qsTr("Continue")
|
||||||
+ "<a href=\""+entities[0]["type"]["url"]+"\">"
|
anchors {
|
||||||
+ stateText.text.slice(entities[0].offset, entities[0].offset + entities[0].length)
|
horizontalCenter: parent.horizontalCenter
|
||||||
+ '</a>'
|
}
|
||||||
+ stateText.text.slice(entities[0].offset + entities[0].length)).replace(/\n/gm, "<br>");
|
enabled: phoneNumberTextField.validInput
|
||||||
|
onClicked: enter()
|
||||||
|
function enter() {
|
||||||
|
initializationPage.loading = true;
|
||||||
|
welcomeColumn.visible = false;
|
||||||
|
tdLibWrapper.setAuthenticationPhoneNumber(phoneNumberTextField.text);
|
||||||
}
|
}
|
||||||
return stateText.text.replace(/\n/gm, "<br>");
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
//JSON.stringify(initializationPage.authorizationStateData, null, 2)//qsTr("Unable to authenticate you with the entered code.")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: acknowledgeTOCButton
|
|
||||||
visible: waitRegistrationInfoLabel.visible
|
|
||||||
text: qsTr("OK")
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
waitRegistrationInfoLabel.acknowledged = true;
|
|
||||||
userFirstNameTextField.focus = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RegExpValidator {
|
|
||||||
id: filledValidator
|
Column {
|
||||||
regExp: /.+/
|
id: pinErrorColumn
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: userFirstNameTextField
|
|
||||||
visible: !waitRegistrationInfoLabel.visible
|
|
||||||
opacity: visible ? 1.0 : 0.0
|
|
||||||
Behavior on opacity { NumberAnimation {} }
|
|
||||||
placeholderText: qsTr("Enter your First Name")
|
|
||||||
labelVisible: false
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
EnterKey.iconSource: !!text ? "image://theme/icon-m-enter-next" : "image://theme/icon-m-text-input"
|
spacing: Theme.paddingLarge
|
||||||
EnterKey.onClicked: {
|
|
||||||
validator = filledValidator
|
|
||||||
if(acceptableInput) userLastNameTextField.focus = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: userLastNameTextField
|
|
||||||
visible: !waitRegistrationInfoLabel.visible
|
|
||||||
opacity: visible ? 1.0 : 0.0
|
|
||||||
Behavior on opacity { NumberAnimation {} }
|
|
||||||
placeholderText: qsTr("Enter your Last Name")
|
|
||||||
labelVisible: false
|
|
||||||
width: parent.width
|
|
||||||
EnterKey.iconSource: !!text ? "image://theme/icon-m-enter-accept" : "image://theme/icon-m-text-input"
|
|
||||||
EnterKey.onClicked: {
|
|
||||||
validator = filledValidator
|
|
||||||
if(acceptableInput) registerUserButton.onClicked(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
id: registerUserButton
|
|
||||||
visible: !waitRegistrationInfoLabel.visible
|
|
||||||
opacity: visible ? 1.0 : 0.0
|
|
||||||
Behavior on opacity { NumberAnimation {} }
|
|
||||||
text: qsTr("Register User")
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
userFirstNameTextField.validator = filledValidator
|
|
||||||
userLastNameTextField.validator = filledValidator
|
|
||||||
if(userFirstNameTextField.acceptableInput && userLastNameTextField.acceptableInput) {
|
|
||||||
tdLibWrapper.registerUser(userFirstNameTextField.text, userLastNameTextField.text);
|
|
||||||
} else if(!userFirstNameTextField.acceptableInput) {
|
|
||||||
userFirstNameTextField.focus = true;
|
|
||||||
} else {
|
|
||||||
userLastNameTextField.focus = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
opacity: visible ? 1 : 0
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
InfoLabel {
|
||||||
|
id: errorInfoLabel
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
text: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: qsTr("OK")
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
pinErrorColumn.visible = false;
|
||||||
|
welcomeColumn.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: enterPasswordColumn
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.paddingLarge
|
||||||
|
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
opacity: visible ? 1.0 : 0.0
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
InfoLabel {
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
text: qsTr("Please enter your password:")
|
||||||
|
}
|
||||||
|
|
||||||
|
PasswordField {
|
||||||
|
id: enterPasswordField
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||||
|
horizontalAlignment: TextInput.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: qsTr("OK")
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
initializationPage.loading = true;
|
||||||
|
enterPasswordColumn.visible = false;
|
||||||
|
tdLibWrapper.setAuthenticationPassword(enterPasswordField.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: enterPinColumn
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.paddingLarge
|
||||||
|
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
opacity: visible ? 1.0 : 0.0
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
InfoLabel {
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
text: qsTr("Please enter the code that you received:")
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: enterPinField
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
inputMethodHints: Qt.ImhDigitsOnly
|
||||||
|
font.pixelSize: Theme.fontSizeExtraLarge
|
||||||
|
width: parent.width - 4 * Theme.paddingLarge
|
||||||
|
horizontalAlignment: TextInput.AlignHCenter
|
||||||
|
EnterKey.iconSource: "image://theme/icon-m-enter-next"
|
||||||
|
EnterKey.enabled: text.length > 0
|
||||||
|
EnterKey.onClicked: enterPinButton.enter()
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: enterPinButton
|
||||||
|
text: qsTr("OK")
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
onClicked: enter()
|
||||||
|
function enter() {
|
||||||
|
initializationPage.loading = true;
|
||||||
|
enterPinColumn.visible = false;
|
||||||
|
tdLibWrapper.setAuthenticationCode(enterPinField.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: linkingErrorColumn
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.paddingLarge
|
||||||
|
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
opacity: visible ? 1.0 : 0.0
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
InfoLabel {
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
text: qsTr("Unable to authenticate you with the entered code.")
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: qsTr("Enter code again")
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
linkingErrorColumn.visible = false;
|
||||||
|
enterPinColumn.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: qsTr("Restart authentication")
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
linkingErrorColumn.visible = false;
|
||||||
|
welcomeColumn.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: waitRegistrationColumn
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.paddingLarge
|
||||||
|
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
opacity: visible ? 1.0 : 0.0
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
InfoLabel {
|
||||||
|
id: waitRegistrationInfoLabel
|
||||||
|
property bool acknowledged
|
||||||
|
font.pixelSize: Theme.fontSizeExtraSmall
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
linkColor: Theme.primaryColor
|
||||||
|
property var stateText: initializationPage.authorizationStateData.authorization_state && initializationPage.authorizationStateData.authorization_state.terms_of_service ?
|
||||||
|
initializationPage.authorizationStateData.authorization_state.terms_of_service.text :
|
||||||
|
null
|
||||||
|
visible: !!stateText && !acknowledged
|
||||||
|
text: {
|
||||||
|
if(!stateText) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
var entities = stateText.entities;
|
||||||
|
if(entities && entities.length > 0 && entities[0]["type"]["@type"] === "textEntityTypeTextUrl") { //we just use the first entity for now.
|
||||||
|
var offset = entities[0].offset;
|
||||||
|
var length = entities[0].length;
|
||||||
|
return (stateText.text.slice(0,entities[0].offset)
|
||||||
|
+ "<a href=\""+entities[0]["type"]["url"]+"\">"
|
||||||
|
+ stateText.text.slice(entities[0].offset, entities[0].offset + entities[0].length)
|
||||||
|
+ '</a>'
|
||||||
|
+ stateText.text.slice(entities[0].offset + entities[0].length)).replace(/\n/gm, "<br>");
|
||||||
|
}
|
||||||
|
return stateText.text.replace(/\n/gm, "<br>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
visible: waitRegistrationInfoLabel.visible
|
||||||
|
text: qsTr("OK")
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
waitRegistrationInfoLabel.acknowledged = true;
|
||||||
|
userFirstNameTextField.focus = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RegExpValidator {
|
||||||
|
id: filledValidator
|
||||||
|
regExp: /.+/
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: userFirstNameTextField
|
||||||
|
visible: !waitRegistrationInfoLabel.visible
|
||||||
|
opacity: visible ? 1.0 : 0.0
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
placeholderText: qsTr("Enter your First Name")
|
||||||
|
labelVisible: false
|
||||||
|
width: parent.width
|
||||||
|
EnterKey.iconSource: !!text ? "image://theme/icon-m-enter-next" : "image://theme/icon-m-text-input"
|
||||||
|
EnterKey.onClicked: {
|
||||||
|
validator = filledValidator
|
||||||
|
if(acceptableInput) userLastNameTextField.focus = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: userLastNameTextField
|
||||||
|
visible: !waitRegistrationInfoLabel.visible
|
||||||
|
opacity: visible ? 1.0 : 0.0
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
placeholderText: qsTr("Enter your Last Name")
|
||||||
|
labelVisible: false
|
||||||
|
width: parent.width
|
||||||
|
EnterKey.iconSource: !!text ? "image://theme/icon-m-enter-accept" : "image://theme/icon-m-text-input"
|
||||||
|
EnterKey.onClicked: {
|
||||||
|
validator = filledValidator
|
||||||
|
if(acceptableInput) registerUserButton.onClicked(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
id: registerUserButton
|
||||||
|
visible: !waitRegistrationInfoLabel.visible
|
||||||
|
opacity: visible ? 1.0 : 0.0
|
||||||
|
Behavior on opacity { FadeAnimation {} }
|
||||||
|
text: qsTr("Register User")
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
userFirstNameTextField.validator = filledValidator
|
||||||
|
userLastNameTextField.validator = filledValidator
|
||||||
|
if(userFirstNameTextField.acceptableInput && userLastNameTextField.acceptableInput) {
|
||||||
|
tdLibWrapper.registerUser(userFirstNameTextField.text, userLastNameTextField.text);
|
||||||
|
} else if(!userFirstNameTextField.acceptableInput) {
|
||||||
|
userFirstNameTextField.focus = true;
|
||||||
|
} else {
|
||||||
|
userLastNameTextField.focus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 1
|
||||||
|
height: Theme.paddingLarge
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
*/
|
*/
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import Sailfish.Silica 1.0
|
import Sailfish.Silica 1.0
|
||||||
|
import WerkWolf.Fernschreiber 1.0
|
||||||
import "../js/functions.js" as Functions
|
import "../js/functions.js" as Functions
|
||||||
|
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
id: settingsPage
|
id: settingsPage
|
||||||
allowedOrientations: Orientation.All
|
allowedOrientations: Orientation.All
|
||||||
|
@ -52,6 +52,57 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ComboBox {
|
||||||
|
id: feedbackComboBox
|
||||||
|
label: qsTr("Notification feedback")
|
||||||
|
description: qsTr("Use non-graphical feedback (sound, vibration) for notifications")
|
||||||
|
menu: ContextMenu {
|
||||||
|
id: feedbackMenu
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
readonly property int value: AppSettings.NotificationFeedbackAll
|
||||||
|
text: qsTr("All events")
|
||||||
|
onClicked: {
|
||||||
|
appSettings.notificationFeedback = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
readonly property int value: AppSettings.NotificationFeedbackNew
|
||||||
|
text: qsTr("Only new events")
|
||||||
|
onClicked: {
|
||||||
|
appSettings.notificationFeedback = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
readonly property int value: AppSettings.NotificationFeedbackNone
|
||||||
|
text: qsTr("None")
|
||||||
|
onClicked: {
|
||||||
|
appSettings.notificationFeedback = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: updateFeedbackSelection()
|
||||||
|
|
||||||
|
function updateFeedbackSelection() {
|
||||||
|
var menuItems = feedbackMenu.children
|
||||||
|
var n = menuItems.length
|
||||||
|
for (var i=0; i<n; i++) {
|
||||||
|
if (menuItems[i].value === appSettings.notificationFeedback) {
|
||||||
|
currentIndex = i
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: appSettings
|
||||||
|
onNotificationFeedbackChanged: {
|
||||||
|
feedbackComboBox.updateFeedbackSelection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SectionHeader {
|
SectionHeader {
|
||||||
text: qsTr("Appearance")
|
text: qsTr("Appearance")
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,3 +25,19 @@
|
||||||
- Differentiate text between other people and current user ("You have..." vs. "Somebody has...")
|
- Differentiate text between other people and current user ("You have..." vs. "Somebody has...")
|
||||||
- Support for the Jolla Tablet
|
- Support for the Jolla Tablet
|
||||||
- New translations (Chinese, Hungarian, Polish, Spanish) - thanks to dashinfantry, edp17, atlochowski, GNUuser
|
- New translations (Chinese, Hungarian, Polish, Spanish) - thanks to dashinfantry, edp17, atlochowski, GNUuser
|
||||||
|
|
||||||
|
* Sun Oct 18 2020 Sebastian J. Wolf <sebastian@ygriega.de> 0.2
|
||||||
|
- Support for sending stickers
|
||||||
|
- Search for emojis from message input field, use :<keyword> to start searching
|
||||||
|
- New icon/logo - thanks to iamnomeutente
|
||||||
|
- Option to customize non-graphical notification feedback - thanks to monich
|
||||||
|
- Add user registration - thanks to jgibbon
|
||||||
|
- Don't show irrelevant groups - thanks to monich
|
||||||
|
- Display information about forwarded messages
|
||||||
|
- Option to avoid displaying stickers like images - thanks to monich
|
||||||
|
- Fix: Don't reset edit/in-reply-to mode after focus change
|
||||||
|
- Fix: Display caption for documents
|
||||||
|
- Fix: Reserve some space for lazy loaded elements
|
||||||
|
- Fix: Don't display error message in case of repeated download of the same file
|
||||||
|
- New translations (Finnish, Italian, Russian), thanks to jorm1s, iamnomeutente, arustg and monich
|
||||||
|
- That was quite a lot - I hope I didn't forget anything. If I did, big sorry and please let me know!
|
||||||
|
|
|
@ -24,6 +24,7 @@ BuildRequires: pkgconfig(Qt5Core)
|
||||||
BuildRequires: pkgconfig(Qt5Qml)
|
BuildRequires: pkgconfig(Qt5Qml)
|
||||||
BuildRequires: pkgconfig(Qt5Quick)
|
BuildRequires: pkgconfig(Qt5Quick)
|
||||||
BuildRequires: pkgconfig(Qt5DBus)
|
BuildRequires: pkgconfig(Qt5DBus)
|
||||||
|
BuildRequires: pkgconfig(Qt5Sql)
|
||||||
BuildRequires: pkgconfig(nemonotifications-qt5)
|
BuildRequires: pkgconfig(nemonotifications-qt5)
|
||||||
BuildRequires: pkgconfig(ngf-qt5)
|
BuildRequires: pkgconfig(ngf-qt5)
|
||||||
BuildRequires: desktop-file-utils
|
BuildRequires: desktop-file-utils
|
||||||
|
|
|
@ -24,6 +24,7 @@ PkgConfigBR:
|
||||||
- Qt5Qml
|
- Qt5Qml
|
||||||
- Qt5Quick
|
- Qt5Quick
|
||||||
- Qt5DBus
|
- Qt5DBus
|
||||||
|
- Qt5Sql
|
||||||
- nemonotifications-qt5
|
- nemonotifications-qt5
|
||||||
- ngf-qt5
|
- ngf-qt5
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
namespace {
|
namespace {
|
||||||
const QString KEY_SEND_BY_ENTER("sendByEnter");
|
const QString KEY_SEND_BY_ENTER("sendByEnter");
|
||||||
const QString KEY_SHOW_STICKERS_AS_IMAGES("showStickersAsImages");
|
const QString KEY_SHOW_STICKERS_AS_IMAGES("showStickersAsImages");
|
||||||
|
const QString KEY_NOTIFICATION_FEEDBACK("notificationFeedback");
|
||||||
}
|
}
|
||||||
|
|
||||||
AppSettings::AppSettings(QObject *parent) : QObject(parent), settings("harbour-fernschreiber", "settings")
|
AppSettings::AppSettings(QObject *parent) : QObject(parent), settings("harbour-fernschreiber", "settings")
|
||||||
|
@ -56,3 +57,17 @@ void AppSettings::setShowStickersAsImages(bool showAsImages)
|
||||||
emit showStickersAsImagesChanged();
|
emit showStickersAsImagesChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppSettings::NotificationFeedback AppSettings::notificationFeedback() const
|
||||||
|
{
|
||||||
|
return (NotificationFeedback) settings.value(KEY_NOTIFICATION_FEEDBACK, (int) NotificationFeedbackAll).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppSettings::setNotificationFeedback(NotificationFeedback feedback)
|
||||||
|
{
|
||||||
|
if (notificationFeedback() != feedback) {
|
||||||
|
LOG(KEY_NOTIFICATION_FEEDBACK << feedback);
|
||||||
|
settings.setValue(KEY_NOTIFICATION_FEEDBACK, (int) feedback);
|
||||||
|
emit notificationFeedbackChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,15 @@ class AppSettings : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool sendByEnter READ getSendByEnter WRITE setSendByEnter NOTIFY sendByEnterChanged)
|
Q_PROPERTY(bool sendByEnter READ getSendByEnter WRITE setSendByEnter NOTIFY sendByEnterChanged)
|
||||||
Q_PROPERTY(bool showStickersAsImages READ showStickersAsImages WRITE setShowStickersAsImages NOTIFY showStickersAsImagesChanged)
|
Q_PROPERTY(bool showStickersAsImages READ showStickersAsImages WRITE setShowStickersAsImages NOTIFY showStickersAsImagesChanged)
|
||||||
|
Q_PROPERTY(NotificationFeedback notificationFeedback READ notificationFeedback WRITE setNotificationFeedback NOTIFY notificationFeedbackChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum NotificationFeedback {
|
||||||
|
NotificationFeedbackNone,
|
||||||
|
NotificationFeedbackNew,
|
||||||
|
NotificationFeedbackAll
|
||||||
|
};
|
||||||
|
Q_ENUM(NotificationFeedback)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AppSettings(QObject *parent = Q_NULLPTR);
|
AppSettings(QObject *parent = Q_NULLPTR);
|
||||||
|
@ -35,9 +44,13 @@ public:
|
||||||
bool showStickersAsImages() const;
|
bool showStickersAsImages() const;
|
||||||
void setShowStickersAsImages(bool showAsImages);
|
void setShowStickersAsImages(bool showAsImages);
|
||||||
|
|
||||||
|
NotificationFeedback notificationFeedback() const;
|
||||||
|
void setNotificationFeedback(NotificationFeedback feedback);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sendByEnterChanged();
|
void sendByEnterChanged();
|
||||||
void showStickersAsImagesChanged();
|
void showStickersAsImagesChanged();
|
||||||
|
void notificationFeedbackChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
|
|
69
src/emojisearchworker.cpp
Normal file
69
src/emojisearchworker.cpp
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2020 Sebastian J. Wolf
|
||||||
|
|
||||||
|
This file is part of Fernschreiber.
|
||||||
|
|
||||||
|
Fernschreiber is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Fernschreiber is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "emojisearchworker.h"
|
||||||
|
|
||||||
|
#define LOG(x) qDebug() << "[EmojiSearchWorker]" << x
|
||||||
|
|
||||||
|
EmojiSearchWorker::~EmojiSearchWorker()
|
||||||
|
{
|
||||||
|
LOG("Destroying myself...");
|
||||||
|
database.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
EmojiSearchWorker::EmojiSearchWorker(QObject *parent) : QThread(parent)
|
||||||
|
{
|
||||||
|
LOG("Initializing Emoji database");
|
||||||
|
QSqlDatabase::removeDatabase("emojis");
|
||||||
|
database = QSqlDatabase::addDatabase("QSQLITE", "emojis");
|
||||||
|
database.setDatabaseName("/usr/share/harbour-fernschreiber/db/emojis.db");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmojiSearchWorker::setParameters(const QString &queryString)
|
||||||
|
{
|
||||||
|
this->queryString = queryString;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmojiSearchWorker::performSearch()
|
||||||
|
{
|
||||||
|
LOG("Performing emoji search" << this->queryString);
|
||||||
|
QVariantList resultList;
|
||||||
|
|
||||||
|
if (database.open()) {
|
||||||
|
QSqlQuery query(database);
|
||||||
|
query.prepare("select * from emojis where description match (:queryString) limit 25");
|
||||||
|
query.bindValue(":queryString", queryString + "*");
|
||||||
|
query.exec();
|
||||||
|
while (query.next()) {
|
||||||
|
if (isInterruptionRequested()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QVariantMap foundEmoji;
|
||||||
|
foundEmoji.insert("file_name", query.value(0).toString());
|
||||||
|
foundEmoji.insert("emoji", query.value(1).toString());
|
||||||
|
foundEmoji.insert("emoji_version", query.value(2).toString());
|
||||||
|
foundEmoji.insert("description", query.value(3).toString());
|
||||||
|
resultList.append(foundEmoji);
|
||||||
|
}
|
||||||
|
database.close();
|
||||||
|
} else {
|
||||||
|
LOG("Unable to perform a query on database" << database.lastError().databaseText());
|
||||||
|
}
|
||||||
|
|
||||||
|
emit searchCompleted(queryString, resultList);
|
||||||
|
}
|
52
src/emojisearchworker.h
Normal file
52
src/emojisearchworker.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2020 Sebastian J. Wolf
|
||||||
|
|
||||||
|
This file is part of Fernschreiber.
|
||||||
|
|
||||||
|
Fernschreiber is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Fernschreiber is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifndef EMOJISEARCHWORKER_H
|
||||||
|
#define EMOJISEARCHWORKER_H
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QVariantList>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
class EmojiSearchWorker : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
void run() Q_DECL_OVERRIDE {
|
||||||
|
performSearch();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
~EmojiSearchWorker() override;
|
||||||
|
explicit EmojiSearchWorker(QObject *parent = nullptr);
|
||||||
|
void setParameters(const QString &queryString);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void searchCompleted(const QString &queryString, const QVariantList &resultList);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSqlDatabase database;
|
||||||
|
QString queryString;
|
||||||
|
|
||||||
|
void performSearch();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EMOJISEARCHWORKER_H
|
|
@ -47,10 +47,11 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
AppSettings *appSettings = new AppSettings(view.data());
|
AppSettings *appSettings = new AppSettings(view.data());
|
||||||
context->setContextProperty("appSettings", appSettings);
|
context->setContextProperty("appSettings", appSettings);
|
||||||
|
qmlRegisterUncreatableType<AppSettings>("WerkWolf.Fernschreiber", 1, 0, "AppSettings", QString());
|
||||||
|
|
||||||
TDLibWrapper *tdLibWrapper = new TDLibWrapper(view.data());
|
TDLibWrapper *tdLibWrapper = new TDLibWrapper(view.data());
|
||||||
context->setContextProperty("tdLibWrapper", tdLibWrapper);
|
context->setContextProperty("tdLibWrapper", tdLibWrapper);
|
||||||
qmlRegisterType<TDLibWrapper>("WerkWolf.Fernschreiber", 1, 0, "TelegramAPI");
|
qmlRegisterUncreatableType<TDLibWrapper>("WerkWolf.Fernschreiber", 1, 0, "TelegramAPI", QString());
|
||||||
|
|
||||||
DBusAdaptor *dBusAdaptor = tdLibWrapper->getDBusAdaptor();
|
DBusAdaptor *dBusAdaptor = tdLibWrapper->getDBusAdaptor();
|
||||||
context->setContextProperty("dBusAdaptor", dBusAdaptor);
|
context->setContextProperty("dBusAdaptor", dBusAdaptor);
|
||||||
|
@ -61,7 +62,7 @@ int main(int argc, char *argv[])
|
||||||
ChatModel chatModel(tdLibWrapper);
|
ChatModel chatModel(tdLibWrapper);
|
||||||
context->setContextProperty("chatModel", &chatModel);
|
context->setContextProperty("chatModel", &chatModel);
|
||||||
|
|
||||||
NotificationManager notificationManager(tdLibWrapper);
|
NotificationManager notificationManager(tdLibWrapper, appSettings);
|
||||||
context->setContextProperty("notificationManager", ¬ificationManager);
|
context->setContextProperty("notificationManager", ¬ificationManager);
|
||||||
|
|
||||||
ProcessLauncher processLauncher;
|
ProcessLauncher processLauncher;
|
||||||
|
|
|
@ -26,12 +26,15 @@
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDBusConnection>
|
#include <QDBusConnection>
|
||||||
#include <QDBusInterface>
|
|
||||||
|
|
||||||
NotificationManager::NotificationManager(TDLibWrapper *tdLibWrapper, QObject *parent) : QObject(parent)
|
#define LOG(x) qDebug() << "[NotificationManager]" << x
|
||||||
|
|
||||||
|
NotificationManager::NotificationManager(TDLibWrapper *tdLibWrapper, AppSettings *appSettings) :
|
||||||
|
mceInterface("com.nokia.mce", "/com/nokia/mce/request", "com.nokia.mce.request", QDBusConnection::systemBus())
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] Initializing...";
|
LOG("Initializing...");
|
||||||
this->tdLibWrapper = tdLibWrapper;
|
this->tdLibWrapper = tdLibWrapper;
|
||||||
|
this->appSettings = appSettings;
|
||||||
this->ngfClient = new Ngf::Client(this);
|
this->ngfClient = new Ngf::Client(this);
|
||||||
|
|
||||||
connect(this->tdLibWrapper, SIGNAL(activeNotificationsUpdated(QVariantList)), this, SLOT(handleUpdateActiveNotifications(QVariantList)));
|
connect(this->tdLibWrapper, SIGNAL(activeNotificationsUpdated(QVariantList)), this, SLOT(handleUpdateActiveNotifications(QVariantList)));
|
||||||
|
@ -44,29 +47,28 @@ NotificationManager::NotificationManager(TDLibWrapper *tdLibWrapper, QObject *pa
|
||||||
connect(this->ngfClient, SIGNAL(eventPlaying(quint32)), this, SLOT(handleNgfEventPlaying(quint32)));
|
connect(this->ngfClient, SIGNAL(eventPlaying(quint32)), this, SLOT(handleNgfEventPlaying(quint32)));
|
||||||
|
|
||||||
if (this->ngfClient->connect()) {
|
if (this->ngfClient->connect()) {
|
||||||
qDebug() << "[NotificationManager] NGF Client successfully initialized...";
|
LOG("NGF Client successfully initialized...");
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "[NotificationManager] Failed to initialize NGF Client...";
|
LOG("Failed to initialize NGF Client...");
|
||||||
}
|
}
|
||||||
|
|
||||||
this->controlLedNotification(false);
|
this->controlLedNotification(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationManager::~NotificationManager()
|
NotificationManager::~NotificationManager()
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] Destroying myself...";
|
LOG("Destroying myself...");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationManager::handleUpdateActiveNotifications(const QVariantList notificationGroups)
|
void NotificationManager::handleUpdateActiveNotifications(const QVariantList notificationGroups)
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] Received active notifications, number of groups:" << notificationGroups.size();
|
LOG("Received active notifications, number of groups:" << notificationGroups.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationManager::handleUpdateNotificationGroup(const QVariantMap notificationGroupUpdate)
|
void NotificationManager::handleUpdateNotificationGroup(const QVariantMap notificationGroupUpdate)
|
||||||
{
|
{
|
||||||
QString notificationGroupId = notificationGroupUpdate.value("notification_group_id").toString();
|
QString notificationGroupId = notificationGroupUpdate.value("notification_group_id").toString();
|
||||||
qDebug() << "[NotificationManager] Received notification group update, group ID:" << notificationGroupId;
|
LOG("Received notification group update, group ID:" << notificationGroupId);
|
||||||
QVariantMap notificationGroup = this->notificationGroups.value(notificationGroupId).toMap();
|
QVariantMap notificationGroup = this->notificationGroups.value(notificationGroupId).toMap();
|
||||||
|
|
||||||
QString chatId = notificationGroupUpdate.value("chat_id").toString();
|
QString chatId = notificationGroupUpdate.value("chat_id").toString();
|
||||||
|
@ -92,7 +94,7 @@ void NotificationManager::handleUpdateNotificationGroup(const QVariantMap notifi
|
||||||
|
|
||||||
// If we have deleted notifications, we need to update possibly existing ones
|
// If we have deleted notifications, we need to update possibly existing ones
|
||||||
if (!removedNotificationIds.isEmpty() && !activeNotifications.isEmpty()) {
|
if (!removedNotificationIds.isEmpty() && !activeNotifications.isEmpty()) {
|
||||||
qDebug() << "[NotificationManager] Some removals happend, but we have " << activeNotifications.size() << "existing notifications.";
|
LOG("Some removals happend, but we have" << activeNotifications.size() << "existing notifications.");
|
||||||
QVariantMap firstActiveNotification = activeNotifications.first().toMap();
|
QVariantMap firstActiveNotification = activeNotifications.first().toMap();
|
||||||
activeNotifications.remove(firstActiveNotification.value("id").toString());
|
activeNotifications.remove(firstActiveNotification.value("id").toString());
|
||||||
QVariantMap newFirstActiveNotification = this->sendNotification(chatId, firstActiveNotification, activeNotifications);
|
QVariantMap newFirstActiveNotification = this->sendNotification(chatId, firstActiveNotification, activeNotifications);
|
||||||
|
@ -125,45 +127,43 @@ void NotificationManager::handleUpdateNotificationGroup(const QVariantMap notifi
|
||||||
|
|
||||||
void NotificationManager::handleUpdateNotification(const QVariantMap updatedNotification)
|
void NotificationManager::handleUpdateNotification(const QVariantMap updatedNotification)
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] Received notification update, group ID:" << updatedNotification.value("notification_group_id").toInt();
|
LOG("Received notification update, group ID:" << updatedNotification.value("notification_group_id").toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationManager::handleChatDiscovered(const QString &chatId, const QVariantMap &chatInformation)
|
void NotificationManager::handleChatDiscovered(const QString &chatId, const QVariantMap &chatInformation)
|
||||||
{
|
{
|
||||||
this->chatListMutex.lock();
|
LOG("Adding chat to internal map" << chatId);
|
||||||
qDebug() << "[NotificationManager] Adding chat to internal map " << chatId;
|
|
||||||
this->chatMap.insert(chatId, chatInformation);
|
this->chatMap.insert(chatId, chatInformation);
|
||||||
this->chatListMutex.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationManager::handleNgfConnectionStatus(const bool &connected)
|
void NotificationManager::handleNgfConnectionStatus(const bool &connected)
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] NGF Daemon connection status changed " << connected;
|
LOG("NGF Daemon connection status changed" << connected);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationManager::handleNgfEventFailed(const quint32 &eventId)
|
void NotificationManager::handleNgfEventFailed(const quint32 &eventId)
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] NGF event failed, id: " << eventId;
|
LOG("NGF event failed, id:" << eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationManager::handleNgfEventCompleted(const quint32 &eventId)
|
void NotificationManager::handleNgfEventCompleted(const quint32 &eventId)
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] NGF event completed, id: " << eventId;
|
LOG("NGF event completed, id:" << eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationManager::handleNgfEventPlaying(const quint32 &eventId)
|
void NotificationManager::handleNgfEventPlaying(const quint32 &eventId)
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] NGF event playing, id: " << eventId;
|
LOG("NGF event playing, id:" << eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationManager::handleNgfEventPaused(const quint32 &eventId)
|
void NotificationManager::handleNgfEventPaused(const quint32 &eventId)
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] NGF event paused, id: " << eventId;
|
LOG("NGF event paused, id:" << eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap NotificationManager::sendNotification(const QString &chatId, const QVariantMap ¬ificationInformation, const QVariantMap &activeNotifications)
|
QVariantMap NotificationManager::sendNotification(const QString &chatId, const QVariantMap ¬ificationInformation, const QVariantMap &activeNotifications)
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] Sending notification" << notificationInformation.value("id").toString();
|
LOG("Sending notification" << notificationInformation.value("id").toString());
|
||||||
|
|
||||||
QVariantMap chatInformation = this->chatMap.value(chatId).toMap();
|
QVariantMap chatInformation = this->chatMap.value(chatId).toMap();
|
||||||
QString chatType = chatInformation.value("type").toMap().value("@type").toString();
|
QString chatType = chatInformation.value("type").toMap().value("@type").toString();
|
||||||
|
@ -179,12 +179,15 @@ QVariantMap NotificationManager::sendNotification(const QString &chatId, const Q
|
||||||
nemoNotification.setAppName("Fernschreiber");
|
nemoNotification.setAppName("Fernschreiber");
|
||||||
nemoNotification.setAppIcon(appIconUrl.toLocalFile());
|
nemoNotification.setAppIcon(appIconUrl.toLocalFile());
|
||||||
nemoNotification.setSummary(chatInformation.value("title").toString());
|
nemoNotification.setSummary(chatInformation.value("title").toString());
|
||||||
nemoNotification.setCategory("x-nemo.messaging.im");
|
|
||||||
nemoNotification.setTimestamp(QDateTime::fromMSecsSinceEpoch(messageMap.value("date").toLongLong() * 1000));
|
nemoNotification.setTimestamp(QDateTime::fromMSecsSinceEpoch(messageMap.value("date").toLongLong() * 1000));
|
||||||
QVariantList remoteActionArguments;
|
QVariantList remoteActionArguments;
|
||||||
remoteActionArguments.append(chatId);
|
remoteActionArguments.append(chatId);
|
||||||
remoteActionArguments.append(messageMap.value("id").toString());
|
remoteActionArguments.append(messageMap.value("id").toString());
|
||||||
nemoNotification.setRemoteAction(Notification::remoteAction("default", "openMessage", "de.ygriega.fernschreiber", "/de/ygriega/fernschreiber", "de.ygriega.fernschreiber", "openMessage", remoteActionArguments));
|
nemoNotification.setRemoteAction(Notification::remoteAction("default", "openMessage", "de.ygriega.fernschreiber", "/de/ygriega/fernschreiber", "de.ygriega.fernschreiber", "openMessage", remoteActionArguments));
|
||||||
|
|
||||||
|
bool needFeedback;
|
||||||
|
const AppSettings::NotificationFeedback feedbackStyle = appSettings->notificationFeedback();
|
||||||
|
|
||||||
if (activeNotifications.isEmpty()) {
|
if (activeNotifications.isEmpty()) {
|
||||||
QString notificationBody;
|
QString notificationBody;
|
||||||
if (addAuthor) {
|
if (addAuthor) {
|
||||||
|
@ -196,13 +199,19 @@ QVariantMap NotificationManager::sendNotification(const QString &chatId, const Q
|
||||||
}
|
}
|
||||||
notificationBody = notificationBody + this->getNotificationText(messageMap.value("content").toMap());
|
notificationBody = notificationBody + this->getNotificationText(messageMap.value("content").toMap());
|
||||||
nemoNotification.setBody(notificationBody);
|
nemoNotification.setBody(notificationBody);
|
||||||
|
needFeedback = (feedbackStyle != AppSettings::NotificationFeedbackNone);
|
||||||
} else {
|
} else {
|
||||||
nemoNotification.setReplacesId(activeNotifications.first().toMap().value("replaces_id").toUInt());
|
nemoNotification.setReplacesId(activeNotifications.first().toMap().value("replaces_id").toUInt());
|
||||||
nemoNotification.setBody(tr("%1 unread messages").arg(activeNotifications.size() + 1));
|
nemoNotification.setBody(tr("%1 unread messages").arg(activeNotifications.size() + 1));
|
||||||
|
needFeedback = (feedbackStyle == AppSettings::NotificationFeedbackAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needFeedback) {
|
||||||
|
nemoNotification.setCategory("x-nemo.messaging.im");
|
||||||
|
ngfClient->play("chat");
|
||||||
}
|
}
|
||||||
|
|
||||||
nemoNotification.publish();
|
nemoNotification.publish();
|
||||||
this->ngfClient->play("chat");
|
|
||||||
this->controlLedNotification(true);
|
this->controlLedNotification(true);
|
||||||
updatedNotificationInformation.insert("replaces_id", nemoNotification.replacesId());
|
updatedNotificationInformation.insert("replaces_id", nemoNotification.replacesId());
|
||||||
return updatedNotificationInformation;
|
return updatedNotificationInformation;
|
||||||
|
@ -210,7 +219,7 @@ QVariantMap NotificationManager::sendNotification(const QString &chatId, const Q
|
||||||
|
|
||||||
void NotificationManager::removeNotification(const QVariantMap ¬ificationInformation)
|
void NotificationManager::removeNotification(const QVariantMap ¬ificationInformation)
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] Removing notification" << notificationInformation.value("id").toString();
|
LOG("Removing notification" << notificationInformation.value("id").toString());
|
||||||
Notification nemoNotification;
|
Notification nemoNotification;
|
||||||
nemoNotification.setReplacesId(notificationInformation.value("replaces_id").toUInt());
|
nemoNotification.setReplacesId(notificationInformation.value("replaces_id").toUInt());
|
||||||
nemoNotification.close();
|
nemoNotification.close();
|
||||||
|
@ -218,21 +227,17 @@ void NotificationManager::removeNotification(const QVariantMap ¬ificationInfo
|
||||||
|
|
||||||
QString NotificationManager::getNotificationText(const QVariantMap ¬ificationContent)
|
QString NotificationManager::getNotificationText(const QVariantMap ¬ificationContent)
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] Getting notification text from content" << notificationContent;
|
LOG("Getting notification text from content" << notificationContent);
|
||||||
|
|
||||||
return FernschreiberUtils::getMessageShortText(notificationContent, false);
|
return FernschreiberUtils::getMessageShortText(notificationContent, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationManager::controlLedNotification(const bool &enabled)
|
void NotificationManager::controlLedNotification(const bool &enabled)
|
||||||
{
|
{
|
||||||
qDebug() << "[NotificationManager] Controlling notification LED" << enabled;
|
static const QString PATTERN("PatternCommunicationIM");
|
||||||
QDBusConnection dbusConnection = QDBusConnection::connectToBus(QDBusConnection::SystemBus, "system");
|
static const QString ACTIVATE("req_led_pattern_activate");
|
||||||
QDBusInterface dbusInterface("com.nokia.mce", "/com/nokia/mce/request", "com.nokia.mce.request", dbusConnection);
|
static const QString DEACTIVATE("req_led_pattern_deactivate");
|
||||||
|
|
||||||
if (enabled) {
|
|
||||||
dbusInterface.call("req_led_pattern_activate", "PatternCommunicationIM");
|
|
||||||
} else {
|
|
||||||
dbusInterface.call("req_led_pattern_deactivate", "PatternCommunicationIM");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
LOG("Controlling notification LED" << enabled);
|
||||||
|
mceInterface.call(enabled ? ACTIVATE : DEACTIVATE, PATTERN);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,15 +21,16 @@
|
||||||
#define NOTIFICATIONMANAGER_H
|
#define NOTIFICATIONMANAGER_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QMutex>
|
#include <QDBusInterface>
|
||||||
#include <ngf-qt5/NgfClient>
|
#include <ngf-qt5/NgfClient>
|
||||||
#include "tdlibwrapper.h"
|
#include "tdlibwrapper.h"
|
||||||
|
#include "appsettings.h"
|
||||||
|
|
||||||
class NotificationManager : public QObject
|
class NotificationManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit NotificationManager(TDLibWrapper *tdLibWrapper, QObject *parent = nullptr);
|
NotificationManager(TDLibWrapper *tdLibWrapper, AppSettings *appSettings);
|
||||||
~NotificationManager() override;
|
~NotificationManager() override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -48,17 +49,20 @@ public slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
TDLibWrapper *tdLibWrapper;
|
|
||||||
Ngf::Client *ngfClient;
|
|
||||||
QVariantMap chatMap;
|
|
||||||
QVariantMap notificationGroups;
|
|
||||||
QMutex chatListMutex;
|
|
||||||
|
|
||||||
QVariantMap sendNotification(const QString &chatId, const QVariantMap ¬ificationInformation, const QVariantMap &activeNotifications);
|
QVariantMap sendNotification(const QString &chatId, const QVariantMap ¬ificationInformation, const QVariantMap &activeNotifications);
|
||||||
void removeNotification(const QVariantMap ¬ificationInformation);
|
void removeNotification(const QVariantMap ¬ificationInformation);
|
||||||
QString getNotificationText(const QVariantMap ¬ificationContent);
|
QString getNotificationText(const QVariantMap ¬ificationContent);
|
||||||
void controlLedNotification(const bool &enabled);
|
void controlLedNotification(const bool &enabled);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
TDLibWrapper *tdLibWrapper;
|
||||||
|
AppSettings *appSettings;
|
||||||
|
Ngf::Client *ngfClient;
|
||||||
|
QVariantMap chatMap;
|
||||||
|
QVariantMap notificationGroups;
|
||||||
|
QDBusInterface mceInterface;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NOTIFICATIONMANAGER_H
|
#endif // NOTIFICATIONMANAGER_H
|
||||||
|
|
|
@ -93,6 +93,8 @@ TDLibWrapper::TDLibWrapper(QObject *parent) : QObject(parent)
|
||||||
connect(this->tdLibReceiver, SIGNAL(stickerSets(QVariantList)), this, SLOT(handleStickerSets(QVariantList)));
|
connect(this->tdLibReceiver, SIGNAL(stickerSets(QVariantList)), this, SLOT(handleStickerSets(QVariantList)));
|
||||||
connect(this->tdLibReceiver, SIGNAL(stickerSet(QVariantMap)), this, SLOT(handleStickerSet(QVariantMap)));
|
connect(this->tdLibReceiver, SIGNAL(stickerSet(QVariantMap)), this, SLOT(handleStickerSet(QVariantMap)));
|
||||||
|
|
||||||
|
connect(&emojiSearchWorker, SIGNAL(searchCompleted(QString, QVariantList)), this, SLOT(handleEmojiSearchCompleted(QString, QVariantList)));
|
||||||
|
|
||||||
this->tdLibReceiver->start();
|
this->tdLibReceiver->start();
|
||||||
|
|
||||||
this->setLogVerbosityLevel();
|
this->setLogVerbosityLevel();
|
||||||
|
@ -467,6 +469,16 @@ void TDLibWrapper::getStickerSet(const QString &setId)
|
||||||
this->sendRequest(requestObject);
|
this->sendRequest(requestObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::searchEmoji(const QString &queryString)
|
||||||
|
{
|
||||||
|
LOG("Searching emoji" << queryString);
|
||||||
|
while (this->emojiSearchWorker.isRunning()) {
|
||||||
|
this->emojiSearchWorker.requestInterruption();
|
||||||
|
}
|
||||||
|
this->emojiSearchWorker.setParameters(queryString);
|
||||||
|
this->emojiSearchWorker.start();
|
||||||
|
}
|
||||||
|
|
||||||
QVariantMap TDLibWrapper::getUserInformation()
|
QVariantMap TDLibWrapper::getUserInformation()
|
||||||
{
|
{
|
||||||
return this->userInformation;
|
return this->userInformation;
|
||||||
|
@ -524,10 +536,14 @@ void TDLibWrapper::copyFileToDownloads(const QString &filePath)
|
||||||
QFileInfo fileInfo(filePath);
|
QFileInfo fileInfo(filePath);
|
||||||
if (fileInfo.exists()) {
|
if (fileInfo.exists()) {
|
||||||
QString downloadFilePath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/" + fileInfo.fileName();
|
QString downloadFilePath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/" + fileInfo.fileName();
|
||||||
if (QFile::copy(filePath, downloadFilePath)) {
|
if (QFile::exists(downloadFilePath)) {
|
||||||
emit copyToDownloadsSuccessful(fileInfo.fileName(), downloadFilePath);
|
emit copyToDownloadsSuccessful(fileInfo.fileName(), downloadFilePath);
|
||||||
} else {
|
} else {
|
||||||
emit copyToDownloadsError(fileInfo.fileName(), downloadFilePath);
|
if (QFile::copy(filePath, downloadFilePath)) {
|
||||||
|
emit copyToDownloadsSuccessful(fileInfo.fileName(), downloadFilePath);
|
||||||
|
} else {
|
||||||
|
emit copyToDownloadsError(fileInfo.fileName(), downloadFilePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
emit copyToDownloadsError(fileInfo.fileName(), filePath);
|
emit copyToDownloadsError(fileInfo.fileName(), filePath);
|
||||||
|
@ -828,6 +844,12 @@ void TDLibWrapper::handleStickerSet(const QVariantMap &stickerSet)
|
||||||
emit stickerSetReceived(stickerSet);
|
emit stickerSetReceived(stickerSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TDLibWrapper::handleEmojiSearchCompleted(const QString &queryString, const QVariantList &resultList)
|
||||||
|
{
|
||||||
|
LOG("Emoji search completed" << queryString);
|
||||||
|
emit emojiSearchSuccessful(resultList);
|
||||||
|
}
|
||||||
|
|
||||||
void TDLibWrapper::setInitialParameters()
|
void TDLibWrapper::setInitialParameters()
|
||||||
{
|
{
|
||||||
LOG("Sending initial parameters to TD Lib");
|
LOG("Sending initial parameters to TD Lib");
|
||||||
|
@ -846,6 +868,7 @@ void TDLibWrapper::setInitialParameters()
|
||||||
initialParameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString());
|
initialParameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString());
|
||||||
initialParameters.insert("system_version", QSysInfo::prettyProductName());
|
initialParameters.insert("system_version", QSysInfo::prettyProductName());
|
||||||
initialParameters.insert("application_version", "0.3");
|
initialParameters.insert("application_version", "0.3");
|
||||||
|
// initialParameters.insert("use_test_dc", true);
|
||||||
requestObject.insert("parameters", initialParameters);
|
requestObject.insert("parameters", initialParameters);
|
||||||
this->sendRequest(requestObject);
|
this->sendRequest(requestObject);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "tdlibreceiver.h"
|
#include "tdlibreceiver.h"
|
||||||
#include "dbusadaptor.h"
|
#include "dbusadaptor.h"
|
||||||
#include "dbusinterface.h"
|
#include "dbusinterface.h"
|
||||||
|
#include "emojisearchworker.h"
|
||||||
|
|
||||||
class TDLibWrapper : public QObject
|
class TDLibWrapper : public QObject
|
||||||
{
|
{
|
||||||
|
@ -129,6 +130,9 @@ public:
|
||||||
Q_INVOKABLE void getInstalledStickerSets();
|
Q_INVOKABLE void getInstalledStickerSets();
|
||||||
Q_INVOKABLE void getStickerSet(const QString &setId);
|
Q_INVOKABLE void getStickerSet(const QString &setId);
|
||||||
|
|
||||||
|
// Others (candidates for extraction ;))
|
||||||
|
Q_INVOKABLE void searchEmoji(const QString &queryString);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const Group* getGroup(qlonglong groupId) const;
|
const Group* getGroup(qlonglong groupId) const;
|
||||||
static ChatMemberStatus chatMemberStatusFromString(const QString &status);
|
static ChatMemberStatus chatMemberStatusFromString(const QString &status);
|
||||||
|
@ -169,6 +173,7 @@ signals:
|
||||||
void installedStickerSetsUpdated(const QVariantList &stickerSetIds);
|
void installedStickerSetsUpdated(const QVariantList &stickerSetIds);
|
||||||
void stickerSetsReceived(const QVariantList &stickerSets);
|
void stickerSetsReceived(const QVariantList &stickerSets);
|
||||||
void stickerSetReceived(const QVariantMap &stickerSet);
|
void stickerSetReceived(const QVariantMap &stickerSet);
|
||||||
|
void emojiSearchSuccessful(const QVariantList &result);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void handleVersionDetected(const QString &version);
|
void handleVersionDetected(const QString &version);
|
||||||
|
@ -204,6 +209,7 @@ public slots:
|
||||||
void handleInstalledStickerSetsUpdated(const QVariantList &stickerSetIds);
|
void handleInstalledStickerSetsUpdated(const QVariantList &stickerSetIds);
|
||||||
void handleStickerSets(const QVariantList &stickerSets);
|
void handleStickerSets(const QVariantList &stickerSets);
|
||||||
void handleStickerSet(const QVariantMap &stickerSet);
|
void handleStickerSet(const QVariantMap &stickerSet);
|
||||||
|
void handleEmojiSearchCompleted(const QString &queryString, const QVariantList &resultList);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setInitialParameters();
|
void setInitialParameters();
|
||||||
|
@ -228,6 +234,8 @@ private:
|
||||||
QVariantMap unreadChatInformation;
|
QVariantMap unreadChatInformation;
|
||||||
QHash<qlonglong,Group*> basicGroups;
|
QHash<qlonglong,Group*> basicGroups;
|
||||||
QHash<qlonglong,Group*> superGroups;
|
QHash<qlonglong,Group*> superGroups;
|
||||||
|
EmojiSearchWorker emojiSearchWorker;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TDLIBWRAPPER_H
|
#endif // TDLIBWRAPPER_H
|
||||||
|
|
|
@ -522,6 +522,26 @@
|
||||||
<source>Show background for stickers and align them centrally like images</source>
|
<source>Show background for stickers and align them centrally like images</source>
|
||||||
<translation>Hintergrund für Sticker anzeigen und sie wie Bilder mittig platzieren.</translation>
|
<translation>Hintergrund für Sticker anzeigen und sie wie Bilder mittig platzieren.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notification feedback</source>
|
||||||
|
<translation>Rückmeldung bei Hinweisen</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All events</source>
|
||||||
|
<translation>Alle Ereignisse</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Only new events</source>
|
||||||
|
<translation>Nur neue Ereignisse</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>None</source>
|
||||||
|
<translation>Keine</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use non-graphical feedback (sound, vibration) for notifications</source>
|
||||||
|
<translation>Nicht-grafische Rückmeldungen (Klänge, Vibration) bei Hinweisen nutzen</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StickerPicker</name>
|
<name>StickerPicker</name>
|
||||||
|
|
|
@ -522,6 +522,26 @@
|
||||||
<source>Show background for stickers and align them centrally like images</source>
|
<source>Show background for stickers and align them centrally like images</source>
|
||||||
<translation>Mostrar fondo para pegatinas y alinearlas centralmente como imágenes</translation>
|
<translation>Mostrar fondo para pegatinas y alinearlas centralmente como imágenes</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notification feedback</source>
|
||||||
|
<translation>Comentarios de notificación</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All events</source>
|
||||||
|
<translation>Todos los eventos</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Only new events</source>
|
||||||
|
<translation>Sólo nuevos eventos</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>None</source>
|
||||||
|
<translation>Ninguno </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use non-graphical feedback (sound, vibration) for notifications</source>
|
||||||
|
<translation>Usa comentarios no gráficos (sonido, vibración) para las notificaciones</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StickerPicker</name>
|
<name>StickerPicker</name>
|
||||||
|
|
|
@ -522,6 +522,26 @@
|
||||||
<source>Show background for stickers and align them centrally like images</source>
|
<source>Show background for stickers and align them centrally like images</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notification feedback</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Only new events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>None</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use non-graphical feedback (sound, vibration) for notifications</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StickerPicker</name>
|
<name>StickerPicker</name>
|
||||||
|
|
|
@ -522,6 +522,26 @@
|
||||||
<source>Show background for stickers and align them centrally like images</source>
|
<source>Show background for stickers and align them centrally like images</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notification feedback</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Only new events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>None</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use non-graphical feedback (sound, vibration) for notifications</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StickerPicker</name>
|
<name>StickerPicker</name>
|
||||||
|
|
|
@ -522,6 +522,26 @@
|
||||||
<source>Show background for stickers and align them centrally like images</source>
|
<source>Show background for stickers and align them centrally like images</source>
|
||||||
<translation>Mostra sfondo per gli sticker e centrali come le immagini.</translation>
|
<translation>Mostra sfondo per gli sticker e centrali come le immagini.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notification feedback</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Only new events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>None</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use non-graphical feedback (sound, vibration) for notifications</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StickerPicker</name>
|
<name>StickerPicker</name>
|
||||||
|
|
|
@ -522,6 +522,26 @@
|
||||||
<source>Show background for stickers and align them centrally like images</source>
|
<source>Show background for stickers and align them centrally like images</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notification feedback</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Only new events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>None</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use non-graphical feedback (sound, vibration) for notifications</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StickerPicker</name>
|
<name>StickerPicker</name>
|
||||||
|
|
|
@ -522,16 +522,36 @@
|
||||||
<source>Show background for stickers and align them centrally like images</source>
|
<source>Show background for stickers and align them centrally like images</source>
|
||||||
<translation>То есть рисовать под ними фон и позиционировать по центру.</translation>
|
<translation>То есть рисовать под ними фон и позиционировать по центру.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notification feedback</source>
|
||||||
|
<translation>Звуки и вибрация</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All events</source>
|
||||||
|
<translation>На каждое событие</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Only new events</source>
|
||||||
|
<translation>Только на новые события</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>None</source>
|
||||||
|
<translation>Никогда</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use non-graphical feedback (sound, vibration) for notifications</source>
|
||||||
|
<translation>Сопровождать уведомления звуками и вибрацией.</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StickerPicker</name>
|
<name>StickerPicker</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Recently used</source>
|
<source>Recently used</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Недавно использованные</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Loading stickers...</source>
|
<source>Loading stickers...</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Загрузка стикеров...</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
|
|
@ -522,6 +522,26 @@
|
||||||
<source>Show background for stickers and align them centrally like images</source>
|
<source>Show background for stickers and align them centrally like images</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notification feedback</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Only new events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>None</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use non-graphical feedback (sound, vibration) for notifications</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StickerPicker</name>
|
<name>StickerPicker</name>
|
||||||
|
|
|
@ -522,6 +522,26 @@
|
||||||
<source>Show background for stickers and align them centrally like images</source>
|
<source>Show background for stickers and align them centrally like images</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Notification feedback</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>All events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Only new events</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>None</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use non-graphical feedback (sound, vibration) for notifications</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>StickerPicker</name>
|
<name>StickerPicker</name>
|
||||||
|
|
Loading…
Reference in a new issue