Skip to content

Commit

Permalink
Page home & explore for community (goplus#973)
Browse files Browse the repository at this point in the history
* community explore & home

* ProjectItem hover details
  • Loading branch information
nighca authored Oct 11, 2024
1 parent 9d123a2 commit 2db9b6c
Show file tree
Hide file tree
Showing 19 changed files with 523 additions and 134 deletions.
5 changes: 5 additions & 0 deletions spx-gui/src/apis/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ export type FileCollection = {
[path: string]: UniversalUrl
}

/** Get time string for spx-backend APIs */
export function timeStringify(time: number) {
return new Date(time).toISOString()
}

export const client = new Client()
54 changes: 53 additions & 1 deletion spx-gui/src/apis/project.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import dayjs from 'dayjs'
import type { FileCollection, ByPage, PaginationParams } from './common'
import { client, IsPublic, ownerAll } from './common'
import { client, IsPublic, ownerAll, timeStringify } from './common'
import { ApiException, ApiExceptionCode } from './common/exception'

export { IsPublic, ownerAll }
Expand Down Expand Up @@ -105,6 +106,14 @@ export type ListProjectParams = PaginationParams & {
owner?: string
/** Filter projects by name pattern */
keyword?: string
/** Filter projects that were created after this timestamp */
createdAfter?: string
/** Filter projects that gained new likes after this timestamp */
likesReceivedAfter?: string
/** Filter projects that were remixed after this timestamp */
remixesReceivedAfter?: string
/** If filter projects created by followees of logged-in user */
fromFollowees?: boolean
/** Field by which to order the results */
orderBy?: 'cTime' | 'uTime' | 'likeCount' | 'remixCount' | 'recentLikeCount' | 'recentRemixCount'
/** Order in which to sort the results */
Expand All @@ -125,6 +134,49 @@ export async function getProject(owner: string, name: string) {
return __adaptProjectData(await client.get(`/project/${encode(owner, name)}`))
}

export enum ExploreOrder {
MostLikes = 'likes',
MostRemixes = 'remix',
FollowingCreated = 'following'
}

export type ExploreParams = {
order: ExploreOrder
count: number
}

/** Get project list for explore purpose */
export async function exploreProjects({ order, count }: ExploreParams) {
// count within the last month
const countAfter = timeStringify(dayjs().subtract(1, 'month').valueOf())
const p: ListProjectParams = {
isPublic: IsPublic.public,
owner: ownerAll,
pageSize: count,
pageIndex: 1
}
switch (order) {
case ExploreOrder.MostLikes:
p.likesReceivedAfter = countAfter
p.orderBy = 'recentLikeCount'
p.sortOrder = 'desc'
break
case ExploreOrder.MostRemixes:
p.remixesReceivedAfter = countAfter
p.orderBy = 'recentRemixCount'
p.sortOrder = 'desc'
break
case ExploreOrder.FollowingCreated:
p.fromFollowees = true
p.createdAfter = countAfter
p.orderBy = 'cTime'
p.sortOrder = 'desc'
break
}
const listResult = await listProject(p)
return listResult.data
}

/**
* Check if given project liked by current logged-in user.
* If not logged in, `false` will be returned.
Expand Down
47 changes: 47 additions & 0 deletions spx-gui/src/components/community/CommunityHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!-- Simple header component for pages like search & explore -->

<template>
<section class="header">
<CenteredWrapper class="header-content">
<h1 class="title">
<slot></slot>
</h1>
<label class="options">
<slot name="options"></slot>
</label>
</CenteredWrapper>
</section>
</template>

<script setup lang="ts">
import CenteredWrapper from '@/components/community/CenteredWrapper.vue'
</script>

<style lang="scss" scoped>
@import '@/utils/utils';
.header {
flex: 0 0 auto;
background-color: var(--ui-color-grey-100);
}
.header-content {
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
.title {
flex: 1 1 0;
@include text-ellipsis;
line-height: 26px;
font-size: 16px;
color: var(--ui-color-title);
}
.options {
flex: 0 0 auto;
}
}
</style>
4 changes: 3 additions & 1 deletion spx-gui/src/components/community/CommunityNavbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
<NavbarWrapper centered>
<template #left>
<NavbarDropdown>
<template #trigger> Create(TODO) </template>
<template #trigger>
<UIIcon type="plus" />
</template>
<UIMenu>
<NavbarNewProjectItem />
<NavbarOpenProjectItem />
Expand Down
80 changes: 80 additions & 0 deletions spx-gui/src/components/community/ProjectsSection.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<!-- Project list as a section -->

<template>
<section>
<header class="header">
<h2 class="title">
<slot name="title"></slot>
</h2>
<RouterLink class="link" :to="linkTo">
<slot name="link"></slot>
<UIIcon class="link-icon" type="arrowRightSmall" />
</RouterLink>
</header>
<ul class="projects">
<UILoading v-if="queryRet.isLoading.value" :mask="false" />
<!-- TODO: simpler UIError & UIEmpty here -->
<UIError v-else-if="queryRet.error.value != null" :retry="queryRet.refetch">
{{ $t(queryRet.error.value.userMessage) }}
</UIError>
<UIEmpty
v-else-if="queryRet.data.value != null && queryRet.data.value.length === 0"
size="large"
/>
<template v-else-if="queryRet.data.value != null">
<slot></slot>
</template>
</ul>
</section>
</template>

<script setup lang="ts">
import type { QueryRet } from '@/utils/exception'
import { UIIcon, UILoading, UIError, UIEmpty } from '@/components/ui'
defineProps<{
linkTo: string
queryRet: QueryRet<unknown[]>
}>()
</script>

<style lang="scss" scoped>
.header {
height: 56px;
display: flex;
justify-content: space-between;
align-items: center;
.title {
line-height: 28px;
font-size: 20px;
color: var(--ui-color-title);
}
.link {
display: flex;
color: var(--ui-color-primary-main);
text-decoration: none;
}
.link-icon {
margin-left: 8px;
width: 20px;
height: 20px;
}
}
.projects {
padding: 20px 0;
margin-bottom: 12px;
// same height as `ProjectItem`, to prevent layout shift
min-height: 260px;
position: relative;
display: flex;
overflow: hidden;
align-items: center;
gap: 20px;
}
</style>
10 changes: 7 additions & 3 deletions spx-gui/src/components/navbar/NavbarProfile.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div v-if="!userStore.userInfo" class="sign-in">
<UIButton :disabled="!isOnline" @click="userStore.signInWithRedirection()">{{
<UIButton type="secondary" :disabled="!isOnline" @click="userStore.signInWithRedirection()">{{
$t({ en: 'Sign in', zh: '登录' })
}}</UIButton>
</div>
Expand Down Expand Up @@ -37,14 +37,18 @@ const { isOnline } = useNetwork()
<style lang="scss" scoped>
.sign-in,
.avatar {
margin: 0 8px;
height: 100%;
display: flex;
align-items: center;
}
.sign-in {
white-space: nowrap;
}
.avatar {
margin-right: 8px;
padding: 0 24px;
padding: 0 16px;
&:hover {
background-color: var(--ui-color-primary-600);
Expand Down
1 change: 1 addition & 0 deletions spx-gui/src/components/navbar/NavbarWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ withDefaults(
.right {
flex-basis: 30%;
display: flex;
gap: 8px;
}
.center {
Expand Down
20 changes: 14 additions & 6 deletions spx-gui/src/components/project/item/ProjectItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,16 @@ const handleRemove = useMessageHandle(
</script>

<style lang="scss" scoped>
@mixin text-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
@import '@/utils/utils';
.project-item {
width: 240px;
flex: 0 0 auto;
overflow: hidden;
border-radius: var(--ui-border-radius-2);
border: 1px solid var(--ui-color-grey-400);
background-color: var(--ui-color-grey-100);
transition: 0.1s;
}
.link {
Expand All @@ -159,6 +157,8 @@ const handleRemove = useMessageHandle(
}
.options {
opacity: 0;
visibility: hidden;
position: absolute;
top: 8px;
right: 8px;
Expand All @@ -171,7 +171,7 @@ const handleRemove = useMessageHandle(
color: var(--ui-color-grey-800);
background-color: var(--ui-color-grey-100);
cursor: pointer;
transition: 0.3s;
transition: 0.1s;
&:hover {
color: var(--ui-color-grey-100);
Expand All @@ -191,6 +191,14 @@ const handleRemove = useMessageHandle(
}
}
.project-item:hover {
box-shadow: 0px 4px 12px 0px rgba(36, 41, 47, 0.08);
.options {
visibility: visible;
opacity: 1;
}
}
.info {
padding: var(--ui-gap-middle);
Expand Down
1 change: 1 addition & 0 deletions spx-gui/src/components/ui/empty/UIEmpty.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ const slots = useSlots()
}
.size-large {
width: 100%;
flex-direction: column;
gap: 12px;
Expand Down
4 changes: 2 additions & 2 deletions spx-gui/src/components/ui/error/UIError.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="ui-error">
<img :src="defaultErrorImg" />
<h5 class="error">
<h5 class="message">
<slot></slot>
</h5>
<button v-if="retry != null" class="retry" @click="retry">{{ retryText }}</button>
Expand Down Expand Up @@ -32,7 +32,7 @@ const retryText = computed(() => config.error?.retryText ?? 'Retry')
justify-content: center;
}
.error {
.message {
margin-top: 12px;
font-size: 16px;
line-height: 26px;
Expand Down
4 changes: 3 additions & 1 deletion spx-gui/src/components/ui/icons/UIIcon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import volumeOff from './volume-off.svg?raw'
import statePublic from './state-public.svg?raw'
import statePrivate from './state-private.svg?raw'
import heart from './heart.svg?raw'
import arrowRightSmall from './arrow-right-small.svg?raw'
const typeIconMap = {
file,
Expand Down Expand Up @@ -70,7 +71,8 @@ const typeIconMap = {
volumeOff,
statePublic,
statePrivate,
heart
heart,
arrowRightSmall
}
export type Type = keyof typeof typeIconMap
Expand Down
3 changes: 3 additions & 0 deletions spx-gui/src/components/ui/icons/arrow-right-small.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 2db9b6c

Please sign in to comment.