QuickAddParser.swift
public func parse(_ input: String, now: Date = Date()) -> QuickAddResult {
var remaining = input.trimmingCharacters(in: .whitespacesAndNewlines)
guard !remaining.isEmpty else { return QuickAddResult(title: "") }
// Los tokens se extraen antes que la prioridad. Al revés, `p1` casaba dentro de
// `+p1` y dejaba un proyecto llamado "" o un teléfono `+34` convertido en proyecto.
let labels = extractTokens(prefix: "@", from: &remaining) + extractTokens(prefix: "#", from: &remaining)
let project = extractTokens(prefix: "+", from: &remaining).first
let priority = extractPriority(from: &remaining)
let recurrence = extractRecurrence(from: &remaining)
let due = extractDate(from: &remaining, now: now)
let rawTitle = remaining
.replacingOccurrences(of: " ", with: " ")
.trimmingCharacters(in: .whitespacesAndNewlines)
let title = Self.clampTitle(rawTitle)
SendTaskReminderPush.php
namespace App\Jobs;
use App\Contracts\ApnsClient;
use App\Models\Task;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
/**
* Recordatorio visible de una tarea próxima a vencer (doc 04 §7). Se dispara desde
* App\Console\Commands\DispatchTaskReminderPushes.
*/
class SendTaskReminderPush implements ShouldQueue
{
use Queueable;
public function __construct(public readonly Task $task) {}
public function handle(ApnsClient $apns): void
{
$owner = $this->task->workspace->user;
foreach ($owner->devices()->where('is_active', true)->get() as $device) {
$result = $apns->send($device->apns_token, [
'aps' => [
'alert' => [
'title' => $this->task->title,
'body' => 'Vence pronto',
],
'sound' => 'default',
],
'task_id' => $this->task->id,
], 'alert');
if ($result->tokenInvalid) {
$device->update(['is_active' => false]);
}
}
}
}