Skip to content
Merged
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
26 changes: 18 additions & 8 deletions apps/geolibre-desktop/src/components/comments/AddCommentDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Trans, useTranslation } from "react-i18next";
import {
Button,
Dialog,
Expand Down Expand Up @@ -46,7 +46,7 @@ export function AddCommentDialog({ pendingComment, onSubmit, onCancel }: AddComm
e.preventDefault();
if (!text.trim()) return;

const finalName = authorName.trim() || savedName?.trim() || "Author";
const finalName = authorName.trim() || savedName?.trim() || t("comments.defaultAuthorName");
if (typeof localStorage !== "undefined" && finalName) {
try {
localStorage.setItem("geolibre_author_name", finalName);
Expand Down Expand Up @@ -99,15 +99,21 @@ export function AddCommentDialog({ pendingComment, onSubmit, onCancel }: AddComm
<>
<Layers className="h-3.5 w-3.5 text-sky-400 shrink-0" />
<span className="truncate font-medium text-foreground">
Anchored to Feature #{String(anchor.featureId)} ({anchor.layerId})
{t("comments.anchoredToFeature", {
id: String(anchor.featureId),
layer: anchor.layerId,
})}
</span>
</>
) : (
<>
<MapPin className="h-3.5 w-3.5 text-amber-400 shrink-0" />
<span className="font-medium text-foreground">
Anchored to (
{lngLat ? `${lngLat[1].toFixed(4)}, ${lngLat[0].toFixed(4)}` : "Map Point"})
{t("comments.anchoredToPoint", {
coords: lngLat
? `${lngLat[1].toFixed(4)}, ${lngLat[0].toFixed(4)}`
: t("comments.mapPoint"),
})}
</span>
</>
)}
Expand Down Expand Up @@ -135,7 +141,11 @@ export function AddCommentDialog({ pendingComment, onSubmit, onCancel }: AddComm
) : (
<div className="flex items-center justify-between text-[11px] text-muted-foreground px-1">
<span>
Posting as <strong className="text-foreground">{savedName}</strong>
<Trans
i18nKey="comments.postingAs"
values={{ name: savedName }}
components={{ strong: <strong className="text-foreground" /> }}
/>
</span>
<button
type="button"
Expand All @@ -150,7 +160,7 @@ export function AddCommentDialog({ pendingComment, onSubmit, onCancel }: AddComm
}}
className="text-primary hover:underline text-[10px]"
>
Change Name
{t("comments.changeName")}
</button>
</div>
)}
Expand All @@ -175,7 +185,7 @@ export function AddCommentDialog({ pendingComment, onSubmit, onCancel }: AddComm

<div className="flex items-center justify-end gap-2 pt-1">
<Button type="button" variant="outline" size="sm" onClick={onCancel}>
Cancel
{t("common.cancel")}
</Button>
<Button
type="submit"
Expand Down
35 changes: 19 additions & 16 deletions apps/geolibre-desktop/src/components/comments/CommentThread.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function CommentThread({
style={{ backgroundColor: comment.author?.color || "#3b82f6" }}
/>
<span className="font-semibold text-foreground truncate">
{comment.author?.name || "Author"}
{comment.author?.name || t("comments.defaultAuthorName")}
</span>
<span className="text-[10px] text-muted-foreground">
{new Date(comment.createdAt).toLocaleDateString(undefined, {
Expand All @@ -76,7 +76,7 @@ export function CommentThread({
</span>
{comment.resolved && (
<span className="text-[10px] bg-emerald-500/10 text-emerald-500 font-medium px-1.5 py-0.5 rounded border border-emerald-500/20">
Resolved
{t("comments.resolvedBadge")}
</span>
)}
</div>
Expand All @@ -88,7 +88,7 @@ export function CommentThread({
size="icon"
className="h-7 w-7 text-muted-foreground hover:text-foreground"
onClick={() => onZoomTo(comment)}
title="Zoom to location"
title={t("comments.zoomToLocation")}
>
<Navigation className="h-3.5 w-3.5" />
</Button>
Expand All @@ -106,7 +106,7 @@ export function CommentThread({
: "text-muted-foreground hover:text-emerald-500",
)}
onClick={() => onToggleResolve(comment.id, !comment.resolved)}
title={comment.resolved ? "Reopen comment thread" : "Mark as resolved"}
title={comment.resolved ? t("comments.reopenThread") : t("comments.markResolved")}
>
{comment.resolved ? (
<RotateCcw className="h-3.5 w-3.5" />
Expand All @@ -128,7 +128,7 @@ export function CommentThread({
onDelete(comment.id);
}
}}
title="Delete comment thread"
title={t("comments.deleteThread")}
>
<Trash2 className="h-3.5 w-3.5" />
</Button>
Expand All @@ -143,18 +143,21 @@ export function CommentThread({
<>
<Layers className="h-3 w-3 text-sky-400 shrink-0" />
<span className="truncate">
Feature #{String(comment.anchor.featureId)} ({comment.anchor.layerId})
{t("comments.anchorFeature", {
id: String(comment.anchor.featureId),
layer: comment.anchor.layerId,
})}
</span>
</>
) : (
<>
<MapPin className="h-3 w-3 text-amber-400 shrink-0" />
<span>
Point (
{comment.anchor.lngLat
? `${comment.anchor.lngLat[1].toFixed(4)}, ${comment.anchor.lngLat[0].toFixed(4)}`
: "Map"}
)
{t("comments.anchorPoint", {
coords: comment.anchor.lngLat
? `${comment.anchor.lngLat[1].toFixed(4)}, ${comment.anchor.lngLat[0].toFixed(4)}`
: t("comments.anchorMap"),
})}
</span>
</>
)}
Expand All @@ -174,7 +177,7 @@ export function CommentThread({
style={{ backgroundColor: reply.author?.color || "#3b82f6" }}
/>
<span className="font-semibold text-foreground text-[11px]">
{reply.author?.name || "Author"}
{reply.author?.name || t("comments.defaultAuthorName")}
</span>
<span className="text-[10px] text-muted-foreground">
{new Date(reply.createdAt).toLocaleDateString(undefined, {
Expand Down Expand Up @@ -202,14 +205,14 @@ export function CommentThread({
onClick={() => setIsReplying(true)}
>
<CornerDownRight className="h-3 w-3" />
<span>Reply</span>
<span>{t("comments.reply")}</span>
</Button>
) : (
<form onSubmit={handleSubmitReply} className="mt-2 space-y-2">
<Textarea
value={replyText}
onChange={(e) => setReplyText(e.target.value)}
placeholder="Write a reply..."
placeholder={t("comments.replyPlaceholder")}
rows={2}
className="text-xs min-h-14 resize-none"
autoFocus
Expand All @@ -224,11 +227,11 @@ export function CommentThread({
setReplyText("");
}}
>
Cancel
{t("common.cancel")}
</Button>
<Button type="submit" variant="default" size="sm" className="gap-1">
<Send className="h-3 w-3" />
<span>Reply</span>
<span>{t("comments.reply")}</span>
</Button>
</div>
</form>
Expand Down
47 changes: 23 additions & 24 deletions apps/geolibre-desktop/src/components/comments/CommentsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export function CommentsPanel({
try {
await collaboration.join(targetCode, selfAuthorName, selfAuthorColor);
} catch (err: unknown) {
setSessionError(err instanceof Error ? err.message : "Failed to join session.");
setSessionError(err instanceof Error ? err.message : t("comments.joinFailed"));
} finally {
setBusy(false);
}
Expand All @@ -184,7 +184,7 @@ export function CommentsPanel({
try {
await collaboration.start(selfAuthorName, selfAuthorColor, "co-edit");
} catch (err: unknown) {
setSessionError(err instanceof Error ? err.message : "Failed to start session.");
setSessionError(err instanceof Error ? err.message : t("comments.startFailed"));
} finally {
setBusy(false);
}
Expand Down Expand Up @@ -249,7 +249,7 @@ export function CommentsPanel({
<div className="flex items-center justify-between h-10 px-3 border-b border-border bg-card shrink-0">
<div className="flex items-center gap-2">
<MessageSquare className="h-4 w-4 text-primary" />
<span className="font-semibold text-xs text-foreground">Comments</span>
<span className="font-semibold text-xs text-foreground">{t("comments.title")}</span>
<span className="inline-flex items-center justify-center h-4 px-1.5 text-[10px] font-bold rounded-full bg-muted text-muted-foreground">
{unresolvedComments.length}
</span>
Expand All @@ -261,10 +261,10 @@ export function CommentsPanel({
size="sm"
onClick={onActivateCommentTool}
className="gap-1 h-7 px-2 text-xs"
title="Place a new review comment on the map"
title={t("comments.addCommentTooltip")}
>
<Plus className="h-3.5 w-3.5" />
<span>Add Comment</span>
<span>{t("comments.addComment")}</span>
<kbd className="ms-1 rounded border border-current/25 px-1 font-mono text-[9px] leading-4 opacity-70">
C
</kbd>
Expand All @@ -284,7 +284,7 @@ export function CommentsPanel({
onChange={(e) => setNameInput(e.target.value)}
onKeyDown={handleNameKeyDown}
onBlur={handleSaveName}
placeholder="Your name…"
placeholder={t("comments.yourNamePlaceholder")}
className="h-6 text-xs flex-1 min-w-0 px-1.5"
/>
<button
Expand All @@ -294,7 +294,7 @@ export function CommentsPanel({
handleSaveName();
}}
className="text-emerald-500 hover:text-emerald-600 shrink-0"
title="Save name"
title={t("comments.saveName")}
>
<Check className="h-3.5 w-3.5" />
</button>
Expand All @@ -307,14 +307,14 @@ export function CommentsPanel({
</span>
) : (
<span className="text-[11px] text-muted-foreground italic truncate">
No name set — comments will show as "Author"
{t("comments.noNameSet", { name: t("comments.defaultAuthorName") })}
</span>
)}
<button
type="button"
onClick={handleEditName}
className="text-muted-foreground hover:text-foreground shrink-0 ms-auto"
title={selfAuthorName ? "Change name" : "Set your name"}
title={selfAuthorName ? t("comments.changeNameTooltip") : t("comments.setYourName")}
>
<Pencil className="h-3 w-3" />
</button>
Expand All @@ -332,7 +332,7 @@ export function CommentsPanel({
<div className="min-w-0 flex-1">
<div className="flex items-center gap-1 text-[10px] font-semibold text-muted-foreground uppercase tracking-wider">
<KeyRound className="h-3 w-3 text-primary shrink-0" />
<span>Session Code</span>
<span>{t("collaborate.sessionCode")}</span>
</div>
<div className="font-mono text-xs font-bold text-primary tracking-widest truncate mt-0.5">
{activeCode}
Expand All @@ -347,7 +347,7 @@ export function CommentsPanel({
title={t("collaborate.copyLink")}
>
<Copy className="h-3 w-3 me-1" />
{copied ? "Copied" : "Copy"}
{copied ? t("collaborate.copied") : t("comments.copy")}
</Button>
</div>
) : (
Expand All @@ -361,7 +361,7 @@ export function CommentsPanel({
className="w-full h-8 text-xs gap-1.5"
>
<Radio className="h-3.5 w-3.5 text-emerald-500" />
{busy ? "Starting…" : "Start Live Session"}
{busy ? t("comments.starting") : t("comments.startLiveSession")}
</Button>
)}

Expand All @@ -373,7 +373,7 @@ export function CommentsPanel({
onChange={(e) =>
setJoinCodeInput(e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, ""))
}
placeholder="ENTER SESSION CODE"
placeholder={t("comments.sessionCodePlaceholder")}
className="h-7 font-mono text-xs uppercase tracking-wider"
disabled={busy}
/>
Expand All @@ -386,7 +386,7 @@ export function CommentsPanel({
className="h-7 px-2.5 text-[11px] gap-1 shrink-0"
>
<Link2 className="h-3 w-3" />
<span>{busy ? "Joining…" : "Connect"}</span>
<span>{busy ? t("comments.joining") : t("comments.connect")}</span>
</Button>
</div>
)}
Expand All @@ -402,10 +402,10 @@ export function CommentsPanel({
/>
<span className="font-medium text-[11px] truncate">
{collab.connecting
? "Connecting…"
? t("collaborate.connecting")
: collab.isActive
? `Live Sync (${collab.participants?.length ?? 1} online)`
: "Offline / Private Workspace"}
? t("comments.liveSync", { count: collab.participants?.length ?? 1 })
: t("comments.offlineWorkspace")}
</span>
</div>
{collab.isActive && (
Expand All @@ -416,7 +416,7 @@ export function CommentsPanel({
onClick={handleDisconnectSession}
className="h-6 px-1.5 text-[10px] text-destructive hover:text-destructive hover:bg-destructive/10 shrink-0"
>
Disconnect
{t("comments.disconnect")}
</Button>
)}
</div>
Expand Down Expand Up @@ -447,10 +447,10 @@ export function CommentsPanel({
)}
>
{f === "open"
? `Open (${unresolvedComments.length})`
? t("comments.filterOpen", { count: unresolvedComments.length })
: f === "resolved"
? `Resolved (${resolvedComments.length})`
: `All (${comments.length})`}
? t("comments.filterResolved", { count: resolvedComments.length })
: t("comments.filterAll", { count: comments.length })}
</button>
))}
</div>
Expand All @@ -461,11 +461,10 @@ export function CommentsPanel({
<div className="flex flex-col items-center justify-center min-h-[160px] text-center p-4 border border-dashed border-border/60 rounded-lg bg-card/40 my-2">
<MessageCircle className="h-8 w-8 text-muted-foreground/50 mb-2" />
<p className="text-xs font-medium text-foreground mb-1">
{filter === "resolved" ? "No resolved comments" : "No open comments"}
{filter === "resolved" ? t("comments.emptyResolved") : t("comments.emptyOpen")}
</p>
<p className="text-[11px] text-muted-foreground max-w-[200px]">
Click &quot;Add Comment&quot; above and select any location or feature on the map to
drop a review note.
{t("comments.emptyHint")}
</p>
</div>
) : (
Expand Down
Loading
Loading