1
0
Fork 0
mirror of https://github.com/HenkKalkwater/harbour-sailfin.git synced 2025-09-05 10:12:46 +00:00

Rewire more of Sailfish frontend into new backend

This should encompass most simple things, besides some larger, trickier
things, like the video streams and the now-broken userdata
This commit is contained in:
Chris Josten 2021-08-11 23:35:33 +02:00
parent df1e134821
commit 7b6c272aa9
47 changed files with 620 additions and 291 deletions

View file

@ -54,7 +54,7 @@ Page {
"Copyright © Chris Josten 2020</p>" +
"<p>Sailfin is Free Software licensed under the <a href='lgpl'>LGPL-v2.1</a> or later, at your choice. " +
"Parts of the code of Sailfin are from other libraries. <a href='3rdparty'>View their licenses here</a>.</p>")
.arg(ApiClient.version)
.arg(apiClient.version)
textFormat: Text.StyledText
color: Theme.secondaryHighlightColor
linkColor: Theme.primaryColor

View file

@ -23,7 +23,6 @@ import nl.netsoj.chris.Jellyfin 1.0 as J
import "../components"
import "../"
import "../Utils.js" as Utils
/**
* Main page, which simply shows some content of every library, as well as next items.
@ -49,7 +48,7 @@ Page {
text: qsTr("Reload")
onClicked: loadModels(true)
}
busy: userViewsLoader.status === J.UsersViewsLoader.Loading
busy: mediaLibraryLoader.status === J.UsersViewsLoader.Loading
}
}
@ -73,7 +72,7 @@ Page {
id: mediaLibraryModel
loader: J.UsersViewsLoader {
id: mediaLibraryLoader
apiClient: ApiClient
apiClient: appWindow.apiClient
}
}
@ -81,7 +80,7 @@ Page {
//- Section header for films and TV shows that an user hasn't completed yet.
text: qsTr("Resume watching")
clickable: false
//busy: userResumeModel.status === J.ApiModel.Loading
busy: userResumeLoader.status === J.ApiModel.Loading
Loader {
width: parent.width
sourceComponent: carrouselView
@ -90,10 +89,12 @@ Page {
J.ItemModel {
id: userResumeModel
// Resume model
/*apiClient: ApiClient
limit: 12
recursive: true*/
loader: J.ResumeItemsLoader {
id: userResumeLoader
apiClient: appWindow.apiClient
limit: 12
//recursive: true*/
}
}
}
}
@ -111,7 +112,7 @@ Page {
J.ItemModel {
id: showNextUpModel
/*apiClient: ApiClient
/*apiClient: appWindow.apiClient
limit: 12*/
}
}
@ -132,7 +133,7 @@ Page {
J.ItemModel {
id: userItemModel
loader: J.LatestMediaLoader {
apiClient: ApiClient
apiClient: appWindow.apiClient
parentId: jellyfinId
}
}
@ -171,7 +172,7 @@ Page {
}
Connections {
target: ApiClient
target: appWindow.apiClient
onAuthenticatedChanged: loadModels(false)
}
@ -181,7 +182,7 @@ Page {
* even if loaded.
*/
function loadModels(force) {
if (force || (ApiClient.authenticated && !_modelsLoaded)) {
if (force || (appWindow.apiClient.authenticated && !_modelsLoaded)) {
_modelsLoaded = true;
mediaLibraryModel.reload()
//userResumeModel.reload()
@ -192,14 +193,15 @@ Page {
Component {
id: carrouselView
SilicaListView {
property bool isPortrait: Utils.usePortraitCover(collectionType)
id: list
clip: true
height: {
if (count > 0) {
if (["tvshows", "movies"].indexOf(collectionType) == -1) {
Constants.libraryDelegateHeight
} else {
if (isPortrait) {
Constants.libraryDelegatePosterHeight
} else {
Constants.libraryDelegateHeight
}
} else {
0
@ -217,12 +219,12 @@ Page {
delegate: LibraryItemDelegate {
property string id: model.jellyfinId
title: model.name
poster: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, model.imageTags["Primary"], "Primary", {"maxHeight": height})
poster: Utils.itemModelImageUrl(appWindow.apiClient.baseUrl, model.jellyfinId, model.imageTags["Primary"], "Primary", {"maxHeight": height})
Binding on blurhash {
when: poster !== ""
value: model.imageBlurHashes["Primary"][model.imageTags["Primary"]]
}
landscape: !Utils.usePortraitCover(collectionType)
landscape: !isPortrait
progress: (typeof model.userData !== "undefined") ? model.userData.playedPercentage / 100 : 0.0
onClicked: {

View file

@ -22,6 +22,7 @@ import Sailfish.Silica 1.0
import nl.netsoj.chris.Jellyfin 1.0 as J
import "../components"
import ".."
Page {
id: settingsPage
@ -69,7 +70,7 @@ Page {
top: parent.top
bottom: parent.bottom
}
source: ApiClient.baseUrl + "/Users/" + ApiClient.userId + "/Images/Primary?tag=" + loggedInUser.primaryImageTag
source: apiClient.baseUrl + "/Users/" + apiClient.userId + "/Images/Primary?tag=" + loggedInUser.primaryImageTag
}
Label {
@ -80,7 +81,7 @@ Page {
bottom: parent.verticalCenter
right: parent.right
}
text: loggedInUser.status == User.Ready ? loggedInUser.name : ApiClient.userId
text: loggedInUser.status == User.Ready ? loggedInUser.name : apiClient.userId
color: Theme.highlightColor
}
@ -92,7 +93,7 @@ Page {
top: parent.verticalCenter
right: parent.right
}
text: ApiClient.baseUrl
text: apiClient.baseUrl
color: Theme.secondaryHighlightColor
}
@ -104,21 +105,23 @@ Page {
ButtonLayout {
Button {
text: qsTr("Log out")
onClicked: remorse.execute(qsTr("Logging out"), ApiClient.deleteSession)
onClicked: remorse.execute(qsTr("Logging out"), apiClient.deleteSession)
}
}
SectionHeader {
//: Other settings
//: Other settings menu item
text: qsTr("Other")
}
IconListItem {
//: Debug information settings menu itemy
text: qsTr("Debug information")
iconSource: "image://theme/icon-s-developer"
onClicked: pageStack.push(Qt.resolvedUrl("settings/DebugPage.qml"))
}
//: About Sailfin settings menu itemy
IconListItem {
text: qsTr("About Sailfin")
iconSource: "image://theme/icon-m-about"

View file

@ -21,7 +21,7 @@ import Sailfish.Silica 1.0
import "../components"
import nl.netsoj.chris.Jellyfin 1.0
import nl.netsoj.chris.Jellyfin 1.0 as J
/**
* Page only containing a video player.
@ -33,7 +33,7 @@ Page {
id: videoPage
// PlaybackBar will hide itself when it encounters a page with such a property
property bool __hidePlaybackBar: true
property JellyfinItem itemData
property var itemData
property int audioTrack
property int subtitleTrack
property bool resume: true

View file

@ -22,7 +22,7 @@ import Sailfish.Silica 1.0
import nl.netsoj.chris.Jellyfin 1.0 as J
import "../../components"
import "../.."
import "../../"
/**
* This page displays details about a film, show, season, episode, and so on.
@ -34,8 +34,6 @@ Page {
id: pageRoot
property string itemId: ""
property alias itemData: jItemLoader.data
//property string itemId: ""
//property var itemData: ({})
property bool _loading: jItemLoader.status === J.ItemLoader.Loading
readonly property bool hasLogo: (typeof itemData.imageTags !== "undefined") && (typeof itemData.imageTags["Logo"] !== "undefined")
property string _chosenBackdropImage: ""
@ -46,10 +44,10 @@ Page {
if (itemData.backdropImageTags.length > 0) {
rand = Math.floor(Math.random() * (itemData.backdropImageTags.length - 0.001))
console.log("Random: ", rand)
_chosenBackdropImage = ApiClient.baseUrl + "/Items/" + itemId + "/Images/Backdrop/" + rand + "?tag=" +itemData.backdropImageTags[rand] + "&maxHeight" + height
_chosenBackdropImage = apiClient.baseUrl + "/Items/" + itemId + "/Images/Backdrop/" + rand + "?tag=" +itemData.backdropImageTags[rand] + "&maxHeight" + height
} else if (itemData.parentBackdropImageTags.length > 0) {
rand = Math.floor(Math.random() * (itemData.parentBackdropImageTags.length - 0.001))
_chosenBackdropImage = ApiClient.baseUrl + "/Items/" + itemData.parentBackdropItemId + "/Images/Backdrop/" + rand + "?tag=" + itemData.parentBackdropImageTags[0]
_chosenBackdropImage = apiClient.baseUrl + "/Items/" + itemData.parentBackdropItemId + "/Images/Backdrop/" + rand + "?tag=" + itemData.parentBackdropImageTags[0]
}
}
@ -86,8 +84,9 @@ Page {
J.ItemLoader {
id: jItemLoader
apiClient: ApiClient
apiClient: appWindow.apiClient
itemId: pageRoot.itemId
autoReload: false
onStatusChanged: {
console.log("Status changed: " + newStatus, JSON.stringify(jItemLoader.data))
if (status === J.ItemLoader.Ready) {
@ -96,15 +95,13 @@ Page {
}
}
Label {
text: "ItemLoader status=%1, \nitemId=%2\nitemData=%3".arg(jItemLoader.status).arg(jItemLoader.itemId).arg(jItemLoader.data)
}
onStatusChanged: {
if (status == PageStatus.Deactivating) {
if (status === PageStatus.Deactivating) {
//appWindow.itemData = ({})
}
if (status == PageStatus.Active) {
if (status === PageStatus.Active) {
console.log("Page ready, ItemID: ", itemId, ", UserID: ", apiClient.userId)
jItemLoader.autoReload = true
//appWindow.itemData = jItemLoader.data
}
}

View file

@ -29,10 +29,13 @@ BaseDetailPage {
J.ItemModel {
id: collectionModel
//sortBy: ["SortName"]
loader: J.UserItemsLoader {
apiClient: ApiClient
id: collectionLoader
apiClient: appWindow.apiClient
parentId: itemData.jellyfinId
autoReload: itemData.jellyfinId.length > 0
onParentIdChanged: if (parentId.length > 0) reload()
sortBy: "SortName"
}
}
@ -61,7 +64,7 @@ BaseDetailPage {
RemoteImage {
id: itemImage
anchors.fill: parent
source: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxWidth": width})
source: Utils.itemModelImageUrl(apiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxWidth": width})
blurhash: model.imageBlurHashes.Primary[model.imageTags.Primary]
fallbackColor: Utils.colorFromString(model.name)
fillMode: Image.PreserveAspectCrop
@ -138,20 +141,20 @@ BaseDetailPage {
MenuItem {
//: Sort order
text: qsTr("Ascending")
onClicked: apply(model.value, ApiModel.Ascending)
onClicked: apply(model.value, "Ascending")
}
MenuItem {
//: Sort order
text: qsTr("Descending")
onClicked: apply(model.value, ApiModel.Descending)
onClicked: apply(model.value, "Descending")
}
}
onClicked: openMenu()
function apply(field, order) {
collectionModel.sortBy = [field];
collectionModel.sortOrder = order;
collectionModel.reload()
collectionLoader.sortBy = field;
collectionLoader.sortOrder = order;
collectionLoader.reload()
pageStack.pop()
}
}

View file

@ -36,11 +36,12 @@ BaseDetailPage {
J.ItemModel {
id: collectionModel
loader: J.UserItemsLoader {
apiClient: ApiClient
//sortBy: ["SortName"]
apiClient: appWindow.apiClient
sortBy: "SortName"
//fields: ["ItemCounts","PrimaryImageAspectRatio","BasicSyncInfo","CanDelete","MediaSourceCount"]
parentId: itemData.jellyfinId
onParentIdChanged: reload()
autoReload: itemData.jellyfinId.length > 0
onParentIdChanged: if (parentId.length > 0) reload()
}
}
RowLayout {
@ -52,9 +53,9 @@ BaseDetailPage {
visible: _twoColumns
Layout.minimumWidth: 1000 / Theme.pixelRatio
Layout.fillHeight: true
/*source: visible
source: visible
? "../../components/music/WideAlbumCover.qml" : ""
onLoaded: bindAlbum(item)*/
onLoaded: bindAlbum(item)
}
Item {height: 1; width: Theme.horizontalPageMargin; visible: wideAlbumCover.visible; }
SilicaListView {
@ -64,8 +65,8 @@ BaseDetailPage {
model: collectionModel
header: Loader {
width: parent.width
/*source: "../../components/music/NarrowAlbumCover.qml"
onLoaded: bindAlbum(item)*/
source: "../../components/music/NarrowAlbumCover.qml"
onLoaded: bindAlbum(item)
}
section {
property: "parentIndexNumber"
@ -79,7 +80,7 @@ BaseDetailPage {
artists: model.artists
duration: model.runTimeTicks
indexNumber: model.indexNumber
onClicked: window.playbackManager.playItem(model.jellyfinId)
onClicked: window.playbackManager.playItemInList(collectionModel, model.index)
}
VerticalScrollDecorator {}
@ -87,7 +88,7 @@ BaseDetailPage {
}
function bindAlbum(item) {
//item.albumArt = Qt.binding(function(){ return Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})})
item.albumArt = Qt.binding(function(){ return Utils.itemImageUrl(apiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})})
item.name = Qt.binding(function(){ return itemData.name})
item.releaseYear = Qt.binding(function() { return itemData.productionYear})
item.albumArtist = Qt.binding(function() { return itemData.albumArtist})

View file

@ -1,7 +1,7 @@
import QtQuick 2.6
import Sailfish.Silica 1.0
import nl.netsoj.chris.Jellyfin 1.0
import nl.netsoj.chris.Jellyfin 1.0 as J
import "../../components"
@ -21,7 +21,7 @@ BaseDetailPage {
RemoteImage {
id: image
source: ApiClient.downloadUrl(itemId)
source: apiClient.downloadUrl(itemId)
fillMode: Image.PreserveAspectFit
anchors.fill: parent

View file

@ -19,19 +19,22 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import QtQuick 2.6
import Sailfish.Silica 1.0
import nl.netsoj.chris.Jellyfin 1.0
import nl.netsoj.chris.Jellyfin 1.0 as J
import "../.."
import "../../components"
import ".."
BaseDetailPage {
ShowEpisodesModel {
J.ItemModel {
id: episodeModel
apiClient: ApiClient
show: itemData.seriesId
seasonId: itemData.jellyfinId
fields: ["Overview"]
loader: J.ShowEpisodesLoader {
apiClient: appWindow.apiClient
seriesId: itemData.seriesId
seasonId: itemData.jellyfinId
fields: [J.ItemFields.Overview]
autoReload: itemData.jellyfinId.length > 0
}
}
Connections {
@ -42,7 +45,7 @@ BaseDetailPage {
SilicaListView {
anchors.fill: parent
contentHeight: content.height
visible: itemData.status !== JellyfinItem.Error
//visible: itemData.status !== JellyfinItem.Error
header: PageHeader {
title: itemData.name
@ -60,7 +63,7 @@ BaseDetailPage {
}
width: Constants.libraryDelegateWidth
height: Constants.libraryDelegateHeight
source: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxHeight": height})
source: Utils.itemModelImageUrl(apiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxHeight": height})
blurhash: model.imageBlurHashes.Primary[model.imageTags.Primary]
fillMode: Image.PreserveAspectCrop
clip: true
@ -157,8 +160,8 @@ BaseDetailPage {
onStatusChanged: {
if (status == PageStatus.Active) {
//console.log(JSON.stringify(itemData))
episodeModel.show = itemData.seriesId
episodeModel.seasonId = itemData.jellyfinId
//episodeModel.show = itemData.seriesId
//episodeModel.seasonId = itemData.jellyfinId
}
}
}

View file

@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import QtQuick 2.6
import Sailfish.Silica 1.0
import nl.netsoj.chris.Jellyfin 1.0
import nl.netsoj.chris.Jellyfin 1.0 as J
import "../../components"
import "../.."
@ -28,7 +28,7 @@ BaseDetailPage {
SilicaFlickable {
anchors.fill: parent
contentHeight: content.height
visible: itemData.status !== JellyfinItem.Error
//visible: itemData.status !== JellyfinItem.Error
Column {
id: content
@ -47,7 +47,7 @@ BaseDetailPage {
RemoteImage {
id: logoImage
anchors.centerIn: parent
source: Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Logo")
source: Utils.itemImageUrl(apiClient.baseUrl, itemData, "Logo")
}
}
@ -63,11 +63,14 @@ BaseDetailPage {
text: qsTr("Seasons")
}
ShowSeasonsModel {
J.ItemModel {
id: showSeasonsModel
apiClient: ApiClient
show: itemData.jellyfinId
onShowChanged: reload()
loader: J.ShowSeasonsLoader {
id: showSeasonLoader
apiClient: appWindow.apiClient
seriesId: itemData.jellyfinId
autoReload: itemData.jellyfinId.length > 0
}
}
Connections {
target: itemData
@ -84,7 +87,7 @@ BaseDetailPage {
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
delegate: LibraryItemDelegate {
poster: Utils.itemModelImageUrl(ApiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxHeight": height})
poster: Utils.itemModelImageUrl(apiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxHeight": height})
blurhash: model.imageBlurHashes["Primary"][model.imageTags.Primary]
title: model.name
onClicked: pageStack.push(Utils.getPageUrl(model.mediaType, model.type), {"itemId": model.jellyfinId})

View file

@ -31,7 +31,7 @@ BaseDetailPage {
enabled: true
text: qsTr("Item type (%1) unsupported").arg(itemData.type)
hintText: qsTr("This is still an alpha version :)")
hintText: qsTr("Fallback page for %2 not found either\nThis is still an alpha version :)").arg(itemData.mediaType)
}
}
}

View file

@ -55,7 +55,7 @@ BaseDetailPage {
PlayToolbar {
id: toolbar
width: parent.width
imageSource: Utils.itemImageUrl(ApiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})
imageSource: Utils.itemImageUrl(apiClient.baseUrl, itemData, "Primary", {"maxWidth": parent.width})
imageAspectRatio: Constants.horizontalVideoAspectRatio
imageBlurhash: itemData.imageBlurHashes["Primary"][itemData.imageTags["Primary"]]
Binding on favourited {

View file

@ -54,7 +54,7 @@ Page {
label: qsTr("Connection state")
value: {
var stateText
switch(ApiClient.websocket.state) {
switch(apiClient.websocket.state) {
case 0:
//- Socket state
stateText = qsTr("Unconnected");
@ -85,7 +85,7 @@ Page {
break;
}
//- Socket state: "state no (state description)"
qsTr("%1 (%2)").arg(ApiClient.websocket.state).arg(stateText)
qsTr("%1 (%2)").arg(apiClient.websocket.state).arg(stateText)
}
}
@ -105,7 +105,7 @@ Page {
Label {
id: deviceProfile
color: Theme.secondaryHighlightColor
text: JSON.stringify(ApiClient.deviceProfile, null, '\t')
text: JSON.stringify(apiClient.deviceProfile, null, '\t')
}
HorizontalScrollDecorator {}
}

View file

@ -26,27 +26,26 @@ import "../.."
* Page to indicate that the application is connecting to a server.
*/
Page {
property string serverName
property string serverAddress
property Page firstPage
allowedOrientations: Orientation.All
BusyLabel {
text: qsTr("Connecting to %1").arg(serverName)
running: true
}
onStatusChanged: {
if (status == PageStatus.Active) {
console.log("Connecting page active");
ApiClient.setupConnection();
}
}
Connections {
target: ApiClient
property string serverName
property string serverAddress
property Page firstPage
allowedOrientations: Orientation.All
BusyLabel {
text: qsTr("Connecting to %1").arg(serverName)
running: true
}
onStatusChanged: {
if (status == PageStatus.Active) {
apiClient.setupConnection();
}
}
Connections {
target: apiClient
onConnectionSuccess: {
console.log("Login success: " + loginMessage);
pageStack.replace(Qt.resolvedUrl("LoginDialog.qml"), {"loginMessage": loginMessage, "firstPage": firstPage});

View file

@ -28,40 +28,40 @@ import "../../"
* This dialog allows manual address entry or use one of the addresses discovered via UDP broadcasts.
*/
Dialog {
id: dialogRoot
allowedOrientations: Orientation.All
// Picks the address of the ComboBox if selected, otherwise the manual address entry
readonly property string address: serverSelect.currentItem._address
readonly property bool addressCorrect: serverSelect.currentIndex > 0 || manualAddress.acceptableInput
readonly property string serverName: serverSelect.currentItem._name
id: dialogRoot
allowedOrientations: Orientation.All
// Picks the address of the ComboBox if selected, otherwise the manual address entry
readonly property string address: serverSelect.currentItem._address
readonly property bool addressCorrect: serverSelect.currentIndex > 0 || manualAddress.acceptableInput
readonly property string serverName: serverSelect.currentItem._name
readonly property bool _isSetupPage: true
acceptDestination: AddServerConnectingPage {
id: connectingPage
serverName: dialogRoot.serverName
serverAddress: address
firstPage: dialogRoot
}
Column {
width: parent.width
DialogHeader {
acceptText: qsTr("Connect")
title: qsTr("Connect to Jellyfin")
}
J.ServerDiscoveryModel {
id: serverModel
}
ComboBox {
id: serverSelect
label: qsTr("Server")
description: qsTr("Sailfin will try to search for Jellyfin servers on your local network automatically")
menu: ContextMenu {
acceptDestination: AddServerConnectingPage {
id: connectingPage
serverName: dialogRoot.serverName
serverAddress: address
firstPage: dialogRoot
}
Column {
width: parent.width
DialogHeader {
acceptText: qsTr("Connect")
title: qsTr("Connect to Jellyfin")
}
J.ServerDiscoveryModel {
id: serverModel
}
ComboBox {
id: serverSelect
label: qsTr("Server")
description: qsTr("Sailfin will try to search for Jellyfin servers on your local network automatically")
menu: ContextMenu {
MenuItem {
// Special values are cool, aren't they?
readonly property string _address: manualAddress.text
@ -106,8 +106,8 @@ Dialog {
function tryConnect() {
console.log("Hi there!")
ApiClient.baseUrl = address;
//ApiClient.setupConnection()
apiClient.baseUrl = address;
//apiClient.setupConnection()
//fakeTimer.start()
}

View file

@ -46,14 +46,14 @@ Dialog {
}
onStatusChanged: {
if(status == PageStatus.Active) {
ApiClient.authenticate(username.text, password.text, true)
apiClient.authenticate(username.text, password.text, true)
}
}
Connections {
target: ApiClient
target: apiClient
onAuthenticatedChanged: {
if (ApiClient.authenticated) {
if (apiClient.authenticated) {
console.log("authenticated!")
pageStack.replaceAbove(null, Qt.resolvedUrl("../MainPage.qml"))
}
@ -70,7 +70,7 @@ Dialog {
/*PublicUserModel {
id: userModel
apiClient: ApiClient
apiClient: appWindow.apiClient
Component.onCompleted: reload();
}*/
@ -103,7 +103,7 @@ Dialog {
model: 0 //userModel
delegate: UserGridDelegate {
name: model.name
image: model.primaryImageTag ? "%1/Users/%2/Images/Primary?tag=%3".arg(ApiClient.baseUrl).arg(model.jellyfinId).arg(model.primaryImageTag) : ""
image: model.primaryImageTag ? "%1/Users/%2/Images/Primary?tag=%3".arg(apiClient.baseUrl).arg(model.jellyfinId).arg(model.primaryImageTag) : ""
highlighted: model.name === username.text
onHighlightedChanged: {
if (highlighted) {