Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!

04.01.2025

Моя спроба зробити Undo/Redo

Був в мене проєкт застосунку для самоменеджменту… який не досяг успіху через відсутність бачення. Втім, рушій я для нього нагородив досить потужний. Застосунок був на React Native, а стан зберігався у базі PouchDB, яка синхронізувалася з сервером CouchDB. Для мене, як я вже писав, CouchDB досі залишається потужною, але нішевою технологією без зрозумілого застосування — приблизно як Clojure.

Отже, механізм Undo/Redo, тобто “скасувати/повторити”. Зазвичай його складність полягає в неоднорідності операцій зі станом. Так, в багатьох (чи усіх) застосунках текстові операції є скасованими, але спробуй змінити блоки, що містять той текст — наприклад, задачі — та можливість скасування втрачається.

CouchDB тут має корисну властивість. Документи в ній мають ревізії, та старі ревізії зберігаються поруч з новими. Це потрібно для реплікації. Є схеми, де реплікуються зміни, а ось в CouchDB реплікуються цілі ревізії. Таким чином, досить довго можна прочитати стару версію документа. Навіть видалення відбувається через нову ревізію з маркером _deleted. Тому: всі дії в моєму проєкті проходили через єдину операцію put, а також — я завжди мав доступ до старих ревізій всіх документів.

Але очевидно, для користувача інтерфейс повинен бути вище за скасування “документ за документом, ревізія за ревізією”. Тому всі функції-дії, що потребували скасування, загорталися в функцію withUndo та отримували всередині особливу реалізацію інтерфейсу DBEffects, яку потім передавали усім операціям з базою. А інтерфейс DBEffects складався з єдиної функції put… з Undo вона не тільки писала в базу, а й складала перелік документів та їх ревізій до об’єкта, створеного в функції withUndo. (Звісно, через put були реалізовані звичайні create, update, delete і так далі.)

Потім, щоб скасувати останню дію, я брав крайній обʼєкт з переліку дій, та вертав назад попередній стан порушених документів. Ось так просто. (Ревізії утворювались нові, до речі, бо як і в Git, старі дані не можна змінити.) Та це дійсно працювало з будь-якими змінами, від примітивних до складних.