status-desktop/storybook/pages/StatusChatInputPage.qml
Alex Jbanca 4a30d13bdc
feat(LinkPreviews): Integrate Link previews with the backend (#12523)
* feat(StatusQ): Adding numberToLocaleStringInCompactForm function to LocaleUtils

This function will format the number in a compact form
E.g: 1000 -> 1K; 1000000 -> 1M; 1100000 -> 1.1M

+ adding tests

fix(statusQ): Update numberToLocaleStringInCompactForm to return the locale number when greter than 999T

fix(StatusQ): extend the test_numberToLocaleStringInCompactForm with new data

* feat(LinkPreviews): Update the link preview area in StatusChatInput to use the new model

Changes:
1. Create a new component `LinkPreviewMiniCardDelegate.qml` that filters the model data to properly fill the link preview card with the needed data based on the preview type
2. Update storybook pages
3. Small updates to LinkPreviewMiniCard

* feat(LinkPreviews): Update the link previews in message history to use the new backend

Changes:
1. Create delegate items for LinkPreviewCard and gif link preview to clean the LinksMessageView component and filter the model data based on the preview type
2. Remove UserProfileCard and reuse the LinkPreviewCard to display contacts link previews
3. Update LinkPreviewCard so that it can accommodate status link previews (communities, channels, contacts). The generic properties (title, description, footer) have been dropped and replaced with specialised properties for each preview type.
4. Fix LinkPreviewCard layout to better accommodate different content variants (missing properties, long/short title, missing description, missing icon)
5. Fixing the link preview context menu and click actions

fix: Move inline components to separate files

Fixing the linux builds using Qt 5.15.2 affected by this bug:
https://bugreports.qt.io/browse/QTBUG-89180

* fix: Align LinkPreviewMiniCard implementation with LinkPreviewCard and remove state based model filtering
2023-10-25 18:20:02 +03:00

291 lines
10 KiB
QML

import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import Storybook 1.0
import Models 1.0
import utils 1.0
import shared.status 1.0
import shared.stores 1.0
SplitView {
id: root
Logs { id: logs }
QtObject {
id: globalUtilsMock
property bool ready: false
property var globalUtils: QtObject {
function plainText(htmlText) {
return htmlText.replace(/(?:<style[^]+?>[^]+?<\/style>|[\n]|<script[^]+?>[^]+?<\/script>|<(?:!|\/?[a-zA-Z]+).*?\/?>)/g,'')
}
function isCompressedPubKey(publicKey) {
return false
}
}
Component.onCompleted: {
Utils.globalUtilsInst = globalUtilsMock.globalUtils
Global.dragArea = null
globalUtilsMock.ready = true
}
}
QtObject {
id: rootStoreMock
property bool ready: false
readonly property ListModel gifColumnA: ListModel {}
readonly property var formationChars: (["*", "`", "~"])
function getSelectedTextWithFormationChars(messageInputField) {
let i = 1
let text = ""
while (true) {
if (messageInputField.selectionStart - i < 0 && messageInputField.selectionEnd + i > messageInputField.length) {
break
}
text = messageInputField.getText(messageInputField.selectionStart - i, messageInputField.selectionEnd + i)
if (!formationChars.includes(text.charAt(0)) ||
!formationChars.includes(text.charAt(text.length - 1))) {
break
}
i++
}
return text
}
Component.onCompleted: {
RootStore.isWalletEnabled = true
RootStore.gifUnfurlingEnabled = true
RootStore.getSelectedTextWithFormationChars = rootStoreMock.getSelectedTextWithFormationChars
RootStore.gifColumnA = rootStoreMock.gifColumnA
rootStoreMock.ready = true
}
}
UsersModel {
id: fakeUsersModel
}
LinkPreviewModel {
id: fakeLinksModel
}
SplitView {
orientation: Qt.Vertical
SplitView.fillWidth: true
//dummy item to position chatInput at the bottom
Item {
SplitView.fillHeight: true
SplitView.fillWidth: true
}
Loader {
id: chatInputLoader
active: rootStoreMock.ready && globalUtilsMock.ready
sourceComponent: StatusChatInput {
id: chatInput
property var globalUtils: globalUtilsMock.globalUtils
property string unformattedText: chatInput.textInput.getText(0, chatInput.textInput.length)
onUnformattedTextChanged: {
textEditConnection.enabled = false
d.loadLinkPreviews(unformattedText)
textEditConnection.enabled = true
}
Connections {
id: textEditConnection
target: chatInput.textInput
function onTextChanged() {
if(unformattedText !== chatInput.textInput.getText(0, chatInput.textInput.length))
unformattedText = chatInput.textInput.getText(0, chatInput.textInput.length)
}
}
enabled: enabledCheckBox.checked
linkPreviewModel: fakeLinksModel
urlsModel: fakeLinksModel
askToEnableLinkPreview: askToEnableLinkPreviewSwitch.checked
onAskToEnableLinkPreviewChanged: {
if(askToEnableLinkPreview) {
fakeLinksModel.clear()
d.loadLinkPreviews(unformattedText)
}
}
usersStore: QtObject {
readonly property var usersModel: fakeUsersModel
}
onSendMessage: {
logs.logEvent("StatusChatInput::sendMessage", ["MessageWithPk"], [chatInput.getTextWithPublicKeys()])
logs.logEvent("StatusChatInput::sendMessage", ["PlainText"], [globalUtilsMock.globalUtils.plainText(chatInput.getTextWithPublicKeys())])
logs.logEvent("StatusChatInput::sendMessage", ["RawText"], [chatInput.textInput.text])
}
onEnableLinkPreviewForThisMessage: {
linkPreviewSwitch.checked = true
askToEnableLinkPreviewSwitch.checked = false
}
onEnableLinkPreview: {
linkPreviewSwitch.checked = true
askToEnableLinkPreviewSwitch.checked = false
}
onDisableLinkPreview: {
linkPreviewSwitch.checked = false
askToEnableLinkPreviewSwitch.checked = false
}
onDismissLinkPreviewSettings: {
askToEnableLinkPreviewSwitch.checked = false
linkPreviewSwitch.checked = false
}
onDismissLinkPreview: (index) => {
fakeLinksModel.setProperty(index, "unfurled", false)
fakeLinksModel.setProperty(index, "immutable", true)
}
}
}
LogsAndControlsPanel {
id: logsAndControlsPanel
SplitView.minimumHeight: 100
SplitView.preferredHeight: 200
logsView.logText: logs.logText
}
QtObject {
id: d
property bool linkPreviewsEnabled: linkPreviewSwitch.checked && !askToEnableLinkPreviewSwitch.checked
onLinkPreviewsEnabledChanged: {
loadLinkPreviews(chatInputLoader.item ? chatInputLoader.item.unformattedText : "")
}
function loadLinkPreviews(text) {
var words = text.split(" ")
fakeLinksModel.clear()
words.forEach(function(word){
if(Utils.isURL(word)) {
const linkPreview = fakeLinksModel.getStandardLinkPreview()
linkPreview.url = encodeURI(word)
linkPreview.unfurled = Math.random() > 0.2
linkPreview.immutable = !d.linkPreviewsEnabled
linkPreview.empty = Math.random() > 0.7
fakeLinksModel.append(linkPreview)
}
})
}
}
}
Pane {
SplitView.minimumWidth: 300
SplitView.preferredWidth: 300
ColumnLayout {
anchors.fill: parent
CheckBox {
id: enabledCheckBox
text: "enabled"
checked: true
}
TabBar {
id: bar
TabButton {
text: "Attachments"
}
TabButton {
text: "Users"
}
}
StackLayout {
currentIndex: bar.currentIndex
ColumnLayout {
id: attachmentsTab
Layout.fillWidth: true
Layout.fillHeight: true
Label {
text: "Images"
Layout.fillWidth: true
}
ComboBox {
id: imageNb
editable: true
model: 20
validator: IntValidator {bottom: 0; top: 20;}
focus: true
onCurrentIndexChanged: {
if(!chatInputLoader.item)
return
const urls = []
for (let i = 0; i < imageNb.currentIndex ; i++) {
urls.push("https://picsum.photos/200/300?random=" + i)
}
console.log(urls.length)
chatInputLoader.item.fileUrlsAndSources = urls
}
}
Label {
text: "Links"
Layout.fillWidth: true
}
Switch {
id: linkPreviewSwitch
text: "Link Preview enabled"
}
Switch {
id: askToEnableLinkPreviewSwitch
text: "Ask to enable Link Preview"
checked: true
}
ComboBox {
id: linksNb
editable: true
model: 20
validator: IntValidator {bottom: 0; top: 20;}
onCurrentIndexChanged: {
if(!chatInputLoader.item)
return
let urls = ""
for (let i = 0; i < linksNb.currentIndex ; i++) {
urls += "https://www.youtube.com/watch?v=9bZkp7q19f0" + Math.floor(Math.random() * 100) + " "
}
chatInputLoader.item.textInput.text = urls
}
}
}
UsersModelEditor {
id: modelEditor
Layout.fillWidth: true
Layout.fillHeight: true
model: fakeUsersModel
onRemoveClicked: fakeUsersModel.remove(index, 1)
onRemoveAllClicked: fakeUsersModel.clear()
onAddClicked: fakeUsersModel.append(modelEditor.getNewUser(fakeUsersModel.count))
}
}
Label {
text: "Attachments"
Layout.fillWidth: true
}
}
}
}
// category: Components
// https://www.figma.com/file/Mr3rqxxgKJ2zMQ06UAKiWL/💬-Chat⎜Desktop?type=design&node-id=23155-66084&mode=design&t=VWBVK4DOUxr1BmTp-0