| Commit message (Collapse) | Author | Age |
| |
|
|
|
|
| |
The styles for the `MessageInput` and `CreateConversationForm` components assume that the container div will be present, and the components will not render as intended without them. Therefore: they are "part of" the component in most of the ways that matter, not part of the context in which the component is used. Moving the divs into the component will make it easier to reuse these components (for example, in swatches).
The diff for this looks worse than it is because of indentation changes.
|
| |
|
|
| |
Existing client state, stored in local storage, is migrated to new keys (that mention "conversation" instead of "channel" where appropriate) the first time the client loads.
|
| | |
|
| |
|
|
| |
This is a **breaking change** for essentially all clients. Thankfully, there's presently just the one, so we don't need to go to much effort to accommoate that; the client is modified in this commit to adapt, users can reload their client, and life will go on.
|
| | |
|
| |
|
|
|
|
| |
vanished.
This may happen if the user has a link to a channel open when the channel is deleted/expires, or if they return to the app after the last channel they looked at has expired.
|
| |
|
|
| |
`session`.
|
| |
|
|
| |
channel's creation time.
|
| |
|
|
| |
been read.
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
| |
There is a subtle race conditon in this code, which is likely not fixable without a protocol change:
* Ghost messages can disappear before their "real" message replacement shows up, if the client finishes sending (i.e., receives an HTTP response on the POST) before the server delivers the real message.
* Ghost messages can be duplicated briefly, if the client receives the real message before the client finishes sending.
Both happen in practice; we make no ordering guarantees between requests.
To aviod this, we'd to give clients a way to correlate pending sends with received messages. This would require fundamentally the same capabilities, like per-operation nonces, that preventing duplicate operations will require.
|
| |
|
|
|
|
| |
A `MessageRun` is a visual container with a specific layout - bordered, with a drop shadow, with a name badge on the top-left, which is either positioned to the left (`other-message`) or right (`own-message`). It is content-agnostic.
This facilitates putting things besides live messages inside of a message run. As a side effect, this gets rid of ActiveChannel; most of what it was doing makes more sense living in the channel view's `+page.svelte`.
|
| |
|
|
|
|
|
|
|
|
| |
A handful of operations are "synchronized" - that is, the server sends back information about them when the client asks to perform them, but notifies _all_ clients of completion through the event stream. As of this writing, these operations include sending and deleting messages, creating and deleting channels, and anything that creates new users.
We can use the outbox for most of these. I've opted _not_ to use the outbox for creating users, as that often takes place when the client is not connected to the event stream (and can't be) and so cannot discover that the operation has been completed after it is sent.
Outboxed tasks are objects, not closures, even though they behave in closure-like ways (`send()` carries out what amounts to "attempt this operation until it succeeds"). This is deliberate; I want the properties of incomplete tasks to be inspectable down the line, so that we can put them in the UI. If they're mere closures, we can't do that.
It is deliberate that `outbox.postToChannel` et al do not return promises. I contemplated it, but it will interact weirdly with outbox serialization, when we get to that. Rather than relying on a promise to determine when an operation has completed, clients of `Outbox` should monitor its properties. (We can add additional view properties to make that easier.)
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is in lieu of saving the outbox. I tried that, and:
* If messages are dropped from the saved outbox before calling `api.postToChannel`, then messages "in flight" are lost when the page is reloaded unless the send succeeds after the client vanishes, as they are not re-sent when the page loads.
* If messages are dropped from the saved outbox after calling `api.postToChannel`, then messages "in flight" are duplicated when the page is reloaded and they get re-sent.
The CAP theorem is real and can hurt you.
The appropriate compensating mechanism would be a client-generated per-operation nonce, with server-side support for replaying responses by nonce if an operation already completed. That's a pretty big undertaking, and it's one we should probably do, but it's larger than I want to take on right now. Instead, we warn the user, and they can make their own decision.
Except we don't, sometimes.
When the client runs in a browser, this event handler prompts the user for confirmation before reloading, navigating away, closing the tab, or quitting. When run in a Safari app container, though, it only warns before reloading. Closing the window or quitting the app do not provoke a prompt. The warning is "best" effort. The failure mode is lost messages, which isn't particularly best.
|
| |
|
|
|
|
|
|
| |
directly from the UI.
This primarily serves to free up the message input immediately, so that the user can start drafting their next message right away. The wait while a message is being sent is actively disruptive when using Pilcrow on a server with noticable latency (hi.grimoire.ca has around 700ms), and this largely alleviates it.
Unsent messages can be lost if the client is closed or deactivated before they make it to the head of the queue.
|
| |
|
|
| |
This was always intended, but it wasn't working that way because `channelsMeta.get(id)?.lastReadAt` evaluates to `undefined`, not `null`. Strict equality (`===`) treats those as distinct values.
|
| |
|
|
|
|
|
|
|
|
|
| |
Sorry about the thousand-line omnibus change; this is functionally a rewrite of the client's state tracking, flavoured to resemble the existing code as far as is possible, rather than something that can be parted out and committed in pieces.
Highlights:
* No more `store.writeable()`s. All state is now tracked using state runs or derivatives. State is still largely structured the way it was, but several bits of nested state have been rewritten to ensure that their properties are reactive just as much as their containers are.
* State is no longer global. `(app)/+layout` manages a stateful session, created via its load hook and started/stopped via component mount and destroy events. The session also tracks an event source for the current state, and feeds events into the state, broadly along the same lines as the previous stores-based approach.
Together these two changes fix up several rough spots integrating state with Svelte, and allow for the possibility of multiple states. This is a major step towards restartable states, and thus towards better connection management, which will require the ability to "start over" once a connection is restored.
|
| | |
|
| |\ |
|
| | | |
|
| | | |
|
| | | |
|
| | |\ |
|
| | | |
| | |
| | |
| | |
| | |
| | | |
the DOM.
Prevents this from breaking during DOM unmounting, when leaving a channel.
|
| | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
This is stored locally, and, while parallel to channel info, is not the
same as.
Eventually, this may hold info about moot/decayed channels, and grow
unbounded. That'll need to be addressed.
|
| | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
When we hit the boot endpoint, we get the server's view of things. If we
just setChannels with that, we overwrite all our locally-stored info in
on things like lastReadAt. So we need to merge data.
Eventually, this might militate for a `meta` key containing an object of
locally stored data, rather than having to handle each key specially.
|
| | |/
|/|
| |
| |
| |
| |
| |
| | |
Access to "global" (maybe "external?") state is now handled at the top level of the component hierarchy, in `+page.svelte`, `+layout.svelte`, and their associated scripts. State is otherwise passed down through props, and changes are passed up through callbacks.
This is - hopefully - groundwork for refactoring state management a bit. I wanted to move access to state out to a smaller number of places, so that I have fewer places to update to implement reconnect logic. My broader goal is to make it easier to refactor these kinds of external side effects, as well, though no such changes are in this branch.
This change also makes testing a mile easier, since tests can interact with props and callbacks instead of emulating the whole HTTP request stack and the Pilcrow API. This change removes do-very-little tests.
|
| | | |
|
| | | |
|
| | | |
|
| | | |
|
| |/
|
|
|
|
|
|
|
|
|
|
|
| |
list.
This fixes a bug. To reproduce:
1. Open the client and log in.
2. Create a new channel using the `create channel` UI.
The expected result - and the behaviour after this commit - is that the newly-created channel will be shown in the sidebar immediately. The buggy behaviour is that it was not, but would appear in the sidebar once the client is reloaded. The channel would also not appear for other clients until they reloaded.
I'm not actually completely sure of _why_ this fixes the bug, but it does.
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| |\ |
|
| | |
| |
| |
| |
| |
| |
| | |
Esc key, 2 second wait after scroll, and whenever the messages inChannel
change. Kinda gross set of things, but, so it goes. This does offer us
the option of extending this to include "when you click 'unread from
here' on a message" in future.
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
This requires both the channels and messages stores to be available, so
we do it in the page file where we've got those stores handy. It's a bit
inefficient (it recreates the entire enrichedChannels array every time
one of them changes), but it'll do for now.
There's also a TODO hinting that we might want an idea of unread-count.
I think that would require a distinct idea of mentions, because we just
want "fact of unread" for general-availability channels, not "this many
messages since you were last here".
|
| |\| |
|
| | | |
|
| | | |
|
| | |
| |
| |
| | |
If you're setting it to a static value, use set.
|
| | | |
|
| | |
| |
| |
| |
| | |
Hide and show channel menu thus. It doesn't gradually pull it out, which
is less than ideal, but it's good enough for now.
|
| | |\ |
|
| | |\ \ |
|
| | | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | | |
This includes a change to tools/run that exposes the dev server on my
local network. This change should not make it into the final form of
this branch.
This is so I can use actual for real touch events on my actual for real
phone, hooked up for remote debugging to my computer so I can see
console events etc.
|