Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev UX Bug · Knowing how many unread messages there are #146

Open
charltongroves opened this issue Feb 24, 2020 · 2 comments
Open

Dev UX Bug · Knowing how many unread messages there are #146

charltongroves opened this issue Feb 24, 2020 · 2 comments
Assignees
Labels
bug

Comments

@charltongroves
Copy link

@charltongroves charltongroves commented Feb 24, 2020

Describe the bug
I have had to jump through an insane amount of hoops to get a simple 'unread notificaiton' badge count working in my app.

Here is all the code i've had to write.

(promise in this case is the promise that's returned from 'setUser' which i need to be resolved in order to figure out the initial 'total_unread_count')

type ChatBadgeProps = {|
  chatClient: ?{ client: ClientType, promise: Promise<mixed> },
|};

let globalUnreadTracker = null;
let globalUnreadListener = null;

function ChatBadge(props: ChatBadgeProps) {
  const [unreadChannels, setUnreadChannels] = React.useState(null);

  // This initialises the unread count with what our client returns on initialisation
  // If this component has been mounted previously, we will use the unread count kept in our
  // globalUnreadTracker. This is necessary as the client annoyingly doesnt keep a correct state of unread count,
  // so we have to do it ourselves.
  React.useEffect(() => {
    if (props.chatClient) {
      const refinedClient = props.chatClient;
      refinedClient.promise.then(() => {
        const newCount = refinedClient.client.user.total_unread_count || 0;
        const refined =
          globalUnreadTracker == null ? newCount : globalUnreadTracker;
        setUnreadChannels(refined);
        globalUnreadTracker = refined;
      });
    }
  }, [props.chatClient]);

  // This sets up a local and global listener to listen for changes to our unread count/
  // The local one exists so we can setState as changes come through
  // The global one exists so we can track changes that happened while this component is unmounted.
  // This means if we read a bunch of messages, we can record those unread_count changes, and then display
  // the correct value when we mount this component again.
  React.useEffect(() => {
    if (props.chatClient) {
      const callbackFunc = (event) => {
        if (event.total_unread_count !== undefined) {
          setUnreadChannels(event.total_unread_count);
          globalUnreadTracker = event.total_unread_count;
        }
      };
      const refinedClient = props.chatClient;
      refinedClient.client.on(callbackFunc);
      if (globalUnreadListener == null) {
        const globalCallbackFunc = (event) => {
          if (event.total_unread_count !== undefined) {
            globalUnreadTracker = event.total_unread_count;
          }
        };
        globalUnreadListener = globalCallbackFunc;
        refinedClient.client.on(globalUnreadListener);
      }
      return () => {
        refinedClient.client.off(callbackFunc);
      };
    }
    return () => {};
  }, [props.chatClient]);
  if (props.chatClient == null) {
    return null;
  }

  const total = unreadChannels || 0;
  if (total > 0) {
    return <Badge color={color.BUBBLE_URGENT} count={total} />;
  }
  return null;
}

A bit of an explanation as to whats going on:
Currently there is no way to reliably query how many unread messages you have. You essentially have to wait until messages come through to see the latest 'count'. The problem is, when we unmount a component, that component cannot 'listen' for changes anymore, so when it gets remounted, it will have no idea how many unread messages there are (client.user.total_unread_count is only accurate immediately after setUser is called, and then is no longer reliable). The only way i could solve this is attaching a global listener to client that operates even when the badge component is unmounted. This 'attempts' to keep track of the unread count but can fail due to a few reasons. Eg, if you minimise the app, the app garbage collection can mean that the listener will stop firing. So when you reopen the app, you will either have to refire 'setUser' to get the latest count, or wait until an event fires to retrigger the listener.

Anyway, its very messy. Ideally the 'client' object would have a reliable up-to-date property that shows the actual unread count. That way you could just set up a timer that checks that value every 500ms and rerenders if it changes.

Dev environment info (please complete/provide the following information):

  • Package version: ^0.6.0
  • react-native or expo version
  • Device/Emulator (android/ios) and OS version

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Additional context
Add any other context about the problem here.

Screenshots
If applicable, add screenshots to help explain your problem.

@familywiser1
Copy link

@familywiser1 familywiser1 commented Feb 25, 2020

It's easy to get count unread messages. You could do something like this - https://getstream.io/chat/docs/unread/?language=js
register the event during the componentDidUpdate and unregister (client.off) the event during componentWillUnmount

Also if you are using expo - you can also show count of unread message on your app icon -

client.on((event) => {
     if (event.total_unread_count !== undefined) {
         console.log(event.total_unread_count);
  Notifications.setBadgeNumberAsync(total_unread_count);
     }

     if (event.unread_channels !== undefined) {
         console.log(event.unread_channels);
     }
});
@charltongroves
Copy link
Author

@charltongroves charltongroves commented Mar 10, 2020

@familywiser1
I'm aware of that, it's not perfect though because if you re-mount, you'll need to wait until an event fires in order to get the latest unread message count, until that happens you will be showing whatever the unread message count was when you first fired 'setUser'.

I did exactly what you described in the code i attached, but i had to add a ton of workarounds to make it actually keep up to date, thats the entire point of this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.