Skip to content

Commit 419de12

Browse files
committed
Add flag for new projects list on user page, update background gradient colors
1 parent 0ac15a3 commit 419de12

File tree

3 files changed

+155
-138
lines changed

3 files changed

+155
-138
lines changed

apps/frontend/src/composables/featureFlags.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({
2929
newProjectCards: false,
3030
projectBackground: false,
3131
searchBackground: false,
32+
newProjectListUserPage: false,
33+
projectCardBackground: false,
3234
// advancedRendering: true,
3335
// externalLinksNewTab: true,
3436
// notUsingBlockers: false,

apps/frontend/src/pages/user/[id].vue

Lines changed: 149 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -52,147 +52,154 @@
5252
</UserHeader>
5353
</div>
5454
<div class="normal-page__content">
55-
<div v-if="navLinks.length >= 2" class="mb-4 max-w-full overflow-x-auto">
56-
<NavTabs :links="navLinks" />
57-
</div>
58-
<div v-if="projects.length > 0">
59-
<ProjectsList :projects="projects" :project-link="(project) => `/project/${project.id}`">
60-
<template #project-actions>
61-
<ButtonStyled color="brand">
62-
<button><DownloadIcon /> Install</button>
63-
</ButtonStyled>
64-
<ButtonStyled circular>
65-
<button v-tooltip="'Follow'">
66-
<HeartIcon />
67-
</button>
68-
</ButtonStyled>
69-
<ButtonStyled circular>
70-
<button v-tooltip="'Save'">
71-
<BookmarkIcon />
72-
</button>
73-
</ButtonStyled>
74-
<ButtonStyled circular type="transparent">
75-
<button>
76-
<MoreVerticalIcon />
77-
</button>
78-
</ButtonStyled>
79-
</template>
80-
</ProjectsList>
81-
82-
<div
83-
v-if="route.params.projectType !== 'collections'"
84-
:class="'project-list display-mode--' + cosmetics.searchDisplayMode.user"
85-
>
86-
<ProjectCard
87-
v-for="project in (route.params.projectType !== undefined
88-
? projects.filter(
89-
(x) =>
90-
x.project_type ===
91-
route.params.projectType.substr(0, route.params.projectType.length - 1),
92-
)
93-
: projects
94-
)
95-
.slice()
96-
.sort((a, b) => b.downloads - a.downloads)"
97-
:id="project.slug || project.id"
98-
:key="project.id"
99-
:name="project.title"
100-
:display="cosmetics.searchDisplayMode.user"
101-
:featured-image="project.gallery.find((element) => element.featured)?.url"
102-
:description="project.description"
103-
:created-at="project.published"
104-
:updated-at="project.updated"
105-
:downloads="project.downloads.toString()"
106-
:follows="project.followers.toString()"
107-
:icon-url="project.icon_url"
108-
:categories="project.categories"
109-
:client-side="project.client_side"
110-
:server-side="project.server_side"
111-
:status="
112-
auth.user && (auth.user.id === user.id || tags.staffRoles.includes(auth.user.role))
113-
? project.status
114-
: null
115-
"
116-
:type="project.project_type"
117-
:color="project.color"
118-
/>
55+
<ProjectsList
56+
v-if="flags.newProjectListUserPage"
57+
:projects="projects.filter((x) => x.status === 'approved' || x.status === 'archived')"
58+
:project-link="(project) => `/project/${project.id}`"
59+
:experimental-colors="flags.projectCardBackground"
60+
>
61+
<template #project-actions>
62+
<ButtonStyled color="brand">
63+
<button><DownloadIcon /> Install</button>
64+
</ButtonStyled>
65+
<ButtonStyled circular>
66+
<button v-tooltip="'Follow'">
67+
<HeartIcon />
68+
</button>
69+
</ButtonStyled>
70+
<ButtonStyled circular>
71+
<button v-tooltip="'Save'">
72+
<BookmarkIcon />
73+
</button>
74+
</ButtonStyled>
75+
<ButtonStyled circular type="transparent">
76+
<button>
77+
<MoreVerticalIcon />
78+
</button>
79+
</ButtonStyled>
80+
</template>
81+
</ProjectsList>
82+
<template v-else>
83+
<div v-if="navLinks.length >= 2" class="mb-4 max-w-full overflow-x-auto">
84+
<NavTabs :links="navLinks" />
11985
</div>
120-
</div>
121-
<div v-else-if="route.params.projectType !== 'collections'" class="error">
122-
<UpToDate class="icon" /><br />
123-
<span v-if="auth.user && auth.user.id === user.id" class="preserve-lines text">
124-
<IntlFormatted :message-id="messages.profileNoProjectsAuthLabel">
125-
<template #create-link="{ children }">
126-
<a class="link" @click.prevent="$refs.modal_creation.show()">
127-
<component :is="() => children" />
128-
</a>
129-
</template>
130-
</IntlFormatted>
131-
</span>
132-
<span v-else class="text">{{ formatMessage(messages.profileNoProjectsLabel) }}</span>
133-
</div>
134-
<div v-if="['collections'].includes(route.params.projectType)" class="collections-grid">
135-
<nuxt-link
136-
v-for="collection in collections"
137-
:key="collection.id"
138-
:to="`/collection/${collection.id}`"
139-
class="card collection-item"
140-
>
141-
<div class="collection">
142-
<Avatar :src="collection.icon_url" class="icon" />
143-
<div class="details">
144-
<h2 class="title">{{ collection.name }}</h2>
86+
<div v-if="projects.length > 0">
87+
<div
88+
v-if="route.params.projectType !== 'collections'"
89+
:class="'project-list display-mode--' + cosmetics.searchDisplayMode.user"
90+
>
91+
<ProjectCard
92+
v-for="project in (route.params.projectType !== undefined
93+
? projects.filter(
94+
(x) =>
95+
x.project_type ===
96+
route.params.projectType.substr(0, route.params.projectType.length - 1),
97+
)
98+
: projects
99+
)
100+
.slice()
101+
.sort((a, b) => b.downloads - a.downloads)"
102+
:id="project.slug || project.id"
103+
:key="project.id"
104+
:name="project.title"
105+
:display="cosmetics.searchDisplayMode.user"
106+
:featured-image="project.gallery.find((element) => element.featured)?.url"
107+
:description="project.description"
108+
:created-at="project.published"
109+
:updated-at="project.updated"
110+
:downloads="project.downloads.toString()"
111+
:follows="project.followers.toString()"
112+
:icon-url="project.icon_url"
113+
:categories="project.categories"
114+
:client-side="project.client_side"
115+
:server-side="project.server_side"
116+
:status="
117+
auth.user &&
118+
(auth.user.id === user.id || tags.staffRoles.includes(auth.user.role))
119+
? project.status
120+
: null
121+
"
122+
:type="project.project_type"
123+
:color="project.color"
124+
/>
125+
</div>
126+
</div>
127+
<div v-else-if="route.params.projectType !== 'collections'" class="error">
128+
<UpToDate class="icon" /><br />
129+
<span v-if="auth.user && auth.user.id === user.id" class="preserve-lines text">
130+
<IntlFormatted :message-id="messages.profileNoProjectsAuthLabel">
131+
<template #create-link="{ children }">
132+
<a class="link" @click.prevent="$refs.modal_creation.show()">
133+
<component :is="() => children" />
134+
</a>
135+
</template>
136+
</IntlFormatted>
137+
</span>
138+
<span v-else class="text">{{ formatMessage(messages.profileNoProjectsLabel) }}</span>
139+
</div>
140+
<div v-if="['collections'].includes(route.params.projectType)" class="collections-grid">
141+
<nuxt-link
142+
v-for="collection in collections"
143+
:key="collection.id"
144+
:to="`/collection/${collection.id}`"
145+
class="card collection-item"
146+
>
147+
<div class="collection">
148+
<Avatar :src="collection.icon_url" class="icon" />
149+
<div class="details">
150+
<h2 class="title">{{ collection.name }}</h2>
151+
<div class="stats">
152+
<LibraryIcon aria-hidden="true" />
153+
Collection
154+
</div>
155+
</div>
156+
</div>
157+
<div class="description">
158+
{{ collection.description }}
159+
</div>
160+
<div class="stat-bar">
161+
<div class="stats"><BoxIcon /> {{ collection.projects?.length || 0 }} projects</div>
145162
<div class="stats">
146-
<LibraryIcon aria-hidden="true" />
147-
Collection
163+
<template v-if="collection.status === 'listed'">
164+
<WorldIcon />
165+
<span> Public </span>
166+
</template>
167+
<template v-else-if="collection.status === 'unlisted'">
168+
<LinkIcon />
169+
<span> Unlisted </span>
170+
</template>
171+
<template v-else-if="collection.status === 'private'">
172+
<LockIcon />
173+
<span> Private </span>
174+
</template>
175+
<template v-else-if="collection.status === 'rejected'">
176+
<XIcon />
177+
<span> Rejected </span>
178+
</template>
148179
</div>
149180
</div>
150-
</div>
151-
<div class="description">
152-
{{ collection.description }}
153-
</div>
154-
<div class="stat-bar">
155-
<div class="stats"><BoxIcon /> {{ collection.projects?.length || 0 }} projects</div>
156-
<div class="stats">
157-
<template v-if="collection.status === 'listed'">
158-
<WorldIcon />
159-
<span> Public </span>
160-
</template>
161-
<template v-else-if="collection.status === 'unlisted'">
162-
<LinkIcon />
163-
<span> Unlisted </span>
164-
</template>
165-
<template v-else-if="collection.status === 'private'">
166-
<LockIcon />
167-
<span> Private </span>
168-
</template>
169-
<template v-else-if="collection.status === 'rejected'">
170-
<XIcon />
171-
<span> Rejected </span>
181+
</nuxt-link>
182+
</div>
183+
<div
184+
v-if="route.params.projectType === 'collections' && collections.length === 0"
185+
class="error"
186+
>
187+
<UpToDate class="icon" /><br />
188+
<span v-if="auth.user && auth.user.id === user.id" class="preserve-lines text">
189+
<IntlFormatted :message-id="messages.profileNoCollectionsAuthLabel">
190+
<template #create-link="{ children }">
191+
<a
192+
class="link"
193+
@click.prevent="(event) => $refs.modal_collection_creation.show(event)"
194+
>
195+
<component :is="() => children" />
196+
</a>
172197
</template>
173-
</div>
174-
</div>
175-
</nuxt-link>
176-
</div>
177-
<div
178-
v-if="route.params.projectType === 'collections' && collections.length === 0"
179-
class="error"
180-
>
181-
<UpToDate class="icon" /><br />
182-
<span v-if="auth.user && auth.user.id === user.id" class="preserve-lines text">
183-
<IntlFormatted :message-id="messages.profileNoCollectionsAuthLabel">
184-
<template #create-link="{ children }">
185-
<a
186-
class="link"
187-
@click.prevent="(event) => $refs.modal_collection_creation.show(event)"
188-
>
189-
<component :is="() => children" />
190-
</a>
191-
</template>
192-
</IntlFormatted>
193-
</span>
194-
<span v-else class="text">{{ formatMessage(messages.profileNoCollectionsLabel) }}</span>
195-
</div>
198+
</IntlFormatted>
199+
</span>
200+
<span v-else class="text">{{ formatMessage(messages.profileNoCollectionsLabel) }}</span>
201+
</div>
202+
</template>
196203
</div>
197204
<div class="normal-page__sidebar">
198205
<UserSidebarOrganizations
@@ -205,6 +212,11 @@
205212
:download-count="sumDownloads"
206213
class="card flex-card experimental-styles-within"
207214
/>
215+
<UserSidebarCollections
216+
:collections="collections.filter((x) => x.status === 'listed')"
217+
:link="(collection) => `/collection/${collection.id}`"
218+
class="card flex-card experimental-styles-within"
219+
/>
208220
<AdPlaceholder
209221
v-if="!auth.user || !isPermission(auth.user.badges, 1 << 0) || flags.showAdsWithPlus"
210222
/>
@@ -233,6 +245,7 @@ import {
233245
UserSidebarBadges,
234246
UserSidebarOrganizations,
235247
ProjectsList,
248+
UserSidebarCollections,
236249
} from "@modrinth/ui";
237250
import NavTabs from "~/components/ui/NavTabs.vue";
238251
import ProjectCard from "~/components/ui/ProjectCard.vue";

packages/ui/src/components/project/ProjectBackgroundGradient.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div :style="{ '--_accent-color': rgbToOklchHue(props.project.color) }" />
2+
<div :style="{ '--_accent-color': color }" />
33
</template>
44
<script setup lang="ts">
55
import { computed } from 'vue'
@@ -15,12 +15,14 @@ const props = withDefaults(
1515
{},
1616
)
1717
18+
const color = computed(() => rgbToOklchHue(props.project.color))
19+
1820
</script>
1921
<style scoped lang="scss">
2022
div {
2123
width: 100%;
2224
height: 60rem;
23-
background: linear-gradient(to bottom, oklch(40% 10% var(--_accent-color) / 25%), transparent);
25+
background: linear-gradient(to bottom, oklch(40% 30% var(--_accent-color) / 10%), transparent);
2426
opacity: 1;
2527
}
2628
</style>

0 commit comments

Comments
 (0)