Skip to content

Commit aa897bd

Browse files
authored
Merge pull request #795 from CodingCatDev/dev
feat(syndication): backfill daily.dev Squad, add full platform link tracking, YouTube embeds, and Reddit Devvit app
2 parents 1b14647 + e6d3a06 commit aa897bd

14 files changed

Lines changed: 1880 additions & 32 deletions

File tree

‎.github/workflows/syndicate-post.yml‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ jobs:
3737

3838
- name: Syndicate Post
3939
run: |
40-
node scripts/run-syndicate-post.mjs
40+
if [ "$TARGET_DOCUMENT_ID" = "backfill-dailydev" ]; then
41+
node scripts/backfill-dailydev-and-links.mjs
42+
else
43+
node scripts/run-syndicate-post.mjs
44+
fi
4145
env:
4246
TARGET_DOCUMENT_ID: ${{ inputs.document_id }}
4347
SANITY_AUTH_TOKEN: ${{ secrets.SANITY_AUTH_TOKEN }}

‎apps/ops/src/App.tsx‎

Lines changed: 171 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useDocuments } from "@sanity/sdk-react";
2-
import { useState } from "react";
2+
import { useEffect, useState } from "react";
33

4-
type WorkspaceTab = "crm" | "bookings" | "billing";
4+
type WorkspaceTab = "crm" | "bookings" | "billing" | "syndication";
55

66
interface SeededGmailDraft {
77
companyName: string;
@@ -111,6 +111,33 @@ export function App() {
111111
const sponsorLeads = useDocuments({ documentType: "sponsorLead" });
112112
const sponsorPool = useDocuments({ documentType: "sponsorPool" });
113113
const bookings = useDocuments({ documentType: "booking" });
114+
const syndications = useDocuments({ documentType: "syndication" });
115+
const [syndicatedContent, setSyndicatedContent] = useState<Array<any>>([]);
116+
117+
useEffect(() => {
118+
const query = encodeURIComponent(
119+
`*[_type in ["post", "podcast"] && !(_id in path("drafts.**"))] | order(coalesce(date, _createdAt) desc)[0...40] {
120+
_id,
121+
_type,
122+
title,
123+
"slug": slug.current,
124+
"date": coalesce(date, _createdAt),
125+
youtube,
126+
dailydev,
127+
devto,
128+
hashnode,
129+
awsbuilder,
130+
medium,
131+
reddit
132+
}`,
133+
);
134+
fetch(`https://${projectId}.apicdn.sanity.io/v2025-09-30/data/query/${dataset}?query=${query}`)
135+
.then((r) => r.json())
136+
.then((d) => {
137+
if (Array.isArray(d?.result)) setSyndicatedContent(d.result);
138+
})
139+
.catch(() => {});
140+
}, [projectId, dataset]);
114141

115142
const handleDiscoverOutbound = async () => {
116143
setDiscovering(true);
@@ -185,6 +212,21 @@ export function App() {
185212
>
186213
3. Stripe 50/50 Billing &amp; Deal Rooms
187214
</button>
215+
<button
216+
type="button"
217+
onClick={() => setActiveTab("syndication")}
218+
style={{
219+
padding: "8px 16px",
220+
borderRadius: 8,
221+
border: "1px solid #ccc",
222+
background: activeTab === "syndication" ? "#111" : "#fff",
223+
color: activeTab === "syndication" ? "#fff" : "#111",
224+
fontWeight: 600,
225+
cursor: "pointer",
226+
}}
227+
>
228+
4. Syndication &amp; Platform Links ({syndications.data?.length || syndicatedContent.length})
229+
</button>
188230
</div>
189231
</header>
190232

@@ -318,6 +360,133 @@ export function App() {
318360
</ul>
319361
</section>
320362
)}
363+
364+
{activeTab === "syndication" && (
365+
<section style={{ padding: 20, border: "1px solid #ddd", borderRadius: 12 }}>
366+
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }}>
367+
<div>
368+
<h2 style={{ margin: 0, fontSize: 18 }}>Cross-Platform Syndication &amp; YouTube Funnel Tracker</h2>
369+
<p style={{ margin: "4px 0 0", color: "#666", fontSize: 13 }}>
370+
Tracks canonical <code>codingcat.dev</code> URLs and outbound links across YouTube, daily.dev Squad, Dev.to, Hashnode, AWS Builder Center, Medium, and Reddit.
371+
</p>
372+
</div>
373+
<div style={{ display: "flex", gap: 8 }}>
374+
<a
375+
href="https://daily.dev/squads/codingcatdev"
376+
target="_blank"
377+
rel="noreferrer"
378+
style={{ padding: "8px 12px", border: "1px solid #ccc", borderRadius: 6, textDecoration: "none", color: "#111", fontSize: 12, fontWeight: 600 }}
379+
>
380+
daily.dev Squad &nearr;
381+
</a>
382+
<a
383+
href="https://dev.to/codingcatdev"
384+
target="_blank"
385+
rel="noreferrer"
386+
style={{ padding: "8px 12px", border: "1px solid #ccc", borderRadius: 6, textDecoration: "none", color: "#111", fontSize: 12, fontWeight: 600 }}
387+
>
388+
Dev.to Org &nearr;
389+
</a>
390+
</div>
391+
</div>
392+
<div style={{ overflowX: "auto" }}>
393+
<table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
394+
<thead>
395+
<tr style={{ textAlign: "left", borderBottom: "2px solid #eee" }}>
396+
<th style={{ padding: "8px 6px" }}>Date</th>
397+
<th style={{ padding: "8px 6px" }}>Title (Canonical)</th>
398+
<th style={{ padding: "8px 6px" }}>YouTube</th>
399+
<th style={{ padding: "8px 6px" }}>daily.dev Squad</th>
400+
<th style={{ padding: "8px 6px" }}>Dev.to</th>
401+
<th style={{ padding: "8px 6px" }}>Hashnode</th>
402+
<th style={{ padding: "8px 6px" }}>AWS Builder</th>
403+
<th style={{ padding: "8px 6px" }}>Medium / Reddit</th>
404+
</tr>
405+
</thead>
406+
<tbody>
407+
{syndicatedContent.map((item) => {
408+
const canonical = `${siteOrigin}/${item._type}/${item.slug}`;
409+
const hnUrl = item.hashnode
410+
? item.hashnode.startsWith("http")
411+
? item.hashnode
412+
: `https://hashnode.codingcat.dev/${item.hashnode}`
413+
: null;
414+
return (
415+
<tr key={item._id} style={{ borderBottom: "1px solid #f0f0f0" }}>
416+
<td style={{ padding: "8px 6px", whiteSpace: "nowrap", color: "#666" }}>
417+
{(item.date || "").slice(0, 10)}
418+
</td>
419+
<td style={{ padding: "8px 6px", fontWeight: 600 }}>
420+
<a href={canonical} target="_blank" rel="noreferrer" style={{ color: "#111" }}>
421+
{item.title}
422+
</a>
423+
</td>
424+
<td style={{ padding: "8px 6px" }}>
425+
{item.youtube ? (
426+
<a href={item.youtube} target="_blank" rel="noreferrer" style={{ color: "#c00", fontWeight: 600 }}>
427+
Watch &nearr;
428+
</a>
429+
) : (
430+
<span style={{ color: "#aaa" }}>-</span>
431+
)}
432+
</td>
433+
<td style={{ padding: "8px 6px" }}>
434+
{item.dailydev ? (
435+
<a href={item.dailydev} target="_blank" rel="noreferrer" style={{ color: "#7c3aed", fontWeight: 600 }}>
436+
Squad &nearr;
437+
</a>
438+
) : (
439+
<span style={{ color: "#aaa" }}>-</span>
440+
)}
441+
</td>
442+
<td style={{ padding: "8px 6px" }}>
443+
{item.devto ? (
444+
<a href={item.devto} target="_blank" rel="noreferrer" style={{ color: "#2563eb", fontWeight: 600 }}>
445+
Dev.to &nearr;
446+
</a>
447+
) : (
448+
<span style={{ color: "#aaa" }}>-</span>
449+
)}
450+
</td>
451+
<td style={{ padding: "8px 6px" }}>
452+
{hnUrl ? (
453+
<a href={hnUrl} target="_blank" rel="noreferrer" style={{ color: "#2563eb", fontWeight: 600 }}>
454+
Hashnode &nearr;
455+
</a>
456+
) : (
457+
<span style={{ color: "#aaa" }}>-</span>
458+
)}
459+
</td>
460+
<td style={{ padding: "8px 6px" }}>
461+
{item.awsbuilder ? (
462+
<a href={item.awsbuilder} target="_blank" rel="noreferrer" style={{ color: "#d97706", fontWeight: 600 }}>
463+
AWS &nearr;
464+
</a>
465+
) : (
466+
<span style={{ color: "#aaa" }}>-</span>
467+
)}
468+
</td>
469+
<td style={{ padding: "8px 6px" }}>
470+
{item.medium && (
471+
<a href={item.medium} target="_blank" rel="noreferrer" style={{ marginRight: 8 }}>
472+
Medium &nearr;
473+
</a>
474+
)}
475+
{item.reddit && (
476+
<a href={item.reddit} target="_blank" rel="noreferrer" style={{ color: "#ea580c" }}>
477+
Reddit &nearr;
478+
</a>
479+
)}
480+
{!item.medium && !item.reddit && <span style={{ color: "#aaa" }}>-</span>}
481+
</td>
482+
</tr>
483+
);
484+
})}
485+
</tbody>
486+
</table>
487+
</div>
488+
</section>
489+
)}
321490
</div>
322491
);
323492
}

‎apps/reddit-devvit/README.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# CodingCat.dev Reddit Devvit Auto-Poster (`r/CodingCatDev`)
2+
3+
This Reddit Devvit app runs inside Reddit's own infrastructure (`@devvit/public-api`) so **it does not require external Reddit Data API approval**.
4+
5+
## What It Does
6+
1. **Polls Sanity (`production`) every 30 minutes** via `Devvit.addSchedulerJob` (or on-demand via the subreddit moderator menu item **"Sync Latest from CodingCat.dev Now"**).
7+
2. **YouTube-First Native Player**:
8+
- When a post or podcast has a `youtube` URL in Sanity, it submits the `https://www.youtube.com/watch?v=<id>` URL as the Reddit Link Post so Reddit renders the **native inline YouTube player** right in the `r/CodingCatDev` feed.
9+
- Automatically adds a pinned moderator comment containing the excerpt, the canonical `https://codingcat.dev/post/<slug>` guide + transcript link, and a direct YouTube subscribe link (`?sub_confirmation=1`).
10+
3. **Automatic Sanity Link Tracking**:
11+
- When `sanityWriteToken` is configured in the app's installation settings, it automatically creates `syndication.<docId>.reddit` and populates the `reddit` URL field on the Sanity document.
12+
13+
## 2-Command Setup & Deployment
14+
15+
```bash
16+
cd apps/reddit-devvit
17+
npx devvit login
18+
npx devvit upload
19+
npx devvit install r/CodingCatDev
20+
```
21+
22+
*(Optional)* After installing on `r/CodingCatDev`, open `https://developers.reddit.com/r/CodingCatDev/apps/codingcat-syndicator` and paste your `SANITY_API_WRITE_TOKEN` into the **Sanity API Write Token** field so every Reddit post URL is automatically saved back to Sanity.

‎apps/reddit-devvit/devvit.yaml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
name: codingcat-syndicator
2+
version: 0.1.0

‎apps/reddit-devvit/package.json‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "@codingcatdev/reddit-devvit",
3+
"version": "0.1.0",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"login": "devvit login",
8+
"upload": "devvit upload",
9+
"playtest": "devvit playtest r/CodingCatDev",
10+
"typecheck": "tsc --noEmit"
11+
},
12+
"dependencies": {
13+
"@devvit/public-api": "^0.11.0"
14+
},
15+
"devDependencies": {
16+
"devvit": "^0.11.0",
17+
"typescript": "^5.7.0"
18+
}
19+
}

0 commit comments

Comments
 (0)