Enable automatic browsing upvotes

This commit is contained in:
jjbrunton committed 2026-08-11 21:52:54 +01:00
1 parent 27012399ec
commit 7bf1aa9d24
26 files changed
+339 -57

No files matched your search

@@ -64,6 +64,12 @@ export function updateBrowsingSettings(
.then((payload) => payload.account);
}
export function runBrowsingSimulation(accountId: string): Promise<GrowthAccountDetail> {
return dashboardRequest(`${accountPath(accountId)}/browsing/run`, { method: 'POST' })
.then((response) => parseResponse(response, GrowthAccountResponseSchema))
.then((payload) => payload.account);
}
export function pauseGrowth(accountId: string, reason?: string): Promise<GrowthAccountDetail> {
return dashboardRequest(`${accountPath(accountId)}/pause`, {
method: 'POST',
@@ -15,6 +15,7 @@ import {
usePauseGrowthMutation,
useRemoveGrowthSubredditMutation,
useResumeGrowthMutation,
useRunBrowsingSimulationMutation,
useUpdateGrowthSettingsMutation,
useUpdateBrowsingSettingsMutation,
useUpdateGrowthSubredditMutation,
@@ -174,6 +175,7 @@ function SubredditRow({
function BrowsingSimulationSection({ account }: { account: GrowthAccountDetail }) {
const update = useUpdateBrowsingSettingsMutation(account.accountId);
const runNow = useRunBrowsingSimulationMutation(account.accountId);
const { settings, stats, todayTarget, recentDecisions } = account.browsing;
const selected = recentDecisions.filter((decision) => decision.decision === 'simulated-upvote');
@@ -181,22 +183,39 @@ function BrowsingSimulationSection({ account }: { account: GrowthAccountDetail }
<section aria-labelledby="browsing-simulation-heading" className="panel settings-card">
<div className="settings-card-heading">
<div>
<span className="eyebrow">Simulation only</span>
<h2 id="browsing-simulation-heading">Browsing simulation</h2>
<p>Tests selection and pacing without submitting votes or any other Reddit write.</p>
<span className="eyebrow">Conservative account activity</span>
<h2 id="browsing-simulation-heading">Browsing sessions</h2>
<p>Selects conservatively; automatic upvotes require their own explicit switch.</p>
</div>
</div>
<div className="settings-card-body">
<div className="state-banner is-warning" role="status">
<strong>No Reddit votes were submitted.</strong>
<span>Every item below is a hypothetical upvote recorded only in RedditBot.</span>
<strong>{account.browsing.noVotesSubmitted ? 'No Reddit votes will be submitted.' : 'Automatic upvotes are enabled.'}</strong>
<span>{account.browsing.noVotesSubmitted
? 'Selections are recorded only in RedditBot.'
: 'Votes run only in running mode with browser dry-run disabled.'}</span>
</div>
<SwitchField
checked={settings.enabled}
description={`Selects ${settings.dailyTargetMin}–${settings.dailyTargetMax} eligible posts or comments during waking hours.`}
label="Simulate browsing selections"
label="Run browsing sessions"
onChange={(enabled) => update.mutate({ enabled })}
/>
<SwitchField
checked={settings.autoVoteEnabled}
description="Submit the selected upvote automatically. Scan mode and browser dry-run always remain simulation-only."
label="Submit selected upvotes"
onChange={(autoVoteEnabled) => update.mutate({ autoVoteEnabled })}
/>
<button
className="button secondary"
disabled={!settings.enabled || runNow.isPending}
onClick={() => runNow.mutate()}
type="button"
>
{runNow.isPending ? 'Running simulation…' : 'Simulate session now'}
</button>
{!settings.enabled ? <p className="field-hint">Enable browsing sessions to run a simulation now.</p> : null}
<dl className="growth-metrics">
<div><dt>Selected today</dt><dd>{stats.selected} / {todayTarget}</dd></div>
<div><dt>Posts</dt><dd>{stats.posts}</dd></div>
@@ -209,9 +228,10 @@ function BrowsingSimulationSection({ account }: { account: GrowthAccountDetail }
</div>
</dl>
{update.isError ? <p className="save-error" role="alert">{errorMessage(update.error)}</p> : null}
{runNow.isError ? <p className="save-error" role="alert">{errorMessage(runNow.error)}</p> : null}
{selected.length === 0 ? (
<div className="empty-state">
<strong>No simulated selections yet</strong>
<strong>No browsing selections yet</strong>
<span>The first session is durably scheduled after this feature is enabled.</span>
</div>
) : (
@@ -222,7 +242,9 @@ function BrowsingSimulationSection({ account }: { account: GrowthAccountDetail }
<a href={`https://www.reddit.com${decision.permalink}`} rel="noreferrer noopener" target="_blank">
r/{decision.subreddit} — {decision.thingKind} by u/{decision.author}
</a>
<span className="pill neutral">Simulated upvote</span>
<span className={`pill ${decision.voteSubmitted ? 'success' : 'neutral'}`}>
{decision.voteSubmitted ? 'Upvote submitted' : 'Simulated selection'}
</span>
</div>
<p className="field-hint">{new Date(decision.decidedAt).toLocaleString()}</p>
</li>
@@ -13,6 +13,7 @@ import {
pauseGrowth,
removeGrowthSubreddit,
resumeGrowth,
runBrowsingSimulation,
updateGrowthSettings,
updateBrowsingSettings,
updateGrowthSubreddit,
@@ -72,6 +73,10 @@ export function useUpdateBrowsingSettingsMutation(accountId: string) {
);
}
export function useRunBrowsingSimulationMutation(accountId: string) {
return useGrowthMutation<void>(accountId, () => runBrowsingSimulation(accountId));
}
export function usePauseGrowthMutation(accountId: string) {
return useGrowthMutation(accountId, (reason?: string) => pauseGrowth(accountId, reason));
}