Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions KNOWLEDGE_BASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,9 @@ Livestream history: on [day two at 7:31:27](https://www.youtube.com/watch?v=nfmV

Add a poll to a top-level commit with **two to four distinct options**, each up to **50 characters** (with the same emoji suggestions and `:shortcode:` conversion as the commit body), and a duration of **one hour, one day, three days, or seven days**. Polls cannot accompany images or be added to replies or quotes. Voting opens when the commit is approved, so time spent pending does not consume the voting period.

Signed-in readers can choose one option and vote once while an approved, visible poll is open; a vote cannot be changed or withdrawn. The author cannot vote in their own poll. Results are visible to the author and people who have voted, and to everyone once the poll closes. Other people's individual votes are not revealed.
Signed-in readers can choose one option and vote once while an approved, visible poll is open; a vote cannot be changed or withdrawn. The author cannot vote in their own poll. Results are visible to the author and to people who have voted. A signed-in reader who can see an open poll can also look at the results without a vote being recorded, and can still vote afterwards. Everyone, including guests, sees the results once the poll closes. Other people's individual votes are not revealed.

Sources: [poll editor](resources/js/components/poll-editor.tsx), [poll results](app/Models/Poll.php), [voting rules](app/Rules/OpenPoll.php), [approval and poll opening](app/Actions/UpdateCommitStatuses.php).
Sources: [poll editor](resources/js/components/poll-editor.tsx), [poll results](app/Models/Poll.php), [viewing results](app/Http/Controllers/CommitPollVoteController.php), [voting rules](app/Rules/OpenPoll.php), [approval and poll opening](app/Actions/UpdateCommitStatuses.php).

### Scheduled commits

Expand Down
24 changes: 24 additions & 0 deletions app/Http/Controllers/CommitPollVoteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,38 @@
namespace App\Http\Controllers;

use App\Actions\CreateCommitPollVote;
use App\Enums\CommitStatus;
use App\Http\Requests\CreateCommitPollVoteRequest;
use App\Models\Commit;
use App\Models\Poll;
use App\Models\User;
use Illuminate\Container\Attributes\CurrentUser;
use Illuminate\Http\JsonResponse;

final readonly class CommitPollVoteController
{
/**
* Counts for an open poll the viewer can already see. Nothing is written, and has_voted stays false until they vote.
*/
public function show(#[CurrentUser] User $user, Commit $commit): JsonResponse
{
abort_unless($commit->isVisibleTo($user), 403);

$poll = Commit::query()->withPollFor($user)->findOrFail($commit->id)->poll;

if (! $poll instanceof Poll) {
abort(404);
}

if ($commit->status !== CommitStatus::Approved || $poll->opened_at === null) {
abort(403);
}

return response()->json([
'poll' => $poll->revealingResults(),
])->header('Cache-Control', 'private, no-store');
}

public function store(CreateCommitPollVoteRequest $request, #[CurrentUser] User $user, Commit $commit, CreateCommitPollVote $action): JsonResponse
{
$action->handle($user, $request->option());
Expand Down
16 changes: 14 additions & 2 deletions app/Models/Poll.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ final class Poll extends Model

use HasUuids;

/**
* Set when a signed-in viewer asks to see an open poll's counts. It does not record a vote.
*/
private bool $revealResults = false;

/**
* @return array<string, string>
*/
Expand All @@ -49,15 +54,15 @@ public function casts(): array
}

/**
* Counts are only revealed to the author, to voters, and to everyone once the poll has closed, so they can't sway a vote. Voters are never revealed.
* Counts stay hidden unless the viewer has voted, owns the poll, the poll has closed, or this copy was asked to reveal them. Voters are never included.
*
* @return array{id: string, closes_at: string|null, is_closed: bool, has_voted: bool, total_votes: int|null, options: list<array{id: string, label: string, votes_count: int|null, voted: bool}>}
*/
public function toArray(): array
{
$options = $this->options->all();
$hasVoted = $this->options->contains(fn (PollOption $option): bool => $option->voted === true);
$showsResults = $hasVoted || $this->is_own === true || $this->isClosed();
$showsResults = $this->revealResults || $hasVoted || $this->is_own === true || $this->isClosed();
$closesAt = $this->closesAt();

return [
Expand All @@ -75,6 +80,13 @@ public function toArray(): array
];
}

public function revealingResults(): self
{
$this->revealResults = true;

return $this;
}

/**
* @return BelongsTo<Commit, $this>
*/
Expand Down
Loading