From 64fb76484bdd3756c46f9fa0d36b045e74fc542b Mon Sep 17 00:00:00 2001 From: Michael Green <84688932+michael-j-green@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:48:49 -0700 Subject: [PATCH] Update to profile cards to display now-playing (#427) --- gaseous-server/Classes/UserProfile.cs | 24 +++++- .../Controllers/V1.1/UserProfileController.cs | 2 +- gaseous-server/Models/UserProfile.cs | 9 ++ .../Support/Database/MySQL/gaseous-1024.sql | 8 +- gaseous-server/wwwroot/scripts/account.js | 83 +++++++++++++++++-- gaseous-server/wwwroot/styles/style.css | 40 +++++++++ 6 files changed, 158 insertions(+), 8 deletions(-) diff --git a/gaseous-server/Classes/UserProfile.cs b/gaseous-server/Classes/UserProfile.cs index c38a0a4..db662e1 100644 --- a/gaseous-server/Classes/UserProfile.cs +++ b/gaseous-server/Classes/UserProfile.cs @@ -1,4 +1,6 @@ using System.Data; +using gaseous_server.Classes.Metadata; +using IGDB.Models; namespace gaseous_server.Classes { @@ -15,8 +17,9 @@ namespace gaseous_server.Classes public Models.UserProfile? GetUserProfile(string UserId) { + // build the user profile object Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "SELECT Id, DisplayName, Quip, AvatarExtension, ProfileBackgroundExtension, UnstructuredData FROM UserProfiles WHERE Id = @userid;"; + string sql = "SELECT Id, UserId, DisplayName, Quip, AvatarExtension, ProfileBackgroundExtension, UnstructuredData FROM UserProfiles WHERE Id = @userid;"; Dictionary dbDict = new Dictionary{ { "userid", UserId } }; @@ -48,6 +51,24 @@ namespace gaseous_server.Classes }; } + // get now playing game - if available + Models.UserProfile.NowPlayingItem? NowPlaying = null; + sql = "SELECT * FROM `view_UserTimeTracking` WHERE UserId = @userid AND UTC_TIMESTAMP() BETWEEN SessionTime AND DATE_ADD(SessionEnd, INTERVAL 2 MINUTE) ORDER BY SessionEnd DESC LIMIT 1;"; + dbDict = new Dictionary{ + { "userid", data.Rows[0]["UserId"].ToString() } + }; + DataTable nowPlayingData = db.ExecuteCMD(sql, dbDict); + if (nowPlayingData.Rows.Count > 0) + { + NowPlaying = new Models.UserProfile.NowPlayingItem + { + Game = Games.GetGame((long)nowPlayingData.Rows[0]["GameId"], false, false, false), + Platform = Platforms.GetPlatform((long)nowPlayingData.Rows[0]["PlatformId"], false, false), + Duration = Convert.ToInt64(nowPlayingData.Rows[0]["SessionLength"]) + }; + } + + // return the user profile object return new Models.UserProfile { UserId = Guid.Parse(data.Rows[0]["Id"].ToString()), @@ -55,6 +76,7 @@ namespace gaseous_server.Classes Quip = data.Rows[0]["Quip"].ToString(), Avatar = Avatar, ProfileBackground = ProfileBackground, + NowPlaying = NowPlaying, Data = Newtonsoft.Json.JsonConvert.DeserializeObject>(data.Rows[0]["UnstructuredData"].ToString()) }; } diff --git a/gaseous-server/Controllers/V1.1/UserProfileController.cs b/gaseous-server/Controllers/V1.1/UserProfileController.cs index da31a83..8000e7a 100644 --- a/gaseous-server/Controllers/V1.1/UserProfileController.cs +++ b/gaseous-server/Controllers/V1.1/UserProfileController.cs @@ -31,7 +31,7 @@ namespace gaseous_server.Controllers [Route("{UserId}")] [ProducesResponseType(typeof(Models.UserProfile), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ResponseCache(CacheProfileName = "5Minute")] + [ResponseCache(CacheProfileName = "Default30")] public ActionResult GetUserProfile(string UserId) { Classes.UserProfile profile = new Classes.UserProfile(); diff --git a/gaseous-server/Models/UserProfile.cs b/gaseous-server/Models/UserProfile.cs index a9e4f74..625a896 100644 --- a/gaseous-server/Models/UserProfile.cs +++ b/gaseous-server/Models/UserProfile.cs @@ -1,3 +1,5 @@ +using IGDB.Models; + namespace gaseous_server.Models { public class UserProfile @@ -5,6 +7,13 @@ namespace gaseous_server.Models public Guid UserId { get; set; } public string DisplayName { get; set; } public string Quip { get; set; } + public NowPlayingItem? NowPlaying { get; set; } + public class NowPlayingItem + { + public Game Game { get; set; } + public Platform Platform { get; set; } + public long Duration { get; set; } + } public ProfileImageItem? Avatar { get; set; } public ProfileImageItem? ProfileBackground { get; set; } public Dictionary Data { get; set; } diff --git a/gaseous-server/Support/Database/MySQL/gaseous-1024.sql b/gaseous-server/Support/Database/MySQL/gaseous-1024.sql index a2ea403..96794e6 100644 --- a/gaseous-server/Support/Database/MySQL/gaseous-1024.sql +++ b/gaseous-server/Support/Database/MySQL/gaseous-1024.sql @@ -90,4 +90,10 @@ SELECT `Games_Roms`.*, CONCAT( ) AS `Path`, `GameLibraries`.`Name` AS `LibraryName` FROM `Games_Roms` - JOIN `GameLibraries` ON `Games_Roms`.`LibraryId` = `GameLibraries`.`Id`; \ No newline at end of file + JOIN `GameLibraries` ON `Games_Roms`.`LibraryId` = `GameLibraries`.`Id`; + +CREATE VIEW view_UserTimeTracking AS +SELECT *, DATE_ADD( + SessionTime, INTERVAL SessionLength MINUTE + ) AS SessionEnd +FROM UserTimeTracking; \ No newline at end of file diff --git a/gaseous-server/wwwroot/scripts/account.js b/gaseous-server/wwwroot/scripts/account.js index 9c5d431..c115e40 100644 --- a/gaseous-server/wwwroot/scripts/account.js +++ b/gaseous-server/wwwroot/scripts/account.js @@ -366,6 +366,18 @@ class ProfileCard { this.Quip.classList.add('profile-card-quip'); this.ProfileBody = document.createElement('div'); this.ProfileBody.classList.add('profile-card-body'); + this.ProfileNowPlaying = document.createElement('div'); + this.ProfileNowPlaying.classList.add('profile-card-now-playing-body'); + this.ProfileNowPlayingLabel = document.createElement('div'); + this.ProfileNowPlayingLabel.classList.add('profile-card-now-playing-label'); + this.ProfileNowPlayingCover = document.createElement('div'); + this.ProfileNowPlayingCover.classList.add('profile-card-now-playing-cover'); + this.ProfileNowPlayingTitle = document.createElement('div'); + this.ProfileNowPlayingTitle.classList.add('profile-card-now-playing-title'); + this.ProfileNowPlayingPlatform = document.createElement('div'); + this.ProfileNowPlayingPlatform.classList.add('profile-card-now-playing-platform'); + this.ProfileNowPlayingDuration = document.createElement('div'); + this.ProfileNowPlayingDuration.classList.add('profile-card-now-playing-duration'); this.Avatar = document.createElement('div'); this.Avatar.classList.add('profile-card-avatar'); @@ -375,12 +387,28 @@ class ProfileCard { this.ProfileBody.appendChild(this.DisplayName); this.ProfileBody.appendChild(this.Quip); + // now playing + this.ProfileNowPlaying.appendChild(this.ProfileNowPlayingLabel); + this.ProfileNowPlaying.appendChild(this.ProfileNowPlayingCover); + this.ProfileNowPlaying.appendChild(this.ProfileNowPlayingTitle); + this.ProfileNowPlaying.appendChild(this.ProfileNowPlayingPlatform); + this.ProfileNowPlaying.appendChild(this.ProfileNowPlayingDuration); + // assemble card this.Card.appendChild(this.BackgroundImage); this.Card.appendChild(this.ProfileBody); + this.Card.appendChild(this.ProfileNowPlaying); this.Card.appendChild(this.Avatar); + this.ProfileData = null; const response = this.#FetchProfile(this); + + // set timeout to refresh the profile card every 30 seconds + let callingObject = this; + setInterval(function () { + callingObject.#FetchProfile(callingObject); + }, 30000); + return this.Card; } @@ -392,12 +420,57 @@ class ProfileCard { } else { const profile = await response.json(); if (profile) { - this.Avatar.appendChild(new Avatar(callingObject.ProfileId, 50, 50)); - if (profile.profileBackground) { - this.BackgroundImage.style = "background-image: url('/api/v1.1/UserProfile/" + callingObject.ProfileId + "/Background');"; + let stillUpdateAnyway = false; + if (callingObject.ProfileData === null) { + callingObject.ProfileData = profile; + stillUpdateAnyway = true; } - this.DisplayName.innerHTML = profile.displayName; - this.Quip.innerHTML = profile.quip; + + // update avatar if different + if (callingObject.ProfileData.avatar !== profile.avatar || stillUpdateAnyway === true) { + callingObject.Avatar.innerHTML = ""; + callingObject.Avatar.appendChild(new Avatar(callingObject.ProfileId, 50, 50)); + } + + // update profile background if different + if (callingObject.ProfileData.profileBackground !== profile.profileBackground || stillUpdateAnyway === true) { + if (profile.profileBackground) { + callingObject.BackgroundImage.style = "background-image: url('/api/v1.1/UserProfile/" + callingObject.ProfileId + "/Background');"; + } else { + callingObject.BackgroundImage.style = ""; + } + } + + // update display name if different + if (callingObject.ProfileData.displayName !== profile.displayName || stillUpdateAnyway === true) { + callingObject.DisplayName.innerHTML = profile.displayName; + } + + // update quip if different + if (callingObject.ProfileData.quip !== profile.quip || stillUpdateAnyway === true) { + callingObject.Quip.innerHTML = profile.quip; + } + + if (profile.nowPlaying) { + callingObject.ProfileNowPlayingLabel.innerHTML = "Now Playing"; + if (profile.nowPlaying.game.cover) { + callingObject.ProfileNowPlayingCover.style = "background-image: url('/api/v1.1/Games/" + profile.nowPlaying.game.id + "/cover/" + profile.nowPlaying.game.cover.id + "/image/cover_big/" + profile.nowPlaying.game.cover.id + ".jpg');"; + } else { + callingObject.ProfileNowPlayingCover.style = "background-image: url('/images/unknowngame.png');"; + } + callingObject.ProfileNowPlayingTitle.innerHTML = profile.nowPlaying.game.name; + callingObject.ProfileNowPlayingPlatform.innerHTML = profile.nowPlaying.platform.name; + if (profile.nowPlaying.duration === 1) { + callingObject.ProfileNowPlayingDuration.innerHTML = profile.nowPlaying.duration + " minute"; + } else { + callingObject.ProfileNowPlayingDuration.innerHTML = profile.nowPlaying.duration + " minutes"; + } + callingObject.ProfileNowPlaying.style.display = ""; + } else { + callingObject.ProfileNowPlaying.style.display = "none"; + } + + callingObject.ProfileData = profile; } } }); diff --git a/gaseous-server/wwwroot/styles/style.css b/gaseous-server/wwwroot/styles/style.css index 4243ee9..4d4f425 100644 --- a/gaseous-server/wwwroot/styles/style.css +++ b/gaseous-server/wwwroot/styles/style.css @@ -2706,6 +2706,46 @@ button:not(.select2-selection__choice__remove):not(.select2-selection__clear):no color: var(--profile-card-quip-text-color); } +.profile-card-now-playing-body { + position: relative; + min-height: 70px; + background-color: var(--profile-card-body-background); + padding-top: 0px; + padding-left: 10px; + padding-right: 10px; + padding-bottom: 10px; +} + +.profile-card-now-playing-label { + font-style: italic; + margin-bottom: 5px; +} + +.profile-card-now-playing-cover { + position: absolute; + left: 10px; + top: 25px; + width: 40px; + height: 40px; + background-position: center center; + background-repeat: no-repeat; + background-size: contain; +} + +.profile-card-now-playing-title { + margin-left: 50px; + font-weight: bold; +} + +.profile-card-now-playing-platform { + margin-left: 50px; +} + +.profile-card-now-playing-duration { + margin-top: 10px; + margin-left: 50px; +} + .password-rules { margin-left: 0; padding-left: 0;