Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ブログ著者とタグのアイコン指定方法を変更 #52

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 45 additions & 53 deletions README_BLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

OUCC BLOG の仕様について記載しています。何もわからない場合はサーバー係に投稿したい Markdown ファイルを渡してください。

ブログの投稿を行う際はブランチ名が `blog/` で始まるブランチを作成し作業してください。このブランチではブログの投稿に関する作業以外は禁止されていますが、後述するようにCIによる支援が得られます
ブログの投稿を行う際はブランチ名が `blog/` で始まるブランチを作成し作業してください。このブランチではブログの投稿に関する作業以外は禁止されていますが、後述するように投稿日時や更新日時が自動で生成されます

ブログに関して作業することを明示しつつこの制約を受けたくない場合は、ブランチ名が `blog/admin/` で始まるブランチで作業してください。

Expand All @@ -20,6 +20,8 @@ tags:
- タグ2
- タグ3
---

これは記事本文です。Markdownで文章を書くことができます。
```

画像ファイルは `src/content/blogs` に新しいディレクトリを作成してそこに配置するか、または外部においてURLで指定してください。
Expand All @@ -28,53 +30,48 @@ tags:

## 記事のメタ情報

記事のメタ情報には投稿日時と更新日時が含まれています。これは `blog/` から始まるブランチで Pull Request を出すと自動的に更新されます
`src/content/blog-metas`にある記事のメタ情報には投稿日時と更新日時が含まれています。これは `blog/` から始まるブランチで Pull Request を出すと自動的に作成・更新されます

以下のようにコマンドを使うことで手動で更新することもできます。

```bash
$ npm run update-blogmeta -- src/content/blogs/NEW-POST1.md src/content/blogs/NEW-POST2.md
```

## 著者
## 著者の追加

`src/content/authors` に JSON ファイルを追加することで著者ページを作ることができます。ファイル名は記事の `author` で指定する際の値となるので自身のIDなどわかりやすい名前にしてください。

`src/content/authors` に JSON ファイルを追加することで新しいタグを作ることができます。ファイル名は記事の `author` で指定する際の値となるのでわかりやすい名前にすることをおすすめします。
著者のスキーマは以下のとおりです。`name` 以外のプロパティは省略可能です。

- name : 著者の表示名
- name : 著者の表示名(必須)
- description : 著者の説明
- github : GitHub アカウントの ID (image を指定しなかった場合、こちらで指定したアカウントのアイコンが使用されます。)
- image : 著者のアイコン
- `svg` は `src/assets/icons/blog` に入っているsvgファイルを指定できます。`name` には拡張子を除いたファイル名を指定してください。
- `external-url` は外部の画像を指定できます。

```ts
interface Author {
name: string
description?: string
github?: string
image?:
| {
type: 'svg'
name: string
}
| {
type: 'external-url'
url: string
}
- github : GitHub アカウントの ID

Example: `src/content/authors/octocat.json`

```json
{
"name": "Octocat",
"description": "GitHubの猫です。",
"github": "octocat"
}
```

## タグ
### 著者のアイコン(任意)

`src/content/authors`にJSONと同じファイル名の画像ファイルを置くことでアイコンを変更できます。ファイル形式はSVGまたはPNGです。例えばJSONが`octocat.json`の場合、アイコンは`octocat.svg`または`octocat.png`としてください。

なお、画像ファイルが存在しない場合はGitHubのアイコンが使用されます。

## タグの追加

`src/content/tags` に JSON ファイルを追加することで新しいタグを作ることができます。ファイル名は記事の `tags` で指定する際の値となるのでわかりやすい名前にすることをおすすめします。

タグのスキーマは以下のとおりです。 `name` 以外のプロパティは省略可能です。

- name : タグの表示名
- name : タグの表示名(必須)
- description : タグの説明
- image : タグのアイコン
- `svg` は `src/assets/icons/blog` に入っているsvgファイルを指定できます。`name` には拡張子を除いたファイル名を指定してください。
- `external-url` は外部の画像を指定できます。
- site : 公式サイト
- url : 公式サイトのURL
- text : リンクの表示名 (指定のない場合 `タグ名 - 公式サイト` となります)
Expand All @@ -85,30 +82,25 @@ interface Author {
- url : GitHubのリポジトリのURL
- text : リンクの表示名 (指定のない場合 `タグ名 - GitHub` となります)

```ts
interface Tag {
name: string
description?: string
image?:
| {
type: 'svg'
name: string
}
| {
type: 'external-url'
url: string
}
site?: {
url: string
text?: string
}
document?: {
url: string
text?: string
}
github?: {
url: string
text?: string
Example: `src/content/tags/csharp.json`

```json
{
"name": "C#",
"description": "C#は、最新のタイプ セーフなオブジェクト指向のプログラミング言語です。 開発者は C# を使用することにより、.NET で稼働する、安全かつ堅牢な多くの種類のアプリケーションを構築できます。",
"site": {
"url": "https://dotnet.microsoft.com/ja-jp/learn/dotnet/what-is-dotnet"
},
"document": {
"url": "https://learn.microsoft.com/ja-jp/dotnet/csharp/programming-guide/"
},
"github": {
"url": "https://github.com/dotnet/runtime",
"text": ".NET Runtime - GitHub"
}
}
```

### タグのアイコン(任意)

`src/content/tags`にJSONと同じファイル名の画像ファイルを置くことでアイコンを変更できます。ファイル形式はSVGまたはPNGです。例えばJSONが`csharp.json`の場合、アイコンは`csharp.svg`または`csharp.png`としてください。
5 changes: 5 additions & 0 deletions src/assets/avatar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/hashtag.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 0 additions & 5 deletions src/assets/icons/blog/default_user.svg

This file was deleted.

18 changes: 0 additions & 18 deletions src/assets/icons/blog/hashtag.svg

This file was deleted.

2 changes: 1 addition & 1 deletion src/components/blog/BlogContent.astro
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const { Content } = await blog.render()
<a
class="hover:underline flex gap-2 items-center"
href={`/blog/authors/${author.id}`}
><AuthorIcon {...author.data} size={28} />{author.data.name}</a
><AuthorIcon author={author} size={28} />{author.data.name}</a
>
</div>
<TagSmallList tags={tags} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/blog/BlogListItem.astro
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const tags = await getEntries(blog.tags)
<div class="px-2 flex gap-3 text-gray-600">
<div class="hover:underline">
<a href={`/blog/authors/${author.id}`} class="flex gap-2 items-center"
><AuthorIcon {...author.data} size={22} />{author.data.name}</a
><AuthorIcon author={author} size={22} />{author.data.name}</a
>
</div>
<time datetime={(blog.updateDate ?? blog.postDate).toISOString()}>
Expand Down
67 changes: 33 additions & 34 deletions src/components/blog/icon/AuthorIcon.astro
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
---
import Icon from '@/components/common/Icon.astro'
import { unreachable } from '@/utils/unreachable'
import type { ImageMetadata } from 'astro'
import type { CollectionEntry } from 'astro:content'
type Props = CollectionEntry<'authors'>['data'] & { size: number }
import defaultImage from '@/assets/avatar.svg'
import Image from '@/components/common/Image.astro'
import { unreachable } from '@/utils/unreachable'
import { getAuthorIcon } from './getAuthorIcon'

interface Props {
author: CollectionEntry<'authors'>
size: number
}

const { size, ...author } = Astro.props
const { author, size } = Astro.props

const image =
author.image.type === 'svg' &&
author.image.name === 'default_user' &&
author.github
? ({
type: 'external-url',
url: `https://github.com/${author.github}.png`,
} as const)
: author.image
const getSrc = async (): Promise<string | ImageMetadata> => {
const icon = await getAuthorIcon(author)
switch (icon.type) {
case 'local-svg':
return icon.imageMetadata
case 'local-png':
return icon.imageMetadata
case 'external-url':
return icon.url
case 'default':
return defaultImage
default:
return unreachable(icon)
}
}
---

{
image.type === 'svg' ? (
<Icon
name={image.name}
alt={`${image.name} のアイコン`}
class="aspect-square rounded-full"
height={size}
width={size}
isBlog
/>
) : image.type === 'external-url' ? (
<img
src={image.url}
height={size}
width={size}
class="aspect-square rounded-full"
/>
) : (
unreachable(image)
)
}
<Image
src={await getSrc()}
alt={`${author.data.name} のアイコン`}
class="aspect-square rounded-full"
width={size}
height={size}
/>
51 changes: 31 additions & 20 deletions src/components/blog/icon/TagIcon.astro
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
---
import Icon from '@/components/common/Icon.astro'
import { unreachable } from '@/utils/unreachable'
import type { ImageMetadata } from 'astro'
import type { CollectionEntry } from 'astro:content'
import defaultImage from '@/assets/hashtag.svg'
import Image from '@/components/common/Image.astro'
import { unreachable } from '@/utils/unreachable'
import { getTagIcon } from './getTagIcon'

interface Props {
tagImage: CollectionEntry<'tags'>['data']['image']
tag: CollectionEntry<'tags'>
size: number
}

const { tagImage, size } = Astro.props
---
const { tag, size } = Astro.props

{
tagImage.type === 'svg' ? (
<Icon
name={tagImage.name}
alt={`${tagImage.name} のアイコン`}
class="aspect-square"
width={size}
height={size}
isBlog
/>
) : tagImage.type === 'external-url' ? (
<img src={tagImage.url} height={size} width={size} class="aspect-square" />
) : (
unreachable(tagImage)
)
const getSrc = async (): Promise<string | ImageMetadata> => {
const icon = await getTagIcon(tag)
switch (icon.type) {
case 'local-svg':
return icon.imageMetadata
case 'local-png':
return icon.imageMetadata
case 'external-url':
return icon.url
case 'default':
return defaultImage
default:
return unreachable(icon)
}
}
---

<Image
src={await getSrc()}
alt={`${tag.data.name} のアイコン`}
class="aspect-square"
width={size}
height={size}
/>
55 changes: 55 additions & 0 deletions src/components/blog/icon/getAuthorIcon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { ImageMetadata } from 'astro'
import type { CollectionEntry } from 'astro:content'

export type AuthorIcon =
| {
type: 'local-svg'
imageMetadata: ImageMetadata
svg: string
}
| {
type: 'local-png'
imageMetadata: ImageMetadata
url: string
}
| {
type: 'external-url'
url: string
}
| {
type: 'default'
}

export const getAuthorIcon = async (
author: CollectionEntry<'authors'>,
): Promise<AuthorIcon> => {
try {
return {
type: 'local-svg',
imageMetadata: (await import(`../../../content/authors/${author.id}.svg`))
.default,
svg: (await import(`../../../content/authors/${author.id}.svg?raw`))
.default,
}
} catch {
try {
return {
type: 'local-png',
imageMetadata: (
await import(`../../../content/authors/${author.id}.png`)
).default,
url: (await import(`../../../content/authors/${author.id}.png?url`))
.default,
}
} catch {
if (author.data.github !== undefined) {
return {
type: 'external-url',
url: `https://github.com/${author.data.github}.png`,
}
} else {
return { type: 'default' }
}
}
}
}
Loading