diff --git a/frontend/packages/app/components/sidebar.tsx b/frontend/packages/app/components/sidebar.tsx
index 8b091f66c..0d2a2f3cd 100644
--- a/frontend/packages/app/components/sidebar.tsx
+++ b/frontend/packages/app/components/sidebar.tsx
@@ -9,6 +9,7 @@ import {
} from '@mintter/shared'
import {
Button,
+ Home,
ListItem,
ListItemProps,
Separator,
@@ -158,6 +159,17 @@ function FullAppSidebar() {
)}
+
+ {
+ navigate({key: 'feed', tab: 'trusted'})
+ }}
+ title="Home Feed"
+ bold
+ icon={Home}
+ />
+
+
+
+ Home Feed
+
+ {subtitle}
+ >
+ )
+ }
+
if (route.key === 'documents') {
let subtitle: string | null = null
if (route.tab === 'drafts') {
diff --git a/frontend/packages/app/models/feed.ts b/frontend/packages/app/models/feed.ts
new file mode 100644
index 000000000..ace71b21d
--- /dev/null
+++ b/frontend/packages/app/models/feed.ts
@@ -0,0 +1,16 @@
+import {useQuery} from '@tanstack/react-query'
+import {useGRPCClient} from '../app-context'
+import {queryKeys} from './query-keys'
+
+export function useFeed() {
+ const grpcClient = useGRPCClient()
+ return useQuery({
+ queryKey: [queryKeys.FEED],
+ queryFn: async () => {
+ return await grpcClient.activityFeed.listEvents({
+ pageSize: 100,
+ pageToken: '',
+ })
+ },
+ })
+}
diff --git a/frontend/packages/app/models/query-keys.ts b/frontend/packages/app/models/query-keys.ts
index 72dc22c1c..d07a8d241 100644
--- a/frontend/packages/app/models/query-keys.ts
+++ b/frontend/packages/app/models/query-keys.ts
@@ -8,6 +8,9 @@ export const queryKeys = {
// NOTE: Arguments to query keys documented in comments
+ // feed
+ FEED: 'FEED',
+
// daemon
GET_DAEMON_INFO: 'GET_DAEMON_INFO',
diff --git a/frontend/packages/app/pages/feed.tsx b/frontend/packages/app/pages/feed.tsx
new file mode 100644
index 000000000..52f380fa6
--- /dev/null
+++ b/frontend/packages/app/pages/feed.tsx
@@ -0,0 +1,56 @@
+import {
+ Globe,
+ PageContainer,
+ RadioButtons,
+ SizableText,
+ XStack,
+ YStack,
+} from '@mintter/ui'
+import {Verified} from '@tamagui/lucide-icons'
+import Footer from '../components/footer'
+import {MainWrapperNoScroll} from '../components/main-wrapper'
+import {useFeed} from '../models/feed'
+import {useNavRoute} from '../utils/navigation'
+import {useNavigate} from '../utils/useNavigate'
+
+const feedTabsOptions = [
+ {key: 'trusted', label: 'Trusted Content', icon: Verified},
+ {key: 'all', label: 'All Content', icon: Globe},
+] as const
+
+function Feed({tab}: {tab: 'trusted' | 'all'}) {
+ const feed = useFeed()
+ console.log(feed.data)
+ return (
+
+ {tab}
+
+ )
+}
+
+export default function FeedPage() {
+ const route = useNavRoute()
+ if (route.key !== 'feed') throw new Error('invalid route')
+ const replace = useNavigate('replace')
+ return (
+ <>
+
+
+
+
+ {
+ replace({...route, tab})
+ }}
+ />
+
+
+
+
+
+
+ >
+ )
+}
diff --git a/frontend/packages/app/pages/main.tsx b/frontend/packages/app/pages/main.tsx
index 8ce402232..fabde1389 100644
--- a/frontend/packages/app/pages/main.tsx
+++ b/frontend/packages/app/pages/main.tsx
@@ -17,6 +17,7 @@ import {NotFoundPage} from './base'
import {DocumentPlaceholder} from './document-placeholder'
import './polyfills'
+var Feed = lazy(() => import('@mintter/app/pages/feed'))
var Documents = lazy(() => import('@mintter/app/pages/documents'))
var Account = lazy(() => import('@mintter/app/pages/account-page'))
var Contacts = lazy(() => import('@mintter/app/pages/contacts-page'))
@@ -38,6 +39,11 @@ function BaseLoading() {
function getPageComponent(navRoute: NavRoute) {
switch (navRoute.key) {
+ case 'feed':
+ return {
+ PageComponent: Feed,
+ Fallback: BaseLoading,
+ }
case 'documents':
return {
PageComponent: Documents,
diff --git a/frontend/packages/app/pages/publication-list-page.tsx b/frontend/packages/app/pages/publication-list-page.tsx
index d846d08f2..f1764490e 100644
--- a/frontend/packages/app/pages/publication-list-page.tsx
+++ b/frontend/packages/app/pages/publication-list-page.tsx
@@ -9,9 +9,8 @@ import {
DialogTitle,
List,
PageContainer,
- Separator,
+ RadioButtons,
Spinner,
- XGroup,
XStack,
} from '@mintter/ui'
@@ -30,7 +29,7 @@ import {
BadgeCheck as Verified,
} from '@tamagui/lucide-icons'
import copyTextToClipboard from 'copy-text-to-clipboard'
-import {ComponentProps, memo} from 'react'
+import {memo} from 'react'
import {useAppContext} from '../app-context'
import {DeleteDocumentDialog} from '../components/delete-dialog'
import {useDeleteDraftDialog} from '../components/delete-draft-dialog'
@@ -71,31 +70,6 @@ export function PublicationListPageUnmemo() {
)
}
-function ToggleGroupItem({
- label,
- icon,
- active,
- onPress,
-}: {
- label: string
- icon: ComponentProps['icon'] | undefined
- active: boolean
- onPress: () => void
-}) {
- return (
-
-
-
- )
-}
-
export function PublishedFirstDocDialog({
input,
onClose,
@@ -151,59 +125,26 @@ export function PublishedFirstDocDialog({
>
)
}
+const documentTabsOptions = [
+ {key: 'trusted', label: 'Trusted Creators', icon: Verified},
+ {key: 'all', label: 'All Creators', icon: Globe},
+ {key: 'drafts', label: 'My Drafts', icon: Pencil},
+] as const
function DocumentTabs() {
const route = useNavRoute()
if (route.key !== 'documents') throw new Error('invalid route')
- const trustedOnly = route.tab === 'trusted' || route.tab == null
- const draftsOnly = route.tab === 'drafts'
- const allDocs = !trustedOnly && !draftsOnly
const replace = useNavigate('replace')
-
return (
- }>
- {
- if (!trustedOnly) {
- replace({
- ...route,
- tab: null,
- })
- }
- }}
- />
- {
- if (!allDocs) {
- replace({
- ...route,
- tab: 'all',
- })
- }
- }}
- />
- {
- if (!draftsOnly) {
- replace({
- ...route,
- tab: 'drafts',
- })
- }
- }}
- />
-
+ {
+ replace({...route, tab})
+ }}
+ />
)
diff --git a/frontend/packages/app/utils/routes.tsx b/frontend/packages/app/utils/routes.tsx
index 0c9c67ced..28430970e 100644
--- a/frontend/packages/app/utils/routes.tsx
+++ b/frontend/packages/app/utils/routes.tsx
@@ -1,7 +1,13 @@
import {groupVariantSchema, publicationVariantSchema} from '@mintter/shared'
import {z} from 'zod'
-export const defaultRoute: NavRoute = {key: 'documents', tab: 'trusted'}
+export const defaultRoute: NavRoute = {key: 'feed', tab: 'trusted'}
+
+export const feedRouteSchema = z.object({
+ key: z.literal('feed'),
+ tab: z.union([z.literal('all'), z.literal('trusted')]),
+})
+export type FeedRoute = z.infer
export const documentsRouteSchema = z.object({
key: z.literal('documents'),
@@ -100,6 +106,7 @@ export const groupRouteSchema = z.object({
export type GroupRoute = z.infer
export const navRouteSchema = z.discriminatedUnion('key', [
+ feedRouteSchema,
contactsRouteSchema,
accountRouteSchema,
settingsRouteSchema,
diff --git a/frontend/packages/shared/src/client/grpc-types.ts b/frontend/packages/shared/src/client/grpc-types.ts
index f2ec12028..7c349ac60 100644
--- a/frontend/packages/shared/src/client/grpc-types.ts
+++ b/frontend/packages/shared/src/client/grpc-types.ts
@@ -1,24 +1,24 @@
-import {Accounts} from './.generated/accounts/v1alpha/accounts_connect'
-import {Daemon} from './.generated/daemon/v1alpha/daemon_connect'
-import {Changes} from './.generated/documents/v1alpha/changes_connect'
-import {Comments} from './.generated/documents/v1alpha/comments_connect'
-import {ContentGraph} from './.generated/documents/v1alpha/content_graph_connect'
-import {Groups} from './.generated/groups/v1alpha/groups_connect'
+import { Accounts } from './.generated/accounts/v1alpha/accounts_connect'
+import { Daemon } from './.generated/daemon/v1alpha/daemon_connect'
+import { Changes } from './.generated/documents/v1alpha/changes_connect'
+import { Comments } from './.generated/documents/v1alpha/comments_connect'
+import { ContentGraph } from './.generated/documents/v1alpha/content_graph_connect'
+import { Groups } from './.generated/groups/v1alpha/groups_connect'
import {
Drafts,
Publications,
} from './.generated/documents/v1alpha/documents_connect'
-import {Document} from './.generated/documents/v1alpha/documents_pb'
-import {Entities} from './.generated/entities/v1alpha/entities_connect'
-import {Networking} from './.generated/networking/v1alpha/networking_connect'
+import { Document } from './.generated/documents/v1alpha/documents_pb'
+import { Entities } from './.generated/entities/v1alpha/entities_connect'
+import { Networking } from './.generated/networking/v1alpha/networking_connect'
export {
Account,
Device,
GetAccountRequest,
ListAccountsRequest,
ListAccountsResponse,
- Profile,
+ Profile
} from './.generated/accounts/v1alpha/accounts_pb'
export type {
GenMnemonicRequest,
@@ -26,16 +26,16 @@ export type {
GetInfoRequest,
Info,
RegisterRequest,
- RegisterResponse,
+ RegisterResponse
} from './.generated/daemon/v1alpha/daemon_pb'
export {
ChangeInfo,
- GetChangeInfoRequest,
+ GetChangeInfoRequest
} from './.generated/documents/v1alpha/changes_pb'
export {
LinkNode,
ListCitationsResponse,
- Link as MttLink,
+ Link as MttLink
} from './.generated/documents/v1alpha/content_graph_pb'
export {
Annotation,
@@ -51,7 +51,7 @@ export {
ListDraftsResponse,
ListPublicationsRequest,
ListPublicationsResponse,
- PublishDraftRequest,
+ PublishDraftRequest
} from './.generated/documents/v1alpha/documents_pb'
export {
Change,
@@ -59,7 +59,7 @@ export {
DiscoverEntityResponse,
EntityTimeline,
GetChangeRequest,
- GetEntityTimelineRequest,
+ GetEntityTimelineRequest
} from './.generated/entities/v1alpha/entities_pb'
export {
Group,
@@ -68,18 +68,21 @@ export {
ListDocumentGroupsResponse,
ListGroupsRequest,
ListGroupsResponse,
- Role,
+ Role
} from './.generated/groups/v1alpha/groups_pb'
export * from './.generated/groups/v1alpha/website_connect'
export * from './.generated/groups/v1alpha/website_pb'
-export {ConnectionStatus} from './.generated/networking/v1alpha/networking_pb'
+export { ConnectionStatus } from './.generated/networking/v1alpha/networking_pb'
export type {
ConnectRequest,
ConnectResponse,
GetPeerInfoRequest,
- PeerInfo,
+ PeerInfo
} from './.generated/networking/v1alpha/networking_pb'
+export * from './.generated/activity/v1alpha/activity_connect'
+export * from './.generated/activity/v1alpha/activity_pb'
+
export {
Accounts,
Changes,
@@ -91,5 +94,6 @@ export {
Entities,
Groups,
Networking,
- Publications,
+ Publications
}
+
diff --git a/frontend/packages/shared/src/grpc-client.ts b/frontend/packages/shared/src/grpc-client.ts
index 91f0fff79..abefde0c5 100644
--- a/frontend/packages/shared/src/grpc-client.ts
+++ b/frontend/packages/shared/src/grpc-client.ts
@@ -1,6 +1,7 @@
import {createPromiseClient, PromiseClient} from '@connectrpc/connect'
import {
Accounts,
+ ActivityFeed,
Changes,
Comments,
ContentGraph,
@@ -25,6 +26,7 @@ export type GRPCClient = {
daemon: PromiseClient
networking: PromiseClient
website: PromiseClient
+ activityFeed: PromiseClient
}
export function createGRPCClient(transport: any): GRPCClient {
@@ -40,5 +42,6 @@ export function createGRPCClient(transport: any): GRPCClient {
groups: createPromiseClient(Groups, transport),
entities: createPromiseClient(Entities, transport),
website: createPromiseClient(Website, transport),
+ activityFeed: createPromiseClient(ActivityFeed, transport),
} as const
}
diff --git a/frontend/packages/ui/src/container.tsx b/frontend/packages/ui/src/container.tsx
index a02243979..ad5a17814 100644
--- a/frontend/packages/ui/src/container.tsx
+++ b/frontend/packages/ui/src/container.tsx
@@ -17,7 +17,7 @@ const variants = {
export function PageContainer({children}: {children: ReactNode}) {
return (
-
+
['icon'] | undefined
+ }>,
+>({
+ options,
+ value,
+ onValue,
+}: {
+ options: Options
+ value: Options[number]['key']
+ onValue: (value: Options[number]['key']) => void
+}) {
+ return (
+ }>
+ {options.map((option) => (
+ {
+ onValue(option.key)
+ }}
+ />
+ ))}
+
+ )
+}
+function RadioButton({
+ label,
+ icon,
+ active,
+ onPress,
+}: {
+ label: string
+ icon: ComponentProps['icon'] | undefined
+ active: boolean
+ onPress: () => void
+}) {
+ return (
+
+
+
+ )
+}