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
77 changes: 51 additions & 26 deletions apps/desktop/src/components/finder/views/contact-view.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RiCornerDownLeftLine } from "@remixicon/react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Building2, CircleMinus, FileText, Pencil, Plus, SearchIcon, TrashIcon, User } from "lucide-react";
import { ArrowUpAZ, Building2, CircleMinus, FileText, Pencil, Plus, SearchIcon, TrashIcon, User } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";

import { commands as dbCommands } from "@hypr/plugin-db";
Expand All @@ -27,6 +27,7 @@ export function ContactView({ userId, initialPersonId, initialOrgId }: ContactVi
const [editingPerson, setEditingPerson] = useState<string | null>(null);
const [editingOrg, setEditingOrg] = useState<string | null>(null);
const [showNewOrg, setShowNewOrg] = useState(false);
const [sortAlphabetically, setSortAlphabetically] = useState(true);
const queryClient = useQueryClient();

// Load organizations once and keep cached (global data)
Expand Down Expand Up @@ -117,9 +118,21 @@ export function ContactView({ userId, initialPersonId, initialOrgId }: ContactVi
return name !== null && name !== "" && name !== "Null";
};

const displayPeople = (selectedOrganization
? allPeopleWithUser.filter(person => person.organization_id === selectedOrganization)
: allPeopleWithUser).filter(person => person.id === userId || isValidName(person.full_name));
const displayPeople = React.useMemo(() => {
let filtered = (selectedOrganization
? allPeopleWithUser.filter(person => person.organization_id === selectedOrganization)
: allPeopleWithUser).filter(person => person.id === userId || isValidName(person.full_name));

if (sortAlphabetically) {
filtered = [...filtered].sort((a, b) => {
const nameA = (a.full_name || a.email || "").toLowerCase();
const nameB = (b.full_name || b.email || "").toLowerCase();
return nameA.localeCompare(nameB);
});
}

return filtered;
}, [selectedOrganization, allPeopleWithUser, userId, sortAlphabetically]);

const selectedPersonData = displayPeople.find(p => p.id === selectedPerson);

Expand Down Expand Up @@ -240,28 +253,40 @@ export function ContactView({ userId, initialPersonId, initialOrgId }: ContactVi
<div className="w-[250px] border-r border-neutral-200 flex flex-col">
<div className="px-3 py-2 border-b border-neutral-200 flex items-center justify-between">
<h3 className="text-xs font-medium text-neutral-600">People</h3>
<button
onClick={() => {
const newPersonId = crypto.randomUUID();
dbCommands.upsertHuman({
id: newPersonId,
organization_id: selectedOrganization,
is_user: false,
full_name: "New Contact",
email: null,
job_title: null,
linkedin_username: null,
}).then(() => {
queryClient.invalidateQueries({ queryKey: ["all-people"] });
queryClient.invalidateQueries({ queryKey: ["user-profile"] });
setSelectedPerson(newPersonId);
setEditingPerson(newPersonId);
});
}}
className="p-0.5 rounded hover:bg-neutral-100 transition-colors"
>
<Plus className="h-3 w-3 text-neutral-500" />
</button>
<div className="flex items-center gap-1">
<button
onClick={() => setSortAlphabetically(!sortAlphabetically)}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deriving the next state from the current value inside the setter can read a stale value when React batches updates; use the functional updater form to ensure correctness (Based on your team's feedback about always using functional updates when toggling boolean state).

Prompt for AI agents
Address the following comment on apps/desktop/src/components/finder/views/contact-view.tsx at line 258:

<comment>Deriving the next state from the current value inside the setter can read a stale value when React batches updates; use the functional updater form to ensure correctness (Based on your team&#39;s feedback about always using functional updates when toggling boolean state).</comment>

<file context>
@@ -240,28 +253,40 @@ export function ContactView({ userId, initialPersonId, initialOrgId }: ContactVi
       &lt;div className=&quot;w-[250px] border-r border-neutral-200 flex flex-col&quot;&gt;
         &lt;div className=&quot;px-3 py-2 border-b border-neutral-200 flex items-center justify-between&quot;&gt;
           &lt;h3 className=&quot;text-xs font-medium text-neutral-600&quot;&gt;People&lt;/h3&gt;
-          &lt;button
-            onClick={() =&gt; {
-              const newPersonId = crypto.randomUUID();
-              dbCommands.upsertHuman({
-                id: newPersonId,
-                organization_id: selectedOrganization,
</file context>
Suggested change
onClick={() => setSortAlphabetically(!sortAlphabetically)}
onClick={() => setSortAlphabetically(prev => !prev)}

className={cn(
"p-0.5 rounded hover:bg-neutral-100 transition-colors",
sortAlphabetically && "bg-neutral-100",
)}
title={sortAlphabetically ? "Sorted A-Z" : "Sort A-Z"}
>
<ArrowUpAZ className="h-3 w-3 text-neutral-500" />
</button>
<button
onClick={() => {
const newPersonId = crypto.randomUUID();
dbCommands.upsertHuman({
id: newPersonId,
organization_id: selectedOrganization,
is_user: false,
full_name: "New Contact",
email: null,
job_title: null,
linkedin_username: null,
}).then(() => {
queryClient.invalidateQueries({ queryKey: ["all-people"] });
queryClient.invalidateQueries({ queryKey: ["user-profile"] });
setSelectedPerson(newPersonId);
setEditingPerson(newPersonId);
});
}}
className="p-0.5 rounded hover:bg-neutral-100 transition-colors"
>
<Plus className="h-3 w-3 text-neutral-500" />
</button>
</div>
</div>
<div className="flex-1 overflow-y-auto">
<div className="p-2">
Expand Down
46 changes: 23 additions & 23 deletions owhisper/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,23 @@
"type"
],
"properties": {
"access_key_id": {
"type": "string"
"type": {
"type": "string",
"enum": [
"aws"
]
},
"id": {
"type": "string"
},
"region": {
"type": "string"
},
"secret_access_key": {
"access_key_id": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"aws"
]
"secret_access_key": {
"type": "string"
}
}
},
Expand All @@ -75,6 +75,15 @@
"type"
],
"properties": {
"type": {
"type": "string",
"enum": [
"deepgram"
]
},
"id": {
"type": "string"
},
"api_key": {
"type": [
"string",
Expand All @@ -83,15 +92,6 @@
},
"base_url": {
"type": "string"
},
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"deepgram"
]
}
}
},
Expand All @@ -103,17 +103,17 @@
"type"
],
"properties": {
"id": {
"type": "string"
},
"model_path": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"whisper-cpp"
]
},
"id": {
"type": "string"
},
"model_path": {
"type": "string"
}
}
}
Expand Down
Loading