1import QtQuick 2.0 2import AsemanTools.Controls 1.0 3import AsemanTools 1.0 4import TelegramQmlLib 1.0 5// import CutegramTypes 1.0 6import QtGraphicalEffects 1.0 7 8Item { 9 id: ad_list 10 width: minimum? 80*Devices.density : Cutegram.currentTheme.dialogListWidth*Devices.density 11 12 property alias telegramObject: dialogs_model.telegram 13 property Dialog currentDialog: telegramObject.dialog(0) 14 15 property bool unminimumForce: false 16 property bool minimum: Cutegram.minimumDialogs && !unminimumForce && !forceUnminimum 17 property bool forceUnminimum: false 18 19 property bool showLastMessage: Cutegram.showLastMessage 20 21 property real typeMessageMediaDocument: 0x2fda2204 22 property real typeMessageMediaContact: 0x5e7d2f39 23 property real typeMessageMediaVideo: 0x5bcf1675 24 property real typeMessageMediaEmpty: 0x3ded6320 25 property real typeMessageMediaAudio: 0xc6b68300 26 property real typeMessageMediaPhoto: 0x3d8ce53d 27 property real typeMessageMediaGeo: 0x56e0d474 28 29 property real typeMessageActionEmpty: 0xb6aef7b0 30 property real typeMessageActionChatDeletePhoto: 0x95e3fbef 31 property real typeMessageActionChatCreate: 0xa6638b9a 32 property real typeMessageActionChatEditTitle: 0xb5a1ce5a 33 property real typeMessageActionChatEditPhoto: 0x7fcb13a8 34 property real typeMessageActionGeoChatCreate: 0x6f038ebc 35 property real typeMessageActionChatDeleteUser: 0xb2ae9b0c 36 property real typeMessageActionChatAddUser: 0x5e3cfc4b 37 property real typeMessageActionGeoChatCheckin: 0xc7d53de 38 39 signal windowRequest(variant dialog) 40 41 onCurrentDialogChanged: { 42 dlist.currentIndex = dialogs_model.indexOf(currentDialog) 43 } 44 45 Behavior on width { 46 NumberAnimation{ easing.type: Easing.OutCubic; duration: 400 } 47 } 48 49 DialogsModel { 50 id: dialogs_model 51 stopUpdating: main.dragging 52 onInitializingChanged: { 53 if( initializing ) 54 indicator.start() 55 else 56 indicator.stop() 57 } 58 } 59 60 Indicator { 61 id: indicator 62 anchors.centerIn: parent 63 light: true 64 modern: true 65 indicatorSize: 20*Devices.density 66 } 67 68 AsemanListView { 69 id: dlist 70 width: Cutegram.currentTheme.dialogListWidth*Devices.density - x 71 height: parent.height 72 x: Cutegram.currentTheme.dialogListScrollWidth*Devices.density 73 clip: true 74 model: dialogs_model 75 maximumFlickVelocity: 2000 76 flickDeceleration: 2000 77 78 currentIndex: -1 79 highlightMoveDuration: 0 80 highlight: Item { 81 width: dlist.width 82 height: showLastMessage? 60*Devices.density : 54*Devices.density 83 84 Rectangle { 85 anchors.fill: parent 86 anchors.topMargin: 3*Devices.density 87 anchors.bottomMargin: 3*Devices.density 88 anchors.rightMargin: -radius 89 color: Cutegram.currentTheme.dialogListHighlightColor 90 radius: 5*Devices.density 91 92 Rectangle { 93 anchors.bottom: parent.bottom 94 anchors.left: parent.right 95 anchors.leftMargin: -parent.radius 96 transformOrigin: Item.BottomLeft 97 rotation: -90 98 width: parent.height 99 height: Cutegram.currentTheme.dialogListShadowWidth*Devices.density 100 opacity: 0.4 101 gradient: Gradient { 102 GradientStop { position: 0.0; color: "#00000000" } 103 GradientStop { position: 1.0; color: Cutegram.currentTheme.dialogListShadowColor } 104 } 105 } 106 } 107 108 Item { 109 id: pointer_frame 110 width: pointer_rct.width*Math.pow(2,0.5)*2 111 height: width 112 x: ad_list.width - width/2 - dlist.x 113 anchors.verticalCenter: parent.verticalCenter 114 visible: false 115 116 Rectangle { 117 id: pointer_rct 118 anchors.centerIn: parent 119 transformOrigin: Item.Center 120 rotation: 45 121 width: Cutegram.currentTheme.dialogPointerHeight*Devices.density 122 height: width 123 color: Cutegram.currentTheme.dialogPointerColor 124 } 125 } 126 127 DropShadow { 128 anchors.fill: pointer_frame 129 radius: Cutegram.currentTheme.dialogListShadowWidth 130 samples: 16 131 color: Cutegram.currentTheme.dialogListShadowColor 132 source: pointer_frame 133 } 134 } 135 136// move: Transition { 137// NumberAnimation { easing.type: Easing.OutCubic; properties: "y"; duration: 300 } 138// } 139// moveDisplaced: Transition { 140// NumberAnimation { easing.type: Easing.OutCubic; properties: "y"; duration: 300 } 141// } 142 143 delegate: Item { 144 id: list_item 145 width: dlist.width 146 height: showLastMessage? 60*Devices.density : 54*Devices.density 147 clip: true 148 149 property Dialog dItem: item 150 151 property bool isChat: dItem.peer.chatId != 0 152 property User user: telegramObject.user(dItem.encrypted?enChatUid:dItem.peer.userId) 153 property Chat chat: telegramObject.chat(dItem.peer.chatId) 154 155 property EncryptedChat enchat: telegramObject.encryptedChat(dItem.peer.userId) 156 property int enChatUid: enchat.adminId==telegramObject.me? enchat.participantId : enchat.adminId 157 158 property Message message: telegramObject.message(dItem.topMessage) 159 property variant msgDate: CalendarConv.fromTime_t(message.date) 160 161 property bool selected: currentDialog==dItem 162 property real itemOpacities: minimum? 0 : 1 163 164 property bool disableBadges: (telegramObject.userData.notify(isChat? chat.id : user.id) & UserData.DisableBadges) 165 166 onSelectedChanged: if(selected) dlist.currentIndex = index 167 168 Connections { 169 target: telegramObject.userData 170 onNotifyChanged: { 171 if(isChat && id == chat.id) 172 disableBadges = value 173 else 174 if(!isChat && id == user.id) 175 disableBadges = value 176 } 177 } 178 179 Behavior on itemOpacities { 180 NumberAnimation{ easing.type: Easing.OutCubic; duration: 400 } 181 } 182 183 Rectangle { 184 anchors.fill: parent 185 opacity: marea.pressed && !selected? 1 : 0 186 anchors.topMargin: 3*Devices.density 187 anchors.bottomMargin: 3*Devices.density 188 color: Cutegram.currentTheme.dialogListHighlightColor 189 radius: 5*Devices.density 190 } 191 192 Item { 193 anchors.fill: parent 194 anchors.topMargin: 3*Devices.density 195 anchors.bottomMargin: 3*Devices.density 196 anchors.leftMargin: 5*Devices.density 197 anchors.rightMargin: 12*Devices.density 198 199 Frame { 200 id: profile_img 201 anchors.top: parent.top 202 anchors.bottom: parent.bottom 203 anchors.left: parent.left 204 anchors.margins: 4*Devices.density 205 width: height 206 backgroundColor: selected || marea.pressed? Cutegram.currentTheme.dialogListHighlightColor : Cutegram.currentTheme.dialogListBackground 207 208 ContactImage { 209 id: contact_img 210 anchors.fill: parent 211 dialog: dItem 212 circleMode: false 213 } 214 } 215 216 Image { 217 anchors.bottom: profile_img.bottom 218 anchors.right: profile_img.right 219 anchors.margins: -4*Devices.density 220 source: "files/online.png" 221 sourceSize: Qt.size(width,height) 222 width: 14*Devices.density 223 height: 14*Devices.density 224 visible: isChat? false : (user.status.classType == contact_img.typeUserStatusOnline) 225 } 226 227 Text { 228 id: title_txt 229 anchors.top: parent.top 230 anchors.bottom: showLastMessage? parent.verticalCenter : parent.bottom 231 anchors.left: profile_img.right 232 anchors.right: time_txt.left 233 anchors.margins: 4*Devices.density 234 font.pixelSize: Math.floor(11*Devices.fontDensity) 235 font.family: Cutegram.currentTheme.dialogListFont.family 236 horizontalAlignment: Text.AlignLeft 237 verticalAlignment: Text.AlignVCenter 238 color: selected || marea.pressed? Cutegram.currentTheme.dialogListHighlightTextColor : Cutegram.currentTheme.dialogListFontColor 239 wrapMode: Text.WrapAnywhere 240 elide: Text.ElideRight 241 maximumLineCount: 1 242 textFormat: Text.RichText 243 text: emojis.textToEmojiText( isChat? chat.title : user.firstName + " " + user.lastName, 16, true) 244 opacity: itemOpacities 245 } 246 247 Image { 248 x: title_txt.x + title_txt.paintedWidth + 4*Devices.density 249 anchors.verticalCenter: parent.verticalCenter 250 source: Cutegram.currentTheme.dialogListLightIcon? "files/lock.png" : "files/lock-dark.png" 251 sourceSize: Qt.size(width, height) 252 visible: dItem.encrypted 253 width: 14*Devices.density 254 height: width 255 } 256 257 Text { 258 id: msg_txt 259 anchors.top: parent.verticalCenter 260 anchors.bottom: parent.bottom 261 anchors.left: profile_img.right 262 anchors.right: parent.right 263 anchors.margins: 4*Devices.density 264 anchors.topMargin: 0 265 textFormat: Text.RichText 266 font.pixelSize: Math.floor(Cutegram.currentTheme.dialogListMessageFont.pointSize*Devices.fontDensity) 267 font.family: Cutegram.currentTheme.dialogListMessageFont.family 268 color: selected || marea.pressed? Cutegram.currentTheme.dialogListHighlightMessageColor : Cutegram.currentTheme.dialogListMessageColor 269 wrapMode: Text.WrapAnywhere 270 elide: Text.ElideRight 271 maximumLineCount: 1 272 lineHeight: 100 273 clip: true 274 opacity: itemOpacities 275 visible: showLastMessage 276 text: { 277 if(!visible) 278 return "" 279 var list = dItem.typingUsers 280 if( list.length ) 281 { 282 if(!isChat) 283 return qsTr("Typing...") 284 else 285 { 286 var result = "" 287 for( var i=0; i<list.length; i++ ) { 288 var uid = list[i] 289 var user = telegramObject.user(list[i]) 290 var uname = user.firstName + " " + user.lastName 291 result += uname.trim() 292 293 if( i < list.length-1 ) 294 result += ", " 295 } 296 297 return result.trim() + " typing..." 298 } 299 } 300 else 301 { 302 var message_text = "" 303 if(message.media.classType != typeMessageMediaEmpty) 304 { 305 switch(message.media.classType) 306 { 307 case typeMessageMediaDocument: 308 message_text = qsTr("Document") 309 break; 310 case typeMessageMediaContact: 311 message_text = qsTr("Contact") 312 break; 313 case typeMessageMediaVideo: 314 message_text = qsTr("Video") 315 break; 316 case typeMessageMediaAudio: 317 message_text = qsTr("Audio") 318 break; 319 case typeMessageMediaPhoto: 320 message_text = qsTr("Photo") 321 break; 322 case typeMessageMediaGeo: 323 message_text = qsTr("Location") 324 break; 325 default: 326 message_text = qsTr("Unknown") 327 break; 328 } 329 } 330 else if(message.action.classType != typeMessageActionEmpty) 331 { 332 var res = "" 333 var userName 334 var fromUserName = telegramObject.user(message.fromId).firstName + " " + telegramObject.user(message.fromId).lastName 335 fromUserName = fromUserName.trim() 336 337 switch(message.action.classType) { 338 case typeMessageActionChatCreate: 339 res = qsTr("%1 created the group \"%2\"").arg(fromUserName).arg(message.action.title) 340 break 341 342 case typeMessageActionChatAddUser: 343 userName = telegramObject.user(message.action.userId).firstName + " " + telegramObject.user(message.action.userId).lastName 344 userName = userName.trim() 345 346 res = qsTr("%1 added %2 to group").arg(fromUserName).arg(userName) 347 break 348 349 case typeMessageActionChatDeleteUser: 350 userName = telegramObject.user(message.action.userId).firstName + " " + telegramObject.user(message.action.userId).lastName 351 userName = userName.trim() 352 353 if(telegramObject.user(message.action.userId).id == telegramObject.user(message.fromId).id) 354 res = qsTr("%1 left the group").arg(userName) 355 else 356 res = qsTr("%1 kicked %2").arg(fromUserName).arg(userName) 357 break; 358 359 case typeMessageActionChatEditTitle: 360 res = qsTr("%1 changed group name to \"%2\"").arg(fromUserName).arg(message.action.title) 361 break 362 363 case typeMessageActionChatEditPhoto: 364 res = qsTr("%1 changed group photo.").arg(fromUserName) 365 break 366 367 case typeMessageActionChatDeletePhoto: 368 res = qsTr("%1 deleted group photo").arg(fromUserName) 369 break 370 371 default: 372 break 373 } 374 375 message_text = emojis.textToEmojiText(res, 16, true) 376 } 377 else 378 { 379 message_text = emojis.textToEmojiText(message.message,16,true) 380 } 381 382 if(isChat && message.action.classType == typeMessageActionEmpty) 383 { 384 var chat_username = "" 385 if(message.fromId == telegramObject.me) 386 chat_username = qsTr("You") 387 else 388 chat_username = telegramObject.user(message.fromId).firstName + " " + telegramObject.user(message.fromId).lastName 389 390 message_text = chat_username.trim() + ": " + message_text 391 } 392 393 return message_text 394 } 395 } 396 } 397 398 Text { 399 id: time_txt 400 anchors.top: parent.top 401 anchors.right: parent.right 402 anchors.margins: 4*Devices.density 403 font.family: Cutegram.currentTheme.dialogListDateFont.family 404 font.pixelSize: Math.floor(Cutegram.currentTheme.dialogListDateFont.pointSize*Devices.fontDensity) 405 color: selected || marea.pressed? Cutegram.currentTheme.dialogListHighlightDateColor : Cutegram.currentTheme.dialogListDateColor 406 text: Cutegram.getTimeString(msgDate) 407 opacity: itemOpacities 408 visible: showLastMessage 409 } 410 411 UnreadItem { 412 unread: dItem.unreadCount 413 visible: unread != 0 && !selected && !disableBadges 414 } 415 } 416 417 MouseArea { 418 id: marea 419 anchors.fill: parent 420 hoverEnabled: true 421 acceptedButtons: Qt.LeftButton | Qt.RightButton 422 onContainsMouseChanged: toggleMinimum(containsMouse) 423 onDoubleClicked: ad_list.windowRequest(list_item.dItem) 424 onPressed: { 425 if( mouse.button == Qt.RightButton ) { 426 var actions, res 427 if(dItem.encrypted) { 428 actions = [qsTr("Open in New Window"), qsTr("Delete")] 429 res = Desktop.showMenu(actions) 430 switch(res) { 431 case 0: 432 ad_list.windowRequest(list_item.dItem) 433 break; 434 case 1: 435 if( Desktop.yesOrNo(View, qsTr("Delete secret chat"), qsTr("Are you sure about deleting this secret chat?")) ) 436 telegramObject.messagesDiscardEncryptedChat(dItem.peer.userId, true) 437 break; 438 } 439 } else if(user.id == telegramObject.cutegramId) { 440 actions = [qsTr("Delete")] 441 res = Desktop.showMenu(actions) 442 switch(res) { 443 case 0: 444 telegramObject.deleteCutegramDialog() 445 break; 446 } 447 } else if(isChat) { 448 actions = [qsTr("Open in New Window"), qsTr("Delete History")] 449 res = Desktop.showMenu(actions) 450 switch(res) { 451 case 0: 452 ad_list.windowRequest(list_item.dItem) 453 break; 454 case 1: 455 if( Desktop.yesOrNo(View, qsTr("Delete History"), qsTr("Are you sure about deleting history?")) ) 456 telegramObject.messagesDeleteHistory(dItem.peer.chatId) 457 break; 458 } 459 } else { 460 var notifyValue = telegramObject.userData.notify(dItem.peer.userId) 461 var notifyOnline = notifyValue & UserData.NotifyOnline 462 var notifyTyping = notifyValue & UserData.NotifyTyping 463 var notifyMenu = {"text": qsTr("Notify when"), 464 "list":[{"text": qsTr("Go online"), "checkable": true, "checked": notifyOnline}]} 465// {"text": qsTr("Start typing"), "checkable": true, "checked": notifyTyping}]} 466 467 actions = [qsTr("Open in New Window"), qsTr("Delete History"), notifyMenu] 468 res = Desktop.showMenu(actions) 469 switch(res) { 470 case 0: 471 ad_list.windowRequest(list_item.dItem) 472 break; 473 case 1: 474 if( Desktop.yesOrNo(View, qsTr("Delete History"), qsTr("Are you sure about deleting history?")) ) 475 telegramObject.messagesDeleteHistory(dItem.peer.userId) 476 break; 477 case 2: 478 var withoutOnline = (notifyValue-notifyOnline) 479 notifyOnline = notifyOnline? 0 : UserData.NotifyOnline 480 telegramObject.userData.setNotify(dItem.peer.userId, notifyOnline|withoutOnline) 481 break; 482 case 3: 483 var withoutTyping = (notifyValue-notifyTyping) 484 notifyTyping = notifyTyping? 0 : UserData.NotifyTyping 485 telegramObject.userData.setNotify(dItem.peer.userId, notifyTyping|withoutTyping) 486 break; 487 } 488 } 489 } 490 } 491 onClicked: { 492 if( mouse.button == Qt.LeftButton ) 493 currentDialog = list_item.dItem 494 } 495 } 496 497 DialogItemTools { 498 anchors.verticalCenter: parent.verticalCenter 499 anchors.right: parent.right 500 anchors.rightMargin: 12*Devices.density 501 height: 22*Devices.density 502 dialog: dItem 503 show: marea.containsMouse 504 opacity: itemOpacities 505 } 506 507 DialogDropFile { 508 anchors.fill: parent 509 dialogItem: dItem 510 color: "#dd222222" 511 onDropped: switch_dialog_timer.restart() 512 onContainsDragChanged: toggleMinimum(containsDrag) 513 } 514 515 Timer { 516 id: switch_dialog_timer 517 interval: 400 518 onTriggered: { 519 currentDialog = list_item.dItem 520 dlist.currentIndex = index 521 } 522 } 523 } 524 525 section.property: "section" 526 section.criteria: ViewSection.FullString 527 section.labelPositioning: ViewSection.InlineLabels 528 section.delegate: Item { 529 height: 30*Devices.density 530 width: dlist.width 531 532 Image { 533 anchors.left: parent.left 534 anchors.bottom: parent.bottom 535 anchors.margins: 4*Devices.density 536 width: 12*Devices.density 537 height: width 538 sourceSize: Qt.size(width,height) 539 smooth: true 540 source: { 541 if( section == 0 ) 542 return Cutegram.currentTheme.dialogListLightIcon? "files/contact-light.png" : "files/contact.png" 543 else 544 if( section == 1 ) 545 return Cutegram.currentTheme.dialogListLightIcon? "files/favorite-light.png" : "files/favorite.png" 546 else 547 if( section == 2 ) 548 return "files/love.png" 549 else 550 return "" 551 } 552 } 553 } 554 } 555 556 MouseArea { 557 anchors.left: parent.right 558 anchors.right: dlist.right 559 anchors.top: parent.top 560 anchors.bottom: parent.bottom 561 hoverEnabled: true 562 } 563 564 NormalWheelScroll { 565 flick: dlist 566 animated: Cutegram.smoothScroll 567 } 568 569 Timer { 570 id: restore_minimum_timer 571 interval: 500 572 onTriggered: unminimumForce = false 573 } 574 575 Timer { 576 id: restore_unminimum_timer 577 interval: 50 578 onTriggered: unminimumForce = true 579 } 580 581 PhysicalScrollBar { 582 scrollArea: dlist; height: dlist.height; width: Cutegram.currentTheme.dialogListScrollWidth*Devices.density 583 anchors.right: dlist.left; anchors.top: dlist.top; color: Cutegram.currentTheme.dialogListScrollColor 584 } 585 586 Button { 587 anchors.right: parent.right 588 anchors.verticalCenter: parent.verticalCenter 589 height: 50*Devices.density 590 width: 10*Devices.density 591 normalColor: enter? "#88ffffff" : "#44ffffff" 592 highlightColor: "#aaffffff" 593 onClicked: Cutegram.minimumDialogs = !Cutegram.minimumDialogs 594 cursorShape: Qt.PointingHandCursor 595 icon: Cutegram.minimumDialogs? "files/arrow-right.png" : "files/arrow-left.png" 596 iconHeight: 8*Devices.density 597 visible: false 598 599 Behavior on color { 600 ColorAnimation{ easing.type: Easing.OutCubic; duration: 400 } 601 } 602 } 603 604 function toggleMinimum(stt) { 605 if(!Cutegram.minimumDialogs) 606 return 607 if(stt) { 608 restore_minimum_timer.stop() 609 restore_unminimum_timer.restart() 610 } else { 611 restore_unminimum_timer.stop() 612 restore_minimum_timer.restart() 613 } 614 } 615 616 function next() { 617 var dialog = dialogs_model.at(dlist.currentIndex+1) 618 if(!dialog) 619 return 620 621 currentDialog = dialog 622 } 623 624 function previous() { 625 var dialog = dialogs_model.at(dlist.currentIndex-1) 626 if(!dialog) 627 return 628 629 currentDialog = dialog 630 } 631} 632