Skip to content

Commit 152f67a

Browse files
committed
fix: Markdown save format issues
1 parent a617937 commit 152f67a

14 files changed

Lines changed: 263 additions & 21 deletions

File tree

frontend/apps/app/android/app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<action android:name="android.speech.RecognitionService"/>
2020
</intent>
2121
</queries>
22-
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:requestLegacyExternalStorage="true">
22+
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:roundIcon="@drawable/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:requestLegacyExternalStorage="true">
2323
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/>
2424
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
2525
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
@@ -38,4 +38,4 @@
3838
</intent-filter>
3939
</activity>
4040
</application>
41-
</manifest>
41+
</manifest>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="108dp"
3+
android:height="108dp"
4+
android:viewportWidth="64"
5+
android:viewportHeight="64">
6+
<path android:fillColor="#A4C9FF" android:pathData="M5.906,32c0,-14.35 11.65,-26 26,-26s26,11.65 26,26 -11.65,26 -26,26 -26,-11.65 -26,-26z" />
7+
<path android:fillColor="#80FFFFFF" android:pathData="M50.335,13.694c4.684,4.688 7.571,11.162 7.571,18.306 0,4.942 -1.382,9.564 -3.78,13.5H13.421c-0.058,-0.9 -0.087,-1.811 -0.087,-2.73 0,-18.079 11.36,-32.757 25.353,-32.757 4.201,0 8.165,1.323 11.648,3.681z" />
8+
<path android:fillColor="#80FFFFFF" android:pathData="M13.034,14.138C8.614,18.791 5.906,25.082 5.906,32c0,4.942 1.382,9.564 3.78,13.5h40.893c0.058,-0.9 0.087,-1.811 0.087,-2.73 0,-18.079 -11.36,-32.757 -25.353,-32.757 -4.457,0 -8.648,1.489 -12.279,4.125z" />
9+
<path android:fillColor="#80FFFFFF" android:pathData="M56.437,40.618c-1.578,4.498 -4.361,8.43 -7.973,11.397 -4.8,0.717 -10.482,1.124 -16.558,1.124 -6.076,0 -11.758,-0.407 -16.558,-1.124 -3.612,-2.967 -6.395,-6.899 -7.973,-11.397 5.843,-1.686 14.668,-2.757 24.531,-2.757s18.688,1.071 24.531,2.757z" />
10+
</vector>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="108dp"
3+
android:height="108dp"
4+
android:viewportWidth="64"
5+
android:viewportHeight="64">
6+
<path android:fillColor="#A4C9FF" android:pathData="M5.906,32c0,-14.35 11.65,-26 26,-26s26,11.65 26,26 -11.65,26 -26,26 -26,-11.65 -26,-26z" />
7+
<path android:fillColor="#80FFFFFF" android:pathData="M50.335,13.694c4.684,4.688 7.571,11.162 7.571,18.306 0,4.942 -1.382,9.564 -3.78,13.5H13.421c-0.058,-0.9 -0.087,-1.811 -0.087,-2.73 0,-18.079 11.36,-32.757 25.353,-32.757 4.201,0 8.165,1.323 11.648,3.681z" />
8+
<path android:fillColor="#80FFFFFF" android:pathData="M13.034,14.138C8.614,18.791 5.906,25.082 5.906,32c0,4.942 1.382,9.564 3.78,13.5h40.893c0.058,-0.9 0.087,-1.811 0.087,-2.73 0,-18.079 -11.36,-32.757 -25.353,-32.757 -4.457,0 -8.648,1.489 -12.279,4.125z" />
9+
<path android:fillColor="#80FFFFFF" android:pathData="M56.437,40.618c-1.578,4.498 -4.361,8.43 -7.973,11.397 -4.8,0.717 -10.482,1.124 -16.558,1.124 -6.076,0 -11.758,-0.407 -16.558,-1.124 -3.612,-2.967 -6.395,-6.899 -7.973,-11.397 5.843,-1.686 14.668,-2.757 24.531,-2.757s18.688,1.071 24.531,2.757z" />
10+
</vector>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<resources>
2-
<string name="app_name">memoneo</string>
2+
<string name="app_name">Memoneo</string>
33
<string name="expo_system_ui_user_interface_style" translatable="false">automatic</string>
44
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
55
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
6-
</resources>
6+
</resources>

frontend/apps/app/app.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"expo": {
3-
"name": "memoneo",
3+
"name": "Memoneo",
44
"slug": "memoneo",
55
"version": "1.0.0",
66
"orientation": "portrait",
14.6 KB
Loading
4.84 KB
Loading
13.2 KB
Loading

frontend/apps/app/components/note/editor/MarkdownToolbar.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@ import { ToolbarButton } from "./ToolbarButton"
1616
type MarkdownToolbarProps = {
1717
bottomInset: number
1818
editorRef: React.RefObject<EnrichedMarkdownTextInputInstance | null>
19+
plainMarkdownActions?: {
20+
bold: () => void
21+
italic: () => void
22+
strikethrough: () => void
23+
underline: () => void
24+
}
1925
styleState: StyleState | null
2026
}
2127

2228
export function MarkdownToolbar({
2329
bottomInset,
2430
editorRef,
31+
plainMarkdownActions,
2532
styleState,
2633
}: MarkdownToolbarProps) {
2734
return (
@@ -33,7 +40,9 @@ export function MarkdownToolbar({
3340
<ToolbarButton
3441
accessibilityLabel="Bold"
3542
active={styleState?.bold.isActive}
36-
onPress={() => editorRef.current?.toggleBold()}>
43+
onPress={() =>
44+
plainMarkdownActions?.bold() ?? editorRef.current?.toggleBold()
45+
}>
3746
<Bold
3847
size={18}
3948
color={styleState?.bold.isActive ? "#f8fafc" : "#a1a1aa"}
@@ -42,7 +51,9 @@ export function MarkdownToolbar({
4251
<ToolbarButton
4352
accessibilityLabel="Italic"
4453
active={styleState?.italic.isActive}
45-
onPress={() => editorRef.current?.toggleItalic()}>
54+
onPress={() =>
55+
plainMarkdownActions?.italic() ?? editorRef.current?.toggleItalic()
56+
}>
4657
<Italic
4758
size={18}
4859
color={styleState?.italic.isActive ? "#f8fafc" : "#a1a1aa"}
@@ -51,7 +62,10 @@ export function MarkdownToolbar({
5162
<ToolbarButton
5263
accessibilityLabel="Underline"
5364
active={styleState?.underline.isActive}
54-
onPress={() => editorRef.current?.toggleUnderline()}>
65+
onPress={() =>
66+
plainMarkdownActions?.underline() ??
67+
editorRef.current?.toggleUnderline()
68+
}>
5569
<Underline
5670
size={18}
5771
color={styleState?.underline.isActive ? "#f8fafc" : "#a1a1aa"}
@@ -60,7 +74,10 @@ export function MarkdownToolbar({
6074
<ToolbarButton
6175
accessibilityLabel="Strikethrough"
6276
active={styleState?.strikethrough.isActive}
63-
onPress={() => editorRef.current?.toggleStrikethrough()}>
77+
onPress={() =>
78+
plainMarkdownActions?.strikethrough() ??
79+
editorRef.current?.toggleStrikethrough()
80+
}>
6481
<Strikethrough
6582
size={18}
6683
color={styleState?.strikethrough.isActive ? "#f8fafc" : "#a1a1aa"}

frontend/apps/app/components/note/editor/NoteEditorBody.tsx

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
import { memo, useEffect, useMemo, useRef } from "react"
1+
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
22
import { StyleSheet, View } from "react-native"
33
import {
44
EnrichedMarkdownTextInput,
55
type EnrichedMarkdownTextInputInstance,
66
type MarkdownTextInputStyle,
77
} from "react-native-enriched-markdown"
88
import { KeyboardAvoidingView } from "react-native-keyboard-controller"
9+
import { useSafeAreaInsets } from "react-native-safe-area-context"
910

11+
import {
12+
escapeMarkdownForPlainEditing,
13+
normalizeNoteBody,
14+
shouldEditAsPlainMarkdown,
15+
} from "./markdownInputMode"
1016
import { MarkdownToolbar } from "./MarkdownToolbar"
11-
import { useSafeAreaInsets } from "react-native-safe-area-context"
1217

1318
type NoteEditorBodyProps = {
1419
defaultBody: string
@@ -44,8 +49,25 @@ function NoteEditorBodyComponent({
4449
if (__DEV__) {
4550
console.log("NoteEditorBody render", JSON.stringify({ defaultBody, noteId }))
4651
}
52+
const normalizedDefaultBody = useMemo(
53+
() => normalizeNoteBody(defaultBody),
54+
[defaultBody],
55+
)
56+
const editAsPlainMarkdown = useMemo(
57+
() => shouldEditAsPlainMarkdown(normalizedDefaultBody),
58+
[normalizedDefaultBody],
59+
)
60+
const editorDefaultValue = useMemo(
61+
() =>
62+
editAsPlainMarkdown
63+
? escapeMarkdownForPlainEditing(normalizedDefaultBody)
64+
: normalizedDefaultBody,
65+
[editAsPlainMarkdown, normalizedDefaultBody],
66+
)
4767
const insets = useSafeAreaInsets()
4868
const editorRef = useRef<EnrichedMarkdownTextInputInstance>(null)
69+
const currentBodyRef = useRef(normalizedDefaultBody)
70+
const [selection, setSelection] = useState({ start: 0, end: 0 })
4971

5072
useEffect(() => {
5173
if (__DEV__) {
@@ -57,6 +79,67 @@ function NoteEditorBodyComponent({
5779
}
5880
}
5981
}, [noteId])
82+
83+
useEffect(() => {
84+
currentBodyRef.current = normalizedDefaultBody
85+
setSelection({ start: 0, end: 0 })
86+
}, [normalizedDefaultBody, noteId])
87+
88+
const handlePlainMarkdownChange = useCallback(
89+
(nextBody: string) => {
90+
const normalizedBody = normalizeNoteBody(nextBody)
91+
currentBodyRef.current = normalizedBody
92+
onBodyChange(normalizedBody)
93+
},
94+
[onBodyChange],
95+
)
96+
97+
const applyPlainMarkdownDelimiter = useCallback(
98+
(delimiter: string) => {
99+
const body = currentBodyRef.current
100+
const start = Math.max(0, Math.min(selection.start, body.length))
101+
const end = Math.max(start, Math.min(selection.end, body.length))
102+
const selectedText = body.slice(start, end)
103+
const nextBody =
104+
body.slice(0, start) +
105+
delimiter +
106+
selectedText +
107+
delimiter +
108+
body.slice(end)
109+
const nextSelection = selectedText
110+
? {
111+
start: start + delimiter.length,
112+
end: end + delimiter.length,
113+
}
114+
: {
115+
start: start + delimiter.length,
116+
end: start + delimiter.length,
117+
}
118+
119+
currentBodyRef.current = nextBody
120+
onBodyChange(nextBody)
121+
editorRef.current?.setValue(escapeMarkdownForPlainEditing(nextBody))
122+
setSelection(nextSelection)
123+
setTimeout(() => {
124+
editorRef.current?.setSelection(nextSelection.start, nextSelection.end)
125+
}, 0)
126+
},
127+
[onBodyChange, selection],
128+
)
129+
130+
const plainMarkdownActions = useMemo(
131+
() =>
132+
editAsPlainMarkdown
133+
? {
134+
bold: () => applyPlainMarkdownDelimiter("**"),
135+
italic: () => applyPlainMarkdownDelimiter("*"),
136+
strikethrough: () => applyPlainMarkdownDelimiter("~~"),
137+
underline: () => applyPlainMarkdownDelimiter("_"),
138+
}
139+
: undefined,
140+
[applyPlainMarkdownDelimiter, editAsPlainMarkdown],
141+
)
142+
60143
const markdownStyle: MarkdownTextInputStyle = useMemo(
61144
() => ({
62145
strong: { color: "#f8fafc" },
@@ -76,9 +159,13 @@ function NoteEditorBodyComponent({
76159
<EnrichedMarkdownTextInput
77160
key={noteId}
78161
ref={editorRef}
79-
defaultValue={defaultBody}
162+
defaultValue={editorDefaultValue}
80163
markdownStyle={markdownStyle}
81-
onChangeMarkdown={onBodyChange}
164+
onChangeMarkdown={editAsPlainMarkdown ? undefined : onBodyChange}
165+
onChangeSelection={setSelection}
166+
onChangeText={
167+
editAsPlainMarkdown ? handlePlainMarkdownChange : undefined
168+
}
82169
placeholder="Start writing..."
83170
placeholderTextColor="#71717a"
84171
selectionColor="#94a3b8"
@@ -87,6 +174,7 @@ function NoteEditorBodyComponent({
87174
<MarkdownToolbar
88175
bottomInset={insets.bottom}
89176
editorRef={editorRef}
177+
plainMarkdownActions={plainMarkdownActions}
90178
styleState={null}
91179
/>
92180
</View>

0 commit comments

Comments
 (0)