Skip to content

Commit 062b007

Browse files
authored
Merge pull request #324 from CloudHub-Social/sync/upstream-dev-into-integration
sync: pull upstream dev (de71434)
2 parents dea7cd0 + da0e85c commit 062b007

39 files changed

Lines changed: 879 additions & 321 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
default: minor
3+
---
4+
5+
Change animal identity to allow arbitrary animals
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
default: minor
3+
---
4+
5+
Add search message keybind and combine sidebar items.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
default: minor
3+
---
4+
5+
Add CTRL+E shortcut to editor to bring sticker menu up

src/app/components/GlobalKeyboardShortcuts.tsx

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,18 @@ import { roomToUnreadAtom } from '$state/room/roomToUnread';
1818
import { useKeyDown } from '$hooks/useKeyDown';
1919
import { getDirectRoomPath, getHomeRoomPath, getSpaceRoomPath } from '$pages/pathUtils';
2020
import { HOME_ROOM_PATH, DIRECT_ROOM_PATH, SPACE_ROOM_PATH } from '$pages/paths';
21-
import { getCanonicalAliasOrRoomId } from '$utils/matrix';
21+
import { getCanonicalAliasOrRoomId, getCanonicalAliasRoomId } from '$utils/matrix';
2222
import { announce } from '$utils/announce';
2323
import {
2424
roomIdToReplyDraftAtomFamily,
2525
roomIdToEditNavRequestAtomFamily,
2626
type IEditNavRequest,
2727
} from '$state/room/roomInputDrafts';
2828
import type { Room } from '$types/matrix-sdk';
29+
import {
30+
getMessageSearchShortcutPath,
31+
getSelectedSpaceIdOrAliasFromPath,
32+
} from '$features/search/searchShortcut';
2933

3034
// Stable fallback atom used when no room is active — prevents atomFamily from
3135
// creating a spurious entry under the empty-string key ''.
@@ -41,19 +45,26 @@ export function GlobalKeyboardShortcuts() {
4145
const unreadIndexRef = useRef(0);
4246

4347
// Derive the current room ID from the URL so we know which room is active.
44-
const roomMatch =
45-
matchPath(HOME_ROOM_PATH, location.pathname) ??
46-
matchPath(DIRECT_ROOM_PATH, location.pathname) ??
47-
matchPath(SPACE_ROOM_PATH, location.pathname);
48+
const homeRoomMatch = matchPath(HOME_ROOM_PATH, location.pathname);
49+
const directRoomMatch = matchPath(DIRECT_ROOM_PATH, location.pathname);
50+
const spaceRoomMatch = matchPath(SPACE_ROOM_PATH, location.pathname);
51+
const roomMatch = homeRoomMatch ?? directRoomMatch ?? spaceRoomMatch;
4852
const roomIdOrAlias = roomMatch?.params.roomIdOrAlias
4953
? decodeURIComponent(roomMatch.params.roomIdOrAlias)
5054
: undefined;
55+
const selectedSpaceIdOrAlias = getSelectedSpaceIdOrAliasFromPath(location.pathname);
56+
const selectedSpaceId =
57+
selectedSpaceIdOrAlias && !selectedSpaceIdOrAlias.startsWith('!')
58+
? mx.getRooms().find((r) => r.getCanonicalAlias() === selectedSpaceIdOrAlias)?.roomId
59+
: selectedSpaceIdOrAlias;
5160
let currentRoom: Room | null = null;
61+
5262
if (roomIdOrAlias) {
5363
if (roomIdOrAlias.startsWith('!')) {
5464
currentRoom = mx.getRoom(roomIdOrAlias);
5565
} else {
56-
currentRoom = mx.getRooms().find((r) => r.getCanonicalAlias() === roomIdOrAlias) ?? null;
66+
const aliasedRoomId = getCanonicalAliasRoomId(mx, roomIdOrAlias);
67+
currentRoom = aliasedRoomId ? mx.getRoom(aliasedRoomId) : null;
5768
}
5869
}
5970
const replyDraftAtomFamily = roomIdToReplyDraftAtomFamily(currentRoom?.roomId ?? '');
@@ -178,10 +189,37 @@ export function GlobalKeyboardShortcuts() {
178189
[currentRoom, setEditNavRequest]
179190
);
180191

192+
/** Ctrl+F: Search for messages */
193+
const handleSearchMessageInRoom = useCallback(
194+
(evt: KeyboardEvent) => {
195+
if (!isKeyHotkey('mod+f', evt)) return;
196+
197+
const path = getMessageSearchShortcutPath({
198+
pathname: location.pathname,
199+
currentSearch: location.search,
200+
selectedSpaceId: selectedSpaceId ?? undefined,
201+
currentRoomId: currentRoom?.roomId,
202+
});
203+
if (!path) return;
204+
205+
const portalContainer = document.getElementById('portalContainer');
206+
if (portalContainer && portalContainer.children.length > 0) {
207+
return;
208+
}
209+
210+
evt.preventDefault();
211+
const roomName = mx.getRoom(currentRoom?.roomId)?.name;
212+
navigate(path);
213+
announce(`Start Searching messages ${roomName ? `in ${roomName}` : ''}`);
214+
},
215+
[mx, currentRoom, navigate, location.pathname, location.search, selectedSpaceId]
216+
);
217+
181218
useKeyDown(window, handleNextUnreadKeyDown);
182219
useKeyDown(window, handleUnreadNavKeyDown);
183220
useKeyDown(window, handleReplyKeyDown);
184221
useKeyDown(window, handleEditKeyDown);
222+
useKeyDown(window, handleSearchMessageInRoom);
185223

186224
return null;
187225
}

src/app/components/icons/phosphor.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
ArrowBendUpRightIcon,
66
ArrowDownIcon,
77
ArrowLeftIcon,
8+
ArrowsLeftRightIcon,
89
ArrowRightIcon,
910
ArrowSquareOutIcon,
1011
ArrowUpIcon,
@@ -315,6 +316,7 @@ export {
315316
ArrowBendUpRightIcon,
316317
ArrowDownIcon as ArrowDown,
317318
ArrowLeftIcon as ArrowLeft,
319+
ArrowsLeftRightIcon as ArrowsLeftRight,
318320
ArrowRightIcon as ArrowRight,
319321
ArrowSquareOutIcon as ArrowSquareOut,
320322
ArrowUpIcon as ArrowUp,

src/app/components/sidebar/Sidebar.css.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const SidebarStack = style([
2727
alignItems: 'center',
2828
gap: config.space.S300,
2929
padding: `${config.space.S300} 0`,
30+
paddingBottom: config.space.S200,
3031
},
3132
]);
3233

@@ -116,6 +117,59 @@ export const SidebarItem = recipe({
116117
},
117118
},
118119
});
120+
121+
const PUSH_Y = 0;
122+
export const SidebarItemBottom = recipe({
123+
base: [
124+
DefaultReset,
125+
{
126+
minHeight: toRem(30),
127+
display: 'flex',
128+
alignItems: 'center',
129+
justifyContent: 'center',
130+
position: 'relative',
131+
transition: 'transform 200ms cubic-bezier(0, 0.8, 0.67, 0.97)',
132+
133+
selectors: {
134+
'&:hover': {
135+
transform: `translateY(${toRem(PUSH_Y)})`,
136+
},
137+
'&::before': {
138+
content: '',
139+
display: 'none',
140+
position: 'absolute',
141+
bottom: toRem(-11.5 - PUSH_Y),
142+
height: toRem(3 + PUSH_Y),
143+
width: toRem(16),
144+
borderRadius: `${toRem(4)} ${toRem(4)} 0 0`,
145+
background: 'CurrentColor',
146+
transition: 'height 200ms linear',
147+
},
148+
'&:hover::before': {
149+
display: 'block',
150+
height: toRem(3),
151+
},
152+
},
153+
},
154+
Disabled,
155+
DropTarget,
156+
],
157+
variants: {
158+
active: {
159+
true: {
160+
selectors: {
161+
'&::before': {
162+
display: 'block',
163+
width: toRem(24),
164+
},
165+
'&:hover::before': {
166+
height: toRem(3 + PUSH_Y),
167+
},
168+
},
169+
},
170+
},
171+
},
172+
});
119173
export type SidebarItemVariants = RecipeVariants<typeof SidebarItem>;
120174

121175
export const SidebarItemBadge = recipe({

src/app/components/sidebar/SidebarItem.tsx

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
import classNames from 'classnames';
2+
import type { Position } from 'folds';
23
import { as, Avatar, Text, Tooltip, TooltipProvider, toRem } from 'folds';
34
import type { ComponentProps, ReactNode, RefCallback } from 'react';
45
import * as css from './Sidebar.css';
56

6-
export const SidebarItem = as<'div', css.SidebarItemVariants>(
7+
export const SidebarItemBottom = as<'div', css.SidebarItemVariants>(
8+
({ as: AsSidebarAvatarBox = 'div', className, active, ...props }, ref) => (
9+
<AsSidebarAvatarBox
10+
className={classNames(css.SidebarItemBottom({ active }), className)}
11+
{...props}
12+
ref={ref}
13+
/>
14+
)
15+
);
16+
17+
export const SidebarItemLeft = as<'div', css.SidebarItemVariants>(
718
({ as: AsSidebarAvatarBox = 'div', className, active, ...props }, ref) => (
819
<AsSidebarAvatarBox
920
className={classNames(css.SidebarItem({ active }), className)}
@@ -13,6 +24,32 @@ export const SidebarItem = as<'div', css.SidebarItemVariants>(
1324
)
1425
);
1526

27+
export const SidebarItem = ({
28+
className,
29+
active,
30+
isBottom,
31+
children,
32+
...props
33+
}: {
34+
className?: string;
35+
active?: boolean;
36+
isBottom?: boolean;
37+
children: ReactNode;
38+
}) => {
39+
if (isBottom)
40+
return (
41+
<SidebarItemBottom className={className} active={active} {...props}>
42+
{children}
43+
</SidebarItemBottom>
44+
);
45+
else
46+
return (
47+
<SidebarItemLeft className={className} active={active} {...props}>
48+
{children}
49+
</SidebarItemLeft>
50+
);
51+
};
52+
1653
export const SidebarItemBadge = as<'div', css.SidebarItemBadgeVariants>(
1754
({ as: AsSidebarBadgeBox = 'div', className, mode, ...props }, ref) => (
1855
<AsSidebarBadgeBox
@@ -26,9 +63,11 @@ export const SidebarItemBadge = as<'div', css.SidebarItemBadgeVariants>(
2663
export function SidebarItemTooltip({
2764
tooltip,
2865
children,
66+
position,
2967
}: {
3068
tooltip?: ReactNode | string;
3169
children: (triggerRef: RefCallback<HTMLElement | SVGElement>) => ReactNode;
70+
position?: Position;
3271
}) {
3372
if (!tooltip) {
3473
return children(() => undefined);
@@ -37,7 +76,7 @@ export function SidebarItemTooltip({
3776
return (
3877
<TooltipProvider
3978
delay={400}
40-
position="Right"
79+
position={position ?? 'Right'}
4180
tooltip={
4281
<Tooltip style={{ maxWidth: toRem(280) }}>
4382
<Text size="H5">{tooltip}</Text>

src/app/components/user-profile/UserRoomProfile.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,17 @@ function UserExtendedSection({
108108
const [renderAnimals] = useSetting(settingsAtom, 'renderAnimals');
109109
const isCat = profile.isCat === true;
110110
const hasCats = profile.hasCats === true;
111+
const isAnimal = profile.isAnimal ?? (isCat && 'cat');
112+
const hasAnimal = profile.hasAnimal ?? (hasCats && 'cats');
113+
const animalNeed = profile.animalNeed ?? 'headpats';
111114

112115
const catStatusText = useMemo(() => {
113116
if (!renderAnimals) return null;
114-
if (isCat && hasCats) return 'Cat with cats—needs pets & love!';
115-
if (isCat) return 'Is a cat—give pets & love!';
116-
if (hasCats) return 'Has cats—send love!';
117+
if (isAnimal && hasAnimal) return `${isAnimal} with ${hasAnimal}, give ${animalNeed}!`;
118+
if (isAnimal) return `Is ${isAnimal}, give ${animalNeed}!`;
119+
if (hasAnimal) return `Has ${hasAnimal}, give ${animalNeed}!`;
117120
return null;
118-
}, [renderAnimals, isCat, hasCats]);
121+
}, [renderAnimals, isAnimal, hasAnimal, animalNeed]);
119122

120123
const languageFilterEnabled = getSettings().filterPronounsBasedOnLanguage ?? false;
121124
const languagesToFilterFor = getSettings().filterPronounsLanguages ?? ['en'];

src/app/features/room/RoomInput.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,11 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
16981698
}
16991699
setReplyDraft(undefined);
17001700
}
1701+
1702+
if (isKeyHotkey('mod+e', evt)) {
1703+
evt.preventDefault();
1704+
setEmojiBoardTab(EmojiBoardTab.Sticker);
1705+
}
17011706
},
17021707
[
17031708
submit,
@@ -1712,6 +1717,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
17121717
editDraft,
17131718
setEditDraft,
17141719
structuredMarkdownAssist,
1720+
setEmojiBoardTab,
17151721
]
17161722
);
17171723

0 commit comments

Comments
 (0)