Activity Feeds V3 is in closed alpha — do not use it in production (just yet).

State Layer

The @stream-io/feeds-client offers a reactive state store which lets you subscribe to relevant changes. This mechanism makes it very easy to know when the UI should be updated.

Client state

The client state stores the connected user.

import { FeedsClient } from "@stream-io/feeds-client";

const client = new FeedsClient("<API key>");
await client.connectUser({ id: "john" }, "<user token>");

// Subscribe to changes
const unsubscribe = client.state.subscribe((state) => {
  // It will return undefined if there is no connected user
  console.log(state.connectedUser);
});

// Unsibscribe when you no longer need/want to receive updates
unsubscribe();

// Read current state
client.state.getLatestValue().connectedUser;

Feed state

The feed state contains all information related to a feed (except for poll data, which is stored in poll state).

You can initialize feeds in two ways:

const feed = client.feed("user", "sara");
// Intialize feed state (will create feed, if it doesn't exist yet)
// Provide the watch flag to receive state updates via WebSocket events
await feed.getOrCreate({ watch: true });

// Query multiple feeds using a filter
const feeds = client.queryFeeds({
  filter: {
    // Your query
  },
  // Provide the watch flag to receive state updates via WebSocket events
  watch: true,
});

Feed state contains the following informations:

// Metadata of the feed
created_at?: Date;
description?: string;
fid?: string;
follower_count?: number;
following_count?: number;
group_id?: string;
id?: string;
member_count?: number;
name?: string;
updated_at?: Date;
created_by?: UserResponse;
deleted_at?: Date;
visibility?: string;
custom?: Record<string, any>;
own_capabilities?: FeedOwnCapability[];

// Feed content
activities?: ActivityResponse[];
aggregated_activities?: AggregatedActivityResponse[];
pinned_activities?: ActivityPinResponse[];
// Comments stored by activity/parent id
comments_by_entity_id?: Record<string, { comments: CommentsResponse[] }>

// Follows
followers?: FollowResponse[];
following?: FollowResponse[];
own_follows?: FollowResponse[]; // Do I follow this feed?

// Notification status
notification_status?: NotificationStatusResponse;

// Feed members
members?: FeedMemberResponse[];
own_membership?: FeedMemberResponse;

// Loading indicators
is_loading: boolean; // true when state initialization is in progress
is_loading_activities: boolean; // true when loading acitivities
followers_pagination: LoadingStates;
following_pagination: LoadingStates;

Once you have a feed instance, you can observe the state:

const unsubscribe = feed.state.subscribe((state) => {
  // Called everytime the state changes
  console.log(state);
});

// or if you only want to observe part of the state
const unsubscribe = feed.state.subscribeWithSelector(
  (state) => ({
    activities: state.activities,
  }),
  (state, prevState) => {
    console.log(state.activities, prevState.activities);
  },
);

// Unsibscribe when you no longer need/want to receive updates
unsubscribe();

// Current state
console.log(feed.state.getLatestValue());

Poll state

Polls can exist with or without being attached to an activity.

One way to get a poll object is to create or query polls:

// Create a poll
const response = await client.createPoll({
  name: "Where should we host our next company event?",
  options: [{ text: "Amsterdam, The Netherlands" }, { text: "Boulder, CO" }],
});

// Or query polls
const response = await client.queryPolls({
  filter: {
    is_closed: true,
  },
  sort: [{ field: "created_at", direction: -1 }],
});

Or you’ll get a poll object by reading activities:

await feed.getOrCreate();

const poll = feed.state.getLatestValue().activities?.[0].poll;

If a poll is attached to an activity, you’ll get voting updates for that poll, if you’re watching the feed.

To receive these state updates you can use the pollFromState method:

await feed.getOrCreate();

// pollResponse object won't receive state updates
const pollResponse = feed.state.getLatestValue().activities?.[0].poll;

// poll object has a state store which can notify about state changes
const poll = client.pollFromState(pollResponse.id);

Once you have a poll instance, you can subscribe to changes:

const poll = client.pollFromState("<poll id>");

const unsubscribe = poll.state.subscribe((state) => {
  // Called everytime the state changes
  console.log(state);
});

// or if you only want to observe part of the state
const unsubscribe = poll.state.subscribeWithSelector(
  (state) => ({
    ownAnswer: state.ownAnswer,
  }),
  (state, prevState) => {
    console.log(state.ownAnswer, prevState.ownAnswer);
  },
);

// Unsibscribe when you no longer need/want to receive updates
unsubscribe();

// Current state
console.log(poll.state.getLatestValue());

Poll state contains the following information:

allow_answers: boolean;
allow_user_suggested_options: boolean;
answers_count: number;
created_at: Date;
created_by_id: string;
description: string;
enforce_unique_vote: boolean;
name: string;
updated_at: Date;
vote_count: number;
latest_answers: PollVote[];
options: PollOption[];
custom: Record<string, any>;
latest_votes_by_option: Record<string, PollVote[]>;
vote_counts_by_option: Record<string, number>;
is_closed?: boolean;
max_votes_allowed?: number;
voting_visibility?: string;
created_by?: User;

// Enriched values
lastActivityAt: Date;
maxVotedOptionIds: OptionId[];
ownVotesByOptionId: Record<OptionId, PollVote>;
ownAnswer?: PollVote; // each user can have only one answer
© Getstream.io, Inc. All Rights Reserved.