Added SteamAPI and achievement searching
This commit is contained in:
parent
b229ff9a15
commit
627cc810ed
61 changed files with 2781 additions and 903 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
tmp/*
|
5
backend/.gitignore
vendored
5
backend/.gitignore
vendored
|
@ -23,8 +23,5 @@ src/main/resources/application-local.properties
|
||||||
# Server Keystore
|
# Server Keystore
|
||||||
src/main/resources/achievements-ssl-key.p12
|
src/main/resources/achievements-ssl-key.p12
|
||||||
|
|
||||||
# Api Keys
|
|
||||||
apikeys/
|
|
||||||
|
|
||||||
# Program Data
|
# Program Data
|
||||||
images/
|
storage/
|
|
@ -4,11 +4,15 @@ import achievements.misc.DbConnection;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||||
|
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
|
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
public class Application {
|
public class Application {
|
||||||
|
@ -34,4 +38,9 @@ public class Application {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RestTemplate restTemplate() {
|
||||||
|
return new RestTemplate();
|
||||||
|
}
|
||||||
}
|
}
|
17
backend/src/main/java/achievements/apis/PlatformAPI.java
Normal file
17
backend/src/main/java/achievements/apis/PlatformAPI.java
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package achievements.apis;
|
||||||
|
|
||||||
|
import achievements.data.APIResponse;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
public abstract class PlatformAPI {
|
||||||
|
|
||||||
|
protected int id;
|
||||||
|
protected RestTemplate rest;
|
||||||
|
|
||||||
|
protected PlatformAPI(int id, RestTemplate rest) {
|
||||||
|
this.id = id;
|
||||||
|
this.rest = rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract APIResponse get(String userId);
|
||||||
|
}
|
102
backend/src/main/java/achievements/apis/SteamAPI.java
Normal file
102
backend/src/main/java/achievements/apis/SteamAPI.java
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package achievements.apis;
|
||||||
|
|
||||||
|
import achievements.apis.steam.GetOwnedGameBody;
|
||||||
|
import achievements.apis.steam.GetPlayerAchievementsBody;
|
||||||
|
import achievements.apis.steam.GetSchemaForGameBody;
|
||||||
|
import achievements.data.APIResponse;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class SteamAPI extends PlatformAPI {
|
||||||
|
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
public SteamAPI(int id, RestTemplate rest) {
|
||||||
|
super(id, rest);
|
||||||
|
try {
|
||||||
|
var file = new FileInputStream("storage/apis/" + id + ".properties");
|
||||||
|
var properties = new Properties();
|
||||||
|
properties.load(file);
|
||||||
|
|
||||||
|
apiKey = properties.getProperty("api-key");
|
||||||
|
file.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public APIResponse get(String userId) {
|
||||||
|
var headers = new HttpHeaders();
|
||||||
|
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
|
||||||
|
var entity = new HttpEntity<String>(headers);
|
||||||
|
var ownedGamesUrl = UriComponentsBuilder.fromHttpUrl("http://api.steampowered.com/IPlayerService/GetOwnedGames/v1/")
|
||||||
|
.queryParam("key", apiKey)
|
||||||
|
.queryParam("steamid", userId)
|
||||||
|
.queryParam("include_appinfo", true)
|
||||||
|
.queryParam("include_played_free_games", true)
|
||||||
|
.toUriString();
|
||||||
|
|
||||||
|
var gameSchemaBaseUrl = UriComponentsBuilder.fromHttpUrl("https://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v2/")
|
||||||
|
.queryParam("key", apiKey);
|
||||||
|
var playerAchievementsBaseUrl = UriComponentsBuilder.fromHttpUrl("https://api.steampowered.com/ISteamUserStats/GetPlayerAchievements/v1/")
|
||||||
|
.queryParam("key", apiKey)
|
||||||
|
.queryParam("steamid", userId);
|
||||||
|
|
||||||
|
var games = new ArrayList<APIResponse.Game>();
|
||||||
|
var ownedResponse = rest.exchange(ownedGamesUrl, HttpMethod.GET, entity, GetOwnedGameBody.class).getBody();
|
||||||
|
for (var game : ownedResponse.getResponse().getGames()) {
|
||||||
|
var newGame = new APIResponse.Game();
|
||||||
|
newGame.setPlatformGameId(Integer.toString(game.getAppid()));
|
||||||
|
newGame.setName(game.getName());
|
||||||
|
newGame.setThumbnail("https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/" + game.getAppid() + "/" + game.getImg_logo_url() + ".jpg");
|
||||||
|
newGame.setPlayed(game.getPlaytime_forever() > 0);
|
||||||
|
|
||||||
|
var achievements = new HashMap<String, APIResponse.Game.Achievement>();
|
||||||
|
|
||||||
|
var gameSchemaUrl = gameSchemaBaseUrl.cloneBuilder()
|
||||||
|
.queryParam("appid", game.getAppid())
|
||||||
|
.toUriString();
|
||||||
|
var playerAchievementsUrl = playerAchievementsBaseUrl.cloneBuilder()
|
||||||
|
.queryParam("appid", game.getAppid())
|
||||||
|
.toUriString();
|
||||||
|
|
||||||
|
|
||||||
|
var schemaResponse = rest.exchange(gameSchemaUrl, HttpMethod.GET, entity, GetSchemaForGameBody.class).getBody().getGame().getAvailableGameStats();
|
||||||
|
if (schemaResponse != null && schemaResponse.getAchievements() != null) {
|
||||||
|
for (var schema : schemaResponse.getAchievements()) {
|
||||||
|
var achievement = new APIResponse.Game.Achievement();
|
||||||
|
achievement.setName(schema.getDisplayName());
|
||||||
|
achievement.setDescription(schema.getDescription());
|
||||||
|
achievement.setStages(1);
|
||||||
|
achievement.setThumbnail(schema.getIcon());
|
||||||
|
achievements.put(schema.getName(), achievement);
|
||||||
|
}
|
||||||
|
|
||||||
|
var playerAchievementsResponse = rest.exchange(playerAchievementsUrl, HttpMethod.GET, entity, GetPlayerAchievementsBody.class).getBody().getPlayerstats().getAchievements();
|
||||||
|
for (var achievement : playerAchievementsResponse) {
|
||||||
|
achievements.get(achievement.getApiname()).setProgress(achievement.getAchieved());
|
||||||
|
}
|
||||||
|
|
||||||
|
newGame.setAchievements(new ArrayList<>(achievements.values()));
|
||||||
|
if (newGame.getAchievements().size() > 0) {
|
||||||
|
games.add(newGame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var response = new APIResponse();
|
||||||
|
response.setGames(games);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package achievements.apis.steam;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GetOwnedGameBody {
|
||||||
|
|
||||||
|
public static class Response {
|
||||||
|
|
||||||
|
public static class Game {
|
||||||
|
@JsonProperty("appid")
|
||||||
|
private int appid;
|
||||||
|
@JsonProperty("name")
|
||||||
|
private String name;
|
||||||
|
@JsonProperty("playtime_forever")
|
||||||
|
private int playtime_forever;
|
||||||
|
@JsonProperty("img_icon_url")
|
||||||
|
private String img_icon_url;
|
||||||
|
@JsonProperty("img_logo_url")
|
||||||
|
private String img_logo_url;
|
||||||
|
|
||||||
|
public int getAppid() {
|
||||||
|
return appid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppid(int appid) {
|
||||||
|
this.appid = appid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPlaytime_forever() {
|
||||||
|
return playtime_forever;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlaytime_forever(int playtime_forever) {
|
||||||
|
this.playtime_forever = playtime_forever;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImg_icon_url() {
|
||||||
|
return img_icon_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImg_icon_url(String img_icon_url) {
|
||||||
|
this.img_icon_url = img_icon_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImg_logo_url() {
|
||||||
|
return img_logo_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImg_logo_url(String img_logo_url) {
|
||||||
|
this.img_logo_url = img_logo_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("game_count")
|
||||||
|
private int game_count;
|
||||||
|
@JsonProperty("games")
|
||||||
|
private List<Game> games;
|
||||||
|
|
||||||
|
public int getGame_count() {
|
||||||
|
return game_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGame_count(int game_count) {
|
||||||
|
this.game_count = game_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Game> getGames() {
|
||||||
|
return games;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGames(List<Game> games) {
|
||||||
|
this.games = games;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("response")
|
||||||
|
private Response response;
|
||||||
|
|
||||||
|
public Response getResponse() {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResponse(Response response) {
|
||||||
|
this.response = response;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package achievements.apis.steam;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GetPlayerAchievementsBody {
|
||||||
|
public static class PlayerStats {
|
||||||
|
public static class Achievement {
|
||||||
|
@JsonProperty("apiname")
|
||||||
|
private String apiname;
|
||||||
|
@JsonProperty("achieved")
|
||||||
|
private int achieved;
|
||||||
|
|
||||||
|
public String getApiname() {
|
||||||
|
return apiname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApiname(String apiname) {
|
||||||
|
this.apiname = apiname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAchieved() {
|
||||||
|
return achieved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAchieved(int achieved) {
|
||||||
|
this.achieved = achieved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("achievements")
|
||||||
|
private List<Achievement> achievements;
|
||||||
|
|
||||||
|
public List<Achievement> getAchievements() {
|
||||||
|
return achievements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAchievements(List<Achievement> achievements) {
|
||||||
|
this.achievements = achievements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("playerstats")
|
||||||
|
private PlayerStats playerstats;
|
||||||
|
|
||||||
|
public PlayerStats getPlayerstats() {
|
||||||
|
return playerstats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayerstats(PlayerStats playerstats) {
|
||||||
|
this.playerstats = playerstats;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package achievements.apis.steam;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GetSchemaForGameBody {
|
||||||
|
public static class Game {
|
||||||
|
public static class GameStats {
|
||||||
|
public static class Achievement {
|
||||||
|
@JsonProperty("name")
|
||||||
|
private String name;
|
||||||
|
@JsonProperty("displayName")
|
||||||
|
private String displayName;
|
||||||
|
@JsonProperty("description")
|
||||||
|
private String description;
|
||||||
|
@JsonProperty("icon")
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisplayName(String displayName) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(String icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("achievements")
|
||||||
|
private List<Achievement> achievements;
|
||||||
|
|
||||||
|
public List<Achievement> getAchievements() {
|
||||||
|
return achievements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAchievements(List<Achievement> achievements) {
|
||||||
|
this.achievements = achievements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("availableGameStats")
|
||||||
|
private GameStats availableGameStats;
|
||||||
|
|
||||||
|
public GameStats getAvailableGameStats() {
|
||||||
|
return availableGameStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvailableGameStats(GameStats availableGameStats) {
|
||||||
|
this.availableGameStats = availableGameStats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("game")
|
||||||
|
private Game game;
|
||||||
|
|
||||||
|
public Game getGame() {
|
||||||
|
return game;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGame(Game game) {
|
||||||
|
this.game = game;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package achievements.controllers;
|
||||||
|
|
||||||
|
import achievements.services.ImageService;
|
||||||
|
import achievements.services.AchievementService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/achievement")
|
||||||
|
public class AchievementController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AchievementService achievementService;
|
||||||
|
@Autowired
|
||||||
|
private ImageService imageService;
|
||||||
|
|
||||||
|
@GetMapping(value = "/{achievement}/image")
|
||||||
|
public void getProfilePicture(@PathVariable("achievement") int achievement, HttpServletResponse response) {
|
||||||
|
var icon = achievementService.getIcon(achievement);
|
||||||
|
imageService.send(icon, "achievement", response);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/auth")
|
@RequestMapping("/auth")
|
||||||
public class LoginController {
|
public class AuthController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AuthenticationService authService;
|
private AuthenticationService authService;
|
|
@ -0,0 +1,27 @@
|
||||||
|
package achievements.controllers;
|
||||||
|
|
||||||
|
import achievements.services.ImageService;
|
||||||
|
import achievements.services.GameService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/game")
|
||||||
|
public class GameController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private GameService gameService;
|
||||||
|
@Autowired
|
||||||
|
private ImageService imageService;
|
||||||
|
|
||||||
|
@GetMapping(value = "/{game}/image")
|
||||||
|
public void getProfilePicture(@PathVariable("game") int game, HttpServletResponse response) {
|
||||||
|
var icon = gameService.getIcon(game);
|
||||||
|
imageService.send(icon, "game", response);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,42 +1,27 @@
|
||||||
package achievements.controllers;
|
package achievements.controllers;
|
||||||
|
|
||||||
|
import achievements.services.ImageService;
|
||||||
import achievements.services.PlatformService;
|
import achievements.services.PlatformService;
|
||||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@RequestMapping("/platform")
|
||||||
public class PlatformController {
|
public class PlatformController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PlatformService platforms;
|
private ImageService imageService;
|
||||||
|
@Autowired
|
||||||
|
private PlatformService platformService;
|
||||||
|
|
||||||
@GetMapping(value = "/platform/image/{id}", produces = "application/json")
|
@GetMapping(value = "/{platform}/image")
|
||||||
public void getPlatformImage(@PathVariable("id") int id, HttpServletResponse response) {
|
public void getIcon(@PathVariable("platform") int platform, HttpServletResponse response) {
|
||||||
try {
|
var icon = platformService.getIcon(platform);
|
||||||
var file = new File("images/platform/" + id + ".png");
|
imageService.send(icon, "platform", response);
|
||||||
if (file.exists()) {
|
|
||||||
var stream = new FileInputStream(file);
|
|
||||||
IOUtils.copy(stream, response.getOutputStream());
|
|
||||||
|
|
||||||
response.setContentType("image/png");
|
|
||||||
response.setStatus(200);
|
|
||||||
response.flushBuffer();
|
|
||||||
stream.close();
|
|
||||||
} else {
|
|
||||||
response.setStatus(HttpStatus.BAD_REQUEST.value());
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package achievements.controllers;
|
||||||
|
|
||||||
|
import achievements.data.query.SearchAchievements;
|
||||||
|
import achievements.services.SearchService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class SearchController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SearchService searchService;
|
||||||
|
|
||||||
|
@PostMapping(value = "/achievements", consumes = "application/json", produces = "application/json")
|
||||||
|
public ResponseEntity searchAchievements(@RequestBody SearchAchievements searchAchievements) {
|
||||||
|
var achievements = searchService.searchAchievements(searchAchievements);
|
||||||
|
if (achievements != null) {
|
||||||
|
return ResponseEntity.ok(achievements);
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.badRequest().body("[]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,20 +2,18 @@ package achievements.controllers;
|
||||||
|
|
||||||
import achievements.data.APError;
|
import achievements.data.APError;
|
||||||
import achievements.data.APPostRequest;
|
import achievements.data.APPostRequest;
|
||||||
import achievements.data.query.AddPlatformRequest;
|
import achievements.data.query.AddPlatform;
|
||||||
import achievements.data.query.RemovePlatformRequest;
|
import achievements.data.query.RemovePlatform;
|
||||||
import achievements.data.query.SetUsername;
|
import achievements.data.query.SetUsername;
|
||||||
|
import achievements.services.ImageService;
|
||||||
import achievements.services.UserService;
|
import achievements.services.UserService;
|
||||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.util.FileCopyUtils;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/user")
|
@RequestMapping("/user")
|
||||||
|
@ -24,6 +22,9 @@ public class UserController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ImageService imageService;
|
||||||
|
|
||||||
@GetMapping(value = "/{user}", produces = "application/json")
|
@GetMapping(value = "/{user}", produces = "application/json")
|
||||||
public ResponseEntity getProfile(@PathVariable("user") int user) {
|
public ResponseEntity getProfile(@PathVariable("user") int user) {
|
||||||
var profile = userService.getProfile(user);
|
var profile = userService.getProfile(user);
|
||||||
|
@ -45,38 +46,21 @@ public class UserController {
|
||||||
|
|
||||||
@GetMapping(value = "/{user}/image")
|
@GetMapping(value = "/{user}/image")
|
||||||
public void getProfilePicture(@PathVariable("user") int user, HttpServletResponse response) {
|
public void getProfilePicture(@PathVariable("user") int user, HttpServletResponse response) {
|
||||||
var pfp = userService.getProfileImageType(user);
|
var profileImage = userService.getProfileImage(user);
|
||||||
if (pfp == null) {
|
imageService.send(profileImage, "user", response);
|
||||||
|
|
||||||
} else {
|
|
||||||
var file = new File("images/user/" + pfp[0] + "." + pfp[1]);
|
|
||||||
response.setContentType("image/" + pfp[2]);
|
|
||||||
try {
|
|
||||||
var stream = new FileInputStream(file);
|
|
||||||
IOUtils.copy(stream, response.getOutputStream());
|
|
||||||
|
|
||||||
response.flushBuffer();
|
|
||||||
stream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/{user}/image", consumes = "multipart/form-data", produces = "application/json")
|
@PostMapping(value = "/{user}/image", consumes = "multipart/form-data", produces = "application/json")
|
||||||
public ResponseEntity setProfilePicture(@PathVariable("user") int user, @RequestPart APPostRequest session, @RequestPart MultipartFile file) {
|
public ResponseEntity setProfilePicture(@PathVariable("user") int user, @RequestPart APPostRequest session, @RequestPart MultipartFile file) {
|
||||||
try {
|
try {
|
||||||
var type = userService.setProfileImageType(user, session.getKey(), file.getContentType());
|
var type = userService.setProfileImage(user, session.getKey(), file);
|
||||||
if ("not_an_image".equals(type)) {
|
if ("not_an_image".equals(type)) {
|
||||||
return ResponseEntity.badRequest().body("{ \"code\": 1, \"message\": \"Not an image type\" }");
|
return ResponseEntity.badRequest().body("{ \"code\": 1, \"message\": \"Not an image type\" }");
|
||||||
} else if ("unsupported_type".equals(type)) {
|
} else if ("unsupported_type".equals(type)) {
|
||||||
return ResponseEntity.badRequest().body("{ \"code\": 1, \"message\": \"Unsupported file type\" }");
|
return ResponseEntity.badRequest().body("{ \"code\": 1, \"message\": \"Unsupported file type\" }");
|
||||||
} else if ("forbidden".equals(type)) {
|
} else if ("forbidden".equals(type)) {
|
||||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("{ \"code\": 2, \"message\": \"Invalid credentials\" }");
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("{ \"code\": 2, \"message\": \"Invalid credentials\" }");
|
||||||
} else if (!"unknown".equals(type)) {
|
} else if ("success".equals(type)) {
|
||||||
var pfp = new FileOutputStream("images/user/" + user + "." + type);
|
|
||||||
FileCopyUtils.copy(file.getInputStream(), pfp);
|
|
||||||
pfp.close();
|
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body("{ \"code\": 0, \"message\": \"Success\" }");
|
return ResponseEntity.status(HttpStatus.CREATED).body("{ \"code\": 0, \"message\": \"Success\" }");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +71,7 @@ public class UserController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/{user}/platforms/add", consumes = "application/json", produces = "application/json")
|
@PostMapping(value = "/{user}/platforms/add", consumes = "application/json", produces = "application/json")
|
||||||
public ResponseEntity addPlatformForUser(@PathVariable("user") int userId, @RequestBody AddPlatformRequest request) {
|
public ResponseEntity addPlatformForUser(@PathVariable("user") int userId, @RequestBody AddPlatform request) {
|
||||||
var result = userService.addPlatform(userId, request);
|
var result = userService.addPlatform(userId, request);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body("{}");
|
return ResponseEntity.status(HttpStatus.CREATED).body("{}");
|
||||||
|
@ -97,7 +81,7 @@ public class UserController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/{user}/platforms/remove", consumes = "application/json", produces = "application/json")
|
@PostMapping(value = "/{user}/platforms/remove", consumes = "application/json", produces = "application/json")
|
||||||
public ResponseEntity removePlatformForUser(@PathVariable("user") int userId, @RequestBody RemovePlatformRequest request) {
|
public ResponseEntity removePlatformForUser(@PathVariable("user") int userId, @RequestBody RemovePlatform request) {
|
||||||
var result = userService.removePlatform(userId, request);
|
var result = userService.removePlatform(userId, request);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body("{}");
|
return ResponseEntity.status(HttpStatus.CREATED).body("{}");
|
||||||
|
|
126
backend/src/main/java/achievements/data/APIResponse.java
Normal file
126
backend/src/main/java/achievements/data/APIResponse.java
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package achievements.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class APIResponse {
|
||||||
|
|
||||||
|
public static class Game {
|
||||||
|
|
||||||
|
public static class Achievement {
|
||||||
|
@JsonProperty("name")
|
||||||
|
private String name;
|
||||||
|
@JsonProperty("description")
|
||||||
|
private String description;
|
||||||
|
@JsonProperty("stages")
|
||||||
|
private int stages;
|
||||||
|
@JsonProperty("progress")
|
||||||
|
private int progress;
|
||||||
|
@JsonProperty("thumbnail")
|
||||||
|
private String thumbnail;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStages() {
|
||||||
|
return stages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStages(int stages) {
|
||||||
|
this.stages = stages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProgress() {
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProgress(int progress) {
|
||||||
|
this.progress = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getThumbnail() {
|
||||||
|
return thumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThumbnail(String thumbnail) {
|
||||||
|
this.thumbnail = thumbnail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("platformGameId")
|
||||||
|
private String platformGameId;
|
||||||
|
@JsonProperty("name")
|
||||||
|
private String name;
|
||||||
|
@JsonProperty("played")
|
||||||
|
private boolean played;
|
||||||
|
@JsonProperty("thumbnail")
|
||||||
|
private String thumbnail;
|
||||||
|
@JsonProperty("achievements")
|
||||||
|
private List<Achievement> achievements;
|
||||||
|
|
||||||
|
public String getPlatformGameId() {
|
||||||
|
return platformGameId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlatformGameId(String platformGameId) {
|
||||||
|
this.platformGameId = platformGameId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlayed() {
|
||||||
|
return played;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayed(boolean played) {
|
||||||
|
this.played = played;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getThumbnail() {
|
||||||
|
return thumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThumbnail(String thumbnail) {
|
||||||
|
this.thumbnail = thumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Achievement> getAchievements() {
|
||||||
|
return achievements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAchievements(List<Achievement> achievements) {
|
||||||
|
this.achievements = achievements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("games")
|
||||||
|
private List<Game> games;
|
||||||
|
|
||||||
|
public List<Game> getGames() {
|
||||||
|
return games;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGames(List<Game> games) {
|
||||||
|
this.games = games;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,122 +1,38 @@
|
||||||
package achievements.data;
|
package achievements.data;
|
||||||
|
|
||||||
import achievements.data.query.NumericFilter;
|
|
||||||
import achievements.data.query.StringFilter;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
public class Achievement {
|
public class Achievement {
|
||||||
|
|
||||||
public static class Query {
|
|
||||||
|
|
||||||
@JsonProperty("sessionKey")
|
|
||||||
private String sessionKey;
|
|
||||||
@JsonProperty("name")
|
|
||||||
private StringFilter name;
|
|
||||||
@JsonProperty("stages")
|
|
||||||
private NumericFilter stages;
|
|
||||||
@JsonProperty("completion")
|
|
||||||
private NumericFilter completion;
|
|
||||||
@JsonProperty("difficulty")
|
|
||||||
private NumericFilter difficulty;
|
|
||||||
@JsonProperty("quality")
|
|
||||||
private NumericFilter quality;
|
|
||||||
|
|
||||||
public Query(String sessionKey, StringFilter name, NumericFilter stages, NumericFilter completion, NumericFilter difficulty, NumericFilter quality) {
|
|
||||||
this.sessionKey = sessionKey;
|
|
||||||
this.name = name;
|
|
||||||
this.stages = stages;
|
|
||||||
this.completion = completion;
|
|
||||||
this.difficulty = difficulty;
|
|
||||||
this.quality = quality;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSessionKey() {
|
|
||||||
return sessionKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSessionKey(String sessionKey) {
|
|
||||||
this.sessionKey = sessionKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringFilter getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(StringFilter name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NumericFilter getStages() {
|
|
||||||
return stages;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStages(NumericFilter stages) {
|
|
||||||
this.stages = stages;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NumericFilter getCompletion() {
|
|
||||||
return completion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCompletion(NumericFilter completion) {
|
|
||||||
this.completion = completion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NumericFilter getDifficulty() {
|
|
||||||
return difficulty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDifficulty(NumericFilter difficulty) {
|
|
||||||
this.difficulty = difficulty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NumericFilter getQuality() {
|
|
||||||
return quality;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setQuality(NumericFilter quality) {
|
|
||||||
this.quality = quality;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonProperty("ID")
|
@JsonProperty("ID")
|
||||||
private int id;
|
private int ID;
|
||||||
@JsonProperty("game")
|
@JsonProperty("game")
|
||||||
private int gameId;
|
private String game;
|
||||||
@JsonProperty("name")
|
@JsonProperty("name")
|
||||||
private String name;
|
private String name;
|
||||||
@JsonProperty("description")
|
@JsonProperty("description")
|
||||||
private String description;
|
private String description;
|
||||||
@JsonProperty("stages")
|
|
||||||
private int stages;
|
|
||||||
@JsonProperty("completion")
|
@JsonProperty("completion")
|
||||||
private float completion;
|
private Integer completion;
|
||||||
@JsonProperty("difficulty")
|
@JsonProperty("difficulty")
|
||||||
private float difficulty;
|
private Float difficulty;
|
||||||
@JsonProperty("quality")
|
@JsonProperty("quality")
|
||||||
private float quality;
|
private Float quality;
|
||||||
|
|
||||||
public Achievement(int id, int gameId, String name, String description, int stages, float completion, float difficulty, float quality) {
|
public int getID() {
|
||||||
this.id = id;
|
return ID;
|
||||||
this.gameId = gameId;
|
|
||||||
this.name = name;
|
|
||||||
this.description = description;
|
|
||||||
this.stages = stages;
|
|
||||||
this.completion = completion;
|
|
||||||
this.difficulty = difficulty;
|
|
||||||
this.quality = quality;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() { return id; }
|
public void setID(int ID) {
|
||||||
|
this.ID = ID;
|
||||||
public void setId(int id) { this.id = id; }
|
|
||||||
|
|
||||||
public int getGameId() {
|
|
||||||
return gameId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGameId(int gameId) {
|
public String getGame() {
|
||||||
this.gameId = gameId;
|
return game;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGame(String game) {
|
||||||
|
this.game = game;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() { return name; }
|
public String getName() { return name; }
|
||||||
|
@ -127,31 +43,27 @@ public class Achievement {
|
||||||
|
|
||||||
public void setDescription(String description) { this.description = description; }
|
public void setDescription(String description) { this.description = description; }
|
||||||
|
|
||||||
public int getStages() { return stages; }
|
public Integer getCompletion() {
|
||||||
|
|
||||||
public void setStages(int stages) { this.stages = stages; }
|
|
||||||
|
|
||||||
public float getCompletion() {
|
|
||||||
return completion;
|
return completion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCompletion(float completion) {
|
public void setCompletion(Integer completion) {
|
||||||
this.completion = completion;
|
this.completion = completion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getDifficulty() {
|
public Float getDifficulty() {
|
||||||
return difficulty;
|
return difficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDifficulty(float difficulty) {
|
public void setDifficulty(Float difficulty) {
|
||||||
this.difficulty = difficulty;
|
this.difficulty = difficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getQuality() {
|
public Float getQuality() {
|
||||||
return quality;
|
return quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setQuality(float quality) {
|
public void setQuality(Float quality) {
|
||||||
this.quality = quality;
|
this.quality = quality;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,11 @@
|
||||||
package achievements.data;
|
package achievements.data;
|
||||||
|
|
||||||
import achievements.data.query.StringFilter;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Game {
|
public class Game {
|
||||||
|
|
||||||
public static class Query {
|
|
||||||
@JsonProperty("name")
|
|
||||||
private StringFilter name;
|
|
||||||
@JsonProperty("platforms")
|
|
||||||
private StringFilter platforms;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonProperty("ID")
|
@JsonProperty("ID")
|
||||||
private int id;
|
private int id;
|
||||||
@JsonProperty("name")
|
@JsonProperty("name")
|
||||||
|
@ -24,13 +15,6 @@ public class Game {
|
||||||
@JsonProperty("achievementCount")
|
@JsonProperty("achievementCount")
|
||||||
private int achievementCount;
|
private int achievementCount;
|
||||||
|
|
||||||
public Game(int id, String name, String platform) {
|
|
||||||
this.id = id;
|
|
||||||
this.name = name;
|
|
||||||
this.platforms = new ArrayList<>();
|
|
||||||
this.platforms.add(platform);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getId() { return id; }
|
public int getId() { return id; }
|
||||||
|
|
||||||
public void setId(int id) { this.id = id; }
|
public void setId(int id) { this.id = id; }
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package achievements.data;
|
package achievements.data;
|
||||||
|
|
||||||
import achievements.data.query.StringFilter;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -11,13 +11,16 @@ public class Session {
|
||||||
private int id;
|
private int id;
|
||||||
@JsonProperty("hue")
|
@JsonProperty("hue")
|
||||||
private int hue;
|
private int hue;
|
||||||
|
@JsonProperty("admin")
|
||||||
|
private boolean admin;
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private boolean used;
|
private boolean used;
|
||||||
|
|
||||||
public Session(String key, int id, int hue) {
|
public Session(String key, int id, int hue, boolean admin) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.hue = hue;
|
this.hue = hue;
|
||||||
|
this.admin = admin;
|
||||||
this.used = false;
|
this.used = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +48,15 @@ public class Session {
|
||||||
this.hue = hue;
|
this.hue = hue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getUsed() {
|
public boolean isAdmin() {
|
||||||
|
return admin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdmin(boolean admin) {
|
||||||
|
this.admin = admin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUsed() {
|
||||||
return used;
|
return used;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ package achievements.data.query;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
public class AddPlatformRequest {
|
public class AddPlatform {
|
||||||
@JsonProperty("sessionKey")
|
@JsonProperty("sessionKey")
|
||||||
private String sessionKey;
|
private String sessionKey;
|
||||||
@JsonProperty("platformId")
|
@JsonProperty("platformId")
|
|
@ -1,32 +0,0 @@
|
||||||
package achievements.data.query;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
public class NumericFilter {
|
|
||||||
|
|
||||||
@JsonProperty("min")
|
|
||||||
private Float min;
|
|
||||||
@JsonProperty("max")
|
|
||||||
private Float max;
|
|
||||||
|
|
||||||
public NumericFilter(Float min, Float max) {
|
|
||||||
this.min = min;
|
|
||||||
this.max = max;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Float getMin() {
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMin(Float min) {
|
|
||||||
this.min = min;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Float getMax() {
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMax(Float max) {
|
|
||||||
this.max = max;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@ package achievements.data.query;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
public class RemovePlatformRequest {
|
public class RemovePlatform {
|
||||||
@JsonProperty("sessionKey")
|
@JsonProperty("sessionKey")
|
||||||
private String sessionKey;
|
private String sessionKey;
|
||||||
@JsonProperty("platformId")
|
@JsonProperty("platformId")
|
|
@ -0,0 +1,97 @@
|
||||||
|
package achievements.data.query;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public class SearchAchievements {
|
||||||
|
|
||||||
|
@JsonProperty("searchTerm")
|
||||||
|
private String searchTerm;
|
||||||
|
@JsonProperty("userId")
|
||||||
|
private Integer userId;
|
||||||
|
@JsonProperty("completed")
|
||||||
|
private boolean completed;
|
||||||
|
@JsonProperty("minCompletion")
|
||||||
|
private Float minCompletion;
|
||||||
|
@JsonProperty("maxCompletion")
|
||||||
|
private Float maxCompletion;
|
||||||
|
@JsonProperty("minDifficulty")
|
||||||
|
private Float minDifficulty;
|
||||||
|
@JsonProperty("maxDifficulty")
|
||||||
|
private Float maxDifficulty;
|
||||||
|
@JsonProperty("minQuality")
|
||||||
|
private Float minQuality;
|
||||||
|
@JsonProperty("maxQuality")
|
||||||
|
private Float maxQuality;
|
||||||
|
|
||||||
|
public String getSearchTerm() {
|
||||||
|
return searchTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchTerm(String searchTerm) {
|
||||||
|
this.searchTerm = searchTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(Integer userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompleted() {
|
||||||
|
return completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompleted(boolean completed) {
|
||||||
|
this.completed = completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getMinCompletion() {
|
||||||
|
return minCompletion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinCompletion(Float minCompletion) {
|
||||||
|
this.minCompletion = minCompletion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getMaxCompletion() {
|
||||||
|
return maxCompletion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxCompletion(Float maxCompletion) {
|
||||||
|
this.maxCompletion = maxCompletion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getMinDifficulty() {
|
||||||
|
return minDifficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinDifficulty(Float minDifficulty) {
|
||||||
|
this.minDifficulty = minDifficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getMaxDifficulty() {
|
||||||
|
return maxDifficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxDifficulty(Float maxDifficulty) {
|
||||||
|
this.maxDifficulty = maxDifficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getMinQuality() {
|
||||||
|
return minQuality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinQuality(Float minQuality) {
|
||||||
|
this.minQuality = minQuality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getMaxQuality() {
|
||||||
|
return maxQuality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxQuality(Float maxQuality) {
|
||||||
|
this.maxQuality = maxQuality;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
package achievements.data.query;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
public class StringFilter {
|
|
||||||
|
|
||||||
@JsonProperty("query")
|
|
||||||
private String query;
|
|
||||||
|
|
||||||
public StringFilter(String query) {
|
|
||||||
this.query = query;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getQuery() {
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setQuery(String query) {
|
|
||||||
this.query = query;
|
|
||||||
}
|
|
||||||
}
|
|
41
backend/src/main/java/achievements/misc/APIList.java
Normal file
41
backend/src/main/java/achievements/misc/APIList.java
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package achievements.misc;
|
||||||
|
|
||||||
|
import achievements.apis.PlatformAPI;
|
||||||
|
import achievements.apis.SteamAPI;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class APIList {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RestTemplate rest;
|
||||||
|
|
||||||
|
public final Map<Integer, PlatformAPI> apis = new HashMap<>();
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private void init() {
|
||||||
|
/*db = dbs.getConnection();
|
||||||
|
try {
|
||||||
|
|
||||||
|
var stmt = db.prepareCall("{call GetPlatforms()}");
|
||||||
|
var results = stmt.executeQuery();
|
||||||
|
|
||||||
|
while (results.next()) {
|
||||||
|
var id = results.getInt("ID");
|
||||||
|
|
||||||
|
// Wanted to pull some skekery with dynamic class loading and external api jars, but...time is of the essence and I need to cut scope as much as possible
|
||||||
|
apis.put(id, new ????(id, rest));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
apis.put(0, new SteamAPI(0, rest));
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,12 +13,12 @@ public class SessionManager {
|
||||||
private HashMap<String, Session> sessions;
|
private HashMap<String, Session> sessions;
|
||||||
|
|
||||||
public SessionManager() {
|
public SessionManager() {
|
||||||
sessions = new HashMap();
|
sessions = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Session generate(int user, int hue) {
|
public Session generate(int user, int hue, boolean admin) {
|
||||||
var key = HashManager.encode(HashManager.generateBytes(16));
|
var key = HashManager.encode(HashManager.generateBytes(16));
|
||||||
var session = new Session(key, user, hue);
|
var session = new Session(key, user, hue, admin);
|
||||||
sessions.put(key, session);
|
sessions.put(key, session);
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,13 @@ public class SessionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validate(int user, String key) {
|
public boolean validate(int user, String key) {
|
||||||
var foreign = sessions.get(key);
|
var session = sessions.get(key);
|
||||||
return foreign != null && user == foreign.getId();
|
return session != null && user == session.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validateAdmin(int user, String key) {
|
||||||
|
var session = sessions.get(key);
|
||||||
|
return session != null && user == session.getId() && session.isAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean refresh(String key) {
|
public boolean refresh(String key) {
|
||||||
|
@ -51,7 +56,7 @@ public class SessionManager {
|
||||||
public void clean() {
|
public void clean() {
|
||||||
var remove = new ArrayList<String>();
|
var remove = new ArrayList<String>();
|
||||||
sessions.forEach((key, session) -> {
|
sessions.forEach((key, session) -> {
|
||||||
if (!session.getUsed()) {
|
if (!session.isUsed()) {
|
||||||
remove.add(session.getKey());
|
remove.add(session.getKey());
|
||||||
} else {
|
} else {
|
||||||
session.setUsed(false);
|
session.setUsed(false);
|
||||||
|
|
111
backend/src/main/java/achievements/services/APIService.java
Normal file
111
backend/src/main/java/achievements/services/APIService.java
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package achievements.services;
|
||||||
|
|
||||||
|
import achievements.misc.APIList;
|
||||||
|
import achievements.misc.DbConnection;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.Types;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class APIService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RestTemplate rest;
|
||||||
|
@Autowired
|
||||||
|
private APIList apis;
|
||||||
|
@Autowired
|
||||||
|
private DbConnection dbs;
|
||||||
|
private Connection db;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private void init() {
|
||||||
|
db = dbs.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFileType(String imagePath) {
|
||||||
|
var path = imagePath.split("\\.");
|
||||||
|
return path[path.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int importUserPlatform(int userId, int platformId, String platformUserId) {
|
||||||
|
try {
|
||||||
|
var response = apis.apis.get(platformId).get(platformUserId);
|
||||||
|
|
||||||
|
var addIfNotGame = db.prepareCall("{call AddIfNotGame(?, ?, ?)}");
|
||||||
|
var addGameToPlatform = db.prepareCall("{call AddGameToPlatform(?, ?, ?)}");
|
||||||
|
var addGameToUser = db.prepareCall("{call AddGameToPlatform(?, ?, ?)}");
|
||||||
|
var addIfNotAchievement = db.prepareCall("{call AddIfNotAchievement(?, ?, ?, ?, ?, ?)}");
|
||||||
|
var setAchievementProgressForUser = db.prepareCall("{call SetAchievementProgressForUser(?, ?, ?, ?)}");
|
||||||
|
|
||||||
|
addIfNotGame.registerOutParameter(3, Types.INTEGER);
|
||||||
|
addIfNotAchievement.registerOutParameter(6, Types.INTEGER);
|
||||||
|
|
||||||
|
for (var game : response.getGames()) {
|
||||||
|
addIfNotGame.setString(1, game.getName());
|
||||||
|
addIfNotGame.setString(2, getFileType(game.getThumbnail()));
|
||||||
|
addIfNotGame.execute();
|
||||||
|
var gameId = addIfNotGame.getInt(3);
|
||||||
|
|
||||||
|
addGameToPlatform.setInt(1, gameId);
|
||||||
|
addGameToPlatform.setInt(2, platformId);
|
||||||
|
addGameToPlatform.setString(3, platformUserId);
|
||||||
|
addGameToPlatform.execute();
|
||||||
|
|
||||||
|
var gameThumbnail = new File("storage/images/game/" + gameId + "." + getFileType(game.getThumbnail()));
|
||||||
|
if (!gameThumbnail.exists()) {
|
||||||
|
var bytes = rest.getForObject(game.getThumbnail(), byte[].class);
|
||||||
|
var stream = new FileOutputStream(gameThumbnail);
|
||||||
|
stream.write(bytes);
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
addGameToUser.setInt(1, gameId);
|
||||||
|
addGameToUser.setInt(2, userId);
|
||||||
|
addGameToUser.setInt(3, platformId);
|
||||||
|
addGameToUser.execute();
|
||||||
|
|
||||||
|
for (var achievement : game.getAchievements()) {
|
||||||
|
addIfNotAchievement.setInt(1, gameId);
|
||||||
|
addIfNotAchievement.setString(2, achievement.getName());
|
||||||
|
addIfNotAchievement.setString(3, achievement.getDescription());
|
||||||
|
addIfNotAchievement.setInt(4, achievement.getStages());
|
||||||
|
addIfNotAchievement.setString(5, getFileType(achievement.getThumbnail()));
|
||||||
|
addIfNotAchievement.execute();
|
||||||
|
var achievementId = addIfNotAchievement.getInt(6);
|
||||||
|
|
||||||
|
var achievementIcon = new File("storage/images/achievement/" + achievementId + "." + getFileType(achievement.getThumbnail()));
|
||||||
|
if (!achievementIcon.exists()) {
|
||||||
|
var bytes = rest.getForObject(achievement.getThumbnail(), byte[].class);
|
||||||
|
var stream = new FileOutputStream(achievementIcon);
|
||||||
|
stream.write(bytes);
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game.isPlayed()) {
|
||||||
|
setAchievementProgressForUser.setInt(1, userId);
|
||||||
|
setAchievementProgressForUser.setInt(2, platformId);
|
||||||
|
setAchievementProgressForUser.setInt(3, achievementId);
|
||||||
|
setAchievementProgressForUser.setInt(4, achievement.getProgress());
|
||||||
|
setAchievementProgressForUser.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addIfNotGame.close();
|
||||||
|
addGameToPlatform.close();
|
||||||
|
addIfNotAchievement.close();
|
||||||
|
setAchievementProgressForUser.close();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package achievements.services;
|
||||||
|
|
||||||
|
import achievements.misc.DbConnection;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.sql.Connection;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class AchievementService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DbConnection dbs;
|
||||||
|
private Connection db;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ImageService imageService;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private void init() {
|
||||||
|
db = dbs.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getIcon(int achievementId) {
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call GetAchievementIcon(?)}");
|
||||||
|
return imageService.getImageType(stmt, achievementId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,7 +68,8 @@ public class AuthenticationService {
|
||||||
statement.getInt(1),
|
statement.getInt(1),
|
||||||
session.generate(
|
session.generate(
|
||||||
statement.getInt(6),
|
statement.getInt(6),
|
||||||
statement.getInt(7)
|
statement.getInt(7),
|
||||||
|
false
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
statement.close();
|
statement.close();
|
||||||
|
@ -95,7 +96,8 @@ public class AuthenticationService {
|
||||||
0,
|
0,
|
||||||
session.generate(
|
session.generate(
|
||||||
result.getInt("ID"),
|
result.getInt("ID"),
|
||||||
result.getInt("Hue")
|
result.getInt("Hue"),
|
||||||
|
result.getBoolean("Admin")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
34
backend/src/main/java/achievements/services/GameService.java
Normal file
34
backend/src/main/java/achievements/services/GameService.java
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package achievements.services;
|
||||||
|
|
||||||
|
import achievements.misc.DbConnection;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.sql.Connection;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class GameService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DbConnection dbs;
|
||||||
|
private Connection db;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ImageService imageService;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private void init() {
|
||||||
|
db = dbs.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getIcon(int gameId) {
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call GetAchievementIcon(?)}");
|
||||||
|
return imageService.getImageType(stmt, gameId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package achievements.services;
|
||||||
|
|
||||||
|
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ImageService {
|
||||||
|
public static final HashMap<String, String> MIME_TO_EXT = new HashMap<>();
|
||||||
|
public static final HashMap<String, String> EXT_TO_MIME = new HashMap<>();
|
||||||
|
static {
|
||||||
|
MIME_TO_EXT.put("apng", "apng");
|
||||||
|
MIME_TO_EXT.put("avif", "avif");
|
||||||
|
MIME_TO_EXT.put("gif", "gif" );
|
||||||
|
MIME_TO_EXT.put("jpeg", "jpg" );
|
||||||
|
MIME_TO_EXT.put("png", "png" );
|
||||||
|
MIME_TO_EXT.put("svg+xml", "svg" );
|
||||||
|
MIME_TO_EXT.put("webp", "webp");
|
||||||
|
|
||||||
|
EXT_TO_MIME.put("apng", "apng" );
|
||||||
|
EXT_TO_MIME.put("avif", "avif" );
|
||||||
|
EXT_TO_MIME.put("gif", "gif" );
|
||||||
|
EXT_TO_MIME.put("jpg", "jpeg" );
|
||||||
|
EXT_TO_MIME.put("png", "png" );
|
||||||
|
EXT_TO_MIME.put("svg", "svg+xml");
|
||||||
|
EXT_TO_MIME.put("webp", "webp" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getImageType(CallableStatement stmt, int id) {
|
||||||
|
try {
|
||||||
|
stmt.setInt(1, id);
|
||||||
|
|
||||||
|
var result = stmt.executeQuery();
|
||||||
|
if (result.next()) {
|
||||||
|
var type = result.getString(1);
|
||||||
|
if (type != null) {
|
||||||
|
return new String[] { id + "." + type, EXT_TO_MIME.get(type) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(String[] image, String type, HttpServletResponse response) {
|
||||||
|
var file = (File) null;
|
||||||
|
var mimeType = (String) null;
|
||||||
|
if (image == null) {
|
||||||
|
file = new File("storage/images/default/" + type + ".png");
|
||||||
|
mimeType = "png";
|
||||||
|
} else {
|
||||||
|
file = new File("storage/images/" + type + "/" + image[0]);
|
||||||
|
mimeType = image[1];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var stream = new FileInputStream(file);
|
||||||
|
IOUtils.copy(stream, response.getOutputStream());
|
||||||
|
|
||||||
|
response.setStatus(200);
|
||||||
|
response.setContentType("image/" + mimeType);
|
||||||
|
response.flushBuffer();
|
||||||
|
stream.close();
|
||||||
|
|
||||||
|
return;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
response.setStatus(500);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,8 +14,21 @@ public class PlatformService {
|
||||||
private DbConnection dbs;
|
private DbConnection dbs;
|
||||||
private Connection db;
|
private Connection db;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ImageService imageService;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
private void init() {
|
private void init() {
|
||||||
db = dbs.getConnection();
|
db = dbs.getConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String[] getIcon(int platformId) {
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call GetPlatformIcon(?)}");
|
||||||
|
return imageService.getImageType(stmt, platformId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
package achievements.services;
|
||||||
|
|
||||||
|
import achievements.data.Achievement;
|
||||||
|
import achievements.data.query.SearchAchievements;
|
||||||
|
import achievements.misc.DbConnection;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class SearchService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DbConnection dbs;
|
||||||
|
private Connection db;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private void init() {
|
||||||
|
db = dbs.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Achievement> searchAchievements(SearchAchievements query) {
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call SearchAchievements(?, ?, ?, ?, ?, ?, ?, ?, ?)}");
|
||||||
|
stmt.setString(1, query.getSearchTerm());
|
||||||
|
stmt.setBoolean(3, query.isCompleted());
|
||||||
|
if (query.getUserId() != null) { stmt.setInt( 2, query.getUserId() ); } else { stmt.setString(2, null); }
|
||||||
|
if (query.getMinCompletion() != null) { stmt.setFloat(4, query.getMinCompletion()); } else { stmt.setString(4, null); }
|
||||||
|
if (query.getMaxCompletion() != null) { stmt.setFloat(5, query.getMaxCompletion()); } else { stmt.setString(5, null); }
|
||||||
|
if (query.getMinDifficulty() != null) { stmt.setFloat(6, query.getMinDifficulty()); } else { stmt.setString(6, null); }
|
||||||
|
if (query.getMaxDifficulty() != null) { stmt.setFloat(7, query.getMaxDifficulty()); } else { stmt.setString(7, null); }
|
||||||
|
if (query.getMinQuality() != null) { stmt.setFloat(8, query.getMinQuality() ); } else { stmt.setString(8, null); }
|
||||||
|
if (query.getMaxQuality() != null) { stmt.setFloat(9, query.getMaxQuality() ); } else { stmt.setString(9, null); }
|
||||||
|
var results = stmt.executeQuery();
|
||||||
|
|
||||||
|
var achievements = new ArrayList<Achievement>();
|
||||||
|
while (results.next()) {
|
||||||
|
var achievement = new Achievement();
|
||||||
|
achievement.setID (results.getInt ("ID" ));
|
||||||
|
achievement.setGame (results.getString("Game" ));
|
||||||
|
achievement.setName (results.getString("Name" ));
|
||||||
|
achievement.setCompletion(results.getInt ("Completion")); if (results.wasNull()) { achievement.setCompletion(null); }
|
||||||
|
achievement.setDifficulty(results.getFloat ("Difficulty")); if (results.wasNull()) { achievement.setDifficulty(null); }
|
||||||
|
achievement.setQuality (results.getFloat ("Quality" )); if (results.wasNull()) { achievement.setQuality (null); }
|
||||||
|
achievements.add(achievement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return achievements;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,24 @@
|
||||||
package achievements.services;
|
package achievements.services;
|
||||||
|
|
||||||
import achievements.data.Profile;
|
import achievements.data.Profile;
|
||||||
import achievements.data.query.AddPlatformRequest;
|
import achievements.data.query.AddPlatform;
|
||||||
import achievements.data.query.RemovePlatformRequest;
|
import achievements.data.query.RemovePlatform;
|
||||||
import achievements.data.query.SetUsername;
|
import achievements.data.query.SetUsername;
|
||||||
import achievements.misc.DbConnection;
|
import achievements.misc.DbConnection;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
|
import static achievements.services.ImageService.MIME_TO_EXT;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class UserService {
|
public class UserService {
|
||||||
|
@ -25,6 +30,12 @@ public class UserService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private AuthenticationService auth;
|
private AuthenticationService auth;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private APIService apiService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ImageService imageService;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
private void init() {
|
private void init() {
|
||||||
db = dbs.getConnection();
|
db = dbs.getConnection();
|
||||||
|
@ -97,55 +108,51 @@ public class UserService {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final HashMap<String, String> VALID_IMAGE_TYPES = new HashMap<>();
|
public String[] getProfileImage(int userId) {
|
||||||
static {
|
|
||||||
VALID_IMAGE_TYPES.put("apng", "apng");
|
|
||||||
VALID_IMAGE_TYPES.put("avif", "avif");
|
|
||||||
VALID_IMAGE_TYPES.put("gif", "gif" );
|
|
||||||
VALID_IMAGE_TYPES.put("jpeg", "jpg" );
|
|
||||||
VALID_IMAGE_TYPES.put("png", "png" );
|
|
||||||
VALID_IMAGE_TYPES.put("svg+xml", "svg" );
|
|
||||||
VALID_IMAGE_TYPES.put("webp", "webp");
|
|
||||||
}
|
|
||||||
public String[] getProfileImageType(int userId) {
|
|
||||||
try {
|
try {
|
||||||
var stmt = db.prepareCall("{call GetUserImage(?)}");
|
var stmt = db.prepareCall("{call GetUserImage(?)}");
|
||||||
stmt.setInt(1, userId);
|
return imageService.getImageType(stmt, userId);
|
||||||
|
} catch (Exception e) {
|
||||||
var result = stmt.executeQuery();
|
|
||||||
if (result.next()) {
|
|
||||||
var type = result.getString("PFP");
|
|
||||||
if (type == null) {
|
|
||||||
return new String[] { "default", "png", "png" };
|
|
||||||
} else {
|
|
||||||
return new String[] { Integer.toString(userId), VALID_IMAGE_TYPES.get(type), type };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String setProfileImageType(int userId, String sessionKey, String type) {
|
public String setProfileImage(int userId, String sessionKey, MultipartFile file) {
|
||||||
try {
|
try {
|
||||||
|
var type = file.getContentType();
|
||||||
if (type.matches("image/.*")) {
|
if (type.matches("image/.*")) {
|
||||||
type = type.substring(6);
|
type = type.substring(6);
|
||||||
var extension = VALID_IMAGE_TYPES.get(type);
|
type = MIME_TO_EXT.get(type);
|
||||||
if (!auth.session().validate(userId, sessionKey)) {
|
if (!auth.session().validate(userId, sessionKey)) {
|
||||||
return "forbidden";
|
return "forbidden";
|
||||||
} else if (extension == null) {
|
} else if (type == null) {
|
||||||
return "unsupported_type";
|
return "unsupported_type";
|
||||||
} else {
|
} else {
|
||||||
var stmt = db.prepareCall("{call SetUserImage(?, ?)}");
|
var stmt = db.prepareCall("{call SetUserImage(?, ?, ?)}");
|
||||||
stmt.setInt(1, userId);
|
stmt.setInt(1, userId);
|
||||||
stmt.setString(2, type);
|
stmt.setString(2, type);
|
||||||
|
stmt.registerOutParameter(3, Types.VARCHAR);
|
||||||
|
|
||||||
stmt.execute();
|
stmt.execute();
|
||||||
|
var oldType = stmt.getString(3);
|
||||||
|
|
||||||
return extension;
|
// Delete old file
|
||||||
|
if (oldType != null && type != oldType) {
|
||||||
|
var oldFile = new File("storage/images/user/" + userId + "." + oldType);
|
||||||
|
if (oldFile.exists()) {
|
||||||
|
oldFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save new file (will overwrite old if file type didn't change)
|
||||||
|
{
|
||||||
|
var image = new FileOutputStream("storage/images/user/" + userId + "." + type);
|
||||||
|
FileCopyUtils.copy(file.getInputStream(), image);
|
||||||
|
image.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "success";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return "not_an_image";
|
return "not_an_image";
|
||||||
|
@ -156,28 +163,41 @@ public class UserService {
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
public int addPlatform(int userId, AddPlatformRequest request) {
|
public int addPlatform(int userId, AddPlatform request) {
|
||||||
try {
|
|
||||||
if (auth.session().validate(userId, request.getSessionKey())) {
|
if (auth.session().validate(userId, request.getSessionKey())) {
|
||||||
var stmt = db.prepareCall("{call AddPlatform(?, ?, ?)}");
|
try {
|
||||||
|
db.setAutoCommit(false);
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call AddUserToPlatform(?, ?, ?)}");
|
||||||
stmt.setInt(1, userId);
|
stmt.setInt(1, userId);
|
||||||
stmt.setInt(2, request.getPlatformId());
|
stmt.setInt(2, request.getPlatformId());
|
||||||
stmt.setString(3, request.getPlatformUserId());
|
stmt.setString(3, request.getPlatformUserId());
|
||||||
|
|
||||||
stmt.execute();
|
stmt.execute();
|
||||||
|
|
||||||
|
int successful = apiService.importUserPlatform(userId, request.getPlatformId(), request.getPlatformUserId());
|
||||||
|
|
||||||
|
if (successful == 0) {
|
||||||
|
db.commit();
|
||||||
|
db.setAutoCommit(true);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
db.rollback();
|
||||||
|
db.setAutoCommit(true);
|
||||||
|
} catch(SQLException e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int removePlatform(int userId, RemovePlatformRequest request) {
|
public int removePlatform(int userId, RemovePlatform request) {
|
||||||
try {
|
try {
|
||||||
if (auth.session().validate(userId, request.getSessionKey())) {
|
if (auth.session().validate(userId, request.getSessionKey())) {
|
||||||
var stmt = db.prepareCall("{call RemovePlatform(?, ?)}");
|
var stmt = db.prepareCall("{call RemoveUserFromPlatform(?, ?)}");
|
||||||
stmt.setInt(1, userId);
|
stmt.setInt(1, userId);
|
||||||
stmt.setInt(2, request.getPlatformId());
|
stmt.setInt(2, request.getPlatformId());
|
||||||
|
|
||||||
|
|
|
@ -30,10 +30,16 @@ app.get("/login", (req, res) => {
|
||||||
res.sendFile(path.join(__dirname + "/webpage/login.html"));
|
res.sendFile(path.join(__dirname + "/webpage/login.html"));
|
||||||
});
|
});
|
||||||
app.get("/", (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
res.sendFile(path.join(__dirname + "/webpage/index.html"));
|
res.sendFile(path.join(__dirname + "/webpage/search_achievements.html"));
|
||||||
});
|
});
|
||||||
app.get("/about", (req, res) => {
|
app.get("/achievements", (req, res) => {
|
||||||
res.sendFile(path.join(__dirname + "/webpage/about.html"));
|
res.sendFile(path.join(__dirname + "/webpage/search_achievements.html"));
|
||||||
|
});
|
||||||
|
app.get("/users", (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname + "/webpage/search_users.html"));
|
||||||
|
});
|
||||||
|
app.get("/games", (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname + "/webpage/search_games.html"));
|
||||||
});
|
});
|
||||||
app.get("/profile/:id", (req, res) => {
|
app.get("/profile/:id", (req, res) => {
|
||||||
res.sendFile(path.join(__dirname + "/webpage/profile.html"));
|
res.sendFile(path.join(__dirname + "/webpage/profile.html"));
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title>Achievements Project</title>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/static/styles/theme.css" />
|
|
||||||
<link rel="stylesheet" href="/static/styles/common.css" />
|
|
||||||
<link rel="stylesheet" href="/static/styles/about.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="navbar">
|
|
||||||
<template data-template="navbar: List<Basic>">
|
|
||||||
<div id="navbar-section-${section}" class="navbar-section">
|
|
||||||
<template data-template="navbar-section-${section}: List<Basic>">
|
|
||||||
<div id="navbar-item-${item}" class="navbar-item" data-page-name="${item}">
|
|
||||||
${title}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div id="content-body">
|
|
||||||
<div id="about-page" class="page">
|
|
||||||
<div class="page-subsection">
|
|
||||||
<div class="page-header">
|
|
||||||
<p class="page-header-text">About</p>
|
|
||||||
<div class="page-header-separator"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="page-subsection">
|
|
||||||
<div class="page-subsection-wrapper">
|
|
||||||
<p id="about-text" class="page-subsection-chunk">Collate achievement data from multiple platforms into a single location. Explore achievement data of yourself and others.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script src="/static/scripts/template.js"></script>
|
|
||||||
<script src="/static/scripts/common.js"></script>
|
|
||||||
<script src="/static/scripts/about.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -28,6 +28,10 @@
|
||||||
<div class="page-header-separator"></div>
|
<div class="page-header-separator"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="importing">
|
||||||
|
<p id="importing-text">Contemplating...</p>
|
||||||
|
<img id="importing-loading" class="ap-loading" src="/static/res/loading.svg" alt="Loading Symbol" />
|
||||||
|
</div>
|
||||||
<template data-template="profile-page">
|
<template data-template="profile-page">
|
||||||
<div id="profile-section-1">
|
<div id="profile-section-1">
|
||||||
<div id="profile-info" class="page-subsection">
|
<div id="profile-info" class="page-subsection">
|
||||||
|
@ -49,7 +53,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="profile-info-pfp-border" class="page-subsection-chunk">
|
<div id="profile-info-pfp-border" class="page-subsection-chunk">
|
||||||
<div id="profile-info-pfp">
|
<div id="profile-info-pfp">
|
||||||
<img id="profile-info-pfp-img" src="/api/user/${id}/image" alt="User's Profile Picture" />
|
<img id="profile-info-pfp-img" class="lazy-img" data-src="/api/user/${id}/image" alt="User's Profile Picture" />
|
||||||
<div id="profile-info-pfp-vignette"></div>
|
<div id="profile-info-pfp-vignette"></div>
|
||||||
<img id="profile-info-pfp-upload" src="/static/res/upload.svg" alt="Upload Image" />
|
<img id="profile-info-pfp-upload" src="/static/res/upload.svg" alt="Upload Image" />
|
||||||
<img id="profile-info-pfp-upload-hover" src="/static/res/upload-hover.svg" alt="Upload Image" />
|
<img id="profile-info-pfp-upload-hover" src="/static/res/upload-hover.svg" alt="Upload Image" />
|
||||||
|
|
144
frontend/webpage/search_achievements.html
Normal file
144
frontend/webpage/search_achievements.html
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Achievements Project</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/static/styles/theme.css" />
|
||||||
|
<link rel="stylesheet" href="/static/styles/common.css" />
|
||||||
|
<link rel="stylesheet" href="/static/styles/search.css" />
|
||||||
|
<link rel="stylesheet" href="/static/styles/search_achievements.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="navbar">
|
||||||
|
<template data-template="navbar: List<Basic>">
|
||||||
|
<div id="navbar-section-${section}" class="navbar-section">
|
||||||
|
<template data-template="navbar-section-${section}: List<Basic>">
|
||||||
|
<div id="navbar-item-${item}" class="navbar-item" data-page-name="${item}">
|
||||||
|
${title}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div id="content-body">
|
||||||
|
<div id="search-achievements-page" class="search page">
|
||||||
|
<div class="page-subsection">
|
||||||
|
<div class="page-header">
|
||||||
|
<p class="page-header-text">Search Achievements</p>
|
||||||
|
<div class="page-header-separator"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="page-subsection">
|
||||||
|
<div id="list-page-search-filters">
|
||||||
|
<div id="list-page-search-dropdown">
|
||||||
|
<div id="search-wrapper" class="page-subsection-wrapper">
|
||||||
|
<div id="list-page-search-pair" class="list-page-search page-subsection-chunk">
|
||||||
|
<label id="achievement-search-button" for="achievement-search">Search</label>
|
||||||
|
<input id="achievement-search-field" type="text" placeholder="Name" name="achievement-search"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="filter-dropdown-wrapper" class="page-subsection-wrapper">
|
||||||
|
<div id="filter-dropdown-stack">
|
||||||
|
<img id="filter-dropdown-button" src="/static/res/dropdown.svg" alt="Dropdown Button"/>
|
||||||
|
<img id="filter-dropdown-button-hover" src="/static/res/dropdown-hover.svg" alt="Dropdown Button"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="list-page-filters-flex">
|
||||||
|
<div class="list-page-filter-section page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Me</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-chunk page-subsection-chunk">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div id="completed-filter" class="list-page-filter">
|
||||||
|
<div class="list-page-filter-checkbox"></div>
|
||||||
|
<p class="list-page-filter-name">Completed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-section page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Difficulty</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-chunk page-subsection-chunk">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div class="list-page-filter">
|
||||||
|
<p class="list-page-filter-label">Min Completion</p>
|
||||||
|
<input id="min-completion-filter" type="text" class="list-page-filter-param"></input>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter">
|
||||||
|
<p class="list-page-filter-label">Max Completion</p>
|
||||||
|
<input id="max-completion-filter" type="text" class="list-page-filter-param"></input>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter">
|
||||||
|
<p class="list-page-filter-label">Min Difficulty</p>
|
||||||
|
<input id="min-difficulty-filter" type="text" class="list-page-filter-param"></input>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter">
|
||||||
|
<p class="list-page-filter-label">Max Difficulty</p>
|
||||||
|
<input id="max-difficulty-filter" type="text" class="list-page-filter-param"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-section page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Quality</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-chunk page-subsection-chunk">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div class="list-page-filter">
|
||||||
|
<p class="list-page-filter-label">Min Quality</p>
|
||||||
|
<input id="min-quality-filter" type="text" class="list-page-filter-param"></input>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter">
|
||||||
|
<p class="list-page-filter-label">Max Quality</p>
|
||||||
|
<input id="max-quality-filter" type="text" class="list-page-filter-param"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-partitions">
|
||||||
|
<div class="list-page-list-partition page-subsection-wrapper">
|
||||||
|
<div class="page-subsection-chunk">
|
||||||
|
<div class="list-page-list">
|
||||||
|
<div class="list-page-header">
|
||||||
|
<p class="list-page-entry-icon"></p>
|
||||||
|
<p class="list-page-entry-text achievement-game-name">Game</p>
|
||||||
|
<p class="list-page-entry-text achievement-name">Name</p>
|
||||||
|
<p class="list-page-entry-text achievement-completion">Completion Rate</p>
|
||||||
|
<p class="list-page-entry-text achievement-difficulty">Difficulty</p>
|
||||||
|
<p class="list-page-entry-text achievement-quality">Quality</p>
|
||||||
|
</div>
|
||||||
|
<template id="achievement-list-template" data-template="achievements-page-list: List<Basic>">
|
||||||
|
<div class="list-page-entry">
|
||||||
|
<img class="list-page-entry-icon lazy-img" data-src="/api/achievement/${achievement_id}/image" alt="Achievement Thumbnail"></img>
|
||||||
|
<p class="list-page-entry-text achievement-game-name">${game_name}</p>
|
||||||
|
<p class="list-page-entry-text achievement-name">${achievement_name}</p>
|
||||||
|
<p class="list-page-entry-text achievement-completion">${completion}</p>
|
||||||
|
<p class="list-page-entry-text achievement-difficulty">${difficulty}</p>
|
||||||
|
<p class="list-page-entry-text achievement-quality">${quality}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<img id="loading-results" class="ap-loading" src="/static/res/loading.svg" alt="Loading Symbol" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/static/scripts/template.js"></script>
|
||||||
|
<script src="/static/scripts/common.js"></script>
|
||||||
|
<script src="/static/scripts/search.js"></script>
|
||||||
|
<script src="/static/scripts/search_achievements.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
<link rel="stylesheet" href="/static/styles/theme.css" />
|
<link rel="stylesheet" href="/static/styles/theme.css" />
|
||||||
<link rel="stylesheet" href="/static/styles/common.css" />
|
<link rel="stylesheet" href="/static/styles/common.css" />
|
||||||
<link rel="stylesheet" href="/static/styles/index.css" />
|
<link rel="stylesheet" href="/static/styles/search.css" />
|
||||||
|
<link rel="stylesheet" href="/static/styles/search_games.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="navbar">
|
<div id="navbar">
|
||||||
|
@ -21,10 +22,10 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div id="content-body">
|
<div id="content-body">
|
||||||
<div id="index-page" class="page">
|
<div id="search-games-page" class="search page">
|
||||||
<div class="page-subsection">
|
<div class="page-subsection">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<p class="page-header-text">Achievements Project</p>
|
<p class="page-header-text">Search Games</p>
|
||||||
<div class="page-header-separator"></div>
|
<div class="page-header-separator"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -126,6 +127,7 @@
|
||||||
</div>
|
</div>
|
||||||
<script src="/static/scripts/template.js"></script>
|
<script src="/static/scripts/template.js"></script>
|
||||||
<script src="/static/scripts/common.js"></script>
|
<script src="/static/scripts/common.js"></script>
|
||||||
<script src="/static/scripts/index.js"></script>
|
<script src="/static/scripts/search.js"></script>
|
||||||
|
<script src="/static/scripts/search_games.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
133
frontend/webpage/search_users.html
Normal file
133
frontend/webpage/search_users.html
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Achievements Project</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/static/styles/theme.css" />
|
||||||
|
<link rel="stylesheet" href="/static/styles/common.css" />
|
||||||
|
<link rel="stylesheet" href="/static/styles/search.css" />
|
||||||
|
<link rel="stylesheet" href="/static/styles/search_users.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="navbar">
|
||||||
|
<template data-template="navbar: List<Basic>">
|
||||||
|
<div id="navbar-section-${section}" class="navbar-section">
|
||||||
|
<template data-template="navbar-section-${section}: List<Basic>">
|
||||||
|
<div id="navbar-item-${item}" class="navbar-item" data-page-name="${item}">
|
||||||
|
${title}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div id="content-body">
|
||||||
|
<div id="search-users-page" class="search page">
|
||||||
|
<div class="page-subsection">
|
||||||
|
<div class="page-header">
|
||||||
|
<p class="page-header-text">Search Users</p>
|
||||||
|
<div class="page-header-separator"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="page-subsection">
|
||||||
|
<div id="list-page-search-filters">
|
||||||
|
<div id="list-page-search-dropdown">
|
||||||
|
<div id="search-wrapper" class="page-subsection-wrapper">
|
||||||
|
<div id="list-page-search-pair" class="list-page-search page-subsection-chunk">
|
||||||
|
<label for="achievement-search">Search</label>
|
||||||
|
<input id="achievement-search" type="text" placeholder="Name" name="achievement-search"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="filter-dropdown-wrapper" class="page-subsection-wrapper">
|
||||||
|
<div id="filter-dropdown-stack">
|
||||||
|
<img id="filter-dropdown-button" src="/static/res/dropdown.svg" alt="Dropdown Button"/>
|
||||||
|
<img id="filter-dropdown-button-hover" src="/static/res/dropdown-hover.svg" alt="Dropdown Button"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="list-page-filters-flex">
|
||||||
|
<div class="list-page-filter-section page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Games</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-chunk page-subsection-chunk">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div id="games-owned-filter" class="list-page-filter">
|
||||||
|
<div class="list-page-filter-checkbox"></div>
|
||||||
|
<p class="list-page-filter-name">Games Owned</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-section page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">General</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-chunk page-subsection-chunk">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div id="from-games-owned-filter" class="list-page-filter">
|
||||||
|
<div class="list-page-filter-checkbox"></div>
|
||||||
|
<p class="list-page-filter-name">From My Games</p>
|
||||||
|
</div>
|
||||||
|
<div id="completed-filter" class="list-page-filter">
|
||||||
|
<div class="list-page-filter-checkbox"></div>
|
||||||
|
<p class="list-page-filter-name">Completed</p>
|
||||||
|
</div>
|
||||||
|
<div id="completed-filter" class="list-page-filter">
|
||||||
|
<div class="list-page-filter-checkbox"></div>
|
||||||
|
<p class="list-page-filter-name">Completed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-section page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Platforms</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-filter-chunk page-subsection-chunk">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div id="games-owned-filter" class="list-page-filter">
|
||||||
|
<div class="list-page-filter-checkbox"></div>
|
||||||
|
<p class="list-page-filter-name">Games Owned</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-page-partitions">
|
||||||
|
<div class="list-page-list-partition page-subsection-wrapper">
|
||||||
|
<div class="page-subsection-chunk">
|
||||||
|
<div class="list-page-list">
|
||||||
|
<div class="list-page-header">
|
||||||
|
<p class="list-page-entry-icon"></p>
|
||||||
|
<p class="list-page-entry-text achievement-name">Name</p>
|
||||||
|
<p class="list-page-entry-text achievement-description">Description</p>
|
||||||
|
<p class="list-page-entry-text achievement-stages">Stages</p>
|
||||||
|
</div>
|
||||||
|
<template data-template="achievements-page-list: List<Basic>">
|
||||||
|
<div class="list-page-entry">
|
||||||
|
<img class="list-page-entry-icon" src="/static/res/dummy_achievement.png" alt="Achievement Thumbnail"></img>
|
||||||
|
<div class="list-page-entry-text-section">
|
||||||
|
<p class="list-page-entry-text achievement-name">${achievement-name}</p>
|
||||||
|
<p class="list-page-entry-text achievement-description">${achievement-description}</p>
|
||||||
|
<p class="list-page-entry-text achievement-stages">${stages}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/static/scripts/template.js"></script>
|
||||||
|
<script src="/static/scripts/common.js"></script>
|
||||||
|
<script src="/static/scripts/search.js"></script>
|
||||||
|
<script src="/static/scripts/search_users.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
frontend/webpage/static/res/loading.svg
Normal file
1
frontend/webpage/static/res/loading.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000"><defs><style>.cls-1{fill:#eee;}</style></defs><path class="cls-1" d="M500,904C276.877,904,96,723.12305,96,500S276.877,96,500,96V0C223.8576,0,0,223.8576,0,500c0,276.14233,223.8576,500,500,500A498.43514,498.43514,0,0,0,853.55334,853.55334l-67.8822-67.8822A402.7356,402.7356,0,0,1,500,904Z"/></svg>
|
After Width: | Height: | Size: 392 B |
|
@ -25,7 +25,6 @@ const loadSession = async () => {
|
||||||
|
|
||||||
await fetch(`/api/auth/refresh`, {
|
await fetch(`/api/auth/refresh`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
|
@ -52,8 +51,10 @@ const commonTemplates = async () => {
|
||||||
{ section: "right" }
|
{ section: "right" }
|
||||||
]);
|
]);
|
||||||
template.apply("navbar-section-left").values([
|
template.apply("navbar-section-left").values([
|
||||||
{ item: "project", title: "Project" },
|
{ item: "achievements", title: "Achievements" },
|
||||||
{ item: "about", title: "About" }
|
{ item: "users", title: "Users" },
|
||||||
|
{ item: "games", title: "Games" },
|
||||||
|
{ item: "import", title: "Import" }
|
||||||
]);
|
]);
|
||||||
if (session) {
|
if (session) {
|
||||||
template.apply("navbar-section-right").values([
|
template.apply("navbar-section-right").values([
|
||||||
|
@ -67,29 +68,35 @@ const commonTemplates = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadLazyImages = () => {
|
||||||
|
const imgs = document.querySelectorAll(".lazy-img");
|
||||||
|
for (const img of imgs) {
|
||||||
|
img.src = img.dataset.src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const connectNavbar = () => {
|
const connectNavbar = () => {
|
||||||
const navItems = document.querySelectorAll(".navbar-item");
|
const navItems = document.querySelectorAll(".navbar-item");
|
||||||
|
|
||||||
|
if (!session || !session.admin) {
|
||||||
|
document.querySelector("#navbar-item-import").remove();
|
||||||
|
}
|
||||||
|
|
||||||
for (const item of navItems) {
|
for (const item of navItems) {
|
||||||
if (item.dataset.pageName === "logout") {
|
if (item.dataset.pageName === "logout") {
|
||||||
item.addEventListener("click", (clickEvent) => {
|
item.addEventListener("click", (clickEvent) => {
|
||||||
fetch(`/api/auth/logout`, {
|
fetch(`/api/auth/logout`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ key: session.key })
|
body: JSON.stringify({ key: session.key })
|
||||||
})
|
});
|
||||||
.then(response => {
|
|
||||||
session = undefined;
|
session = undefined;
|
||||||
window.location.href = "/login";
|
window.location.href = "/login";
|
||||||
});
|
});
|
||||||
});
|
|
||||||
} else if (item.dataset.pageName === "profile") {
|
} else if (item.dataset.pageName === "profile") {
|
||||||
item.addEventListener("click", (clickEvent) => window.location.href = `/profile/${session.id}`);
|
item.addEventListener("click", (clickEvent) => window.location.href = `/profile/${session.id}`);
|
||||||
} else if (item.dataset.pageName === "project") {
|
|
||||||
item.addEventListener("click", (clickEvent) => window.location.href = `/`);
|
|
||||||
} else {
|
} else {
|
||||||
item.addEventListener("click", (clickEvent) => window.location.href = `/${item.dataset.pageName}`);
|
item.addEventListener("click", (clickEvent) => window.location.href = `/${item.dataset.pageName}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
const expandTemplates = async () => {
|
|
||||||
await commonTemplates();
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadFilters = () => {
|
|
||||||
const filtersButton = document.querySelector("#filter-dropdown-stack");
|
|
||||||
const filters = document.querySelector("#list-page-filters-flex");
|
|
||||||
|
|
||||||
filtersButton.addEventListener("click", (clickEvent) => {
|
|
||||||
filtersButton.classList.toggle("active");
|
|
||||||
filters.classList.toggle("active");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("load", async (loadEvent) => {
|
|
||||||
loadRoot();
|
|
||||||
loadSession();
|
|
||||||
|
|
||||||
await expandTemplates();
|
|
||||||
await template.expand();
|
|
||||||
|
|
||||||
connectNavbar();
|
|
||||||
loadFilters();
|
|
||||||
});
|
|
|
@ -11,6 +11,7 @@ window.addEventListener("load", async (loadEvent) => {
|
||||||
password: document.querySelector("#password"),
|
password: document.querySelector("#password"),
|
||||||
confirm: document.querySelector("#confirm" )
|
confirm: document.querySelector("#confirm" )
|
||||||
};
|
};
|
||||||
|
fields.email.focus();
|
||||||
|
|
||||||
const createUser = document.querySelector("#create-user-button");
|
const createUser = document.querySelector("#create-user-button");
|
||||||
const login = document.querySelector("#login-button");
|
const login = document.querySelector("#login-button");
|
||||||
|
@ -80,7 +81,6 @@ window.addEventListener("load", async (loadEvent) => {
|
||||||
freeze();
|
freeze();
|
||||||
fetch(`/api/auth/create_user`, {
|
fetch(`/api/auth/create_user`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
|
@ -141,7 +141,6 @@ window.addEventListener("load", async (loadEvent) => {
|
||||||
freeze();
|
freeze();
|
||||||
fetch(`/api/auth/login`, {
|
fetch(`/api/auth/login`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,14 +2,25 @@ let profileId = window.location.pathname.split('/').pop();
|
||||||
let isReturn = false;
|
let isReturn = false;
|
||||||
let profileData = null;
|
let profileData = null;
|
||||||
const loadProfile = () => {
|
const loadProfile = () => {
|
||||||
{
|
|
||||||
const lists = document.querySelectorAll(".profile-list");
|
const lists = document.querySelectorAll(".profile-list");
|
||||||
|
const checkLists = () => {
|
||||||
for (const list of lists) {
|
for (const list of lists) {
|
||||||
if (list.querySelectorAll(".profile-entry").length === 0) {
|
let found = false;
|
||||||
list.parentElement.removeChild(list);
|
const entries = list.querySelectorAll(".profile-entry");
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (window.getComputedStyle(entry).getPropertyValue('display') !== 'none') {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
list.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
list.style.display = 'block';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
checkLists();
|
||||||
|
|
||||||
{
|
{
|
||||||
const validImageFile = (type) => {
|
const validImageFile = (type) => {
|
||||||
|
@ -32,7 +43,6 @@ const loadProfile = () => {
|
||||||
if (usernameField.value !== '') {
|
if (usernameField.value !== '') {
|
||||||
fetch(`/api/user/${profileId}/username`, {
|
fetch(`/api/user/${profileId}/username`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
|
@ -89,7 +99,6 @@ const loadProfile = () => {
|
||||||
|
|
||||||
fetch(`/api/user/${profileId}/image`, {
|
fetch(`/api/user/${profileId}/image`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
|
||||||
body: data
|
body: data
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
if (upload.classList.contains("active")) {
|
if (upload.classList.contains("active")) {
|
||||||
|
@ -141,6 +150,7 @@ const loadProfile = () => {
|
||||||
for (const platform of platforms) {
|
for (const platform of platforms) {
|
||||||
platform.classList.toggle("editing");
|
platform.classList.toggle("editing");
|
||||||
}
|
}
|
||||||
|
checkLists();
|
||||||
};
|
};
|
||||||
editPlatformsButton.addEventListener("click", togglePlatformEdit);
|
editPlatformsButton.addEventListener("click", togglePlatformEdit);
|
||||||
savePlatformsButton.addEventListener("click", togglePlatformEdit);
|
savePlatformsButton.addEventListener("click", togglePlatformEdit);
|
||||||
|
@ -156,7 +166,6 @@ const loadProfile = () => {
|
||||||
steamButtons[1].addEventListener("click", (clickEvent) => {
|
steamButtons[1].addEventListener("click", (clickEvent) => {
|
||||||
fetch(`/api/user/${profileId}/platforms/remove`, {
|
fetch(`/api/user/${profileId}/platforms/remove`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
|
@ -213,11 +222,11 @@ const expandTemplates = async () => {
|
||||||
template.apply("profile-platforms-list").promise(profileData.then(data =>
|
template.apply("profile-platforms-list").promise(profileData.then(data =>
|
||||||
data.platforms.map(platform => ({
|
data.platforms.map(platform => ({
|
||||||
platform_id: platform.id,
|
platform_id: platform.id,
|
||||||
img: `<img class="profile-entry-icon" src="/api/platform/image/${platform.id}" alt="Steam Logo" />`,
|
img: `<img class="profile-entry-icon" src="/api/platform/${platform.id}/image" alt="Steam Logo" />`,
|
||||||
name: platform.name,
|
name: platform.name,
|
||||||
connected: platform.connected ? "connected" : "",
|
connected: platform.connected ? "connected" : "",
|
||||||
add:
|
add:
|
||||||
(platform.id === 0 ? `<img id="add-steam" class="platform-add" src="https://community.cloudflare.steamstatic.com/public/images/signinthroughsteam/sits_01.png" alt="Add" />` :
|
(platform.id === 0 ? `<img id="add-steam" class="platform-add" src="https://steamcdn-a.akamaihd.net/steamcommunity/public/images/steamworks_docs/english/sits_small.png" alt="Add" />` :
|
||||||
(platform.id === 1 ? `<p class="platform-unsupported">Coming soon...</p>` :
|
(platform.id === 1 ? `<p class="platform-unsupported">Coming soon...</p>` :
|
||||||
(platform.id === 2 ? `<p class="platform-unsupported">Coming soon...</p>` :
|
(platform.id === 2 ? `<p class="platform-unsupported">Coming soon...</p>` :
|
||||||
"")))
|
"")))
|
||||||
|
@ -228,6 +237,7 @@ const expandTemplates = async () => {
|
||||||
window.addEventListener("load", async (loadEvent) => {
|
window.addEventListener("load", async (loadEvent) => {
|
||||||
await loadCommon();
|
await loadCommon();
|
||||||
|
|
||||||
|
var importing = document.querySelector("#importing");
|
||||||
if (!/\d+/.test(profileId)) {
|
if (!/\d+/.test(profileId)) {
|
||||||
isReturn = true;
|
isReturn = true;
|
||||||
const platform = profileId;
|
const platform = profileId;
|
||||||
|
@ -238,6 +248,9 @@ window.addEventListener("load", async (loadEvent) => {
|
||||||
delete session.lastProfile;
|
delete session.lastProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const importingText = importing.querySelector("#importing-text");
|
||||||
|
importingText.textContent = `Importing from ${platform}...`;
|
||||||
|
importing.style.display = `flex`;
|
||||||
if (platform === 'steam') {
|
if (platform === 'steam') {
|
||||||
const query = new URLSearchParams(window.location.search);
|
const query = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
@ -246,9 +259,8 @@ window.addEventListener("load", async (loadEvent) => {
|
||||||
} else {
|
} else {
|
||||||
// Regex courtesy of https://github.com/liamcurry/passport-steam/blob/master/lib/passport-steam/strategy.js
|
// Regex courtesy of https://github.com/liamcurry/passport-steam/blob/master/lib/passport-steam/strategy.js
|
||||||
var steamId = /^https?:\/\/steamcommunity\.com\/openid\/id\/(\d+)$/.exec(query.get('openid.claimed_id'))[1];
|
var steamId = /^https?:\/\/steamcommunity\.com\/openid\/id\/(\d+)$/.exec(query.get('openid.claimed_id'))[1];
|
||||||
await fetch("/api/user/platforms/add", {
|
await fetch(`/api/user/${profileId}/platforms/add`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
|
@ -266,13 +278,15 @@ window.addEventListener("load", async (loadEvent) => {
|
||||||
} else {
|
} else {
|
||||||
// Handle error
|
// Handle error
|
||||||
}
|
}
|
||||||
|
importing.remove();
|
||||||
|
|
||||||
profileData = fetch(`/api/user/${profileId}`, { method: 'GET', mode: 'cors' })
|
profileData = fetch(`/api/user/${profileId}`, { method: 'GET' })
|
||||||
.then(response => response.json());
|
.then(response => response.json());
|
||||||
|
|
||||||
await expandTemplates();
|
await expandTemplates();
|
||||||
await template.expand();
|
await template.expand();
|
||||||
|
|
||||||
|
loadLazyImages();
|
||||||
connectNavbar();
|
connectNavbar();
|
||||||
loadProfile();
|
loadProfile();
|
||||||
});
|
});
|
26
frontend/webpage/static/scripts/search.js
Normal file
26
frontend/webpage/static/scripts/search.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
const expandTemplates = async () => {
|
||||||
|
await commonTemplates();
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadFilters = () => {
|
||||||
|
const filtersButton = document.querySelector("#filter-dropdown-stack");
|
||||||
|
const filtersSection = document.querySelector("#list-page-filters-flex");
|
||||||
|
|
||||||
|
filtersButton.addEventListener("click", (clickEvent) => {
|
||||||
|
filtersButton.classList.toggle("active");
|
||||||
|
filtersSection.classList.toggle("active");
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterCheckboxes = document.querySelectorAll(".list-page-filter-checkbox");
|
||||||
|
for (const checkbox of filterCheckboxes) {
|
||||||
|
checkbox.parentElement.addEventListener("click", (clickEvent) => {
|
||||||
|
checkbox.parentElement.classList.toggle("selected");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadCommonSearch = async () => {
|
||||||
|
await loadCommon();
|
||||||
|
|
||||||
|
await expandTemplates();
|
||||||
|
};
|
108
frontend/webpage/static/scripts/search_achievements.js
Normal file
108
frontend/webpage/static/scripts/search_achievements.js
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
let templateList = null;
|
||||||
|
let templateText = null;
|
||||||
|
const saveTemplate = () => {
|
||||||
|
const templateElement = document.querySelector("#achievement-list-template");
|
||||||
|
templateList = templateElement.parentElement;
|
||||||
|
templateText = templateElement.outerHTML;
|
||||||
|
templateElement.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadAchievementSearch = () => {
|
||||||
|
const loading = document.querySelector("#loading-results");
|
||||||
|
|
||||||
|
|
||||||
|
const searchButton = document.querySelector("#achievement-search-button");
|
||||||
|
const searchField = document.querySelector("#achievement-search-field" );
|
||||||
|
|
||||||
|
const completed = document.querySelector("#completed-filter");
|
||||||
|
const minCompletion = document.querySelector("#min-completion-filter");
|
||||||
|
const maxCompletion = document.querySelector("#max-completion-filter");
|
||||||
|
const minDifficulty = document.querySelector("#min-difficulty-filter");
|
||||||
|
const maxDifficulty = document.querySelector("#max-difficulty-filter");
|
||||||
|
const minQuality = document.querySelector("#min-quality-filter" );
|
||||||
|
const maxQuality = document.querySelector("#max-quality-filter" );
|
||||||
|
|
||||||
|
let canSearch = true;
|
||||||
|
const loadList = async () => {
|
||||||
|
if (canSearch) {
|
||||||
|
canSearch = false;
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
searchTerm: searchField.value,
|
||||||
|
userId: completed.classList.contains('active') ? session.id : null,
|
||||||
|
completed: completed.classList.contains('active'),
|
||||||
|
minCompletion: minCompletion.value === '' ? null : Number(minCompletion.value),
|
||||||
|
maxCompletion: maxCompletion.value === '' ? null : Number(maxCompletion.value),
|
||||||
|
minDifficulty: minDifficulty.value === '' ? null : Number(minDifficulty.value),
|
||||||
|
maxDifficulty: maxDifficulty.value === '' ? null : Number(maxDifficulty.value),
|
||||||
|
minQuality: minQuality.value === '' ? null : Number(minQuality.value ),
|
||||||
|
maxQuality: maxQuality.value === '' ? null : Number(maxQuality.value ),
|
||||||
|
};
|
||||||
|
console.log(body);
|
||||||
|
let successful = true;
|
||||||
|
if (Number.isNaN(body.minCompletion)) { successful = false; minCompletion.style.backgroundColor = 'var(--error)'; } else { minCompletion.style.backgroundColor = 'var(--foreground)'; }
|
||||||
|
if (Number.isNaN(body.maxCompletion)) { successful = false; maxCompletion.style.backgroundColor = 'var(--error)'; } else { maxCompletion.style.backgroundColor = 'var(--foreground)'; }
|
||||||
|
if (Number.isNaN(body.minDifficulty)) { successful = false; minDifficulty.style.backgroundColor = 'var(--error)'; } else { minDifficulty.style.backgroundColor = 'var(--foreground)'; }
|
||||||
|
if (Number.isNaN(body.maxDifficulty)) { successful = false; maxDifficulty.style.backgroundColor = 'var(--error)'; } else { maxDifficulty.style.backgroundColor = 'var(--foreground)'; }
|
||||||
|
if (Number.isNaN(body.minQuality )) { successful = false; minQuality.style.backgroundColor = 'var(--error)'; } else { minQuality.style.backgroundColor = 'var(--foreground)'; }
|
||||||
|
if (Number.isNaN(body.maxQuality )) { successful = false; maxQuality.style.backgroundColor = 'var(--error)'; } else { maxQuality.style.backgroundColor = 'var(--foreground)'; }
|
||||||
|
|
||||||
|
if (!successful) {
|
||||||
|
canSearch = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const entry of templateList.querySelectorAll(".list-page-entry")) {
|
||||||
|
entry.remove();
|
||||||
|
}
|
||||||
|
templateList.innerHTML += templateText;
|
||||||
|
loading.style.display = 'block';
|
||||||
|
|
||||||
|
const data = fetch("/api/achievements", {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
|
||||||
|
template.clear();
|
||||||
|
template.apply('achievements-page-list').promise(data.then(data => data.map(item => ({
|
||||||
|
achievement_id: item.ID,
|
||||||
|
achievement_name: item.name,
|
||||||
|
game_name: item.game,
|
||||||
|
completion: item.completion == null ? 'N/A' : item.completion + '%',
|
||||||
|
difficulty: item.difficulty == null ? 'N/A' : item.difficulty + ' / 10',
|
||||||
|
quality: item.quality == null ? 'N/A' : item.quality + ' / 10'
|
||||||
|
}))));
|
||||||
|
await template.expand();
|
||||||
|
data.then(data => {
|
||||||
|
loading.style.display = 'none';
|
||||||
|
canSearch = true;
|
||||||
|
loadLazyImages();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
searchButton.addEventListener("click", loadList);
|
||||||
|
searchField.addEventListener("keydown", (keyEvent) => {
|
||||||
|
if (keyEvent.key === 'Enter') {
|
||||||
|
loadList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loadList();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("load", async (loadEvent) => {
|
||||||
|
await loadCommonSearch();
|
||||||
|
|
||||||
|
saveTemplate();
|
||||||
|
await template.expand();
|
||||||
|
|
||||||
|
connectNavbar();
|
||||||
|
loadFilters();
|
||||||
|
|
||||||
|
await loadAchievementSearch();
|
||||||
|
});
|
|
@ -136,6 +136,10 @@ var template = template || {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template.clear = () => {
|
||||||
|
templateEntryMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
const parseType = (type) => {
|
const parseType = (type) => {
|
||||||
let result = type.match(/^\s*(\w+)\s*(?:<(.*)>)?\s*$/);
|
let result = type.match(/^\s*(\w+)\s*(?:<(.*)>)?\s*$/);
|
||||||
let id = result[1];
|
let id = result[1];
|
||||||
|
|
|
@ -70,6 +70,15 @@ html, body {
|
||||||
background-color: var(--accent-value3);
|
background-color: var(--accent-value3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes load {
|
||||||
|
from { transform: rotateZ(0deg ); }
|
||||||
|
to { transform: rotateZ(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.ap-loading {
|
||||||
|
animation: 1.5s cubic-bezier(0.4, 0.15, 0.6, 0.85) 0s infinite running load;
|
||||||
|
}
|
||||||
|
|
||||||
.ap-button {
|
.ap-button {
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
background-color: var(--accent-value2);
|
background-color: var(--accent-value2);
|
||||||
|
@ -226,201 +235,3 @@ html, body {
|
||||||
|
|
||||||
background-color: var(--accent-value3);
|
background-color: var(--accent-value3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-page-search {
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-search > label,
|
|
||||||
.list-page-search > input {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 12px 20px;
|
|
||||||
|
|
||||||
color: var(--foreground);
|
|
||||||
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-search > label {
|
|
||||||
background-color: var(--accent-value2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-search > label:hover {
|
|
||||||
background-color: var(--accent-value3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-search > label:active {
|
|
||||||
background-color: var(--accent-value1);
|
|
||||||
|
|
||||||
transition-property: background-color;
|
|
||||||
transition-duration: 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-search > input {
|
|
||||||
background-color: var(--distinction);
|
|
||||||
|
|
||||||
border: 0;
|
|
||||||
|
|
||||||
flex-grow: 1;
|
|
||||||
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
transition-property: background-color, color;
|
|
||||||
transition-duration: 0.075s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-search > input:focus {
|
|
||||||
background-color: var(--foreground);
|
|
||||||
|
|
||||||
color: var(--background);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-partitions {
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: max-content;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-filter-partition {
|
|
||||||
width: 20%;
|
|
||||||
max-width: 640px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-filter-chunk {
|
|
||||||
background-color: var(--distinction);
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-filter {
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: max-content;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-filter-checkbox {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
|
|
||||||
background-color: var(--foreground);
|
|
||||||
|
|
||||||
border: 3px solid var(--foreground);
|
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
transition-property: background-color, border-color;
|
|
||||||
transition-duration: 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-filter:hover > .list-page-filter-checkbox {
|
|
||||||
background-color: var(--foreground);
|
|
||||||
border-color: var(--selected-accent1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-filter.selected > .list-page-filter-checkbox {
|
|
||||||
background-color: var(--selected-accent1);
|
|
||||||
border-color: var(--selected-accent1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-filter.selected:hover > .list-page-filter-checkbox {
|
|
||||||
background-color: var(--selected-accent0);
|
|
||||||
border-color: var(--selected-accent1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-filter-name {
|
|
||||||
margin: 0;
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
color: var(--foreground);
|
|
||||||
|
|
||||||
font-size: 24px;
|
|
||||||
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-list-partition {
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-list {
|
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-header {
|
|
||||||
width: 100%;
|
|
||||||
height: 64px;
|
|
||||||
|
|
||||||
background-color: var(--accent-value2);
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
color: var(--foreground);
|
|
||||||
font-size: 24px;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-entry {
|
|
||||||
width: 100%;
|
|
||||||
height: 64px;
|
|
||||||
|
|
||||||
background-color: var(--distinction);
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
color: var(--foreground);
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-entry-icon {
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-entry-text {
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 12px;
|
|
||||||
height: 64px;
|
|
||||||
line-height: 64px;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
border-top: 1px solid var(--background);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-header > .list-page-entry-text {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
#index-page {
|
|
||||||
max-width: 1600px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#list-page-search-filters {
|
|
||||||
width: 100%;
|
|
||||||
height: max-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
#list-page-search-dropdown {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#search-wrapper {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#list-page-search-pair {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filter-dropdown-wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 84px;
|
|
||||||
width: 84px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filter-dropdown-stack {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filter-dropdown-stack.active {
|
|
||||||
transform: rotateZ(-90deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#filter-dropdown-button {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filter-dropdown-stack:hover > #filter-dropdown-button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filter-dropdown-button-hover {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filter-dropdown-stack:hover > #filter-dropdown-button-hover {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#list-page-filters-flex {
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: max-content;
|
|
||||||
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
#list-page-filters-flex.active {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-filter-section {
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
flex-basis: 0;
|
|
||||||
flex-grow: 1;
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
#list-page-filters-background {
|
|
||||||
background-color: var(--distinction);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-entry-text.achievement-name {
|
|
||||||
flex-grow: 3;
|
|
||||||
flex-basis: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-entry-text.achievement-description {
|
|
||||||
flex-grow: 6;
|
|
||||||
flex-basis: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-entry-text.achievement-stages {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-basis: 0;
|
|
||||||
}
|
|
|
@ -2,8 +2,6 @@
|
||||||
--form-spacing: 48px;
|
--form-spacing: 48px;
|
||||||
|
|
||||||
--element-spacing: 12px;
|
--element-spacing: 12px;
|
||||||
|
|
||||||
--error: #F95959;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#login-page {
|
#login-page {
|
||||||
|
|
|
@ -2,6 +2,26 @@
|
||||||
max-width: 1600px;
|
max-width: 1600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#importing {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#importing-text {
|
||||||
|
margin: 0;
|
||||||
|
height: 96px;
|
||||||
|
font-size: 64px;
|
||||||
|
line-height: 96px;
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
#importing-loading {
|
||||||
|
height: 64px;
|
||||||
|
width: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
.profile-list {
|
.profile-list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
|
@ -150,7 +170,7 @@
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
|
||||||
background-color: var(--background);
|
background-color: var(--background-dark);
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
@ -178,7 +198,7 @@
|
||||||
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
background-color: var(--background);
|
background-color: var(--background-dark);
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
|
|
330
frontend/webpage/static/styles/search.css
Normal file
330
frontend/webpage/static/styles/search.css
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
#list-page-search-filters {
|
||||||
|
width: 100%;
|
||||||
|
height: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
#list-page-search-dropdown {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#list-page-search-pair {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter-dropdown-wrapper {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 84px;
|
||||||
|
width: 84px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter-dropdown-stack {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter-dropdown-stack.active {
|
||||||
|
transform: rotateZ(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter-dropdown-button {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter-dropdown-stack:hover > #filter-dropdown-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter-dropdown-button-hover {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter-dropdown-stack:hover > #filter-dropdown-button-hover {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#list-page-filters-flex {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: max-content;
|
||||||
|
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
#list-page-filters-flex.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter-section {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
flex-basis: max-content;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter-partition {
|
||||||
|
width: 20%;
|
||||||
|
max-width: 640px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter-chunk {
|
||||||
|
background-color: var(--distinction);
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: max-content;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter-checkbox {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
|
background-color: var(--foreground);
|
||||||
|
|
||||||
|
border: 3px solid var(--foreground);
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
transition-property: background-color, border-color;
|
||||||
|
transition-duration: 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter:hover > .list-page-filter-checkbox {
|
||||||
|
background-color: var(--foreground);
|
||||||
|
border-color: var(--selected-accent1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter.selected > .list-page-filter-checkbox {
|
||||||
|
background-color: var(--selected-accent1);
|
||||||
|
border-color: var(--selected-accent1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter.selected:hover > .list-page-filter-checkbox {
|
||||||
|
background-color: var(--selected-accent0);
|
||||||
|
border-color: var(--selected-accent1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter-name,
|
||||||
|
.list-page-filter-label {
|
||||||
|
margin: 0;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
color: var(--foreground);
|
||||||
|
|
||||||
|
font-size: 24px;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter-label {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-filter-param {
|
||||||
|
padding: 4px;
|
||||||
|
width: 25%;
|
||||||
|
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--background);
|
||||||
|
background-color: var(--foreground);
|
||||||
|
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#list-page-filters-background {
|
||||||
|
background-color: var(--distinction);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-entry-text {
|
||||||
|
flex-basis: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page.search {
|
||||||
|
max-width: 1720px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-search {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-search > label,
|
||||||
|
.list-page-search > input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 12px 20px;
|
||||||
|
|
||||||
|
color: var(--foreground);
|
||||||
|
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-search > label {
|
||||||
|
background-color: var(--accent-value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-search > label:hover {
|
||||||
|
background-color: var(--accent-value3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-search > label:active {
|
||||||
|
background-color: var(--accent-value1);
|
||||||
|
|
||||||
|
transition-property: background-color;
|
||||||
|
transition-duration: 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-search > input {
|
||||||
|
background-color: var(--distinction);
|
||||||
|
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
transition-property: background-color, color;
|
||||||
|
transition-duration: 0.075s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-search > input:focus {
|
||||||
|
background-color: var(--foreground);
|
||||||
|
|
||||||
|
color: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-partitions {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: max-content;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.list-page-list-partition {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-list {
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-header {
|
||||||
|
width: 100%;
|
||||||
|
height: 64px;
|
||||||
|
|
||||||
|
background-color: var(--accent-value2);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
color: var(--foreground);
|
||||||
|
font-size: 24px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-entry {
|
||||||
|
width: 100%;
|
||||||
|
height: 64px;
|
||||||
|
|
||||||
|
background-color: var(--distinction);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
color: var(--foreground);
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-entry-icon {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-entry-text {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 12px;
|
||||||
|
height: 64px;
|
||||||
|
line-height: 64px;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-entry > .list-page-entry-text {
|
||||||
|
border-top: 1px solid var(--background);
|
||||||
|
border-left: 1px solid var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-header > .list-page-entry-text {
|
||||||
|
border-left: 1px solid var(--accent-value0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading-results {
|
||||||
|
margin: 16px 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 64px;
|
||||||
|
object-fit: contain;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
}
|
5
frontend/webpage/static/styles/search_achievements.css
Normal file
5
frontend/webpage/static/styles/search_achievements.css
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.list-page-entry-text.achievement-game-name { flex-grow: 1.75; }
|
||||||
|
.list-page-entry-text.achievement-name { flex-grow: 2; }
|
||||||
|
.list-page-entry-text.achievement-completion { flex-grow: 1; }
|
||||||
|
.list-page-entry-text.achievement-quality { flex-grow: 1; }
|
||||||
|
.list-page-entry-text.achievement-difficulty { flex-grow: 1; }
|
|
@ -18,4 +18,6 @@
|
||||||
|
|
||||||
--selected-accent0: #2266CC;
|
--selected-accent0: #2266CC;
|
||||||
--selected-accent1: #3388FF;
|
--selected-accent1: #3388FF;
|
||||||
|
|
||||||
|
--error: #F95959;
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
639
sql/DataProcs.sql
Normal file
639
sql/DataProcs.sql
Normal file
|
@ -0,0 +1,639 @@
|
||||||
|
---------------------------------------
|
||||||
|
-- GET USER NAME AND STATS PROCEDURE --
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetUserNameAndStats(
|
||||||
|
@userId INT,
|
||||||
|
@username VARCHAR(32) OUTPUT,
|
||||||
|
@completed INT OUTPUT,
|
||||||
|
@average INT OUTPUT,
|
||||||
|
@perfect INT OUTPUT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
|
||||||
|
SELECT @username = Username
|
||||||
|
FROM [User]
|
||||||
|
WHERE ID = @userId
|
||||||
|
|
||||||
|
IF @username IS NULL
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
|
||||||
|
SELECT @completed = SUM(Completed)
|
||||||
|
FROM GameCompletionByUser
|
||||||
|
WHERE UserID = @userId
|
||||||
|
|
||||||
|
SELECT @average = AVG((Completed * 100) / Total)
|
||||||
|
FROM GameCompletionByUser
|
||||||
|
WHERE UserID = @userId
|
||||||
|
|
||||||
|
SELECT @perfect = COUNT(GameID)
|
||||||
|
FROM GameCompletionByUser
|
||||||
|
WHERE UserID = @userId AND Completed = Total
|
||||||
|
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
SELECT * FROM [User]
|
||||||
|
|
||||||
|
----------------------------------
|
||||||
|
-- GET USER PLATFORMS PROCEDURE --
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetUserPlatforms(
|
||||||
|
@userId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
SELECT [Platform].ID, [PlatformName], (CASE WHEN UserID IS NOT NULL THEN 1 ELSE 0 END) AS Connected
|
||||||
|
FROM [Platform]
|
||||||
|
LEFT JOIN IsOn ON IsOn.PlatformID = [Platform].ID AND UserID = @userId
|
||||||
|
ORDER BY [Platform].ID
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
-- GET USER RATINGS PROCEDURE --
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetUserRatings(
|
||||||
|
@userId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
SELECT Game.Name AS GameName, Achievement.Name AS AchievementName, Quality, Difficulty, Rating.[Description]
|
||||||
|
FROM Rating
|
||||||
|
JOIN Achievement ON Achievement.ID = Rating.AchievementID
|
||||||
|
JOIN Game ON Game.ID = Achievement.GameID
|
||||||
|
WHERE UserID = @userId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
------------------------------
|
||||||
|
-- GET USER IMAGE PROCEDURE --
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetUserImage(
|
||||||
|
@userId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
SELECT ProfileImage FROM [User] WHERE ID = @userId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
------------------
|
||||||
|
-- SET USERNAME --
|
||||||
|
------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE SetUsername(
|
||||||
|
@userId INT,
|
||||||
|
@username VARCHAR(32)
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
UPDATE [User] SET Username = @username WHERE ID = @userId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
------------------------------
|
||||||
|
-- SET USER IMAGE PROCEDURE --
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE SetUserImage(
|
||||||
|
@userId INT,
|
||||||
|
@type ImageType,
|
||||||
|
@oldType ImageType OUTPUT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
SELECT @oldType = ProfileImage FROM [User] WHERE ID = @userId
|
||||||
|
UPDATE [User] SET ProfileImage = @type WHERE ID = @userId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
--------------------------
|
||||||
|
-- ADD USER TO PLATFORM --
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE AddUserToPlatform(
|
||||||
|
@userId INT,
|
||||||
|
@platformId INT,
|
||||||
|
@platformUserID VARCHAR(32)
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF NOT @platformId IN (SELECT ID FROM [Platform])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No platform with the specified ID was found'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
IF EXISTS (SELECT * FROM IsOn WHERE UserID = @userId AND PlatformID = @platformId)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'User already exists on specified platform'
|
||||||
|
RETURN 3
|
||||||
|
END
|
||||||
|
INSERT INTO IsOn VALUES (@userId, @platformId, @platformUserId)
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
-------------------------------
|
||||||
|
-- REMOVE USER FROM PLATFORM --
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE RemoveUserFromPlatform(
|
||||||
|
@userId INT,
|
||||||
|
@platformId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF NOT @platformId IN (SELECT ID FROM [Platform])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No platform with the specified ID was found'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
IF NOT EXISTS (SELECT UserID FROM IsOn WHERE UserID = @userId AND PlatformID = @platformId)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'User does not exist on specified platform'
|
||||||
|
RETURN 3
|
||||||
|
END
|
||||||
|
DELETE FROM IsOn WHERE UserID = @userId AND PlatformID = @platformId
|
||||||
|
DELETE FROM Progress WHERE UserID = @userId AND PlatformID = @platformId
|
||||||
|
DELETE FROM Owns WHERE UserID = @userId AND PlatformID = @platformId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
------------------
|
||||||
|
-- ADD PLATFORM --
|
||||||
|
------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE AddPlatform(
|
||||||
|
@name VARCHAR(32),
|
||||||
|
@platformId INT OUTPUT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF @name IS NULL
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Platform name cannot be null'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
INSERT INTO [Platform] VALUES (@name)
|
||||||
|
SET @platformId = @@IDENTITY
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
-- REMOVE PLATFORM --
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE RemovePlatform(
|
||||||
|
@platformId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @platformId IN (SELECT ID FROM [Platform])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No platform with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF @platformId IN (SELECT PlatformID FROM ExistsOn)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'All games must be removed from the specified platform before it can be removed'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
DELETE FROM [Platform] WHERE ID = @platformId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
-- GET PLATFORMS --
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetPlatforms
|
||||||
|
AS
|
||||||
|
SELECT ID, PlatformName FROM [Platform]
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
-----------------------
|
||||||
|
-- GET PLATFORM NAME --
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetPlatformName(
|
||||||
|
@platformId INT,
|
||||||
|
@name VARCHAR(32) OUTPUT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
SELECT @name = PlatformName FROM [Platform] WHERE ID = @platformId
|
||||||
|
IF @name IS NULL
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No platform with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
-----------------------
|
||||||
|
-- GET PLATFORM ICON --
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetPlatformIcon(
|
||||||
|
@platformId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @platformId IN (SELECT ID FROM [Platform])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No platform with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
SELECT Icon FROM [Platform] WHERE ID = @platformId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
--------------
|
||||||
|
-- ADD GAME --
|
||||||
|
--------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE AddGame(
|
||||||
|
@name VARCHAR(32),
|
||||||
|
@image ImageType,
|
||||||
|
@gameId INT OUTPUT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF @name IS NULL
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Game name cannot be null'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF @name IN (SELECT [Name] FROM Game)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Game with specified name already exists'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
INSERT INTO Game VALUES (@name, @image)
|
||||||
|
SET @gameId = @@IDENTITY
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
-- ADD IF NOT GAME --
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE AddIfNotGame(
|
||||||
|
@name VARCHAR(32),
|
||||||
|
@image VARCHAR(11),
|
||||||
|
@gameId INT OUTPUT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF @name IS NULL
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Game name cannot be null'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
-- Ideally game name wouldn't have to be unique, but I don't know of another way to sync games across platforms when they share no IDing system
|
||||||
|
IF NOT @name IN (SELECT [Name] FROM Game)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO Game VALUES (@name, @image)
|
||||||
|
END
|
||||||
|
SELECT @gameId = ID FROM Game WHERE [Name] = @name
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
-- REMOVE GAME --
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE RemoveGame(
|
||||||
|
@gameId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @gameId IN (SELECT ID FROM Game)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No game with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
DELETE FROM Game WHERE ID = @gameId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
-- GET GAME ICON --
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetGameIcon(
|
||||||
|
@gameId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @gameId IN (SELECT ID FROM [Game])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No game with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
SELECT Icon FROM [Game] WHERE ID = @gameId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
----------------------
|
||||||
|
-- ADD GAME TO USER --
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE AddGameToUser(
|
||||||
|
@gameId INT,
|
||||||
|
@userId INT,
|
||||||
|
@platformId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @gameId IN (SELECT ID FROM Game)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No game with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
IF NOT @platformId IN (SELECT ID FROM [Platform])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No platform with the specified ID was found'
|
||||||
|
RETURN 3
|
||||||
|
END
|
||||||
|
IF NOT EXISTS (SELECT * FROM IsOn WHERE UserID = @userId AND PlatformID = @platformId)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'User is not on specified platform'
|
||||||
|
RETURN 4
|
||||||
|
END
|
||||||
|
IF EXISTS (SELECT * FROM Owns WHERE GameID = @gameId AND UserID = @userId AND PlatformID = @platformId)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Game is already owned by specified user on specified platform'
|
||||||
|
RETURN 5
|
||||||
|
END
|
||||||
|
INSERT INTO Owns VALUES (@userId, @gameId, @platformId)
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
-- REMOVE GAME FROM USER --
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE RemoveGameFromUser(
|
||||||
|
@gameId INT,
|
||||||
|
@userId INT,
|
||||||
|
@platformId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @gameId IN (SELECT ID FROM Game)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No game with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
IF NOT @platformId IN (SELECT ID FROM [Platform])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No platform with the specified ID was found'
|
||||||
|
RETURN 3
|
||||||
|
END
|
||||||
|
IF NOT EXISTS (SELECT * FROM Owns WHERE GameID = @gameId AND UserID = @userId AND PlatformID = @platformId)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Game is not owned by specified user on specified platform'
|
||||||
|
RETURN 4
|
||||||
|
END
|
||||||
|
DELETE FROM Owns WHERE UserID = @userId AND GameID = @gameId AND PlatformID = @platformId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
--------------------------
|
||||||
|
-- ADD GAME TO PLATFORM --
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE AddGameToPlatform(
|
||||||
|
@gameId INT,
|
||||||
|
@platformId INT,
|
||||||
|
@platformGameId VARCHAR(32)
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @gameId IN (SELECT ID FROM Game)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No game with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF NOT @platformId IN (SELECT ID FROM [Platform])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No platform with the specified ID was found'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
IF EXISTS (SELECT * FROM ExistsOn WHERE GameID = @gameId AND PlatformID = @platformId)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Game already exists on specified platform'
|
||||||
|
RETURN 3
|
||||||
|
END
|
||||||
|
INSERT INTO ExistsOn VALUES (@gameId, @platformId, @platformGameId)
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
-------------------------------
|
||||||
|
-- REMOVE GAME FROM PLATFORM --
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE RemoveGameFromPlatform(
|
||||||
|
@gameId INT,
|
||||||
|
@platformId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @gameId IN (SELECT ID FROM Game)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No game with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF NOT @platformId IN (SELECT ID FROM [Platform])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No platform with the specified ID was found'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
IF NOT EXISTS (SELECT * FROM ExistsOn WHERE GameID = @gameId AND PlatformID = @platformId)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Game does not exist on specified platform'
|
||||||
|
RETURN 3
|
||||||
|
END
|
||||||
|
DELETE FROM ExistsOn WHERE GameID = @gameId AND PlatformID = @platformId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
-- ADD ACHIEVEMENT --
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE AddAchievement(
|
||||||
|
@gameId INT,
|
||||||
|
@name VARCHAR(128),
|
||||||
|
@description VARCHAR(512),
|
||||||
|
@stages INT,
|
||||||
|
@image ImageType,
|
||||||
|
@achievementId INT OUTPUT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @gameId IN (SELECT ID FROM Game)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No game with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF @name IS NULL
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Achievement name cannot be null'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
IF @stages IS NULL
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Achievement stages cannot be null'
|
||||||
|
RETURN 3
|
||||||
|
END
|
||||||
|
IF @name IN (SELECT [Name] FROM Achievement WHERE GameID = @gameId)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Achievement with specified name already exists for specified game'
|
||||||
|
RETURN 4
|
||||||
|
END
|
||||||
|
INSERT INTO Achievement VALUES (@gameId, @name, @description, @stages, @image)
|
||||||
|
SET @achievementId = @@IDENTITY
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
----------------------------
|
||||||
|
-- ADD IF NOT ACHIEVEMENT --
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE AddIfNotAchievement(
|
||||||
|
@gameId INT,
|
||||||
|
@name VARCHAR(128),
|
||||||
|
@description VARCHAR(512),
|
||||||
|
@stages INT,
|
||||||
|
@image VARCHAR(11),
|
||||||
|
@achievementId INT OUTPUT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @gameId IN (SELECT ID FROM Game)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No game with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF @name IS NULL
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Achievement name cannot be null'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
IF @stages IS NULL
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Achievement stages cannot be null'
|
||||||
|
RETURN 3
|
||||||
|
END
|
||||||
|
IF NOT @name IN (SELECT [Name] FROM Achievement WHERE GameID = @gameId)
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO Achievement VALUES (@gameId, @name, @description, @stages, @image)
|
||||||
|
END
|
||||||
|
SELECT @achievementId = ID FROM Achievement WHERE [Name] = @name AND GameID = @gameId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
------------------------
|
||||||
|
-- REMOVE ACHIEVEMENT --
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE RemoveAchievement(
|
||||||
|
@achievementId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @achievementId IN (SELECT ID FROM Achievement)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No achievement with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
DELETE FROM Achievement WHERE ID = @achievementId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
--------------------------
|
||||||
|
-- GET ACHIEVEMENT ICON --
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetAchievementIcon(
|
||||||
|
@achievementId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @achievementId IN (SELECT ID FROM Achievement)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No achievement with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
SELECT Icon FROM Achievement WHERE ID = @achievementId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
---------------------------------------
|
||||||
|
-- SET ACHIEVEMENT PROGRESS FOR USER --
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE SetAchievementProgressForUser(
|
||||||
|
@userId INT,
|
||||||
|
@platformId INT,
|
||||||
|
@achievementId INT,
|
||||||
|
@progress INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF NOT @platformId IN (SELECT ID FROM [Platform])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No platform with the specified ID was found'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
IF NOT @achievementId IN (SELECT ID FROM Achievement)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No achievement with the specified ID was found'
|
||||||
|
RETURN 3
|
||||||
|
END
|
||||||
|
IF EXISTS (SELECT * FROM Progress WHERE AchievementID = @achievementId AND UserID = @userId AND PlatformID = @platformId)
|
||||||
|
BEGIN
|
||||||
|
UPDATE Progress SET Progress = @progress WHERE AchievementID = @achievementId AND UserID = @userId AND PlatformID = @platformId
|
||||||
|
END
|
||||||
|
ELSE
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO Progress VALUES (@userId, @platformId, @achievementId, @progress)
|
||||||
|
END
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
60
sql/SearchProcs.sql
Normal file
60
sql/SearchProcs.sql
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
-----------------------
|
||||||
|
-- SEARCH ACHIEVEMENTS --
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE SearchAchievements(
|
||||||
|
@searchTerm VARCHAR(32),
|
||||||
|
@userId INT,
|
||||||
|
@completed BIT,
|
||||||
|
@minCompletion FLOAT,
|
||||||
|
@maxCompletion FLOAT,
|
||||||
|
@minDifficulty FLOAT,
|
||||||
|
@maxDifficulty FLOAT,
|
||||||
|
@minQuality FLOAT,
|
||||||
|
@maxQuality FLOAT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF @userId IS NULL AND @completed = 1
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Cannot search for completed achievements with no user specified'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
|
||||||
|
IF @searchTerm IS NULL OR @searchTerm = ''
|
||||||
|
SET @searchTerm = '%'
|
||||||
|
ELSE
|
||||||
|
SET @searchTerm = '%' + @searchTerm + '%'
|
||||||
|
PRINT @searchTerm
|
||||||
|
|
||||||
|
IF NOT @userId IS NULL
|
||||||
|
SELECT TOP 100 Game.[Name] AS Game, Achievement.[Name], Completion, Difficulty, Quality
|
||||||
|
FROM Achievement
|
||||||
|
JOIN MaxProgress ON AchievementID = Achievement.ID AND UserID = @userId
|
||||||
|
JOIN Game ON Game.ID = GameID
|
||||||
|
JOIN AchievementCompletion AC ON AC.ID = Achievement.ID
|
||||||
|
JOIN AchievementRatings AR ON AR.ID = Achievement.ID
|
||||||
|
WHERE (Game.[Name] LIKE @searchTerm OR Achievement.[Name] LIKE @searchTerm)
|
||||||
|
AND (@completed <> 1 OR Progress = Stages )
|
||||||
|
AND (@minCompletion IS NULL OR @minCompletion <= Completion)
|
||||||
|
AND (@maxCompletion IS NULL OR @maxCompletion >= Completion)
|
||||||
|
AND (@minDifficulty IS NULL OR @minDifficulty <= Difficulty)
|
||||||
|
AND (@maxDifficulty IS NULL OR @maxDifficulty >= Difficulty)
|
||||||
|
AND (@minQuality IS NULL OR @minQuality <= Quality )
|
||||||
|
AND (@maxQuality IS NULL OR @maxQuality >= Quality )
|
||||||
|
ELSE
|
||||||
|
SELECT TOP 100 Achievement.ID, Game.[Name] AS Game, Achievement.[Name], Completion, Quality, Difficulty
|
||||||
|
FROM Achievement
|
||||||
|
JOIN Game ON Game.ID = GameID
|
||||||
|
JOIN AchievementCompletion AC ON AC.ID = Achievement.ID
|
||||||
|
JOIN AchievementRatings AR ON AR.ID = Achievement.ID
|
||||||
|
WHERE (Game.[Name] LIKE @searchTerm OR Achievement.[Name] LIKE @searchTerm)
|
||||||
|
AND (@minCompletion IS NULL OR @minCompletion <= Completion)
|
||||||
|
AND (@maxCompletion IS NULL OR @maxCompletion >= Completion)
|
||||||
|
AND (@minDifficulty IS NULL OR @minDifficulty <= Difficulty)
|
||||||
|
AND (@maxDifficulty IS NULL OR @maxDifficulty >= Difficulty)
|
||||||
|
AND (@minQuality IS NULL OR @minQuality <= Quality )
|
||||||
|
AND (@maxQuality IS NULL OR @maxQuality >= Quality )
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
EXEC SearchAchievements '', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
|
@ -27,6 +27,9 @@
|
||||||
|
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
--CREATE TYPE ImageType FROM VARCHAR(4) NULL
|
||||||
|
--GO
|
||||||
|
|
||||||
CREATE TABLE [User] (
|
CREATE TABLE [User] (
|
||||||
ID INT IDENTITY(0, 1) NOT NULL,
|
ID INT IDENTITY(0, 1) NOT NULL,
|
||||||
Email VARCHAR(254) NOT NULL,
|
Email VARCHAR(254) NOT NULL,
|
||||||
|
@ -36,7 +39,9 @@ CREATE TABLE [User] (
|
||||||
Hue INT NOT NULL
|
Hue INT NOT NULL
|
||||||
CONSTRAINT HueDefault DEFAULT 0
|
CONSTRAINT HueDefault DEFAULT 0
|
||||||
CONSTRAINT HueConstraint CHECK (0 <= Hue AND Hue <= 360),
|
CONSTRAINT HueConstraint CHECK (0 <= Hue AND Hue <= 360),
|
||||||
PFP VARCHAR(11) NULL,
|
ProfileImage ImageType,
|
||||||
|
[Admin] BIT NOT NULL
|
||||||
|
CONSTRAINT AdmivDefault DEFAULT 0,
|
||||||
Verified BIT NOT NULL
|
Verified BIT NOT NULL
|
||||||
CONSTRAINT VerifiedDefault DEFAULT 0
|
CONSTRAINT VerifiedDefault DEFAULT 0
|
||||||
PRIMARY KEY(ID)
|
PRIMARY KEY(ID)
|
||||||
|
@ -44,14 +49,15 @@ CREATE TABLE [User] (
|
||||||
|
|
||||||
CREATE TABLE [Platform] (
|
CREATE TABLE [Platform] (
|
||||||
ID INT IDENTITY(0, 1) NOT NULL,
|
ID INT IDENTITY(0, 1) NOT NULL,
|
||||||
PlatformName VARCHAR(32) NOT NULL
|
PlatformName VARCHAR(32) NOT NULL,
|
||||||
|
Icon ImageType
|
||||||
PRIMARY KEY(ID)
|
PRIMARY KEY(ID)
|
||||||
)
|
)
|
||||||
|
|
||||||
CREATE TABLE [Game] (
|
CREATE TABLE [Game] (
|
||||||
ID INT IDENTITY(0, 1) NOT NULL,
|
ID INT IDENTITY(0, 1) NOT NULL,
|
||||||
Name VARCHAR(32) NOT NULL,
|
Name VARCHAR(32) NOT NULL,
|
||||||
Thumbnail VARCHAR(256) NULL
|
Icon ImageType
|
||||||
PRIMARY KEY(ID)
|
PRIMARY KEY(ID)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -61,7 +67,7 @@ CREATE TABLE [Achievement] (
|
||||||
Name VARCHAR(128) NOT NULL,
|
Name VARCHAR(128) NOT NULL,
|
||||||
Description VARCHAR(512) NULL,
|
Description VARCHAR(512) NULL,
|
||||||
Stages INT NOT NULL,
|
Stages INT NOT NULL,
|
||||||
Thumbnail VARCHAR(256) NULL
|
Icon ImageType
|
||||||
PRIMARY KEY(ID)
|
PRIMARY KEY(ID)
|
||||||
FOREIGN KEY(GameID) REFERENCES [Game](ID)
|
FOREIGN KEY(GameID) REFERENCES [Game](ID)
|
||||||
ON UPDATE CASCADE
|
ON UPDATE CASCADE
|
||||||
|
@ -89,7 +95,7 @@ CREATE TABLE [Progress] (
|
||||||
PlatformID INT NOT NULL,
|
PlatformID INT NOT NULL,
|
||||||
AchievementID INT NOT NULL,
|
AchievementID INT NOT NULL,
|
||||||
Progress INT NOT NULL
|
Progress INT NOT NULL
|
||||||
PRIMARY KEY(UserID, AchievementID)
|
PRIMARY KEY(UserID, PlatformID, AchievementID)
|
||||||
FOREIGN KEY(UserID) REFERENCES [User](ID)
|
FOREIGN KEY(UserID) REFERENCES [User](ID)
|
||||||
ON UPDATE CASCADE
|
ON UPDATE CASCADE
|
||||||
ON DELETE CASCADE,
|
ON DELETE CASCADE,
|
||||||
|
@ -117,7 +123,7 @@ CREATE TABLE [IsOn] (
|
||||||
CREATE TABLE [ExistsOn] (
|
CREATE TABLE [ExistsOn] (
|
||||||
GameID INT NOT NULL,
|
GameID INT NOT NULL,
|
||||||
PlatformID INT NOT NULL,
|
PlatformID INT NOT NULL,
|
||||||
PlatformGameID INT NOT NULL
|
PlatformGameID VARCHAR(32) NOT NULL
|
||||||
PRIMARY KEY(GameID, PlatformID)
|
PRIMARY KEY(GameID, PlatformID)
|
||||||
FOREIGN KEY(GameID) REFERENCES [Game](ID)
|
FOREIGN KEY(GameID) REFERENCES [Game](ID)
|
||||||
ON UPDATE CASCADE
|
ON UPDATE CASCADE
|
186
sql/UserData.sql
186
sql/UserData.sql
|
@ -1,186 +0,0 @@
|
||||||
---------------------------------------
|
|
||||||
-- GET USER NAME AND STATS PROCEDURE --
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
CREATE PROCEDURE GetUserNameAndStats(
|
|
||||||
@userId INT,
|
|
||||||
@username VARCHAR(32) OUTPUT,
|
|
||||||
@completed INT OUTPUT,
|
|
||||||
@average INT OUTPUT,
|
|
||||||
@perfect INT OUTPUT
|
|
||||||
)
|
|
||||||
AS
|
|
||||||
BEGIN TRANSACTION
|
|
||||||
|
|
||||||
SELECT @username = Username
|
|
||||||
FROM [User]
|
|
||||||
WHERE ID = @userId
|
|
||||||
|
|
||||||
IF @username IS NULL
|
|
||||||
BEGIN
|
|
||||||
PRINT 'No user found with specified id'
|
|
||||||
ROLLBACK TRANSACTION
|
|
||||||
RETURN 1
|
|
||||||
END
|
|
||||||
|
|
||||||
DECLARE @progress TABLE (GameID INT, Completed INT, Total INT)
|
|
||||||
INSERT INTO @progress
|
|
||||||
SELECT GameID, SUM(CASE WHEN Progress.Progress = Achievement.Stages THEN 1 ELSE 0 END) AS Completed, COUNT(AchievementID) AS Total
|
|
||||||
FROM Achievement
|
|
||||||
JOIN Progress ON
|
|
||||||
Progress.UserID = @userId
|
|
||||||
AND Progress.AchievementID = Achievement.ID
|
|
||||||
GROUP BY GameID
|
|
||||||
COMMIT TRANSACTION
|
|
||||||
|
|
||||||
SELECT @completed = SUM(Completed)
|
|
||||||
FROM @progress
|
|
||||||
|
|
||||||
SELECT @average = AVG((Completed * 100) / Total)
|
|
||||||
FROM @progress
|
|
||||||
|
|
||||||
SELECT @perfect = COUNT(GameID)
|
|
||||||
FROM @progress
|
|
||||||
WHERE Completed = Total
|
|
||||||
|
|
||||||
RETURN 0
|
|
||||||
GO
|
|
||||||
|
|
||||||
----------------------------------
|
|
||||||
-- GET USER PLATFORMS PROCEDURE --
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
CREATE PROCEDURE GetUserPlatforms(
|
|
||||||
@userId INT
|
|
||||||
)
|
|
||||||
AS
|
|
||||||
SELECT [Platform].ID, [PlatformName], (CASE WHEN UserID IS NOT NULL THEN 1 ELSE 0 END) AS Connected
|
|
||||||
FROM [Platform]
|
|
||||||
LEFT JOIN IsOn ON IsOn.PlatformID = [Platform].ID
|
|
||||||
ORDER BY [Platform].ID
|
|
||||||
GO
|
|
||||||
|
|
||||||
--------------------------------
|
|
||||||
-- GET USER RATINGS PROCEDURE --
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
CREATE PROCEDURE GetUserRatings(
|
|
||||||
@userId INT
|
|
||||||
)
|
|
||||||
AS
|
|
||||||
SELECT Game.Name AS GameName, Achievement.Name AS AchievementName, Quality, Difficulty, Rating.[Description]
|
|
||||||
FROM Rating
|
|
||||||
JOIN Achievement ON Achievement.ID = Rating.AchievementID
|
|
||||||
JOIN Game ON Game.ID = Achievement.GameID
|
|
||||||
WHERE UserID = @userId
|
|
||||||
GO
|
|
||||||
|
|
||||||
------------------------------
|
|
||||||
-- GET USER IMAGE PROCEDURE --
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
CREATE PROCEDURE GetUserImage(
|
|
||||||
@userId INT
|
|
||||||
)
|
|
||||||
AS
|
|
||||||
IF NOT EXISTS (SELECT * FROM [User] WHERE ID = @userId)
|
|
||||||
BEGIN
|
|
||||||
PRINT 'No user with specified ID found'
|
|
||||||
RETURN 1
|
|
||||||
END
|
|
||||||
SELECT PFP FROM [User] WHERE ID = @userId
|
|
||||||
RETURN 0
|
|
||||||
GO
|
|
||||||
|
|
||||||
------------------
|
|
||||||
-- SET USERNAME --
|
|
||||||
------------------
|
|
||||||
|
|
||||||
CREATE PROCEDURE SetUsername(
|
|
||||||
@userId INT,
|
|
||||||
@username VARCHAR(32)
|
|
||||||
)
|
|
||||||
AS
|
|
||||||
IF NOT EXISTS (SELECT * FROM [User] WHERE ID = @userId)
|
|
||||||
BEGIN
|
|
||||||
PRINT 'No user with specified ID found'
|
|
||||||
RETURN 1
|
|
||||||
END
|
|
||||||
UPDATE [User] SET Username = @username WHERE ID = @userId
|
|
||||||
RETURN 0
|
|
||||||
GO
|
|
||||||
|
|
||||||
------------------------------
|
|
||||||
-- SET USER IMAGE PROCEDURE --
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
CREATE PROCEDURE SetUserImage(
|
|
||||||
@userId INT,
|
|
||||||
@type VARCHAR(11)
|
|
||||||
)
|
|
||||||
AS
|
|
||||||
IF NOT EXISTS (SELECT * FROM [User] WHERE ID = @userId)
|
|
||||||
BEGIN
|
|
||||||
PRINT 'No user with specified ID found'
|
|
||||||
RETURN 1
|
|
||||||
END
|
|
||||||
UPDATE [User] SET PFP = @type WHERE ID = @userId
|
|
||||||
RETURN 0
|
|
||||||
GO
|
|
||||||
|
|
||||||
---------------------------
|
|
||||||
-- ADD USER TO PROCEDURE --
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
CREATE PROCEDURE AddPlatform(
|
|
||||||
@userId INT,
|
|
||||||
@platformId INT,
|
|
||||||
@platformUserID VARCHAR(32)
|
|
||||||
)
|
|
||||||
AS
|
|
||||||
IF NOT EXISTS (SELECT * FROM [User] WHERE ID = @userId)
|
|
||||||
BEGIN
|
|
||||||
PRINT 'No user with specified ID found'
|
|
||||||
RETURN 1
|
|
||||||
END
|
|
||||||
IF NOT EXISTS (SELECT * FROM [Platform] WHERE ID = @platformId)
|
|
||||||
BEGIN
|
|
||||||
PRINT 'No platform with specified ID found'
|
|
||||||
RETURN 2
|
|
||||||
END
|
|
||||||
IF EXISTS (SELECT * FROM IsOn WHERE UserID = @userId AND PlatformID = @platformId)
|
|
||||||
BEGIN
|
|
||||||
PRINT 'User already exists on platform'
|
|
||||||
RETURN 3
|
|
||||||
END
|
|
||||||
INSERT INTO IsOn VALUES (@userId, @platformId, @platformUserId)
|
|
||||||
RETURN 0
|
|
||||||
GO
|
|
||||||
|
|
||||||
--------------------------------
|
|
||||||
-- REMOVE USER FROM PROCEDURE --
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
CREATE PROCEDURE RemovePlatform(
|
|
||||||
@userId INT,
|
|
||||||
@platformId INT
|
|
||||||
)
|
|
||||||
AS
|
|
||||||
IF NOT EXISTS (SELECT * FROM [User] WHERE ID = @userId)
|
|
||||||
BEGIN
|
|
||||||
PRINT 'No user with specified ID found'
|
|
||||||
RETURN 1
|
|
||||||
END
|
|
||||||
IF NOT EXISTS (SELECT * FROM [Platform] WHERE ID = @platformId)
|
|
||||||
BEGIN
|
|
||||||
PRINT 'No platform with specified ID found'
|
|
||||||
RETURN 2
|
|
||||||
END
|
|
||||||
IF NOT EXISTS (SELECT * FROM IsOn WHERE UserID = @userId AND PlatformID = @platformId)
|
|
||||||
BEGIN
|
|
||||||
PRINT 'User does not exist on platform'
|
|
||||||
RETURN 3
|
|
||||||
END
|
|
||||||
DELETE FROM IsOn WHERE UserID = @userId AND PlatformID = @platformId
|
|
||||||
RETURN 0
|
|
||||||
GO
|
|
35
sql/Views.sql
Normal file
35
sql/Views.sql
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
-- The maximum progress a user has on an achievement across all platforms
|
||||||
|
CREATE VIEW MaxProgress
|
||||||
|
AS
|
||||||
|
SELECT UserID, AchievementID, MAX(Progress) AS Progress
|
||||||
|
FROM Progress
|
||||||
|
GROUP BY UserID, AchievementID
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- List of games and users with the number of completed achievements out of the total achievements the user has completed
|
||||||
|
CREATE VIEW GameCompletionByUser
|
||||||
|
AS
|
||||||
|
SELECT UserID, GameID, SUM(CASE WHEN Progress = Stages THEN 1 ELSE 0 END) AS Completed, COUNT(AchievementID) AS Total
|
||||||
|
FROM Achievement
|
||||||
|
JOIN MaxProgress ON AchievementID = Achievement.ID
|
||||||
|
GROUP BY UserID, GameID
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- List of achievements and the percentage of people who have completed it
|
||||||
|
CREATE VIEW AchievementCompletion
|
||||||
|
AS
|
||||||
|
SELECT Achievement.ID, (CASE WHEN COUNT(UserID) = 0 THEN NULL ELSE (SUM(CASE WHEN Progress = Stages THEN 1 ELSE 0 END) * 100 / COUNT(UserID)) END) AS Completion
|
||||||
|
FROM Achievement
|
||||||
|
LEFT JOIN MaxProgress ON AchievementID = Achievement.ID
|
||||||
|
GROUP BY Achievement.ID
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- List of achievements and their average quality and difficulty ratings filling with null as necessary
|
||||||
|
CREATE VIEW AchievementRatings
|
||||||
|
AS
|
||||||
|
SELECT Achievement.ID, AVG(Quality) AS Quality, AVG(Difficulty) AS Difficulty
|
||||||
|
FROM Achievement
|
||||||
|
LEFT JOIN Rating ON AchievementID = Achievement.ID
|
||||||
|
GROUP BY Achievement.ID
|
||||||
|
GO
|
||||||
|
|
Loading…
Reference in a new issue