diff --git a/PlaylistApp.Server/Controllers/SteamController.cs b/PlaylistApp.Server/Controllers/SteamController.cs index d2e472ee..1648f574 100644 --- a/PlaylistApp.Server/Controllers/SteamController.cs +++ b/PlaylistApp.Server/Controllers/SteamController.cs @@ -22,7 +22,7 @@ public SteamController(ISteamService steamService, ISteamOrchestrator steamOrche } [HttpPost("getuseractionlog")] - public async Task GetGamesBySteamId([FromBody] SteamActionLogRequest steamActionLogRequest) + public async Task> GetGamesBySteamId([FromBody] SteamActionLogRequest steamActionLogRequest) { return await steamOrchestrator.CollectActionItemsFromSteam(steamActionLogRequest); } diff --git a/PlaylistApp.Server/Services/SteamServices/ISteamOrchestrator.cs b/PlaylistApp.Server/Services/SteamServices/ISteamOrchestrator.cs index dfefc05b..cf0735df 100644 --- a/PlaylistApp.Server/Services/SteamServices/ISteamOrchestrator.cs +++ b/PlaylistApp.Server/Services/SteamServices/ISteamOrchestrator.cs @@ -5,6 +5,6 @@ namespace PlaylistApp.Server.Services.SteamServices { public interface ISteamOrchestrator { - Task CollectActionItemsFromSteam(SteamActionLogRequest steamActionLogRequest); + Task> CollectActionItemsFromSteam(SteamActionLogRequest steamActionLogRequest); } } \ No newline at end of file diff --git a/PlaylistApp.Server/Services/SteamServices/SteamAchievementService/SteamAchievementService2.cs b/PlaylistApp.Server/Services/SteamServices/SteamAchievementService/SteamAchievementService2.cs index 487181a7..e98be3e8 100644 --- a/PlaylistApp.Server/Services/SteamServices/SteamAchievementService/SteamAchievementService2.cs +++ b/PlaylistApp.Server/Services/SteamServices/SteamAchievementService/SteamAchievementService2.cs @@ -1,14 +1,9 @@ -using IGDB.Models; -using Microsoft.EntityFrameworkCore; -using NSubstitute; +using Microsoft.EntityFrameworkCore; using PlaylistApp.Server.Data; -using PlaylistApp.Server.DTOs.SteamData.SteamAchievements; -using PlaylistApp.Server.DTOs.SteamData.SteamGames; using PlaylistApp.Server.Requests.AddRequests; using PlaylistApp.Server.Services.IGDBServices; using PlaylistApp.Server.Services.SteamServices.SteamAchievementService.SteamAchievementService; using PlaylistApp.Server.Services.UserAchievementServices; -using System.Security.Cryptography; namespace PlaylistApp.Server.Services.SteamServices.SteamAchievementService; @@ -40,6 +35,11 @@ public async Task AddMissingAchievementsToUser(Guid userId, string steamId) } var userPlatformGames = await context.UserGames.Where(x => x.UserId == user.Id).Include(x => x.PlatformGame).ToListAsync(); + HashSet userPlatformGameIds = new(userPlatformGames.Select(x => x.PlatformGameId)); + List userPgsPlatformKeys = new(userPlatformGames.Select(x => x.PlatformGame.PlatformKey ?? string.Empty)); + + List playerStatsResponse = await GetAllSteamAchievements(userPgsPlatformKeys, steamId); + var userAchievements = await context.UserAchievements.Where(x => x.UserId == user.Id).Include(a => a.Achievement).ThenInclude(pg => pg.PlatformGame).ToListAsync(); AddMultipleUserAchievementRequest AddAchievementsRequest = new(); @@ -47,31 +47,36 @@ public async Task AddMissingAchievementsToUser(Guid userId, string steamId) try { + var allPlatformGameAchievements = await context.Achievements + .Where(a => userPlatformGameIds.Contains(a.PlatformGameId)) + .ToListAsync(); foreach (var pg in userPlatformGames) { if (pg.PlatformGame.PlatformKey is not null) { - string url = $"https://api.steampowered.com/ISteamUserStats/GetPlayerAchievements/v0001/?appid={pg.PlatformGame.PlatformKey}&key={steamKey}&steamid={steamId}"; - HttpResponseMessage response = await client.GetAsync(url); - - PlayerStatsResponse jsonResponse = await response.Content.ReadFromJsonAsync() ?? new PlayerStatsResponse(); - - foreach (var ach in jsonResponse.PlayerStats.Achievements) + foreach (var jsonResponse in playerStatsResponse) { - var matchingAch = userAchievements.Where(x => x.Achievement.ExternalId == ach.Apiname).Where(x => x.Achievement.PlatformGame.Id == pg.Id).FirstOrDefault(); - if (ach.Achieved == 1 && matchingAch is null) + foreach (var ach in jsonResponse.PlayerStats.Achievements) { - var projectPlaylistAchievement = await context.Achievements.Where(x => x.PlatformGameId == pg.PlatformGameId && x.ExternalId == ach.Apiname).FirstOrDefaultAsync(); - if (projectPlaylistAchievement is not null) + var matchingAch = userAchievements.Where(x => x.Achievement.ExternalId == ach.Apiname).Where(x => x.Achievement.PlatformGame.Id == pg.Id).FirstOrDefault(); + if (ach.Achieved == 1 && matchingAch is null) { - AddUserAchievementRequest request = new() + //var projectPlaylistAchievement = await context.Achievements.Where(x => x.PlatformGameId == pg.PlatformGameId && x.ExternalId == ach.Apiname).FirstOrDefaultAsync(); // Make this line faster and maybe not use the context every time + + var projectPlaylistAchievement = allPlatformGameAchievements + .FirstOrDefault(x => x.PlatformGameId == pg.PlatformGameId && x.ExternalId == ach.Apiname); + + if (projectPlaylistAchievement is not null) { - UserGuid = userId, - IsSelfSubmitted = false, - DateAchieved = Conversions.UnixTimeToDateTime(ach.UnlockTime), - AchievementId = projectPlaylistAchievement.Id, - }; - AddAchievementsRequest.UserAchievementRequests.Add(request); + AddUserAchievementRequest request = new() + { + UserGuid = userId, + IsSelfSubmitted = false, + DateAchieved = Conversions.UnixTimeToDateTime(ach.UnlockTime), + AchievementId = projectPlaylistAchievement.Id, + }; + AddAchievementsRequest.UserAchievementRequests.Add(request); + } } } } @@ -84,4 +89,20 @@ public async Task AddMissingAchievementsToUser(Guid userId, string steamId) throw new Exception("An Error occured when fetching achievements from Steam: " + e.Message); } } + + public async Task> GetAllSteamAchievements(List userPgsPlatformKeys, string steamId) + { + List responses = new(); + + var steamKey = config["steamkey"]; + foreach (var key in userPgsPlatformKeys) + { + string url = $"https://api.steampowered.com/ISteamUserStats/GetPlayerAchievements/v0001/?appid={key}&key={steamKey}&steamid={steamId}"; + HttpResponseMessage response = await client.GetAsync(url); + + responses.Add(await response.Content.ReadFromJsonAsync() ?? new PlayerStatsResponse()); + } + + return responses; + } } \ No newline at end of file diff --git a/PlaylistApp.Server/Services/SteamServices/SteamGameService/ISteamService.cs b/PlaylistApp.Server/Services/SteamServices/SteamGameService/ISteamService.cs index 22c8dabb..0f576293 100644 --- a/PlaylistApp.Server/Services/SteamServices/SteamGameService/ISteamService.cs +++ b/PlaylistApp.Server/Services/SteamServices/SteamGameService/ISteamService.cs @@ -9,10 +9,10 @@ public interface ISteamService public Task GetGamesFromUserBasedOffOfSteamId(string steamId); public Task> ConvertSteamToPlatformGames(OwnedGamesResponse jsonResponse); - public Task FindGameInconsistenciesWithUserAccount(List matchingPlatformGames, List steamGames, Guid userGuid); + public Task> FindGameInconsistenciesWithUserAccount(List matchingPlatformGames, List steamGames, Guid userGuid); public Task AddMissingGamesToUserGames(OwnedGamesResponse response, Guid userGuid); - public Task FixTimeDifferences(OwnedGamesResponse response, List matchingPlatformGames, List steamGames, Guid userGuid); + public Task> FixTimeDifferences(OwnedGamesResponse response, List matchingPlatformGames, List steamGames, Guid userGuid); public string ExtractSteamIdFromUrl(string urlParams); public void AddSteamKeyToUser(string userId, string steamId); public Task AddSteamUsernameToUser(string userGuid, string steamId); diff --git a/PlaylistApp.Server/Services/SteamServices/SteamGameService/SteamService.cs b/PlaylistApp.Server/Services/SteamServices/SteamGameService/SteamService.cs index 46c23dec..098eb1ac 100644 --- a/PlaylistApp.Server/Services/SteamServices/SteamGameService/SteamService.cs +++ b/PlaylistApp.Server/Services/SteamServices/SteamGameService/SteamService.cs @@ -83,9 +83,8 @@ public async Task> ConvertSteamToPlatformGames(OwnedGamesResp return matchingPlatformGames; } - public async Task FindGameInconsistenciesWithUserAccount(List matchingPlatformGames, List steamGames, Guid userId) + public async Task> FindGameInconsistenciesWithUserAccount(List matchingPlatformGames, List steamGames, Guid userId) { - ItemAction action = new(); using var context = await dbContextFactory.CreateDbContextAsync(); var duplicatePlatformKeys = matchingPlatformGames @@ -104,35 +103,52 @@ public async Task FindGameInconsistenciesWithUserAccount(List g.Game) .Include(g => g.User) .ToList(); - HashSet userGameIds = new HashSet(usersGames.Select(x => x.PlatformGameId)); + HashSet userGameIds = new(usersGames.Select(x => x.PlatformGameId)); + + List actionsToSend = new(); int counter = 0; string previousGame = ""; - foreach (PlatformGame pg in gamesWithDuplicatePlatformKeys) + + List> pgsGroupedByGame = gamesWithDuplicatePlatformKeys + .GroupBy(x => x.GameId) + .Select(group => group.ToList()) + .ToList(); + + foreach (List pgGroup in pgsGroupedByGame) { - var steamGame = steamGames.Where(x => x.AppId.ToString() == pg.PlatformKey).FirstOrDefault(); - var userGameFromPlatform = usersGames.Where(x => x.PlatformGame.GameId == pg.GameId).ToList(); - if (steamGame != null && !userGameFromPlatform.Any()) // if the user doesn't already have this game + ItemAction actionToAdd = new(); + actionToAdd.ErrorType = $"We found multiple platforms for {pgGroup.First().Game.Title}"; + + foreach (var pg in pgGroup) { - if (pg.Game.Title != previousGame) - { - counter++; - } - previousGame = pg.Game.Title; + var steamGame = steamGames.Where(x => x.AppId.ToString() == pg.PlatformKey).FirstOrDefault(); + var userGameFromPlatform = usersGames.Where(x => x.PlatformGame.GameId == pg.GameId).ToList(); - action.ItemOptions.Add(new ItemOption() + if (steamGame != null && !userGameFromPlatform.Any()) // if the user doesn't already have this game { - ErrorText = pg.Platform.PlatformName, - ResolveUrl = $"/action/platforms/?hours={steamGame.PlaytimeForever}&pgid={pg.Id}&user={userId}", - GameTitle = pg.Game.Title, - Hours=steamGame.PlaytimeForever, - }); + + if (pg.Game.Title != previousGame) + { + counter++; + } + previousGame = pg.Game.Title; + + actionToAdd.ItemOptions.Add(new ItemOption() + { + ErrorText = pg.Platform.PlatformName, + ResolveUrl = $"/action/platforms/?hours={steamGame.PlaytimeForever}&pgid={pg.Id}&user={userId}", + GameTitle = pg.Game.Title, + Hours = steamGame.PlaytimeForever, + }); + + } } + actionsToSend.Add(actionToAdd); } - action.ErrorType = "Data mismatch. Select which data is correct."; - return action; + return actionsToSend; } public async Task AddMissingGamesToUserGames(OwnedGamesResponse response, Guid userGuid) @@ -189,9 +205,8 @@ public async Task AddMissingGamesToUserGames(OwnedGamesResponse response, Guid u await context.SaveChangesAsync(); } - public async Task FixTimeDifferences(OwnedGamesResponse response, List matchingPlatformGames, List steamGames, Guid userGuid) + public async Task> FixTimeDifferences(OwnedGamesResponse response, List matchingPlatformGames, List steamGames, Guid userGuid) { - ItemAction action = new ItemAction(); var duplicatePlatformKeys = matchingPlatformGames .GroupBy(pg => pg.PlatformKey) @@ -217,35 +232,43 @@ public async Task FixTimeDifferences(OwnedGamesResponse response, Li HashSet userGameIds = new HashSet(usersGames.Select(x => x.PlatformGameId)); int counter = 0; + List actionsToSend = new(); + foreach (PlatformGame pg in matchingPlatformGames) { UserGame? ug = usersGames.Where(x => x.PlatformGameId == pg.Id && x.UserId == userId).FirstOrDefault(); SteamRawGame? steamGame = steamGames.Where(x => x.AppId.ToString() == pg.PlatformKey).FirstOrDefault(); - - if (ug!.TimePlayed != steamGame?.PlaytimeForever) { counter++; - action.ItemOptions.Add(new ItemOption() - { - ErrorText = $"Steam record ", - ResolveUrl = $"/action/hours?hours={steamGame!.PlaytimeForever}&pgid={ug.PlatformGame.Id}&user={userGuid}", - GameTitle = ug.PlatformGame.Game.Title, - Hours = steamGame!.PlaytimeForever, - }); - action.ItemOptions.Add(new ItemOption() + ItemAction action = new ItemAction() { - ErrorText = $"Playlist record ", - ResolveUrl = $"/action/hours?hours={ug.TimePlayed}&pgid={ug.PlatformGame.Id}&user={userGuid}", - GameTitle = ug.PlatformGame.Game.Title, - Hours = (int)(ug.TimePlayed!), - }); + ErrorType = "We found a difference in hours. Which hours are correct?", + ItemOptions = new List + { + new ItemOption() + { + ErrorText = $"Steam record ", + ResolveUrl = $"/action/hours?hours={steamGame!.PlaytimeForever}&pgid={ug.PlatformGame.Id}&user={userGuid}", + GameTitle = ug.PlatformGame.Game.Title, + Hours = steamGame!.PlaytimeForever, + }, + new ItemOption() + { + ErrorText = $"Playlist record ", + ResolveUrl = $"/action/hours?hours={ug.TimePlayed}&pgid={ug.PlatformGame.Id}&user={userGuid}", + GameTitle = ug.PlatformGame.Game.Title, + Hours = (int)(ug.TimePlayed!), + } + }, + }; + + actionsToSend.Add(action); } } - action.ErrorType = "Data mismatch. Select which data is correct."; - return action; + return actionsToSend; } public string ExtractSteamIdFromUrl(string urlParams) diff --git a/PlaylistApp.Server/Services/SteamServices/SteamOrchestrator.cs b/PlaylistApp.Server/Services/SteamServices/SteamOrchestrator.cs index fecf62c2..18691e36 100644 --- a/PlaylistApp.Server/Services/SteamServices/SteamOrchestrator.cs +++ b/PlaylistApp.Server/Services/SteamServices/SteamOrchestrator.cs @@ -19,7 +19,7 @@ public SteamOrchestrator(ISteamService steamService, ISteamAchievementService st this.steamAchievementService = steamAchievementService; } - public async Task CollectActionItemsFromSteam(SteamActionLogRequest steamActionLogRequest) + public async Task> CollectActionItemsFromSteam(SteamActionLogRequest steamActionLogRequest) { // step 1: get all games from steam (SteamRawGames) OwnedGamesResponse steamApiResponse = await steamService.GetGamesFromUserBasedOffOfSteamId(steamActionLogRequest.UserSteamId); @@ -31,18 +31,18 @@ public async Task CollectActionItemsFromSteam(SteamActionLogRequest List platformGamesFromSteam = await steamService.ConvertSteamToPlatformGames(steamApiResponse); // step 3: Check for game inconsistencies (games that the user doesn't have in their library but they show multiple platforms) - ItemAction itemActions = await steamService.FindGameInconsistenciesWithUserAccount(platformGamesFromSteam, steamGames, steamActionLogRequest.UserId); + List itemActions = await steamService.FindGameInconsistenciesWithUserAccount(platformGamesFromSteam, steamGames, steamActionLogRequest.UserId); // step 4: Add games that don't have any problems to the user await steamService.AddMissingGamesToUserGames(steamApiResponse, steamActionLogRequest.UserId); // step 5: Find games user has but with different hours. - ItemAction itemActions2 = await steamService.FixTimeDifferences(steamApiResponse, platformGamesFromSteam, steamGames, steamActionLogRequest.UserId); + List itemActions2 = await steamService.FixTimeDifferences(steamApiResponse, platformGamesFromSteam, steamGames, steamActionLogRequest.UserId); // step 6: of synced games, auto add achievements user hasn't added to playlist yet (under development) await steamAchievementService.AddMissingAchievementsToUser(steamActionLogRequest.UserId, steamActionLogRequest.UserSteamId); - itemActions.ItemOptions.AddRange(itemActions2.ItemOptions); + itemActions.AddRange(itemActions2); return itemActions; } diff --git a/PlaylistApp.Test/Services/SteamServiceTests.cs b/PlaylistApp.Test/Services/SteamServiceTests.cs new file mode 100644 index 00000000..08cd4f1a --- /dev/null +++ b/PlaylistApp.Test/Services/SteamServiceTests.cs @@ -0,0 +1,18 @@ +using PlaylistApp.Server.Services.SteamServices.SteamAchievementService.SteamAchievementService; +using PlaylistApp.Server.Services.SteamServices.SteamGameService; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlaylistApp.Test.Services; + +public class SteamServiceTests +{ + [Fact] + public void SteamAchievementServiceTest1() + { + ISteamAchievementService steamAchSvc; + } +} diff --git a/playlistapp.client/src/ApiServices/SteamService.ts b/playlistapp.client/src/ApiServices/SteamService.ts index 1726264a..2c13b078 100644 --- a/playlistapp.client/src/ApiServices/SteamService.ts +++ b/playlistapp.client/src/ApiServices/SteamService.ts @@ -10,7 +10,7 @@ export const SteamService = { console.error("No Steam Action Log Request provided."); } try { - const response = await axios.post( + const response = await axios.post( `${import.meta.env.VITE_URL}/Steam/getuseractionlog`, steamActionLogRequest, { @@ -19,7 +19,6 @@ export const SteamService = { }, } ); - //console.log(Date.now().toString(), response.data.itemOptions); return response.data; } catch (error) { console.error( diff --git a/playlistapp.client/src/individual_components/Review.tsx b/playlistapp.client/src/individual_components/Review.tsx index f8330981..4722d948 100644 --- a/playlistapp.client/src/individual_components/Review.tsx +++ b/playlistapp.client/src/individual_components/Review.tsx @@ -6,7 +6,7 @@ import formatDate from "@/lib/date.ts"; interface props { review: GameReview; - currentUserGuid: string; + currentUserGuid: string | undefined; } const Review: React.FC = ({ review, currentUserGuid }) => { @@ -17,7 +17,7 @@ const Review: React.FC = ({ review, currentUserGuid }) => { Neil image
@@ -46,7 +46,8 @@ const Review: React.FC = ({ review, currentUserGuid }) => { : `` }`} > - {currentUserGuid == review.user.guid ? ( + {currentUserGuid != undefined && + currentUserGuid == review.user.guid ? (
= ({ review, currentUserGuid }) => {
- + {currentUserGuid != undefined && ( + + )} ); }; diff --git a/playlistapp.client/src/individual_components/ReviewModal.tsx b/playlistapp.client/src/individual_components/ReviewModal.tsx index 56dede96..91b190ee 100644 --- a/playlistapp.client/src/individual_components/ReviewModal.tsx +++ b/playlistapp.client/src/individual_components/ReviewModal.tsx @@ -4,9 +4,11 @@ import { UserAccountContextInterface } from "@/@types/userAccount"; import { UserAccountContext } from "@/contexts/UserAccountContext"; import { GameQueries } from "@/queries/GameQueries"; import { GameReviewQueries } from "@/queries/GameReviewQueries"; +import { PencilSquareIcon } from "@heroicons/react/24/outline"; import Slider from "@mui/material/Slider"; import React, { FC } from "react"; import { useRef, useState } from "react"; +import toast from "react-hot-toast"; import { useParams } from "react-router-dom"; interface props { @@ -23,7 +25,7 @@ const ReviewModal: FC = ({ editReview, }) => { const [isOpen, setIsOpen] = useState(false); - const [val, setVal] = useState(editVal ? editVal : 0); + const [currentRatingVal, setVal] = useState(editVal ? editVal : 0); const [reviewText, setReviewText] = useState( editReview ? editReview : "" ); @@ -71,11 +73,21 @@ const ReviewModal: FC = ({ const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); + if (reviewText.length == 0 || reviewText == "") { + toast.error("You can't leave a review without any text."); + return; + } + + if (currentRatingVal == 0) { + toast.error("You can't leave a review with a rating of 0"); + return; + } + if (game && usr && !editReview && !editVal) { setReview({ gameId: game?.id, userId: usr?.id, - rating: val, + rating: currentRatingVal, text: reviewText, }); @@ -87,7 +99,7 @@ const ReviewModal: FC = ({ if (game && usr && gameReviewId && editReview && editVal) { setUpdateReview({ gameReviewId: gameReviewId, - rating: val, + rating: currentRatingVal, reviewText: reviewText, }); @@ -106,28 +118,17 @@ const ReviewModal: FC = ({ return ( <> {editReview && editVal ? ( - - - - + /> ) : (