import QtQuick 2.6 import Sailfish.Silica 1.0 import "../components" import "../components/chatInformationPage" import "../js/twemoji.js" as Emoji import "../js/functions.js" as Functions Page { id: chatInformationPage allowedOrientations: Orientation.All property string searchString property int chatOnlineMemberCount: 0; property int myUserId: tdLibWrapper.getUserInformation().id; property bool isPrivateChat: false property bool isBasicGroup: false property bool isSuperGroup: false property bool isChannel: false property string chatPartnerGroupId property bool userIsMember: (isPrivateChat && chatInformation["@type"]) || // should be optimized (isBasicGroup || isSuperGroup) && ( (groupInformation.status["@type"] === "chatMemberStatusMember") || (groupInformation.status["@type"] === "chatMemberStatusAdministrator") || (groupInformation.status["@type"] === "chatMemberStatusRestricted" && groupInformation.status.is_member) || (groupInformation.status["@type"] === "chatMemberStatusCreator" && groupInformation.status.is_member) ) property variant chatInformation:({}); property variant privateChatUserInformation:({}); property variant chatPartnerFullInformation:({}); property variant chatPartnerProfilePhotos:([]); property variant groupInformation: ({}); property variant groupFullInformation: ({}); property alias membersList: membersList function initializePage() { console.log("[ChatInformationPage] Initializing chat info page..."); membersList.clear(); var chatType = chatInformation.type["@type"]; switch(chatType) { case "chatTypePrivate": isPrivateChat = true; chatInformationPage.chatPartnerGroupId = chatInformation.type.user_id.toString(); if(!privateChatUserInformation.id) { privateChatUserInformation = tdLibWrapper.getUserInformation(chatInformationPage.chatPartnerGroupId); } tdLibWrapper.getUserFullInfo(chatInformationPage.chatPartnerGroupId); tdLibWrapper.getUserProfilePhotos(chatInformationPage.chatPartnerGroupId, 100, 0); break; case "chatTypeBasicGroup": isBasicGroup = true; chatInformationPage.chatPartnerGroupId = chatInformation.type.basic_group_id.toString(); if(!groupInformation.id) { groupInformation = tdLibWrapper.getBasicGroup(chatInformationPage.chatPartnerGroupId); } tdLibWrapper.getGroupFullInfo(chatInformationPage.chatPartnerGroupId, false); break; case "chatTypeSupergroup": isSuperGroup = true; chatInformationPage.chatPartnerGroupId = chatInformation.type.supergroup_id.toString(); if(!groupInformation.id) { groupInformation = tdLibWrapper.getSuperGroup(chatInformationPage.chatPartnerGroupId); } tdLibWrapper.getGroupFullInfo(chatInformationPage.chatPartnerGroupId, true); isChannel = groupInformation.is_channel; break; } if(!isPrivateChat) { updateGroupStatusText(); } } function scrollUp(force) { if(force) { // animation does not always work while quick scrolling scrollUpTimer.start() } else { scrollUpAnimation.start() } } function scrollDown(force) { if(force) { scrollDownTimer.start() } else { scrollDownAnimation.start() } } function handleBasicGroupFullInfo(groupFullInfo) { groupFullInformation = groupFullInfo; membersList.clear(); if(groupFullInfo.members && groupFullInfo.members.length > 0) { for(var memberIndex in groupFullInfo.members) { var memberData = groupFullInfo.members[memberIndex]; var userInfo = tdLibWrapper.getUserInformation(memberData.user_id) || {user:{}, bot_info:{}}; memberData.user = userInfo; memberData.bot_info = memberData.bot_info || {}; membersList.append(memberData); } groupInformation.member_count = groupFullInfo.members.length updateGroupStatusText(); } } Connections { target: tdLibWrapper onChatOnlineMemberCountUpdated: { if ((isSuperGroup || isBasicGroup) && chatInformation.id.toString() === chatId) { chatOnlineMemberCount = onlineMemberCount; updateGroupStatusText(); } } onSupergroupFullInfoReceived: { if(isSuperGroup && chatInformationPage.chatPartnerGroupId === groupId) { groupFullInformation = groupFullInfo; } } onSupergroupFullInfoUpdated: { if(isSuperGroup && chatInformationPage.chatPartnerGroupId === groupId) { groupFullInformation = groupFullInfo; } } onBasicGroupFullInfoReceived: { if(isBasicGroup && chatInformationPage.chatPartnerGroupId === groupId) { handleBasicGroupFullInfo(groupFullInfo) } } onBasicGroupFullInfoUpdated: { if(isBasicGroup && chatInformationPage.chatPartnerGroupId === groupId) { handleBasicGroupFullInfo(groupFullInfo) } } onUserFullInfoReceived: { if(isPrivateChat && userFullInfo["@extra"] === chatInformationPage.chatPartnerGroupId) { chatPartnerFullInformation = userFullInfo; } } onUserFullInfoUpdated: { if(isPrivateChat && userId === chatInformationPage.chatPartnerGroupId) { chatPartnerFullInformation = userFullInfo; } } onUserProfilePhotosReceived: { if(isPrivateChat && extra === chatInformationPage.chatPartnerGroupId) { chatPartnerProfilePhotos = photos; } } onChatPermissionsUpdated: { if (chatInformation.id.toString() === chatId) { // set whole object to trigger change var newInformation = chatInformation; newInformation.permissions = permissions chatInformation = newInformation } } onChatTitleUpdated: { if (chatInformation.id.toString() === chatId) { // set whole object to trigger change var newInformation = chatInformation; newInformation.title = title chatInformation = newInformation } } } Component.onCompleted: initializePage(); ListModel { id: membersList } AppNotification { id: infoNotification } SilicaFlickable { id: pageFlickable anchors.fill: parent PullDownMenu { MenuItem { visible: (isBasicGroup || isSuperGroup) && userIsMember text: qsTr("Leave Group") onClicked: { // ensure it's done even if the page is closed: var remorse = Remorse.popupAction(appWindow, qsTr("Leaving chat"), (function(chatid) { return function() { tdLibWrapper.leaveChat(chatid); // this does not care about the response (ideally type "ok" without further reference) for now pageStack.pop(pageStack.find( function(page){ return(page._depth === 0)} )); }; }(chatInformation.id.toString()))) } } MenuItem { visible: userIsMember onClicked: { var newNotificationSettings = chatInformation.notification_settings; if (newNotificationSettings.mute_for > 0) { newNotificationSettings.mute_for = 0; } else { newNotificationSettings.mute_for = 6666666; } tdLibWrapper.setChatNotificationSettings(chat_id, newNotificationSettings); } text: chatInformation.notification_settings.mute_for > 0 ? qsTr("Unmute Chat") : qsTr("Mute Chat") } // MenuItem { //TODO Implement // visible: !userIsMember // onClicked: { // tdLibWrapper.joinChat(chat_id); // } // text: qsTr("Join Chat") // } } // header PageHeader { id: headerItem z: 5 Item { id: imageContainer property bool hasImage: typeof chatInformation.photo !== "undefined" property int minDimension: chatInformationPage.isLandscape ? Theme.itemSizeSmall : Theme.itemSizeMedium property int maxDimension: Screen.width / 2 property int minX: Theme.horizontalPageMargin property int maxX: (chatInformationPage.width - maxDimension)/2 property int minY: (parent.height - minDimension)/2 property int maxY: parent.height property double tweenFactor: { if(!hasImage) { return 0 } return 1 - Math.max(0, Math.min(1, contentFlickable.contentY / maxDimension)) } function getEased(min,max,factor) { return min + (max-min)*factor } width: getEased(minDimension,maxDimension, tweenFactor) height: width x: getEased(minX,maxX, tweenFactor) y: getEased(minY,maxY, tweenFactor) ProfileThumbnail { id: chatPictureThumbnail photoData: imageContainer.hasImage ? chatInformation.photo.small : "" replacementStringHint: headerItem.title width: parent.width height: width radius: imageContainer.minDimension / 2 opacity: profilePictureLoader.status !== Loader.Ready || profilePictureLoader.item.opacity < 1 ? 1.0 : 0.0 } Loader { id: profilePictureLoader active: imageContainer.hasImage asynchronous: true anchors.fill: chatPictureThumbnail source: chatInformationPage.isPrivateChat ? "../components/chatInformationPage/ChatInformationProfilePictureList.qml" : "../components/chatInformationPage/ChatInformationProfilePicture.qml" } } leftMargin: imageContainer.minDimension + Theme.horizontalPageMargin + Theme.paddingMedium title: chatInformation.title !== "" ? Emoji.emojify(chatInformation.title, Theme.fontSizeLarge) : qsTr("Unknown") description: chatInformationPage.isPrivateChat ? ("@"+(chatInformationPage.privateChatUserInformation.username || chatInformationPage.chatPartnerGroupId)) : "" } SilicaFlickable { id: contentFlickable contentHeight: groupInfoItem.height + tabViewLoader.height clip: true interactive: true//groupInfoItem.height > pageFlickable.height * 0.5 anchors { top: headerItem.bottom bottom: parent.bottom left: parent.left right: parent.right } NumberAnimation { id: scrollDownAnimation target: contentFlickable to: groupInfoItem.height property: "contentY" duration: 500 easing.type: Easing.InOutCubic } NumberAnimation { id: scrollUpAnimation target: contentFlickable to: 0 property: "contentY" duration: 500 easing.type: Easing.InOutCubic property Timer scrollUpTimer: Timer { id: scrollUpTimer interval: 50 onTriggered: { contentFlickable.scrollToTop() } } property Timer scrollDownTimer: Timer { id: scrollDownTimer interval: 50 onTriggered: { contentFlickable.scrollToBottom() } } } Column { id: groupInfoItem bottomPadding: Theme.paddingLarge topPadding: Theme.paddingLarge anchors { top: parent.top left: parent.left leftMargin: Theme.horizontalPageMargin right: parent.right rightMargin: Theme.horizontalPageMargin } Item { //large image placeholder width: parent.width height: imageContainer.hasImage ? imageContainer.maxDimension : 0 } ChatInformationEditArea { visible: canEdit canEdit: !chatInformationPage.isPrivateChat && chatInformationPage.groupInformation.status && (chatInformationPage.groupInformation.status.can_change_info || chatInformationPage.groupInformation.status["@type"] === "chatMemberStatusCreator") headerText: qsTr("Chat Title", "group title header") text: chatInformation.title onSaveButtonClicked: { if(!editItem.errorHighlight) { tdLibWrapper.setChatTitle(chatInformationPage.chatInformation.id, textValue); } else { isEditing = true } } onTextEdited: { if(textValue.length > 0 && textValue.length < 129) { editItem.errorHighlight = false editItem.label = "" editItem.placeholderText = "" } else { editItem.label = qsTr("Enter 1-128 characters") editItem.placeholderText = editItem.label editItem.errorHighlight = true } } } ChatInformationEditArea { canEdit: (chatInformationPage.isPrivateChat && chatInformationPage.privateChatUserInformation.id === chatInformationPage.myUserId) || ((chatInformationPage.isBasicGroup || chatInformationPage.isSuperGroup) && chatInformationPage.groupInformation && (chatInformationPage.groupInformation.status.can_change_info || chatInformationPage.groupInformation.status["@type"] === "chatMemberStatusCreator")) emptyPlaceholderText: qsTr("There is no information text available, yet.") headerText: qsTr("Info", "group or user infotext header") multiLine: true text: (chatInformationPage.isPrivateChat ? chatInformationPage.chatPartnerFullInformation.bio : chatInformationPage.groupFullInformation.description) || "" onSaveButtonClicked: { if(chatInformationPage.isPrivateChat) { // own bio tdLibWrapper.setBio(textValue); } else { // group info tdLibWrapper.setChatDescription(chatInformationPage.chatInformation.id, textValue); } } } ChatInformationTextItem { headerText: qsTr("Phone Number", "user phone number header") text: (chatInformationPage.isPrivateChat && chatInformationPage.privateChatUserInformation.phone_number ? "+"+chatInformationPage.privateChatUserInformation.phone_number : "") || "" isLinkedLabel: true } SectionHeader { font.pixelSize: Theme.fontSizeExtraSmall visible: !!inviteLinkItem.text height: visible ? Theme.itemSizeExtraSmall : 0 text: qsTr("Invite Link", "header") x: 0 } Row { width: parent.width visible: !!inviteLinkItem.text ChatInformationTextItem { id: inviteLinkItem text: !isPrivateChat ? groupFullInformation.invite_link : "" width: parent.width - inviteLinkButton.width } IconButton { id: inviteLinkButton icon.source: "image://theme/icon-m-clipboard" anchors.verticalCenter: inviteLinkItem.verticalCenter onClicked: { inviteLinkField.selectAll(); inviteLinkField.copy(); inviteLinkField.select(0,0); infoNotification.show(qsTr("The Invite Link has been copied to the clipboard.")); } TextField { id: inviteLinkField width: 0 height: 0 text: groupFullInformation.invite_link ? groupFullInformation.invite_link : "" visible: false readOnly: true opacity: 0 } } } Item { width: parent.width height: Theme.paddingLarge } Separator { width: parent.width color: Theme.primaryColor horizontalAlignment: Qt.AlignHCenter opacity: (tabViewLoader.status === Loader.Ready && tabViewLoader.item.count > 0) ? 1.0 : 0.0 Behavior on opacity { PropertyAnimation {duration: 500}} } } Loader { id: tabViewLoader asynchronous: true active: true anchors { left: parent.left right: parent.right top: groupInfoItem.bottom } sourceComponent: Component { ChatInformationTabView { id: tabView height: tabView.count > 0 ? chatInformationPage.height - headerItem.height : 0 } } } } } function updateGroupStatusText() { if (chatOnlineMemberCount > 0) { headerItem.description = qsTr("%1 members, %2 online").arg(Functions.getShortenedCount(groupInformation.member_count)).arg(Functions.getShortenedCount(chatOnlineMemberCount)); } else { if (isChannel) { headerItem.description = qsTr("%1 subscribers").arg(Functions.getShortenedCount(groupInformation.member_count)); } else { headerItem.description = qsTr("%1 members").arg(Functions.getShortenedCount(groupInformation.member_count)); } } } }