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
23 changes: 16 additions & 7 deletions src/assets/svgs/wavy-dash-icon.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { StyleProp, ViewStyle } from 'react-native';
import { StyleProp, View, ViewStyle } from 'react-native';
import Svg, { Path } from 'react-native-svg';

interface Props {
Expand All @@ -12,14 +12,23 @@
* Wavy dash (〰️) glyph used as the Waves bottom-tab icon. Single Twemoji path,
* tinted via `color` so it follows the active/inactive tab tint like the other
* (vector-font) tab icons.
*
* The layout `style` (the tab bar's shared paddingTop offset) is applied to a
* wrapping View, not the <Svg>: react-native-svg does not honor padding the way
* the vector-icon <Text> glyphs do, so applying it to <Svg> left the icon ~15px
* too high. The wrapper sizes to the SVG and inherits the same top offset, and
* the parent tab cell centers it horizontally — keeping the Waves icon aligned
* with the other icons across phones and tablets on both iOS and Android.
*/
const WavyDashIcon = ({ color = '#000000', size = 26, style }: Props) => (
<Svg width={size} height={size} viewBox="0 0 36 36" style={style}>
<Path
fill={color}
d="M27 23c-2.589 0-4.005-2.549-5.374-5.014C20.537 16.026 19.411 14 18 14c-1.412 0-2.537 2.026-3.626 3.986C13.004 20.451 11.588 23 9 23c-2.65 0-3.853-2.706-4.914-5.094C3.038 15.546 2.256 14 1 14a1 1 0 0 1 0-2c2.65 0 3.853 2.706 4.914 5.094C6.962 19.453 7.744 21 9 21c1.412 0 2.537-2.026 3.626-3.986C13.996 14.549 15.412 12 18 12c2.589 0 4.005 2.549 5.374 5.014C24.463 18.974 25.589 21 27 21c1.256 0 2.037-1.547 3.086-3.906C31.147 14.706 32.351 12 35 12a1 1 0 1 1 0 2c-1.256 0-2.037 1.546-3.086 3.906C30.853 20.294 29.649 23 27 23z"
/>
</Svg>
<View style={style}>
<Svg width={size} height={size} viewBox="0 0 36 36">
<Path
fill={color}
d="M27 23c-2.589 0-4.005-2.549-5.374-5.014C20.537 16.026 19.411 14 18 14c-1.412 0-2.537 2.026-3.626 3.986C13.004 20.451 11.588 23 9 23c-2.65 0-3.853-2.706-4.914-5.094C3.038 15.546 2.256 14 1 14a1 1 0 0 1 0-2c2.65 0 3.853 2.706 4.914 5.094C6.962 19.453 7.744 21 9 21c1.412 0 2.537-2.026 3.626-3.986C13.996 14.549 15.412 12 18 12c2.589 0 4.005 2.549 5.374 5.014C24.463 18.974 25.589 21 27 21c1.256 0 2.037-1.547 3.086-3.906C31.147 14.706 32.351 12 35 12a1 1 0 1 1 0 2c-1.256 0-2.037 1.546-3.086 3.906C30.853 20.294 29.649 23 27 23z"

Check warning on line 28 in src/assets/svgs/wavy-dash-icon.tsx

View workflow job for this annotation

GitHub Actions / Lint & Unit Tests

This line has a length of 538. Maximum allowed is 100

Check warning on line 28 in src/assets/svgs/wavy-dash-icon.tsx

View workflow job for this annotation

GitHub Actions / Lint & Unit Tests

This line has a length of 538. Maximum allowed is 100

Check warning on line 28 in src/assets/svgs/wavy-dash-icon.tsx

View workflow job for this annotation

GitHub Actions / Lint & Unit Tests

This line has a length of 538. Maximum allowed is 100
/>
</Svg>
</View>
);

export default WavyDashIcon;
22 changes: 20 additions & 2 deletions src/components/markdownEditor/view/markdownEditorView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,13 @@ const MarkdownEditorView = ({
// Resume at the user's last caret position instead of jumping to the end
// of the body (which scrolls a long draft to the bottom). Clamp to the
// current length in case the body shrank since the caret was saved.
const savedCaret = store.getState().editor.caretMap?.[_caretKey] ?? 0;
const caret = Math.min(savedCaret, draftBody.length);
// When no caret was saved — a legacy draft, one created on another device,
// or the first open after this shipped — fall back to the end of the body
// (the long-standing behavior). This keeps the feature purely additive and
// avoids dropping the auto-focused cursor at position 0 (prepend-on-type).
const savedCaret = store.getState().editor.caretMap?.[_caretKey];
const caret =
typeof savedCaret === 'number' ? Math.min(savedCaret, draftBody.length) : draftBody.length;
_setTextAndSelection({
selection: { start: caret, end: caret },
text: draftBody,
Expand Down Expand Up @@ -241,6 +246,19 @@ const MarkdownEditorView = ({
[],
);

// On unmount, flush (not cancel) the pending debounced writes so the latest
// caret and body are committed synchronously before the editor tears down.
// Cancelling would drop the last cursor move / keystrokes within the debounce
// window — leaving a stale caret and body on reopen — and letting them fire
// late would write after unmount. Flushing both closes that window cleanly.
useEffect(
() => () => {
_persistCaret.flush();
_debouncedOnTextChange.flush();
},
[_persistCaret, _debouncedOnTextChange],
);

const _handleOnSelectionChange = async (event) => {
bodySelectionRef.current = event.nativeEvent.selection;
_persistCaret(event.nativeEvent.selection.start);
Expand Down
Loading