Merge branch 'fix-ws-follow' into 'develop'

Fix streaming follow update

Closes #1469

See merge request soapbox-pub/soapbox!2627
This commit is contained in:
Alex Gleason 2023-07-22 21:57:59 +00:00
commit 3844086083
4 changed files with 59 additions and 52 deletions

View File

@ -1,4 +1,7 @@
import { getLocale, getSettings } from 'soapbox/actions/settings';
import { importEntities } from 'soapbox/entity-store/actions';
import { Entities } from 'soapbox/entity-store/entities';
import { selectEntity } from 'soapbox/entity-store/selectors';
import messages from 'soapbox/locales/messages';
import { ChatKeys, IChat, isLastMessage } from 'soapbox/queries/chats';
import { queryClient } from 'soapbox/queries/client';
@ -26,21 +29,11 @@ import {
} from './timelines';
import type { IStatContext } from 'soapbox/contexts/stat-context';
import type { Relationship } from 'soapbox/schemas';
import type { AppDispatch, RootState } from 'soapbox/store';
import type { APIEntity, Chat } from 'soapbox/types/entities';
const STREAMING_CHAT_UPDATE = 'STREAMING_CHAT_UPDATE';
const STREAMING_FOLLOW_RELATIONSHIPS_UPDATE = 'STREAMING_FOLLOW_RELATIONSHIPS_UPDATE';
const updateFollowRelationships = (relationships: APIEntity) =>
(dispatch: AppDispatch, getState: () => RootState) => {
const me = getState().me;
return dispatch({
type: STREAMING_FOLLOW_RELATIONSHIPS_UPDATE,
me,
...relationships,
});
};
const removeChatMessage = (payload: string) => {
const data = JSON.parse(payload);
@ -190,9 +183,52 @@ const connectTimelineStream = (
};
});
function followStateToRelationship(followState: string) {
switch (followState) {
case 'follow_pending':
return { following: false, requested: true };
case 'follow_accept':
return { following: true, requested: false };
case 'follow_reject':
return { following: false, requested: false };
default:
return {};
}
}
interface FollowUpdate {
state: 'follow_pending' | 'follow_accept' | 'follow_reject'
follower: {
id: string
follower_count: number
following_count: number
}
following: {
id: string
follower_count: number
following_count: number
}
}
function updateFollowRelationships(update: FollowUpdate) {
return (dispatch: AppDispatch, getState: () => RootState) => {
const me = getState().me;
const relationship = selectEntity<Relationship>(getState(), Entities.RELATIONSHIPS, update.following.id);
if (update.follower.id === me && relationship) {
const updated = {
...relationship,
...followStateToRelationship(update.state),
};
// Add a small delay to deal with API race conditions.
setTimeout(() => dispatch(importEntities([updated], Entities.RELATIONSHIPS)), 300);
}
};
}
export {
STREAMING_CHAT_UPDATE,
STREAMING_FOLLOW_RELATIONSHIPS_UPDATE,
connectTimelineStream,
type TimelineStreamOpts,
};

View File

@ -5,6 +5,7 @@ import z from 'zod';
import { useAppDispatch, useAppSelector, useLoading } from 'soapbox/hooks';
import { importEntities } from '../actions';
import { selectEntity } from '../selectors';
import type { Entity } from '../types';
import type { EntitySchema, EntityPath, EntityFn } from './types';
@ -34,7 +35,7 @@ function useEntity<TEntity extends Entity>(
const defaultSchema = z.custom<TEntity>();
const schema = opts.schema || defaultSchema;
const entity = useAppSelector(state => state.entities[entityType]?.store[entityId] as TEntity | undefined);
const entity = useAppSelector(state => selectEntity<TEntity>(state, entityType, entityId));
const isEnabled = opts.enabled ?? true;
const isLoading = isFetching && !entity;

View File

@ -26,6 +26,14 @@ function useListState<K extends keyof EntityListState>(path: EntitiesPath, key:
return useAppSelector(state => selectListState(state, path, key));
}
/** Get a single entity by its ID from the store. */
function selectEntity<TEntity extends Entity>(
state: RootState,
entityType: string, id: string,
): TEntity | undefined {
return state.entities[entityType]?.store[id] as TEntity | undefined;
}
/** Get list of entities from Redux. */
function selectEntities<TEntity extends Entity>(state: RootState, path: EntitiesPath): readonly TEntity[] {
const cache = selectCache(state, path);
@ -63,5 +71,6 @@ export {
selectListState,
useListState,
selectEntities,
selectEntity,
findEntity,
};

View File

@ -1,7 +1,6 @@
import { Map as ImmutableMap } from 'immutable';
import get from 'lodash/get';
import { STREAMING_FOLLOW_RELATIONSHIPS_UPDATE } from 'soapbox/actions/streaming';
import { type Relationship, relationshipSchema } from 'soapbox/schemas';
import { ACCOUNT_NOTE_SUBMIT_SUCCESS } from '../actions/account-notes';
@ -67,44 +66,12 @@ const importPleromaAccounts = (state: State, accounts: APIEntities) => {
return state;
};
const followStateToRelationship = (followState: string) => {
switch (followState) {
case 'follow_pending':
return { following: false, requested: true };
case 'follow_accept':
return { following: true, requested: false };
case 'follow_reject':
return { following: false, requested: false };
default:
return {};
}
};
const updateFollowRelationship = (state: State, id: string, followState: string) => {
const relationship = state.get(id) || relationshipSchema.parse({ id });
return state.set(id, {
...relationship,
...followStateToRelationship(followState),
});
};
export default function relationships(state: State = ImmutableMap<string, Relationship>(), action: AnyAction) {
switch (action.type) {
case ACCOUNT_IMPORT:
return importPleromaAccount(state, action.account);
case ACCOUNTS_IMPORT:
return importPleromaAccounts(state, action.accounts);
// case ACCOUNT_FOLLOW_REQUEST:
// return state.setIn([action.id, 'following'], true);
// case ACCOUNT_FOLLOW_FAIL:
// return state.setIn([action.id, 'following'], false);
// case ACCOUNT_UNFOLLOW_REQUEST:
// return state.setIn([action.id, 'following'], false);
// case ACCOUNT_UNFOLLOW_FAIL:
// return state.setIn([action.id, 'following'], true);
// case ACCOUNT_FOLLOW_SUCCESS:
// case ACCOUNT_UNFOLLOW_SUCCESS:
case ACCOUNT_BLOCK_SUCCESS:
case ACCOUNT_UNBLOCK_SUCCESS:
case ACCOUNT_MUTE_SUCCESS:
@ -122,12 +89,6 @@ export default function relationships(state: State = ImmutableMap<string, Relati
return setDomainBlocking(state, action.accounts, true);
case DOMAIN_UNBLOCK_SUCCESS:
return setDomainBlocking(state, action.accounts, false);
case STREAMING_FOLLOW_RELATIONSHIPS_UPDATE:
if (action.follower.id === action.me) {
return updateFollowRelationship(state, action.following.id, action.state);
} else {
return state;
}
default:
return state;
}