[gnome-games/wip/exalm/tnum: 5/24] collection: Introduce UserCollection
- From: Alexander Mikhaylenko <alexm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games/wip/exalm/tnum: 5/24] collection: Introduce UserCollection
- Date: Wed, 19 Aug 2020 18:22:23 +0000 (UTC)
commit 8354a3df4dafc9f1fa70ae4396cffab8ccb7172f
Author: Neville <nevilleantony98 gmail com>
Date: Thu Aug 6 23:25:03 2020 +0530
collection: Introduce UserCollection
UserCollections are collections that users can create. UserCollections
can be created, renamed and removed.
Also adds queries to support UserCollections. Two new tables are
introduced:
- user_collections table to store collection uuid and title
- user_collections_games table to store games that belong to a
collection
src/collection/user-collection.vala | 130 ++++++++++++++++++++++
src/database/database.vala | 209 ++++++++++++++++++++++++++++++++++++
src/meson.build | 1 +
3 files changed, 340 insertions(+)
---
diff --git a/src/collection/user-collection.vala b/src/collection/user-collection.vala
new file mode 100644
index 000000000..7ca55bef7
--- /dev/null
+++ b/src/collection/user-collection.vala
@@ -0,0 +1,130 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+private class Games.UserCollection : Object, Collection {
+ private GameModel game_model;
+ private Database database;
+ private GenericSet<Uid> load_game_uids;
+ private GenericSet<Uid> game_uids;
+ private GameCollection game_collection;
+ private string id;
+ private string title;
+ private ulong idle_id = 0;
+ private ulong on_game_added_id = 0;
+
+ public bool is_empty {
+ get { return false; }
+ }
+
+ public UserCollection (string id, string title, Database database) {
+ this.id = id;
+ this.title = title;
+ this.database = database;
+
+ game_uids = new GenericSet<Uid> (Uid.hash, Uid.equal);
+
+ game_collection = Application.get_default ().get_collection ();
+ on_game_added_id = game_collection.game_added.connect (on_game_added);
+ game_collection.game_removed.connect (on_game_removed);
+ game_collection.game_replaced.connect (on_game_replaced);
+
+ game_model = new GameModel ();
+ game_model.always_replace = true;
+ }
+
+ public string get_id () {
+ return id;
+ }
+
+ public string get_title () {
+ return title;
+ }
+
+ public void set_title (string value) {
+ if (title == value)
+ return;
+
+ try {
+ if (database.rename_user_collection (this, value))
+ title = value;
+ }
+ catch (Error e) {
+ critical ("%s", e.message);
+
+ return;
+ }
+ }
+
+ public GameModel get_game_model () {
+ return game_model;
+ }
+
+ public bool get_hide_stars () {
+ return false;
+ }
+
+ public CollectionType get_collection_type () {
+ return USER;
+ }
+
+ public void load () {
+ try {
+ load_game_uids = database.list_games_in_user_collection (this);
+ load_game_uids.foreach ((uid) => game_uids.add (uid));
+ }
+ catch (Error e) {
+ critical ("Failed to load favorite game uids: %s", e.message);
+ }
+ }
+
+ public void add_games (Game[] games) {
+ try {
+ foreach (var game in games)
+ if (database.add_game_to_user_collection (game, this))
+ game_model.add_game (game);
+
+ games_changed ();
+ }
+ catch (Error e) {
+ critical ("Failed to add games to user collection: %s", e.message);
+ }
+ }
+
+ public void remove_games (Game[] games) {
+ try {
+ foreach (var game in games)
+ if (database.remove_game_from_user_collection (game, this))
+ game_model.remove_game (game);
+
+ games_changed ();
+ }
+ catch (Error e) {
+ critical ("Failed to remove games from user collection: %s", e.message);
+ }
+ }
+
+ public void on_game_added (Game game) {
+ if (load_game_uids.remove (game.uid)) {
+ game_model.add_game (game);
+
+ if (idle_id == 0)
+ idle_id = Idle.add (() => {
+ games_changed ();
+ idle_id = 0;
+ return Source.REMOVE;
+ });
+
+ if (load_game_uids.length == 0)
+ game_collection.disconnect (on_game_added_id);
+ }
+ }
+
+ public void on_game_removed (Game game) {
+ game_model.remove_game (game);
+ games_changed ();
+ }
+
+ public void on_game_replaced (Game game, Game prev_game) {
+ if (game_uids.contains (prev_game.uid))
+ game_model.replace_game (game, prev_game);
+ }
+}
diff --git a/src/database/database.vala b/src/database/database.vala
index 236552c6b..366fae63a 100644
--- a/src/database/database.vala
+++ b/src/database/database.vala
@@ -31,6 +31,24 @@ private class Games.Database : Object {
);
""";
+ private const string CREATE_USER_COLLECTIONS_TABLE_QUERY = """
+ CREATE TABLE IF NOT EXISTS user_collections (
+ id INTEGER PRIMARY KEY NOT NULL,
+ collection_id TEXT NOT NULL UNIQUE,
+ title TEXT NOT NULL UNIQUE
+ );
+ """;
+
+ private const string CREATE_USER_COLLECTIONS_GAMES_TABLE_QUERY = """
+ CREATE TABLE IF NOT EXISTS user_collections_games (
+ id INTEGER PRIMARY KEY NOT NULL,
+ collection TEXT NOT NULL,
+ game TEXT NOT NULL,
+ FOREIGN KEY(collection) REFERENCES user_collections(collection_id) ON DELETE CASCADE,
+ FOREIGN KEY(game) REFERENCES games(uid) ON DELETE CASCADE
+ );
+ """;
+
private const string ADD_GAME_QUERY = """
INSERT INTO games (uid, title, platform, media_set) VALUES ($UID, $TITLE, $PLATFORM,
$MEDIA_SET);
""";
@@ -93,6 +111,46 @@ private class Games.Database : Object {
SELECT uid FROM games WHERE last_played IS NOT NULL;
""";
+ private const string ADD_USER_COLLECTION_QUERY = """
+ INSERT INTO user_collections (collection_id, title) VALUES ($COLLECTION_ID, $TITLE);
+ """;
+
+ private const string ADD_GAME_TO_USER_COLLECTION_QUERY = """
+ INSERT INTO user_collections_games (collection, game)
+ VALUES ($COLLECTION_ID, $GAME_UID);
+ """;
+
+ private const string DOES_USER_COLLECTION_EXIST_QUERY = """
+ SELECT EXISTS (SELECT 1 FROM user_collections
+ WHERE collection_id = $COLLECTION_ID LIMIT 1);
+ """;
+
+ private const string IS_GAME_IN_USER_COLLECTION_QUERY = """
+ SELECT EXISTS (SELECT 1 FROM user_collections_games
+ WHERE game = $GAME_UID AND collection = $COLLECTION_ID LIMIT 1);
+ """;
+
+ private const string LIST_USER_COLLECTIONS_QUERY = """
+ SELECT collection_id, title FROM user_collections;
+ """;
+
+ private const string LIST_GAMES_IN_USER_COLLECTION_QUERY = """
+ SELECT games.uid FROM games JOIN user_collections_games
+ ON games.uid = game WHERE collection = $COLLECTION_ID;
+ """;
+
+ private const string REMOVE_USER_COLLECTION_QUERY = """
+ DELETE FROM user_collections WHERE collection_id = $COLLECTION_ID;
+ """;
+
+ private const string REMOVE_GAME_FROM_USER_COLLECTION_QUERY = """
+ DELETE FROM user_collections_games WHERE collection = $COLLECTION_ID AND game = $GAME_UID;
+ """;
+
+ private const string RENAME_USER_COLLECTION_QUERY = """
+ UPDATE user_collections SET title = $TITLE WHERE collection_id = $COLLECTION_ID;
+ """;
+
private Sqlite.Statement add_game_query;
private Sqlite.Statement add_game_uri_query;
private Sqlite.Statement update_game_query;
@@ -113,6 +171,16 @@ private class Games.Database : Object {
private Sqlite.Statement update_recently_played_game_query;
private Sqlite.Statement list_recently_played_games_query;
+ private Sqlite.Statement add_user_collection_query;
+ private Sqlite.Statement add_game_to_user_collection_query;
+ private Sqlite.Statement does_user_collection_exist_query;
+ private Sqlite.Statement is_game_in_user_collection_query;
+ private Sqlite.Statement list_user_collections_query;
+ private Sqlite.Statement list_games_in_user_collection_query;
+ private Sqlite.Statement remove_user_collection_query;
+ private Sqlite.Statement remove_game_from_user_collection_query;
+ private Sqlite.Statement rename_user_collection_query;
+
public Database (string path) throws Error {
if (Sqlite.Database.open (path, out database) != Sqlite.OK)
throw new DatabaseError.COULDNT_OPEN ("Couldn’t open the database for “%s”.", path);
@@ -120,6 +188,8 @@ private class Games.Database : Object {
exec (CREATE_RESOURCES_TABLE_QUERY, null);
exec (CREATE_GAMES_TABLE_QUERY, null);
exec (CREATE_URIS_TABLE_QUERY, null);
+ exec (CREATE_USER_COLLECTIONS_TABLE_QUERY, null);
+ exec (CREATE_USER_COLLECTIONS_GAMES_TABLE_QUERY, null);
}
public void prepare_statements () {
@@ -143,6 +213,16 @@ private class Games.Database : Object {
update_recently_played_game_query = prepare (database,
UPDATE_RECENTLY_PLAYED_GAME_QUERY);
list_recently_played_games_query = prepare (database,
LIST_RECENTLY_PLAYED_GAMES_QUERY);
+
+ add_user_collection_query = prepare (database, ADD_USER_COLLECTION_QUERY);
+ add_game_to_user_collection_query = prepare (database,
ADD_GAME_TO_USER_COLLECTION_QUERY);
+ does_user_collection_exist_query = prepare (database,
DOES_USER_COLLECTION_EXIST_QUERY);
+ is_game_in_user_collection_query = prepare (database,
IS_GAME_IN_USER_COLLECTION_QUERY);
+ list_user_collections_query = prepare (database, LIST_USER_COLLECTIONS_QUERY);
+ list_games_in_user_collection_query = prepare (database,
LIST_GAMES_IN_USER_COLLECTION_QUERY);
+ remove_user_collection_query = prepare (database, REMOVE_USER_COLLECTION_QUERY);
+ remove_game_from_user_collection_query = prepare (database,
REMOVE_GAME_FROM_USER_COLLECTION_QUERY);
+ rename_user_collection_query = prepare (database, RENAME_USER_COLLECTION_QUERY);
}
catch (Error e) {
critical ("Failed to prepare statements: %s", e.message);
@@ -466,4 +546,133 @@ private class Games.Database : Object {
if (result != Sqlite.DONE)
throw new DatabaseError.EXECUTION_FAILED ("Failed to update last played date-time of
%s", game.name);
}
+
+ private bool does_user_collection_exist (string uuid) throws Error {
+ if (!Uuid.string_is_valid (uuid))
+ return false;
+
+ does_user_collection_exist_query.reset ();
+ bind_text (does_user_collection_exist_query, "$COLLECTION_ID", uuid);
+
+ if (does_user_collection_exist_query.step () == Sqlite.ROW)
+ return does_user_collection_exist_query.column_int (0) == 1;
+
+ return false;
+ }
+
+ private bool is_game_in_user_collection (string game_uid, string collection_id) throws Error {
+ is_game_in_user_collection_query.reset ();
+ bind_text (is_game_in_user_collection_query, "$GAME_UID", game_uid);
+ bind_text (is_game_in_user_collection_query, "$COLLECTION_ID", collection_id);
+
+ if (is_game_in_user_collection_query.step () == Sqlite.ROW)
+ return is_game_in_user_collection_query.column_text (0) == "1";
+
+ return false;
+ }
+
+ public bool add_user_collection (UserCollection collection) throws Error {
+ if (does_user_collection_exist (collection.get_id ())) {
+ critical ("A collection named %s already exists", collection.get_title ());
+ return false;
+ }
+
+ add_user_collection_query.reset ();
+ bind_text (add_user_collection_query, "$COLLECTION_ID", collection.get_id ());
+ bind_text (add_user_collection_query, "$TITLE", collection.get_title ());
+
+ var result = add_user_collection_query.step ();
+ if (result != Sqlite.DONE)
+ throw new DatabaseError.EXECUTION_FAILED ("Failed to add user collection %s: %s",
+ collection.get_title (), result.to_string
());
+
+ return true;
+ }
+
+ public bool add_game_to_user_collection (Game game, UserCollection collection) throws Error {
+ if (is_game_in_user_collection (game.uid.to_string (), collection.get_id ()))
+ return false;
+
+ add_game_to_user_collection_query.reset ();
+ bind_text (add_game_to_user_collection_query, "$COLLECTION_ID", collection.get_id ());
+ bind_text (add_game_to_user_collection_query, "$GAME_UID", game.uid.to_string ());
+
+ var result = add_game_to_user_collection_query.step ();
+ if (result != Sqlite.DONE)
+ throw new DatabaseError.EXECUTION_FAILED ("Failed to add %s to user collection %s",
+ game.name, collection.get_title ());
+
+ return true;
+ }
+
+ public GenericSet<UserCollection> get_user_collections () throws Error {
+ var collections = new GenericSet<UserCollection> (Collection.hash, Collection.equal);
+
+ while (list_user_collections_query.step () == Sqlite.ROW) {
+ var uuid = list_user_collections_query.column_text (0);
+ var title = list_user_collections_query.column_text (1);
+ var collection = new UserCollection (uuid, title, this);
+ collections.add (collection);
+ }
+
+ return collections;
+ }
+
+ public GenericSet<Uid> list_games_in_user_collection (UserCollection collection) throws Error {
+ var games = new GenericSet<Uid> (Uid.hash, Uid.equal);
+
+ list_games_in_user_collection_query.reset ();
+ bind_text (list_games_in_user_collection_query, "$COLLECTION_ID", collection.get_id ());
+
+ while (list_games_in_user_collection_query.step () == Sqlite.ROW)
+ games.add (new Uid (list_games_in_user_collection_query.column_text (0)));
+
+ return games;
+ }
+
+ public bool remove_user_collection (UserCollection collection) throws Error {
+ if (!does_user_collection_exist (collection.get_id ()))
+ return false;
+
+ remove_user_collection_query.reset ();
+ bind_text (remove_user_collection_query, "$COLLECTION_ID", collection.get_id ());
+
+ var result = remove_user_collection_query.step ();
+ if (result != Sqlite.DONE)
+ throw new DatabaseError.EXECUTION_FAILED ("Failed to remove user collection %s",
collection.get_title ());
+
+ return true;
+ }
+
+ public bool remove_game_from_user_collection (Game game, UserCollection collection) throws Error {
+ if (!is_game_in_user_collection (game.uid.to_string (), collection.get_id ()))
+ return false;
+
+ remove_game_from_user_collection_query.reset ();
+ bind_text (remove_game_from_user_collection_query, "$GAME_UID", game.uid.to_string ());
+ bind_text (remove_game_from_user_collection_query, "$COLLECTION_ID", collection.get_id ());
+
+ var result = remove_game_from_user_collection_query.step ();
+ if (result != Sqlite.DONE)
+ throw new DatabaseError.EXECUTION_FAILED ("Failed to remove %s from user collection
%s",
+ game.name, collection.get_title ());
+
+ return true;
+ }
+
+ public bool rename_user_collection (UserCollection collection, string title) throws Error {
+ if (!does_user_collection_exist (collection.get_id ()))
+ return false;
+
+ rename_user_collection_query.reset ();
+ bind_text (rename_user_collection_query, "$TITLE", title);
+ bind_text (rename_user_collection_query, "$COLLECTION_ID", collection.get_id ());
+
+ var result = rename_user_collection_query.step ();
+ if (result != Sqlite.DONE)
+ throw new DatabaseError.EXECUTION_FAILED ("Failed to rename user collection %s",
+ collection.get_title ());
+
+ return true;
+ }
}
diff --git a/src/meson.build b/src/meson.build
index d0fa1f49e..7db764e86 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -17,6 +17,7 @@ vala_sources = [
'collection/dummy-add-collection.vala',
'collection/favorites-collection.vala',
'collection/recently-played-collection.vala',
+ 'collection/user-collection.vala',
'command/command-error.vala',
'command/command-runner.vala',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]