Fix streaming follow update
Fixes https://gitlab.com/soapbox-pub/soapbox/-/issues/1469
This commit is contained in:
parent
26dfcb728b
commit
1addfb96a9
4 changed files with 59 additions and 52 deletions
|
@ -1,4 +1,7 @@
|
||||||
import { getLocale, getSettings } from 'soapbox/actions/settings';
|
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 messages from 'soapbox/locales/messages';
|
||||||
import { ChatKeys, IChat, isLastMessage } from 'soapbox/queries/chats';
|
import { ChatKeys, IChat, isLastMessage } from 'soapbox/queries/chats';
|
||||||
import { queryClient } from 'soapbox/queries/client';
|
import { queryClient } from 'soapbox/queries/client';
|
||||||
|
@ -26,21 +29,11 @@ import {
|
||||||
} from './timelines';
|
} from './timelines';
|
||||||
|
|
||||||
import type { IStatContext } from 'soapbox/contexts/stat-context';
|
import type { IStatContext } from 'soapbox/contexts/stat-context';
|
||||||
|
import type { Relationship } from 'soapbox/schemas';
|
||||||
import type { AppDispatch, RootState } from 'soapbox/store';
|
import type { AppDispatch, RootState } from 'soapbox/store';
|
||||||
import type { APIEntity, Chat } from 'soapbox/types/entities';
|
import type { APIEntity, Chat } from 'soapbox/types/entities';
|
||||||
|
|
||||||
const STREAMING_CHAT_UPDATE = 'STREAMING_CHAT_UPDATE';
|
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 removeChatMessage = (payload: string) => {
|
||||||
const data = JSON.parse(payload);
|
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 {
|
export {
|
||||||
STREAMING_CHAT_UPDATE,
|
STREAMING_CHAT_UPDATE,
|
||||||
STREAMING_FOLLOW_RELATIONSHIPS_UPDATE,
|
|
||||||
connectTimelineStream,
|
connectTimelineStream,
|
||||||
type TimelineStreamOpts,
|
type TimelineStreamOpts,
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@ import z from 'zod';
|
||||||
import { useAppDispatch, useAppSelector, useLoading } from 'soapbox/hooks';
|
import { useAppDispatch, useAppSelector, useLoading } from 'soapbox/hooks';
|
||||||
|
|
||||||
import { importEntities } from '../actions';
|
import { importEntities } from '../actions';
|
||||||
|
import { selectEntity } from '../selectors';
|
||||||
|
|
||||||
import type { Entity } from '../types';
|
import type { Entity } from '../types';
|
||||||
import type { EntitySchema, EntityPath, EntityFn } from './types';
|
import type { EntitySchema, EntityPath, EntityFn } from './types';
|
||||||
|
@ -34,7 +35,7 @@ function useEntity<TEntity extends Entity>(
|
||||||
const defaultSchema = z.custom<TEntity>();
|
const defaultSchema = z.custom<TEntity>();
|
||||||
const schema = opts.schema || defaultSchema;
|
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 isEnabled = opts.enabled ?? true;
|
||||||
const isLoading = isFetching && !entity;
|
const isLoading = isFetching && !entity;
|
||||||
|
|
|
@ -26,6 +26,14 @@ function useListState<K extends keyof EntityListState>(path: EntitiesPath, key:
|
||||||
return useAppSelector(state => selectListState(state, path, 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. */
|
/** Get list of entities from Redux. */
|
||||||
function selectEntities<TEntity extends Entity>(state: RootState, path: EntitiesPath): readonly TEntity[] {
|
function selectEntities<TEntity extends Entity>(state: RootState, path: EntitiesPath): readonly TEntity[] {
|
||||||
const cache = selectCache(state, path);
|
const cache = selectCache(state, path);
|
||||||
|
@ -63,5 +71,6 @@ export {
|
||||||
selectListState,
|
selectListState,
|
||||||
useListState,
|
useListState,
|
||||||
selectEntities,
|
selectEntities,
|
||||||
|
selectEntity,
|
||||||
findEntity,
|
findEntity,
|
||||||
};
|
};
|
|
@ -1,7 +1,6 @@
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
import { Map as ImmutableMap } from 'immutable';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
|
|
||||||
import { STREAMING_FOLLOW_RELATIONSHIPS_UPDATE } from 'soapbox/actions/streaming';
|
|
||||||
import { type Relationship, relationshipSchema } from 'soapbox/schemas';
|
import { type Relationship, relationshipSchema } from 'soapbox/schemas';
|
||||||
|
|
||||||
import { ACCOUNT_NOTE_SUBMIT_SUCCESS } from '../actions/account-notes';
|
import { ACCOUNT_NOTE_SUBMIT_SUCCESS } from '../actions/account-notes';
|
||||||
|
@ -67,44 +66,12 @@ const importPleromaAccounts = (state: State, accounts: APIEntities) => {
|
||||||
return state;
|
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) {
|
export default function relationships(state: State = ImmutableMap<string, Relationship>(), action: AnyAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ACCOUNT_IMPORT:
|
case ACCOUNT_IMPORT:
|
||||||
return importPleromaAccount(state, action.account);
|
return importPleromaAccount(state, action.account);
|
||||||
case ACCOUNTS_IMPORT:
|
case ACCOUNTS_IMPORT:
|
||||||
return importPleromaAccounts(state, action.accounts);
|
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_BLOCK_SUCCESS:
|
||||||
case ACCOUNT_UNBLOCK_SUCCESS:
|
case ACCOUNT_UNBLOCK_SUCCESS:
|
||||||
case ACCOUNT_MUTE_SUCCESS:
|
case ACCOUNT_MUTE_SUCCESS:
|
||||||
|
@ -122,12 +89,6 @@ export default function relationships(state: State = ImmutableMap<string, Relati
|
||||||
return setDomainBlocking(state, action.accounts, true);
|
return setDomainBlocking(state, action.accounts, true);
|
||||||
case DOMAIN_UNBLOCK_SUCCESS:
|
case DOMAIN_UNBLOCK_SUCCESS:
|
||||||
return setDomainBlocking(state, action.accounts, false);
|
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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue