Skip to content

Commit

Permalink
Pages' title (#1023)
Browse files Browse the repository at this point in the history
* clear search input when left from search page

* comment update for field thumbnail of project

* adjust order for project extra info

* page titles

* use displayName instead of name for user

* typo

* sync search input from URL query
  • Loading branch information
nighca authored Oct 25, 2024
1 parent 68e96bc commit 0189fb8
Show file tree
Hide file tree
Showing 20 changed files with 203 additions and 35 deletions.
2 changes: 1 addition & 1 deletion spx-gui/src/apis/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export type ProjectData = {
description: string
/** Instructions on how to interact with the project */
instructions: string
/** Universal URL of the project's thumbnail image */
/** Universal URL of the project's thumbnail image, may be empty (`""`) */
thumbnail: string
/** Number of times the project has been viewed */
viewCount: number
Expand Down
26 changes: 25 additions & 1 deletion spx-gui/src/components/community/CommunityNavbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { getStringParam } from '@/utils/route'
import { UIMenu, UITextInput, UIIcon } from '@/components/ui'
import NavbarWrapper from '@/components/navbar/NavbarWrapper.vue'
import NavbarDropdown from '../navbar/NavbarDropdown.vue'
import NavbarNewProjectItem from '@/components/navbar/NavbarNewProjectItem.vue'
import NavbarOpenProjectItem from '@/components/navbar/NavbarOpenProjectItem.vue'
import { searchKeywordQueryParamName } from '@/pages/community/search.vue'
import { getSearchRoute } from '@/router'
const router = useRouter()
Expand All @@ -41,6 +43,28 @@ const searchInput = ref('')
function handleSearch() {
router.push(getSearchRoute(searchInput.value))
}
// Clear search input when leaving search page
watch(
() => router.currentRoute.value.meta.isSearch,
(isSearch, oldIsSearch) => {
if (oldIsSearch && !isSearch) searchInput.value = ''
}
)
// Sync search input from query
watch(
() => router.currentRoute.value,
(r) => {
if (r.meta.isSearch) {
const keywordInQuery = getStringParam(router, searchKeywordQueryParamName)
if (keywordInQuery != null && keywordInQuery !== searchInput.value) {
searchInput.value = keywordInQuery
}
}
},
{ immediate: true }
)
</script>

<style lang="scss" scoped>
Expand Down
2 changes: 1 addition & 1 deletion spx-gui/src/components/community/project/OwnerInfo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const { data: user } = useUser(() => props.owner)
<template>
<UserLink class="owner-info" :user="user?.username ?? null">
<i class="avatar" :style="user != null ? { backgroundImage: `url(${user.avatar})` } : null"></i>
{{ owner }}
{{ user?.displayName }}
</UserLink>
</template>

Expand Down
19 changes: 16 additions & 3 deletions spx-gui/src/components/community/project/RemixedFrom.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
<script setup lang="ts">
import { computed } from 'vue'
import { parseRemixSource, stringifyRemixSource } from '@/apis/project'
import { parseRemixSource } from '@/apis/project'
import { getProjectPageRoute } from '@/router'
import { useUser } from '@/stores/user'
import RouterUILink from '@/components/common/RouterUILink.vue'
const props = defineProps<{
remixedFrom: string
}>()
const remixedFrom = computed(() => parseRemixSource(props.remixedFrom))
const { data: owner } = useUser(() => remixedFrom.value.owner)
const title = computed(() => {
if (owner.value == null) return null
return {
en: `${remixedFrom.value.project} created by ${owner.value.displayName}`,
zh: `${remixedFrom.value.project},由 ${owner.value.displayName} 创建`
}
})
</script>

<template>
<p class="remixed-from">
{{ $t({ en: 'Remixed from', zh: '改编自' }) }}
<RouterUILink :to="getProjectPageRoute(remixedFrom.owner, remixedFrom.project)" type="boring">
{{ stringifyRemixSource(remixedFrom.owner, remixedFrom.project) }}
<RouterUILink
:to="getProjectPageRoute(remixedFrom.owner, remixedFrom.project)"
type="boring"
:title="$t(title)"
>
{{ remixedFrom.project }}
</RouterUILink>
</p>
</template>
2 changes: 1 addition & 1 deletion spx-gui/src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createI18n, type Lang } from './utils/i18n'

const LOCALSTORAGE_KEY_LANGUAGE = 'spx-gui-language'

export const initI18n = async (app: App) => {
export const initI18n = (app: App) => {
const currentLanguage = (localStorage.getItem(LOCALSTORAGE_KEY_LANGUAGE) || 'en') as Lang
const i18n = createI18n({ lang: currentLanguage })

Expand Down
4 changes: 2 additions & 2 deletions spx-gui/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ async function initApp() {

initUserStore(app)
initApiClient()
await initRouter(app)
await initI18n(app)
initRouter(app)
initI18n(app)

app.use(VueKonva as any, {
customNodes: { CustomTransformer }
Expand Down
1 change: 1 addition & 0 deletions spx-gui/src/models/project/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export class Project extends Disposable {
visibility?: Visibility
description?: string
instructions?: string
/** Universal URL of the project's thumbnail image, may be empty (`""`) */
thumbnail?: string
viewCount?: number
likeCount?: number
Expand Down
15 changes: 12 additions & 3 deletions spx-gui/src/pages/community/explore.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
<template #options>
<UITagRadioGroup v-model:value="order">
<UITagRadio :value="Order.MostLikes">
{{ $t({ en: 'Most recent likes', zh: '最近最受喜欢' }) }}
{{ $t(titles[Order.MostLikes]) }}
</UITagRadio>
<UITagRadio :value="Order.MostRemixes">
{{ $t({ en: 'Most recent remixes', zh: '最近最多改编' }) }}
{{ $t(titles[Order.MostRemixes]) }}
</UITagRadio>
<UITagRadio :value="Order.FollowingCreated">
{{ $t({ en: 'My following created', zh: '你关注的用户创作' }) }}
{{ $t(titles[Order.FollowingCreated]) }}
</UITagRadio>
</UITagRadioGroup>
</template>
Expand All @@ -27,6 +27,7 @@
<script setup lang="ts">
import { useQuery } from '@/utils/query'
import { useRouteQueryParamStrEnum } from '@/utils/route'
import { usePageTitle } from '@/utils/utils'
import { exploreProjects, ExploreOrder as Order } from '@/apis/project'
import { UITagRadioGroup, UITagRadio } from '@/components/ui'
import ListResultWrapper from '@/components/common/ListResultWrapper.vue'
Expand All @@ -37,6 +38,14 @@ import { useEnsureSignedIn } from '@/utils/user'
const order = useRouteQueryParamStrEnum('o', Order, Order.MostLikes)
const titles = {
[Order.MostLikes]: { en: 'Most recent likes', zh: '最近最受喜欢' },
[Order.MostRemixes]: { en: 'Most recent remixes', zh: '最近最多改编' },
[Order.FollowingCreated]: { en: 'My following created', zh: '你关注的用户创作' }
}
usePageTitle(() => titles[order.value])
const maxCount = 50
const ensureSignedIn = useEnsureSignedIn()
Expand Down
3 changes: 3 additions & 0 deletions spx-gui/src/pages/community/home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { useMessageHandle } from '@/utils/exception'
import { useQuery } from '@/utils/query'
import { usePageTitle } from '@/utils/utils'
import { ExploreOrder, exploreProjects, listProject } from '@/apis/project'
import { getExploreRoute, getProjectEditorRoute, getUserPageRoute } from '@/router'
import { useUserStore } from '@/stores/user'
Expand All @@ -165,6 +166,8 @@ import { useCreateProject } from '@/components/project'
import ProjectItem from '@/components/project/ProjectItem.vue'
import newProjectIcon from '@/components/navbar/icons/new.svg'
usePageTitle([])
const isDesktopLarge = useResponsive('desktop-large')
const numInRow = computed(() => (isDesktopLarge.value ? 5 : 4))
Expand Down
29 changes: 23 additions & 6 deletions spx-gui/src/pages/community/project.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { useQuery } from '@/utils/query'
import { useIsLikingProject, useLikeProject, useUnlikeProject } from '@/stores/liking'
import { humanizeCount, humanizeExactCount, untilNotNull } from '@/utils/utils'
import { useEnsureSignedIn } from '@/utils/user'
import { usePageTitle } from '@/utils/utils'
import { ownerAll, recordProjectView, stringifyRemixSource, Visibility } from '@/apis/project'
import { listProject } from '@/apis/project'
import { Project } from '@/models/project'
import { useUserStore } from '@/stores/user'
import { useUser, useUserStore } from '@/stores/user'
import { getProjectEditorRoute, getUserPageRoute } from '@/router'
import {
UIIcon,
Expand Down Expand Up @@ -66,6 +67,22 @@ const {
}
)
const { data: ownerInfo } = useUser(() => props.owner)
usePageTitle(() => {
if (ownerInfo.value == null) return null
return [
{
en: props.name,
zh: props.name
},
{
en: ownerInfo.value.displayName,
zh: ownerInfo.value.displayName
}
]
})
watch(
() => [props.owner, props.name],
() => userStore.isSignedIn() && recordProjectView(props.owner, props.name),
Expand Down Expand Up @@ -309,17 +326,17 @@ const remixesRet = useQuery(
<div class="info">
<OwnerInfo :owner="project.owner!" />
<p class="extra">
<span class="part" :title="$t(viewCount!.title)">
<UIIcon type="eye" />
{{ $t(viewCount!.text) }}
</span>
<template v-if="isOwner">
<i class="sep"></i>
<span class="part" :title="$t(likeCount!.title)">
<UIIcon type="heart" />
{{ $t(likeCount!.text) }}
</span>
<i class="sep"></i>
</template>
<span class="part" :title="$t(viewCount!.title)">
<UIIcon type="eye" />
{{ $t(viewCount!.text) }}
</span>
<i class="sep"></i>
<span class="part" :title="$t(remixCount!.title)">
<UIIcon type="remix" />
Expand Down
13 changes: 12 additions & 1 deletion spx-gui/src/pages/community/search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
</CenteredWrapper>
</template>

<script lang="ts">
// `?q=123`
export const searchKeywordQueryParamName = 'q'
</script>

<script setup lang="ts">
import { computed } from 'vue'
import {
Expand All @@ -65,13 +70,19 @@ import {
} from '@/utils/route'
import { useQuery } from '@/utils/query'
import { Visibility, listProject, ownerAll, type ListProjectParams } from '@/apis/project'
import { usePageTitle } from '@/utils/utils'
import { UISelect, UISelectOption, UIPagination, useResponsive } from '@/components/ui'
import ListResultWrapper from '@/components/common/ListResultWrapper.vue'
import CenteredWrapper from '@/components/community/CenteredWrapper.vue'
import CommunityHeader from '@/components/community/CommunityHeader.vue'
import ProjectItem from '@/components/project/ProjectItem.vue'
const keyword = useRouteQueryParamStr('q', '')
usePageTitle({
en: 'Project search results',
zh: '项目搜索结果'
})
const keyword = useRouteQueryParamStr(searchKeywordQueryParamName, '')
const isDesktopLarge = useResponsive('desktop-large')
const numInRow = computed(() => (isDesktopLarge.value ? 5 : 4))
Expand Down
13 changes: 12 additions & 1 deletion spx-gui/src/pages/community/user/followers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import { computed } from 'vue'
import { useRouteQueryParamInt } from '@/utils/route'
import { useQuery } from '@/utils/query'
import { usePageTitle } from '@/utils/utils'
import { listUsers } from '@/apis/user'
import { useUser } from '@/stores/user'
import { UIPagination } from '@/components/ui'
import ListResultWrapper from '@/components/common/ListResultWrapper.vue'
import UserContent from '@/components/community/user/content/UserContent.vue'
Expand All @@ -12,6 +14,15 @@ const props = defineProps<{
name: string
}>()
const { data: user } = useUser(() => props.name)
usePageTitle(() => {
if (user.value == null) return null
return {
en: `Followers of ${user.value.displayName}`,
zh: `${user.value.displayName} 的关注者`
}
})
const pageSize = 8
const page = useRouteQueryParamInt('p', 1)
const pageTotal = computed(() => Math.ceil((queryRet.data.value?.total ?? 0) / pageSize))
Expand Down Expand Up @@ -39,7 +50,7 @@ const queryRet = useQuery(
</template>
<ListResultWrapper v-slot="slotProps" :query-ret="queryRet" :height="496">
<ul class="users">
<UserItem v-for="user in slotProps.data.data" :key="user.id" :user="user" />
<UserItem v-for="u in slotProps.data.data" :key="u.id" :user="u" />
</ul>
</ListResultWrapper>
<UIPagination
Expand Down
13 changes: 12 additions & 1 deletion spx-gui/src/pages/community/user/following.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import { computed } from 'vue'
import { useRouteQueryParamInt } from '@/utils/route'
import { useQuery } from '@/utils/query'
import { usePageTitle } from '@/utils/utils'
import { listUsers } from '@/apis/user'
import { useUser } from '@/stores/user'
import { UIPagination } from '@/components/ui'
import ListResultWrapper from '@/components/common/ListResultWrapper.vue'
import UserContent from '@/components/community/user/content/UserContent.vue'
Expand All @@ -12,6 +14,15 @@ const props = defineProps<{
name: string
}>()
const { data: user } = useUser(() => props.name)
usePageTitle(() => {
if (user.value == null) return null
return {
en: `${user.value.displayName} is following`,
zh: `${user.value.displayName} 关注的用户`
}
})
const pageSize = 8
const page = useRouteQueryParamInt('p', 1)
const pageTotal = computed(() => Math.ceil((queryRet.data.value?.total ?? 0) / pageSize))
Expand Down Expand Up @@ -39,7 +50,7 @@ const queryRet = useQuery(
</template>
<ListResultWrapper v-slot="slotProps" :query-ret="queryRet" :height="496">
<ul class="users">
<UserItem v-for="user in slotProps.data.data" :key="user.id" :user="user" />
<UserItem v-for="u in slotProps.data.data" :key="u.id" :user="u" />
</ul>
</ListResultWrapper>
<UIPagination
Expand Down
11 changes: 11 additions & 0 deletions spx-gui/src/pages/community/user/likes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import { computed } from 'vue'
import { useRouteQueryParamInt } from '@/utils/route'
import { useQuery } from '@/utils/query'
import { usePageTitle } from '@/utils/utils'
import { Visibility, listProject, ownerAll } from '@/apis/project'
import { useUser } from '@/stores/user'
import { UIPagination, useResponsive } from '@/components/ui'
import ListResultWrapper from '@/components/common/ListResultWrapper.vue'
import UserContent from '@/components/community/user/content/UserContent.vue'
Expand All @@ -12,6 +14,15 @@ const props = defineProps<{
name: string
}>()
const { data: user } = useUser(() => props.name)
usePageTitle(() => {
if (user.value == null) return null
return {
en: `Projects ${user.value.displayName} likes`,
zh: `${user.value.displayName} 喜欢的项目`
}
})
const isDesktopLarge = useResponsive('desktop-large')
const numInRow = computed(() => (isDesktopLarge.value ? 5 : 4))
const pageSize = computed(() => numInRow.value * 2)
Expand Down
11 changes: 11 additions & 0 deletions spx-gui/src/pages/community/user/overview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { useResponsive } from '@/components/ui'
import CommunityCard from '@/components/community/CommunityCard.vue'
import ProjectsSection from '@/components/community/ProjectsSection.vue'
import ProjectItem from '@/components/project/ProjectItem.vue'
import { useUser } from '@/stores/user'
import { usePageTitle } from '@/utils/utils'
const props = defineProps<{
name: string
Expand All @@ -15,6 +17,15 @@ const props = defineProps<{
const isDesktopLarge = useResponsive('desktop-large')
const numInRow = computed(() => (isDesktopLarge.value ? 5 : 4))
const { data: user } = useUser(() => props.name)
usePageTitle(() => {
if (user.value == null) return null
return {
en: `User ${user.value.displayName}`,
zh: `用户 ${user.value.displayName}`
}
})
const projectsRoute = computed(() => {
return getUserPageRoute(props.name, 'projects')
})
Expand Down
Loading

0 comments on commit 0189fb8

Please sign in to comment.