diff --git a/KNOWLEDGE_BASE.md b/KNOWLEDGE_BASE.md index 4f6391d9f..a1ba473aa 100644 --- a/KNOWLEDGE_BASE.md +++ b/KNOWLEDGE_BASE.md @@ -510,7 +510,7 @@ Settings → Appearance offers: These appearance choices are saved in the browser, not on the account, so each browser keeps its own. Settings → Profile lets you choose one of six lane colors (Orange, Green, Violet, Teal, Rose, Ochre), a custom color, or the automatic color used around your identity and activity graph. -Profile settings also let you share a timezone. Timestamps can show the author's local time as well as the viewer's when a timezone is shared. Leaving it unset uses the browser's timezone for your display without publishing your timezone. A separate, off-by-default setting shows your current local time on your profile, next to your location and website; it only applies while a timezone is set, and clearing the timezone turns it off. These /dev/null preferences are separate from your GitHub-managed profile information. +Profile settings also let you share a timezone. Timestamps can show the author's local time as well as the viewer's when a timezone is shared. Leaving it unset uses the browser's timezone for your display without publishing your timezone. A separate, off-by-default setting shows your current local time on your profile, next to your location and website, in direct Remote headers, and beside members who have opted in on group Remotes. It only applies while a timezone is set, and clearing the timezone turns it off. These /dev/null preferences are separate from your GitHub-managed profile information. Sources: [appearance settings](resources/js/pages/appearance/update.tsx), [lane picker](resources/js/components/lane-picker.tsx), [timezone picker](resources/js/components/timezone-picker.tsx), [local time](resources/js/components/local-time.tsx), [timestamp tooltip](resources/js/components/timestamp-tooltip.tsx). diff --git a/app/Http/Controllers/ConversationController.php b/app/Http/Controllers/ConversationController.php index e8f486958..6fd1366a0 100644 --- a/app/Http/Controllers/ConversationController.php +++ b/app/Http/Controllers/ConversationController.php @@ -7,6 +7,7 @@ use App\Actions\UpdateConversationReadThrough; use App\Http\Requests\ListConversationsRequest; use App\Http\Requests\ShowConversationRequest; +use App\Http\Resources\ConversationResource; use App\Models\Conversation; use App\Models\User; use App\Queries\ConversationsQuery; @@ -20,7 +21,12 @@ public function index(ListConversationsRequest $request, #[CurrentUser] User $user, ConversationsQuery $conversations): Response { return Inertia::render('conversation/index', [ - 'inbox' => fn (): array => $conversations->get($user), + 'inbox' => function () use ($request, $user, $conversations): array { + $inbox = $conversations->get($user); + $inbox['conversations'] = ConversationResource::collection($inbox['conversations'])->resolve($request); + + return $inbox; + }, 'partner' => null, 'thread' => null, ]); @@ -44,8 +50,16 @@ public function show(ShowConversationRequest $request, #[CurrentUser] User $user return $thread; }, - 'inbox' => fn (): array => $conversations->get($user), - 'partner' => $partner, + 'inbox' => function () use ($request, $user, $conversations): array { + $inbox = $conversations->get($user); + $inbox['conversations'] = ConversationResource::collection($inbox['conversations'])->resolve($request); + + return $inbox; + }, + 'partner' => [ + ...$partner->toArray(), + 'timezone' => $partner->show_local_time ? $partner->timezone : null, + ], ]); } } diff --git a/app/Http/Controllers/InboxController.php b/app/Http/Controllers/InboxController.php index ce8b3dc5d..70a6f2829 100644 --- a/app/Http/Controllers/InboxController.php +++ b/app/Http/Controllers/InboxController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; use App\Http\Requests\ListInboxRequest; +use App\Http\Resources\ConversationResource; use App\Models\User; use App\Queries\ConversationsQuery; use Illuminate\Container\Attributes\CurrentUser; @@ -14,10 +15,15 @@ { public function index(ListInboxRequest $request, #[CurrentUser] User $user, ConversationsQuery $conversations): JsonResponse { + $inbox = $conversations->get($user, $request->tab(), $request->term(), $request->beforeId()); + $inbox['conversations'] = ConversationResource::collection($inbox['conversations'])->resolve($request); + return response()->json([ ...($request->notificationBaseline() ? ['notification_baseline' => $conversations->notificationBaseline($user)] : []), - ...$conversations->get($user, $request->tab(), $request->term(), $request->beforeId()), - ...($request->notificationConversations() ? ['notification_conversations' => $conversations->get($user, pinnedFirst: false)['conversations']] : []), + ...$inbox, + ...($request->notificationConversations() ? [ + 'notification_conversations' => ConversationResource::collection($conversations->get($user, pinnedFirst: false)['conversations'])->resolve($request), + ] : []), 'unread' => $conversations->unreadCount($user), 'requests' => $conversations->requestCount($user), ])->header('Cache-Control', 'private, no-store'); diff --git a/app/Http/Controllers/OrgController.php b/app/Http/Controllers/OrgController.php index 0e779a994..57f54a69d 100644 --- a/app/Http/Controllers/OrgController.php +++ b/app/Http/Controllers/OrgController.php @@ -10,6 +10,7 @@ use App\Http\Requests\CreateOrgRequest; use App\Http\Requests\ShowOrgRequest; use App\Http\Requests\UpdateOrgRequest; +use App\Http\Resources\ConversationResource; use App\Models\Conversation; use App\Models\User; use App\Queries\ConversationsQuery; @@ -32,11 +33,25 @@ public function show(ShowOrgRequest $request, #[CurrentUser] User $user, Convers 'thread' => function () use ($user, $org, $threads, $action): array { $thread = $threads->org($user, $org); + $thread['members'] = $thread['members']->map(function (array $member): array { + $member['user'] = [ + ...$member['user']->toArray(), + 'timezone' => $member['user']->show_local_time ? $member['user']->timezone : null, + ]; + + return $member; + }); + $action->handle($user, $org, $thread['messages']->last()->id ?? 0); return $thread; }, - 'inbox' => fn (): array => $conversations->get($user), + 'inbox' => function () use ($request, $user, $conversations): array { + $inbox = $conversations->get($user); + $inbox['conversations'] = ConversationResource::collection($inbox['conversations'])->resolve($request); + + return $inbox; + }, 'partner' => null, 'org' => ['id' => $org->id, 'name' => $org->name], ]); diff --git a/app/Http/Controllers/OrgMessageController.php b/app/Http/Controllers/OrgMessageController.php index 83bdb1823..426427313 100644 --- a/app/Http/Controllers/OrgMessageController.php +++ b/app/Http/Controllers/OrgMessageController.php @@ -17,9 +17,19 @@ { public function index(ListOrgMessagesRequest $request, #[CurrentUser] User $user, Conversation $org, ConversationThreadQuery $threads): JsonResponse { + $thread = $threads->org($user, $org, $request->afterId(), $request->beforeId(), $request->reactionsAfter()); + $thread['members'] = $thread['members']->map(function (array $member): array { + $member['user'] = [ + ...$member['user']->toArray(), + 'timezone' => $member['user']->show_local_time ? $member['user']->timezone : null, + ]; + + return $member; + }); + return response()->json([ 'org' => ['id' => $org->id, 'name' => $org->name], - ...$threads->org($user, $org, $request->afterId(), $request->beforeId(), $request->reactionsAfter()), + ...$thread, ])->header('Cache-Control', 'private, no-store'); } diff --git a/app/Http/Resources/ConversationResource.php b/app/Http/Resources/ConversationResource.php index a9d601123..a3b89b6ef 100644 --- a/app/Http/Resources/ConversationResource.php +++ b/app/Http/Resources/ConversationResource.php @@ -30,7 +30,9 @@ public function toArray(Request $request): array return [ ...$conversation, - 'partner' => $conversation['partner'] instanceof User ? UserResource::make($conversation['partner']) : null, + 'partner' => $conversation['partner'] instanceof User ? [ + ...UserResource::make($conversation['partner'])->toArray($request), + ] : null, 'members' => $conversation['members']?->map(fn (array $member): array => [ 'user' => UserResource::make($member['user']), 'role' => $member['role'], diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index 1f1d0b638..91aef1caa 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -36,6 +36,8 @@ public function toArray(Request $request): array 'bio' => $this->bio, 'company' => $this->company, 'location' => $this->location, + 'timezone' => $this->show_local_time ? $this->timezone : null, + 'show_local_time' => $this->show_local_time, 'website' => $this->website, 'github_url' => "https://github.com/{$this->handle}", 'html_url' => route('user.show', $this->resource), diff --git a/resources/js/components/chat/chat-dock.tsx b/resources/js/components/chat/chat-dock.tsx index 53aef369c..afffd7626 100644 --- a/resources/js/components/chat/chat-dock.tsx +++ b/resources/js/components/chat/chat-dock.tsx @@ -15,6 +15,7 @@ import { ConversationActions } from '@/components/chat/conversation-actions'; import { ConversationThread } from '@/components/chat/conversation-thread'; import { InboxPanel } from '@/components/chat/inbox-panel'; import { MemberStack, OrgHeader, orgTitle } from '@/components/chat/org'; +import { RemoteLocalTime } from '@/components/chat/remote-local-time'; import { SearchDialog } from '@/components/search-dialog'; import { Tooltip, @@ -63,8 +64,9 @@ function DockedChat({ user }: { user: User }) { const target: ChatTarget | null = dock.conversation === null ? null - : (threads[dock.conversation]?.target ?? - (summary === undefined ? null : targetOf(summary))); + : summary === undefined + ? (threads[dock.conversation]?.target ?? null) + : targetOf(summary); const thread = target === null ? undefined : threads[targetKey(target)]; const open = (next: ChatTarget) => { @@ -217,17 +219,22 @@ function DockedChat({ user }: { user: User }) { avatarClassName="size-6" /> )} - - {target.kind === 'direct' - ? target.partner.name - : orgTitle( - target.org, - thread?.members ?? - summary?.members ?? - null, - user, - )} - +