Skip to content

Commit

Permalink
add useSubscribe
Browse files Browse the repository at this point in the history
Co-authored-by: Necati Özmen <[email protected]>
  • Loading branch information
umutzd and necatiozmen committed Nov 19, 2021
1 parent fbd781d commit 91e4837
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 35 deletions.
9 changes: 4 additions & 5 deletions examples/dataProvider/supabase/src/pages/posts/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,28 @@ import {
import { IPost, ICategory } from "interfaces";

export const PostList: React.FC<IResourceComponentsProps> = () => {
const { tableProps, sorter, tableQueryResult } = useTable<IPost>({
const { tableProps, sorter } = useTable<IPost>({
initialSorter: [
{
field: "id",
order: "asc",
},
],
liveMode: "controlled",
onLiveEvent: (event) => {
tableQueryResult.refetch();
},
liveMode: "immediate",
});

const createMany = useCreateMany();

const categoryIds =
tableProps?.dataSource?.map((item) => item.categoryId) ?? [];

const { data, isLoading } = useMany<ICategory>({
resource: "categories",
ids: categoryIds,
queryOptions: {
enabled: categoryIds.length > 0,
},
liveMode: "immediate",
});

const { selectProps } = useSelect<ICategory>({
Expand Down
6 changes: 5 additions & 1 deletion examples/dataProvider/supabase/src/pages/posts/show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import { IPost, ICategory } from "interfaces";
const { Title, Text } = Typography;

export const PostShow: React.FC<IResourceComponentsProps> = () => {
const { queryResult } = useShow<IPost>();
const { queryResult } = useShow<IPost>({
liveMode: "immediate",
});

const { data, isLoading } = queryResult;
const record = data?.data;

Expand All @@ -25,6 +28,7 @@ export const PostShow: React.FC<IResourceComponentsProps> = () => {
queryOptions: {
enabled: !!record,
},
liveMode: "immediate",
});

return (
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/contexts/live/ILiveContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ export type LiveEvent = {
export type ILiveContext =
| {
publish: (event: LiveEvent) => void;
subscribe: (
channel: string,
type: LiveEvent["type"],
subscriptionCallback: (event: LiveEvent) => void,
) => any;
subscribe: (options: {
channel: string;
type: LiveEvent["type"];
callback: (event: LiveEvent) => void;
}) => any;
unsubscribe: (subscription: any) => void;
}
| undefined;
Expand Down
28 changes: 13 additions & 15 deletions packages/core/src/hooks/data/useList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,31 +77,29 @@ export const useList = <
const getAllQueries = useCacheQueries();

useEffect(() => {
if (liveDataContext) {
const { subscribe, unsubscribe } = liveDataContext;

const subscription = subscribe(
`resource/${resource}`,
"*",
(event) => {
console.log("event", event);
let subscription: any;

if (liveMode) {
subscription = liveDataContext?.subscribe({
channel: `resource/${resource}`,
type: "*",
callback: (event) => {
if (liveMode === "immediate") {
getAllQueries(resource).forEach((query) => {
queryClient.invalidateQueries(query.queryKey);
});
} else {
} else if (liveMode === "controlled") {
onLiveEvent?.(event);
}
},
);

return () => {
unsubscribe(subscription);
};
});
}

return () => undefined;
return () => {
if (subscription) {
liveDataContext?.unsubscribe(subscription);
}
};
}, []);

const queryResponse = useQuery<GetListResponse<TData>, TError>(
Expand Down
17 changes: 15 additions & 2 deletions packages/core/src/hooks/data/useMany.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useContext, useEffect } from "react";
import { useContext } from "react";
import { QueryObserverResult, useQuery, UseQueryOptions } from "react-query";
import { ArgsProps } from "antd/lib/notification";

Expand All @@ -9,8 +9,9 @@ import {
GetManyResponse,
HttpError,
MetaDataQuery,
LiveEvent,
} from "../../interfaces";
import { useTranslate, useCheckError } from "@hooks";
import { useTranslate, useCheckError, useSubscription } from "@hooks";
import { handleNotification } from "@definitions";

export type UseManyProps<TData, TError> = {
Expand All @@ -20,6 +21,8 @@ export type UseManyProps<TData, TError> = {
successNotification?: ArgsProps | false;
errorNotification?: ArgsProps | false;
metaData?: MetaDataQuery;
liveMode?: undefined | "immediate" | "controlled";
onLiveEvent?: (event: LiveEvent) => void;
};

/**
Expand All @@ -43,13 +46,23 @@ export const useMany = <
successNotification,
errorNotification,
metaData,
liveMode,
onLiveEvent,
}: UseManyProps<TData, TError>): QueryObserverResult<
GetManyResponse<TData>
> => {
const { getMany } = useContext<IDataContext>(DataContext);
const translate = useTranslate();
const { mutate: checkError } = useCheckError();

useSubscription({
resource,
channel: `resources/${resource}`,
enabled: true,
liveMode,
onLiveEvent,
});

const queryResponse = useQuery<GetManyResponse<TData>, TError>(
[`resource/getMany/${resource}`, ids],
() => getMany<TData>({ resource, ids, metaData }),
Expand Down
16 changes: 14 additions & 2 deletions packages/core/src/hooks/data/useOne.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { useContext } from "react";
import { QueryObserverResult, useQuery, UseQueryOptions } from "react-query";

import { DataContext } from "@contexts/data";
import {
GetOneResponse,
IDataContext,
HttpError,
BaseRecord,
MetaDataQuery,
LiveEvent,
} from "../../interfaces";
import { useCheckError, useTranslate } from "@hooks";
import { useCheckError, useTranslate, useSubscription } from "@hooks";
import { ArgsProps } from "antd/lib/notification";
import { handleNotification } from "@definitions";

Expand All @@ -20,6 +20,8 @@ export type UseOneProps<TData, TError> = {
successNotification?: ArgsProps | false;
errorNotification?: ArgsProps | false;
metaData?: MetaDataQuery;
liveMode?: undefined | "immediate" | "controlled";
onLiveEvent?: (event: LiveEvent) => void;
};

/**
Expand All @@ -43,11 +45,21 @@ export const useOne = <
successNotification,
errorNotification,
metaData,
liveMode,
onLiveEvent,
}: UseOneProps<TData, TError>): QueryObserverResult<GetOneResponse<TData>> => {
const { getOne } = useContext<IDataContext>(DataContext);
const translate = useTranslate();
const { mutate: checkError } = useCheckError();

useSubscription({
resource,
channel: `resources/${resource}/${id}`,
enabled: queryOptions?.enabled,
liveMode,
onLiveEvent,
});

const queryResponse = useQuery<GetOneResponse<TData>, TError>(
[`resource/getOne/${resource}`, { id }],
() => getOne<TData>({ resource, id, metaData }),
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/hooks/form/useEditForm/useEditForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export const useEditForm = <
enabled: isEdit && editId !== undefined,
},
metaData,
liveMode: "immediate",
});

const { data, isFetching } = queryResult;
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./auth";
export * from "./data";
export * from "./live";
export * from "./resource";
export * from "./notification";
export * from "./useFileUploadState";
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/hooks/live/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./useSubscription";
54 changes: 54 additions & 0 deletions packages/core/src/hooks/live/useSubscription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useContext, useEffect } from "react";
import { useQueryClient } from "react-query";
import { useCacheQueries } from "@hooks";
import { ILiveContext, LiveEvent } from "../../interfaces";
import { LiveContext } from "@contexts/live";

export type UseSubscriptionProps = {
channel: string;
resource: string;
liveMode?: "immediate" | "controlled";
enabled?: boolean;
onLiveEvent?: (event: LiveEvent) => void;
};

export const useSubscription = ({
resource,
channel,
enabled = false,
liveMode,
onLiveEvent,
}: UseSubscriptionProps): void => {
const queryClient = useQueryClient();
const getAllQueries = useCacheQueries();
const liveDataContext = useContext<ILiveContext>(LiveContext);

useEffect(() => {
let subscription: any;

if (liveMode && enabled) {
subscription = liveDataContext?.subscribe({
channel,
type: "*",
callback: (event) => {
if (liveMode === "immediate") {
getAllQueries(resource).forEach((query) => {
queryClient.invalidateQueries(query.queryKey);
});
} else if (liveMode === "controlled") {
onLiveEvent?.(event);
}
},
});
}

console.log("subscribed", channel);

return () => {
if (subscription) {
liveDataContext?.unsubscribe(subscription);
console.log("unsubscribed", channel);
}
};
}, [enabled]);
};
7 changes: 7 additions & 0 deletions packages/core/src/hooks/show/useShow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
GetOneResponse,
SuccessErrorNotification,
MetaDataQuery,
LiveEvent,
} from "../../interfaces";

export type useShowReturnType<TData extends BaseRecord = BaseRecord> = {
Expand All @@ -21,6 +22,8 @@ export type useShowProps = {
resource?: string;
id?: string;
metaData?: MetaDataQuery;
liveMode?: undefined | "immediate" | "controlled";
onLiveEvent?: (event: LiveEvent) => void;
} & SuccessErrorNotification;

/**
Expand All @@ -36,6 +39,8 @@ export const useShow = <TData extends BaseRecord = BaseRecord>({
successNotification,
errorNotification,
metaData,
liveMode,
onLiveEvent,
}: useShowProps = {}): useShowReturnType<TData> => {
const { useParams } = useRouterContext();

Expand All @@ -57,6 +62,8 @@ export const useShow = <TData extends BaseRecord = BaseRecord>({
successNotification,
errorNotification,
metaData,
liveMode,
onLiveEvent,
});

return {
Expand Down
17 changes: 12 additions & 5 deletions packages/supabase/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,21 +183,28 @@ const dataProvider = (supabaseClient: SupabaseClient): DataProvider => {

const liveProvider = (supabaseClient: SupabaseClient): LiveProvider => {
return {
subscribe: (channel, type, cb): RealtimeSubscription => {
const [, resourceForSupabase] = channel.split("/");
subscribe: ({ channel, type, callback }): RealtimeSubscription => {
const [, resource, id] = channel.split("/");

return supabaseClient
const resourceForSupabase = id
? `${resource}:id=eq.${id}`
: resource;

const client = supabaseClient
.from(resourceForSupabase)
.on(supabaseTypes[type], (payload) => {
console.log(payload);
cb({
callback({
channel,
type: liveTypes[payload.eventType],
date: new Date(payload.commit_timestamp),
payload: payload.new,
});
})
.subscribe();

console.log(supabaseClient.getSubscriptions());

return client;
},

unsubscribe: (subscription: RealtimeSubscription) => {
Expand Down

0 comments on commit 91e4837

Please sign in to comment.