Skip to content

Commit fe015a5

Browse files
authored
Overall optimization (#1250)
* work start * work in progress * before merge * commit before merge * ran tests
1 parent 5172054 commit fe015a5

File tree

9 files changed

+273
-153
lines changed

9 files changed

+273
-153
lines changed

apps/desktop/src/components/finder/views/contact-view.tsx

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { RiCornerDownLeftLine } from "@remixicon/react";
22
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
33
import { Building2, CircleMinus, FileText, Pencil, Plus, SearchIcon, TrashIcon, User } from "lucide-react";
4-
import { useEffect, useRef, useState } from "react";
4+
import React, { useEffect, useRef, useState } from "react";
55

66
import { commands as dbCommands } from "@hypr/plugin-db";
77
import { type Human, type Organization } from "@hypr/plugin-db";
@@ -20,25 +20,35 @@ interface ContactViewProps {
2020
}
2121

2222
export function ContactView({ userId, initialPersonId, initialOrgId }: ContactViewProps) {
23+
// Simple state initialization - handles both normal and deep-link cases
2324
const [selectedOrganization, setSelectedOrganization] = useState<string | null>(initialOrgId || null);
2425
const [selectedPerson, setSelectedPerson] = useState<string | null>(initialPersonId || null);
26+
2527
const [editingPerson, setEditingPerson] = useState<string | null>(null);
2628
const [editingOrg, setEditingOrg] = useState<string | null>(null);
2729
const [showNewOrg, setShowNewOrg] = useState(false);
2830
const queryClient = useQueryClient();
2931

32+
// Load organizations once and keep cached (global data)
3033
const { data: organizations = [] } = useQuery({
31-
queryKey: ["organizations", userId],
34+
queryKey: ["organizations"],
3235
queryFn: () => dbCommands.listOrganizations(null),
3336
});
3437

35-
const { data: people = [] } = useQuery({
36-
queryKey: ["organization-members", selectedOrganization],
37-
queryFn: () =>
38-
selectedOrganization ? dbCommands.listOrganizationMembers(selectedOrganization) : Promise.resolve([]),
39-
enabled: !!selectedOrganization,
38+
// Load user's own profile
39+
const { data: userProfile } = useQuery({
40+
queryKey: ["user-profile", userId],
41+
queryFn: async () => {
42+
try {
43+
return await dbCommands.getHuman(userId);
44+
} catch (error) {
45+
console.error("Error fetching user profile:", error);
46+
return null;
47+
}
48+
},
4049
});
4150

51+
// Load all people once and keep cached (user-specific data)
4252
const { data: allPeople = [] } = useQuery({
4353
queryKey: ["all-people", userId],
4454
queryFn: async () => {
@@ -50,14 +60,32 @@ export function ContactView({ userId, initialPersonId, initialOrgId }: ContactVi
5060
return [];
5161
}
5262
},
53-
enabled: !selectedOrganization,
5463
});
5564

65+
// Merge user profile with all people, ensuring user's own profile is included
66+
const allPeopleWithUser = React.useMemo(() => {
67+
if (!userProfile) {
68+
return allPeople;
69+
}
70+
71+
// Check if user is already in the list
72+
const userInList = allPeople.some(person => person.id === userId);
73+
74+
if (userInList) {
75+
return allPeople;
76+
} else {
77+
// Add user profile to the beginning of the list
78+
return [userProfile, ...allPeople];
79+
}
80+
}, [allPeople, userProfile, userId]);
81+
82+
// Person sessions - only runs when person is selected
5683
const { data: personSessions = [] } = useQuery({
57-
queryKey: ["person-sessions", selectedPerson, userId],
84+
queryKey: ["person-sessions", selectedPerson || "none"],
5885
queryFn: async () => {
86+
// Safety check - this should never run when selectedPerson is null
5987
if (!selectedPerson) {
60-
return [];
88+
throw new Error("Query should not run when selectedPerson is null");
6189
}
6290

6391
const sessions = await dbCommands.listSessions({
@@ -81,35 +109,20 @@ export function ContactView({ userId, initialPersonId, initialOrgId }: ContactVi
81109

82110
return sessionsWithPerson;
83111
},
84-
enabled: !!selectedPerson,
112+
enabled: selectedPerson !== null && selectedPerson !== undefined && selectedPerson !== "",
113+
gcTime: 5 * 60 * 1000,
114+
staleTime: 30 * 1000,
85115
});
86116

87-
const displayPeople = selectedOrganization ? people : allPeople;
117+
// Client-side filtering: filter allPeopleWithUser by organization when one is selected
118+
const displayPeople = selectedOrganization
119+
? allPeopleWithUser.filter(person => person.organization_id === selectedOrganization)
120+
: allPeopleWithUser;
88121

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

91-
// Handle initial person selection
92-
useEffect(() => {
93-
if (initialPersonId && allPeople.length > 0) {
94-
const person = allPeople.find(p => p.id === initialPersonId);
95-
if (person) {
96-
setSelectedPerson(initialPersonId);
97-
if (person.organization_id) {
98-
setSelectedOrganization(person.organization_id);
99-
}
100-
}
101-
}
102-
}, [initialPersonId, allPeople]);
103-
104-
// Handle initial organization selection
105-
useEffect(() => {
106-
if (initialOrgId && organizations.length > 0) {
107-
const org = organizations.find(o => o.id === initialOrgId);
108-
if (org) {
109-
setSelectedOrganization(initialOrgId);
110-
}
111-
}
112-
}, [initialOrgId, organizations]);
124+
// Simple initialization - no complex useEffects needed
125+
// Initial state is set directly in useState above
113126

114127
const handleSessionClick = (sessionId: string) => {
115128
const path = { to: "/app/note/$id", params: { id: sessionId } } as const satisfies LinkProps;
@@ -134,7 +147,7 @@ export function ContactView({ userId, initialPersonId, initialOrgId }: ContactVi
134147
mutationFn: (personId: string) => dbCommands.deleteHuman(personId),
135148
onSuccess: () => {
136149
queryClient.invalidateQueries({ queryKey: ["all-people"] });
137-
queryClient.invalidateQueries({ queryKey: ["organization-members"] });
150+
queryClient.invalidateQueries({ queryKey: ["user-profile"] });
138151

139152
if (selectedPerson === selectedPersonData?.id) {
140153
setSelectedPerson(null);
@@ -241,7 +254,7 @@ export function ContactView({ userId, initialPersonId, initialOrgId }: ContactVi
241254
linkedin_username: null,
242255
}).then(() => {
243256
queryClient.invalidateQueries({ queryKey: ["all-people"] });
244-
queryClient.invalidateQueries({ queryKey: ["organization-members"] });
257+
queryClient.invalidateQueries({ queryKey: ["user-profile"] });
245258
setSelectedPerson(newPersonId);
246259
setEditingPerson(newPersonId);
247260
});
@@ -268,7 +281,12 @@ export function ContactView({ userId, initialPersonId, initialOrgId }: ContactVi
268281
</span>
269282
</div>
270283
<div className="flex-1 min-w-0">
271-
<div className="font-medium truncate">{person.full_name || person.email || "Unnamed"}</div>
284+
<div className="font-medium truncate flex items-center gap-1">
285+
{person.full_name || person.email || "Unnamed"}
286+
{person.id === userId && (
287+
<span className="text-xs bg-blue-100 text-blue-700 px-1.5 py-0.5 rounded-full">You</span>
288+
)}
289+
</div>
272290
{person.email && person.full_name && (
273291
<div className="text-xs text-neutral-500 truncate">{person.email}</div>
274292
)}
@@ -303,8 +321,11 @@ export function ContactView({ userId, initialPersonId, initialOrgId }: ContactVi
303321
<div className="flex-1">
304322
<div className="flex items-start justify-between">
305323
<div>
306-
<h2 className="text-lg font-semibold">
324+
<h2 className="text-lg font-semibold flex items-center gap-2">
307325
{selectedPersonData.full_name || "Unnamed Contact"}
326+
{selectedPersonData.id === userId && (
327+
<span className="text-sm bg-blue-100 text-blue-700 px-2 py-1 rounded-full">You</span>
328+
)}
308329
</h2>
309330
{selectedPersonData.job_title && (
310331
<p className="text-sm text-neutral-600">{selectedPersonData.job_title}</p>
@@ -433,7 +454,7 @@ function EditPersonForm({
433454
}),
434455
onSuccess: () => {
435456
queryClient.invalidateQueries({ queryKey: ["all-people"] });
436-
queryClient.invalidateQueries({ queryKey: ["organization-members"] });
457+
queryClient.invalidateQueries({ queryKey: ["user-profile"] });
437458
onSave();
438459
},
439460
onError: () => {

apps/desktop/src/components/right-panel/hooks/useChatLogic.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,14 @@ export function useChatLogic({
138138
return;
139139
}
140140

141-
if (messages.length >= 2 && !getLicense.data?.valid) {
141+
if (messages.length >= 4 && !getLicense.data?.valid) {
142142
if (userId) {
143143
await analyticsCommands.event({
144144
event: "pro_license_required_chat",
145145
distinct_id: userId,
146146
});
147147
}
148-
await message("2 messages are allowed per session for free users.", {
148+
await message("2 messages are allowed per conversation for free users.", {
149149
title: "Pro License Required",
150150
kind: "info",
151151
});

apps/desktop/src/components/workspace-calendar/event-card.tsx

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,49 @@
11
import { Trans } from "@lingui/react/macro";
2-
import { useQuery } from "@tanstack/react-query";
32
import type { LinkProps } from "@tanstack/react-router";
43
import { format } from "date-fns";
54
import { Calendar, FileText, Pen } from "lucide-react";
65
import { useMemo, useState } from "react";
76

87
import { useHypr } from "@/contexts";
98
import { openURL } from "@/utils/shell";
10-
import type { Event } from "@hypr/plugin-db";
11-
import { commands as dbCommands } from "@hypr/plugin-db";
9+
import type { Event, Human, Session } from "@hypr/plugin-db";
1210
import { commands as windowsCommands } from "@hypr/plugin-windows";
1311
import { Popover, PopoverContent, PopoverTrigger } from "@hypr/ui/components/ui/popover";
1412

1513
export function EventCard({
1614
event,
1715
showTime = false,
16+
session = null,
17+
participants = [],
1818
}: {
1919
event: Event;
2020
showTime?: boolean;
21+
session?: Session | null;
22+
participants?: Human[];
2123
}) {
2224
const { userId } = useHypr();
23-
const session = useQuery({
24-
queryKey: ["event-session", event.id],
25-
queryFn: async () => dbCommands.getSession({ calendarEventId: event.id }),
26-
});
27-
28-
const participants = useQuery({
29-
queryKey: ["participants", session.data?.id],
30-
queryFn: async () => {
31-
if (!session.data?.id) {
32-
return [];
33-
}
34-
const participants = await dbCommands.sessionListParticipants(session.data.id);
35-
return participants.sort((a, b) => {
36-
if (a.is_user && !b.is_user) {
37-
return 1;
38-
}
39-
if (!a.is_user && b.is_user) {
40-
return -1;
41-
}
42-
return 0;
43-
});
44-
},
45-
enabled: !!session.data?.id,
46-
});
4725

4826
const participantsPreview = useMemo(() => {
49-
const count = participants.data?.length ?? 0;
27+
const count = participants?.length ?? 0;
5028
if (count === 0) {
5129
return null;
5230
}
5331

54-
return participants.data?.map(participant => {
32+
return participants?.map(participant => {
5533
if (participant.id === userId && !participant.full_name) {
5634
return "You";
5735
}
5836
return participant.full_name ?? "??";
5937
});
60-
}, [participants.data, userId]);
38+
}, [participants, userId]);
6139

6240
const [open, setOpen] = useState(false);
6341

6442
const handleClick = () => {
6543
setOpen(false);
6644

67-
if (session.data) {
68-
const id = session.data.id;
45+
if (session) {
46+
const id = session.id;
6947
const url = { to: "/app/note/$id", params: { id } } as const satisfies LinkProps;
7048
windowsCommands.windowShow({ type: "main" }).then(() => {
7149
windowsCommands.windowEmitNavigate({ type: "main" }, {
@@ -129,15 +107,15 @@ export function EventCard({
129107
</div>
130108
)}
131109

132-
{session.data
110+
{session
133111
? (
134112
<div
135113
className="flex items-center gap-2 px-2 py-1 bg-neutral-50 border border-neutral-200 rounded-md cursor-pointer hover:bg-neutral-100 transition-colors"
136114
onClick={handleClick}
137115
>
138116
<FileText className="size-3 text-neutral-600 flex-shrink-0" />
139117
<div className="text-xs font-medium text-neutral-800 truncate">
140-
{session.data.title || "Untitled Note"}
118+
{session.title || "Untitled Note"}
141119
</div>
142120
</div>
143121
)

0 commit comments

Comments
 (0)