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
31 changes: 17 additions & 14 deletions src/app/components/SyncConnectionStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function SyncConnectionStatusBanner({ status }: SyncConnectionStatusProps
<Box
style={{
position: 'fixed',
top: config.space.S300,
top: `calc(var(--safe-area-inset-top, env(safe-area-inset-top, 0px)) + ${config.space.S300})`,
left: 0,
right: 0,
zIndex: 1000,
Expand All @@ -84,6 +84,21 @@ export function SyncConnectionStatusBanner({ status }: SyncConnectionStatusProps
exit="exit"
style={{
willChange: shouldReduceMotion ? 'opacity' : 'transform, opacity',
borderRadius: '9999px',
overflow: 'hidden',
backdropFilter: 'blur(64px) saturate(160%)',
WebkitBackdropFilter: 'blur(64px) saturate(160%)',
backgroundColor: `color-mix(in srgb, ${
status.variant === 'Primary'
? 'var(--sable-primary-main)'
: status.variant === 'Success'
? 'var(--sable-success-main)'
: status.variant === 'Warning'
? 'var(--sable-warn-main)'
: status.variant === 'Critical'
? 'var(--sable-crit-main)'
: 'currentColor'
} 32%, transparent)`,
}}
>
<Box
Expand All @@ -103,19 +118,7 @@ export function SyncConnectionStatusBanner({ status }: SyncConnectionStatusProps
? 'var(--sable-crit-main)'
: 'currentColor'
} 30%, transparent)`,
backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(20px)',
backgroundColor: `color-mix(in srgb, ${
status.variant === 'Primary'
? 'var(--sable-primary-main)'
: status.variant === 'Success'
? 'var(--sable-success-main)'
: status.variant === 'Warning'
? 'var(--sable-warn-main)'
: status.variant === 'Critical'
? 'var(--sable-crit-main)'
: 'currentColor'
} 18%, transparent)`,
backgroundColor: 'transparent',
position: 'relative',
overflow: 'hidden',
}}
Expand Down
5 changes: 0 additions & 5 deletions src/app/components/editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -390,11 +390,6 @@ export const CustomEditor = forwardRef<HTMLDivElement, CustomEditorProps>(

const handleKeydown: KeyboardEventHandler = useCallback(
(evt) => {
// mobile ignores config option
if (mobileOrTablet() && evt.key === 'Enter' && !evt.shiftKey) {
return;
}

onKeyDown?.(evt);

const shortcutToggled = toggleKeyboardShortcut(editor, evt, shortcutOverrides);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { color, config, toRem } from 'folds';

export const Container = style({
position: 'fixed',
top: 'calc(env(safe-area-inset-top, 0px) + 16px)',
top: 'calc(var(--safe-area-inset-top, env(safe-area-inset-top, 0px)) + 16px)',
left: '50%',
transform: 'translateX(-50%)',
zIndex: 9998,
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/message/modals/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ export function OptionsEmojiBoard({
anchor={emojiBoardAnchor}
style={isModal ? { width: '100%' } : {}}
content={
<Menu>
<Menu className={isModal ? css.MessageOptionsMenu : undefined}>
{dragOpts?.dragHandle}
{ActualMessage}
<EmojiBoard
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,15 @@ const slideOut = keyframes({
// env() fallback handles the window between page load and injection.
export const BannerContainer = style({
position: 'fixed',
// Use env(safe-area-inset-top) to respect device-specific safe areas (notches, etc)
// This also helps position correctly on iOS when the keyboard is open
top: 'env(safe-area-inset-top, 0)',
// Use safe-area-inset-top to position right below device-specific safe areas (notches, etc)
top: 'var(--safe-area-inset-top, env(safe-area-inset-top, 0px))',
left: 0,
right: 0,
zIndex: 9999,
display: 'flex',
flexDirection: 'column',
gap: config.space.S200,
padding: config.space.S400,
paddingTop: `calc(${config.space.S400} + var(--sable-inset-top, env(safe-area-inset-top, 0px)))`,
pointerEvents: 'none',
alignItems: 'flex-end',

Expand All @@ -50,7 +48,7 @@ export const BannerContainer = style({
// iOS-specific: Position relative to the visible viewport when keyboard is open
position: 'fixed',
// Support both old and new safe area syntax
top: 'max(env(safe-area-inset-top, 0px), constant(safe-area-inset-top, 0px))',
top: 'max(var(--safe-area-inset-top, 0px), env(safe-area-inset-top, 0px), constant(safe-area-inset-top, 0px))',
},
},
});
Expand All @@ -70,7 +68,7 @@ export const Banner = style({
boxShadow: `0 ${toRem(8)} ${toRem(32)} rgba(0, 0, 0, 0.45), 0 ${toRem(2)} ${toRem(8)} rgba(0, 0, 0, 0.3)`,
cursor: 'pointer',
width: '100%',
maxWidth: '50em',
maxWidth: toRem(420),
animationName: slideIn,
animationDuration: '260ms',
animationTimingFunction: 'cubic-bezier(0.22, 0.8, 0.6, 1)',
Expand Down
2 changes: 1 addition & 1 deletion src/app/features/room/message/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export const Pronouns = as<
selectedLanguages
);

const limit = getSettings().pronounPillMaxCount ?? 3;
const limit = getSettings().pronounPillMaxCount ?? (mobileOrTablet() ? 1 : 3);
const maxPillLength = getSettings().pronounPillMaxLength ?? 16;

// if language specific pronouns can't be found matching the filter return unfiltered
Expand Down
44 changes: 34 additions & 10 deletions src/app/features/room/message/styles.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,27 @@ export const MessageOptionsWrappedMessage = style({
export const MessageOptionsMenu = style({
width: '100%',
maxHeight: '100%',
position: 'absolute',
bottom: '0',
position: 'relative',
display: 'flex',
flexDirection: 'column',
borderBottomLeftRadius: '0 !important',
borderBottomRightRadius: '0 !important',
borderBottom: 'none !important',
borderTopLeftRadius: `${toRem(20)} !important`,
borderTopRightRadius: `${toRem(20)} !important`,
paddingBottom: `calc(${config.space.S400} + var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px))) !important`,
selectors: {
'&::after': {
content: '""',
position: 'absolute',
top: '100%',
left: 0,
right: 0,
height: '300px',
backgroundColor: 'inherit',
border: 'none',
},
},
});

export const PreventSelect = style({
Expand All @@ -66,22 +83,29 @@ export const MessageNickEditor = style({
});

export const MessageMobileOptionsWrapped = style({
position: 'absolute',
bottom: '0',
zIndex: '104',
width: '100%',
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 1004,
width: '100vw',
height: '100%',
backgroundColor: color.Other.Overlay,
});

export const MessageMobileOptionsContainer = style({
position: 'absolute',
bottom: '0',
zIndex: '105',
position: 'fixed',
bottom: 0,
left: 0,
right: 0,
zIndex: 1005,
width: '100%',
height: '85%',
maxHeight: '85vh',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-end',
overflow: 'visible',
animation: `${keyframes({
from: { transform: 'translateY(100%)' },
to: { transform: 'translateY(0)' },
Expand Down
22 changes: 5 additions & 17 deletions src/app/features/settings/general/General.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ function DateAndTime() {
);
}

function Editor({ isMobile }: Readonly<{ isMobile: boolean }>) {
function Editor() {
const [enterForNewline, setEnterForNewline] = useSetting(settingsAtom, 'enterForNewline');
const [editorToolbar, setEditorToolbar] = useSetting(settingsAtom, 'editorToolbar');
const [editorOldAddFile, setEditorOldAddFile] = useSetting(settingsAtom, 'editorOldAddFile');
Expand All @@ -434,24 +434,12 @@ function Editor({ isMobile }: Readonly<{ isMobile: boolean }>) {
return (
<Box direction="Column" gap="100">
<Text size="L400">Editor</Text>
<SequenceCard
className={SequenceCardStyle}
variant="SurfaceVariant"
direction="Column"
style={{ opacity: isMobile ? 0.5 : 1 }}
>
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
<SettingTile
title="ENTER for Newline"
focusId="enter-for-newline"
description={`Use ${isMacOS() ? KeySymbol.Command : 'Ctrl'} + ENTER to send message. ${isMobile ? '(Disabled on Mobile)' : ''}`}
after={
<Switch
variant="Primary"
value={enterForNewline}
onChange={setEnterForNewline}
disabled={isMobile}
/>
}
description={`Use ${isMacOS() ? KeySymbol.Command : 'Ctrl'} + ENTER to send message.`}
after={<Switch variant="Primary" value={enterForNewline} onChange={setEnterForNewline} />}
/>
</SequenceCard>
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
Expand Down Expand Up @@ -1611,7 +1599,7 @@ export function General({ requestBack, requestClose }: Readonly<GeneralProps>) {
<Box direction="Column" gap="700">
<DateAndTime />
<Gestures isMobile={mobileOrTablet()} />
<Editor isMobile={mobileOrTablet()} />
<Editor />
<Messages />
<Embeds />
<Calls />
Expand Down
4 changes: 2 additions & 2 deletions src/app/state/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ export const defaultSettings: Settings = {
isPeopleDrawer: true,
isWidgetDrawer: false,
memberSortFilterIndex: 0,
enterForNewline: false,
enterForNewline: mobileOrTablet(),
editorToolbar: false,
editorOldAddFile: false,
composerToolbarOpen: false,
Expand Down Expand Up @@ -350,7 +350,7 @@ export const defaultSettings: Settings = {
privacyBlurEmotes: false,
showPronouns: true,
parsePronouns: true,
pronounPillMaxCount: 3,
pronounPillMaxCount: mobileOrTablet() ? 1 : 3,
pronounPillMaxLength: 16,
renderGlobalNameColors: true,
renderUserCards: 'both',
Expand Down
4 changes: 3 additions & 1 deletion src/app/styles/edgeToEdgeInsets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ describe('android edge-to-edge inset contract', () => {
);

expect(notificationBannerStyles).toContain("position: 'fixed'");
expect(notificationBannerStyles).toContain("top: 'env(safe-area-inset-top, 0)'");
expect(notificationBannerStyles).toContain(
"top: 'var(--safe-area-inset-top, env(safe-area-inset-top, 0px))'"
);
expect(telemetryBannerStyles).toContain("position: 'fixed'");
});
});
10 changes: 10 additions & 0 deletions src/app/utils/user-agent.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { isTauri } from '@tauri-apps/api/core';
import { type as osType } from '@tauri-apps/plugin-os';
import { UAParser } from 'ua-parser-js';

const result = new UAParser(window.navigator.userAgent).getResult();

const isMobileOrTablet = (() => {
if (isTauri()) {
try {
const tauriOs = osType();
if (tauriOs === 'android' || tauriOs === 'ios') return true;
} catch {
// Fallback to UA parsing if plugin-os is not ready/available
}
}
const { os, device } = result;
if (device.type === 'mobile' || device.type === 'tablet') return true;
if (os.name === 'Android' || os.name === 'iOS') return true;
Expand Down
Loading