feat(settings): reduce animations appearance flag
Build, Test & Push Auth Service / quality-check (push) Successful in 53s
Build, Test & Push Auth Service / docker (push) Successful in 1m31s

This commit is contained in:
lobo committed 2026-10-01 15:09:25 +02:00
1 parent a60011ce9f
commit 878ede914b
11 files changed
+30 -17

No files matched your search

+2 -2
View File
@@ -33,8 +33,8 @@ To Be Played is split into three repositories:
as verified. A one-time state plus a binding cookie tie the answer to the browser that started the link, so a
foreign Steam login link cannot move someone's Steam ID into another account. IDs typed in before this stay, but
unverified: a verified link from another account takes them away. Unlinking is possible.
- Appearance settings synced across devices: accent color, corner style, wide layout, background and dashboard
banner from the user's own game art. Only whitelisted presets and Steam app ids are stored, never raw CSS or URLs.
- Appearance settings synced across devices: accent color, corner style, wide layout, reduce animations, background and
dashboard banner from the user's own game art. Only whitelisted presets and Steam app ids are stored, never raw CSS or URLs.
- Opt-in public profile at `/u/<display name>`. Auth answers with a short-lived **profile ticket** (EdDSA JWT,
10 minutes, `typ=public-profile`) that Core accepts for the public library data only. The profile has its own
look (accent, corners, banner and background from own game art) and a **tile layout** (`profile_tiles`: the shown
@@ -13,7 +13,8 @@ public record AppearanceRequest(
Boolean wideLayout,
@Positive Long backgroundAppid,
@Positive Long bannerAppid,
Boolean showBanner) {
Boolean showBanner,
Boolean reduceMotion) {
public static final String ACCENTS = "violet|blue|green|orange|pink|red";
public static final String CORNERS = "sharp|default|round";
@@ -1,5 +1,5 @@
package auth_service.modules.user.api.dto.response;
public record AppearanceResponse(String accent, String corners, boolean wideLayout, Long backgroundAppid,
Long bannerAppid, boolean showBanner) {
Long bannerAppid, boolean showBanner, boolean reduceMotion) {
}
@@ -56,7 +56,8 @@ public abstract class ApiMapper {
private static AppearanceResponse toApi(Appearance appearance) {
return new AppearanceResponse(appearance.accent(), appearance.corners(), appearance.wideLayout(),
appearance.backgroundAppid(), appearance.bannerAppid(), appearance.showBanner());
appearance.backgroundAppid(), appearance.bannerAppid(), appearance.showBanner(),
appearance.reduceMotion());
}
private static ProfileStyleResponse toApi(ProfileStyle style) {
@@ -81,7 +82,8 @@ public abstract class ApiMapper {
public static Appearance toDomain(AppearanceRequest request) {
// A missing showBanner keeps the banner on, the default.
return new Appearance(request.accent(), request.corners(), Boolean.TRUE.equals(request.wideLayout()),
request.backgroundAppid(), request.bannerAppid(), !Boolean.FALSE.equals(request.showBanner()));
request.backgroundAppid(), request.bannerAppid(), !Boolean.FALSE.equals(request.showBanner()),
Boolean.TRUE.equals(request.reduceMotion()));
}
public static UserSettings toDomain(UserSettingsRequest request) {
@@ -4,9 +4,10 @@ package auth_service.modules.user.model;
* @param backgroundAppid the Steam app whose art fills the page background; null for none
* @param bannerAppid the Steam app whose art is the dashboard banner; null for the most played game of the last
* two weeks
* @param reduceMotion viewer setting: every animated look is static, also on other users' profiles
*/
public record Appearance(String accent, String corners, boolean wideLayout, Long backgroundAppid,
Long bannerAppid, boolean showBanner) {
Long bannerAppid, boolean showBanner, boolean reduceMotion) {
public static final Appearance DEFAULT = new Appearance("violet", "default", false, null, null, true);
public static final Appearance DEFAULT = new Appearance("violet", "default", false, null, null, true, false);
}
@@ -50,6 +50,9 @@ public class UserSettingsEntity {
@Column(name = "show_banner", nullable = false)
private boolean showBanner;
@Column(name = "reduce_motion", nullable = false)
private boolean reduceMotion;
@Column(name = "profile_accent", nullable = false)
private String profileAccent;
@@ -28,7 +28,8 @@ public abstract class EntityMapper {
.theme(settings.getTheme())
.publicProfile(settings.isPublicProfile())
.appearance(new Appearance(settings.getAccent(), settings.getCorners(), settings.isWideLayout(),
settings.getBackgroundAppid(), settings.getBannerAppid(), settings.isShowBanner()))
settings.getBackgroundAppid(), settings.getBannerAppid(), settings.isShowBanner(),
settings.isReduceMotion()))
.profileStyle(new ProfileStyle(settings.getProfileAccent(), settings.getProfileCorners(),
settings.getProfileBannerAppid(), settings.getProfileBackgroundAppid()))
.dashboardTiles(settings.getDashboardTiles() == null ? null
@@ -66,6 +67,7 @@ public abstract class EntityMapper {
target.setBackgroundAppid(appearance.backgroundAppid());
target.setBannerAppid(appearance.bannerAppid());
target.setShowBanner(appearance.showBanner());
target.setReduceMotion(appearance.reduceMotion());
ProfileStyle style = settings.getProfileStyle();
target.setProfileAccent(style.accent());
target.setProfileCorners(style.corners());
@@ -0,0 +1,3 @@
-- Viewer setting: every animated look is static, also on other users' profiles.
ALTER TABLE user_settings
ADD COLUMN reduce_motion BOOLEAN NOT NULL DEFAULT FALSE;
@@ -328,7 +328,7 @@ class UserControllerTest extends AbstractWebMvcTest {
void getCurrentUser_ShowsAppearance() throws Exception {
var email = "test@example.com";
var settings = UserSettings.builder().language("en").theme("dark")
.appearance(new Appearance("blue", "round", true, 620L, 1086940L, false)).build();
.appearance(new Appearance("blue", "round", true, 620L, 1086940L, false, true)).build();
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(new UserPrincipal("1", null), null, Collections.emptyList())
);
@@ -341,7 +341,8 @@ class UserControllerTest extends AbstractWebMvcTest {
.andExpect(jsonPath("$.settings.appearance.wideLayout").value(true))
.andExpect(jsonPath("$.settings.appearance.backgroundAppid").value(620))
.andExpect(jsonPath("$.settings.appearance.bannerAppid").value(1086940))
.andExpect(jsonPath("$.settings.appearance.showBanner").value(false));
.andExpect(jsonPath("$.settings.appearance.showBanner").value(false))
.andExpect(jsonPath("$.settings.appearance.reduceMotion").value(true));
}
@Test
@@ -629,10 +630,10 @@ class UserControllerTest extends AbstractWebMvcTest {
mockMvc.perform(put("/api/users/settings/appearance")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"accent\":\"green\",\"corners\":\"sharp\",\"wideLayout\":true,\"backgroundAppid\":620,"
+ "\"bannerAppid\":1086940,\"showBanner\":false}"))
+ "\"bannerAppid\":1086940,\"showBanner\":false,\"reduceMotion\":true}"))
.andExpect(status().isNoContent());
verify(userService).updateAppearance(1L, new Appearance("green", "sharp", true, 620L, 1086940L, false));
verify(userService).updateAppearance(1L, new Appearance("green", "sharp", true, 620L, 1086940L, false, true));
}
@Test
@@ -645,7 +646,7 @@ class UserControllerTest extends AbstractWebMvcTest {
.content("{\"accent\":\"violet\",\"corners\":\"default\"}"))
.andExpect(status().isNoContent());
verify(userService).updateAppearance(1L, new Appearance("violet", "default", false, null, null, true));
verify(userService).updateAppearance(1L, new Appearance("violet", "default", false, null, null, true, false));
}
// Each body differs from a valid one in one field only, so the 400 comes from that field's constraint.
@@ -276,7 +276,7 @@ class UserRepositoryTest extends AbstractPersistenceTest {
.registrationDate(LocalDateTime.now())
.build());
userRepository.update(userEntity.getId(),
user -> user.getSettings().setAppearance(new Appearance("pink", "sharp", false, 220L, null, true)));
user -> user.getSettings().setAppearance(new Appearance("pink", "sharp", false, 220L, null, true, true)));
entityManager.flush();
updateSettings(userEntity.getId(), null,
@@ -285,7 +285,7 @@ class UserRepositoryTest extends AbstractPersistenceTest {
entityManager.clear();
var settings = findByEmail("keep@example.com").orElseThrow().getSettings();
assertEquals(new Appearance("pink", "sharp", false, 220L, null, true), settings.getAppearance());
assertEquals(new Appearance("pink", "sharp", false, 220L, null, true, true), settings.getAppearance());
}
@Test
@@ -180,7 +180,7 @@ class UserServiceTest extends AbstractUnitTest {
@Test
void updateAppearance_StoresItWithoutANewToken() {
var appearance = new Appearance("red", "round", false, null, null, true);
var appearance = new Appearance("red", "round", false, null, null, true, false);
userService.updateAppearance(1L, appearance);