TryQuiet / quiet

A private, p2p alternative to Slack and Discord built on Tor & IPFS
https://www.tryquiet.org
GNU General Public License v3.0
1.96k stars 85 forks source link

"Join/Create community" and "Username registration" screens are graying out input #1677

Closed siepra closed 10 months ago

siepra commented 1 year ago

The problem:

Right now, if our backend is not yet running, a user cannot start the process of joining or creating a community, because the fields are greyed out. This is bad because it interrupts the first time user experience and appears broken: it is very unusual for users to run into inactive fields in an onboarding flow, so users might assume brokenness.

How it should work (ideal solution)

Users should be able to start the process of joining or creating a community. We shouldn't do anything that requires the backend until they submit. Once they submit their community info (community name or invite) and choose their username, they can see a progress indicator. If the backend is not yet running, the progress indicator can say "Starting backend".

How it should work (acceptable solution)

Instead of showing inactive fields on the join/create screens, we can show our splash screen. Ideally it would include an explanatory note in this phase, e.g. "Starting backend".

Note: we should not show a splash screen while the backend is starting when when users are returning to an existing community, because this will make the app much less useful on iOS.

Notes from @siepra:

It also causes greyed-out input on chat screen and context menu dots on channel list screen to "blink" due to short time between restoring the app and initializing backend services.

In AppDelegate.m we use applicationDidEnterBackground and applicationWillEnterForeground lifecycle callbacks to notify react-native about app state changes but unfortunately the UI operations needs react-native context to be active which makes those callbacks not fully effective (changes to UI are being applied only after fully coming back to foreground which makes components "blink"). In fact, we should be using applicationWillResignActive for any UI operations but it turns out it also doesn't reach react-native.

There're two sagas responsible for marking UI ready and performing redirections: onConnectedSaga and redirectionSaga but they do well for initial redirections and are not aware of app changing it's state between active and idle

holmesworcester commented 1 year ago

@Kacper-RF is this something we can work on without @siepra and @vinkabuki ?

holmesworcester commented 1 year ago

Also, we should let users open/paste an invite link and choose a username even if the backend is not running.

"Starting backend" steps can become part of the progress indicator when users are joining a community.

Kacper-RF commented 1 year ago

Maybe some quick fix for a better UX would not be to gray out the input but to disable the submit button?

EmiM commented 1 year ago

How this task corresponds with https://github.com/TryQuiet/quiet/issues/1657? Should we have a splashscreen? Your last comment here says something about progress indicator but I think we were talking about getting rid of it?

We need designs and very precise description on how starting application should look like on each OS. This is necessary not only for dev team but also for Kinga so she could accept or reject changes.

EmiM commented 1 year ago

@holmesworcester could you write your decisions/propositions here?

holmesworcester commented 1 year ago

Okay, so I think the principles are:

  1. Users should only have to wait when absolutely necessary.
  2. Wait times should be as short as possible.
  3. Users should see informative status when they're waiting more than 5 seconds.
  4. ...Unless any of these would add an uncomfortable amount of complexity. Then we can defer it, make an issue, etc.

here's my ideal picture of how joining goes:

  1. User clicks like or opens app
  2. Join/create screens visible ASAP.
  3. Users should be able to create a community and choose a username instantly with no delays. Any CPU intensive stuff like generating keys should happen after the user picks a username. (vinkabuki just worked on this)
  4. The joining process can take a while, so we should show informative progress when we are joining a community. If backend has not started yet we can show "Starting backend" and any other helpful progress information.

Updated the issue description to reflect these. Follow the issue description!

holmesworcester commented 1 year ago

@EmiM I updated the ticket description. Can you confirm that it is sufficiently clear?

siepra commented 1 year ago

To see the full picture, let's divide our logic holding objects (sagas) into two categories:

While the first ones can be fully operative on their own, the seconds ones requires backend to be up and running. There are two conditions that has to be met in order to be able to interact with backend:

Information about the web socket client connection status can only be reached directly from within mobile/desktop package (unless someone knows the way to pass the local selector value up to state-manager).

Unfortunately, we share the vast majority of the second-type sagas in state-manager package which means it's hard to suspend its execution until backend becomes operative (for the reason I mentioned above, and also for the fact that changes made to state-manager affects mobile and desktop equally)

So all the possible solutions have their pros and cons:

  1. Block the inputs until backend's ready
  1. Use splash screen on app start
  1. Proxy state-manager action dispatches through local sagas
  1. Suspend execution of state-manager sagas until backend becomes operative

Let me know what you think about it. Also don't hesitate to share some other ideas

@holmesworcester @vinkabuki @Kacper-RF

holmesworcester commented 1 year ago

Because this issue is focused on the join/create screen, maybe the solution is to show our splash screen until these fields are available, but don't show a place screen when the user is returning to an already joined community.

Does this make sense?

So basically Option 2 above but we only block frontend when a community has not been joined.

I'm also interested in exploring option 3 because that seems required for letting users send messages immediately when the backend is still unavailable, which is desired. But I think we should learn more about iOS and tackle some other stuff before committing to this strategy.

siepra commented 1 year ago

I don't agree because it'll be coming up every time user brings the app back to frontend on iOS. So in fact joining/creating is only a small percentage of cases.

I only marked option number 2 because I wanted to mention all the possibilities, but in fact this is exactly what we've just replaced with blocking inputs (to speed up frontend).

Also I think showing splash screen conditionally in those circumstances will be messy and won't actually improve anything.

holmesworcester commented 1 year ago

Why will showing the splash screen conditionally on join/create screens not improve anything? It seems to be a solution to the problem, i.e. "inactive fields are unexpected and seem broken, so don't show inactive fields".

The way I picture doing it is, add something to the frontend so that instead of showing inactive fields we show something that looks just like our splash screen.

Anyway, a solution to this ticket simply requires not showing grey / inactive fields to the user on the join/create screens without making any other parts of the app worse.

siepra commented 1 year ago

RE: Anyway, a solution to this ticket simply requires not showing grey / inactive fields to the user on the join/create screens without making any other parts of the app worse.

It doesn't really correspond to what you've said earlier:

That's why I claim splash screen won't actually improve anything. It'd only be an alternative way of doing what we already do.

holmesworcester commented 1 year ago

I'm going by the title of the ticket.

The description is a proposed and better solution, which you're saying is hard or messy. If that solution is hard or messy, other solutions are okay too.

It's very unusual for an app to have temporarily inactive fields in the onboarding screens, so users will see brokenness. It's not unusual to have splash screens, so that solves the brokenness, which is the main issue.

Also I don't really get why it's undesirable complexity to hold invite link and username as variables in the frontend and submit them to the backend once it's alive. To me it sounds like something that shouldn't be a hard change, and if it is I'm curious why and if there's something we can do to make changes like this easier. Either solution is good for me.

siepra commented 1 year ago

RE: Also I don't really get why it's undesirable complexity to hold invite link and username as variables in the frontend and submit them to the backend once it's alive

It is not. The challenging part is to distinguish when to submit data to backend from state-manager. Once again read carefully what I posted in the comment above or look into the code.

RE: I'm going by the title of the ticket.

Ok, then I'll replace blocking inputs with splash screen. Although I'd change the How it should work: subtitle. Otherwise Kinga will be confused while doing QA session on this task.

holmesworcester commented 1 year ago

Sure, I can change the description.

From your description of the challenge involved in knowing when we can submit to backend, the part that is not clear to me is why the frontend can't retry submission every second until it gets a response, or some other blind strategy that does not require knowing.

holmesworcester commented 1 year ago

Description updated! Looks good? @siepra

siepra commented 1 year ago

RE: why the frontend can't retry submission every second until it gets a response,

That's because it's not frontend (mobile/desktop) but state-manager that actually talks to the backend. Here is the sample code of what it should look like to work like you said (and like I would prefer it to work):

// packages/state-manager

export function emitEvent() {
    while (true) {
        const ready = yield* select(initSelectors.ready) // web socket client is conneted (&other conditions)
        if (ready) {
           yield* call(socket, socket.emit)
           break
       }
    }
}

The problem is ready is not reachable within state-manager. We would be able to wrap it into yet another frontend saga:

// packages/mobile

import { emitEvent } from '@quiet/state-manager'

while (true) {
    const ready = yield* select(initSelectors.ready)
    if (ready) {
        yield* call(emitEvent)
    }
}

Although it means we'd be calling local sagas instead of state-manager directly which would be different from what we do in desktop's containers, and the only responsibility of that sagas would be to suspend event emitting (messy)

siepra commented 1 year ago

I based the view on the following design https://www.figma.com/file/y8h6w8PYR9jyI3zjYHL9Cl/Mobile-%2B-desktop-%2B-prototypes?type=design&node-id=1427-34705&mode=design&t=PSjrUfLPLrGOoVLP-4 Although currently our screens are not 1:1 with Jason's designs (which were made later) so I decided to keep it center-oriented until we catch up with the other components.

https://github.com/TryQuiet/quiet/assets/15381135/bde51631-7185-4bd0-8dd1-d1a6242e2481

holmesworcester commented 1 year ago

Looks great!

It might be worth adding a real splash screen image to replace the 'powered by react native' screen right now, just to make sure the transition between both screens feels nice.

There's a separate issue for it but it might make sense to tackle now.