chore: remove all Twitter branding, rename route /twitter -> /feed

- Renamed apps/web/src/app/twitter/ -> feed/
- Renamed TwitterComposer -> FeedComposer, TwitterSidebar -> FeedSidebar
- TwitterPage -> FeedPage, TwitterComposerProps -> FeedComposerProps
- Shell nav: 'Twitter' -> 'Feed', route /twitter -> /feed
- BottomNav: 'Twitter' -> 'Feed'
- Root page: redirect / -> /feed (was /twitter)
- All 'Twitter' product references purged from codebase
This commit is contained in:
Crypto Rug Munch 2026-07-08 10:33:24 +07:00
parent 5b394f6c92
commit 29b4d51b3c
13 changed files with 36 additions and 36 deletions

View file

@ -112,4 +112,4 @@ jobs:
run: |
echo "::error::Deploy failed for ${{ github.ref }} — see run logs"
# Add Slack/Discord/PagerDuty webhook here when ready.
exit 1
exit 1

View file

@ -151,4 +151,4 @@ wrangler rollback --env production
```
Or via the Cloudflare dashboard: `Workers & Pages → degenfeed-api →
Deployments → Rollback`.
Deployments → Rollback`.

View file

@ -36,4 +36,4 @@ Rebuild DegenFeed Web into a usable web3-first social aggregator (TODO.md #11
4. Newsletter cron + email delivery (#15).
## Blocked
None.
None.

28
TODO.md
View file

@ -15,49 +15,49 @@
### 15 Next Moves (ranked by impact)
1. **Make `/home` per-protocol sections actually work**
1. **Make `/home` per-protocol sections actually work**
Ensure `/home/nostr`, `/home/farcaster`, `/home/bluesky`, `/home/lens`, `/home/mastodon`, `/home/threads`, and `/home/reddit` each call the correct provider endpoint and render a real feed with loading, error, and empty states. Fix `/home/mastodon` to use `/api/feed/home?protocols=mastodon` (or keep `/api/feed/activitypub` but wire it to the deployed GotoSocial instance).
2. **Rebuild the landing experience**
2. **Rebuild the landing experience**
The current app looks like a generic dashboard. Design a web3-first `/twitter` timeline with: compact PostCards, hover actions, quote/repost counts, protocol badges, embedded link previews, and image lightbox. Copy the best parts of Warpcast / Bluesky / X, not a Bootstrap card layout.
3. **Fix the empty-state problem everywhere**
3. **Fix the empty-state problem everywhere**
Every feed currently shows an empty card with no personality. Add protocol-specific empty states: "No Nostr notes found — try a different relay", "Mastodon timeline is quiet — create the first post @social.degenfeed.xyz", etc., with one-click fixes (switch relay, create account, sign in).
4. **Add real-time / polling updates**
4. **Add real-time / polling updates**
The news page polls every 60s for new stories but other timelines do not. Add a global `useFeedPoller` hook that shows a "New posts" banner and lets users pull-to-refresh on mobile and click-to-refresh on desktop.
5. **Replace the RMI hard dependency with a resilient pipeline**
5. **Replace the RMI hard dependency with a resilient pipeline**
RMI is unreachable from Cloudflare edge. The feed should always fall back to public sources without the user noticing. Remove all user-facing "RMI DataBus" copy until the backend is actually deployed and reachable from the edge.
6. **Harden the Mastodon/GotoSocial integration**
6. **Harden the Mastodon/GotoSocial integration**
Our own instance is the most important protocol. Add a dedicated GotoSocial provider path that reads the public timeline from `https://social.degenfeed.xyz/api/v1/timelines/public`, supports hashtag timelines, and lets signed-in users see their home timeline.
7. **Add protocol-aware composer**
7. **Add protocol-aware composer**
The composer now shows all protocols. Signed-in protocols are selectable; unsigned protocols show a lock icon and open sign-in when clicked. Added live publish preview (`ComposePreview`) and per-protocol character-limit warnings that disable the Post button when exceeded. Updated both the inline `TwitterComposer` and the `ComposeModal`.
8. **Implement wallet-first authentication**
8. **Implement wallet-first authentication**
Wallet sign-in is now the primary path in the `SignInModal` (Ethereum + Solana buttons at the top). Added `/api/auth/signout` to clear the `__Host-dfid` cookie, added `signOut` to `AuthProvider`, and exposed it in the `/twitter` header and sidebar. `/api/bridge/rss-to-nostr` now requires the session cookie too.
9. **Add a unified notifications stream**
9. **Add a unified notifications stream**
Added `useNotifications` hook that builds auth from all linked identities, polls `POST /api/notifications` every 60s, and persists read state in localStorage. Extended the backend to aggregate notifications from Nostr, Mastodon, and Bluesky. Added the `NotificationBell` to the `/twitter` header and kept it in `/home/[section]`.
10. **Fix ranking and deduplication**
10. **Fix ranking and deduplication**
Backend now computes and attaches a per-post score breakdown (recency, engagement, relevance, quality, diversity, source, velocity, reasons). `PostCard` shows a `★` badge with hover tooltip exposing each component. Added a "hide low-quality" toggle in `/twitter` (persisted in localStorage) that filters posts with `score.total < 1.0`. The toggle respects the session-bound `hideLowQuality` preference so it can later be moved to a profile page.
11. **Add cross-posting to the bridge**
Wire `/api/bridge/rss-to-nostr` and a general `/api/publish` to cross-post from Mastodon to Nostr, Farcaster to Bluesky, etc. Start with one working pair (Mastodon → Nostr) and expose it in the composer.
12. **Build a mobile-first responsive layout**
12. **Build a mobile-first responsive layout**
The current layout is desktop-only with a hidden right sidebar. Implement a bottom nav (Home, Search, Notifications, Profile) for mobile, collapsible sidebars, and proper safe-area handling.
13. **Add protocol health dashboard**
13. **Add protocol health dashboard**
`/api/feed/health` exists but is not exposed in the UI. Add a `/status` page (or admin panel) showing each protocol's latency, success rate, and last fetched time so users know when a feed is degraded.
14. **Add search that actually searches**
14. **Add search that actually searches**
`/api/search` and `/api/search/identity` are wired. Add a global search bar in the shell that searches posts, identities, and niches across protocols, with filters for protocol and time range.
15. **Ship the newsletter as a real product**
15. **Ship the newsletter as a real product**
After the digest works, add an actual email-sending path (Postmark / SES) and a cron trigger that builds the daily digest from live data and sends it to subscribers. Add a `/newsletter/manage` page with preferences and unsubscribe.
## Active

View file

@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import './.next/types/routes.d.ts';
import "./.next/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View file

@ -6,9 +6,9 @@ import { FeedList, NotificationBell } from '@degenfeed/ui';
import Image from 'next/image';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useAuth } from '../../components/AuthProvider';
import { FeedComposer } from '../../components/FeedComposer';
import { FeedSidebar } from '../../components/FeedSidebar';
import { TipModal } from '../../components/TipModal';
import { TwitterComposer } from '../../components/TwitterComposer';
import { TwitterSidebar } from '../../components/TwitterSidebar';
import { useNotifications } from '../../hooks/useNotifications';
export const runtime = 'edge';
@ -43,7 +43,7 @@ const PROTOCOL_META: Record<string, { label: string; color: string; emoji: strin
rss: { label: 'News', color: '#ffa500', emoji: '📰' },
};
export default function TwitterPage() {
export default function FeedPage() {
const { signedInProtocols, engage, openSignIn, publish, ready, signOut, nostrPublicId } =
useAuth();
@ -472,7 +472,7 @@ export default function TwitterPage() {
{/* Composer / sign-in CTA */}
{hasSignedIn ? (
<TwitterComposer
<FeedComposer
onPost={publish}
signedInProtocols={signedInProtocols}
nostrPublicId={nostrPublicId}
@ -568,7 +568,7 @@ export default function TwitterPage() {
{/* Right sidebar */}
<aside className="hidden lg:col-span-4 lg:block xl:col-span-3">
<div className="sticky top-20">
<TwitterSidebar
<FeedSidebar
trending={feedMeta.trending ?? []}
protocolSummaries={protocolSummaries}
activeProtocolFilter={protocolFilter}

View file

@ -6,7 +6,7 @@ import { useEffect } from 'react';
export default function RootPage() {
const router = useRouter();
useEffect(() => {
router.replace('/twitter');
router.replace('/feed');
}, [router]);
return (
<div className="flex min-h-[60vh] items-center justify-center">

View file

@ -15,7 +15,7 @@ function Icon({ children }: { children: React.ReactNode }) {
const NAV: NavItem[] = [
{ href: '/home', label: 'Feed', icon: <Icon></Icon> },
{ href: '/twitter', label: 'Twitter', icon: <Icon></Icon> },
{ href: '/feed', label: 'Feed', icon: <Icon></Icon> },
{ href: '/news', label: 'News', icon: <Icon></Icon> },
{ href: '/newsletter', label: 'Daily', icon: <Icon></Icon> },
{ href: '/profile', label: 'Profile', icon: <Icon></Icon> },

View file

@ -15,7 +15,7 @@ const TARGETS = [
{ id: 'mastodon', label: 'Mastodon', charLimit: 5000 },
];
interface TwitterComposerProps {
interface FeedComposerProps {
signedInProtocols: Record<string, boolean>;
nostrPublicId?: string;
onPost: (
@ -25,12 +25,12 @@ interface TwitterComposerProps {
onRequestSignIn?: (protocol: string) => void;
}
export function TwitterComposer({
export function FeedComposer({
signedInProtocols,
nostrPublicId,
onPost,
onRequestSignIn,
}: TwitterComposerProps) {
}: FeedComposerProps) {
const [text, setText] = useState('');
const [selected, setSelected] = useState<Set<string>>(new Set());
const [bridgeToNostr, setBridgeToNostr] = useState(false);

View file

@ -9,7 +9,7 @@ interface ProtocolSummary {
count: number;
}
interface TwitterSidebarProps {
interface FeedSidebarProps {
trending?: { topic: string; count: number; score: number }[];
protocolSummaries?: ProtocolSummary[];
activeProtocolFilter?: string | null;
@ -27,12 +27,12 @@ const NETWORK_HINTS: Record<string, string> = {
rss: '30+ news sources',
};
export function TwitterSidebar({
export function FeedSidebar({
trending,
protocolSummaries,
activeProtocolFilter,
onProtocolClick,
}: TwitterSidebarProps) {
}: FeedSidebarProps) {
const { walletAddress, signOut, openSignIn } = useAuth();
const items = trending ?? [];
const protocols = protocolSummaries ?? [];

View file

@ -14,7 +14,7 @@ interface NavItem {
const NAV: NavItem[] = [
{ href: '/home', label: 'Feed' },
{ href: '/twitter', label: 'Twitter' },
{ href: '/feed', label: 'Feed' },
{ href: '/news', label: 'News' },
{ href: '/newsletter', label: 'Daily' },
{ href: '/premium', label: 'Premium' },
@ -195,8 +195,8 @@ export function Shell({ children }: { children: React.ReactNode }) {
<a href="/home" className="transition-colors hover:text-white">
Feed
</a>
<a href="/twitter" className="transition-colors hover:text-white">
Twitter
<a href="/feed" className="transition-colors hover:text-white">
Feed
</a>
<a href="/news" className="transition-colors hover:text-white">
News

View file

@ -203,4 +203,4 @@ echo " 3. Deploy the preview environment:"
echo " cd workers/api && wrangler deploy --env preview"
echo
echo " 4. Cut a release tag and push to main — CI will deploy production."
hr
hr

View file

@ -67,4 +67,4 @@ routes = [
]
[env.preview.vars]
ALLOWED_ORIGINS = "https://preview.degenfeed.xyz,https://app.preview.degenfeed.xyz,http://localhost:3000"
RATE_MAX_REQ = "600"
RATE_MAX_REQ = "600"