Final Product
This commit is contained in:
parent
a8cf583569
commit
a9f44c29af
48 changed files with 3908 additions and 581 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
tmp/*
|
tmp/*
|
||||||
|
sql/CreateBackendUser.sql
|
||||||
|
|
|
@ -55,47 +55,56 @@ public class SteamAPI extends PlatformAPI {
|
||||||
.queryParam("steamid", userId);
|
.queryParam("steamid", userId);
|
||||||
|
|
||||||
var games = new ArrayList<APIResponse.Game>();
|
var games = new ArrayList<APIResponse.Game>();
|
||||||
var ownedResponse = rest.exchange(ownedGamesUrl, HttpMethod.GET, entity, GetOwnedGameBody.class).getBody();
|
try {
|
||||||
for (var game : ownedResponse.getResponse().getGames()) {
|
var ownedResponse = rest.exchange(ownedGamesUrl, HttpMethod.GET, entity, GetOwnedGameBody.class).getBody();
|
||||||
var newGame = new APIResponse.Game();
|
for (var game : ownedResponse.getResponse().getGames()) {
|
||||||
newGame.setPlatformGameId(Integer.toString(game.getAppid()));
|
var newGame = new APIResponse.Game();
|
||||||
newGame.setName(game.getName());
|
newGame.setPlatformGameId(Integer.toString(game.getAppid()));
|
||||||
// Technically this is not the advertised logo url, but it's used be steamcommunity.com
|
newGame.setName(game.getName());
|
||||||
// and steamdb.info and it gives better aspect ratios and it means I don't need the random
|
// Technically this is not the advertised logo url, but it's used be steamcommunity.com
|
||||||
// logo_url field
|
// and steamdb.info and it gives better aspect ratios and it means I don't need the random
|
||||||
newGame.setThumbnail("https://cdn.cloudflare.steamstatic.com/steam/apps/" + game.getAppid() + "/header.jpg");
|
// logo_url field
|
||||||
newGame.setPlayed(game.getPlaytime_forever() > 0);
|
newGame.setThumbnail("https://cdn.cloudflare.steamstatic.com/steam/apps/" + game.getAppid() + "/header.jpg");
|
||||||
|
newGame.setPlayed(game.getPlaytime_forever() > 0);
|
||||||
|
|
||||||
var achievements = new HashMap<String, APIResponse.Game.Achievement>();
|
var achievements = new HashMap<String, APIResponse.Game.Achievement>();
|
||||||
|
|
||||||
var gameSchemaUrl = gameSchemaBaseUrl.cloneBuilder()
|
var gameSchemaUrl = gameSchemaBaseUrl.cloneBuilder()
|
||||||
.queryParam("appid", game.getAppid())
|
.queryParam("appid", game.getAppid())
|
||||||
.toUriString();
|
.toUriString();
|
||||||
var playerAchievementsUrl = playerAchievementsBaseUrl.cloneBuilder()
|
var playerAchievementsUrl = playerAchievementsBaseUrl.cloneBuilder()
|
||||||
.queryParam("appid", game.getAppid())
|
.queryParam("appid", game.getAppid())
|
||||||
.toUriString();
|
.toUriString();
|
||||||
|
|
||||||
var schemaResponse = rest.exchange(gameSchemaUrl, HttpMethod.GET, entity, GetSchemaForGameBody.class).getBody().getGame().getAvailableGameStats();
|
try {
|
||||||
if (schemaResponse != null && schemaResponse.getAchievements() != null) {
|
var schemaResponse = rest.exchange(gameSchemaUrl, HttpMethod.GET, entity, GetSchemaForGameBody.class).getBody().getGame().getAvailableGameStats();
|
||||||
for (var schema : schemaResponse.getAchievements()) {
|
if (schemaResponse != null && schemaResponse.getAchievements() != null) {
|
||||||
var achievement = new APIResponse.Game.Achievement();
|
for (var schema : schemaResponse.getAchievements()) {
|
||||||
achievement.setName(schema.getDisplayName());
|
var achievement = new APIResponse.Game.Achievement();
|
||||||
achievement.setDescription(schema.getDescription());
|
achievement.setName(schema.getDisplayName());
|
||||||
achievement.setStages(1);
|
achievement.setDescription(schema.getDescription());
|
||||||
achievement.setThumbnail(schema.getIcon());
|
achievement.setStages(1);
|
||||||
achievements.put(schema.getName(), achievement);
|
achievement.setThumbnail(schema.getIcon());
|
||||||
}
|
achievements.put(schema.getName(), achievement);
|
||||||
|
}
|
||||||
|
|
||||||
var playerAchievementsResponse = rest.exchange(playerAchievementsUrl, HttpMethod.GET, entity, GetPlayerAchievementsBody.class).getBody().getPlayerstats().getAchievements();
|
var playerAchievementsResponse = rest.exchange(playerAchievementsUrl, HttpMethod.GET, entity, GetPlayerAchievementsBody.class).getBody().getPlayerstats().getAchievements();
|
||||||
for (var achievement : playerAchievementsResponse) {
|
for (var achievement : playerAchievementsResponse) {
|
||||||
achievements.get(achievement.getApiname()).setProgress(achievement.getAchieved());
|
achievements.get(achievement.getApiname()).setProgress(achievement.getAchieved());
|
||||||
}
|
}
|
||||||
|
|
||||||
newGame.setAchievements(new ArrayList<>(achievements.values()));
|
newGame.setAchievements(new ArrayList<>(achievements.values()));
|
||||||
if (newGame.getAchievements().size() > 0) {
|
if (newGame.getAchievements().size() > 0) {
|
||||||
games.add(newGame);
|
games.add(newGame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Forbidden APPID: " + game.getAppid());
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
var response = new APIResponse();
|
var response = new APIResponse();
|
||||||
response.setGames(games);
|
response.setGames(games);
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
package achievements.controllers;
|
package achievements.controllers;
|
||||||
|
|
||||||
|
import achievements.data.APError;
|
||||||
|
import achievements.data.request.RateAchievement;
|
||||||
import achievements.services.ImageService;
|
import achievements.services.ImageService;
|
||||||
import achievements.services.AchievementService;
|
import achievements.services.AchievementService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
@ -19,9 +20,41 @@ public class AchievementController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ImageService imageService;
|
private ImageService imageService;
|
||||||
|
|
||||||
|
@GetMapping(value = "/{achievement}", produces = "application/json")
|
||||||
|
public ResponseEntity getAchievement(@PathVariable("achievement") int achievementId) {
|
||||||
|
var achievement = achievementService.getAchievement(achievementId);
|
||||||
|
if (achievement == null) {
|
||||||
|
return ResponseEntity.badRequest().body(new APError(1, "Failed to get achievement"));
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.ok(achievement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/{achievement}/image")
|
@GetMapping(value = "/{achievement}/image")
|
||||||
public void getProfilePicture(@PathVariable("achievement") int achievement, HttpServletResponse response) {
|
public void getProfilePicture(@PathVariable("achievement") int achievement, HttpServletResponse response) {
|
||||||
var icon = achievementService.getIcon(achievement);
|
var icon = achievementService.getIcon(achievement);
|
||||||
imageService.send(icon, "achievement", response);
|
imageService.send(icon, "achievement", response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/{achievement}/rating/{user}")
|
||||||
|
public ResponseEntity getRating(@PathVariable("achievement") int achievement, @PathVariable("user") int user) {
|
||||||
|
var rating = achievementService.getRating(achievement, user);
|
||||||
|
if (rating == null) {
|
||||||
|
return ResponseEntity.badRequest().body("{}");
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.ok(rating);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/{achievement}/rating/{user}")
|
||||||
|
public ResponseEntity setRating(@PathVariable("achievement") int achievement, @PathVariable("user") int user, @RequestBody RateAchievement rating) {
|
||||||
|
var review = achievementService.setRating(achievement, user, rating);
|
||||||
|
if (review == null) {
|
||||||
|
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("{}");
|
||||||
|
} else if (review.getSessionKey() == null) {
|
||||||
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(review);
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body("{}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,11 +65,18 @@ public class AuthController {
|
||||||
|
|
||||||
@PostMapping(value = "/refresh", consumes = "application/json", produces = "application/json")
|
@PostMapping(value = "/refresh", consumes = "application/json", produces = "application/json")
|
||||||
public ResponseEntity refresh(@RequestBody Session key) {
|
public ResponseEntity refresh(@RequestBody Session key) {
|
||||||
if (authService.refresh(key)) {
|
if (key.getId() == -1) {
|
||||||
return ResponseEntity.ok("{}");
|
if (authService.openAuth()) {
|
||||||
} else {
|
if (authService.refresh(key)) {
|
||||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{}");
|
return ResponseEntity.ok(key);
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.ok(authService.session().generate(-1, 0, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (authService.refresh(key)) {
|
||||||
|
return ResponseEntity.ok(key);
|
||||||
}
|
}
|
||||||
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/logout", consumes = "application/json")
|
@PostMapping(value = "/logout", consumes = "application/json")
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package achievements.controllers;
|
||||||
|
|
||||||
|
import achievements.data.importing.ImportPlatform;
|
||||||
|
import achievements.data.importing.ImportUser;
|
||||||
|
import achievements.data.importing.ImportUserPlatform;
|
||||||
|
import achievements.services.ImportService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/import")
|
||||||
|
public class ImportController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ImportService importService;
|
||||||
|
|
||||||
|
@PostMapping(value = "/platform", consumes = "application/json", produces = "application/json")
|
||||||
|
public ResponseEntity createPlatform(@RequestBody ImportPlatform platform) {
|
||||||
|
var response = importService.importPlatform(platform);
|
||||||
|
if (response == 0) {
|
||||||
|
return ResponseEntity.ok("{}");
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.badRequest().body("{}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/user", consumes = "application/json", produces = "application/json")
|
||||||
|
public ResponseEntity createUser(@RequestBody ImportUser user) {
|
||||||
|
var response = importService.importUser(user);
|
||||||
|
if (response == 0) {
|
||||||
|
return ResponseEntity.ok("{}");
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.badRequest().body("{}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/user/platform", consumes = "application/json", produces = "application/json")
|
||||||
|
public ResponseEntity addUserToPlatform(@RequestBody ImportUserPlatform userPlatform) {
|
||||||
|
var response = importService.importUserPlatform(userPlatform);
|
||||||
|
if (response == 0) {
|
||||||
|
return ResponseEntity.ok("{}");
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.badRequest().body("{}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -72,7 +72,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 AddPlatform request) {
|
public ResponseEntity addPlatformForUser(@PathVariable("user") int userId, @RequestBody AddPlatform request) {
|
||||||
var result = userService.addPlatform(userId, request);
|
var result = userService.addPlatform(userId, request, true);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body("{}");
|
return ResponseEntity.status(HttpStatus.CREATED).body("{}");
|
||||||
} else {
|
} else {
|
||||||
|
@ -89,4 +89,14 @@ public class UserController {
|
||||||
return ResponseEntity.badRequest().body("{}");
|
return ResponseEntity.badRequest().body("{}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/{user}/noteworthy", produces = "application/json")
|
||||||
|
public ResponseEntity getNoteworthy(@PathVariable("user") int userId) {
|
||||||
|
var result = userService.getNoteworthy(userId);
|
||||||
|
if (result != null) {
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
} else {
|
||||||
|
return ResponseEntity.badRequest().body("{}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,59 @@ public class Profile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Rating {
|
||||||
|
@JsonProperty("achievementId")
|
||||||
|
private int achievementId;
|
||||||
|
@JsonProperty("name")
|
||||||
|
private String name;
|
||||||
|
@JsonProperty("difficulty")
|
||||||
|
private Float difficulty;
|
||||||
|
@JsonProperty("quality")
|
||||||
|
private Float quality;
|
||||||
|
@JsonProperty("review")
|
||||||
|
private String review;
|
||||||
|
|
||||||
|
public int getAchievementId() {
|
||||||
|
return achievementId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAchievementId(int achievementId) {
|
||||||
|
this.achievementId = achievementId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getDifficulty() {
|
||||||
|
return difficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDifficulty(Float difficulty) {
|
||||||
|
this.difficulty = difficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getQuality() {
|
||||||
|
return quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuality(Float quality) {
|
||||||
|
this.quality = quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReview() {
|
||||||
|
return review;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReview(String review) {
|
||||||
|
this.review = review;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JsonProperty("username")
|
@JsonProperty("username")
|
||||||
private String username;
|
private String username;
|
||||||
@JsonProperty("completed")
|
@JsonProperty("completed")
|
||||||
|
@ -52,8 +105,8 @@ public class Profile {
|
||||||
private List<Achievement> noteworthy;
|
private List<Achievement> noteworthy;
|
||||||
@JsonProperty("platforms")
|
@JsonProperty("platforms")
|
||||||
private List<Platform> platforms;
|
private List<Platform> platforms;
|
||||||
/*@JsonProperty("ratings")
|
@JsonProperty("ratings")
|
||||||
private List<Rating> ratings;*/
|
private List<Rating> ratings;
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username;
|
return username;
|
||||||
|
@ -102,4 +155,12 @@ public class Profile {
|
||||||
public void setPlatforms(List<Platform> platforms) {
|
public void setPlatforms(List<Platform> platforms) {
|
||||||
this.platforms = platforms;
|
this.platforms = platforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Rating> getRatings() {
|
||||||
|
return ratings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRatings(List<Rating> ratings) {
|
||||||
|
this.ratings = ratings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,6 @@ public class Session {
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private boolean used;
|
private boolean used;
|
||||||
|
|
||||||
public Session(String key, int id, int hue, boolean admin) {
|
|
||||||
this.key = key;
|
|
||||||
this.id = id;
|
|
||||||
this.hue = hue;
|
|
||||||
this.admin = admin;
|
|
||||||
this.used = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,6 @@ public class User {
|
||||||
@JsonProperty("password")
|
@JsonProperty("password")
|
||||||
public String password;
|
public String password;
|
||||||
|
|
||||||
public User(String email, String username, String password) {
|
|
||||||
this.email = email;
|
|
||||||
this.username = username;
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEmail() {
|
public String getEmail() {
|
||||||
return email;
|
return email;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package achievements.data.importing;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public class ImportPlatform {
|
||||||
|
|
||||||
|
@JsonProperty("userId")
|
||||||
|
private int userId;
|
||||||
|
@JsonProperty("sessionKey")
|
||||||
|
private String sessionKey;
|
||||||
|
@JsonProperty("name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public int getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(int userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionKey() {
|
||||||
|
return sessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSessionKey(String sessionKey) {
|
||||||
|
this.sessionKey = sessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package achievements.data.importing;
|
||||||
|
|
||||||
|
import achievements.data.User;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public class ImportUser extends User {
|
||||||
|
|
||||||
|
@JsonProperty("userId")
|
||||||
|
private int userId;
|
||||||
|
@JsonProperty("sessionKey")
|
||||||
|
private String sessionKey;
|
||||||
|
@JsonProperty("admin")
|
||||||
|
private boolean admin;
|
||||||
|
|
||||||
|
public int getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(int userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionKey() {
|
||||||
|
return sessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSessionKey(String sessionKey) {
|
||||||
|
this.sessionKey = sessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAdmin() {
|
||||||
|
return admin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdmin(boolean admin) {
|
||||||
|
this.admin = admin;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package achievements.data.importing;
|
||||||
|
|
||||||
|
import achievements.data.request.AddPlatform;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public class ImportUserPlatform extends AddPlatform {
|
||||||
|
|
||||||
|
@JsonProperty("userId")
|
||||||
|
private int userId;
|
||||||
|
@JsonProperty("userEmail")
|
||||||
|
private String userEmail;
|
||||||
|
|
||||||
|
public int getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(int userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserEmail() {
|
||||||
|
return userEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserEmail(String userEmail) {
|
||||||
|
this.userEmail = userEmail;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package achievements.data.request;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public class RateAchievement {
|
||||||
|
|
||||||
|
@JsonProperty("sessionKey")
|
||||||
|
private String sessionKey;
|
||||||
|
@JsonProperty("difficulty")
|
||||||
|
private Float difficulty;
|
||||||
|
@JsonProperty("quality")
|
||||||
|
private Float quality;
|
||||||
|
@JsonProperty("review")
|
||||||
|
private String review;
|
||||||
|
|
||||||
|
public String getSessionKey() {
|
||||||
|
return sessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSessionKey(String sessionKey) {
|
||||||
|
this.sessionKey = sessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getDifficulty() {
|
||||||
|
return difficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDifficulty(Float difficulty) {
|
||||||
|
this.difficulty = difficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getQuality() {
|
||||||
|
return quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuality(Float quality) {
|
||||||
|
this.quality = quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReview() {
|
||||||
|
return review;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReview(String review) {
|
||||||
|
this.review = review;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,63 @@ package achievements.data.response.search;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class Achievement {
|
public class Achievement {
|
||||||
|
|
||||||
|
public static class Rating {
|
||||||
|
@JsonProperty("userId")
|
||||||
|
private int userId;
|
||||||
|
@JsonProperty("username")
|
||||||
|
private String username;
|
||||||
|
@JsonProperty("difficulty")
|
||||||
|
private Float difficulty;
|
||||||
|
@JsonProperty("quality")
|
||||||
|
private Float quality;
|
||||||
|
@JsonProperty("review")
|
||||||
|
private String review;
|
||||||
|
|
||||||
|
public int getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(int userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getDifficulty() {
|
||||||
|
return difficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDifficulty(Float difficulty) {
|
||||||
|
this.difficulty = difficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float getQuality() {
|
||||||
|
return quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuality(Float quality) {
|
||||||
|
this.quality = quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReview() {
|
||||||
|
return review;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReview(String review) {
|
||||||
|
this.review = review;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JsonProperty("ID")
|
@JsonProperty("ID")
|
||||||
private int ID;
|
private int ID;
|
||||||
@JsonProperty("game")
|
@JsonProperty("game")
|
||||||
|
@ -18,6 +73,8 @@ public class Achievement {
|
||||||
private Float difficulty;
|
private Float difficulty;
|
||||||
@JsonProperty("quality")
|
@JsonProperty("quality")
|
||||||
private Float quality;
|
private Float quality;
|
||||||
|
@JsonProperty("ratings")
|
||||||
|
private List<Rating> ratings;
|
||||||
|
|
||||||
public int getID() {
|
public int getID() {
|
||||||
return ID;
|
return ID;
|
||||||
|
@ -66,4 +123,12 @@ public class Achievement {
|
||||||
public void setQuality(Float quality) {
|
public void setQuality(Float quality) {
|
||||||
this.quality = quality;
|
this.quality = quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Rating> getRatings() {
|
||||||
|
return ratings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRatings(List<Rating> ratings) {
|
||||||
|
this.ratings = ratings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,11 @@ public class SessionManager {
|
||||||
|
|
||||||
public Session generate(int user, int hue, boolean admin) {
|
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, admin);
|
var session = new Session();
|
||||||
|
session.setKey(key);
|
||||||
|
session.setId(user);
|
||||||
|
session.setHue(hue);
|
||||||
|
session.setAdmin(admin);
|
||||||
sessions.put(key, session);
|
sessions.put(key, session);
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class APIService {
|
||||||
|
|
||||||
addGameToPlatform.setInt(1, gameId);
|
addGameToPlatform.setInt(1, gameId);
|
||||||
addGameToPlatform.setInt(2, platformId);
|
addGameToPlatform.setInt(2, platformId);
|
||||||
addGameToPlatform.setString(3, platformUserId);
|
addGameToPlatform.setString(3, game.getPlatformGameId());
|
||||||
addGameToPlatform.execute();
|
addGameToPlatform.execute();
|
||||||
|
|
||||||
var gameThumbnail = new File("storage/images/game/" + gameId + "." + getFileType(game.getThumbnail()));
|
var gameThumbnail = new File("storage/images/game/" + gameId + "." + getFileType(game.getThumbnail()));
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package achievements.services;
|
package achievements.services;
|
||||||
|
|
||||||
|
import achievements.data.request.RateAchievement;
|
||||||
|
import achievements.data.response.search.Achievement;
|
||||||
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 javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class AchievementService {
|
public class AchievementService {
|
||||||
|
@ -17,6 +21,9 @@ public class AchievementService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ImageService imageService;
|
private ImageService imageService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthenticationService authService;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
private void init() {
|
private void init() {
|
||||||
db = dbs.getConnection();
|
db = dbs.getConnection();
|
||||||
|
@ -31,4 +38,109 @@ public class AchievementService {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Achievement getAchievement(int achievementId) {
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call GetAchievement(?)}");
|
||||||
|
stmt.setInt(1, achievementId);
|
||||||
|
|
||||||
|
var result = stmt.executeQuery();
|
||||||
|
if (result.next()) {
|
||||||
|
var achievement = new Achievement();
|
||||||
|
achievement.setID(result.getInt("ID"));
|
||||||
|
achievement.setName(result.getString("Name"));
|
||||||
|
achievement.setCompletion(result.getInt("Completion")); if (result.wasNull()) { achievement.setCompletion(null); }
|
||||||
|
achievement.setDescription(result.getString("Description"));
|
||||||
|
achievement.setDifficulty(result.getFloat("Difficulty")); if (result.wasNull()) { achievement.setDifficulty(null); }
|
||||||
|
achievement.setQuality(result.getFloat("Quality")); if (result.wasNull()) { achievement.setQuality(null); }
|
||||||
|
|
||||||
|
stmt = db.prepareCall("{call GetRatingsForAchievement(?)}");
|
||||||
|
stmt.setInt(1, achievementId);
|
||||||
|
|
||||||
|
var ratings = new ArrayList<Achievement.Rating>();
|
||||||
|
var results = stmt.executeQuery();
|
||||||
|
while (results.next()) {
|
||||||
|
var rating = new Achievement.Rating();
|
||||||
|
rating.setUserId(results.getInt("UserID"));
|
||||||
|
rating.setUsername(results.getString("Username"));
|
||||||
|
rating.setDifficulty(results.getFloat("Difficulty")); if (results.wasNull()) { rating.setDifficulty(null); }
|
||||||
|
rating.setQuality(results.getFloat("Quality")); if (results.wasNull()) { rating.setQuality(null); }
|
||||||
|
rating.setReview(results.getString("Description"));
|
||||||
|
ratings.add(rating);
|
||||||
|
}
|
||||||
|
achievement.setRatings(ratings);
|
||||||
|
return achievement;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateAchievement getRating(int achievement, int user) {
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call HasProgress(?, ?, ?)}");
|
||||||
|
stmt.setInt(1, user);
|
||||||
|
stmt.setInt(2, achievement);
|
||||||
|
stmt.registerOutParameter(3, Types.BOOLEAN);
|
||||||
|
|
||||||
|
stmt.execute();
|
||||||
|
if (stmt.getBoolean(3)) {
|
||||||
|
stmt = db.prepareCall("{call GetRating(?, ?)}");
|
||||||
|
stmt.setInt(1, user);
|
||||||
|
stmt.setInt(2, achievement);
|
||||||
|
|
||||||
|
var result = stmt.executeQuery();
|
||||||
|
if (result.next()) {
|
||||||
|
var rating = new RateAchievement();
|
||||||
|
rating.setDifficulty(result.getFloat("Difficulty")); if (result.wasNull()) { rating.setDifficulty(null); }
|
||||||
|
rating.setQuality(result.getFloat("Quality")); if (result.wasNull()) { rating.setQuality(null); }
|
||||||
|
rating.setReview(result.getString("Description"));
|
||||||
|
return rating;
|
||||||
|
} else {
|
||||||
|
return new RateAchievement();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RateAchievement setRating(int achievementId, int userId, RateAchievement rateAchievement) {
|
||||||
|
if (authService.session().validate(userId, rateAchievement.getSessionKey())) {
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call SetRating(?, ?, ?, ?, ?)}");
|
||||||
|
stmt.setInt(1, userId);
|
||||||
|
stmt.setInt(2, achievementId);
|
||||||
|
stmt.setFloat(3, rateAchievement.getDifficulty());
|
||||||
|
stmt.setFloat(4, rateAchievement.getQuality());
|
||||||
|
stmt.setString(5, rateAchievement.getReview());
|
||||||
|
|
||||||
|
stmt.execute();
|
||||||
|
return rateAchievement;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call GetRating(?, ?)}");
|
||||||
|
stmt.setInt(1, userId);
|
||||||
|
stmt.setInt(2, achievementId);
|
||||||
|
|
||||||
|
var result = stmt.executeQuery();
|
||||||
|
if (result.next()) {
|
||||||
|
var rating = new RateAchievement();
|
||||||
|
rating.setDifficulty(result.getFloat("Difficulty"));
|
||||||
|
rating.setQuality(result.getFloat("Quality"));
|
||||||
|
rating.setReview(result.getString("Review"));
|
||||||
|
return rating;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,11 +84,14 @@ public class AuthenticationService {
|
||||||
public LoginResponse login(User user) {
|
public LoginResponse login(User user) {
|
||||||
var response = new LoginResponse(-1);
|
var response = new LoginResponse(-1);
|
||||||
try {
|
try {
|
||||||
var statement = db.prepareCall("{call GetUserLogin(?)}");
|
var statement = db.prepareCall("{? = call GetUserLogin(?)}");
|
||||||
statement.setString(1, user.email);
|
statement.registerOutParameter(1, Types.INTEGER);
|
||||||
|
statement.setString(2, user.email);
|
||||||
|
|
||||||
var result = statement.executeQuery();
|
statement.execute();
|
||||||
if (result.next()) {
|
if (statement.getInt(1) == 0) {
|
||||||
|
var result = statement.executeQuery();
|
||||||
|
result.next();
|
||||||
var salt = result.getString("Salt");
|
var salt = result.getString("Salt");
|
||||||
var hash = result.getString("Password");
|
var hash = result.getString("Password");
|
||||||
if (Password.validate(salt, user.getPassword(), hash)) {
|
if (Password.validate(salt, user.getPassword(), hash)) {
|
||||||
|
@ -115,6 +118,19 @@ public class AuthenticationService {
|
||||||
|
|
||||||
public boolean refresh(Session key) { return session.refresh(key.getKey()); }
|
public boolean refresh(Session key) { return session.refresh(key.getKey()); }
|
||||||
|
|
||||||
|
public boolean openAuth() {
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call HasUser(?)}");
|
||||||
|
stmt.registerOutParameter(1, Types.BOOLEAN);
|
||||||
|
|
||||||
|
stmt.execute();
|
||||||
|
return !stmt.getBoolean(1);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void logout(Session key) {
|
public void logout(Session key) {
|
||||||
session.remove(key.getKey());
|
session.remove(key.getKey());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package achievements.services;
|
||||||
|
|
||||||
|
import achievements.data.importing.ImportPlatform;
|
||||||
|
import achievements.data.importing.ImportUser;
|
||||||
|
import achievements.data.importing.ImportUserPlatform;
|
||||||
|
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.sql.Types;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ImportService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DbConnection dbs;
|
||||||
|
private Connection db;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthenticationService authService;
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
db = dbs.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int importPlatform(ImportPlatform platform) {
|
||||||
|
if (authService.session().validateAdmin(platform.getUserId(), platform.getSessionKey())) {
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call AddPlatform(?, ?)}");
|
||||||
|
stmt.setString(1, platform.getName());
|
||||||
|
stmt.registerOutParameter(2, Types.INTEGER);
|
||||||
|
|
||||||
|
stmt.execute();
|
||||||
|
return 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int importUser(ImportUser user) {
|
||||||
|
if (authService.session().validateAdmin(user.getUserId(), user.getSessionKey())) {
|
||||||
|
try {
|
||||||
|
var response = authService.createUser(user);
|
||||||
|
if (user.isAdmin()) {
|
||||||
|
var stmt = db.prepareCall("{call OpUser(?)}");
|
||||||
|
stmt.setInt(1, response.session.getId());
|
||||||
|
stmt.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int importUserPlatform(ImportUserPlatform userPlatform) {
|
||||||
|
if (authService.session().validateAdmin(userPlatform.getUserId(), userPlatform.getSessionKey())) {
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call GetIdFromEmail(?, ?)}");
|
||||||
|
stmt.setString(1, userPlatform.getUserEmail());
|
||||||
|
stmt.registerOutParameter(2, Types.INTEGER);
|
||||||
|
|
||||||
|
stmt.execute();
|
||||||
|
return userService.addPlatform(stmt.getInt(2), userPlatform, false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import achievements.data.Profile;
|
||||||
import achievements.data.request.AddPlatform;
|
import achievements.data.request.AddPlatform;
|
||||||
import achievements.data.request.RemovePlatform;
|
import achievements.data.request.RemovePlatform;
|
||||||
import achievements.data.request.SetUsername;
|
import achievements.data.request.SetUsername;
|
||||||
|
import achievements.data.response.search.Achievement;
|
||||||
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;
|
||||||
|
@ -17,6 +18,7 @@ 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.List;
|
||||||
|
|
||||||
import static achievements.services.ImageService.MIME_TO_EXT;
|
import static achievements.services.ImageService.MIME_TO_EXT;
|
||||||
|
|
||||||
|
@ -64,6 +66,8 @@ public class UserService {
|
||||||
if (average != null) {
|
if (average != null) {
|
||||||
profile.setAverage(Integer.parseInt(average));
|
profile.setAverage(Integer.parseInt(average));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +87,24 @@ public class UserService {
|
||||||
profile.setPlatforms(platforms);
|
profile.setPlatforms(platforms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var stmt = db.prepareCall("{call GetRatingsByUser(?)}");
|
||||||
|
stmt.setInt(1, userId);
|
||||||
|
|
||||||
|
var results = stmt.executeQuery();
|
||||||
|
var ratings = new ArrayList<Profile.Rating>();
|
||||||
|
while (results.next()) {
|
||||||
|
var rating = new Profile.Rating();
|
||||||
|
rating.setAchievementId(results.getInt("AchievementID"));
|
||||||
|
rating.setName(results.getString("Name"));
|
||||||
|
rating.setDifficulty(results.getFloat("Difficulty")); if (results.wasNull()) { rating.setDifficulty(null); }
|
||||||
|
rating.setQuality(results.getFloat("Quality")); if (results.wasNull()) { rating.setQuality(null); }
|
||||||
|
rating.setReview(results.getString("Description"));
|
||||||
|
ratings.add(rating);
|
||||||
|
}
|
||||||
|
profile.setRatings(ratings);
|
||||||
|
}
|
||||||
|
|
||||||
return profile;
|
return profile;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -163,8 +185,8 @@ public class UserService {
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
public int addPlatform(int userId, AddPlatform request) {
|
public int addPlatform(int userId, AddPlatform request, boolean validate) {
|
||||||
if (auth.session().validate(userId, request.getSessionKey())) {
|
if (!validate || auth.session().validate(userId, request.getSessionKey())) {
|
||||||
try {
|
try {
|
||||||
db.setAutoCommit(false);
|
db.setAutoCommit(false);
|
||||||
try {
|
try {
|
||||||
|
@ -211,5 +233,24 @@ public class UserService {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Achievement> getNoteworthy(int userId) {
|
||||||
|
try {
|
||||||
|
var stmt = db.prepareCall("{call GetNoteworthyAchievementsForUser(?)}");
|
||||||
|
stmt.setInt(1, userId);
|
||||||
|
|
||||||
|
var results = stmt.executeQuery();
|
||||||
|
var achievements = new ArrayList<Achievement>();
|
||||||
|
while (results.next()) {
|
||||||
|
var achievement = new Achievement();
|
||||||
|
achievement.setID(results.getInt("ID"));
|
||||||
|
achievement.setName(results.getString("Name"));
|
||||||
|
achievement.setCompletion(results.getInt("Completion"));
|
||||||
|
achievements.add(achievement);
|
||||||
|
}
|
||||||
|
return achievements;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
3
frontend/.gitignore
vendored
3
frontend/.gitignore
vendored
|
@ -1,3 +1,6 @@
|
||||||
# Node files
|
# Node files
|
||||||
node_modules/
|
node_modules/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
# Import Data
|
||||||
|
import.json
|
||||||
|
|
|
@ -22,27 +22,14 @@ passport.use(new SteamStrategy({
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use("/", morgan("dev"));
|
app.use("/", morgan("dev"));
|
||||||
app.use("/static", express.static("webpage/static"));
|
app.use("/static", express.static("webpage/static"));
|
||||||
app.get("/login", (req, res) => {
|
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) => res.sendFile(path.join(__dirname + "/webpage/search_achievements.html")));
|
||||||
});
|
app.get("/achievements", (req, res) => res.sendFile(path.join(__dirname + "/webpage/search_achievements.html")));
|
||||||
app.get("/", (req, res) => {
|
app.get("/users", (req, res) => res.sendFile(path.join(__dirname + "/webpage/search_users.html")));
|
||||||
res.sendFile(path.join(__dirname + "/webpage/search_achievements.html"));
|
app.get("/games", (req, res) => res.sendFile(path.join(__dirname + "/webpage/search_games.html")));
|
||||||
});
|
app.get("/import", (req, res) => res.sendFile(path.join(__dirname + "/webpage/import.html")));
|
||||||
app.get("/achievements", (req, res) => {
|
app.get("/achievement/:id", (req, res) => res.sendFile(path.join(__dirname + "/webpage/achievement.html")));
|
||||||
res.sendFile(path.join(__dirname + "/webpage/search_achievements.html"));
|
app.get("/user/:id", (req, res) => res.sendFile(path.join(__dirname + "/webpage/user.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("/achievement/:id", (req, res) => {
|
|
||||||
res.sendFile(path.join(__dirname + "/webpage/achievement.html"));
|
|
||||||
});
|
|
||||||
app.get("/user/:id", (req, res) => {
|
|
||||||
res.sendFile(path.join(__dirname + "/webpage/user.html"));
|
|
||||||
});
|
|
||||||
app.get("/auth/steam", passport.authenticate('steam'), (req, res) => {});
|
app.get("/auth/steam", passport.authenticate('steam'), (req, res) => {});
|
||||||
|
|
||||||
// --- API Forward --- //
|
// --- API Forward --- //
|
||||||
|
|
163
frontend/webpage/achievement.html
Normal file
163
frontend/webpage/achievement.html
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
<!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/achievement.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="achievement-page" class="page">
|
||||||
|
<div class="page-subsection">
|
||||||
|
<div class="page-header">
|
||||||
|
<p class="page-header-text">Achievement</p>
|
||||||
|
<div class="page-header-separator"></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="achievement-page">
|
||||||
|
<div id="achievement-section-0">
|
||||||
|
<div id="achievement-info" class="page-subsection">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div id="achievement-info-subheader" class="page-subheader">
|
||||||
|
<div id="achievement-info-flex" class="page-subheader-flex">
|
||||||
|
<p id="achievement-name-text" class="page-subheader-text">${name}</p>
|
||||||
|
<img id="achievement-icon-img" class="lazy-img" data-src="/api/achievement/${id}/image" alt="Achievement Icon" />
|
||||||
|
</div>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<p id="achievement-description-text" class="page-subsection-chunk ap-text">${description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="achievement-section-1">
|
||||||
|
<div id="achievement-stats" class="page-subsection">
|
||||||
|
<div id="achievement-stats-numeric">
|
||||||
|
<div id="achievement-completion" class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Completion Rate</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div id="achievement-completion-stack">
|
||||||
|
<img id="achievement-completion-background" src="/static/res/completion.svg">
|
||||||
|
<canvas id="achievement-completion-canvas"></canvas>
|
||||||
|
<p id="achievement-completion-text">${completion}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="achievement-difficulty" class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Difficulty</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<p id="achievement-difficulty-text">${difficulty}</p>
|
||||||
|
</div>
|
||||||
|
<div id="achievement-quality" class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Quality</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<p id="achievement-quality-text">${quality}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="achievement-rating" class="page-subsection">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<div class="page-subheader-flex">
|
||||||
|
<p class="page-subheader-text">My Rating</p>
|
||||||
|
<span id="rating-save-stack" class="achievement-save-stack">
|
||||||
|
<img class="achievement-save page-subheader-icon" src="/static/res/save.svg" alt="Save Platforms" />
|
||||||
|
<img class="achievement-save-hover page-subheader-icon" src="/static/res/save-hover.svg" alt="Save Platforms Hovered" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div id="achievement-rating-subsection" class="page-subsection-chunk">
|
||||||
|
<div id="achievement-rating-numeric">
|
||||||
|
<div id="achievement-difficulty-rating" class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Difficulty</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="achievement-rating-text-flex">
|
||||||
|
<input type="text" id="achievement-difficulty-rating-text" class="achievement-rating-text" value="${my_difficulty}"></input>
|
||||||
|
<p class="achievement-rating-max-text">/ 10</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="achievement-quality-rating" class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Quality</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="achievement-rating-text-flex">
|
||||||
|
<input type="text" id="achievement-quality-rating-text" class="achievement-rating-text" value="${my_quality}"></input>
|
||||||
|
<p class="achievement-rating-max-text">/ 10</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="achievement-description-rating" class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Review</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<textarea id="achievement-review-rating-text">${my_review}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="achievement-section-2">
|
||||||
|
<div id="achievement-ratings" class="page-subsection">
|
||||||
|
<div class="page-subsection-wrapper">
|
||||||
|
<div class="page-subheader">
|
||||||
|
<p class="page-subheader-text">Ratings</p>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<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 rating-username">Username</p>
|
||||||
|
<p class="list-page-entry-text rating-difficulty">Difficulty</p>
|
||||||
|
<p class="list-page-entry-text rating-quality">Quality</p>
|
||||||
|
<p class="list-page-entry-text rating-review">Review</p>
|
||||||
|
</div>
|
||||||
|
<template data-template="rating-list: List<Basic>">
|
||||||
|
<div class="list-page-entry rating" data-id="${user_id}">
|
||||||
|
<img class="list-page-entry-icon lazy-img" data-src="/api/user/${user_id}/image" alt="User Image"></img>
|
||||||
|
<p class="list-page-entry-text rating-username">${user_username}</p>
|
||||||
|
<p class="list-page-entry-text rating-difficulty">${user_difficulty}</p>
|
||||||
|
<p class="list-page-entry-text rating-quality">${user_quality}</p>
|
||||||
|
<p class="list-page-entry-text rating-review">${user_review}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/static/scripts/template.js"></script>
|
||||||
|
<script src="/static/scripts/common.js"></script>
|
||||||
|
<script src="/static/scripts/achievement.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
39
frontend/webpage/import.html
Normal file
39
frontend/webpage/import.html
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Achievements Project | Import</title>
|
||||||
|
<link rel="stylesheet" href="/static/styles/theme.css" />
|
||||||
|
<link rel="stylesheet" href="/static/styles/common.css" />
|
||||||
|
<link rel="stylesheet" href="/static/styles/import.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="navbar"></div>
|
||||||
|
<div id="content-body">
|
||||||
|
<div id="import-page" class="page">
|
||||||
|
<div class="page-subsection">
|
||||||
|
<div class="page-header">
|
||||||
|
<p class="page-header-text">Import</p>
|
||||||
|
<div class="page-header-separator"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="import-dropzone" class="page-subsection">
|
||||||
|
<div id="import-dropzone-wrapper" class="page-subsection-wrapper">
|
||||||
|
<div id="upload-wrapper">
|
||||||
|
<div id="upload-icon-stack">
|
||||||
|
<img id="import-icon-base" src="/static/res/import.svg" alt="Import Icon">
|
||||||
|
<img id="import-icon-hover" src="/static/res/import-hover.svg" alt="Import Icon Hover">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="import-console">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/scripts/template.js"></script>
|
||||||
|
<script src="/static/scripts/common.js"></script>
|
||||||
|
<script src="/static/scripts/import.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -119,7 +119,7 @@
|
||||||
<p id="achievement-header-quality" class="list-page-entry-text achievement-quality">Quality</p>
|
<p id="achievement-header-quality" class="list-page-entry-text achievement-quality">Quality</p>
|
||||||
</div>
|
</div>
|
||||||
<template id="achievement-list-template" data-template="achievements-page-list: List<Basic>">
|
<template id="achievement-list-template" data-template="achievements-page-list: List<Basic>">
|
||||||
<div id="achievement-entry-${achievement_id}" class="list-page-entry achievement">
|
<div class="list-page-entry achievement" data-id="${achievement_id}">
|
||||||
<img class="list-page-entry-icon lazy-img" data-src="/api/achievement/${achievement_id}/image" alt="Achievement Icon"></img>
|
<img class="list-page-entry-icon lazy-img" data-src="/api/achievement/${achievement_id}/image" alt="Achievement Icon"></img>
|
||||||
<p class="list-page-entry-text achievement-game-name">${game_name}</p>
|
<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-name">${achievement_name}</p>
|
||||||
|
|
1
frontend/webpage/static/res/import-hover.svg
Normal file
1
frontend/webpage/static/res/import-hover.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
1
frontend/webpage/static/res/import.svg
Normal file
1
frontend/webpage/static/res/import.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
|
@ -1,8 +0,0 @@
|
||||||
window.addEventListener("load", async (loadEvent) => {
|
|
||||||
await loadCommon();
|
|
||||||
|
|
||||||
await commonTemplates();
|
|
||||||
await template.expand();
|
|
||||||
|
|
||||||
connectNavbar();
|
|
||||||
});
|
|
167
frontend/webpage/static/scripts/achievement.js
Normal file
167
frontend/webpage/static/scripts/achievement.js
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
let achievementId = window.location.pathname.split('/').pop();
|
||||||
|
let isReturn = false;
|
||||||
|
let achievementData = null;
|
||||||
|
let myRating = {};
|
||||||
|
const loadAchievement = () => {
|
||||||
|
if (myRating.invalid) {
|
||||||
|
document.querySelector("#achievement-rating").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const description = document.querySelector("#achievement-description-text");
|
||||||
|
if (description.textContent === '') {
|
||||||
|
description.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Canvasing
|
||||||
|
|
||||||
|
const completionCanvas = document.querySelector("#achievement-completion-canvas");
|
||||||
|
|
||||||
|
const STROKE_WIDTH = 0.18;
|
||||||
|
const style = window.getComputedStyle(completionCanvas);
|
||||||
|
const context = completionCanvas.getContext('2d');
|
||||||
|
|
||||||
|
const drawCanvas = () => achievementData.then(data => {
|
||||||
|
const width = Number(style.getPropertyValue('width').slice(0, -2));
|
||||||
|
const height = width;
|
||||||
|
|
||||||
|
context.canvas.width = width;
|
||||||
|
context.canvas.height = height;
|
||||||
|
context.clearRect(0, 0, width, height);
|
||||||
|
context.strokeStyle = root.getProperty('--accent-value3');
|
||||||
|
context.lineWidth = (width / 2) * STROKE_WIDTH;
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(width / 2, height / 2, (width / 2) * (1 - STROKE_WIDTH / 2), -0.5 * Math.PI, (-0.5 + (data.completion === null ? 0 : (data.completion / 100) * 2)) * Math.PI);
|
||||||
|
context.stroke();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('resize', drawCanvas);
|
||||||
|
drawCanvas();
|
||||||
|
|
||||||
|
if (!myRating.invalid) {
|
||||||
|
const saveReview = document.querySelector("#rating-save-stack");
|
||||||
|
|
||||||
|
const myDifficulty = document.querySelector("#achievement-difficulty-rating-text");
|
||||||
|
const myQuality = document.querySelector("#achievement-quality-rating-text");
|
||||||
|
const myReview = document.querySelector("#achievement-review-rating-text");
|
||||||
|
|
||||||
|
const reviewInput = () => {
|
||||||
|
saveReview.style.display = 'block';
|
||||||
|
}
|
||||||
|
myDifficulty.addEventListener('input', reviewInput);
|
||||||
|
myQuality.addEventListener('input', reviewInput);
|
||||||
|
myReview.addEventListener('input', reviewInput);
|
||||||
|
|
||||||
|
const saveInputOnEnter = (keyEvent) => {
|
||||||
|
if (keyEvent.key === 'Enter') {
|
||||||
|
saveReview.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myDifficulty.addEventListener('keydown', saveInputOnEnter);
|
||||||
|
myQuality.addEventListener('keydown', saveInputOnEnter);
|
||||||
|
|
||||||
|
saveReview.addEventListener('click', (clickEvent) => {
|
||||||
|
let successful = true;
|
||||||
|
const difficulty = Number(myDifficulty.value);
|
||||||
|
const quality = Number(myQuality.value );
|
||||||
|
if ((Number.isNaN(difficulty) && myDifficulty.value !== '') || difficulty < 0 || difficulty > 10) {
|
||||||
|
myDifficulty.style.backgroundColor = 'var(--error)';
|
||||||
|
successful = false;
|
||||||
|
}
|
||||||
|
if ((Number.isNaN(quality) && myQuality.value !== '') || quality < 0 || quality > 10) {
|
||||||
|
myQuality.style.backgroundColor = 'var(--error)';
|
||||||
|
successful = false;
|
||||||
|
}
|
||||||
|
if (successful) {
|
||||||
|
myDifficulty.style.backgroundColor = 'var(--foreground)';
|
||||||
|
myQuality.style.backgroundColor = 'var(--foreground)';
|
||||||
|
saveReview.style.display = 'none';
|
||||||
|
fetch(`/api/achievement/${achievementId}/rating/${session.id}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sessionKey: session.key,
|
||||||
|
difficulty: difficulty,
|
||||||
|
quality: quality,
|
||||||
|
review: myReview.value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.status === 401) {
|
||||||
|
responese.json().then(data => {
|
||||||
|
myDifficulty.value = data.difficulty ? data.difficulty : '';
|
||||||
|
myQuality.value = data.quality ? data.quality : '';
|
||||||
|
myReview.value = data.review ? data.review : '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const ratings = document.querySelectorAll(".list-page-entry.rating");
|
||||||
|
for (const rating of ratings) {
|
||||||
|
rating.addEventListener("click", (clickEvent) => {
|
||||||
|
window.location.href = `/user/${rating.dataset.id}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandTemplates = async () => {
|
||||||
|
await commonTemplates();
|
||||||
|
if (session.key) {
|
||||||
|
myRating = await fetch(`/api/achievement/${achievementId}/rating/${session.id}`, { method: 'GET' })
|
||||||
|
.then(response => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
return { invalid: true };
|
||||||
|
} else {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
myRating = { invalid: true };
|
||||||
|
}
|
||||||
|
template.apply("achievement-page").promise(achievementData.then(data => ({
|
||||||
|
id: achievementId,
|
||||||
|
name: data.name,
|
||||||
|
description: data.description ? data.description : '',
|
||||||
|
completion: data.completion === null ? "N/A" : `${data.completion}%`,
|
||||||
|
difficulty: data.difficulty === null ? "N/A" : `${data.difficulty} / 10`,
|
||||||
|
quality: data.quality === null ? "N/A" : `${data.quality} / 10`,
|
||||||
|
my_difficulty: myRating.difficulty ? myRating.difficulty : '',
|
||||||
|
my_quality: myRating.quality ? myRating.quality : '',
|
||||||
|
my_review: myRating.review ? myRating.review : '',
|
||||||
|
})));
|
||||||
|
template.apply("rating-list").promise(achievementData.then(data => data.ratings.map(data => ({
|
||||||
|
user_id: data.userId,
|
||||||
|
user_username: data.username,
|
||||||
|
user_difficulty: data.difficulty,
|
||||||
|
user_quality: data.quality,
|
||||||
|
user_review: data.review
|
||||||
|
}))));
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("load", async (loadEvent) => {
|
||||||
|
await loadCommon();
|
||||||
|
|
||||||
|
var importing = document.querySelector("#importing");
|
||||||
|
if (/\d+/.test(achievementId)) {
|
||||||
|
achievementId = Number(achievementId);
|
||||||
|
} else {
|
||||||
|
// Handle error
|
||||||
|
}
|
||||||
|
importing.remove();
|
||||||
|
|
||||||
|
achievementData = fetch(`/api/achievement/${achievementId}`, { method: 'GET' })
|
||||||
|
.then(response => response.json());
|
||||||
|
|
||||||
|
await expandTemplates();
|
||||||
|
await template.expand();
|
||||||
|
|
||||||
|
loadLazyImages();
|
||||||
|
connectNavbar();
|
||||||
|
loadAchievement();
|
||||||
|
});
|
|
@ -9,37 +9,51 @@ const loadRoot = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let session = null;
|
let session = { id: null };
|
||||||
|
const clearSession = () => session = { id: null };
|
||||||
const loadSession = async () => {
|
const loadSession = async () => {
|
||||||
window.addEventListener('beforeunload', (beforeUnloadEvent) => {
|
window.addEventListener('beforeunload', (beforeUnloadEvent) => {
|
||||||
if (session) {
|
window.sessionStorage.setItem('session', JSON.stringify(session));
|
||||||
window.sessionStorage.setItem('session', JSON.stringify(session));
|
|
||||||
} else {
|
|
||||||
window.sessionStorage.removeItem('session');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
session = JSON.parse(window.sessionStorage.getItem('session'));
|
session = JSON.parse(window.sessionStorage.getItem('session')) || { id: -1 };
|
||||||
if (session) {
|
if (session.hue) {
|
||||||
root.setProperty('--accent-hue', session.hue);
|
root.setProperty('--accent-hue', session.hue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session.id !== null) {
|
||||||
await fetch(`/api/auth/refresh`, {
|
await fetch(`/api/auth/refresh`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ key: session.key })
|
body: JSON.stringify({ key: session.key, id: session.id })
|
||||||
})
|
})
|
||||||
.then(async response => ({ status: response.status, data: await response.json() }))
|
.then(async response => ({ status: response.status, data: await response.json() }))
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (response.status !== 200 && window.location.pathname != "/login") {
|
if (response.status !== 200 && window.location.pathname !== "/login") {
|
||||||
delete session.key;
|
session.id = null;
|
||||||
window.location.href = "/login";
|
session.key = null;
|
||||||
|
if (session.id !== -1) {
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
session.key = response.data.key;
|
||||||
|
session.id = response.data.id;
|
||||||
|
if (session.id === -1 && window.location.pathname !== '/import') {
|
||||||
|
window.location.href = '/import';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const authenticate = (obj) => {
|
||||||
|
obj.sessionKey = session.key;
|
||||||
|
obj.userId = session.id;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
const loadCommon = async () => {
|
const loadCommon = async () => {
|
||||||
loadRoot();
|
loadRoot();
|
||||||
await loadSession();
|
await loadSession();
|
||||||
|
@ -56,7 +70,7 @@ const commonTemplates = async () => {
|
||||||
{ item: "games", title: "Games" },
|
{ item: "games", title: "Games" },
|
||||||
{ item: "import", title: "Import" }
|
{ item: "import", title: "Import" }
|
||||||
]);
|
]);
|
||||||
if (session) {
|
if (session.id !== -1 && session.id !== null) {
|
||||||
template.apply("navbar-section-right").values([
|
template.apply("navbar-section-right").values([
|
||||||
{ item: "profile", title: "Profile" },
|
{ item: "profile", title: "Profile" },
|
||||||
{ item: "logout", title: "Logout" }
|
{ item: "logout", title: "Logout" }
|
||||||
|
@ -76,29 +90,31 @@ const loadLazyImages = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const connectNavbar = () => {
|
const connectNavbar = () => {
|
||||||
const navItems = document.querySelectorAll(".navbar-item");
|
if (session.id !== -1) {
|
||||||
|
const navItems = document.querySelectorAll(".navbar-item");
|
||||||
|
|
||||||
if (!session || !session.admin) {
|
if (!session.admin) {
|
||||||
document.querySelector("#navbar-item-import").remove();
|
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',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ key: session.key })
|
body: JSON.stringify({ key: session.key })
|
||||||
|
});
|
||||||
|
clearSession();
|
||||||
|
window.location.href = "/login";
|
||||||
});
|
});
|
||||||
session = undefined;
|
} else if (item.dataset.pageName === "profile") {
|
||||||
window.location.href = "/login";
|
item.addEventListener("click", (clickEvent) => window.location.href = `/user/${session.id}`);
|
||||||
});
|
} else {
|
||||||
} else if (item.dataset.pageName === "profile") {
|
item.addEventListener("click", (clickEvent) => window.location.href = `/${item.dataset.pageName}`);
|
||||||
item.addEventListener("click", (clickEvent) => window.location.href = `/user/${session.id}`);
|
}
|
||||||
} else {
|
|
||||||
item.addEventListener("click", (clickEvent) => window.location.href = `/${item.dataset.pageName}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
105
frontend/webpage/static/scripts/import.js
Normal file
105
frontend/webpage/static/scripts/import.js
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
let consoleTop = true;
|
||||||
|
let importConsole = null;
|
||||||
|
const appendLine = (line) => {
|
||||||
|
const template = document.createElement("template");
|
||||||
|
template.innerHTML = `<p class="console-entry ${consoleTop ? 'top' : ''}">${line}</p>`
|
||||||
|
importConsole.appendChild(template.content.firstElementChild);
|
||||||
|
consoleTop = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadConsole = () => {
|
||||||
|
importConsole = document.querySelector("#import-console");
|
||||||
|
|
||||||
|
const dropzone = document.querySelector("#import-dropzone");
|
||||||
|
const uploadWrapper = document.querySelector("#upload-wrapper");
|
||||||
|
const upload = (dropEvent) => {
|
||||||
|
dropEvent.preventDefault();
|
||||||
|
|
||||||
|
dropzone.classList.remove('active');
|
||||||
|
if (dropEvent.dataTransfer.files) {
|
||||||
|
const file = dropEvent.dataTransfer.files[0];
|
||||||
|
if (file.type === 'application/json') {
|
||||||
|
importConsole.style.display = 'block';
|
||||||
|
uploadWrapper.style.display = 'none';
|
||||||
|
file.text().then(data => JSON.parse(data)).then(data => {
|
||||||
|
let uploads = Promise.resolve();
|
||||||
|
for (let i = 0; i < data.platforms.length; ++i) {
|
||||||
|
const platform = data.platforms[i];
|
||||||
|
uploads = uploads
|
||||||
|
.then(() => {
|
||||||
|
appendLine(`(${i + 1}/${data.platforms.length}) Creating platform: ${platform.name}`);
|
||||||
|
}).then(() => fetch(
|
||||||
|
'/api/import/platform', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(authenticate(platform))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < data.users.length; ++i) {
|
||||||
|
const user = data.users[i];
|
||||||
|
const userPlatforms = user.platforms;
|
||||||
|
delete user.platforms;
|
||||||
|
uploads = uploads
|
||||||
|
.then(() => {
|
||||||
|
appendLine(`(${i + 1}/${data.users.length}) Creating user: ${user.username}`);
|
||||||
|
}).then(() => fetch(
|
||||||
|
'/api/import/user', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(authenticate(user))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
for (let j = 0; j < userPlatforms.length; ++j) {
|
||||||
|
const platform = userPlatforms[j];
|
||||||
|
platform.userEmail = user.email;
|
||||||
|
uploads = uploads
|
||||||
|
.then(() => {
|
||||||
|
appendLine(` (${j + 1}/${userPlatforms.length}) Importing platform data: ${data.platforms[platform.platformId].name}`);
|
||||||
|
}).then(() => fetch(
|
||||||
|
'/api/import/user/platform', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(authenticate(platform))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uploads = uploads.then(() => {
|
||||||
|
if (session.id === -1) {
|
||||||
|
clearSession();
|
||||||
|
window.location.href = '/login';
|
||||||
|
} else {
|
||||||
|
importConsole.innerHTML = '';
|
||||||
|
importConsole.style.display = 'none';
|
||||||
|
uploadWrapper.style.display = 'block';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
dropzone.addEventListener("drop", upload);
|
||||||
|
dropzone.addEventListener("dragover", (dragEvent) => {
|
||||||
|
dragEvent.preventDefault();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("load", async (loadEvent) => {
|
||||||
|
await loadCommon();
|
||||||
|
|
||||||
|
await commonTemplates();
|
||||||
|
await template.expand();
|
||||||
|
|
||||||
|
connectNavbar();
|
||||||
|
loadConsole();
|
||||||
|
});
|
|
@ -1,7 +1,7 @@
|
||||||
window.addEventListener("load", async (loadEvent) => {
|
window.addEventListener("load", async (loadEvent) => {
|
||||||
await loadCommon();
|
await loadCommon();
|
||||||
|
|
||||||
if (session && session.key) {
|
if (session.key) {
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ window.addEventListener("load", async (loadEvent) => {
|
||||||
const header = document.querySelector("#login-header-text");
|
const header = document.querySelector("#login-header-text");
|
||||||
const error = document.querySelector("#error-message");
|
const error = document.querySelector("#error-message");
|
||||||
|
|
||||||
if (session) {
|
if (!session.key && session.id) {
|
||||||
error.style.display = "block";
|
error.style.display = "block";
|
||||||
error.textContent = "You have been signed out due to inactivity";
|
error.textContent = "You have been signed out due to inactivity";
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,13 @@ const loadAchievementSearch = () => {
|
||||||
loading.style.display = 'none';
|
loading.style.display = 'none';
|
||||||
canSearch = true;
|
canSearch = true;
|
||||||
loadLazyImages();
|
loadLazyImages();
|
||||||
|
|
||||||
|
const entries = document.querySelectorAll(".list-page-entry.achievement");
|
||||||
|
for (const entry of entries) {
|
||||||
|
entry.addEventListener("click", (clickEvent) => {
|
||||||
|
window.location.href = `/achievement/${entry.dataset.id}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
|
|
|
@ -85,7 +85,6 @@ const loadGameSearch = () => {
|
||||||
}))));
|
}))));
|
||||||
await template.expand();
|
await template.expand();
|
||||||
data.then(data => {
|
data.then(data => {
|
||||||
console.log(data);
|
|
||||||
loading.style.display = 'none';
|
loading.style.display = 'none';
|
||||||
canSearch = true;
|
canSearch = true;
|
||||||
loadLazyImages();
|
loadLazyImages();
|
||||||
|
|
|
@ -60,6 +60,11 @@ const loadProfile = () => {
|
||||||
usernameField.value = usernameField.value.substring(0, 32);
|
usernameField.value = usernameField.value.substring(0, 32);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
usernameField.addEventListener("keydown", (keyEvent) => {
|
||||||
|
if (keyEvent.key === "Enter") {
|
||||||
|
saveProfileButton.click();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const pfp = document.querySelector("#profile-info-pfp-img");
|
const pfp = document.querySelector("#profile-info-pfp-img");
|
||||||
const pfpStack = document.querySelector("#profile-info-pfp");
|
const pfpStack = document.querySelector("#profile-info-pfp");
|
||||||
|
@ -188,7 +193,6 @@ const loadProfile = () => {
|
||||||
// Canvasing
|
// Canvasing
|
||||||
|
|
||||||
const completionCanvas = document.querySelector("#profile-completion-canvas");
|
const completionCanvas = document.querySelector("#profile-completion-canvas");
|
||||||
const completionText = document.querySelector("#profile-completion-text");
|
|
||||||
|
|
||||||
const STROKE_WIDTH = 0.18;
|
const STROKE_WIDTH = 0.18;
|
||||||
const style = window.getComputedStyle(completionCanvas);
|
const style = window.getComputedStyle(completionCanvas);
|
||||||
|
@ -214,6 +218,24 @@ const loadProfile = () => {
|
||||||
if (profileId === session.id) {
|
if (profileId === session.id) {
|
||||||
document.querySelector("#profile-page").classList.add("self");
|
document.querySelector("#profile-page").classList.add("self");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const noteworthy = document.querySelectorAll(".list-page-entry.achievement");
|
||||||
|
for (const achievement of noteworthy) {
|
||||||
|
achievement.addEventListener("click", (clickEvent) => {
|
||||||
|
window.location.href = `/achievement/${achievement.dataset.id}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const ratings = document.querySelectorAll(".list-page-entry.rating");
|
||||||
|
for (const rating of ratings) {
|
||||||
|
rating.addEventListener("click", (clickEvent) => {
|
||||||
|
window.location.href = `/achievement/${rating.dataset.id}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const expandTemplates = async () => {
|
const expandTemplates = async () => {
|
||||||
|
@ -225,6 +247,18 @@ const expandTemplates = async () => {
|
||||||
average: data.average === null ? "N/A" : data.average + "%",
|
average: data.average === null ? "N/A" : data.average + "%",
|
||||||
perfect: data.perfect,
|
perfect: data.perfect,
|
||||||
})));
|
})));
|
||||||
|
template.apply("profile-noteworthy-list").promise(
|
||||||
|
fetch(`/api/user/${profileId}/noteworthy`, {
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => data.map(data => ({
|
||||||
|
achievement_id: data.ID,
|
||||||
|
achievement_name: data.name,
|
||||||
|
completion: data.completion
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
);
|
||||||
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,
|
||||||
|
@ -238,6 +272,13 @@ const expandTemplates = async () => {
|
||||||
"")))
|
"")))
|
||||||
}))
|
}))
|
||||||
));
|
));
|
||||||
|
template.apply("rating-list").promise(profileData.then(data => data.ratings.map(data => ({
|
||||||
|
achievement_id: data.achievementId,
|
||||||
|
rating_achievement: data.name,
|
||||||
|
rating_difficulty: data.difficulty,
|
||||||
|
rating_quality: data.quality,
|
||||||
|
rating_review: data.review
|
||||||
|
}))));
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("load", async (loadEvent) => {
|
window.addEventListener("load", async (loadEvent) => {
|
||||||
|
@ -247,7 +288,7 @@ window.addEventListener("load", async (loadEvent) => {
|
||||||
if (!/\d+/.test(profileId)) {
|
if (!/\d+/.test(profileId)) {
|
||||||
isReturn = true;
|
isReturn = true;
|
||||||
const platform = profileId;
|
const platform = profileId;
|
||||||
if (!session) {
|
if (!session.key) {
|
||||||
window.location.href = "/404";
|
window.location.href = "/404";
|
||||||
} else {
|
} else {
|
||||||
profileId = session.lastProfile;
|
profileId = session.lastProfile;
|
||||||
|
@ -278,7 +319,7 @@ window.addEventListener("load", async (loadEvent) => {
|
||||||
window.history.replaceState({}, '', `/profile/${profileId}`);
|
window.history.replaceState({}, '', `/profile/${profileId}`);
|
||||||
} else if (/\d+/.test(profileId)) {
|
} else if (/\d+/.test(profileId)) {
|
||||||
profileId = Number(profileId);
|
profileId = Number(profileId);
|
||||||
if (session) {
|
if (session.key) {
|
||||||
session.lastProfile = profileId;
|
session.lastProfile = profileId;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
#about-page {
|
|
||||||
max-width: 1600px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#about-text {
|
|
||||||
margin: 0;
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
font-size: 18px;
|
|
||||||
color: var(--foreground);
|
|
||||||
|
|
||||||
background-color: var(--distinction);
|
|
||||||
}
|
|
459
frontend/webpage/static/styles/achievement.css
Normal file
459
frontend/webpage/static/styles/achievement.css
Normal file
|
@ -0,0 +1,459 @@
|
||||||
|
#achievement-page {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-list {
|
||||||
|
width: 100%;
|
||||||
|
height: max-content;
|
||||||
|
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
box-shadow: 0px 0px 8px 8px var(--shadow-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-entry {
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
height: 64px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
background-color: var(--distinction);
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-entry-left {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-entry-right {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-entry-icon {
|
||||||
|
width: 64px;
|
||||||
|
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-entry-text {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 0px 16px;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
line-height: 64px;
|
||||||
|
|
||||||
|
color: var(--foreground);
|
||||||
|
|
||||||
|
font-size: 24px;
|
||||||
|
|
||||||
|
border-top: 1px solid var(--background);
|
||||||
|
|
||||||
|
flex-basis: max-content;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top > .achievement-entry-text {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-entry-text.platform-name {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-info {
|
||||||
|
height: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-info-flex {
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-name-text {
|
||||||
|
flex-grow: 0;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-icon-img {
|
||||||
|
flex-grow: 0;
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
margin-left: 32px;
|
||||||
|
|
||||||
|
font-size: 32px;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
object-fit: contain;
|
||||||
|
|
||||||
|
background-color: var(--background-dark);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-description-text {
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--foreground);
|
||||||
|
background: var(--distinction);
|
||||||
|
margin: 0;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-section-1 {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: max-content;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-stats {
|
||||||
|
flex-basis: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-stats-numeric {
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-completion-stack {
|
||||||
|
flex-grow: 1;
|
||||||
|
height: max-content;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-completion-background {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-completion-canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-completion-text {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
color: var(--foreground);
|
||||||
|
font-size: 64px;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-difficulty,
|
||||||
|
#achievement-quality {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-difficulty-text {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
height: 48px;
|
||||||
|
|
||||||
|
color: var(--foreground);
|
||||||
|
|
||||||
|
font-size: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-quality-text {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
height: 48px;
|
||||||
|
|
||||||
|
color: var(--foreground);
|
||||||
|
|
||||||
|
font-size: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-rating {
|
||||||
|
flex-basis: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-save-stack {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-save-stack.active {
|
||||||
|
display: block;
|
||||||
|
width: max-content;
|
||||||
|
height: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-save-stack:hover .achievement-save,
|
||||||
|
.achievement-save-hover {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-save,
|
||||||
|
.achievement-save-stack:hover .achievement-save-hover {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-rating-subsection {
|
||||||
|
background-color: var(--distinction);
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-rating-numeric {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-rating-text-flex {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-difficulty-rating,
|
||||||
|
#achievement-quality-rating {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-rating-text {
|
||||||
|
flex-basis: max-content;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.achievement-rating-max-text {
|
||||||
|
flex-grow: 0;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
margin-left: 16px;
|
||||||
|
font-size: 24px;
|
||||||
|
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-difficulty-rating-text,
|
||||||
|
#achievement-quality-rating-text,
|
||||||
|
#achievement-review-rating-text {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 20px;
|
||||||
|
color: var(--background);
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#achievement-review-rating-text {
|
||||||
|
width: 100%;
|
||||||
|
height: 96px;
|
||||||
|
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#profile-section-2 {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: max-content;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#profile-hardest {
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#profile-platforms {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#profile-platforms .profile-entry {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#profile-platforms .profile-entry-text {
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
#profile-platforms .profile-entry.connected,
|
||||||
|
#profile-platforms .profile-entry.editing {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#profile-page .profile-edit-stack,
|
||||||
|
#profile-page .profile-save-stack {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#profile-page.self .profile-edit-stack,
|
||||||
|
#profile-page.self .profile-save-stack.active {
|
||||||
|
display: block;
|
||||||
|
width: max-content;
|
||||||
|
height: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
#profile-page.self .profile-edit-stack.active,
|
||||||
|
#profile-page.self .profile-save-stack {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-edit-stack:hover > .profile-edit-hover,
|
||||||
|
.profile-edit {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-edit-stack:hover > .profile-edit,
|
||||||
|
.profile-edit-hover {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-save-stack:hover > .profile-save-hover,
|
||||||
|
.profile-save {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-save-stack:hover > .profile-save,
|
||||||
|
.profile-save-hover {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-entry .platform-remove-stack,
|
||||||
|
.profile-entry .platform-add,
|
||||||
|
.profile-entry .platform-unsupported,
|
||||||
|
.profile-entry.connected.editing .platform-add {
|
||||||
|
border-top: 1px solid var(--background);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-entry.connected.editing .platform-remove-stack {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
width: max-content;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.platform-remove, .platform-remove-hover {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 12px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-entry.connected.editing .platform-remove-stack .platform-remove,
|
||||||
|
.profile-entry.connected.editing .platform-remove-stack:hover .platform-remove-hover {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-entry.connected.editing .platform-remove-stack:hover .platform-remove,
|
||||||
|
.profile-entry.connected.editing .platform-remove-stack .platform-remove-hover {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-entry .platform-add {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
padding: 16px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-entry.editing .platform-add {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-entry.editing .platform-unsupported {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0% 2%;
|
||||||
|
line-height: 63px;
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--foreground-disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
#profile-ratings {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-entry.rating:hover {
|
||||||
|
background-color: var(--background-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-entry-text.rating-username { flex-grow: 1.5; }
|
||||||
|
.list-page-entry-text.rating-difficulty { flex-grow: 1; }
|
||||||
|
.list-page-entry-text.rating-quality { flex-grow: 1; }
|
||||||
|
.list-page-entry-text.rating-review { flex-grow: 5; }
|
||||||
|
|
||||||
|
.list-page-header > .list-page-entry-text.rating-username:hover,
|
||||||
|
.list-page-header > .list-page-entry-text.rating-difficulty:hover,
|
||||||
|
.list-page-header > .list-page-entry-text.rating-quality:hover,
|
||||||
|
.list-page-header > .list-page-entry-text.rating-review:hover {
|
||||||
|
background-color: var(--accent-value2);
|
||||||
|
}
|
|
@ -14,6 +14,14 @@ html, body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
#navbar {
|
#navbar {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
|
@ -235,3 +243,350 @@ html, body {
|
||||||
|
|
||||||
background-color: var(--accent-value3);
|
background-color: var(--accent-value3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
transition-property: background-color;
|
||||||
|
transition-duration: 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
|
|
||||||
|
transition-property: background-color;
|
||||||
|
transition-duration: 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-header > .list-page-entry-text:hover {
|
||||||
|
background-color: var(--accent-value1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-header > .list-page-entry-text:active {
|
||||||
|
background-color: var(--accent-value0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading-results {
|
||||||
|
margin: 16px 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 64px;
|
||||||
|
object-fit: contain;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
}
|
78
frontend/webpage/static/styles/import.css
vendored
Normal file
78
frontend/webpage/static/styles/import.css
vendored
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#import-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#import-dropzone {
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#import-dropzone-wrapper {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#upload-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#upload-icon-stack {
|
||||||
|
width: max-content;
|
||||||
|
height: max-content;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#import-icon-base,
|
||||||
|
#import-icon-hover {
|
||||||
|
width: 256px;
|
||||||
|
height: 256px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#import-icon-hover {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#import-dropzone.active #import-icon-base,
|
||||||
|
#import-icon-hover {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#import-icon-base,
|
||||||
|
#import-dropzone.active #import-icon-hover {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#import-console {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-entry {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding-top: 0.25em;
|
||||||
|
height: max-content;
|
||||||
|
|
||||||
|
color: var(--foreground);
|
||||||
|
font-size: 24px;
|
||||||
|
font-family: 'Ubuntu Mono', monospace;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-entry.top {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
|
@ -1,346 +0,0 @@
|
||||||
#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;
|
|
||||||
|
|
||||||
transition-property: background-color;
|
|
||||||
transition-duration: 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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);
|
|
||||||
|
|
||||||
transition-property: background-color;
|
|
||||||
transition-duration: 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-header > .list-page-entry-text:hover {
|
|
||||||
background-color: var(--accent-value1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-page-header > .list-page-entry-text:active {
|
|
||||||
background-color: var(--accent-value0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#loading-results {
|
|
||||||
margin: 16px 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 64px;
|
|
||||||
object-fit: contain;
|
|
||||||
|
|
||||||
display: none;
|
|
||||||
}
|
|
|
@ -109,11 +109,11 @@
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile-info {
|
#profile-left {
|
||||||
width: 50%;
|
widtH: 50%;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
|
|
||||||
max-width: 480px;
|
max-width: 512px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile-info-username-text.active,
|
#profile-info-username-text.active,
|
||||||
|
@ -309,11 +309,6 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile-platforms {
|
|
||||||
flex-grow: 1;
|
|
||||||
max-width: 480px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#profile-platforms .profile-entry {
|
#profile-platforms .profile-entry {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -417,5 +412,37 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile-ratings {
|
#profile-ratings {
|
||||||
flex-grow: 1;
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-entry.achievement:hover {
|
||||||
|
background-color: var(--background-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.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; }
|
||||||
|
|
||||||
|
.list-page-header > .list-page-entry-text.-aichevevement-name:hover,
|
||||||
|
.list-page-header > .list-page-entry-text.-aichevevement-completion:hover,
|
||||||
|
.list-page-header > .list-page-entry-text.-aichevevement-quality:hover,
|
||||||
|
.list-page-header > .list-page-entry-text.-aichevevement-difficulty:hover {
|
||||||
|
background-color: var(--accent-value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-entry.rating:hover {
|
||||||
|
background-color: var(--background-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-page-entry-text.rating-achievement { flex-grow: 2; }
|
||||||
|
.list-page-entry-text.rating-difficulty { flex-grow: 1; }
|
||||||
|
.list-page-entry-text.rating-quality { flex-grow: 1; }
|
||||||
|
.list-page-entry-text.rating-review { flex-grow: 5; }
|
||||||
|
|
||||||
|
.list-page-header > .list-page-entry-text.rating-achievement:hover,
|
||||||
|
.list-page-header > .list-page-entry-text.rating-difficulty:hover,
|
||||||
|
.list-page-header > .list-page-entry-text.rating-quality:hover,
|
||||||
|
.list-page-header > .list-page-entry-text.rating-review:hover {
|
||||||
|
background-color: var(--accent-value2);
|
||||||
}
|
}
|
|
@ -34,30 +34,63 @@
|
||||||
</div>
|
</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-left">
|
||||||
<div class="page-subsection-wrapper">
|
<div id="profile-info" class="page-subsection">
|
||||||
<div class="page-subheader">
|
<div class="page-subsection-wrapper">
|
||||||
<div id="profile-info-name" class="page-subheader-flex">
|
<div class="page-subheader">
|
||||||
<p id="profile-info-username-text" class="page-subheader-text">${username}</p>
|
<div id="profile-info-name" class="page-subheader-flex">
|
||||||
<input id="profile-info-username-field" class="page-subheader-text" value="${username}"/>
|
<p id="profile-info-username-text" class="page-subheader-text">${username}</p>
|
||||||
<span id="info-edit-stack" class="profile-edit-stack">
|
<input id="profile-info-username-field" class="page-subheader-text" value="${username}"/>
|
||||||
<img class="profile-edit page-subheader-icon" src="/static/res/edit.svg" alt="Edit Platforms" />
|
<span id="info-edit-stack" class="profile-edit-stack">
|
||||||
<img class="profile-edit-hover page-subheader-icon" src="/static/res/edit-hover.svg" alt="Edit Platforms" />
|
<img class="profile-edit page-subheader-icon" src="/static/res/edit.svg" alt="Edit Platforms" />
|
||||||
</span>
|
<img class="profile-edit-hover page-subheader-icon" src="/static/res/edit-hover.svg" alt="Edit Platforms" />
|
||||||
<span id="info-save-stack" class="profile-save-stack">
|
</span>
|
||||||
<img class="profile-save page-subheader-icon" src="/static/res/save.svg" alt="Save Platforms" />
|
<span id="info-save-stack" class="profile-save-stack">
|
||||||
<img class="profile-save-hover page-subheader-icon" src="/static/res/save-hover.svg" alt="Edit Platforms" />
|
<img class="profile-save page-subheader-icon" src="/static/res/save.svg" alt="Save Platforms" />
|
||||||
</span>
|
<img class="profile-save-hover page-subheader-icon" src="/static/res/save-hover.svg" alt="Edit Platforms" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div id="profile-info-pfp-border" class="page-subsection-chunk">
|
||||||
|
<div id="profile-info-pfp">
|
||||||
|
<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>
|
||||||
|
<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-invalid" src="/static/res/upload-invalid.svg" alt="Invalid Image" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="page-subheader-separator"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="profile-info-pfp-border" class="page-subsection-chunk">
|
</div>
|
||||||
<div id="profile-info-pfp">
|
<div id="profile-platforms" class="page-subsection">
|
||||||
<img id="profile-info-pfp-img" class="lazy-img" data-src="/api/user/${id}/image" alt="User's Profile Picture" />
|
<div class="page-subsection-wrapper">
|
||||||
<div id="profile-info-pfp-vignette"></div>
|
<div class="page-subheader">
|
||||||
<img id="profile-info-pfp-upload" src="/static/res/upload.svg" alt="Upload Image" />
|
<div class="page-subheader-flex">
|
||||||
<img id="profile-info-pfp-upload-hover" src="/static/res/upload-hover.svg" alt="Upload Image" />
|
<p class="page-subheader-text">Platforms</p>
|
||||||
<img id="profile-info-pfp-upload-invalid" src="/static/res/upload-invalid.svg" alt="Invalid Image" />
|
<span id="platform-edit-stack" class="profile-edit-stack">
|
||||||
|
<img id="platform-edit" class="profile-edit page-subheader-icon" src="/static/res/edit.svg" alt="Edit Platforms" />
|
||||||
|
<img id="platform-edit-hover" class="profile-edit-hover page-subheader-icon" src="/static/res/edit-hover.svg" alt="Edit Platforms" />
|
||||||
|
</span>
|
||||||
|
<span id="platform-save-stack" class="profile-save-stack">
|
||||||
|
<img id="platform-save" class="profile-save page-subheader-icon" src="/static/res/save.svg" alt="Save Platforms" />
|
||||||
|
<img id="platform-save-hover" class="profile-save-hover page-subheader-icon" src="/static/res/save-hover.svg" alt="Edit Platforms" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="page-subheader-separator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="profile-list page-subsection-chunk">
|
||||||
|
<template data-template="profile-platforms-list: List<Basic>">
|
||||||
|
<div class="profile-entry ${connected}">
|
||||||
|
${img}
|
||||||
|
<p class="profile-entry-text platform-name">${name}</p>
|
||||||
|
<div id="platform-${platform_id}" class="platform-remove-stack">
|
||||||
|
<img class="platform-remove" src="/static/res/cancel.svg" alt="Cancel" />
|
||||||
|
<img class="platform-remove-hover" src="/static/res/cancel-hover.svg" alt="Cancel" />
|
||||||
|
</div>
|
||||||
|
${add}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -88,47 +121,50 @@
|
||||||
<p class="page-subheader-text">Noteworthy Achievements</p>
|
<p class="page-subheader-text">Noteworthy Achievements</p>
|
||||||
<div class="page-subheader-separator"></div>
|
<div class="page-subheader-separator"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="list-page-list">
|
||||||
</div>
|
<div class="list-page-header">
|
||||||
</div>
|
<p class="list-page-entry-icon"></p>
|
||||||
<div id="profile-section-2">
|
<p id="achievement-header-name" class="list-page-entry-text achievement-name">Name</p>
|
||||||
<div id="profile-platforms" class="page-subsection">
|
<p id="achievement-header-completion" class="list-page-entry-text achievement-completion">Completion %</p>
|
||||||
<div class="page-subsection-wrapper">
|
</div>
|
||||||
<div class="page-subheader">
|
<template data-template="profile-noteworthy-list: List<Basic>">
|
||||||
<div class="page-subheader-flex">
|
<div class="list-page-entry achievement" data-id="${achievement_id}">
|
||||||
<p class="page-subheader-text">Platforms</p>
|
<img class="list-page-entry-icon lazy-img" data-src="/api/achievement/${achievement_id}/image" alt="Achievement Icon"></img>
|
||||||
<span id="platform-edit-stack" class="profile-edit-stack">
|
<p class="list-page-entry-text achievement-name">${achievement_name}</p>
|
||||||
<img id="platform-edit" class="profile-edit page-subheader-icon" src="/static/res/edit.svg" alt="Edit Platforms" />
|
<p class="list-page-entry-text achievement-completion">${completion}</p>
|
||||||
<img id="platform-edit-hover" class="profile-edit-hover page-subheader-icon" src="/static/res/edit-hover.svg" alt="Edit Platforms" />
|
|
||||||
</span>
|
|
||||||
<span id="platform-save-stack" class="profile-save-stack">
|
|
||||||
<img id="platform-save" class="profile-save page-subheader-icon" src="/static/res/save.svg" alt="Save Platforms" />
|
|
||||||
<img id="platform-save-hover" class="profile-save-hover page-subheader-icon" src="/static/res/save-hover.svg" alt="Edit Platforms" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="page-subheader-separator"></div>
|
|
||||||
</div>
|
|
||||||
<div class="profile-list page-subsection-chunk">
|
|
||||||
<template data-template="profile-platforms-list: List<Basic>">
|
|
||||||
<div class="profile-entry ${connected}">
|
|
||||||
${img}
|
|
||||||
<p class="profile-entry-text platform-name">${name}</p>
|
|
||||||
<div id="platform-${platform_id}" class="platform-remove-stack">
|
|
||||||
<img class="platform-remove" src="/static/res/cancel.svg" alt="Cancel" />
|
|
||||||
<img class="platform-remove-hover" src="/static/res/cancel-hover.svg" alt="Cancel" />
|
|
||||||
</div>
|
|
||||||
${add}
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="profile-section-2">
|
||||||
<div id="profile-ratings" class="page-subsection">
|
<div id="profile-ratings" class="page-subsection">
|
||||||
<div class="page-subsection-wrapper">
|
<div class="page-subsection-wrapper">
|
||||||
<div class="page-subheader">
|
<div class="page-subheader">
|
||||||
<p class="page-subheader-text">Ratings</p>
|
<p class="page-subheader-text">Ratings</p>
|
||||||
<div class="page-subheader-separator"></div>
|
<div class="page-subheader-separator"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<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 rating-achievement">Achievement</p>
|
||||||
|
<p class="list-page-entry-text rating-difficulty">Difficulty</p>
|
||||||
|
<p class="list-page-entry-text rating-quality">Quality</p>
|
||||||
|
<p class="list-page-entry-text rating-review">Review</p>
|
||||||
|
</div>
|
||||||
|
<template data-template="rating-list: List<Basic>">
|
||||||
|
<div class="list-page-entry rating" data-id="${achievement_id}">
|
||||||
|
<img class="list-page-entry-icon lazy-img" data-src="/api/achievement/${achievement_id}/image" alt="Achievement Image"></img>
|
||||||
|
<p class="list-page-entry-text rating-achievement">${rating_achievement}</p>
|
||||||
|
<p class="list-page-entry-text rating-difficulty">${rating_difficulty}</p>
|
||||||
|
<p class="list-page-entry-text rating-quality">${rating_quality}</p>
|
||||||
|
<p class="list-page-entry-text rating-review">${rating_review}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,52 @@
|
||||||
|
DELETE FROM [User]
|
||||||
|
DELETE FROM [Game]
|
||||||
|
DELETE FROM [Platform]
|
||||||
|
GO
|
||||||
|
|
||||||
|
--------------
|
||||||
|
-- HAS USER --
|
||||||
|
--------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE HasUser(
|
||||||
|
@result BIT OUTPUT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
SET @result = CASE WHEN EXISTS (SELECT * FROM [User]) THEN 1 ELSE 0 END
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
-------------
|
||||||
|
-- OP USER --
|
||||||
|
-------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE OpUser(
|
||||||
|
@userId INT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
UPDATE [User] SET Admin = 1 WHERE @userId = [User].ID
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
-----------------------
|
||||||
|
-- GET ID FROM EMAIL --
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetIdFromEmail(
|
||||||
|
@email VARCHAR(254),
|
||||||
|
@userId INT OUTPUT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
SELECT @userId = ID
|
||||||
|
FROM [User]
|
||||||
|
WHERE Email = @email
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
-- GET USER NAME AND STATS PROCEDURE --
|
-- GET USER NAME AND STATS PROCEDURE --
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
ALTER PROCEDURE GetUserNameAndStats(
|
CREATE PROCEDURE GetUserNameAndStats(
|
||||||
@userId INT,
|
@userId INT,
|
||||||
@username VARCHAR(32) OUTPUT,
|
@username VARCHAR(32) OUTPUT,
|
||||||
@completed INT OUTPUT,
|
@completed INT OUTPUT,
|
||||||
|
@ -42,7 +86,7 @@ SELECT * FROM [User]
|
||||||
-- GET USER PLATFORMS PROCEDURE --
|
-- GET USER PLATFORMS PROCEDURE --
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
ALTER PROCEDURE GetUserPlatforms(
|
CREATE PROCEDURE GetUserPlatforms(
|
||||||
@userId INT
|
@userId INT
|
||||||
)
|
)
|
||||||
AS
|
AS
|
||||||
|
@ -62,7 +106,7 @@ GO
|
||||||
-- GET USER RATINGS PROCEDURE --
|
-- GET USER RATINGS PROCEDURE --
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
ALTER PROCEDURE GetUserRatings(
|
CREATE PROCEDURE GetUserRatings(
|
||||||
@userId INT
|
@userId INT
|
||||||
)
|
)
|
||||||
AS
|
AS
|
||||||
|
@ -83,7 +127,7 @@ GO
|
||||||
-- GET USER IMAGE PROCEDURE --
|
-- GET USER IMAGE PROCEDURE --
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
ALTER PROCEDURE GetUserImage(
|
CREATE PROCEDURE GetUserImage(
|
||||||
@userId INT
|
@userId INT
|
||||||
)
|
)
|
||||||
AS
|
AS
|
||||||
|
@ -100,7 +144,7 @@ GO
|
||||||
-- SET USERNAME --
|
-- SET USERNAME --
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
ALTER PROCEDURE SetUsername(
|
CREATE PROCEDURE SetUsername(
|
||||||
@userId INT,
|
@userId INT,
|
||||||
@username VARCHAR(32)
|
@username VARCHAR(32)
|
||||||
)
|
)
|
||||||
|
@ -118,7 +162,7 @@ GO
|
||||||
-- SET USER IMAGE PROCEDURE --
|
-- SET USER IMAGE PROCEDURE --
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
ALTER PROCEDURE SetUserImage(
|
CREATE PROCEDURE SetUserImage(
|
||||||
@userId INT,
|
@userId INT,
|
||||||
@type ImageType,
|
@type ImageType,
|
||||||
@oldType ImageType OUTPUT
|
@oldType ImageType OUTPUT
|
||||||
|
@ -138,7 +182,7 @@ GO
|
||||||
-- ADD USER TO PLATFORM --
|
-- ADD USER TO PLATFORM --
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
ALTER PROCEDURE AddUserToPlatform(
|
CREATE PROCEDURE AddUserToPlatform(
|
||||||
@userId INT,
|
@userId INT,
|
||||||
@platformId INT,
|
@platformId INT,
|
||||||
@platformUserID VARCHAR(32)
|
@platformUserID VARCHAR(32)
|
||||||
|
@ -167,7 +211,7 @@ GO
|
||||||
-- REMOVE USER FROM PLATFORM --
|
-- REMOVE USER FROM PLATFORM --
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
ALTER PROCEDURE RemoveUserFromPlatform(
|
CREATE PROCEDURE RemoveUserFromPlatform(
|
||||||
@userId INT,
|
@userId INT,
|
||||||
@platformId INT
|
@platformId INT
|
||||||
)
|
)
|
||||||
|
@ -197,7 +241,7 @@ GO
|
||||||
-- ADD PLATFORM --
|
-- ADD PLATFORM --
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
ALTER PROCEDURE AddPlatform(
|
CREATE PROCEDURE AddPlatform(
|
||||||
@name VARCHAR(32),
|
@name VARCHAR(32),
|
||||||
@platformId INT OUTPUT
|
@platformId INT OUTPUT
|
||||||
)
|
)
|
||||||
|
@ -207,7 +251,7 @@ BEGIN
|
||||||
PRINT 'Platform name cannot be null'
|
PRINT 'Platform name cannot be null'
|
||||||
RETURN 1
|
RETURN 1
|
||||||
END
|
END
|
||||||
INSERT INTO [Platform] VALUES (@name)
|
INSERT INTO [Platform] VALUES (@name, 'png')
|
||||||
SET @platformId = @@IDENTITY
|
SET @platformId = @@IDENTITY
|
||||||
RETURN 0
|
RETURN 0
|
||||||
GO
|
GO
|
||||||
|
@ -216,7 +260,7 @@ GO
|
||||||
-- REMOVE PLATFORM --
|
-- REMOVE PLATFORM --
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
ALTER PROCEDURE RemovePlatform(
|
CREATE PROCEDURE RemovePlatform(
|
||||||
@platformId INT
|
@platformId INT
|
||||||
)
|
)
|
||||||
AS
|
AS
|
||||||
|
@ -238,7 +282,7 @@ GO
|
||||||
-- GET PLATFORMS --
|
-- GET PLATFORMS --
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
ALTER PROCEDURE GetPlatforms
|
CREATE PROCEDURE GetPlatforms
|
||||||
AS
|
AS
|
||||||
SELECT ID, PlatformName FROM [Platform]
|
SELECT ID, PlatformName FROM [Platform]
|
||||||
RETURN 0
|
RETURN 0
|
||||||
|
@ -248,7 +292,7 @@ GO
|
||||||
-- GET PLATFORM NAME --
|
-- GET PLATFORM NAME --
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
ALTER PROCEDURE GetPlatformName(
|
CREATE PROCEDURE GetPlatformName(
|
||||||
@platformId INT,
|
@platformId INT,
|
||||||
@name VARCHAR(32) OUTPUT
|
@name VARCHAR(32) OUTPUT
|
||||||
)
|
)
|
||||||
|
@ -266,7 +310,7 @@ GO
|
||||||
-- GET PLATFORM ICON --
|
-- GET PLATFORM ICON --
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
ALTER PROCEDURE GetPlatformIcon(
|
CREATE PROCEDURE GetPlatformIcon(
|
||||||
@platformId INT
|
@platformId INT
|
||||||
)
|
)
|
||||||
AS
|
AS
|
||||||
|
@ -283,7 +327,7 @@ GO
|
||||||
-- ADD GAME --
|
-- ADD GAME --
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
ALTER PROCEDURE AddGame(
|
CREATE PROCEDURE AddGame(
|
||||||
@name VARCHAR(32),
|
@name VARCHAR(32),
|
||||||
@image ImageType,
|
@image ImageType,
|
||||||
@gameId INT OUTPUT
|
@gameId INT OUTPUT
|
||||||
|
@ -308,7 +352,7 @@ GO
|
||||||
-- ADD IF NOT GAME --
|
-- ADD IF NOT GAME --
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
ALTER PROCEDURE AddIfNotGame(
|
CREATE PROCEDURE AddIfNotGame(
|
||||||
@name VARCHAR(32),
|
@name VARCHAR(32),
|
||||||
@image VARCHAR(11),
|
@image VARCHAR(11),
|
||||||
@gameId INT OUTPUT
|
@gameId INT OUTPUT
|
||||||
|
@ -332,7 +376,7 @@ GO
|
||||||
-- REMOVE GAME --
|
-- REMOVE GAME --
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
ALTER PROCEDURE RemoveGame(
|
CREATE PROCEDURE RemoveGame(
|
||||||
@gameId INT
|
@gameId INT
|
||||||
)
|
)
|
||||||
AS
|
AS
|
||||||
|
@ -496,7 +540,7 @@ GO
|
||||||
-- ADD ACHIEVEMENT --
|
-- ADD ACHIEVEMENT --
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
ALTER PROCEDURE AddAchievement(
|
CREATE PROCEDURE AddAchievement(
|
||||||
@gameId INT,
|
@gameId INT,
|
||||||
@name VARCHAR(128),
|
@name VARCHAR(128),
|
||||||
@description VARCHAR(512),
|
@description VARCHAR(512),
|
||||||
|
@ -637,3 +681,186 @@ END
|
||||||
RETURN 0
|
RETURN 0
|
||||||
GO
|
GO
|
||||||
|
|
||||||
|
------------------------------------------
|
||||||
|
-- GET NOTEWORTHY ACHIEVEMENTS FOR USER --
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetNoteworthyAchievementsForUser (
|
||||||
|
@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 TOP 5 Achievement.ID, Name, Completion
|
||||||
|
FROM Achievement
|
||||||
|
JOIN MaxProgress ON Achievement.ID = MaxProgress.AchievementID
|
||||||
|
JOIN AchievementCompletion AC ON AC.ID = Achievement.ID
|
||||||
|
WHERE UserID = @userId AND Progress = Stages
|
||||||
|
ORDER BY Completion ASC, NumberUsers DESC
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
-- GET ACHIEVEMENT --
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetAchievement (
|
||||||
|
@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 Achievement.ID, Name, Completion, Description, Difficulty, Quality
|
||||||
|
FROM Achievement
|
||||||
|
LEFT JOIN AchievementCompletion AC ON Achievement.ID = AC.ID
|
||||||
|
LEFT JOIN AchievementRatings AR ON Achievement.ID = AR.ID
|
||||||
|
WHERE Achievement.ID = @achievementId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
---------------------------------
|
||||||
|
-- GET RATINGS FOR ACHIEVEMENT --
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetRatingsForAchievement(
|
||||||
|
@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 UserID, [Username], Difficulty, Quality, [Description]
|
||||||
|
FROM Rating
|
||||||
|
JOIN [User] ON [User].ID = Rating.UserID
|
||||||
|
WHERE AchievementID = @achievementId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
-- GET RATINGS BY USER --
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetRatingsByUser(
|
||||||
|
@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 AchievementID, Achievement.[Name], Difficulty, Quality, Rating.[Description]
|
||||||
|
FROM Rating
|
||||||
|
JOIN Achievement ON Achievement.ID = Rating.UserID
|
||||||
|
WHERE UserID = @userId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
EXEC GetRatingsByUser 0
|
||||||
|
|
||||||
|
------------------
|
||||||
|
-- HAS PROGRESS --
|
||||||
|
------------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE HasProgress (
|
||||||
|
@userId INT,
|
||||||
|
@achievementId INT,
|
||||||
|
@result BIT OUTPUT
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF NOT @achievementId IN (SELECT ID FROM Achievement)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No achievement with the specified ID was found'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
SET @result = CASE WHEN EXISTS (SELECT * FROM Progress WHERE UserID = @userId AND AchievementID = @achievementId) THEN 1 ELSE 0 END
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
----------------
|
||||||
|
-- GET RATING --
|
||||||
|
----------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE GetRating(
|
||||||
|
@userId INT,
|
||||||
|
@achievementId 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 @achievementId IN (SELECT ID FROM Achievement)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No achievement with the specified ID was found'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
SELECT Difficulty, Quality, [Description]
|
||||||
|
FROM Rating
|
||||||
|
WHERE UserID = @userId AND AchievementID = @achievementId
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
||||||
|
----------------
|
||||||
|
-- SET RATING --
|
||||||
|
----------------
|
||||||
|
|
||||||
|
CREATE PROCEDURE SetRating(
|
||||||
|
@userId INT,
|
||||||
|
@achievementId INT,
|
||||||
|
@difficulty FLOAT,
|
||||||
|
@quality FLOAT,
|
||||||
|
@review VARCHAR(1024)
|
||||||
|
)
|
||||||
|
AS
|
||||||
|
IF NOT @userId IN (SELECT ID FROM [User])
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No user with the specified ID was found'
|
||||||
|
RETURN 1
|
||||||
|
END
|
||||||
|
IF NOT @achievementId IN (SELECT ID FROM Achievement)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'No achievement with the specified ID was found'
|
||||||
|
RETURN 2
|
||||||
|
END
|
||||||
|
IF NOT EXISTS (SELECT * FROM Progress WHERE UserID = @userId AND AchievementID = @achievementId)
|
||||||
|
BEGIN
|
||||||
|
PRINT 'User does not have progress on achievement'
|
||||||
|
RETURN 3
|
||||||
|
END
|
||||||
|
IF @difficulty < 0 OR @difficulty > 10
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Difficult must be between 0 and 10'
|
||||||
|
RETURN 4
|
||||||
|
END
|
||||||
|
IF @quality < 0 OR @quality > 10
|
||||||
|
BEGIN
|
||||||
|
PRINT 'Quality must be between 0 and 10'
|
||||||
|
RETURN 5
|
||||||
|
END
|
||||||
|
IF @quality IS NULL AND @quality IS NULL AND @review IS NULL
|
||||||
|
DELETE FROM Rating WHERE UserID = @userId AND AchievementID = @achievementId
|
||||||
|
ELSE IF EXISTS (SELECT * FROM Rating WHERE UserID = @userId AND AchievementID = @achievementId)
|
||||||
|
UPDATE Rating SET
|
||||||
|
Quality = @quality,
|
||||||
|
Difficulty = @difficulty,
|
||||||
|
[Description] = @review
|
||||||
|
WHERE UserID = @userId AND AchievementID = @achievementId
|
||||||
|
ELSE
|
||||||
|
INSERT INTO Rating VALUES (@userId, @achievementId, @quality, @difficulty, @review)
|
||||||
|
RETURN 0
|
||||||
|
GO
|
||||||
|
|
21
sql/Indexes.sql
Normal file
21
sql/Indexes.sql
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
-----------------
|
||||||
|
-- Email Index --
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
CREATE NONCLUSTERED INDEX EmailIndex ON [User](Email)
|
||||||
|
|
||||||
|
------------------
|
||||||
|
-- Name Indexes --
|
||||||
|
------------------
|
||||||
|
|
||||||
|
CREATE NONCLUSTERED INDEX UsernameIndex ON [User](Username)
|
||||||
|
CREATE NONCLUSTERED INDEX GameNameIndex ON Game(Name)
|
||||||
|
CREATE NONCLUSTERED INDEX AchievementNameIndex ON Achievement(Name)
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
-- Rating Indexes --
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
CREATE NONCLUSTERED INDEX DifficultyIndex ON Rating(Difficulty)
|
||||||
|
CREATE NONCLUSTERED INDEX QualityIndex ON Rating(Quality)
|
||||||
|
|
1339
sql/Mondo.sql
Normal file
1339
sql/Mondo.sql
Normal file
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,7 @@
|
||||||
-- SEARCH ACHIEVEMENTS --
|
-- SEARCH ACHIEVEMENTS --
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
ALTER PROCEDURE SearchAchievements(
|
CREATE PROCEDURE SearchAchievements(
|
||||||
@searchTerm VARCHAR(32),
|
@searchTerm VARCHAR(32),
|
||||||
@userId INT,
|
@userId INT,
|
||||||
@completed BIT,
|
@completed BIT,
|
||||||
|
@ -61,7 +61,7 @@ GO
|
||||||
-- SEARCH USERS --
|
-- SEARCH USERS --
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
ALTER PROCEDURE SearchUsers(
|
CREATE PROCEDURE SearchUsers(
|
||||||
@searchTerm VARCHAR(32),
|
@searchTerm VARCHAR(32),
|
||||||
@minOwned INT,
|
@minOwned INT,
|
||||||
@maxOwned INT,
|
@maxOwned INT,
|
||||||
|
@ -118,7 +118,7 @@ GO
|
||||||
-- SEARCH GAMES --
|
-- SEARCH GAMES --
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
ALTER PROCEDURE SearchGames(
|
CREATE PROCEDURE SearchGames(
|
||||||
@searchTerm VARCHAR(32),
|
@searchTerm VARCHAR(32),
|
||||||
@userId INT,
|
@userId INT,
|
||||||
@owned BIT,
|
@owned BIT,
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
|
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
--CREATE TYPE ImageType FROM VARCHAR(4) NULL
|
CREATE TYPE ImageType FROM VARCHAR(4) NULL
|
||||||
--GO
|
GO
|
||||||
|
|
||||||
CREATE TABLE [User] (
|
CREATE TABLE [User] (
|
||||||
ID INT IDENTITY(0, 1) NOT NULL,
|
ID INT IDENTITY(0, 1) NOT NULL,
|
||||||
|
|
|
@ -18,7 +18,7 @@ GO
|
||||||
-- List of achievements and the percentage of people who have completed it
|
-- List of achievements and the percentage of people who have completed it
|
||||||
CREATE VIEW AchievementCompletion
|
CREATE VIEW AchievementCompletion
|
||||||
AS
|
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
|
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, COUNT(UserID) AS NumberUsers
|
||||||
FROM Achievement
|
FROM Achievement
|
||||||
LEFT JOIN MaxProgress ON AchievementID = Achievement.ID
|
LEFT JOIN MaxProgress ON AchievementID = Achievement.ID
|
||||||
GROUP BY Achievement.ID
|
GROUP BY Achievement.ID
|
||||||
|
|
Loading…
Reference in a new issue