mirror of
https://git.sr.ht/~emersion/gamja
synced 2024-11-14 19:25:26 -05:00
Add a settings dialog
Add an option to hide chat events or always expand them. Closes: https://todo.sr.ht/~emersion/gamja/73
This commit is contained in:
parent
e3c2d85a94
commit
baaf576d82
7 changed files with 143 additions and 20 deletions
|
@ -11,12 +11,13 @@ import NetworkForm from "./network-form.js";
|
|||
import AuthForm from "./auth-form.js";
|
||||
import RegisterForm from "./register-form.js";
|
||||
import VerifyForm from "./verify-form.js";
|
||||
import SettingsForm from "./settings-form.js";
|
||||
import Composer from "./composer.js";
|
||||
import ScrollManager from "./scroll-manager.js";
|
||||
import Dialog from "./dialog.js";
|
||||
import { html, Component, createRef } from "../lib/index.js";
|
||||
import { strip as stripANSI } from "../lib/ansi.js";
|
||||
import { SERVER_BUFFER, BufferType, ReceiptType, ServerStatus, Unread, State, getServerName, receiptFromMessage, isReceiptBefore, isMessageBeforeReceipt } from "../state.js";
|
||||
import { SERVER_BUFFER, BufferType, ReceiptType, ServerStatus, Unread, BufferEventsDisplayMode, State, getServerName, receiptFromMessage, isReceiptBefore, isMessageBeforeReceipt } from "../state.js";
|
||||
import commands from "../commands.js";
|
||||
import { setup as setupKeybindings } from "../keybindings.js";
|
||||
import * as store from "../store.js";
|
||||
|
@ -221,6 +222,13 @@ export default class App extends Component {
|
|||
this.handleRegisterSubmit = this.handleRegisterSubmit.bind(this);
|
||||
this.handleVerifyClick = this.handleVerifyClick.bind(this);
|
||||
this.handleVerifySubmit = this.handleVerifySubmit.bind(this);
|
||||
this.handleOpenSettingsClick = this.handleOpenSettingsClick.bind(this);
|
||||
this.handleSettingsChange = this.handleSettingsChange.bind(this);
|
||||
|
||||
this.state.settings = {
|
||||
...this.state.settings,
|
||||
...store.settings.load(),
|
||||
};
|
||||
|
||||
this.bufferStore = new store.Buffer();
|
||||
|
||||
|
@ -632,7 +640,10 @@ export default class App extends Component {
|
|||
});
|
||||
this.setState({ connectParams: params });
|
||||
|
||||
let client = new Client(fillConnectParams(params));
|
||||
let client = new Client({
|
||||
...fillConnectParams(params),
|
||||
eventPlayback: this.state.settings.bufferEvents !== BufferEventsDisplayMode.HIDE,
|
||||
});
|
||||
client.debug = this.debug;
|
||||
|
||||
this.clients.set(serverID, client);
|
||||
|
@ -1321,6 +1332,10 @@ export default class App extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
disconnectAll() {
|
||||
this.close(this.state.buffers.keys().next().value);
|
||||
}
|
||||
|
||||
executeCommand(s) {
|
||||
let parts = s.split(" ");
|
||||
let name = parts[0].toLowerCase().slice(1);
|
||||
|
@ -1681,6 +1696,15 @@ export default class App extends Component {
|
|||
this.dismissDialog();
|
||||
}
|
||||
|
||||
handleOpenSettingsClick() {
|
||||
this.openDialog("settings");
|
||||
}
|
||||
|
||||
handleSettingsChange(settings) {
|
||||
store.settings.put(settings);
|
||||
this.setState({ settings });
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.baseTitle = document.title;
|
||||
setupKeybindings(this);
|
||||
|
@ -1742,6 +1766,7 @@ export default class App extends Component {
|
|||
onReconnect=${() => this.reconnect()}
|
||||
onAddNetwork=${this.handleAddNetworkClick}
|
||||
onManageNetwork=${() => this.handleManageNetworkClick(activeBuffer.server)}
|
||||
onOpenSettings=${this.handleOpenSettingsClick}
|
||||
/>
|
||||
</section>
|
||||
`;
|
||||
|
@ -1850,6 +1875,18 @@ export default class App extends Component {
|
|||
</>
|
||||
`;
|
||||
break;
|
||||
case "settings":
|
||||
dialog = html`
|
||||
<${Dialog} title="Settings" onDismiss=${this.dismissDialog}>
|
||||
<${SettingsForm}
|
||||
settings=${this.state.settings}
|
||||
onChange=${this.handleSettingsChange}
|
||||
onDisconnect=${() => this.disconnectAll()}
|
||||
onClose=${() => this.dismissDialog()}
|
||||
/>
|
||||
</>
|
||||
`;
|
||||
break;
|
||||
}
|
||||
|
||||
let error = null;
|
||||
|
@ -1906,6 +1943,7 @@ export default class App extends Component {
|
|||
buffer=${activeBuffer}
|
||||
server=${activeServer}
|
||||
bouncerNetwork=${activeBouncerNetwork}
|
||||
settings=${this.state.settings}
|
||||
onChannelClick=${this.handleChannelClick}
|
||||
onNickClick=${this.handleNickClick}
|
||||
onAuthClick=${() => this.handleAuthClick(activeBuffer.server)}
|
||||
|
|
|
@ -74,6 +74,12 @@ export default function BufferHeader(props) {
|
|||
onClick=${props.onReconnect}
|
||||
>Reconnect</button>
|
||||
`;
|
||||
let settingsButton = html`
|
||||
<button
|
||||
key="settings"
|
||||
onClick="${props.onOpenSettings}"
|
||||
>Settings</button>
|
||||
`;
|
||||
|
||||
if (props.server.isBouncer) {
|
||||
if (props.server.bouncerNetID) {
|
||||
|
@ -99,27 +105,16 @@ export default function BufferHeader(props) {
|
|||
} else if (props.server.status === ServerStatus.DISCONNECTED) {
|
||||
actions.push(reconnectButton);
|
||||
}
|
||||
actions.push(html`
|
||||
<button
|
||||
key="disconnect"
|
||||
class="danger"
|
||||
onClick=${props.onClose}
|
||||
>Disconnect</button>
|
||||
`);
|
||||
actions.push(settingsButton);
|
||||
}
|
||||
} else {
|
||||
if (fullyConnected) {
|
||||
actions.push(joinButton);
|
||||
actions.push(settingsButton);
|
||||
} else if (props.server.status === ServerStatus.DISCONNECTED) {
|
||||
actions.push(reconnectButton);
|
||||
}
|
||||
actions.push(html`
|
||||
<button
|
||||
key="disconnect"
|
||||
class="danger"
|
||||
onClick=${props.onClose}
|
||||
>Disconnect</button>
|
||||
`);
|
||||
actions.push(settingsButton);
|
||||
}
|
||||
break;
|
||||
case BufferType.CHANNEL:
|
||||
|
|
|
@ -2,7 +2,7 @@ import { html, Component } from "../lib/index.js";
|
|||
import linkify from "../lib/linkify.js";
|
||||
import * as irc from "../lib/irc.js";
|
||||
import { strip as stripANSI } from "../lib/ansi.js";
|
||||
import { BufferType, ServerStatus, getNickURL, getChannelURL, getMessageURL, isMessageBeforeReceipt } from "../state.js";
|
||||
import { BufferType, ServerStatus, BufferEventsDisplayMode, getNickURL, getChannelURL, getMessageURL, isMessageBeforeReceipt } from "../state.js";
|
||||
import * as store from "../store.js";
|
||||
import Membership from "./membership.js";
|
||||
|
||||
|
@ -546,7 +546,8 @@ function sameDate(d1, d2) {
|
|||
|
||||
export default class Buffer extends Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return this.props.buffer !== nextProps.buffer;
|
||||
return this.props.buffer !== nextProps.buffer ||
|
||||
this.props.settings !== nextProps.settings;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -557,6 +558,7 @@ export default class Buffer extends Component {
|
|||
|
||||
let server = this.props.server;
|
||||
let bouncerNetwork = this.props.bouncerNetwork;
|
||||
let settings = this.props.settings;
|
||||
let serverName = server.name;
|
||||
|
||||
let children = [];
|
||||
|
@ -633,6 +635,10 @@ export default class Buffer extends Component {
|
|||
buf.messages.forEach((msg) => {
|
||||
let sep = [];
|
||||
|
||||
if (settings.bufferEvents === BufferEventsDisplayMode.HIDE && canFoldMessage(msg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasUnreadSeparator && buf.type != BufferType.SERVER && !isMessageBeforeReceipt(msg, buf.prevReadReceipt)) {
|
||||
sep.push(html`<${UnreadSeparator} key="unread"/>`);
|
||||
hasUnreadSeparator = true;
|
||||
|
@ -651,7 +657,7 @@ export default class Buffer extends Component {
|
|||
}
|
||||
|
||||
// TODO: consider checking the time difference too
|
||||
if (canFoldMessage(msg)) {
|
||||
if (settings.bufferEvents === BufferEventsDisplayMode.FOLD && canFoldMessage(msg)) {
|
||||
foldMessages.push(msg);
|
||||
return;
|
||||
}
|
||||
|
|
71
components/settings-form.js
Normal file
71
components/settings-form.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
import { html, Component } from "../lib/index.js";
|
||||
|
||||
export default class SettingsForm extends Component {
|
||||
state = {};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state.bufferEvents = props.settings.bufferEvents;
|
||||
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
handleChange(event) {
|
||||
let target = event.target;
|
||||
let value = target.type == "checkbox" ? target.checked : target.value;
|
||||
this.setState({ [target.name]: value }, () => {
|
||||
this.props.onChange(this.state);
|
||||
});
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<form onChange=${this.handleChange} onSubmit=${this.handleSubmit}>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="bufferEvents"
|
||||
value="fold"
|
||||
checked=${this.state.bufferEvents === "fold"}
|
||||
/>
|
||||
Show and fold chat events
|
||||
</label>
|
||||
<br/>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="bufferEvents"
|
||||
value="expand"
|
||||
checked=${this.state.bufferEvents === "expand"}
|
||||
/>
|
||||
Show and expand chat events
|
||||
</label>
|
||||
<br/>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="bufferEvents"
|
||||
value="hide"
|
||||
checked=${this.state.bufferEvents === "hide"}
|
||||
/>
|
||||
Hide chat events
|
||||
</label>
|
||||
<br/><br/>
|
||||
|
||||
<button type="button" class="danger" onClick=${() => this.props.onDisconnect()}>
|
||||
Disconnect
|
||||
</button>
|
||||
<button>
|
||||
Close
|
||||
</button>
|
||||
</form>
|
||||
`;
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ const permanentCaps = [
|
|||
|
||||
"draft/account-registration",
|
||||
"draft/chathistory",
|
||||
"draft/event-playback",
|
||||
"draft/extended-monitor",
|
||||
|
||||
"soju.im/bouncer-networks",
|
||||
|
@ -124,6 +123,7 @@ export default class Client extends EventTarget {
|
|||
saslExternal: false,
|
||||
bouncerNetwork: null,
|
||||
ping: 0,
|
||||
eventPlayback: true,
|
||||
};
|
||||
debug = false;
|
||||
batches = new Map();
|
||||
|
@ -624,6 +624,9 @@ export default class Client extends EventTarget {
|
|||
if (!this.params.bouncerNetwork) {
|
||||
wantCaps.push("soju.im/bouncer-networks-notify");
|
||||
}
|
||||
if (this.params.eventPlayback) {
|
||||
wantCaps.push("draft/event-playback");
|
||||
}
|
||||
|
||||
let msg = this.caps.requestAvailable(wantCaps);
|
||||
if (msg) {
|
||||
|
|
9
state.js
9
state.js
|
@ -34,6 +34,12 @@ export const ReceiptType = {
|
|||
READ: "read",
|
||||
};
|
||||
|
||||
export const BufferEventsDisplayMode = {
|
||||
FOLD: "fold",
|
||||
EXPAND: "expand",
|
||||
HIDE: "hide",
|
||||
};
|
||||
|
||||
export function getNickURL(nick) {
|
||||
return "irc:///" + encodeURIComponent(nick) + ",isuser";
|
||||
}
|
||||
|
@ -209,6 +215,9 @@ export const State = {
|
|||
buffers: new Map(),
|
||||
activeBuffer: null,
|
||||
bouncerNetworks: new Map(),
|
||||
settings: {
|
||||
bufferEvents: BufferEventsDisplayMode.FOLD,
|
||||
},
|
||||
};
|
||||
},
|
||||
updateServer(state, id, updater) {
|
||||
|
|
1
store.js
1
store.js
|
@ -26,6 +26,7 @@ class Item {
|
|||
|
||||
export const autoconnect = new Item("autoconnect");
|
||||
export const naggedProtocolHandler = new Item("naggedProtocolHandler");
|
||||
export const settings = new Item("settings");
|
||||
|
||||
function debounce(f, delay) {
|
||||
let timeout = null;
|
||||
|
|
Loading…
Reference in a new issue