Add ability to set games/roms as favouriteFixes #49

* Add ability to set games/roms as favourite
Fixes #49
This commit is contained in:
Michael Green
2024-02-08 12:47:22 +11:00
committed by GitHub
parent 73174a30f0
commit b5511a3c51
12 changed files with 287 additions and 8 deletions

View File

@@ -0,0 +1,58 @@
using IGDB.Models;
namespace gaseous_server.Classes
{
public class Favourites
{
public bool GetFavourite(string userid, long GameId)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Favourites WHERE UserId=@userid AND GameId=@gameid";
Dictionary<string, object> dbDict = new Dictionary<string, object>{
{ "userid", userid },
{ "gameid", GameId}
};
if (db.ExecuteCMD(sql, dbDict).Rows.Count > 0)
{
return true;
}
else
{
return false;
}
}
public bool SetFavourite(string userid, long GameId, bool Favourite)
{
bool CurrentFavourite = GetFavourite(userid, GameId);
if (CurrentFavourite == Favourite)
{
return Favourite;
}
else
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql;
Dictionary<string, object> dbDict = new Dictionary<string, object>{
{ "userid", userid },
{ "gameid", GameId}
};
if (CurrentFavourite == true)
{
// delete existing value
sql = "DELETE FROM Favourites WHERE UserId=@userid AND GameId=@gameid";
}
else
{
// insert new value
sql = "INSERT INTO Favourites (UserId, GameId) VALUES (@userid, @gameid)";
}
db.ExecuteNonQuery(sql, dbDict);
return Favourite;
}
}
}
}

View File

@@ -566,6 +566,7 @@ namespace gaseous_server.Classes.Metadata
public double? TotalRating { get; set; } public double? TotalRating { get; set; }
public int? TotalRatingCount { get; set; } public int? TotalRatingCount { get; set; }
public bool HasSavedGame { get; set; } = false; public bool HasSavedGame { get; set; } = false;
public bool IsFavourite { get; set; } = false;
public DateTimeOffset? FirstReleaseDate { get; set; } public DateTimeOffset? FirstReleaseDate { get; set; }
public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; } public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; }
public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; } public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; }

View File

@@ -611,6 +611,81 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/favourite")]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GameGetFavouriteAsync(long GameId)
{
try
{
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
if (gameObject != null)
{
var user = await _userManager.GetUserAsync(User);
if (user != null)
{
Favourites favourites = new Favourites();
return Ok(favourites.GetFavourite(user.Id, GameId));
}
else
{
return NotFound();
}
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Route("{GameId}/favourite")]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GameSetFavouriteAsync(long GameId, bool favourite)
{
try
{
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
if (gameObject != null)
{
var user = await _userManager.GetUserAsync(User);
if (user != null)
{
Favourites favourites = new Favourites();
return Ok(favourites.SetFavourite(user.Id, GameId, favourite));
}
else
{
return NotFound();
}
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpGet]

View File

@@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting;
using static gaseous_server.Classes.Metadata.AgeRatings; using static gaseous_server.Classes.Metadata.AgeRatings;
using Asp.Versioning; using Asp.Versioning;
using Humanizer;
namespace gaseous_server.Controllers.v1_1 namespace gaseous_server.Controllers.v1_1
{ {
@@ -148,6 +149,7 @@ namespace gaseous_server.Controllers.v1_1
public GameAgeRatingItem GameAgeRating { get; set; } = new GameAgeRatingItem(); public GameAgeRatingItem GameAgeRating { get; set; } = new GameAgeRatingItem();
public GameSortingItem Sorting { get; set; } = new GameSortingItem(); public GameSortingItem Sorting { get; set; } = new GameSortingItem();
public bool HasSavedGame { get; set; } public bool HasSavedGame { get; set; }
public bool IsFavourite { get; set; }
public class GameRatingItem public class GameRatingItem
@@ -213,6 +215,12 @@ namespace gaseous_server.Controllers.v1_1
whereClauses.Add(hasSavesTemp); whereClauses.Add(hasSavesTemp);
} }
if (model.IsFavourite == true)
{
string isFavTemp = "Favourite = 1";
havingClauses.Add(isFavTemp);
}
if (model.MinimumReleaseYear != -1) if (model.MinimumReleaseYear != -1)
{ {
string releaseTempMinVal = "FirstReleaseDate >= @minreleasedate"; string releaseTempMinVal = "FirstReleaseDate >= @minreleasedate";
@@ -488,7 +496,11 @@ SELECT DISTINCT
Game.AgeGroupId, Game.AgeGroupId,
Game.RomCount, Game.RomCount,
RomSavedStates.RomSaveCount, RomSavedStates.RomSaveCount,
RomGroupSavedStates.MediaGroupSaveCount RomGroupSavedStates.MediaGroupSaveCount,
CASE
WHEN Favourites.UserId IS NULL THEN 0
ELSE 1
END AS Favourite
FROM FROM
(SELECT DISTINCT (SELECT DISTINCT
Game.*, Game.*,
@@ -533,7 +545,9 @@ FROM
LEFT JOIN LEFT JOIN
Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId
LEFT JOIN LEFT JOIN
Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause; Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId
LEFT JOIN
Favourites ON Game.Id = Favourites.GameId AND Favourites.UserId = @userid " + whereClause + " " + havingClause + " " + orderByClause;
List<Games.MinimalGameItem> RetVal = new List<Games.MinimalGameItem>(); List<Games.MinimalGameItem> RetVal = new List<Games.MinimalGameItem>();
DataTable dbResponse = db.ExecuteCMD(sql, whereParams); DataTable dbResponse = db.ExecuteCMD(sql, whereParams);
@@ -563,6 +577,14 @@ FROM
{ {
retMinGame.HasSavedGame = false; retMinGame.HasSavedGame = false;
} }
if ((int)dbResponse.Rows[i]["Favourite"] == 0)
{
retMinGame.IsFavourite = false;
}
else
{
retMinGame.IsFavourite = true;
}
RetVal.Add(retMinGame); RetVal.Add(retMinGame);
} }

View File

@@ -0,0 +1,8 @@
CREATE TABLE `Favourites` (
`UserId` varchar(45) NOT NULL,
`GameId` bigint(20) NOT NULL,
PRIMARY KEY (`UserId`,`GameId`),
KEY `idx_GameId` (`GameId`),
KEY `idx_UserId` (`UserId`),
CONSTRAINT `ApplicationUser_Favourite` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
);

View File

@@ -63,6 +63,7 @@
<None Remove="Support\Database\MySQL\gaseous-1018.sql" /> <None Remove="Support\Database\MySQL\gaseous-1018.sql" />
<None Remove="Support\Database\MySQL\gaseous-1019.sql" /> <None Remove="Support\Database\MySQL\gaseous-1019.sql" />
<None Remove="Support\Database\MySQL\gaseous-1020.sql" /> <None Remove="Support\Database\MySQL\gaseous-1020.sql" />
<None Remove="Support\Database\MySQL\gaseous-1021.sql" />
<None Remove="Classes\Metadata\" /> <None Remove="Classes\Metadata\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -106,5 +107,6 @@
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1018.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1018.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1019.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1019.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1020.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1020.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1021.sql" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 24 24" id="favourite" data-name="Line Color" xmlns="http://www.w3.org/2000/svg" class="icon line-color"><path id="primary" d="M19.57,5.44a4.91,4.91,0,0,1,0,6.93L12,20,4.43,12.37A4.91,4.91,0,0,1,7.87,4a4.9,4.9,0,0,1,3.44,1.44,4.46,4.46,0,0,1,.69.88,4.46,4.46,0,0,1,.69-.88,4.83,4.83,0,0,1,6.88,0Z" style="fill: none; stroke: rgb(0, 0, 0); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path></svg>

After

Width:  |  Height:  |  Size: 597 B

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" id="favourite" class="icon glyph"><path d="M20.28,4.74a5.82,5.82,0,0,0-8.28,0,5.82,5.82,0,0,0-8.28,0,5.94,5.94,0,0,0,0,8.34l7.57,7.62a1,1,0,0,0,1.42,0l7.57-7.62a5.91,5.91,0,0,0,0-8.34Z"></path></svg>

After

Width:  |  Height:  |  Size: 422 B

View File

@@ -72,6 +72,12 @@
<span class="gamestatistics_label">Total Play Time</span> <span class="gamestatistics_label">Total Play Time</span>
<span id="gamestatistics_timeplayed_value" class="gamestatistics_value">-</span> <span id="gamestatistics_timeplayed_value" class="gamestatistics_value">-</span>
</div> </div>
<div id="gamestatistics_favourite" class="gamestatistics_box">
<div id="gamestatistics_favourite_button">
</div>
</div>
</div> </div>
<div id="gamesummarytext"> <div id="gamesummarytext">
<span id="gamesummarytext_label" class="line-clamp-4"></span> <span id="gamesummarytext_label" class="line-clamp-4"></span>
@@ -128,8 +134,6 @@
var selectedScreenshot = 0; var selectedScreenshot = 0;
ajaxCall('/api/v1.1/Games/' + gameId, 'GET', function (result) { ajaxCall('/api/v1.1/Games/' + gameId, 'GET', function (result) {
console.log(result);
// populate games page // populate games page
gameData = result; gameData = result;
@@ -290,6 +294,27 @@
} }
}); });
// load favourites
ajaxCall('/api/v1.1/games/' + gameId + '/favourite', 'GET', function (result) {
var gameFavButton = document.getElementById('gamestatistics_favourite_button');
var gameFavIcon = document.createElement('img');
gameFavIcon.id = "gamestatistics_favourite";
gameFavIcon.className = "favouriteicon";
gameFavIcon.title = "Favourite";
gameFavIcon.alt = "Favourite";
if (result == true) {
gameFavIcon.setAttribute("src", '/images/favourite-filled.svg');
gameFavIcon.setAttribute('onclick', "SetGameFavourite(false);");
} else {
gameFavIcon.setAttribute("src", '/images/favourite-empty.svg');
gameFavIcon.setAttribute('onclick', "SetGameFavourite(true);");
}
gameFavButton.innerHTML = '';
gameFavButton.appendChild(gameFavIcon);
});
// load release date // load release date
var gameSummaryRelease = document.getElementById('gamesummary_firstrelease'); var gameSummaryRelease = document.getElementById('gamesummary_firstrelease');
if (result.firstReleaseDate) { if (result.firstReleaseDate) {
@@ -503,7 +528,6 @@
if (result.length == 0) { if (result.length == 0) {
mediaGroup.style.display = 'none'; mediaGroup.style.display = 'none';
} else { } else {
console.log(result);
mediaGroup.style.display = ''; mediaGroup.style.display = '';
mediaGroupDiv.innerHTML = ''; mediaGroupDiv.innerHTML = '';
var mgTable = document.createElement('table'); var mgTable = document.createElement('table');
@@ -999,12 +1023,9 @@
$('#rom_edit_fixplatform').on('select2:select', function (e) { $('#rom_edit_fixplatform').on('select2:select', function (e) {
var platformData = e.params.data; var platformData = e.params.data;
console.log(platformData);
var gameValue = $('#rom_edit_fixgame').select2('data'); var gameValue = $('#rom_edit_fixgame').select2('data');
if (gameValue) { if (gameValue) {
console.log(gameValue[0]);
setRomFixGameDropDown(); setRomFixGameDropDown();
} }
}); });
@@ -1170,4 +1191,30 @@
JSON.stringify(romIds) JSON.stringify(romIds)
); );
} }
function SetGameFavourite(status) {
ajaxCall(
'/api/v1.1/games/' + gameId + '/favourite?favourite=' + status,
'POST',
function (result) {
var gameFavButton = document.getElementById('gamestatistics_favourite_button');
var gameFavIcon = document.createElement('img');
gameFavIcon.id = "gamestatistics_favourite";
gameFavIcon.className = "favouriteicon";
gameFavIcon.title = "Favourite";
gameFavIcon.alt = "Favourite";
if (result == true) {
gameFavIcon.setAttribute("src", '/images/favourite-filled.svg');
gameFavIcon.setAttribute('onclick', "SetGameFavourite(false);");
} else {
gameFavIcon.setAttribute("src", '/images/favourite-empty.svg');
gameFavIcon.setAttribute('onclick', "SetGameFavourite(true);");
}
gameFavButton.innerHTML = '';
gameFavButton.appendChild(gameFavIcon);
}
);
}
</script> </script>

View File

@@ -55,6 +55,11 @@ function formatFilterPanel(containerElement, result) {
"id": "savestatesavailable", "id": "savestatesavailable",
"name": "Game has save states avaialble", "name": "Game has save states avaialble",
"gameCount": 0 "gameCount": 0
},
{
"id": "favourite",
"name": "Favourite",
"gameCount": 0
} }
], true, true); ], true, true);
@@ -531,6 +536,7 @@ function executeFilter1_1(pageNumber, pageSize) {
model = { model = {
"Name": document.getElementById('filter_panel_search').value, "Name": document.getElementById('filter_panel_search').value,
"HasSavedGame": document.getElementById('filter_panel_item_settings_checkbox_savestatesavailable').checked, "HasSavedGame": document.getElementById('filter_panel_item_settings_checkbox_savestatesavailable').checked,
"isFavourite": document.getElementById('filter_panel_item_settings_checkbox_favourite').checked,
"Platform": GetFilterQuery1_1('platform'), "Platform": GetFilterQuery1_1('platform'),
"Genre": GetFilterQuery1_1('genre'), "Genre": GetFilterQuery1_1('genre'),
"GameMode": GetFilterQuery1_1('gamemode'), "GameMode": GetFilterQuery1_1('gamemode'),
@@ -554,6 +560,7 @@ function executeFilter1_1(pageNumber, pageSize) {
"SortAscending": orderByDirection "SortAscending": orderByDirection
} }
}; };
console.log(model);
existingSearchModel = model; existingSearchModel = model;
} else { } else {

View File

@@ -330,6 +330,14 @@ function renderGameIcon(gameObject, showTitle, showRatings, showClassification,
gameImageBox.appendChild(gameSaveIcon); gameImageBox.appendChild(gameSaveIcon);
} }
// add favourite game icon
if (gameObject.isFavourite == true) {
var gameFavIcon = document.createElement('img');
gameFavIcon.src = '/images/favourite-filled.svg';
gameFavIcon.className = classes['game_tile_box_favouritegame favouriteicon'];
gameImageBox.appendChild(gameFavIcon);
}
if (gameObject.totalRating || displayClassification == true) { if (gameObject.totalRating || displayClassification == true) {
var gameImageRatingBanner = document.createElement('div'); var gameImageRatingBanner = document.createElement('div');
gameImageRatingBanner.className = classes['game_tile_box_ratingbanner']; gameImageRatingBanner.className = classes['game_tile_box_ratingbanner'];
@@ -380,6 +388,7 @@ function getViewModeClasses(listView) {
"game_tile_image lazy": "game_tile_image lazy", "game_tile_image lazy": "game_tile_image lazy",
"game_tile_image unknown": "game_tile_image unknown", "game_tile_image unknown": "game_tile_image unknown",
"game_tile_box_savedgame savedstateicon": "game_tile_box_savedgame savedstateicon", "game_tile_box_savedgame savedstateicon": "game_tile_box_savedgame savedstateicon",
"game_tile_box_favouritegame favouriteicon": "game_tile_box_favouritegame favouriteicon",
"game_tile_box_ratingbanner": "game_tile_box_ratingbanner", "game_tile_box_ratingbanner": "game_tile_box_ratingbanner",
"rating_image_overlay": "rating_image_overlay", "rating_image_overlay": "rating_image_overlay",
"game_tile_label": "game_tile_label" "game_tile_label": "game_tile_label"
@@ -393,6 +402,7 @@ function getViewModeClasses(listView) {
"game_tile_image lazy": "game_tile_image_row lazy", "game_tile_image lazy": "game_tile_image_row lazy",
"game_tile_image unknown": "game_tile_image_row unknown", "game_tile_image unknown": "game_tile_image_row unknown",
"game_tile_box_savedgame savedstateicon": "game_tile_box_savedgame_row savedstateicon", "game_tile_box_savedgame savedstateicon": "game_tile_box_savedgame_row savedstateicon",
"game_tile_box_favouritegame favouriteicon": "game_tile_box_favouritegame_row favouriteicon",
"game_tile_box_ratingbanner": "game_tile_box_ratingbanner_row", "game_tile_box_ratingbanner": "game_tile_box_ratingbanner_row",
"rating_image_overlay": "rating_image_overlay_row", "rating_image_overlay": "rating_image_overlay_row",
"game_tile_label": "game_tile_label_row" "game_tile_label": "game_tile_label_row"

View File

@@ -671,6 +671,18 @@ input[name='filter_panel_range_max'] {
right: 10px; right: 10px;
} }
.game_tile_box_favouritegame {
position: absolute;
top: 1px;
left: 5px;
}
.game_tile_box_favouritegame_row {
position: absolute;
top: 30%;
right: 10px;
}
.game_tile_image { .game_tile_image {
max-width: 200px; max-width: 200px;
min-width: 150px; min-width: 150px;
@@ -930,6 +942,31 @@ iframe {
padding: 3px; padding: 3px;
} }
#gamestatistics_favourite {
float: right;
}
#gamestatistics_favourite_button {
margin: 5px;
padding-top: 5px;
padding-bottom: 5px;
padding-left: 2px;
padding-right: 7px;
width: 30px;
height: 30px;
text-align: center;
background-color: rgba(56, 56, 56, 0.3);
border-radius: 5px;
border-style: solid;
border-color: transparent;
border-width: 0px;
cursor: pointer;
}
#gamestatistics_favourite_button:hover {
background-color: rgba(56, 56, 56, 0.9);
}
#gamesummarytext_label { #gamesummarytext_label {
} }
@@ -969,6 +1006,14 @@ table .romrow:nth-child(odd) {
cursor: pointer; cursor: pointer;
} }
.favouriteicon {
height: 24px;
width: 24px;
margin-top: 4px;
cursor: pointer;
filter: invert(11%) sepia(100%) saturate(6854%) hue-rotate(359deg) brightness(103%) contrast(122%) drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));
}
th { th {
text-align: left; text-align: left;
padding: 5px; padding: 5px;