~emersion/public-inbox

Store and recall last 5 messages per buffer with arrow keys v1 NEEDS REVISION

sentriz: 1
 Store and recall last 5 messages per buffer with arrow keys

 3 files changed, 67 insertions(+), 1 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~emersion/public-inbox/patches/29482/mbox | git am -3
Learn more about email & git

[PATCH] Store and recall last 5 messages per buffer with arrow keys Export this patch

Hi! This patch let's you use the arrow keys up/down to recall the last
5 messages sent on a per-buffer basis. The lines are kept in a new localStorage
key. (I can also reuse one of the existing localStorage keys if you like)
A side-effect of this change (and actually lead to simpler implementation)
is that unsent message drafts are stored as well. Eg. you can start typing
a message a message to a user, then switch to a different buffer or even
reload gamja, then go back to the buffer and sent the message.
(this required that a `key` prop is passed to <Composer /> so that we get a
unique render per buffer, giving it a chance to fetch from localStorage again)

Thanks!
---
 components/app.js      |  6 ++++++
 components/composer.js | 27 ++++++++++++++++++++++++++-
 store.js               | 35 +++++++++++++++++++++++++++++++++++
 3 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/components/app.js b/components/app.js
index cde062a..46a5deb 100644
--- a/components/app.js
+++ b/components/app.js
@@ -222,6 +222,7 @@ export default class App extends Component {
		this.handleVerifySubmit = this.handleVerifySubmit.bind(this);

		this.bufferStore = new store.Buffer();
		this.sentStore = new store.SentLines();

		configPromise.then((config) => {
			this.handleConfig(config);
@@ -1888,11 +1889,16 @@ export default class App extends Component {
			</>
			${memberList}
			<${Composer}
				key=${activeBuffer.id}
				ref=${this.composer}
				readOnly=${composerReadOnly}
				onSubmit=${this.handleComposerSubmit}
				autocomplete=${this.autocomplete}
				commandOnly=${commandOnly}

				sentGet=${() => this.sentStore.get(String(activeBuffer.id))}
				sentPush=${(text) => this.sentStore.push(String(activeBuffer.id), text)}
				sentUpdate=${(text) => this.sentStore.update(String(activeBuffer.id), text)}
			/>
			${dialog}
			${error}
diff --git a/components/composer.js b/components/composer.js
index f029381..9c1162c 100644
--- a/components/composer.js
+++ b/components/composer.js
@@ -3,6 +3,7 @@ import { html, Component, createRef } from "../lib/index.js";
export default class Composer extends Component {
	state = {
		text: "",
		sentLinesIndex: 0,
	};
	textInput = createRef();
	lastAutocomplete = null;
@@ -15,11 +16,18 @@ export default class Composer extends Component {
		this.handleInputKeyDown = this.handleInputKeyDown.bind(this);
		this.handleWindowKeyDown = this.handleWindowKeyDown.bind(this);
		this.handleWindowPaste = this.handleWindowPaste.bind(this);

		// populate the initial text input from the store if we have it
		this.state.text = props.sentGet()?.[this.state.sentLinesIndex] || "";
	}

	handleInput(event) {
		this.setState({ [event.target.name]: event.target.value });

		if (event.target.name === "text") {
			this.props.sentUpdate(event.target.value);
		}

		if (this.props.readOnly && event.target.name === "text" && !event.target.value) {
			event.target.blur();
		}
@@ -28,10 +36,26 @@ export default class Composer extends Component {
	handleSubmit(event) {
		event.preventDefault();
		this.props.onSubmit(this.state.text);
		this.setState({ text: "" });
		this.props.sentPush("");
		this.setState({ text: "", sentLinesIndex: 0 });
	}

	handleLoadSentLine(diff) {
		const i = this.state.sentLinesIndex + diff;
		const lines = this.props.sentGet();
		if (i >= lines.length || i < 0) return;

		this.setState({ text: lines[i], sentLinesIndex: i });
	}

	handleInputKeyDown(event) {
		switch (event.key) {
			case "ArrowUp":
				return this.handleLoadSentLine(+1);
			case "ArrowDown":
				return this.handleLoadSentLine(-1);
		}

		let input = event.target;

		if (!this.props.autocomplete || event.key !== "Tab") {
@@ -114,6 +138,7 @@ export default class Composer extends Component {
		this.lastAutocomplete = autocomplete;

		this.setState({ text: autocomplete.text });
		this.props.sentUpdate(autocomplete.text);
	}

	handleWindowKeyDown(event) {
diff --git a/store.js b/store.js
index 6bf31aa..d288e56 100644
--- a/store.js
+++ b/store.js
@@ -146,3 +146,38 @@ export class Buffer {
		this.save();
	}
}

export class SentLines {
	raw = new Item("sent_lines");
	m = null;

	constructor() {
		const obj = this.raw.load();
		this.m = new Map(Object.entries(obj || {}));
	}

	save() {
		this.raw.put(Object.fromEntries(this.m));
	}

	get(buf) {
		return this.m.get(buf) || [];
	}

	push(buf, text) {
		if (!this.m.has(buf)) {
			this.m.set(buf, []);
		}
		this.m.get(buf).unshift(text);
		this.m.get(buf).splice(5);
		this.save();
	}

	update(buf, text) {
		if (!this.m.has(buf)) {
			this.m.set(buf, []);
		}
		this.m.get(buf)[0] = text;
		this.save();
	}
}
-- 
2.35.1
Hm, this doesn't feel very react-ish... Not sure how to improve that...