six-degrees 0.4.0-beta.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +5 -5
- package/.next/standalone/.next/required-server-files.json +6 -1
- package/.next/standalone/.next/routes-manifest.json +12 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/outreach/route.js +1 -1
- package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/import.html +1 -1
- package/.next/standalone/.next/server/app/import.rsc +13 -13
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/launch.html +2 -2
- package/.next/standalone/.next/server/app/launch.rsc +13 -13
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/paths.html +1 -1
- package/.next/standalone/.next/server/app/paths.rsc +13 -13
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/profile.html +1 -1
- package/.next/standalone/.next/server/app/profile.rsc +13 -13
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/queue.html +1 -1
- package/.next/standalone/.next/server/app/queue.rsc +13 -13
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/scores.html +1 -1
- package/.next/standalone/.next/server/app/scores.rsc +13 -13
- package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +13 -13
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/setup.html +1 -1
- package/.next/standalone/.next/server/app/setup.rsc +13 -13
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-rnla2._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00la_y0._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0i5hdu4._.js → [root-of-the-server]__03ydxxh._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__07t47cm._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0rc27za._.js → [root-of-the-server]__0bat8nx._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cj5jqc._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e0fei3._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljcioj._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljnyho._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o5-tcg._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qrnous._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uag-k5._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0um1uey._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wmexuc._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xu34sz._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__133knx1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__14ou7j5._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__15-17hl._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__16iu0hv._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19b7mig._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bbcwub._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1eris29._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1gjo_cq._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qcm08_._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rphsdk._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1w25q0m._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -6
- package/.next/standalone/.next/server/chunks/_1fn30_l._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +12 -8
- package/.next/standalone/.next/server/chunks/{lib_rpc_02ndsg2.js → lib_rpc_03i8xef.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_08q5vp6.js → lib_rpc_08fc3rp.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_00tx2sm.js → lib_rpc_08r2t_q.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_02-qa4d.js → lib_rpc_0btpv1t.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +5 -5
- package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_01f38ty._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_06w_gl4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0hw_qnj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_13kzrwv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_15xuwkv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1gpbbw9._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1hgd5dp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0cu6-an._.js → _1vpfyd-._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_20w06i-._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_components_settings_SectorPicker_1c-29qw.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_launch_page_19t0dyd.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +11 -9
- package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +5 -5
- package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/lib_1bn38gs._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_client_components_server-async-storage_0t1dato.js +3 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
- package/.next/standalone/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
- package/.next/standalone/.next/static/chunks/0l1568qff0efj.js +1 -0
- package/.next/standalone/.next/static/chunks/0muwt9bykzr-1.js +1 -0
- package/.next/standalone/.next/static/chunks/0w76iraw7ij3k.js +18 -0
- package/.next/standalone/.next/static/chunks/135n87ydxjrra.js +2 -0
- package/.next/standalone/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
- package/.next/standalone/.next/static/chunks/28bu26ausqzmn.js +1 -0
- package/.next/standalone/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
- package/.next/standalone/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
- package/.next/standalone/.next/static/chunks/2xjho0lroiwlq.js +1 -0
- package/.next/standalone/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
- package/.next/standalone/.next/static/chunks/3qh78g2aqhprd.js +2 -0
- package/.next/standalone/CHANGELOG.md +152 -0
- package/.next/standalone/CONTRIBUTING.md +15 -4
- package/.next/standalone/README.md +21 -15
- package/.next/standalone/SECURITY.md +10 -0
- package/.next/standalone/app/api/ingest/route.js +6 -1
- package/.next/standalone/app/api/scraper/route.js +79 -11
- package/.next/standalone/app/api/update-images/route.js +41 -7
- package/.next/standalone/app/avatars/[file]/route.js +6 -1
- package/.next/standalone/app/components/Avatar.js +5 -2
- package/.next/standalone/app/components/ChainView.js +659 -469
- package/.next/standalone/app/components/EmptyState.js +1 -1
- package/.next/standalone/app/components/FieldStep.js +94 -0
- package/.next/standalone/app/components/ForceGraph.js +183 -768
- package/.next/standalone/app/components/GridView.js +9 -7
- package/.next/standalone/app/components/LinkedInLimits.js +22 -7
- package/.next/standalone/app/components/ListView.js +6 -4
- package/.next/standalone/app/components/MappingProgress.js +5 -6
- package/.next/standalone/app/components/OnboardingGate.js +1 -1
- package/.next/standalone/app/components/OrbitGraph.js +3 -1
- package/.next/standalone/app/components/OutlinkQuest.js +5 -4
- package/.next/standalone/app/components/RingsView.js +9 -8
- package/.next/standalone/app/components/Sidebar.js +172 -112
- package/.next/standalone/app/components/TheirCircle.js +18 -11
- package/.next/standalone/app/components/settings/SectorPicker.js +202 -0
- package/.next/standalone/app/components/settings/SectorSection.js +17 -191
- package/.next/standalone/app/components/settings/TierSection.js +7 -12
- package/.next/standalone/app/components/views.js +2 -2
- package/.next/standalone/app/globals.css +7 -0
- package/.next/standalone/app/launch/page.js +2 -2
- package/.next/standalone/app/page.js +130 -25
- package/.next/standalone/app/paths/page.js +3 -2
- package/.next/standalone/app/queue/page.js +18 -10
- package/.next/standalone/app/setup/page.js +423 -260
- package/.next/standalone/db/schema.js +1 -1
- package/.next/standalone/eslint.config.mjs +3 -3
- package/.next/standalone/install.sh +3 -3
- package/.next/standalone/lib/chain-layout.js +139 -0
- package/.next/standalone/lib/csv.js +4 -6
- package/.next/standalone/lib/data-export.js +2 -1
- package/.next/standalone/lib/data-folder.js +4 -2
- package/.next/standalone/lib/galaxy.js +12 -0
- package/.next/standalone/lib/ingest.js +15 -8
- package/.next/standalone/lib/linkedin-limits.js +30 -8
- package/.next/standalone/lib/photos.js +47 -0
- package/.next/standalone/lib/quest.js +8 -3
- package/.next/standalone/lib/reach.js +113 -0
- package/.next/standalone/lib/scan-state.js +1 -0
- package/.next/standalone/lib/scanner-setup.js +20 -1
- package/.next/standalone/lib/scoring.js +769 -18
- package/.next/standalone/lib/scraper-client.js +27 -0
- package/.next/standalone/lib/sector-focus.js +13 -0
- package/.next/standalone/lib/separation.js +4 -2
- package/.next/standalone/lib/settings-client.js +21 -0
- package/.next/standalone/lib/settings.js +3 -1
- package/.next/standalone/next.config.mjs +25 -3
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/scripts/build-app.mjs +11 -2
- package/.next/standalone/scripts/check-node.mjs +1 -1
- package/.next/standalone/scripts/image_store.py +77 -21
- package/.next/standalone/scripts/scrape.py +243 -29
- package/.next/standalone/server.js +1 -1
- package/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
- package/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
- package/.next/static/chunks/0l1568qff0efj.js +1 -0
- package/.next/static/chunks/0muwt9bykzr-1.js +1 -0
- package/.next/static/chunks/0w76iraw7ij3k.js +18 -0
- package/.next/static/chunks/135n87ydxjrra.js +2 -0
- package/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
- package/.next/static/chunks/28bu26ausqzmn.js +1 -0
- package/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
- package/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
- package/.next/static/chunks/2xjho0lroiwlq.js +1 -0
- package/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
- package/.next/static/chunks/3qh78g2aqhprd.js +2 -0
- package/CHANGELOG.md +152 -0
- package/README.md +21 -15
- package/SECURITY.md +10 -0
- package/db/schema.js +1 -1
- package/lib/chain-layout.js +139 -0
- package/lib/csv.js +4 -6
- package/lib/data-export.js +2 -1
- package/lib/data-folder.js +4 -2
- package/lib/galaxy.js +12 -0
- package/lib/ingest.js +15 -8
- package/lib/linkedin-limits.js +30 -8
- package/lib/photos.js +47 -0
- package/lib/quest.js +8 -3
- package/lib/reach.js +113 -0
- package/lib/scan-state.js +1 -0
- package/lib/scanner-setup.js +20 -1
- package/lib/scoring.js +769 -18
- package/lib/scraper-client.js +27 -0
- package/lib/sector-focus.js +13 -0
- package/lib/separation.js +4 -2
- package/lib/settings-client.js +21 -0
- package/lib/settings.js +3 -1
- package/package.json +1 -1
- package/scripts/check-node.mjs +1 -1
- package/scripts/image_store.py +77 -21
- package/scripts/scrape.py +243 -29
- package/.next/standalone/.next/server/chunks/lib_rpc_0_8i3ft.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0huvw-3.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0ip2vdh.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0yi9xvd.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1jyhd0o.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1kn59-i.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1q2jkza.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1vpm1sk.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1x6p_31.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0223v9s._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0bcf13g._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0wxbq78._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0z88zcl._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1-87bs1._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_17gui6a._.js +0 -3
- package/.next/standalone/.next/static/chunks/0_yurn4q91vq8.js +0 -16
- package/.next/standalone/.next/static/chunks/0d_mh-06srh2k.js +0 -1
- package/.next/standalone/.next/static/chunks/0x3oadfikvb1c.js +0 -1
- package/.next/standalone/.next/static/chunks/1d5hl1io798n2.js +0 -1
- package/.next/standalone/.next/static/chunks/1qt67cvj9sjwi.js +0 -1
- package/.next/standalone/.next/static/chunks/1roznk7hkjes1.js +0 -44
- package/.next/standalone/.next/static/chunks/2_bf2n4gstad9.js +0 -33
- package/.next/standalone/.next/static/chunks/36gbwek52x_w-.js +0 -2
- package/.next/standalone/.next/static/chunks/3a55suosthntc.js +0 -1
- package/.next/standalone/.next/static/chunks/3fq1zq-6cfwbc.js +0 -1
- package/.next/standalone/.next/static/chunks/3nbnio971zlji.js +0 -2
- package/.next/standalone/.next/static/chunks/3s6awp3qq9s7w.js +0 -1
- package/.next/static/chunks/0_yurn4q91vq8.js +0 -16
- package/.next/static/chunks/0d_mh-06srh2k.js +0 -1
- package/.next/static/chunks/0x3oadfikvb1c.js +0 -1
- package/.next/static/chunks/1d5hl1io798n2.js +0 -1
- package/.next/static/chunks/1qt67cvj9sjwi.js +0 -1
- package/.next/static/chunks/1roznk7hkjes1.js +0 -44
- package/.next/static/chunks/2_bf2n4gstad9.js +0 -33
- package/.next/static/chunks/36gbwek52x_w-.js +0 -2
- package/.next/static/chunks/3a55suosthntc.js +0 -1
- package/.next/static/chunks/3fq1zq-6cfwbc.js +0 -1
- package/.next/static/chunks/3nbnio971zlji.js +0 -2
- package/.next/static/chunks/3s6awp3qq9s7w.js +0 -1
- /package/.next/standalone/.next/static/{NnWlxbDF24KhrU1eHiEnv → WseE_5HuU-1q0ywC9CP3U}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{NnWlxbDF24KhrU1eHiEnv → WseE_5HuU-1q0ywC9CP3U}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{NnWlxbDF24KhrU1eHiEnv → WseE_5HuU-1q0ywC9CP3U}/_ssgManifest.js +0 -0
- /package/.next/static/{NnWlxbDF24KhrU1eHiEnv → WseE_5HuU-1q0ywC9CP3U}/_buildManifest.js +0 -0
- /package/.next/static/{NnWlxbDF24KhrU1eHiEnv → WseE_5HuU-1q0ywC9CP3U}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{NnWlxbDF24KhrU1eHiEnv → WseE_5HuU-1q0ywC9CP3U}/_ssgManifest.js +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState, useRef, useMemo, useCallback } from 'react';
|
|
4
|
-
import {
|
|
3
|
+
import { Suspense, useEffect, useState, useRef, useMemo, useCallback } from 'react';
|
|
4
|
+
import { useSearchParams } from 'next/navigation';
|
|
5
|
+
import { scraperStatus, beginScrape, notReadyMessage, busyReason, isCircleScan, loadScanNotes, NO_SCAN_NOTES } from '../lib/scraper-client';
|
|
5
6
|
import useScanner from './components/useScanner';
|
|
6
7
|
import useRequests from './components/useRequests';
|
|
7
8
|
import { requestCount } from '../lib/requests-client';
|
|
8
9
|
import { loadNetwork } from '../lib/network';
|
|
9
|
-
import { resolveView } from './components/views';
|
|
10
|
+
import { VIEWS, resolveView } from './components/views';
|
|
10
11
|
import Sidebar from './components/Sidebar';
|
|
11
12
|
import FilterPanel from './components/FilterPanel';
|
|
12
13
|
import OnboardingGate from './components/OnboardingGate';
|
|
@@ -19,6 +20,18 @@ import Link from 'next/link';
|
|
|
19
20
|
// One shared empty list, so "no 2nd-degree data" is the same value every render.
|
|
20
21
|
const NO_DEGREE2 = [];
|
|
21
22
|
|
|
23
|
+
// A job that changes the network. Setting the scanner up and signing in don't.
|
|
24
|
+
const CHANGES_NETWORK = (job) => Boolean(job) && !['install', 'setup', 'login'].includes(job.action);
|
|
25
|
+
|
|
26
|
+
/** Counts for the header and panels, from the two lists. */
|
|
27
|
+
function statsFor(d1, d2) {
|
|
28
|
+
const tierCounts = {};
|
|
29
|
+
d1.forEach(c => { tierCounts[c.tier] = (tierCounts[c.tier] || 0) + 1; });
|
|
30
|
+
const d2TierCounts = {};
|
|
31
|
+
d2.forEach(c => { d2TierCounts[c.tier] = (d2TierCounts[c.tier] || 0) + 1; });
|
|
32
|
+
return { total: d1.length, tiers: tierCounts, d2Total: d2.length, d2Tiers: d2TierCounts };
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
const TIER_COLORS = {
|
|
23
36
|
S: '#FFD700',
|
|
24
37
|
A: '#9B59B6',
|
|
@@ -28,9 +41,13 @@ const TIER_COLORS = {
|
|
|
28
41
|
};
|
|
29
42
|
|
|
30
43
|
export default function Home() {
|
|
44
|
+
// Suspense because HomeInner reads the address's ?chain= (useSearchParams),
|
|
45
|
+
// which Next requires to sit inside one for the page to build.
|
|
31
46
|
return (
|
|
32
47
|
<OnboardingGate>
|
|
33
|
-
<
|
|
48
|
+
<Suspense>
|
|
49
|
+
<HomeInner />
|
|
50
|
+
</Suspense>
|
|
34
51
|
</OnboardingGate>
|
|
35
52
|
);
|
|
36
53
|
}
|
|
@@ -54,16 +71,26 @@ function HomeInner() {
|
|
|
54
71
|
const [selected, setSelected] = useState(null);
|
|
55
72
|
const focusNodeRef = useRef(null);
|
|
56
73
|
const [filter, setFilter] = useState('all');
|
|
57
|
-
|
|
74
|
+
// A link to someone's circle in Bridge Chains, /?chain=<id> (the Scan page's
|
|
75
|
+
// "watch it fill in"), opens Degrees on it. The router's search params rather
|
|
76
|
+
// than window.location: on a click from another page the address bar only
|
|
77
|
+
// changes after this page has rendered, and the map opened on the Galaxy.
|
|
78
|
+
const linkedChain = useSearchParams().get('chain');
|
|
79
|
+
const [chainOpen, setChainOpen] = useState(linkedChain);
|
|
80
|
+
const [mode, setMode] = useState(() => (chainOpen ? 'degrees' : 'network'));
|
|
58
81
|
const [loading, setLoading] = useState(true);
|
|
59
82
|
const [stats, setStats] = useState(null);
|
|
60
83
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
|
|
61
84
|
const [filterPanelCollapsed, setFilterPanelCollapsed] = useState(true);
|
|
62
|
-
const [visualMode, setVisualMode] = useState('galaxy');
|
|
85
|
+
const [visualMode, setVisualMode] = useState(() => (chainOpen ? 'chain' : 'galaxy'));
|
|
63
86
|
const [notifications, setNotifications] = useState([]);
|
|
64
87
|
const [showNotifs, setShowNotifs] = useState(false);
|
|
65
88
|
const [csvMode, setCsvMode] = useState(false);
|
|
66
89
|
const [csvSource, setCsvSource] = useState('csv');
|
|
90
|
+
// What the scanner noted: hidden lists, and lists read with nothing new in
|
|
91
|
+
// them. With the network, they say who is ready for a circle scan (lib/reach.js).
|
|
92
|
+
const [scanNotes, setScanNotes] = useState(NO_SCAN_NOTES);
|
|
93
|
+
const shapeRef = useRef('');
|
|
67
94
|
// Everyone with a request out, shared with every view (lib/requests-client.js).
|
|
68
95
|
const pendingCount = requestCount(useRequests());
|
|
69
96
|
|
|
@@ -85,21 +112,43 @@ function HomeInner() {
|
|
|
85
112
|
|
|
86
113
|
setDegree1(d1);
|
|
87
114
|
setDegree2(d2);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
d1.forEach(c => { tierCounts[c.tier] = (tierCounts[c.tier] || 0) + 1; });
|
|
91
|
-
const d2TierCounts = {};
|
|
92
|
-
d2.forEach(c => { d2TierCounts[c.tier] = (d2TierCounts[c.tier] || 0) + 1; });
|
|
93
|
-
setStats({ total: d1.length, tiers: tierCounts, d2Total: d2.length, d2Tiers: d2TierCounts });
|
|
115
|
+
shapeRef.current = networkShape(d1, d2);
|
|
116
|
+
setStats(statsFor(d1, d2));
|
|
94
117
|
setLoading(false);
|
|
95
118
|
}
|
|
96
119
|
load();
|
|
97
120
|
if (!IS_DEMO && !hasCsvNetwork()) {
|
|
121
|
+
loadScanNotes().then(setScanNotes);
|
|
98
122
|
// Fetch notifications
|
|
99
123
|
fetch(`/api/notifications?userId=${userId}`).then(r => r.json()).then(d => setNotifications(d.notifications || [])).catch(() => {});
|
|
100
124
|
}
|
|
101
125
|
}, [userId]);
|
|
102
126
|
|
|
127
|
+
// The network again, after a scan: circles fill in while they're scanned
|
|
128
|
+
// (Blake, 2026-09-28: "the d3 dots as it builds more in"). Only a real change
|
|
129
|
+
// is published, because the graph views rebuild their scene whenever these
|
|
130
|
+
// lists change identity (TRAPS §29).
|
|
131
|
+
const reload = useCallback(async () => {
|
|
132
|
+
if (IS_DEMO || hasCsvNetwork() || !userId) return;
|
|
133
|
+
try {
|
|
134
|
+
const [{ degree1: d1, degree2: d2 }, notes] = await Promise.all([loadNetwork(userId), loadScanNotes()]);
|
|
135
|
+
setScanNotes((prev) => (JSON.stringify(prev) === JSON.stringify(notes) ? prev : notes));
|
|
136
|
+
const shape = networkShape(d1, d2);
|
|
137
|
+
if (shape === shapeRef.current) return;
|
|
138
|
+
shapeRef.current = shape;
|
|
139
|
+
setDegree1(d1);
|
|
140
|
+
setDegree2(d2);
|
|
141
|
+
setStats(statsFor(d1, d2));
|
|
142
|
+
} catch { /* the app restarting, say: keep what's on screen */ }
|
|
143
|
+
}, [userId]);
|
|
144
|
+
|
|
145
|
+
// Followed once: Bridge Chains has opened it, so leaving that view and coming
|
|
146
|
+
// back, or reloading, starts from the overview.
|
|
147
|
+
const chainOpened = useCallback(() => {
|
|
148
|
+
setChainOpen(null);
|
|
149
|
+
try { window.history.replaceState(null, '', window.location.pathname); } catch { /* the link stays */ }
|
|
150
|
+
}, []);
|
|
151
|
+
|
|
103
152
|
const isDegreesMode = mode === 'degrees';
|
|
104
153
|
const connections = degree1;
|
|
105
154
|
|
|
@@ -138,22 +187,24 @@ function HomeInner() {
|
|
|
138
187
|
if (loading) {
|
|
139
188
|
return (
|
|
140
189
|
<div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', background: '#0a0a1a', color: '#fff' }}>
|
|
141
|
-
<div style={{ fontSize: 24, fontWeight: 700 }}>Loading
|
|
190
|
+
<div style={{ fontSize: 24, fontWeight: 700 }}>Loading Six Degrees…</div>
|
|
142
191
|
<div style={{ fontSize: 14, color: '#888', marginTop: 8 }}>Mapping your LinkedIn network</div>
|
|
143
192
|
</div>
|
|
144
193
|
);
|
|
145
194
|
}
|
|
146
195
|
|
|
147
|
-
// Resolved once, because two places care: the renderer below, and the
|
|
196
|
+
// Resolved once, because two places care: the renderer below, and the Orbit
|
|
148
197
|
// toggle, which hides over Separation.
|
|
149
198
|
const view = resolveView(visualMode, mode);
|
|
199
|
+
// Scanning and the Scan page belong to your own network, not the sample or a CSV.
|
|
200
|
+
const canScan = !IS_DEMO && !csvMode;
|
|
150
201
|
|
|
151
202
|
return (
|
|
152
203
|
<div data-map style={{ height: '100vh', overflow: 'hidden', background: '#0a0a1a', color: '#fff', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }}>
|
|
153
204
|
<header style={{ padding: isMobile ? '10px 12px' : '20px 30px', borderBottom: '1px solid rgba(255,255,255,0.1)' }}>
|
|
154
205
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: isMobile ? 4 : 8, flexWrap: isMobile ? 'wrap' : 'nowrap', gap: isMobile ? 6 : 0 }}>
|
|
155
206
|
<h1 style={{ fontSize: isMobile ? 16 : 28, fontWeight: 700, margin: 0, background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>
|
|
156
|
-
|
|
207
|
+
Six Degrees
|
|
157
208
|
</h1>
|
|
158
209
|
<div style={{ display: 'flex', gap: isMobile ? 2 : 4, background: 'rgba(255,255,255,0.08)', borderRadius: 8, padding: isMobile ? 2 : 3, flexWrap: isMobile ? 'wrap' : 'nowrap' }}>
|
|
159
210
|
<button
|
|
@@ -327,7 +378,7 @@ function HomeInner() {
|
|
|
327
378
|
</div>
|
|
328
379
|
)}
|
|
329
380
|
</div>}
|
|
330
|
-
{/* Refresh button —
|
|
381
|
+
{/* Refresh button — Check for new, once you say yes; notifies */}
|
|
331
382
|
{!IS_DEMO && !csvMode && <RefreshButton isMobile={isMobile} />}
|
|
332
383
|
{/* Profile icon — top right */}
|
|
333
384
|
<a href={IS_DEMO ? '/launch' : csvMode ? '/import' : '/profile'} style={{
|
|
@@ -423,6 +474,10 @@ function HomeInner() {
|
|
|
423
474
|
// all your scanned circles, and "already connected" needs everyone.
|
|
424
475
|
fullDegree1: degree1,
|
|
425
476
|
fullDegree2: degree2,
|
|
477
|
+
scanNotes,
|
|
478
|
+
canScan,
|
|
479
|
+
chainOpen,
|
|
480
|
+
onChainOpened: chainOpened,
|
|
426
481
|
};
|
|
427
482
|
return <View {...viewProps} />;
|
|
428
483
|
})()}
|
|
@@ -439,12 +494,16 @@ function HomeInner() {
|
|
|
439
494
|
onSelect={(node) => { setSelected(node || null); }}
|
|
440
495
|
onSwitchMode={(newMode) => { setMode(newMode); setFilter('all'); }}
|
|
441
496
|
onFocusNode={(nodeId) => { if (focusNodeRef.current) focusNodeRef.current(nodeId); }}
|
|
497
|
+
csvSource={IS_DEMO ? 'sample' : csvMode ? csvSource : null}
|
|
498
|
+
scanNotes={scanNotes}
|
|
499
|
+
canScan={canScan}
|
|
442
500
|
/>
|
|
501
|
+
{canScan && <NetworkRefresh onChange={reload} live={view.key === 'chain'} />}
|
|
443
502
|
|
|
444
|
-
{/*
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
phone it sat on top of the score column. */}
|
|
503
|
+
{/* Orbit, one tap from Bridge Chains — bottom right, Degrees mode only.
|
|
504
|
+
It used to say "Galaxy" and show Orbit: the Galaxy is a Network
|
|
505
|
+
Circle view, so asking for it here fell back to Orbit. Hidden over
|
|
506
|
+
Separation, where on a phone it sat on top of the score column. */}
|
|
448
507
|
{isDegreesMode && view.key !== 'separation' && (
|
|
449
508
|
<div style={{
|
|
450
509
|
position: 'absolute', bottom: 20, right: 20, zIndex: 20,
|
|
@@ -452,19 +511,19 @@ function HomeInner() {
|
|
|
452
511
|
background: 'rgba(0,0,0,0.7)', borderRadius: 20, padding: '6px 14px',
|
|
453
512
|
backdropFilter: 'blur(8px)', border: '1px solid rgba(255,255,255,0.08)',
|
|
454
513
|
}}>
|
|
455
|
-
<span style={{ fontSize: 10, color: '#666' }}
|
|
514
|
+
<span style={{ fontSize: 10, color: '#666' }}>{VIEWS.orbit.icon} {VIEWS.orbit.label}</span>
|
|
456
515
|
<div
|
|
457
|
-
onClick={() => setVisualMode(
|
|
516
|
+
onClick={() => setVisualMode(view.key === 'orbit' ? 'chain' : 'orbit')}
|
|
458
517
|
style={{
|
|
459
518
|
width: 32, height: 18, borderRadius: 9, cursor: 'pointer',
|
|
460
|
-
background:
|
|
519
|
+
background: view.key === 'orbit' ? '#FF6B35' : 'rgba(255,255,255,0.15)',
|
|
461
520
|
position: 'relative', transition: 'background 0.2s',
|
|
462
521
|
}}
|
|
463
522
|
>
|
|
464
523
|
<div style={{
|
|
465
524
|
width: 14, height: 14, borderRadius: '50%', background: '#fff',
|
|
466
525
|
position: 'absolute', top: 2,
|
|
467
|
-
left:
|
|
526
|
+
left: view.key === 'orbit' ? 16 : 2,
|
|
468
527
|
transition: 'left 0.2s',
|
|
469
528
|
}} />
|
|
470
529
|
</div>
|
|
@@ -475,8 +534,47 @@ function HomeInner() {
|
|
|
475
534
|
);
|
|
476
535
|
}
|
|
477
536
|
|
|
537
|
+
/**
|
|
538
|
+
* A cheap fingerprint of the network: who is in it, at which degree and in
|
|
539
|
+
* whose circle, and how they're marked. Equal fingerprints mean nothing a view
|
|
540
|
+
* draws has changed.
|
|
541
|
+
*/
|
|
542
|
+
function networkShape(d1, d2) {
|
|
543
|
+
let h = 0x811c9dc5;
|
|
544
|
+
const mix = (text) => {
|
|
545
|
+
for (let i = 0; i < text.length; i++) h = Math.imul(h ^ text.charCodeAt(i), 0x01000193);
|
|
546
|
+
};
|
|
547
|
+
for (const r of [...d1, ...d2]) {
|
|
548
|
+
mix(`${r.id}|${r.degree}|${r.source_connection_id ?? ''}|${r.unlocked_from_bridge_id ?? ''}|${r.tier}|${r.power_score}|${r.unlock_status ?? ''}|${r.profile_image_url ?? ''};`);
|
|
549
|
+
}
|
|
550
|
+
return `${d1.length}:${d2.length}:${h >>> 0}`;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Looks at the network again when a scan has changed it: once when any scan
|
|
554
|
+
// ends, and every 20 seconds while a circle is being scanned with Bridge Chains
|
|
555
|
+
// open, since the scanner saves every 10 pages and the circle fills in as it
|
|
556
|
+
// does. Its own component for the same reason as RefreshButton below.
|
|
557
|
+
function NetworkRefresh({ onChange, live }) {
|
|
558
|
+
const scan = useScanner();
|
|
559
|
+
const ended = scan.finished.find(CHANGES_NETWORK)?.startedAt ?? null;
|
|
560
|
+
const filling = live && scan.running && isCircleScan(scan);
|
|
561
|
+
useEffect(() => {
|
|
562
|
+
if (ended != null) onChange();
|
|
563
|
+
}, [ended, onChange]);
|
|
564
|
+
useEffect(() => {
|
|
565
|
+
if (!filling) return undefined;
|
|
566
|
+
const timer = setInterval(onChange, 20000);
|
|
567
|
+
return () => clearInterval(timer);
|
|
568
|
+
}, [filling, onChange]);
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
|
|
478
572
|
// Its own component so the scanner's answer, which changes every second or two
|
|
479
573
|
// while a scan runs, re-renders this button and not the whole map.
|
|
574
|
+
//
|
|
575
|
+
// It asks first, as a company scan does, and says what it will do and what it
|
|
576
|
+
// costs: one click on a small icon used to open a Chrome window on LinkedIn
|
|
577
|
+
// with no word of what it was about to do.
|
|
480
578
|
function RefreshButton({ isMobile }) {
|
|
481
579
|
const scan = useScanner();
|
|
482
580
|
const busy = busyReason(scan);
|
|
@@ -486,6 +584,13 @@ function RefreshButton({ isMobile }) {
|
|
|
486
584
|
try {
|
|
487
585
|
const blocked = notReadyMessage(await scraperStatus());
|
|
488
586
|
if (blocked) { alert(blocked); return; }
|
|
587
|
+
const ok = window.confirm(
|
|
588
|
+
'Check for new connections?\n\nThis opens a Chrome window on your LinkedIn connections ' +
|
|
589
|
+
'list and reads it from the newest, stopping once it reaches people already saved. It ' +
|
|
590
|
+
'usually takes under a minute. It reads your own list, not a search, so it doesn’t ' +
|
|
591
|
+
'use your search budget, but like any scan it is LinkedIn traffic from your account.',
|
|
592
|
+
);
|
|
593
|
+
if (!ok) return;
|
|
489
594
|
await beginScrape('refresh');
|
|
490
595
|
alert('Checking for new connections — watch it on the Scan page.');
|
|
491
596
|
} catch (e) {
|
|
@@ -493,7 +598,7 @@ function RefreshButton({ isMobile }) {
|
|
|
493
598
|
}
|
|
494
599
|
}}
|
|
495
600
|
disabled={Boolean(busy)}
|
|
496
|
-
title={busy ? `${busy}. One scan at a time.` : '
|
|
601
|
+
title={busy ? `${busy}. One scan at a time.` : 'Check for new connections'}
|
|
497
602
|
style={{
|
|
498
603
|
width: isMobile ? 28 : 32, height: isMobile ? 28 : 32, borderRadius: '50%', border: 'none',
|
|
499
604
|
cursor: busy ? 'not-allowed' : 'pointer', opacity: busy ? 0.4 : 1,
|
|
@@ -12,6 +12,7 @@ import { useUser } from '../components/UserProvider';
|
|
|
12
12
|
import useScanner from '../components/useScanner';
|
|
13
13
|
import Link from 'next/link';
|
|
14
14
|
import { companyOf, getSeniority } from '../../lib/companies';
|
|
15
|
+
import { localPhoto } from '../../lib/photos';
|
|
15
16
|
|
|
16
17
|
const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
|
|
17
18
|
const TABS = [['map', 'Map'], ['industries', 'Industries'], ['companies', 'Companies']];
|
|
@@ -466,8 +467,8 @@ function PathPersonRow({ p, level, companyPeople, d1Data, selectedCompany }) {
|
|
|
466
467
|
}}>{p.connected ? '✓' : '🔒'}</div>
|
|
467
468
|
|
|
468
469
|
{/* Photo */}
|
|
469
|
-
{(p.profile_image_url || p.imageUrl) ? (
|
|
470
|
-
<img src={p.profile_image_url || p.imageUrl} alt="" style={{
|
|
470
|
+
{localPhoto(p.profile_image_url || p.imageUrl) ? (
|
|
471
|
+
<img src={localPhoto(p.profile_image_url || p.imageUrl)} alt="" style={{
|
|
471
472
|
width: 26, height: 26, borderRadius: '50%', objectFit: 'cover', flexShrink: 0,
|
|
472
473
|
}} onError={e => e.target.style.display = 'none'} />
|
|
473
474
|
) : (
|
|
@@ -11,6 +11,9 @@ import Link from 'next/link';
|
|
|
11
11
|
import useRequests from '../components/useRequests';
|
|
12
12
|
import { hasRequest, markRequested, undoRequest } from '../../lib/requests-client';
|
|
13
13
|
import { keyFor } from '../../lib/separation';
|
|
14
|
+
import { localPhoto } from '../../lib/photos';
|
|
15
|
+
import { reachIndex } from '../../lib/reach';
|
|
16
|
+
import { loadScanNotes } from '../../lib/scraper-client';
|
|
14
17
|
|
|
15
18
|
const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
|
|
16
19
|
|
|
@@ -51,6 +54,8 @@ function QueueInner() {
|
|
|
51
54
|
const [view, setView] = useState('quest'); // quest | recs | pending
|
|
52
55
|
const [added, setAdded] = useState([]);
|
|
53
56
|
const [mappedIds, setMappedIds] = useState(() => new Set());
|
|
57
|
+
// Whose circle can be scanned next, hidden lists known (lib/reach.js).
|
|
58
|
+
const [reach, setReach] = useState(null);
|
|
54
59
|
const [bridgeFilter, setBridgeFilter] = useState(null);
|
|
55
60
|
|
|
56
61
|
// Default to bridge view — shows D1 person → their D2 people
|
|
@@ -75,14 +80,16 @@ function QueueInner() {
|
|
|
75
80
|
if (IS_DEMO) return;
|
|
76
81
|
if (!userId) return;
|
|
77
82
|
async function load() {
|
|
78
|
-
const [net, pendingRes] = await Promise.all([
|
|
83
|
+
const [net, pendingRes, notes] = await Promise.all([
|
|
79
84
|
loadNetwork(userId),
|
|
80
85
|
fetch(`/api/outreach?userId=${userId}`).then(r => r.json()).catch(() => ({ pending: [] })),
|
|
86
|
+
loadScanNotes(),
|
|
81
87
|
]);
|
|
82
88
|
const d1 = net.degree1;
|
|
83
89
|
const d2 = net.degree2;
|
|
84
90
|
setAdded(d1.filter(c => c.unlocked_from_bridge_id));
|
|
85
91
|
setMappedIds(new Set(d2.map(c => c.source_connection_id).filter(Boolean)));
|
|
92
|
+
setReach(reachIndex(d1, d2, notes));
|
|
86
93
|
const d1Urls = new Set(d1.map(c => c.profile_url));
|
|
87
94
|
const bridgeById = {};
|
|
88
95
|
d1.forEach(c => { bridgeById[c.id] = c; });
|
|
@@ -323,6 +330,7 @@ function QueueInner() {
|
|
|
323
330
|
sentIds={sentIds}
|
|
324
331
|
added={added}
|
|
325
332
|
mappedIds={mappedIds}
|
|
333
|
+
reach={reach}
|
|
326
334
|
onSend={(r) => markRequested(r, { bridgeId: r.source_connection_id }).catch(() => {})}
|
|
327
335
|
onUndo={(r) => undoRequest(r).catch(() => {})}
|
|
328
336
|
/>
|
|
@@ -355,8 +363,8 @@ function QueueInner() {
|
|
|
355
363
|
border: '1px solid rgba(255,107,53,0.1)',
|
|
356
364
|
}}>
|
|
357
365
|
{/* Photo */}
|
|
358
|
-
{p.profile_image_url ? (
|
|
359
|
-
<img src={p.profile_image_url} alt="" style={{
|
|
366
|
+
{localPhoto(p.profile_image_url) ? (
|
|
367
|
+
<img src={localPhoto(p.profile_image_url)} alt="" style={{
|
|
360
368
|
width: 36, height: 36, borderRadius: '50%', objectFit: 'cover', flexShrink: 0,
|
|
361
369
|
border: `2px solid ${TIER_COLORS[p.tier] || '#555'}`,
|
|
362
370
|
}} onError={e => { e.target.style.display = 'none'; }} />
|
|
@@ -436,8 +444,8 @@ function QueueInner() {
|
|
|
436
444
|
border: `1px solid ${TIER_COLORS[bridge.tier] || '#555'}20`,
|
|
437
445
|
}}>
|
|
438
446
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
|
439
|
-
{bridge.profile_image_url ? (
|
|
440
|
-
<img src={bridge.profile_image_url} alt="" style={{
|
|
447
|
+
{localPhoto(bridge.profile_image_url) ? (
|
|
448
|
+
<img src={localPhoto(bridge.profile_image_url)} alt="" style={{
|
|
441
449
|
width: 36, height: 36, borderRadius: '50%', objectFit: 'cover',
|
|
442
450
|
border: `2px solid ${TIER_COLORS[bridge.tier]}`,
|
|
443
451
|
}} onError={e => { e.target.style.display = 'none'; }} />
|
|
@@ -563,8 +571,8 @@ function QueueInner() {
|
|
|
563
571
|
<input type="checkbox" checked={selected.has(r.id)}
|
|
564
572
|
onChange={() => toggleSelect(r.id)} onClick={e => e.stopPropagation()}
|
|
565
573
|
style={{ cursor: 'pointer', flexShrink: 0 }} />
|
|
566
|
-
{r.profile_image_url ? (
|
|
567
|
-
<img src={r.profile_image_url} alt="" style={{
|
|
574
|
+
{localPhoto(r.profile_image_url) ? (
|
|
575
|
+
<img src={localPhoto(r.profile_image_url)} alt="" style={{
|
|
568
576
|
width: 28, height: 28, borderRadius: '50%', objectFit: 'cover', flexShrink: 0,
|
|
569
577
|
border: `1.5px solid ${TIER_COLORS[r.tier] || '#555'}`,
|
|
570
578
|
}} onError={e => { e.target.style.display = 'none'; }} />
|
|
@@ -610,8 +618,8 @@ function QueueInner() {
|
|
|
610
618
|
style={{ cursor: 'pointer', flexShrink: 0 }} />
|
|
611
619
|
|
|
612
620
|
{/* Photo */}
|
|
613
|
-
{r.profile_image_url ? (
|
|
614
|
-
<img src={r.profile_image_url} alt="" style={{
|
|
621
|
+
{localPhoto(r.profile_image_url) ? (
|
|
622
|
+
<img src={localPhoto(r.profile_image_url)} alt="" style={{
|
|
615
623
|
width: 32, height: 32, borderRadius: '50%', objectFit: 'cover', flexShrink: 0,
|
|
616
624
|
border: `1.5px solid ${TIER_COLORS[r.tier] || '#555'}`,
|
|
617
625
|
}} onError={e => { e.target.style.display = 'none'; e.target.nextSibling.style.display = 'flex'; }} />
|
|
@@ -619,7 +627,7 @@ function QueueInner() {
|
|
|
619
627
|
<div style={{
|
|
620
628
|
width: 32, height: 32, borderRadius: '50%', flexShrink: 0, fontSize: 12, fontWeight: 700,
|
|
621
629
|
background: TIER_COLORS[r.tier] || '#555', color: r.tier === 'S' ? '#000' : '#fff',
|
|
622
|
-
display: r.profile_image_url ? 'none' : 'flex', alignItems: 'center', justifyContent: 'center',
|
|
630
|
+
display: localPhoto(r.profile_image_url) ? 'none' : 'flex', alignItems: 'center', justifyContent: 'center',
|
|
623
631
|
}}>{r.name?.charAt(0)}</div>
|
|
624
632
|
|
|
625
633
|
{/* Info */}
|