Skip to content

Commit 0b430d8

Browse files
authored
Feat better mobile context menu (#946)
<!-- Please read https://github.com/SableClient/Sable/blob/dev/CONTRIBUTING.md before submitting your pull request --> ### Description This PR implements a context menu for mobile, refactors the option menus, and unifies events and messages to a limited degree, it also fixes some focus issues The pr does NOT fix quickmenu being underlayed in the thread root, or the entirety or focus issues. These issues could be fixed in the future with the help of this by creating an atom and showing the quickemenu as a unique item in the RoomView but that would still require a lot of work, it also does NOT unify the events and messages entirely or reduce the amount of data that is requested before usage, though it enables it for future development, nor does it the rightclicking outside of the MessageInternal/children but still within the message not bringing the menu (view of the menu on a random real message) <img width="1080" height="2307" alt="image" src="https://github.com/user-attachments/assets/90a85aaa-abde-4b15-a81f-b13dc2fb2a8d" /> (maximum size of a mobile menu) <img width="1080" height="2307" alt="image" src="https://github.com/user-attachments/assets/a5e98400-aee2-469a-8721-057d04077885" /> (this pr does NOT remove the mobile quickmenu so you can still access the message the old way) <img width="1080" height="2307" alt="image" src="https://github.com/user-attachments/assets/f31c2288-7f9c-4b35-b775-8dd9fa216636" /> If you wish to select text from a message you have to first bring the menu and then select it from it Fixes # #### Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [x] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update ### Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings ### AI disclosure: - [ ] Partially AI assisted (clarify which code was AI assisted and briefly explain what it does). - [ ] Fully AI generated (explain what all the generated code does in moderate detail). <!-- Write any explanation required here, but do not generate the explanation using AI!! You must prove you understand what the code in this PR does. --> This is being written w the assistance of whatever music is in my ears while writing it :p
2 parents de71434 + 9c01b79 commit 0b430d8

22 files changed

Lines changed: 1973 additions & 1045 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+
Add a new Mobile Context
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 reactions to arbitrary events

.changeset/fix_ghost_quickmenu.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
default: patch
3+
---
4+
5+
Fix ghost quickmenu

src/app/components/SwipeableMessageWrapper.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export function SwipeableMessageWrapper({
7171
onReply,
7272
}: {
7373
children: ReactNode;
74-
onReply: () => void;
74+
onReply?: () => void;
7575
}) {
7676
const settings = useAtomValue(settingsAtom);
7777

@@ -83,7 +83,7 @@ export function SwipeableMessageWrapper({
8383
[settings.mobileGestures, settings.rightSwipeAction]
8484
);
8585

86-
if (!isSwipeToReplyEnabled) {
86+
if (!isSwipeToReplyEnabled || !onReply) {
8787
return children;
8888
}
8989

src/app/components/emoji-board/EmojiBoard.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ type EmojiBoardProps = {
386386
onStickerSelect?: (mxc: string, shortcode: string, label: string) => void;
387387
allowTextCustomEmoji?: boolean;
388388
addToRecentEmoji?: boolean;
389+
isFullWidth?: boolean;
389390
};
390391

391392
export function EmojiBoard({
@@ -399,6 +400,7 @@ export function EmojiBoard({
399400
onStickerSelect,
400401
allowTextCustomEmoji,
401402
addToRecentEmoji = true,
403+
isFullWidth,
402404
}: Readonly<EmojiBoardProps>) {
403405
const mx = useMatrixClient();
404406
const [saveStickerEmojiBandwidth] = useSetting(settingsAtom, 'saveStickerEmojiBandwidth');
@@ -539,8 +541,12 @@ export function EmojiBoard({
539541
returnFocusOnDeactivate,
540542
initialFocus: false,
541543
onDeactivate: requestClose,
542-
clickOutsideDeactivates: true,
543-
allowOutsideClick: true,
544+
545+
allowOutsideClick: (e) => {
546+
e.preventDefault();
547+
requestClose();
548+
return false;
549+
},
544550
isKeyForward: (evt: KeyboardEvent) =>
545551
!editableActiveElement() && isKeyHotkey(['arrowdown', 'arrowright'], evt),
546552
isKeyBackward: (evt: KeyboardEvent) =>
@@ -578,6 +584,7 @@ export function EmojiBoard({
578584
/>
579585
)
580586
}
587+
isFullWidth={isFullWidth}
581588
>
582589
<Box grow="Yes">
583590
<EmojiGroupHolder

src/app/components/emoji-board/components/Layout.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ export const EmojiBoardLayout = as<
99
header: ReactNode;
1010
sidebar?: ReactNode;
1111
children: ReactNode;
12+
isFullWidth?: boolean;
1213
}
13-
>(({ className, header, sidebar, children, ...props }, ref) => (
14+
>(({ className, header, sidebar, children, isFullWidth, ...props }, ref) => (
1415
<Box
1516
display="InlineFlex"
16-
className={classNames(css.Base, className)}
17+
className={classNames(css.Base({ isFullWidth }), className)}
1718
direction="Row"
1819
{...props}
1920
ref={ref}

src/app/components/emoji-board/components/styles.css.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,33 @@
11
import { style } from '@vanilla-extract/css';
2+
import { recipe } from '@vanilla-extract/recipes';
23
import { toRem, color, config, DefaultReset, FocusOutline } from 'folds';
34

45
/**
56
* Layout
67
*/
78

8-
export const Base = style({
9-
maxWidth: toRem(432),
10-
width: `calc(100vw - 2 * ${config.space.S400})`,
11-
height: toRem(450),
12-
backgroundColor: color.Surface.Container,
13-
color: color.Surface.OnContainer,
14-
border: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
15-
borderRadius: config.radii.R400,
16-
boxShadow: config.shadow.E200,
17-
overflow: 'hidden',
9+
export const Base = recipe({
10+
base: [
11+
{
12+
maxWidth: toRem(432),
13+
width: `calc(100vw - 2 * ${config.space.S400})`,
14+
height: toRem(450),
15+
backgroundColor: color.Surface.Container,
16+
color: color.Surface.OnContainer,
17+
border: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
18+
borderRadius: config.radii.R400,
19+
boxShadow: config.shadow.E200,
20+
overflow: 'hidden',
21+
},
22+
],
23+
variants: {
24+
isFullWidth: {
25+
true: {
26+
maxWidth: '100vw',
27+
width: `calc(100vw - ${config.borderWidth.B300})`,
28+
},
29+
},
30+
},
1831
});
1932

2033
export const Header = style({

src/app/components/event-readers/EventReaders.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export const EventReaders = as<'div', EventReadersProps>(
2828
const openProfile = useOpenUserRoomProfile();
2929
const space = useSpaceOptionally();
3030
const nicknames = useAtomValue(nicknamesAtom);
31-
3231
const getName = (userId: string) =>
3332
getMemberDisplayName(room, userId, nicknames) ?? getMxIdLocalPart(userId) ?? userId;
3433

src/app/components/message/layout/Base.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const MessageBase = as<'div', css.MessageBaseVariants>(
1313
collapse,
1414
autoCollapse,
1515
space,
16+
mobile,
1617
...props
1718
},
1819
ref
@@ -27,6 +28,7 @@ export const MessageBase = as<'div', css.MessageBaseVariants>(
2728
collapse,
2829
autoCollapse,
2930
space,
31+
mobile,
3032
}),
3133
className
3234
)}

src/app/components/message/layout/layout.css.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ export const messageJumpHighlight = style({
6565
animationIterationCount: 'infinite',
6666
});
6767

68+
const MobileVariant = styleVariants({
69+
true: {
70+
WebkitUserSelect: 'none',
71+
msUserSelect: 'none',
72+
userSelect: 'none',
73+
MozUserSelect: 'none',
74+
},
75+
});
76+
6877
const HighlightVariant = styleVariants({
6978
true: [messageJumpHighlight],
7079
});
@@ -108,6 +117,8 @@ export const MessageBase = recipe({
108117
borderRadius: `0 ${config.radii.R400} ${config.radii.R400} 0`,
109118
minHeight: toRem(16),
110119
contain: 'layout',
120+
flexGrow: '1',
121+
width: '100',
111122
},
112123
],
113124
variants: {
@@ -124,6 +135,7 @@ export const MessageBase = recipe({
124135
notifyHighlight: NotifyHighlightVariant,
125136
selected: SelectedVariant,
126137
isMarked: MarkedVariant,
138+
mobile: MobileVariant,
127139
},
128140
defaultVariants: {
129141
space: '400',

0 commit comments

Comments
 (0)