- Configurable tier-based Auto Play with a saved user toggle.

Admin tier assignment without payment via Users > Edit > Tier Override.
Fixed live group membership refresh, polling, unread indicators, and 
incoming voice clip population.
Added asset cache busting so clients receive the fixes immediately.
This commit is contained in:
Ty Clifford
2026-06-08 15:59:04 -04:00
parent 063b8dc3e8
commit d0e4a870be
9 changed files with 209 additions and 42 deletions
+13 -2
View File
@@ -174,6 +174,7 @@ function initDB(PDO $db): void {
email TEXT,
email_verified_at INTEGER,
plan_id INTEGER,
manual_plan_id INTEGER,
stripe_customer_id TEXT,
stripe_subscription_id TEXT,
subscription_status TEXT NOT NULL DEFAULT 'free',
@@ -187,6 +188,7 @@ function initDB(PDO $db): void {
'email' => 'TEXT',
'email_verified_at' => 'INTEGER',
'plan_id' => 'INTEGER',
'manual_plan_id' => 'INTEGER',
'stripe_customer_id' => 'TEXT',
'stripe_subscription_id' => 'TEXT',
'subscription_status' => "TEXT NOT NULL DEFAULT 'free'",
@@ -347,6 +349,7 @@ function seedPlans(PDO $db): void {
'price' => 0, 'sort' => 0,
'features' => [
'voice_messages' => true, 'recording_status' => false, 'auto_voice_send' => false,
'auto_voice_play' => false,
'group_member_limit' => 2, 'username_color' => false, 'text_archive_days' => 30,
'text_export' => true, 'voice_archive' => false, 'voice_export' => false, 'email_2fa' => false,
],
@@ -356,6 +359,7 @@ function seedPlans(PDO $db): void {
'price' => 499, 'sort' => 10,
'features' => [
'voice_messages' => true, 'recording_status' => true, 'auto_voice_send' => true,
'auto_voice_play' => true,
'group_member_limit' => 4, 'username_color' => true, 'text_archive_days' => 90,
'text_export' => true, 'voice_archive' => true, 'voice_export' => true, 'email_2fa' => true,
],
@@ -365,6 +369,7 @@ function seedPlans(PDO $db): void {
'price' => 999, 'sort' => 20,
'features' => [
'voice_messages' => true, 'recording_status' => true, 'auto_voice_send' => true,
'auto_voice_play' => true,
'group_member_limit' => 8, 'username_color' => true, 'text_archive_days' => 365,
'text_export' => true, 'voice_archive' => true, 'voice_export' => true, 'email_2fa' => true,
],
@@ -413,9 +418,10 @@ function planById(int $planId): ?array {
function userPlan(array $user): array {
$free = null;
$effectivePlanId = (int)($user['manual_plan_id'] ?? 0) ?: (int)($user['plan_id'] ?? 0);
foreach (plans(false) as $plan) {
if ($plan['slug'] === 'free') $free = $plan;
if ((int)($user['plan_id'] ?? 0) === $plan['id']) return $plan;
if ($effectivePlanId === $plan['id']) return $plan;
}
return $free ?? ['id' => 0, 'slug' => 'free', 'name' => 'Free', 'features' => []];
}
@@ -434,6 +440,7 @@ function userPublicPayload(array $user): array {
'email' => $user['email'] ?? null,
'email_verified' => !empty($user['email_verified_at']),
'plan' => ['id' => $plan['id'], 'slug' => $plan['slug'], 'name' => $plan['name']],
'plan_source' => !empty($user['manual_plan_id']) ? 'admin' : 'billing',
'features' => $plan['features'],
'subscription' => [
'status' => $user['subscription_status'] ?? 'free',
@@ -533,7 +540,9 @@ function requireGroupAccess(array $user, ?int $groupId): void {
function userGroups(int $userId): array {
$stmt = getDB()->prepare("SELECT g.id,g.name,g.owner_id,g.created_at,gm.role,
(SELECT COUNT(*) FROM group_members x WHERE x.group_id=g.id) AS member_count
(SELECT COUNT(*) FROM group_members x WHERE x.group_id=g.id) AS member_count,
(SELECT MAX(m.id) FROM messages m WHERE m.group_id=g.id) AS latest_message_id,
(SELECT MAX(m.created_at) FROM messages m WHERE m.group_id=g.id) AS latest_message_at
FROM chat_groups g JOIN group_members gm ON gm.group_id=g.id
WHERE gm.user_id=? ORDER BY g.updated_at DESC,g.id DESC");
$stmt->execute([$userId]);
@@ -544,6 +553,8 @@ function userGroups(int $userId): array {
$group['id'] = (int)$group['id'];
$group['owner_id'] = (int)$group['owner_id'];
$group['member_count'] = (int)$group['member_count'];
$group['latest_message_id'] = (int)($group['latest_message_id'] ?? 0);
$group['latest_message_at'] = (int)($group['latest_message_at'] ?? 0);
$members->execute([$group['id']]);
$group['members'] = $members->fetchAll();
}