diff --git a/preview/config.ts b/preview/config.ts
index 28d102278..215ed098d 100644
--- a/preview/config.ts
+++ b/preview/config.ts
@@ -80,6 +80,12 @@ export default () =>
memberships: [payloads[0], payloads[1], payloads[2]], // You can post only if you have either Tier-1, Tier-2, or Tier-3.
},
},
+ slots: {
+ 'page:content:home:before-content': {
+ title: 'News',
+ items: 5,
+ },
+ },
},
{
id: 'default-3',
diff --git a/preview/src/theme/Default.astro b/preview/src/theme/Default.astro
index e6f154209..b098edebf 100644
--- a/preview/src/theme/Default.astro
+++ b/preview/src/theme/Default.astro
@@ -1,5 +1,13 @@
---
+import { ClubsSlotName, type ClubsPropsPages } from '@devprotocol/clubs-core'
import '@devprotocol/clubs-core/styles'
+
+type Props = ClubsPropsPages
+const { clubs } = Astro.props
+
+const SlotsPageContentHomeBeforeContent = clubs.slots.filter(
+ (slot) => slot.slot === ClubsSlotName.PageContentHomeBeforeContent,
+)
---
@@ -11,6 +19,11 @@ import '@devprotocol/clubs-core/styles'
+ {
+ SlotsPageContentHomeBeforeContent.map((Slot) => (
+
+ ))
+ }
diff --git a/src/db/redis.ts b/src/db/redis.ts
index 309a6de7b..768f62f99 100644
--- a/src/db/redis.ts
+++ b/src/db/redis.ts
@@ -15,23 +15,25 @@ import {
import { reduceBy } from 'ramda'
import { uuidToQuery } from '../fixtures/search'
-const defaultClient = createClient({
- url: import.meta.env.REDIS_URL,
- username: import.meta.env.REDIS_USERNAME ?? '',
- password: import.meta.env.REDIS_PASSWORD ?? '',
- socket: {
- keepAlive: 1,
- reconnectStrategy: 1,
- },
-})
-
-export type RedisDefaultClient = typeof defaultClient
+const defaultClient = () =>
+ createClient({
+ url: import.meta.env.REDIS_URL,
+ username: import.meta.env.REDIS_USERNAME ?? '',
+ password: import.meta.env.REDIS_PASSWORD ?? '',
+ socket: {
+ keepAlive: 1,
+ reconnectStrategy: 1,
+ },
+ })
+
+export type RedisDefaultClient = ReturnType
export const getDefaultClient = async () => {
- if (defaultClient.isOpen === false) {
- await defaultClient.connect()
+ const client = defaultClient()
+ if (client.isOpen === false) {
+ await client.connect()
}
- return defaultClient
+ return client
}
export const getAllPosts = async ({
diff --git a/src/index.ts b/src/index.ts
index e2ebb600c..54e7ab4d5 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -12,13 +12,14 @@ import {
type ClubsApiPath,
SinglePath,
type Membership,
+ ClubsSlotName,
} from '@devprotocol/clubs-core'
import {
addCommentHandler,
deleteCommentHandler,
fetchCommentsHandler,
} from './apiHandler/comment'
-import { maskFactory } from './fixtures/masking'
+import { mask, maskFactory } from './fixtures/masking'
import { addReactionHandler } from './apiHandler/reactions'
import {
addPostHandler,
@@ -27,7 +28,11 @@ import {
} from './apiHandler/posts'
import { v5 as uuidv5 } from 'uuid'
import { verifyMessage } from 'ethers'
-import { whenDefinedAll, type UndefinedOr } from '@devprotocol/util-ts'
+import {
+ isNotError,
+ whenDefinedAll,
+ type UndefinedOr,
+} from '@devprotocol/util-ts'
import Screenshot1 from './assets/images/posts-1.jpg'
import Screenshot2 from './assets/images/posts-2.jpg'
import Screenshot3 from './assets/images/posts-3.jpg'
@@ -459,52 +464,75 @@ export const getApiPaths = (async (
export const getSlots = (async (options, __, { paths, factory }) => {
const [path1, path2, path3] = paths
+ const feeds =
+ (options.find(({ key }) => key === 'feeds')
+ ?.value as readonly OptionsDatabase[]) ?? []
- if (factory === 'admin' && path1 === 'posts' && path2 === undefined) {
- const addSlotsValue = {
- slot: 'admin:aside:after-built-in-buttons',
- component: NavigationLink,
- props: {
- navigation: {
- display: 'Create a new feed',
- path: '/admin/posts/new',
- },
- },
- }
-
- return [addSlotsValue]
- }
-
- if (factory === 'admin' && path1 === 'posts' && path2 === 'edit' && path3) {
- const feeds =
- (options.find(({ key }) => key === 'feeds')
- ?.value as readonly OptionsDatabase[]) ?? []
- const feed = feeds.find((feed) => feed.id === path3)
-
- const label = feed?.title
- ? `Add '${feed.title}' to the menu`
- : `Add 'Posts' to the menu`
- const display = feed?.title ? feed.title : 'Posts'
- const path = feed?.slug ? `/${feed.slug}` : '/posts'
-
- const addSlotsValue = {
- slot: 'admin:aside:after-built-in-buttons',
- component: CreateNavigationLink,
- props: {
- createNavigation: {
- label,
- link: {
- display,
- path,
+ return [
+ ...(factory === 'page' // == The public feed page with PageContentHomeBeforeContent slot
+ ? (
+ await Promise.all(
+ feeds
+ .filter(
+ (feed) =>
+ feed.slots?.[ClubsSlotName.PageContentHomeBeforeContent] !==
+ undefined,
+ )
+ .map(async (feed) => {
+ const posts = await getAllPosts('documents:redis', {
+ key: feed.database.key,
+ })
+ return [
+ {
+ slot: ClubsSlotName.PageContentHomeBeforeContent,
+ component: Readme, // @@@TODO: This should be replaced in another component
+ props: {
+ posts: isNotError(posts) ? posts.map(mask) : [],
+ feedId: feed.id,
+ },
+ },
+ ]
+ }),
+ )
+ ).flat()
+ : []),
+ ...(factory === 'admin' && path1 === 'posts' && path2 === undefined // == The feeds list page
+ ? [
+ {
+ slot: 'admin:aside:after-built-in-buttons',
+ component: NavigationLink,
+ props: {
+ navigation: {
+ display: 'Create a new feed',
+ path: '/admin/posts/new',
+ },
+ },
},
- },
- },
- }
-
- return [addSlotsValue]
- }
-
- return []
+ ]
+ : []),
+ ...((feed) =>
+ feed &&
+ factory === 'admin' &&
+ path1 === 'posts' &&
+ path2 === 'edit' &&
+ path3 // == The feed edit page
+ ? [
+ {
+ slot: 'admin:aside:after-built-in-buttons',
+ component: CreateNavigationLink,
+ props: {
+ createNavigation: {
+ label: `Add '${feed.title ?? 'Posts'}' to the menu`,
+ link: {
+ display: feed.title ?? 'Posts',
+ path: `/${feed.slug ?? 'posts'}`,
+ },
+ },
+ },
+ },
+ ]
+ : [])(feeds.find((feed) => feed.id === path3)),
+ ]
}) satisfies ClubsFunctionGetSlots
export default {
diff --git a/src/types.ts b/src/types.ts
index 37a846403..3497be703 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -2,6 +2,7 @@ import type {
ClubsGeneralUnit,
Membership as MembershipCore,
} from '@devprotocol/clubs-core'
+import { ClubsSlotName } from '@devprotocol/clubs-core'
export type Option = {
readonly key: 'posts'
@@ -46,6 +47,7 @@ export type Comment = CommentPrimitives & {
readonly updated_at: Date
}
+const { PageContentHomeBeforeContent } = ClubsSlotName
export type OptionsDatabase = {
readonly id: string
readonly slug?: string
@@ -55,6 +57,12 @@ export type OptionsDatabase = {
readonly memberships: readonly [] | readonly Uint8Array[]
}
}
+ readonly slots?: {
+ readonly [PageContentHomeBeforeContent]?: {
+ readonly title: string
+ readonly items: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
+ }
+ }
readonly database:
| {
readonly type: 'encoded:redis'