From 887503e40bd47bbc25ae2da569ff8590835674fb Mon Sep 17 00:00:00 2001
From: Lim Chee Aun <cheeaun@gmail.com>
Date: Sat, 16 Sep 2023 22:57:35 +0800
Subject: [PATCH] Auto-list composing

Automatically create lists like "- " or "12. " when press Enter
---
 src/components/compose.jsx | 58 ++++++++++++++++++++++++++++++++------
 1 file changed, 50 insertions(+), 8 deletions(-)

diff --git a/src/components/compose.jsx b/src/components/compose.jsx
index 8b552321..fc4174aa 100644
--- a/src/components/compose.jsx
+++ b/src/components/compose.jsx
@@ -1175,6 +1175,17 @@ function Compose({
   );
 }
 
+function autoResizeTextarea(textarea) {
+  if (!textarea) return;
+  const { value, offsetHeight, scrollHeight, clientHeight } = textarea;
+  if (offsetHeight < window.innerHeight) {
+    // NOTE: This check is needed because the offsetHeight return 50000 (really large number) on first render
+    // No idea why it does that, will re-investigate in far future
+    const offset = offsetHeight - clientHeight;
+    textarea.style.height = value ? scrollHeight + offset + 'px' : null;
+  }
+}
+
 const Textarea = forwardRef((props, ref) => {
   const { masto } = api();
   const [text, setText] = useState(ref.current?.value || '');
@@ -1367,15 +1378,46 @@ const Textarea = forwardRef((props, ref) => {
         ref={ref}
         name="status"
         value={text}
-        onInput={(e) => {
-          const { scrollHeight, offsetHeight, clientHeight, value } = e.target;
-          setText(value);
-          if (offsetHeight < window.innerHeight) {
-            // NOTE: This check is needed because the offsetHeight return 50000 (really large number) on first render
-            // No idea why it does that, will re-investigate in far future
-            const offset = offsetHeight - clientHeight;
-            e.target.style.height = value ? scrollHeight + offset + 'px' : null;
+        onKeyDown={(e) => {
+          // Get line before cursor position after pressing 'Enter'
+          const { key, target } = e;
+          if (key === 'Enter') {
+            try {
+              const { value, selectionStart } = target;
+              const textBeforeCursor = value.slice(0, selectionStart);
+              const lastLine = textBeforeCursor.split('\n').slice(-1)[0];
+              if (lastLine) {
+                // If line starts with "- " or "12. "
+                if (/^\s*(-|\d+\.)\s/.test(lastLine)) {
+                  // insert "- " at cursor position
+                  const [_, preSpaces, bullet, postSpaces, anything] =
+                    lastLine.match(/^(\s*)(-|\d+\.)(\s+)(.+)?/) || [];
+                  if (anything) {
+                    e.preventDefault();
+                    const [number] = bullet.match(/\d+/) || [];
+                    const newBullet = number ? `${+number + 1}.` : '-';
+                    const text = `\n${preSpaces}${newBullet}${postSpaces}`;
+                    target.setRangeText(text, selectionStart, selectionStart);
+                    const pos = selectionStart + text.length;
+                    target.setSelectionRange(pos, pos);
+                  } else {
+                    // trim the line before the cursor, then insert new line
+                    const pos = selectionStart - lastLine.length;
+                    target.setRangeText('', pos, selectionStart);
+                  }
+                  autoResizeTextarea(target);
+                }
+              }
+            } catch (e) {
+              // silent fail
+              console.error(e);
+            }
           }
+        }}
+        onInput={(e) => {
+          const { target } = e;
+          setText(target.value);
+          autoResizeTextarea(target);
           props.onInput?.(e);
         }}
         style={{