PageSourceSearch

https://app.tweeksocial.com/assets/AutoFollow-BeYQVoDR.js

js tweeksocial.com collected 2026-10-02 01:47:55 UTC 26,481 bytes, 4 lines download raw bytes

1import{c as Be,s as Ee,P as De,u as h,F as $,t as q,j as e,T as A,B as c,r as i,q as Ye,C as k,h as le,al as ke,i as ie,k as Xe,d as _,x as U,D as Je,G as Ze,m as et,n as tt,o as st,p as at,A as S,I as Se,an as nt,b as ot,V as rt,W as lt,X as it,Y as ct,_ as dt,$ as ut,a0 as mt,a1 as xt,a2 as ht}from"./index-B1Dqzo5J.js";import{S as pt}from"./switch-DBPZcVAR.js";import{S as ce,a as de,b as ue,c as me,d as xe}from"./select-Ca3d8v_T.js";import{B as ft}from"./badge-BMocXsDX.js";import{A as V,a as K}from"./alert-DwiRAobI.js";import{u as gt}from"./use-selected-account-KbUdbeZM.js";import{S as wt}from"./scroll-area-7sOwTdRD.js";import{E as jt}from"./ExtensionAutomationCard-CXZRsYg5.js";import{F as vt}from"./FreePlanUpgradePrompt-B9GvmjnO.js";import{R as yt}from"./ResumesIn-mqxOIjbp.js";import{C as Ae}from"./clock-vYorjXDH.js";import{C as pe}from"./chrome-C4v3d477.js";import{P as Nt}from"./play-BtCJAbER.js";import{R as Te}from"./refresh-cw-DBD9bhK9.js";import{P as bt}from"./plus-CZgM6C45.js";import{T as Ct}from"./trash-2-CkEccJ5c.js";import{E as he}from"./external-link-DgBxRess.js";import{H as kt}from"./history-Da1_XgOf.js";import{C as St}from"./circle-check-BWAwCnof.js";import{f as At}from"./formatDistanceToNow-HYtYLUgw.js";import"./index-B3_Bjc4w.js";import"./progress-CahChz5b.js";import"./collapsible-CaXuVoyF.js";import"./pause-DGlDBHc3.js";import"./arrow-right-CsT2ecdt.js";import"./en-US-BddybvhD.js";const Tt=Be("ArrowDown",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]]),Ft="https://chromewebstore.google.com/";function Et({accountId:p}){const{toast:N}=Ee(),g=De(),T=["/api/extension/status",p],{data:w}=h({queryKey:T,queryFn:async()=>{const d=await fetch(`/api/extension/status/${p}`,{credentials:"include"});if(!d.ok)throw new Error("Failed to load extension status");return d.json()},enabled:!!p,refetchInterval:15e3}),F=$({mutationFn:async d=>(await q("POST",`/api/extension/control/${p}`,{action:d})).json(),onSuccess:()=>g.invalidateQueries({queryKey:T}),onError:d=>N({title:"Could not start",description:d.message,variant:"destructive"})}),a=w?.diagnosis;if(!a||a.state==="working"&&!a.gap)return null;const E=a.state==="working";return e.jsxs(V,{className:E?"":a.state==="blocked"?"border-destructive/40 bg-destructive/5":a.state==="stopped"?"border-amber-500/40 bg-amber-500/5":a.state==="waiting"?"border-sky-500/40 bg-sky-500/5":"","data-testid":"alert-automation-diagnosis",children:[E?e.jsx(Ae,{className:"h-4 w-4 text-muted-foreground"}):a.state==="blocked"?e.jsx(A,{className:"h-4 w-4 text-destructive"}):a.state==="stopped"?e.jsx(A,{className:"h-4 w-4 text-amber-600 dark:text-amber-500"}):a.state==="waiting"?e.jsx(Ae,{className:"h-4 w-4 text-sky-600 dark:text-sky-400"}):e.jsx(pe,{className:"h-4 w-4"}),e.jsx(K,{children:e.jsxs("div",{className:"flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"font-semibold text-foreground","data-testid":"text-diagnosis-title",children:a.title}),e.jsx("p",{className:"text-sm",children:a.detail}),a.gap&&e.jsxs("p",{className:"text-sm text-muted-foreground","data-testid":"text-diagnosis-gap",children:[a.gap," Nothing runs while the browser is closed."]}),a.resumesAt&&e.jsx(yt,{at:a.resumesAt}),a.fix&&e.jsx("p",{className:"text-sm font-medium text-foreground",children:a.fix})]}),a.state==="stopped"&&w?.settings?.isRunning===!1&&e.jsxs(c,{size:"sm",className:"shrink-0",disabled:F.isPending,onClick:()=>F.mutate("start"),"data-testid":"button-diagnosis-start",children:[e.jsx(Nt,{className:"mr-1.5 h-3.5 w-3.5"})," Start"]}),w?.connected===!1&&e.jsx(c,{variant:"outline",size:"sm",className:"shrink-0",onClick:()=>window.open(Ft,"_blank"),children:"Get the extension"})]})})]})}const Dt="https://chromewebstore.google.com/detail/tweeksocial/ccjpkljlocbggilkcbeekabapplgofga";function Fe({n:p,title:N,subtitle:g}){return e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-primary text-sm font-bold text-primary-foreground",children:p}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("h2",{className:"text-lg font-semibold leading-tight",children:N}),e.jsx("p",{className:"text-sm text-muted-foreground",children:g})]}),e.jsx("div",{className:"ml-2 hidden h-px flex-1 bg-border sm:block"})]})}function ds(){const[p,N]=i.useState(!1),[g,T]=i.useState(""),[w,F]=i.useState(null),[a,E]=i.useState(null),[d,z]=i.useState(""),[M,Ie]=i.useState(""),[l,D]=i.useState("scrape"),{toast:r}=Ee(),x=De(),{user:O}=Ye(),Pe=i.useRef(null);i.useEffect(()=>{const t=new URLSearchParams(window.location.search);if(t.get("checkout")!=="success")return;const n=t.get("session_id");window.history.replaceState({},"",window.location.pathname),(async()=>{try{n?(await q("POST","/api/checkout/verify",{sessionId:n}
1),r({title:"Payment successful",description:"Auto Follow has been unlocked for your account."})):r({title:"Payment successful",description:"Your addon is being activated."})}catch{r({title:"Payment received",description:"Activation is taking a moment. If it doesn't unlock shortly, please refresh or contact support."})}finally{x.invalidateQueries({queryKey:["/api/auth/user"]}),x.invalidateQueries({queryKey:["/api/subscription"]})}})()},[]);const{data:I,isLoading:Re}=h({queryKey:["/api/accounts"]}),[s,Le]=gt(I),{data:fe,isLoading:_e}=h({queryKey:["/api/scrapes"]}),{data:Q}=h({queryKey:["/api/csv-uploads"]}),{data:Ue}=h({queryKey:["/api/accounts",s,"ai-mode"],enabled:!!s}),W=Ue?.isActive??!1,{data:j,isLoading:It,refetch:G}=h({queryKey:["/api/accounts",s,"auto-follow"],enabled:!!s,refetchInterval:3e4}),{data:B,refetch:Pt}=h({queryKey:["/api/accounts",s,"auto-follow","history"],enabled:!!s}),{data:H,refetch:ge}=h({queryKey:["/api/accounts",s,"auto-follow","tasks"],enabled:!!s}),Y=O?.subscription,qe=(Y?.planType||"free")==="lifetime",Me=Y!=null&&Y.hasAutoFollow===!1,Oe=async()=>{try{if(qe){const t=await q("POST","/api/checkout/auto-follow-addon"),{url:n}=await t.json();window.location.href=n}else window.location.href="/pricing"}catch(t){r({title:"Error",description:t.message,variant:"destructive"})}};if(Me)return e.jsx("div",{className:"container mx-auto py-12 max-w-4xl",children:e.jsxs(k,{className:"border-2 border-primary/20 shadow-lg overflow-hidden relative",children:[e.jsx("div",{className:"absolute inset-0 bg-gradient-to-br from-primary/5 to-transparent pointer-events-none"}),e.jsxs(le,{className:"text-center pb-8 border-b bg-muted/20",children:[e.jsx("div",{className:"mx-auto w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mb-4",children:e.jsx(ke,{className:"h-8 w-8 text-primary"})}),e.jsx(ie,{className:"text-3xl font-bold",children:"Auto Follow Campaigns"}),e.jsx(Xe,{className:"text-lg mt-2 max-w-2xl mx-auto",children:"Grow your audience automatically with smart, safe follow campaigns."})]}),e.jsx(_,{className:"space-y-8 py-12",children:e.jsxs("div",{className:"text-center space-y-6 bg-muted/30 p-8 rounded-xl border border-dashed border-primary/30",children:[e.jsx("h3",{className:"text-xl font-semibold",children:"Unlock Auto Follow for your Lifetime Plan"}),e.jsx("p",{className:"text-muted-foreground max-w-lg mx-auto",children:"Unlock the Auto Follow and Unfollow features for your Lifetime plan."}),e.jsx(c,{size:"lg",className:"w-full sm:w-auto px-8",onClick:Oe,children:"Unlock for $249 / year"})]})})]})});i.useEffect(()=>{if(!O)return;let t=null,n=null,o=0,m=!1;const f=20,R=3e3,b=()=>{if(!m)try{const y=window.location.protocol==="https:"?"wss:":"ws:",be=window.location.host;if(!be)return;const Ke=`${y}//${be}/ws`;t=new WebSocket(Ke),Pe.current=t,t.onopen=()=>{console.log("[AutoFollow] WebSocket connected"),o=0,t?.send(JSON.stringify({type:"register_user",userId:O.id}))},t.onmessage=C=>{try{const u=JSON.parse(C.data);if(u.type==="auto_follow_update"&&s&&u.accountId===s){console.log("WS RECV auto_follow_update:",u.stats),x.setQueryData(["/api/accounts",s,"auto-follow"],L=>L&&u.stats);const Ce={id:Date.now(),targetHandle:u.targetHandle,status:u.status,followedAt:u.timestamp,createdAt:u.timestamp,errorMessage:u.error};x.setQueryData(["/api/accounts",s,"auto-follow","history"],L=>L?[Ce,...L].filter((ze,Qe,We)=>We.findIndex(Ge=>Ge.id===ze.id)===Qe).slice(0,50):[Ce]),x.invalidateQueries({queryKey:["/api/accounts",s,"auto-follow","tasks"]})}}catch(u){console.error("WS Parse error",u)}},t.onerror=C=>{console.warn("[AutoFollow] WebSocket error:",C)},t.onclose=()=>{if(!m&&o<f){const C=Math.min(R*Math.pow(1.5,o),3e4);console.log(`[AutoFollow] WebSocket closed, reconnecting in ${Math.round(C/1e3)}s (attempt ${o+1})`),o++,n=setTimeout(b,C)}}}catch(y){console.warn("[AutoFollow] WebSocket connection failed:",y)}},v=setTimeout(b,50);return()=>{m=!0,clearTimeout(v),n&&clearTimeout(n),t&&(t.onclose=null,t.readyState===WebSocket.OPEN&&t.close())}},[O,s,x]);const X=2e3,J=I?.find(t=>t.id===s),we=J?.lastSyncFollowersCount??0,Z=J?.lastSyncFollowingCount??0,ee=Z>=X&&we<Z*.5,te=$({mutationFn:async()=>(await q("POST",`/api/accounts/${s}/refresh-counts`)).json(),onSuccess:t=>{x.invalidateQueries({queryKey:["/api/accounts"]}
1),r({title:t.canFollow?"Auto Follow is available":"Counts updated",description:`${t.followersCount.toLocaleString()} followers, ${t.followingCount.toLocaleString()} following.`+(t.canFollow?" Auto Follow is available.":` At ${t.ratioThreshold?.toLocaleString?.()??X.toLocaleString()}+ following, at least 1 follower per 2 followed accounts is required.`)})},onError:t=>r({title:"Could not refresh",description:t.message,variant:"destructive"})}),[se,ae]=i.useState(null),P=$({mutationFn:async({accountId:t,scrapeRunId:n,csvUploadId:o,csvHandles:m,taskId:f,isEnabled:R,name:b})=>{ae(null);const v=await fetch(`/api/accounts/${t}/auto-follow`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({scrapeRunId:n,csvUploadId:o,csvHandles:m,taskId:f,isEnabled:R,name:b})});if(!v.ok){const y=await v.json().catch(()=>({message:"Unknown error"}));throw v.status===409?(ae(y.message||"Cannot enable Auto Follow right now."),new Error(y.message||"Conflict")):new Error(y.message||await v.text())}return v.json()},onSuccess:()=>{x.invalidateQueries({queryKey:["/api/accounts",s,"auto-follow"]}),ge(),N(!1),T(""),F(null),E(null),z(""),D("scrape"),r({title:"Success",description:"Campaign settings updated."})},onError:t=>{se||r({title:"Error",description:t.message,variant:"destructive"})}}),He=()=>{if(!s||!g.trim()){r({title:"Validation Error",description:"Please provide a campaign name.",variant:"destructive"});return}if(l==="scrape"&&!w){r({title:"Validation Error",description:"Please select a scrape run.",variant:"destructive"});return}if(l==="csv_saved"&&!a){r({title:"Validation Error",description:"Please select a saved CSV.",variant:"destructive"});return}if(l==="csv_paste"&&!d.trim()){r({title:"Validation Error",description:"Please paste some handles.",variant:"destructive"});return}if(l==="tweet_likers"&&!M.trim()){r({title:"Validation Error",description:"Please provide a tweet URL.",variant:"destructive"});return}if(l==="tweet_likers"&&!/\/status\/\d+/.test(M)){r({title:"Validation Error",description:"Invalid tweet URL. It should look like https://x.com/user/status/123456",variant:"destructive"});return}const t=l==="scrape"?{scrapeRunId:w}:l==="csv_saved"?{csvUploadId:a}:l==="tweet_likers"?{tweetUrl:M.trim()}:{csvHandles:d};P.mutate({accountId:s,...t,name:g,isEnabled:!0})},$e=(t,n)=>{s&&P.mutate({accountId:s,taskId:t,isEnabled:n})},je=fe?.filter(t=>t.status!=="cancelled"&&(t.filteredCount>0||t.scrapedCount>0))||[],{data:ve}=h({queryKey:["/api/extension/status",s],queryFn:async()=>{const t=await fetch(`/api/extension/status/${s}`,{credentials:"include"});if(!t.ok)throw new Error("Failed to load extension status");return t.json()},enabled:!!s,refetchInterval:15e3}),ye=!!ve?.connected,Ve=ye&&!!ve?.settings?.followCampaignsEnabled,Ne=$({mutationFn:async t=>(await q("DELETE",`/api/accounts/${s}/auto-follow/tasks/${t}`)).json(),onSuccess:()=>{r({title:"Campaign deleted"}),x.invalidateQueries({queryKey:["/api/accounts",s,"auto-follow","tasks"]}),ge()},onError:t=>{r({title:"Could not delete campaign",description:t.message,variant:"destructive"})}}),ne=H?.filter(t=>t.task.status==="active").length||0,[Lt,oe]=i.useState(""),[_t,re]=i.useState(0);return i.useEffect(()=>{if(!j?.nextFollowAt||!j?.isInCooldown){oe(""),re(0);return}const t=new Date(j.nextFollowAt).getTime(),n=j.cooldownRemainingMs||0,o=setInterval(()=>{const m=Date.now(),f=Math.max(0,t-m);if(f<=0){oe("Following now..."),re(100),G(),clearInterval(o);return}const R=Math.floor(f/6e4),b=Math.floor(f%6e4/1e3);oe(`${R}m ${b.toString().padStart(2,"0")}s`),re(n>0?Math.min(100,(n-f)/n*100):0)},1e3);return()=>clearInterval(o)},[j?.nextFollowAt,j?.isInCooldown,j?.cooldownRemainingMs,G]),e.jsxs("div",{className:"container mx-auto py-6 space-y-6",children:[e.jsx(vt,{kind:"follow"}),s&&e.jsx(Et,{accountId:s}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsxs("h1",{className:"text-3xl font-bold flex items-center gap-3",children:[e.jsx(ke,{className:"h-8 w-8 text-primary"}),"Auto Follow Campaigns"]}),e.jsx("p",{className:"text-muted-foreground mt-1",children:"Create a campaign here — the Chrome extension does the following from your own browser, at a safe pace."})]}),e.jsx("div",{className:"flex gap-2",children:e.jsxs(c,{variant:"outline",size:"sm",onClick:()=>G(),children:[e.jsx(Te,{className:"h-4 w-4 mr-2"}),"Refresh"]})})]}),se&&e.jsxs("div",{className:"flex items-start gap-3 p-4 rounded-lg bg-amber-500/10 border border-amber-500/30 text-amber-700 dark:text-amber-400",children:[e.jsx(A,{className:"h-5 w-5 mt-0.5 shrink-0"}),e.jsxs("div",{className:"flex-1",children:[e.jsx("p",{className:"font-semibold",children:"Cannot Enable Auto Follow"}),e.jsx("p",{className:"text-sm mt-1",children:se}),e.jsxs("p",{className:"text-sm mt-1 text-muted-foreground",children:["Go to the ",e.jsx("a",{href:"/unfollow-non-followers",className:"underline text-primary",children:"Auto Unfollow Manager"})," and wait for the job to complete, then try again."]})]}),e.jsx("button",{onClick:()=>ae(null),className:"text-amber-600 hover:text-amber-800 text-xl leading-none",children:"×"})]}),e.jsxs(k,{children:[e.jsx(le,{className:"pb-3",children:e.jsx(ie,{className:"text-lg",children:"Select Account"})}),e.jsx(_,{children:Re?e.jsx(U,{className:"h-6 w-6 animate-spin"}):I&&I.length>0?e.jsxs(ce,{value:s?.toString()??"",onValueChange:t=>Le(parseInt(t)),children:[e.jsx(de,{className:"w-full md:w-[300px]",children:e.jsx(ue,{placeholder:"Select an account"})}),e.jsx(me,{children:I.map(t=>e.jsxs(xe,{value:t.id.toString(),children:["@",t.handle]},t.id))})]}):e.jsx(V,{children:e.jsx(K,{children:"No accounts found. Please add a Twitter account first."})})})]}),s&&W&&e.jsxs(V,{className:"border-yellow-500/30 bg-yellow-500/5",children:[e.jsx(A,{className:"h-4 w-4 text-yellow-500"}),e.jsx(K,{className:"text-yellow-700 dark:text-yellow-300 font-medium",children:"AI Mode is currently active for this account. Please disable AI Mode before starting Auto Follow."})]}),s&&ee&&e.jsxs(V,{className:"border-red-500/30 bg-red-500/5",children:[e.jsx(A,{className:"h-4 w-4 text-red-500"}),e.jsxs(K,{className:"text-red-700 dark:text-red-300",children:[e.jsx("p",{className:"font-semibold",children:"Followers/Following Ratio Too Low"}),e.jsxs("p",{className:"text-sm mt-1",children:["Your account @",J?.handle," has ",we.toLocaleStr
1ing()," followers and ",Z.toLocaleString()," following. After ",X.toLocaleString()," following, Auto Follow requires at least 1 follower for every 2 accounts followed."]}),e.jsxs("p",{className:"text-sm mt-1",children:[e.jsx("a",{href:"/unfollow-non-followers",className:"underline text-primary font-medium",children:"Go to Auto Unfollow Manager"})," to clean up your following list."]}),e.jsx("p",{className:"text-sm mt-2 text-muted-foreground",children:"Already unfollowed them? Refresh the count to unlock Auto Follow."}),e.jsx("p",{className:"text-sm mt-2 font-medium",children:Ve?"Auto Follow is switched on and still running — this warning does not stop it. Its progress is below, where you can pause it.":"Everything below still works. You can watch progress and start or pause the extension; only creating a new campaign waits until the ratio improves."}),e.jsx(c,{size:"sm",variant:"outline",className:"mt-2",disabled:te.isPending,onClick:()=>te.mutate(),"data-testid":"button-refresh-counts",children:te.isPending?e.jsxs(e.Fragment,{children:[e.jsx(U,{className:"mr-1.5 h-3.5 w-3.5 animate-spin"})," Checking X…"]}):e.jsxs(e.Fragment,{children:[e.jsx(Te,{className:"mr-1.5 h-3.5 w-3.5"})," Refresh follower counts"]})})]})]}),s&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[e.jsx(Fe,{n:1,title:"Create a campaign",subtitle:"Pick who this account should follow"}),e.jsxs(Je,{open:p,onOpenChange:N,children:[e.jsx(Ze,{asChild:!0,children:e.jsxs(c,{className:"shrink-0",disabled:ne>0||ee,title:ee?"Improve the ratio first — see the warning above":ne>0?"Only 1 campaign per account — delete the existing one first":void 0,children:[e.jsx(bt,{className:"mr-2 h-4 w-4"}),ne>0?"Campaign Active":"Create Campaign"]})}),e.jsxs(et,{children:[e.jsxs(tt,{children:[e.jsx(st,{children:"Create Follow Campaign"}),e.jsx(at,{children:"Select a source of users and name your campaign."})]}),e.jsxs("div",{className:"space-y-4 py-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(S,{htmlFor:"camp-name",children:"Campaign Name"}),e.jsx(Se,{id:"camp-name",placeholder:"e.g. Graphic Designers Q1",value:g,onChange:t=>T(t.target.value)})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(S,{children:"Source Type"}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(c,{type:"button",size:"sm",variant:l==="scrape"?"default":"outline",onClick:()=>D("scrape"),children:"Scrape Run"}),e.jsx(c,{type:"button",size:"sm",variant:l==="csv_saved"?"default":"outline",onClick:()=>D("csv_saved"),children:"Saved Open DM CSVs"}),e.jsx(c,{type:"button",size:"sm",variant:l==="csv_paste"?"default":"outline",onClick:()=>D("csv_paste"),children:"Paste Handles"}),e.jsx(c,{type:"button",size:"sm",variant:l==="tweet_likers"?"default":"outline",onClick:()=>D("tweet_likers"),children:"Tweet Likers"})]})]}),l==="scrape"?e.jsxs("div",{className:"space-y-2",children:[e.jsx(S,{children:"Lead Source (Scrape Run)"}),_e?e.jsx(U,{className:"h-4 w-4 animate-spin"}):je.length>0?e.jsxs(ce,{value:w?.toString()??"",onValueChange:t=>F(parseInt(t)),children:[e.jsx(de,{children:e.jsx(ue,{placeholder:"Select a scrape run"})}),e.jsx(me,{children:je.map(t=>e.jsxs(xe,{value:t.id.toString(),children:["@",t.sourceHandle," (",t.filteredCount," users) - ",new Date(t.createdAt).toLocaleDateString()]},t.id))})]}):e.jsx("div",{className:"text-sm text-red-500",children:"No scrape runs available. Please scrape some followers first."})]}):l==="csv_saved"?e.jsxs("div",{className:"space-y-2",children:[e.jsx(S,{children:"Lead Source (Saved Open DM CSV)"}),Q?Q.length>0?e.jsxs(ce,{value:a?.toString()??"",onValueChange:t=>E(parseInt(t)),children:[e.jsx(de,{children:e.jsx(ue,{placeholder:"Select a saved CSV"})}),e.jsx(me,{children:Q.map(t=>e.jsxs(xe,{value:t.id.toString(),children:[t.fileName," (",t.filteredCount," users) - ",new Date(t.createdAt).toLocaleDateString()]},t.id))})]}):e.jsx("div",{className:"text-sm text-red-500",children:"No CSV uploads available. Please use the Upload CSV page to upload one first."}):e.jsx(U,{className:"h-4 w-4 animate-spin"}),e.jsx("div",{className:"text-xs text-muted-foreground mt-1",children:'Tip: Go to the "Upload CSV" page to upload new lists specifically filtered for open DMs.'})]}):l==="tweet_likers"?e.jsxs("div",{className:"space-y-2",children:[e.jsx(S,{children:"Tweet URL"}),e.jsx(Se,{placeholder:"https://x.com/username/status/1234567890",value:M,onChange:t=>Ie(t.target.value)}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Paste a tweet/post URL. All users who liked that tweet will be added to the follow queue."})]}):e.jsxs("div",{className:"space-y-2",children:[e.jsx(S,{children:"Paste Handles (one per line, or comma-separated)"}),e.jsx("textarea",{className:"w-full min-h-[120px] p-3 rounded-md border border-input bg-background text-sm resize-none focus:outline-none focus:ring-2 focus:ring-ring",placeholder:`@user1
2@user2
3user3
4user4, user5`,value:d,onChange:t=>z(t.target.value)}),d.trim()&&e.jsxs("p",{className:"text-xs text-muted-foreground",children:[d.split(/[\n,\r\s]+/).filter(t=>t.trim().replace(/^@/,"").length>0).length," handles detected"]})]})]}),e.jsx(nt,{children:e.jsxs(c,{onClick:He,disabled:P.isPending||W,children:[P.isPending&&e.jsx(U,{className:"h-4 w-4 mr-2 animate-spin"}),"Create & Start"]})})]})]})]}),!H||H.length===0?e.jsx(k,{children:e.jsx(_,{className:"py-8 text-center text-muted-foreground",children:"No campaigns found. Create one to start following users."})}):e.jsx("div",{className:"grid gap-4",children:H.map(t=>{const n=fe?.find(o=>o.id===t.task.scrapeRunId);return e.jsx(k,{className:"overflow-hidden",children:e.jsxs("div",{className:"flex items-center justify-between p-4 bg-card",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"font-semibold text-lg",children:t.task.name||`Campaign #${t.task.id}`}),e.jsx(ft,{variant:t.task.status==="active"?"default":"outline",className:t.task.status==="active"?"bg-green-600 hover:bg-green-700":"",children:t.task.status})]}),e.jsxs("div",{className:"text-sm text-muted-foreground flex items-center gap-2",children:[e.jsx(ot,{className:"h-3 w-3"}),"Source: ",e.jsxs("span",{className:"text-foreground",children:["@",n?.sourceHandle||"Unknown"]}
4),e.jsx("span",{className:"text-muted-foreground/50",children:"•"}),e.jsxs("span",{children:[t.pendingCount," pending users"]})]})]}),e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:[e.jsx("span",{children:t.task.status==="active"?"Running":"Paused"}),e.jsx(pt,{checked:t.task.status==="active",onCheckedChange:o=>$e(t.task.id,o),disabled:P.isPending||W})]}),e.jsxs(rt,{children:[e.jsx(lt,{asChild:!0,children:e.jsx(c,{variant:"ghost",size:"icon",className:"text-muted-foreground hover:text-destructive",title:"Delete campaign",disabled:Ne.isPending,children:e.jsx(Ct,{className:"h-4 w-4"})})}),e.jsxs(it,{children:[e.jsxs(ct,{children:[e.jsxs(dt,{children:["Delete “",t.task.name||`Campaign #${t.task.id}`,"”?"]}),e.jsxs(ut,{children:["This removes the campaign and its ",t.pendingCount," pending",t.pendingCount===1?" user":" users",". Accounts already followed stay followed — this only clears the queue. It cannot be undone."]})]}),e.jsxs(mt,{children:[e.jsx(xt,{children:"Cancel"}),e.jsx(ht,{className:"bg-destructive text-destructive-foreground hover:bg-destructive/90",onClick:()=>Ne.mutate(t.task.id),children:"Delete campaign"})]})]})]})]})]})},t.task.id)})})]}),s&&e.jsxs("div",{className:"flex flex-col items-center gap-1 py-1",children:[e.jsx(Tt,{className:"h-7 w-7 animate-nudge-down text-primary"}),e.jsx("span",{className:"text-sm font-medium text-muted-foreground",children:"A campaign only runs once you start it in the extension"})]}),s&&e.jsxs("div",{className:"space-y-4",children:[e.jsx(Fe,{n:2,title:"Run it in the extension",subtitle:"Following happens in your own browser, not on our servers"}),ye?e.jsx(jt,{accountId:s,module:"followCampaigns",showStatus:!1}):e.jsx(k,{className:"border-2 border-dashed border-primary/40 bg-primary/5",children:e.jsxs(_,{className:"flex flex-col items-center gap-4 py-10 text-center",children:[e.jsx("div",{className:"flex h-14 w-14 items-center justify-center rounded-full bg-primary/10",children:e.jsx(pe,{className:"h-7 w-7 text-primary"})}),e.jsxs("div",{className:"max-w-md space-y-1.5",children:[e.jsx("p",{className:"text-lg font-semibold",children:"Install the Chrome extension"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Your campaign is sent to the extension, which follows one account at a time from your own browser with human-like delays. Nothing runs until it's installed and connected."})]}),e.jsxs(c,{size:"lg",onClick:()=>window.open(Dt,"_blank"),"data-testid":"button-install-extension",children:[e.jsx(pe,{className:"mr-2 h-4 w-4"}),"Install the extension",e.jsx(he,{className:"ml-2 h-3.5 w-3.5"})]}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["Already installed? Paste your connection code from the"," ",e.jsx("a",{href:"/accounts",className:"text-primary hover:underline",children:"Add X Accounts"})," page."]})]})})]}),s&&e.jsx("div",{children:e.jsxs(k,{className:"h-full flex flex-col max-h-[calc(100vh-200px)]",children:[e.jsx(le,{className:"py-4 border-b",children:e.jsxs(ie,{className:"text-lg flex items-center gap-2",children:[e.jsx(kt,{className:"h-4 w-4"}),"Recent Activity"]})}),e.jsx(_,{className:"flex-1 p-0 relative overflow-hidden",children:e.jsx(wt,{className:"h-full",children:e.jsxs("div",{className:"p-4 space-y-4",children:[B?.map(t=>{const n=t.status==="followed",o=t.status==="skipped",m=n?"text-green-500":o?"text-yellow-500":"text-red-500";return e.jsxs("div",{className:"flex items-start gap-3 text-sm pb-3 border-b last:border-0 last:pb-0",children:[n?e.jsx(St,{className:`h-4 w-4 ${m} mt-0.5 shrink-0`}):o?e.jsx(A,{className:`h-4 w-4 ${m} mt-0.5 shrink-0`}):e.jsx(he,{className:"h-4 w-4 text-blue-500 mt-0.5 shrink-0"}),e.jsxs("div",{className:"space-y-1 min-w-0",children:[e.jsx("div",{className:"font-medium truncate",children:t.targetHandle&&e.jsxs("a",{href:`https://x.com/${t.targetHandle}`,target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline flex items-center gap-1",children:["@",t.targetHandle,e.jsx(he,{className:"h-3 w-3 inline"})]})}),e.jsx("div",{className:"text-xs text-muted-foreground",children:t.followedAt&&new Date(t.followedAt).getFullYear()>1970?At(new Date(t.followedAt),{addSuffix:!0}):"Just now"})]})]},t.id)}),(!B||B.length===0)&&e.jsx("div",{className:"text-center text-muted-foreground py-8",children:"No recent activity"})]})})})]})})]})}export{ds as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.