Commit 3663cbdd authored by Camilo Higuita's avatar Camilo Higuita

linking own view and allow searching collection of linked device

parent 31e08113
......@@ -32,9 +32,9 @@
class InterfaceConnFailedException : public QException
{
public:
void raise() const { throw *this; }
InterfaceConnFailedException *clone() const { return new InterfaceConnFailedException(*this); }
public:
void raise() const { throw *this; }
InterfaceConnFailedException *clone() const { return new InterfaceConnFailedException(*this); }
};
#elif defined(Q_OS_WINDOWS)
#elif defined(Q_OS_DARWIN)
......@@ -61,9 +61,9 @@ Babe::Babe(QObject *parent) : CollectionDB(parent)
{
switch(table)
{
case BAE::TABLE::TRACKS: emit this->refreshTracks(); break;
case BAE::TABLE::ALBUMS: emit this->refreshAlbums(); break;
case BAE::TABLE::ARTISTS: emit this->refreshArtists(); break;
case BAE::TABLE::TRACKS: emit this->refreshTracks(); break;
case BAE::TABLE::ALBUMS: emit this->refreshAlbums(); break;
case BAE::TABLE::ARTISTS: emit this->refreshArtists(); break;
}
});
......@@ -234,28 +234,19 @@ void Babe::linkDecoder(QString json)
qDebug()<<"DECODING LINKER MSG"<<json;
auto ask = link.decode(json);
auto code = ask[BAE::SLANG[BAE::W::CODE]].toInt();
auto msg = ask[BAE::SLANG[BAE::W::MSG]].toString();
switch(code)
if(code == LINK::CODE::CONNECTED)
{
case LINK::CODE::CONNECTED:
{
this->link.deviceName = msg;
emit this->link.serverConReady(msg);
break;
}
case LINK::CODE::PLAYLISTS:
{
auto playlists = this->getPlaylists();
QVariantMap map;
map.insert(LINK::DECODE[LINK::CODE::PLAYLISTS], playlists);
link.sendToClient(map);
break;
}
default: break;
this->link.deviceName = msg;
emit this->link.serverConReady(msg);
}
else
{
auto res = this->getDBDataQML(msg);
link.sendToClient(link.packResponse(static_cast<LINK::CODE>(code), res));
}
}
......
......@@ -3,8 +3,11 @@ import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
import QtQuick.Controls.Material 2.1
import "services/local"
import org.kde.kirigami 2.2 as Kirigami
import Link.Codes 1.0
//import QtQuick.Controls.Imagine 2.3
import "utils"
......@@ -107,7 +110,8 @@ Kirigami.ApplicationWindow
"playlists" : 3,
"babeit": 4,
"search" : 5,
"youtube" : 6
"youtube" : 6,
"linking" :7
})
property bool mainlistEmpty : !mainPlaylist.table.count > 0
......@@ -767,6 +771,11 @@ Kirigami.ApplicationWindow
id: youtubeView
}
LinkingView
{
id: linkingView
}
}
......@@ -876,40 +885,4 @@ Kirigami.ApplicationWindow
onBabeIt: Player.babeTrack()
}
Connections
{
target: link
onServerConReady:
{
isServing = true
H.notify(deviceName, "You're now linked!")
}
onClientConError:
{
isLinked = false
H.notify("Linking error", "error connecting to server")
}
onDevicesLinked:
{
isLinked = true
H.notify("Linked!", "The link is ready")
H.addPlaylist({ playlist: "Linked", playlistIcon: "link"})
}
onClientConDisconnected:
{
isLinked = false;
H.notify("Unlinked!", "The client is disconnected")
}
onServerConDisconnected:
{
isServing = false;
H.notify("Unlinked!", "The server is disconnected")
}
}
}
......@@ -80,5 +80,7 @@
<file>services/web/WebView.qml</file>
<file>services/web/WebView_A.qml</file>
<file>services/local/LinkingDialog.qml</file>
<file>services/local/LinkingView.qml</file>
<file>services/local/LinkingListModel.qml</file>
</qresource>
</RCC>
......@@ -7,7 +7,6 @@ import "../../utils/Help.js" as H
BabeDialog
{
id: linkingDialogRoot
title: "Add "+ tracks.length +" tracks to..."
standardButtons: Dialog.Save | Dialog.Cancel
margins: contentMargins
......
import QtQuick 2.9
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2
import org.kde.kirigami 2.2 as Kirigami
import "../../utils"
import "../../widgets/PlaylistsView"
import "../../view_models"
import "../../db/Queries.js" as Q
import "../../utils/Help.js" as H
import Link.Codes 1.0
BabeList
{
id: linkingListRoot
headerBarExit: false
headerBarTitle: isLinked ?link.getIp() : qsTr("Disconnected")
headerBarLeft: BabeButton
{
anim : true
iconName : "view-refresh"
onClicked : refreshPlaylists()
}
headerBarRight: BabeButton
{
id: menuBtn
iconName: "application-menu"
onClicked: linkingConf.open()
}
ListModel
{
id: linkingListModel
ListElement { playlist: qsTr("Albums"); playlistIcon: "view-media-album-cover"}
ListElement { playlist: qsTr("Artists"); playlistIcon: "view-media-artist"}
ListElement { playlist: qsTr("Most Played"); playlistIcon: "view-media-playcount" /*query: Q.Query.mostPlayedTracks*/ }
ListElement { playlist: qsTr("Favorites"); playlistIcon: "view-media-favorite"}
ListElement { playlist: qsTr("Recent"); playlistIcon: "view-media-recent"}
ListElement { playlist: qsTr("Babes"); playlistIcon: "love"}
ListElement { playlist: qsTr("Online"); playlistIcon: "internet-services"}
ListElement { playlist: qsTr("Tags"); playlistIcon: "tag"}
ListElement { playlist: qsTr("Relationships"); playlistIcon: "view-media-similarartists"}
ListElement { playlist: qsTr("Popular"); playlistIcon: "view-media-chart"}
ListElement { playlist: qsTr("Genres"); playlistIcon: "view-media-genre"}
}
model: linkingListModel
delegate : PlaylistViewDelegate
{
id: delegate
width: linkingListRoot.width
Connections
{
target : delegate
onClicked :
{
currentIndex = index
var playlist = linkingListModel.get(index).playlist
switch(playlist)
{
case "Artists":
populateExtra(LINK.FILTER, "select artist as tag from artists", playlist)
break
case "Albums":
populateExtra(LINK.FILTER, "select album as tag, artist from albums", playlist)
break
case "Most Played":
playlistViewRoot.populate(Q.GET.mostPlayedTracks);
break;
case "Favorites":
filterList.section.property = "stars"
playlistViewRoot.populate(Q.GET.favoriteTracks);
break;
case "Recent":
playlistViewRoot.populate(Q.GET.recentTracks);
break;
case "Babes":
playlistViewRoot.populate(Q.GET.babedTracks);
break;
case "Online":
playlistViewRoot.populate(Q.GET.favoriteTracks);
break;
case "Tags":
populateExtra(LINK.FILTER, Q.GET.tags, playlist)
break;
case "Relationships":
playlistViewRoot.populate(Q.GET.favoriteTracks);
break;
case "Popular":
playlistViewRoot.populate(Q.GET.favoriteTracks);
break;
case "Genres":
populateExtra(LINK.FILTER, Q.GET.genres, playlist)
break;
default:
playlistViewRoot.populate(Q.GET.playlistTracks_.arg(playlist));
break;
}
}
}
}
}
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import "../../view_models"
import "../../view_models/BabeTable"
import "../../widgets/PlaylistsView"
import "../../utils/Help.js" as H
import "../../db/Queries.js" as Q
import Link.Codes 1.0
import org.kde.kirigami 2.2 as Kirigami
ColumnLayout
{
id: linkingViewRoot
property alias linkingConf : linkingConf
signal rowClicked(var track)
signal quickPlayTrack(var track)
signal playAll(var tracks)
signal playSync(var playlist)
signal appendAll(var tracks)
spacing: 0
Kirigami.PageRow
{
id: linkingPage
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
separatorVisible: wideMode
initialPage: [playlistList, linkingResults]
defaultColumnWidth: Kirigami.Units.gridUnit * 15
interactive: false
LinkingDialog
{
id: linkingConf
}
Page
{
id: playlistList
clip: true
anchors.fill: parent
SwipeView
{
id: linkingSwipe
anchors.fill: parent
interactive: false
clip: true
LinkingListModel
{
id: linkingModel
}
BabeList
{
id: linkingFilter
headerBarExitIcon: "arrow-left"
model : ListModel {}
delegate: BabeDelegate
{
id: delegate
label : tag
Connections
{
target: delegate
onClicked: populateFromFilter(index, linkingFilter.headerBarTitle)
}
}
onExit: linkingSwipe.currentIndex = 0
}
}
}
Page
{
id: linkingResults
anchors.fill: parent
clip: true
BabeTable
{
id: filterList
anchors.fill: parent
quickPlayVisible: true
coverArtVisible: false
trackRating: true
trackDuration: false
headerBarVisible: true
headerBarExitIcon: "arrow-left"
headerBarExit: !linkingPage.wideMode
headerBarTitle: linkingPage.wideMode ? "" : linkingModel.model.get(linkingModel.currentIndex).playlist
onExit: if(!linkingPage.wideMode)
linkingPage.currentIndex = 0
holder.message: "<h2>"+link.getDeviceName()+"</h2><p>Your linked playlist is empty</p>"
holder.emoji: "qrc:/assets/face-hug.png"
appendBtn.visible: false
playAllBtn.visible: false
menuBtn.visible: false
section.criteria: ViewSection.FullString
section.delegate: BabeDelegate
{
label: filterList.section.property === qsTr("stars") ? H.setStars(section) : section
isSection: true
boldLabel: true
fontFamily: "Material Design Icons"
}
Connections
{
target: filterList
onRowClicked: {}
onQuickPlayTrack:
{
}
onPlayAll: {}
onPulled: {}
}
}
}
}
Kirigami.Separator
{
visible: !isMobile
Layout.fillWidth: true
width: parent.width
height: 1
}
ToolBar
{
id: searchBox
Layout.fillWidth: true
width: parent.width
height: toolBarHeight
position: ToolBar.Footer
Rectangle
{
anchors.fill: parent
z: -999
color: backgroundColor
}
RowLayout
{
anchors.fill: parent
TextInput
{
id: searchInput
color: foregroundColor
Layout.fillWidth: true
Layout.fillHeight: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
selectByMouse: !root.isMobile
selectionColor: babeHighlightColor
selectedTextColor: foregroundColor
focus: true
text: ""
wrapMode: TextEdit.Wrap
onAccepted: runSearch(searchInput.text)
}
BabeButton
{
Layout.rightMargin: contentMargins
iconName: "edit-clear"
onClicked: searchInput.clear()
}
}
}
Connections
{
target: link
onServerConReady:
{
isServing = true
H.notify(deviceName, "You're now linked!")
}
onClientConError:
{
isLinked = false
H.notify("Linking error", "error connecting to server")
}
onDevicesLinked:
{
isLinked = true
H.notify("Linked!", "The link is ready")
refreshPlaylists()
}
onClientConDisconnected:
{
isLinked = false;
H.notify("Unlinked!", "The client is disconnected")
}
onServerConDisconnected:
{
isServing = false;
H.notify("Unlinked!", "The server is disconnected")
}
onResponseReady: parseResponse(res)
}
function refreshPlaylists()
{
for(var i=11; i < linkingModel.count; i++)
linkingModel.model.remove(i)
if(isLinked)
link.ask(LINK.PLAYLISTS,Q.GET.playlists)
}
function appendPlaylists(res)
{
if(res.length>0)
for(var i in res)
linkingModel.model.append(res[i])
}
function appendToExtraList(res)
{
if(res.length>0)
for(var i in res)
linkingFilter.model.append(res[i])
}
function populateExtra(code, query, title)
{
linkingSwipe.currentIndex = 1
link.ask(code, query)
linkingFilter.clearTable()
linkingFilter.headerBarTitle = title
}
function parseResponse(res)
{
switch(res.CODE)
{
case LINK.PLAYLISTS:
appendPlaylists(res.MSG)
break
case LINK.FILTER:
appendToExtraList(res.MSG)
break
case LINK.SEARCHFOR:
populate(res.MSG)
break
default: console.log(res.CODE, res.MSG); break;
}
}
function populateFromFilter(index, title)
{
linkingFilter.currentIndex = index
var tag = linkingFilter.model.get(index).tag
switch(title)
{
case "Albums":
var artist = linkingFilter.model.get(index).artist
var query = Q.GET.albumTracks_.arg(tag)
link.ask(LINK.SEARCHFOR, query.arg(artist))
break
case "Artists":
query = Q.GET.artistTracks_.arg(tag)
link.ask(LINK.SEARCHFOR, query.arg(tag))
break
case "Genres":
query = Q.GET.genreTracks_.arg(tag)
link.ask(LINK.SEARCHFOR, query.arg(tag))
break
case "Tags":
query = Q.GET.tagTracks_.arg(tag)
link.ask(LINK.SEARCHFOR, query.arg(tag))
break
default: break
}
}
function populate(tracks)
{
if(!linkingPage.wideMode)
linkingPage.currentIndex = 1
filterList.clearTable()
if(tracks.length>0)
for(var i in tracks)
filterList.model.append(tracks[i])
}
function runSearch(searchTxt)
{
if(searchTxt)
if(searchTxt !== filterList.headerBarTitle)
{
filterList.headerBarTitle = searchTxt
link.ask(LINK.SEARCHFOR, searchTxt)
}
}
function clearSearch()
{
searchInput.clear()
youtubeTable.clearTable()
youtubeTable.headerBarTitle = ""
searchRes = []
}
}
......@@ -9,12 +9,10 @@
QString Linking::stringify(const QVariantMap &map)
{
if(map.isEmpty()) return "{}";
auto jsonResponse = QJsonDocument::fromVariant(map);
QString JSON(jsonResponse.toJson(QJsonDocument::Compact));
auto JSON = QString("{ \"%1\" : \"%2\", \"%3\" : \"%4\" }").arg(BAE::SLANG[BAE::W::CODE],
QString::number(map[BAE::SLANG[BAE::W::CODE]].toInt()),
BAE::SLANG[BAE::W::MSG],
map[BAE::SLANG[BAE::W::MSG]].toString());
qDebug()<<"strigified json"<<JSON;
return JSON;
}
......@@ -34,11 +32,21 @@ Linking::Linking(QObject *parent) : QObject(parent)
connect(&client, &QWebSocket::disconnected, this, &Linking::clientConDisconnected);
connect(&client, &QWebSocket::textMessageReceived, [this](QString msg)
{
emit this->responseReady(decode(msg));
auto decoded = decode(msg);
qDebug()<<"client recived message"<<msg;
emit this->responseReady(decoded);
});
}
QVariantMap Linking::packResponse(const LINK::CODE &code, const QVariant &content)
{
QVariantMap map;
map.insert(BAE::SLANG[BAE::W::CODE], code);
map.insert(BAE::SLANG[BAE::W::MSG], content);
return map;
}
void Linking::init(const int &index)
{
qDebug()<<"Got connected with index"<<index;
......@@ -75,26 +83,19 @@ QString Linking::getDeviceName()
void Linking::ask(int code, QString msg)
{
auto JSON = QString("{ \"%1\" : \"%2\", \"%3\" : \"%4\" }").arg(BAE::SLANG[BAE::W::CODE],
QString::number(code),
BAE::SLANG[BAE::W::MSG],
msg);
client.sendTextMessage(JSON);
qDebug()<<"msg sent as json to server";
bDebug::Instance()->msg("Sending msg to server: "+QString::number(code)+" :: "+ msg);
client.sendTextMessage(stringify(packResponse(static_cast<LINK::CODE>(code), msg)));
}
QVariantMap Linking::decode(const QString &json)
{
qDebug()<<"trying to decode msg";
bDebug::Instance()->msg("Decoding client msg");
QJsonParseError jsonParseError;
auto jsonResponse = QJsonDocument::fromJson(json.toUtf8(), &jsonParseError);
if (jsonParseError.error != QJsonParseError::NoError) return QVariantMap ();
if (!jsonResponse.isObject()) return QVariantMap ();
qDebug()<<"trying to decode msg2";
QJsonObject mainJsonObject(jsonResponse.object());
auto data = mainJsonObject.toVariantMap();
......@@ -138,10 +139,13 @@ void Linking::sendToClient(QVariantMap map)
{
auto json = stringify(map);
qDebug()<<"Seing message to client:" <<json;
qDebug()<<map;
server->sendMessageTo(0, json);
}
void Linking::handleError(QAbstractSocket::SocketError error)
{
qDebug()<<error;
emit this->clientConError("An error happened connecting to server");
}
......@@ -19,9 +19,8 @@ namespace LINK
DISCONNECTED = 3,
SEARCHFOR = 4,
PLAYLISTS = 5,
GETQUERY = 6,
FILTER = 6
};
Q_ENUM_NS(CODE);
static QMap<CODE, QString> DECODE =
......@@ -31,8 +30,7 @@ namespace LINK
{CODE::DISCONNECTED, "DISCONNECTED"},
{CODE::SEARCHFOR, "SEARCHFOR"},
{CODE::PLAYLISTS, "PLAYLISTS"},
{CODE::GETQUERY, "GETQUERY"}
{CODE::FILTER, "FILTER"},
};
}
......@@ -52,6 +50,7 @@ class Linking : public QObject
explicit Linking(QObject *parent = nullptr);