Merge branch 'master' into improve-request

This commit is contained in:
mutoe 2020-10-19 19:04:49 +08:00 committed by GitHub
commit fe45801e9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 228 additions and 66 deletions

View File

@ -0,0 +1,29 @@
<template>
<router-link
:to="to"
v-bind="attrs"
>
<slot />
</router-link>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue'
import type { RouteParams } from 'vue-router'
import type { AppRouteNames } from '../routes'
export default defineComponent({
name: 'AppLink',
props: {
name: { type: String as PropType<AppRouteNames>, required: true },
params: { type: Object as PropType<RouteParams>, default: () => ({}) },
},
setup (props, { attrs }) {
return {
to: props,
attrs,
}
},
})
</script>

View File

@ -7,24 +7,26 @@
</div>
<div class="card-footer">
<RouterLink
:to="`/profile/${comment.author.username}`"
<AppLink
name="profile"
:params="{username: comment.author.username}"
class="comment-author"
>
<img
:src="comment.author.image"
class="comment-author-img"
>
</RouterLink>
</AppLink>
&nbsp;
<RouterLink
:to="`/profile/${comment.author.username}`"
<AppLink
name="profile"
:params="{username: comment.author.username}"
class="comment-author"
>
{{ comment.author.username }}
</RouterLink>
</AppLink>
<span class="date-posted">{{ (new Date(comment.createdAt)).toLocaleDateString() }}</span>

View File

@ -3,12 +3,13 @@
<a href=""><img :src="article.author?.image"></a>
<div class="info">
<RouterLink
:to="`/profile/${article.author?.username}`"
<AppLink
name="profile"
:params="{username: article.author?.username}"
class="author"
>
{{ article.author?.username }}
</RouterLink>
</AppLink>
<span class="date">{{ (new Date(article.createdAt)).toLocaleDateString() }}</span>
</div>
@ -30,12 +31,13 @@
&nbsp;&nbsp;
<RouterLink
<AppLink
class="btn btn-outline-secondary btn-sm"
:to="`/editor/${article.slug}`"
name="editor"
:params="{slug: article.slug}"
>
<i class="ion-edit" /> Edit Article
</RouterLink>
</AppLink>
&nbsp;&nbsp;

View File

@ -1,30 +1,35 @@
<template>
<div class="article-preview">
<div class="article-meta">
<RouterLink :to="`/profile/${article.author.username}`">
<AppLink
name="profile"
:params="{username: article.author.username}"
>
<img :src="article.author.image">
</RouterLink>
</AppLink>
<div class="info">
<RouterLink
:to="`/profile/${article.author.username}`"
<AppLink
name="profile"
:params="{username: article.author.username}"
class="author"
>
{{ article.author.username }}
</RouterLink>
</AppLink>
<span class="date">{{ new Date(article.createdAt).toDateString() }}</span>
</div>
<button class="btn btn-outline-primary btn-sm pull-xs-right">
<i class="ion-heart" /> {{ article.favoritesCount }}
</button>
</div>
<RouterLink
:to="`/article/${article.slug}`"
<AppLink
name="article"
:params="{slug: article.slug}"
class="preview-link"
>
<h1>{{ article.title }}</h1>
<p>{{ article.description }}</p>
<span>Read more...</span>
</RouterLink>
</AppLink>
</div>
</template>

View File

@ -5,28 +5,32 @@
:key="link.type"
class="nav-item"
>
<RouterLink
<AppLink
class="nav-link"
active-class="active"
:to="link.href"
:name="link.name"
:params="link.params"
>
<i
v-if="link.icon"
:class="link.icon"
/> {{ link.title }}
</RouterLink>
</AppLink>
</li>
</ul>
</template>
<script lang="ts">
import { computed, defineComponent, toRefs } from 'vue'
import type { RouteParams } from 'vue-router'
import type { AppRouteNames } from '../routes'
type ArticleNavLinkType = 'globalFeed' | 'myFeed' | 'tag' | 'author' | 'favorited'
interface ArticleNavLink {
name: AppRouteNames
params?: Partial<RouteParams>
type: ArticleNavLinkType
href: string
title: string
icon?: string
}
@ -44,11 +48,36 @@ export default defineComponent({
const { useGlobalFeed, useMyFeed, useTag, useAuthor, useFavorited } = toRefs(props)
const allLinks = computed<ArticleNavLink[]>(() => [
{ type: 'globalFeed', href: '/', title: 'Global Feed' },
{ type: 'myFeed', href: '/my-feeds', title: 'Your Feed' },
{ type: 'tag', href: `/tag/${useTag.value}`, title: useTag.value, icon: 'ion-pound' },
{ type: 'author', href: `/profile/${useAuthor.value}`, title: 'My articles' },
{ type: 'favorited', href: `/profile/${useFavorited.value}/favorites`, title: 'Favorited Articles' },
{
type: 'globalFeed',
name: 'global-feed',
title: 'Global Feed',
},
{
type: 'myFeed',
name: 'my-feed',
title: 'Your Feed',
},
{
type: 'author',
name: 'profile',
params: { username: useAuthor.value },
title: 'My articles',
},
{
type: 'tag',
name: 'tag',
params: { tag: useTag.value },
title: useTag.value,
icon: 'ion-pound',
},
{
type: 'favorited',
name: 'profile-favorites',
params: { username: useFavorited.value },
title: 'Favorited Articles',
},
])
const show = computed<Record<ArticleNavLinkType, boolean>>(() => ({

View File

@ -1,12 +1,12 @@
<template>
<footer>
<div class="container">
<RouterLink
to="/"
<AppLink
name="global-feed"
class="logo-font"
>
conduit
</RouterLink>
</AppLink>
<span class="attribution">
An interactive learning project from <a href="https://thinkster.io">Thinkster</a>. Code &amp; design licensed under MIT.
</span>

View File

@ -1,29 +1,30 @@
<template>
<nav class="navbar navbar-light">
<div class="container">
<RouterLink
<AppLink
class="navbar-brand"
to="/"
name="global-feed"
>
conduit
</RouterLink>
</AppLink>
<ul class="nav navbar-nav pull-xs-right">
<li
v-for="link in navLinks"
:key="link.to"
:key="link.name"
class="nav-item"
>
<RouterLink
<AppLink
class="nav-link"
active-class="active"
:to="link.to"
:name="link.name"
:params="link.params"
>
<i
v-if="link.icon"
:class="link.icon"
/> {{ link.title }}
</RouterLink>
</AppLink>
</li>
<!-- TODO: remove logout link -->
<li

View File

@ -1,11 +1,14 @@
import { computed, Ref } from 'vue'
import type { RouteParams } from 'vue-router'
import type { AppRouteNames } from '../routes'
interface UseLinksProps {
user: Ref<User | null>
}
interface NavLink {
to: string
name: AppRouteNames
params?: Partial<RouteParams>
title: string
icon?: string
display: 'all' | 'anonym' | 'authorized'
@ -16,12 +19,39 @@ export function useNavigationLinks ({ user }: UseLinksProps) {
const displayStatus = computed(() => username.value ? 'authorized' : 'anonym')
const allNavLinks: NavLink[] = [
{ to: '/', title: 'Home', display: 'all' },
{ to: '/login', title: 'Sign in', display: 'anonym' },
{ to: '/register', title: 'Sign up', display: 'anonym' },
{ to: '/editor', title: 'New Post', display: 'authorized', icon: 'ion-compose' },
{ to: '/settings', title: 'Settings', display: 'authorized', icon: 'ion-gear-a' },
{ to: `/profile/${username.value}`, title: username.value || '', display: 'authorized' },
{
name: 'global-feed',
title: 'Home',
display: 'all',
},
{
name: 'login',
title: 'Sign in',
display: 'anonym',
},
{
name: 'register',
title: 'Sign up',
display: 'anonym',
},
{
name: 'editor',
title: 'New Post',
display: 'authorized',
icon: 'ion-compose',
},
{
name: 'settings',
title: 'Settings',
display: 'authorized',
icon: 'ion-gear-a',
},
{
name: 'profile',
params: { username: username.value },
title: username.value || '',
display: 'authorized',
},
]
const navLinks = computed(() => allNavLinks.filter(

View File

@ -2,14 +2,15 @@
<p>Popular Tags</p>
<div class="tag-list">
<RouterLink
<AppLink
v-for="tag in tags"
:key="tag"
:to="`/tag/${tag}`"
name="tag"
:params="{tag}"
class="tag-pill tag-default"
>
{{ tag }}
</RouterLink>
</AppLink>
</div>
</template>

View File

@ -2,14 +2,17 @@ import router from './routes'
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
import registerGlobalComponents from './plugins/global-components'
import { request } from './services'
import parseStorageGet from './utils/parse-storage-get'
const token = parseStorageGet('user')?.token
request.setAuthorizationHeader(token)
createApp(App)
.use(router)
.use(store)
.mount('#app')
const app = createApp(App)
app.use(router)
app.use(store)
registerGlobalComponents(app)
app.mount('#app')

View File

@ -7,9 +7,9 @@
Sign in
</h1>
<p class="text-xs-center">
<RouterLink to="/register">
<AppLink name="register">
Need an account?
</RouterLink>
</AppLink>
</p>
<ul class="error-messages">

View File

@ -7,9 +7,9 @@
Sign up
</h1>
<p class="text-xs-center">
<RouterLink to="/login">
<AppLink name="login">
Have an account?
</RouterLink>
</AppLink>
</p>
<ul class="error-messages">

View File

@ -0,0 +1,7 @@
import type { App } from 'vue'
import AppLink from '../components/AppLink.vue'
export default function registerGlobalComponents (app: App) {
app.component('AppLink', AppLink)
}

View File

@ -1,17 +1,70 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import Home from './pages/Home.vue'
export type AppRouteNames = 'global-feed'
|'my-feed'
|'tag'
|'article'
|'login'
|'register'
|'profile'
|'profile-favorites'
|'editor'
|'settings'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/my-feeds', component: Home },
{ path: '/tag/:tag', component: Home },
{ path: '/article/:slug', component: () => import('./pages/Article.vue') },
{ path: '/login', component: () => import('./pages/Login.vue') },
{ path: '/register', component: () => import('./pages/Register.vue') },
{ path: '/profile/:username', component: () => import('./pages/Profile.vue') },
{ path: '/profile/:username/favorites', component: () => import('./pages/Profile.vue') },
{
name: 'global-feed',
path: '/',
component: Home,
},
{
name: 'my-feed',
path: '/my-feeds',
component: Home,
},
{
name: 'tag',
path: '/tag/:tag',
component: Home,
},
{
name: 'article',
path: '/article/:slug',
component: () => import('./pages/Article.vue'),
},
{
name: 'login',
path: '/login',
component: () => import('./pages/Login.vue'),
},
{
name: 'register',
path: '/register',
component: () => import('./pages/Register.vue'),
},
{
name: 'profile',
path: '/profile/:username',
component: () => import('./pages/Profile.vue'),
},
{
name: 'profile-favorites',
path: '/profile/:username/favorites',
component: () => import('./pages/Profile.vue'),
},
{
name: 'editor',
path: '/editor',
redirect: '/',
},
{
name: 'settings',
path: '/settings',
redirect: '/',
},
],
})