feat: refactor the cache
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
"fs-extra": "^10.0.1",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"linkify-it": "^3.0.3",
|
||||
"localforage": "^1.10.0",
|
||||
"mini-css-extract-plugin": "^2.5.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dev-utils": "^12.0.0",
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { useState } from "react";
|
||||
import localforage from "localforage";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { initChannelMsg } from "./slices/message.channel";
|
||||
import { initUserMsg } from "./slices/message.user";
|
||||
import { setChannels } from "./slices/channels";
|
||||
import { setMark } from "./slices/visit.mark";
|
||||
import { KEY_UID } from "./config";
|
||||
const initCache = () => {
|
||||
const uid = localStorage.getItem(KEY_UID) || "";
|
||||
const name = `cache_db_${uid}`;
|
||||
// const channels = localforage.createInstance({
|
||||
// name,
|
||||
// storeName: "channels",
|
||||
// description: "channel list",
|
||||
// });
|
||||
// const contacts = localforage.createInstance({
|
||||
// name,
|
||||
// storeName: "contacts",
|
||||
// description: "contact list",
|
||||
// });
|
||||
// const msgs_user = localforage.createInstance({
|
||||
// name,
|
||||
// storeName: "msgs_dm",
|
||||
// description: "DM message list",
|
||||
// });
|
||||
// const msgs_channel = localforage.createInstance({
|
||||
// name,
|
||||
// storeName: "msgs_channel",
|
||||
// description: "channel message list",
|
||||
// });
|
||||
window.CACHE = localforage.createInstance({
|
||||
name,
|
||||
storeName: "data",
|
||||
description: "rustchat cache data by uid",
|
||||
});
|
||||
};
|
||||
export const useRehydrate = () => {
|
||||
const [iterated, setIterated] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const rehydrate = async () => {
|
||||
const res = await window.CACHE.iterate((data, key) => {
|
||||
switch (key) {
|
||||
case "visitMark":
|
||||
dispatch(setMark(data));
|
||||
break;
|
||||
case "channels":
|
||||
dispatch(setChannels(data));
|
||||
break;
|
||||
// case "contacts":
|
||||
// dispatch(initChannelMsg(data));
|
||||
// break;
|
||||
case "channelMessage":
|
||||
dispatch(initChannelMsg(data));
|
||||
break;
|
||||
case "userMessage":
|
||||
dispatch(initUserMsg(data));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
setIterated(true);
|
||||
console.log("iterate", res);
|
||||
};
|
||||
return { rehydrate, cacheFirst: iterated };
|
||||
};
|
||||
|
||||
export default initCache;
|
||||
@@ -10,5 +10,8 @@ export const googleClientID =
|
||||
"418687074928-naojba82n9ktf0rkvnqoor4nhr54ql1b.apps.googleusercontent.com";
|
||||
// "840319286941-6ds7lbvk55eq8mjortf68cb2ll65lprt.apps.googleusercontent.com";
|
||||
export const tokenHeader = "X-API-Key";
|
||||
export const KEY_TOKEN = "RUSTCHAT_TOKEN";
|
||||
export const KEY_REFRESH_TOKEN = "RUSTCHAT_REFRESH_TOKEN";
|
||||
export const KEY_UID = "RUSTCHAT_CURR_UID";
|
||||
|
||||
export default BASE_URL;
|
||||
|
||||
@@ -33,11 +33,11 @@ export const authApi = createApi({
|
||||
}),
|
||||
// 获取openid
|
||||
getOpenid: builder.mutation({
|
||||
query: ({ issuer_url, redirect_uri }) => ({
|
||||
query: ({ issuer, redirect_uri }) => ({
|
||||
url: "/token/openid/authorize",
|
||||
method: "POST",
|
||||
body: {
|
||||
issuer_url,
|
||||
issuer,
|
||||
redirect_uri,
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -1,73 +1,79 @@
|
||||
import { fetchBaseQuery } from '@reduxjs/toolkit/query';
|
||||
import toast from 'react-hot-toast';
|
||||
import { updateToken, clearAuthData } from '../slices/auth.data';
|
||||
import BASE_URL, { tokenHeader } from '../config';
|
||||
const whiteList = ['login', 'checkInviteTokenValid'];
|
||||
import { fetchBaseQuery } from "@reduxjs/toolkit/query";
|
||||
import toast from "react-hot-toast";
|
||||
import { updateToken, clearAuthData } from "../slices/auth.data";
|
||||
import BASE_URL, { tokenHeader } from "../config";
|
||||
const whiteList = ["login", "checkInviteTokenValid", "getServer", "getOpenid"];
|
||||
const baseQuery = fetchBaseQuery({
|
||||
baseUrl: BASE_URL,
|
||||
prepareHeaders: (headers, { getState, endpoint }) => {
|
||||
console.log('req', endpoint);
|
||||
const { token } = getState().authData;
|
||||
if (token && !whiteList.includes(endpoint)) {
|
||||
headers.set(tokenHeader, token);
|
||||
}
|
||||
// 发送channel msg (临时举措)
|
||||
// if (endpoint == "sendChannelMsg") {
|
||||
// headers.set("Content-Type", "text/plain");
|
||||
// }
|
||||
return headers;
|
||||
}
|
||||
baseUrl: BASE_URL,
|
||||
prepareHeaders: (headers, { getState, endpoint }) => {
|
||||
console.log("req", endpoint);
|
||||
const { token } = getState().authData;
|
||||
if (token && !whiteList.includes(endpoint)) {
|
||||
headers.set(tokenHeader, token);
|
||||
}
|
||||
// 发送channel msg (临时举措)
|
||||
// if (endpoint == "sendChannelMsg") {
|
||||
// headers.set("Content-Type", "text/plain");
|
||||
// }
|
||||
return headers;
|
||||
},
|
||||
});
|
||||
const baseQueryWithReauth = async (args, api, extraOptions) => {
|
||||
let result = await baseQuery(args, api, extraOptions);
|
||||
if (result.error) {
|
||||
console.log('api error', result.error);
|
||||
switch (result.error.originalStatus) {
|
||||
case 404:
|
||||
{
|
||||
toast.error('Request Not Found');
|
||||
}
|
||||
break;
|
||||
case 500:
|
||||
{
|
||||
toast.error(result.error.data || 'server error');
|
||||
}
|
||||
break;
|
||||
case 401:
|
||||
{
|
||||
// try to get a new token with refreshToken
|
||||
const { token, refreshToken } = api.getState().authData;
|
||||
const refreshResult = await baseQuery(
|
||||
{
|
||||
url: '/token/renew',
|
||||
method: 'POST',
|
||||
body: {
|
||||
token,
|
||||
refresh_token: refreshToken
|
||||
}
|
||||
},
|
||||
api,
|
||||
extraOptions
|
||||
);
|
||||
console.log({ refreshResult });
|
||||
if (refreshResult.data) {
|
||||
// store the new token
|
||||
api.dispatch(updateToken(refreshResult.data));
|
||||
// retry the initial query
|
||||
result = await baseQuery(args, api, extraOptions);
|
||||
} else {
|
||||
toast.error('token expired, please login again');
|
||||
api.dispatch(clearAuthData());
|
||||
}
|
||||
}
|
||||
let result = await baseQuery(args, api, extraOptions);
|
||||
if (result?.error) {
|
||||
console.log("api error", result.error.originalStatus, api.endpoint);
|
||||
switch (result.error.originalStatus || result.error.status) {
|
||||
case 404:
|
||||
{
|
||||
toast.error("Request Not Found");
|
||||
}
|
||||
break;
|
||||
case 500:
|
||||
{
|
||||
toast.error(result.error.data || "server error");
|
||||
}
|
||||
break;
|
||||
case 401:
|
||||
{
|
||||
if (api.endpoint === "renew") {
|
||||
toast.error("token expired, please login again");
|
||||
api.dispatch(clearAuthData());
|
||||
location.href = "/#/login";
|
||||
return;
|
||||
}
|
||||
// try to get a new token with refreshToken
|
||||
const { token, refreshToken } = api.getState().authData;
|
||||
const refreshResult = await baseQuery(
|
||||
{
|
||||
url: "/token/renew",
|
||||
method: "POST",
|
||||
body: {
|
||||
token,
|
||||
refresh_token: refreshToken,
|
||||
},
|
||||
},
|
||||
api,
|
||||
extraOptions
|
||||
);
|
||||
console.log({ refreshResult });
|
||||
if (refreshResult.data) {
|
||||
// store the new token
|
||||
api.dispatch(updateToken(refreshResult.data));
|
||||
// retry the initial query
|
||||
result = await baseQuery(args, api, extraOptions);
|
||||
} else {
|
||||
toast.error("token expired, please login again");
|
||||
api.dispatch(clearAuthData());
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return result;
|
||||
};
|
||||
|
||||
export default baseQueryWithReauth;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { createApi } from "@reduxjs/toolkit/query/react";
|
||||
import { REHYDRATE } from "redux-persist";
|
||||
import toast from "react-hot-toast";
|
||||
import baseQuery from "./base.query";
|
||||
import { ContentTypes } from "../config";
|
||||
@@ -12,11 +11,6 @@ import {
|
||||
export const channelApi = createApi({
|
||||
reducerPath: "channel",
|
||||
baseQuery,
|
||||
extractRehydrationInfo(action, { reducerPath }) {
|
||||
if (action.type === REHYDRATE) {
|
||||
return action.payload ? action.payload[reducerPath] : undefined;
|
||||
}
|
||||
},
|
||||
refetchOnFocus: true,
|
||||
endpoints: (builder) => ({
|
||||
getChannels: builder.query({
|
||||
@@ -84,11 +78,12 @@ export const channelApi = createApi({
|
||||
};
|
||||
dispatch(addPendingMessage({ type: "channel", msg: tmpMsg }));
|
||||
try {
|
||||
const {
|
||||
data: { gid, ...rest },
|
||||
} = await queryFulfilled;
|
||||
const { data: server_mid } = await queryFulfilled;
|
||||
console.log("channel server mid", server_mid);
|
||||
// 此处的id,是指给谁发的
|
||||
dispatch(addChannelMsg({ id: gid, ...rest, unread: false }));
|
||||
dispatch(
|
||||
addChannelMsg({ ...tmpMsg, mid: server_mid, unread: false })
|
||||
);
|
||||
dispatch(removePendingMessage({ id, mid, type: "channel" }));
|
||||
} catch {
|
||||
console.log("channel message send failed");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { createApi } from "@reduxjs/toolkit/query/react";
|
||||
import { REHYDRATE } from "redux-persist";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
import baseQuery from "./base.query";
|
||||
@@ -14,11 +13,6 @@ import {
|
||||
export const contactApi = createApi({
|
||||
reducerPath: "contact",
|
||||
baseQuery,
|
||||
extractRehydrationInfo(action, { reducerPath }) {
|
||||
if (action.type === REHYDRATE) {
|
||||
return action.payload ? action.payload[reducerPath] : undefined;
|
||||
}
|
||||
},
|
||||
endpoints: (builder) => ({
|
||||
getContacts: builder.query({
|
||||
query: () => ({ url: `user` }),
|
||||
@@ -96,9 +90,13 @@ export const contactApi = createApi({
|
||||
};
|
||||
dispatch(addPendingMessage({ type: "user", msg: tmpMsg }));
|
||||
try {
|
||||
const { data } = await queryFulfilled;
|
||||
// 走sse推送
|
||||
const { data: server_mid } = await queryFulfilled;
|
||||
// console.log("wtf", wtf);
|
||||
// 此处的id,是指给谁发的
|
||||
dispatch(addUserMsg({ id, ...data, unread: false }));
|
||||
dispatch(
|
||||
addUserMsg({ id, ...tmpMsg, mid: server_mid, unread: false })
|
||||
);
|
||||
dispatch(removePendingMessage({ id, mid, type: "user" }));
|
||||
} catch {
|
||||
toast.error("Send Message Failed");
|
||||
@@ -115,6 +113,7 @@ export const {
|
||||
useUpdateInfoMutation,
|
||||
useUpdateAvatarMutation,
|
||||
useGetContactsQuery,
|
||||
useLazyGetContactsQuery,
|
||||
useSendMsgMutation,
|
||||
useRegisterMutation,
|
||||
} = contactApi;
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { createApi } from "@reduxjs/toolkit/query/react";
|
||||
import BASE_URL from "../config";
|
||||
// import { REHYDRATE } from 'redux-persist';
|
||||
|
||||
import baseQuery from "./base.query";
|
||||
|
||||
export const serverApi = createApi({
|
||||
reducerPath: "server",
|
||||
baseQuery,
|
||||
// extractRehydrationInfo(action, { reducerPath }) {
|
||||
// if (action.type === REHYDRATE) {
|
||||
// return action.payload ? action.payload[reducerPath] : undefined;
|
||||
// }
|
||||
// },
|
||||
endpoints: (builder) => ({
|
||||
getServer: builder.query({
|
||||
query: () => ({ url: `admin/system/organization` }),
|
||||
@@ -78,6 +72,7 @@ export const {
|
||||
useGetAgoraConfigQuery,
|
||||
useUpdateAgoraConfigMutation,
|
||||
useGetServerQuery,
|
||||
useLazyGetServerQuery,
|
||||
useUpdateServerMutation,
|
||||
useUpdateLogoMutation,
|
||||
} = serverApi;
|
||||
|
||||
+18
-15
@@ -1,12 +1,10 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import BASE_URL from "../config";
|
||||
import BASE_URL, { KEY_REFRESH_TOKEN, KEY_TOKEN, KEY_UID } from "../config";
|
||||
import { getNonNullValues } from "../../common/utils";
|
||||
const initialState = {
|
||||
user: null,
|
||||
usersVersion: null,
|
||||
afterMid: null,
|
||||
token: null,
|
||||
refreshToken: null,
|
||||
token: localStorage.getItem(KEY_TOKEN),
|
||||
refreshToken: localStorage.getItem(KEY_REFRESH_TOKEN),
|
||||
};
|
||||
const authDataSlice = createSlice({
|
||||
name: "authData",
|
||||
@@ -17,6 +15,14 @@ const authDataSlice = createSlice({
|
||||
state.user = user;
|
||||
state.token = token;
|
||||
state.refreshToken = refresh_token;
|
||||
// set local data
|
||||
localStorage.setItem(KEY_TOKEN, token);
|
||||
localStorage.setItem(KEY_REFRESH_TOKEN, refresh_token);
|
||||
localStorage.setItem(KEY_UID, user.uid);
|
||||
},
|
||||
setUserData(state, action) {
|
||||
const user = action.payload;
|
||||
state.user = user;
|
||||
},
|
||||
updateLoginedUserByLogs(state, action) {
|
||||
const logs = action.payload;
|
||||
@@ -44,29 +50,26 @@ const authDataSlice = createSlice({
|
||||
state.user = null;
|
||||
state.token = null;
|
||||
state.refreshToken = null;
|
||||
// remove local data
|
||||
localStorage.removeItem(KEY_TOKEN);
|
||||
localStorage.removeItem(KEY_REFRESH_TOKEN);
|
||||
localStorage.removeItem(KEY_UID);
|
||||
},
|
||||
updateToken(state, action) {
|
||||
const { token, refresh_token } = action.payload;
|
||||
console.log("refresh token");
|
||||
state.token = token;
|
||||
state.refreshToken = refresh_token;
|
||||
},
|
||||
setUsersVersion(state, action) {
|
||||
const { version } = action.payload;
|
||||
state.usersVersion = version;
|
||||
},
|
||||
setAfterMid(state, action) {
|
||||
const { mid } = action.payload;
|
||||
state.afterMid = mid;
|
||||
localStorage.setItem(KEY_TOKEN, token);
|
||||
localStorage.setItem(KEY_REFRESH_TOKEN, refresh_token);
|
||||
},
|
||||
},
|
||||
});
|
||||
export const {
|
||||
updateToken,
|
||||
setAuthData,
|
||||
setUserData,
|
||||
clearAuthData,
|
||||
setUsersVersion,
|
||||
setAfterMid,
|
||||
updateLoginedUserByLogs,
|
||||
} = authDataSlice.actions;
|
||||
export default authDataSlice.reducer;
|
||||
|
||||
@@ -4,15 +4,20 @@ const channelsSlice = createSlice({
|
||||
name: `channels`,
|
||||
initialState,
|
||||
reducers: {
|
||||
clearChannels() {
|
||||
return initialState;
|
||||
},
|
||||
setChannels(state, action) {
|
||||
console.log("set channels store", state);
|
||||
const chs = action.payload || [];
|
||||
return Object.fromEntries(
|
||||
chs.map((c) => {
|
||||
const { gid, ...rest } = c;
|
||||
return [gid, rest];
|
||||
})
|
||||
);
|
||||
return Array.isArray(chs)
|
||||
? Object.fromEntries(
|
||||
chs.map((c) => {
|
||||
const { gid, ...rest } = c;
|
||||
return [gid, rest];
|
||||
})
|
||||
)
|
||||
: chs;
|
||||
},
|
||||
|
||||
updateChannel(state, action) {
|
||||
@@ -41,6 +46,7 @@ const channelsSlice = createSlice({
|
||||
},
|
||||
});
|
||||
export const {
|
||||
clearChannels,
|
||||
setChannels,
|
||||
addChannel,
|
||||
deleteChannel,
|
||||
|
||||
@@ -5,6 +5,9 @@ const contactsSlice = createSlice({
|
||||
name: `contacts`,
|
||||
initialState,
|
||||
reducers: {
|
||||
clearContacts() {
|
||||
return initialState;
|
||||
},
|
||||
setContacts(state, action) {
|
||||
console.log("set Contacts store", state);
|
||||
const contacts = action.payload || [];
|
||||
@@ -21,8 +24,8 @@ const contactsSlice = createSlice({
|
||||
case "update":
|
||||
{
|
||||
const vals = getNonNullValues(rest);
|
||||
console.log("update vals", vals);
|
||||
const curr = state.find(({ uid: id }) => id == uid);
|
||||
console.log("update vals", vals, curr);
|
||||
if (curr) {
|
||||
Object.keys(vals).forEach((k) => {
|
||||
curr[k] = vals[k];
|
||||
@@ -32,7 +35,14 @@ const contactsSlice = createSlice({
|
||||
break;
|
||||
case "create":
|
||||
{
|
||||
state.push({ uid, ...rest });
|
||||
const idx = state.findIndex((o) => {
|
||||
return o.uid == uid;
|
||||
});
|
||||
if (idx > -1) {
|
||||
state.splice(idx, 1, { uid, ...rest });
|
||||
} else {
|
||||
state.push({ uid, ...rest });
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "delete":
|
||||
@@ -56,7 +66,7 @@ const contactsSlice = createSlice({
|
||||
onlines.forEach((item) => {
|
||||
const { uid, online = false } = item;
|
||||
const curr = state.find(({ uid: id }) => id == uid);
|
||||
console.log("update user status", curr, online);
|
||||
// console.log("update user status", curr, online);
|
||||
if (curr) {
|
||||
curr.online = online;
|
||||
}
|
||||
@@ -65,6 +75,7 @@ const contactsSlice = createSlice({
|
||||
},
|
||||
});
|
||||
export const {
|
||||
clearContacts,
|
||||
setContacts,
|
||||
removeContact,
|
||||
updateUsersByLogs,
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { isObjectEqual } from "../../common/utils";
|
||||
|
||||
const initialState = {
|
||||
// accessLogs: {},
|
||||
pendingMsgs: [],
|
||||
};
|
||||
const initialState = {};
|
||||
|
||||
const channelMsgSlice = createSlice({
|
||||
name: "channelMessage",
|
||||
initialState,
|
||||
reducers: {
|
||||
clearChannelMsg() {
|
||||
return initialState;
|
||||
},
|
||||
initChannelMsg(state, action) {
|
||||
return action.payload;
|
||||
},
|
||||
addChannelMsg(state, action) {
|
||||
const {
|
||||
id,
|
||||
@@ -48,22 +51,13 @@ const channelMsgSlice = createSlice({
|
||||
obj.unread = false;
|
||||
});
|
||||
},
|
||||
addPendingMsg(state, action) {
|
||||
state.pendingMsgs.push(action.payload);
|
||||
},
|
||||
removePendingMsg(state, action) {
|
||||
const timestamp = action.payload;
|
||||
state.pendingMsgs = state.pendingMsgs.filter(
|
||||
(m) => m.timestamp != timestamp
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
export const {
|
||||
clearChannelMsg,
|
||||
initChannelMsg,
|
||||
clearChannelMsgUnread,
|
||||
setChannelMsgRead,
|
||||
addChannelMsg,
|
||||
addPendingMsg,
|
||||
removePendingMsg,
|
||||
} = channelMsgSlice.actions;
|
||||
export default channelMsgSlice.reducer;
|
||||
|
||||
@@ -8,6 +8,9 @@ const pendingMessageSlice = createSlice({
|
||||
name: "pendingMessage",
|
||||
initialState,
|
||||
reducers: {
|
||||
clearPendingMsg() {
|
||||
return initialState;
|
||||
},
|
||||
addPendingMessage(state, action) {
|
||||
const { type = "user", msg } = action.payload;
|
||||
const { id, mid } = msg;
|
||||
@@ -23,6 +26,7 @@ const pendingMessageSlice = createSlice({
|
||||
},
|
||||
});
|
||||
export const {
|
||||
clearPendingMsg,
|
||||
addPendingMessage,
|
||||
removePendingMessage,
|
||||
} = pendingMessageSlice.actions;
|
||||
|
||||
@@ -6,6 +6,12 @@ const userMsgSlice = createSlice({
|
||||
name: "userMessage",
|
||||
initialState,
|
||||
reducers: {
|
||||
clearUserMsg() {
|
||||
return initialState;
|
||||
},
|
||||
initUserMsg(state, action) {
|
||||
return action.payload;
|
||||
},
|
||||
addUserMsg(state, action) {
|
||||
const {
|
||||
id,
|
||||
@@ -44,5 +50,11 @@ const userMsgSlice = createSlice({
|
||||
},
|
||||
},
|
||||
});
|
||||
export const { addUserMsg, setUserMsgRead, removeMsg } = userMsgSlice.actions;
|
||||
export const {
|
||||
clearUserMsg,
|
||||
initUserMsg,
|
||||
addUserMsg,
|
||||
setUserMsgRead,
|
||||
removeMsg,
|
||||
} = userMsgSlice.actions;
|
||||
export default userMsgSlice.reducer;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
// import { KEY_REFRESH_TOKEN, KEY_TOKEN, KEY_UID } from "../config";
|
||||
const initialState = {
|
||||
usersVersion: null,
|
||||
afterMid: null,
|
||||
};
|
||||
const visitMarkSlice = createSlice({
|
||||
name: "visitMark",
|
||||
initialState,
|
||||
reducers: {
|
||||
clearMark() {
|
||||
return initialState;
|
||||
},
|
||||
setMark(state, action) {
|
||||
const mark = action.payload;
|
||||
return mark;
|
||||
},
|
||||
setUsersVersion(state, action) {
|
||||
const { version } = action.payload;
|
||||
state.usersVersion = version;
|
||||
},
|
||||
setAfterMid(state, action) {
|
||||
const { mid } = action.payload;
|
||||
state.afterMid = mid;
|
||||
},
|
||||
},
|
||||
});
|
||||
export const {
|
||||
setMark,
|
||||
setUsersVersion,
|
||||
setAfterMid,
|
||||
clearMark,
|
||||
} = visitMarkSlice.actions;
|
||||
export default visitMarkSlice.reducer;
|
||||
+66
-23
@@ -1,38 +1,81 @@
|
||||
import { configureStore, combineReducers } from "@reduxjs/toolkit";
|
||||
import {
|
||||
configureStore,
|
||||
combineReducers,
|
||||
createListenerMiddleware,
|
||||
} from "@reduxjs/toolkit";
|
||||
import { setupListeners } from "@reduxjs/toolkit/query";
|
||||
import authDataReducer from "./slices/auth.data";
|
||||
import visitMarkReducer from "./slices/visit.mark";
|
||||
import uiReducer from "./slices/ui";
|
||||
import channelsReducer from "./slices/channels";
|
||||
import contactsReducer from "./slices/contacts";
|
||||
import pendingMsgReducer from "./slices/message.pending";
|
||||
import channelMsgReducer from "./slices/message.channel";
|
||||
import userMsgReducer from "./slices/message.user";
|
||||
import { authApi } from "./services/auth";
|
||||
import { contactApi } from "./services/contact";
|
||||
import { channelApi } from "./services/channel";
|
||||
import { serverApi } from "./services/server";
|
||||
const getCacheKey = (action = "") => {
|
||||
const [type] = action.split("/");
|
||||
const keys = [
|
||||
"visitMark",
|
||||
"channels",
|
||||
"contacts",
|
||||
"userMessage",
|
||||
"channelMessage",
|
||||
// "pendingMessage",
|
||||
];
|
||||
const [tmp = ""] = keys.filter((k) => k == type);
|
||||
return tmp;
|
||||
};
|
||||
const reducer = combineReducers({
|
||||
ui: uiReducer,
|
||||
visitMark: visitMarkReducer,
|
||||
contacts: contactsReducer,
|
||||
channels: channelsReducer,
|
||||
pendingMessage: pendingMsgReducer,
|
||||
userMessage: userMsgReducer,
|
||||
channelMessage: channelMsgReducer,
|
||||
authData: authDataReducer,
|
||||
[authApi.reducerPath]: authApi.reducer,
|
||||
[contactApi.reducerPath]: contactApi.reducer,
|
||||
[channelApi.reducerPath]: channelApi.reducer,
|
||||
[serverApi.reducerPath]: serverApi.reducer,
|
||||
});
|
||||
// Create the middleware instance and methods
|
||||
const listenerMiddleware = createListenerMiddleware();
|
||||
|
||||
const getStore = () => {
|
||||
const reducer = combineReducers({
|
||||
ui: uiReducer,
|
||||
channels: channelsReducer,
|
||||
userMsg: userMsgReducer,
|
||||
channelMsg: channelMsgReducer,
|
||||
authData: authDataReducer,
|
||||
[authApi.reducerPath]: authApi.reducer,
|
||||
[contactApi.reducerPath]: contactApi.reducer,
|
||||
[channelApi.reducerPath]: channelApi.reducer,
|
||||
[serverApi.reducerPath]: serverApi.reducer,
|
||||
});
|
||||
const store = configureStore({
|
||||
reducer,
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware().concat(
|
||||
// Add one or more listener entries that look for specific actions.
|
||||
// They may contain any sync or async logic, similar to thunks.
|
||||
listenerMiddleware.startListening({
|
||||
predicate: (action, currentState, previousState) => {
|
||||
const { type = "" } = action;
|
||||
|
||||
return !!getCacheKey(type);
|
||||
// console.log("listener predicate", action, currentState, previousState);
|
||||
// return true;
|
||||
},
|
||||
effect: async (action, listenerApi) => {
|
||||
const { type = "" } = action;
|
||||
const key = getCacheKey(type);
|
||||
console.log("listener effect", key, listenerApi.getState(), window.CACHE);
|
||||
if (key && window.CACHE) {
|
||||
await window.CACHE.setItem(key, listenerApi.getState()[key]);
|
||||
}
|
||||
},
|
||||
});
|
||||
const store = configureStore({
|
||||
reducer,
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware()
|
||||
.concat(
|
||||
authApi.middleware,
|
||||
contactApi.middleware,
|
||||
channelApi.middleware,
|
||||
serverApi.middleware
|
||||
),
|
||||
});
|
||||
setupListeners(store.dispatch);
|
||||
return store;
|
||||
};
|
||||
export default getStore;
|
||||
)
|
||||
.prepend(listenerMiddleware.middleware),
|
||||
});
|
||||
setupListeners(store.dispatch);
|
||||
export default store;
|
||||
|
||||
@@ -57,7 +57,12 @@ const StyledWrapper = styled.div`
|
||||
right: 0;
|
||||
}
|
||||
`;
|
||||
export default function AvatarUploader({ url = "", name = "", uploadImage }) {
|
||||
export default function AvatarUploader({
|
||||
url = "",
|
||||
name = "",
|
||||
uploadImage,
|
||||
disabled = false,
|
||||
}) {
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [currSrc, setCurrSrc] = useState("");
|
||||
useEffect(() => {
|
||||
@@ -80,21 +85,29 @@ export default function AvatarUploader({ url = "", name = "", uploadImage }) {
|
||||
<StyledWrapper>
|
||||
<div className="avatar">
|
||||
<img src={currSrc} alt="avatar" />
|
||||
<div className="tip">{uploading ? `Uploading` : `Change Avatar`}</div>
|
||||
<input
|
||||
multiple={false}
|
||||
onChange={handleUpload}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
name="avatar"
|
||||
id="avatar"
|
||||
/>
|
||||
{!disabled && (
|
||||
<>
|
||||
<div className="tip">
|
||||
{uploading ? `Uploading` : `Change Avatar`}
|
||||
</div>
|
||||
<input
|
||||
multiple={false}
|
||||
onChange={handleUpload}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
name="avatar"
|
||||
id="avatar"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<img
|
||||
src="https://static.nicegoodthings.com/project/rustchat/icon.avatar.uploader.svg"
|
||||
alt="icon"
|
||||
className="icon"
|
||||
/>
|
||||
{!disabled && (
|
||||
<img
|
||||
src="https://static.nicegoodthings.com/project/rustchat/icon.avatar.uploader.svg"
|
||||
alt="icon"
|
||||
className="icon"
|
||||
/>
|
||||
)}
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,10 +17,9 @@ import {
|
||||
import {
|
||||
updateToken,
|
||||
clearAuthData,
|
||||
setUsersVersion,
|
||||
setAfterMid,
|
||||
updateLoginedUserByLogs,
|
||||
} from "../../../app/slices/auth.data";
|
||||
import { setUsersVersion, setAfterMid } from "../../../app/slices/visit.mark";
|
||||
|
||||
import { addChannelMsg } from "../../../app/slices/message.channel";
|
||||
import { addUserMsg } from "../../../app/slices/message.user";
|
||||
@@ -161,50 +160,74 @@ const NotificationHub = ({ usersVersion = 0, afterMid = 0 }) => {
|
||||
dispatch(deleteChannel(data.gid));
|
||||
break;
|
||||
case "chat":
|
||||
// console.log("chat data", data);
|
||||
if (data.gid) {
|
||||
// channel msg
|
||||
const { gid, ...rest } = data;
|
||||
console.log("compare", rest, currUser, rest.from_uid != currUser.uid);
|
||||
const isSelf = rest.from_uid == currUser.uid;
|
||||
dispatch(
|
||||
addChannelMsg({
|
||||
id: gid,
|
||||
// 自己发的 就不用标记未读
|
||||
unread: !isSelf,
|
||||
...rest,
|
||||
})
|
||||
);
|
||||
// group message notification
|
||||
if (!isSelf) {
|
||||
showNotification({
|
||||
body: rest.content,
|
||||
data: {
|
||||
path: `/chat/channel/${gid}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// user msg
|
||||
const isSelf = data.from_uid == currUser.uid;
|
||||
dispatch(
|
||||
addUserMsg({
|
||||
id: data.from_uid,
|
||||
unread: !isSelf,
|
||||
...data,
|
||||
})
|
||||
);
|
||||
if (!isSelf) {
|
||||
showNotification({
|
||||
body: data.content,
|
||||
data: {
|
||||
path: `/chat/dm/${data.from_uid}`,
|
||||
},
|
||||
});
|
||||
{
|
||||
// console.log("chat data", data);
|
||||
const {
|
||||
detail: { target },
|
||||
} = data;
|
||||
const {
|
||||
created_at,
|
||||
mid,
|
||||
from_uid,
|
||||
detail: { content, content_type, expires_in, type },
|
||||
} = data;
|
||||
if (typeof target.gid !== "undefined") {
|
||||
// channel msg
|
||||
const gid = target.gid;
|
||||
const isSelf = from_uid == currUser.uid;
|
||||
dispatch(
|
||||
addChannelMsg({
|
||||
id: gid,
|
||||
from_uid,
|
||||
// 自己发的 就不用标记未读
|
||||
unread: !isSelf,
|
||||
created_at,
|
||||
mid,
|
||||
content,
|
||||
content_type,
|
||||
expires_in,
|
||||
type,
|
||||
})
|
||||
);
|
||||
// group message notification
|
||||
if (!isSelf) {
|
||||
showNotification({
|
||||
body: content,
|
||||
data: {
|
||||
path: `/chat/channel/${gid}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// user msg
|
||||
const isSelf = data.from_uid == currUser.uid;
|
||||
|
||||
dispatch(
|
||||
addUserMsg({
|
||||
// 此处需要特别注意
|
||||
id: isSelf ? target.uid : from_uid,
|
||||
from_uid: from_uid,
|
||||
unread: !isSelf,
|
||||
created_at,
|
||||
mid,
|
||||
content,
|
||||
content_type,
|
||||
expires_in,
|
||||
type,
|
||||
})
|
||||
);
|
||||
if (!isSelf) {
|
||||
showNotification({
|
||||
body: data.content,
|
||||
data: {
|
||||
path: `/chat/dm/${data.from_uid}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
// 更新after_mid
|
||||
dispatch(setAfterMid({ mid: data.mid }));
|
||||
}
|
||||
// 更新after_mid
|
||||
dispatch(setAfterMid({ mid: data.mid }));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -214,7 +237,7 @@ const NotificationHub = ({ usersVersion = 0, afterMid = 0 }) => {
|
||||
};
|
||||
return null;
|
||||
};
|
||||
// function compareToken(prevHub, nextHub) {
|
||||
// return prevHub.token === nextHub.token;
|
||||
// }
|
||||
export default React.memo(NotificationHub);
|
||||
function compareToken(prevHub, nextHub) {
|
||||
return prevHub.token === nextHub.token;
|
||||
}
|
||||
export default React.memo(NotificationHub, compareToken);
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
// import React from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { clearAuthData } from "../../../app/slices/auth.data";
|
||||
import { toggleSetting } from "../../../app/slices/ui";
|
||||
import { clearAuthData } from "../../../app/slices/auth.data";
|
||||
import { clearMark } from "../../../app/slices/visit.mark";
|
||||
import { clearChannels } from "../../../app/slices/channels";
|
||||
import { clearContacts } from "../../../app/slices/contacts";
|
||||
import { clearChannelMsg } from "../../../app/slices/message.channel";
|
||||
import { clearUserMsg } from "../../../app/slices/message.user";
|
||||
import { clearPendingMsg } from "../../../app/slices/message.pending";
|
||||
// import BASE_URL from "../../app/config";
|
||||
import { useLazyLogoutQuery } from "../../../app/services/auth";
|
||||
import Button from "../styled/Button";
|
||||
import Checkbox from "../styled/Checkbox";
|
||||
const StyledConfirm = styled.div`
|
||||
padding: 32px;
|
||||
filter: drop-shadow(0px 25px 50px rgba(31, 41, 55, 0.25));
|
||||
@@ -20,12 +26,26 @@ const StyledConfirm = styled.div`
|
||||
color: #374151;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.desc {
|
||||
.desc,
|
||||
.clear {
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #6b7280;
|
||||
margin-bottom: 64px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.clear {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
.txt {
|
||||
cursor: pointer;
|
||||
color: orange;
|
||||
margin-right: 12px;
|
||||
}
|
||||
input {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.btns {
|
||||
width: 100%;
|
||||
@@ -37,24 +57,48 @@ const StyledConfirm = styled.div`
|
||||
`;
|
||||
import Modal from "../Modal";
|
||||
export default function LogoutConfirmModal({ closeModal }) {
|
||||
const [clearLocal, setClearLocal] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const [logout, { isLoading, isSuccess }] = useLazyLogoutQuery();
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
};
|
||||
const handleCheck = (evt) => {
|
||||
setClearLocal(evt.target.checked);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
dispatch(toggleSetting());
|
||||
if (clearLocal) {
|
||||
console.log("clear all store");
|
||||
dispatch(clearMark());
|
||||
dispatch(clearChannelMsg());
|
||||
dispatch(clearUserMsg());
|
||||
dispatch(clearChannels());
|
||||
dispatch(clearContacts());
|
||||
dispatch(clearPendingMsg());
|
||||
}
|
||||
dispatch(clearAuthData());
|
||||
// closeModal();
|
||||
dispatch(toggleSetting());
|
||||
navigate("/login");
|
||||
}
|
||||
}, [isSuccess]);
|
||||
}, [isSuccess, clearLocal]);
|
||||
return (
|
||||
<Modal id="modal-modal">
|
||||
<StyledConfirm className="animate__animated animate__fadeInDown animate__faster">
|
||||
<h3 className="title">Log Out</h3>
|
||||
<p className="desc">Are you sure want to log out this account?</p>
|
||||
<div className="clear">
|
||||
<label htmlFor="clear_cb" className="txt">
|
||||
Clear local data
|
||||
</label>
|
||||
<Checkbox
|
||||
name="clear_cb"
|
||||
checked={clearLocal}
|
||||
onChange={handleCheck}
|
||||
/>
|
||||
</div>
|
||||
<div className="btns">
|
||||
<Button onClick={closeModal}>Cancel</Button>
|
||||
<Button onClick={handleLogout} className="danger">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
// import { useSelector } from "react-redux";
|
||||
import { useSelector } from "react-redux";
|
||||
import {
|
||||
useGetServerQuery,
|
||||
useUpdateServerMutation,
|
||||
@@ -66,6 +66,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
`;
|
||||
export default function Overview() {
|
||||
const currUser = useSelector((store) => store.authData.user);
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [values, setValues] = useState(null);
|
||||
const { data, refetch } = useGetServerQuery();
|
||||
@@ -118,28 +119,33 @@ export default function Overview() {
|
||||
|
||||
if (!values) return null;
|
||||
const { name, description, logo } = values;
|
||||
const isAdmin = currUser?.is_admin;
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="logo">
|
||||
<div className="preview">
|
||||
<LogoUploader
|
||||
disabled={!isAdmin}
|
||||
url={uploadSuccess ? `${logo}?t=${new Date().getTime()}` : logo}
|
||||
name={name}
|
||||
uploadImage={uploadLogo}
|
||||
/>
|
||||
</div>
|
||||
<div className="upload">
|
||||
<div className="tip">
|
||||
Minimum size is 128x128, We recommend at least 512x512 for the
|
||||
server. Max size limited to 5M.
|
||||
{isAdmin && (
|
||||
<div className="upload">
|
||||
<div className="tip">
|
||||
Minimum size is 128x128, We recommend at least 512x512 for the
|
||||
server. Max size limited to 5M.
|
||||
</div>
|
||||
<button className="btn">Upload Image</button>
|
||||
</div>
|
||||
<button className="btn">Upload Image</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="inputs">
|
||||
<div className="input">
|
||||
<Label htmlFor="name">Server Name</Label>
|
||||
<Input
|
||||
disabled={!isAdmin}
|
||||
data-type="name"
|
||||
onChange={handleChange}
|
||||
value={name}
|
||||
@@ -151,6 +157,7 @@ export default function Overview() {
|
||||
<div className="input">
|
||||
<Label htmlFor="desc">Server Description</Label>
|
||||
<Textarea
|
||||
disabled={!isAdmin}
|
||||
data-type="description"
|
||||
onChange={handleChange}
|
||||
value={description ?? ""}
|
||||
|
||||
@@ -27,9 +27,9 @@ export default function ChannelChat({
|
||||
const dispatch = useDispatch();
|
||||
const { msgs, users, pendingMsgs } = useSelector((store) => {
|
||||
return {
|
||||
msgs: store.channelMsg[cid] || {},
|
||||
msgs: store.channelMessage[cid] || {},
|
||||
users: store.contacts,
|
||||
pendingMsgs: store.pendingMsg.channel[cid] || {},
|
||||
pendingMsgs: store.pendingMessage.channel[cid] || {},
|
||||
};
|
||||
});
|
||||
const handleClearUnreads = () => {
|
||||
|
||||
@@ -13,8 +13,8 @@ export default function DMChat({ uid = "", dropFiles = [] }) {
|
||||
const contacts = useSelector((store) => store.contacts);
|
||||
const { msgs, pendingMsgs } = useSelector((store) => {
|
||||
return {
|
||||
msgs: store.userMsg[uid] || {},
|
||||
pendingMsgs: store.pendingMsg.user[uid] || {},
|
||||
msgs: store.userMessage[uid] || {},
|
||||
pendingMsgs: store.pendingMessage.user[uid] || {},
|
||||
};
|
||||
});
|
||||
const [currUser, setCurrUser] = useState(null);
|
||||
@@ -31,7 +31,7 @@ export default function DMChat({ uid = "", dropFiles = [] }) {
|
||||
}, [dropFiles]);
|
||||
|
||||
if (!currUser) return null;
|
||||
console.log("user msgs", msgs);
|
||||
// console.log("user msgs", msgs);
|
||||
return (
|
||||
<Layout
|
||||
setDragFiles={setDragFiles}
|
||||
@@ -75,7 +75,7 @@ export default function DMChat({ uid = "", dropFiles = [] }) {
|
||||
})
|
||||
.map(([mid, msg]) => {
|
||||
if (!msg) return null;
|
||||
console.log("user msg", msg);
|
||||
// console.log("user msg", msg);
|
||||
const {
|
||||
from_uid,
|
||||
content,
|
||||
|
||||
@@ -20,17 +20,19 @@ import DMList from "./DMList";
|
||||
export default function ChatPage() {
|
||||
const [channelDropFiles, setChannelDropFiles] = useState([]);
|
||||
const [userDropFiles, setUserDropFiles] = useState([]);
|
||||
const { channels, UserMsgData, ChannelMsgData } = useSelector((store) => {
|
||||
return {
|
||||
channels: store.channels,
|
||||
UserMsgData: store.userMsg,
|
||||
ChannelMsgData: store.channelMsg,
|
||||
};
|
||||
});
|
||||
const { contacts, channels, UserMsgData, ChannelMsgData } = useSelector(
|
||||
(store) => {
|
||||
return {
|
||||
contacts: store.contacts,
|
||||
channels: store.channels,
|
||||
UserMsgData: store.userMessage,
|
||||
ChannelMsgData: store.channelMessage,
|
||||
};
|
||||
}
|
||||
);
|
||||
const [channelModalVisible, setChannelModalVisible] = useState(false);
|
||||
const [contactsModalVisible, setContactsModalVisible] = useState(false);
|
||||
const { channel_id, user_id } = useParams();
|
||||
const contacts = useSelector((store) => store.contacts);
|
||||
const toggleContactsModalVisible = () => {
|
||||
setContactsModalVisible((prev) => !prev);
|
||||
};
|
||||
|
||||
@@ -19,22 +19,14 @@ export default function HomePage() {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
ui: { menuExpand, setting, channelSetting },
|
||||
authData: { usersVersion, afterMid },
|
||||
visitMark: { usersVersion, afterMid },
|
||||
} = useSelector((store) => {
|
||||
return {
|
||||
authData: store.authData,
|
||||
visitMark: store.visitMark,
|
||||
ui: store.ui,
|
||||
contacts: store.contacts,
|
||||
};
|
||||
});
|
||||
const { data, loading, error, success } = usePreload();
|
||||
|
||||
// useEffect(() => {
|
||||
// if (authData) {
|
||||
// dispatch(setAuthData(data));
|
||||
// }
|
||||
// }, [authData]);
|
||||
|
||||
const toggleExpand = () => {
|
||||
dispatch(toggleMenuExpand());
|
||||
};
|
||||
|
||||
@@ -1,38 +1,54 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useGetContactsQuery } from "../../app/services/contact";
|
||||
import { clearAuthData } from "../../app/slices/auth.data";
|
||||
import initCache, { useRehydrate } from "../../app/cache";
|
||||
import { useLazyGetContactsQuery } from "../../app/services/contact";
|
||||
import { clearAuthData, setUserData } from "../../app/slices/auth.data";
|
||||
import { setContacts } from "../../app/slices/contacts";
|
||||
|
||||
// import { useGetChannelsQuery } from "../../app/services/channel";
|
||||
import { useGetServerQuery } from "../../app/services/server";
|
||||
import { useLazyGetServerQuery } from "../../app/services/server";
|
||||
import { KEY_UID } from "../../app/config";
|
||||
// pollingInterval: 0,
|
||||
const querySetting = {
|
||||
refetchOnMountOrArgChange: true,
|
||||
};
|
||||
// const querySetting = {
|
||||
// refetchOnMountOrArgChange: true,
|
||||
// };
|
||||
export default function usePreload() {
|
||||
const { rehydrate, cacheFirst } = useRehydrate();
|
||||
const [checked, setChecked] = useState(false);
|
||||
const loginedUser = useSelector((store) => {
|
||||
return store.authData.user;
|
||||
});
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
isLoading: contactsLoading,
|
||||
isSuccess: contactsSuccess,
|
||||
isError: contactsError,
|
||||
data: contacts,
|
||||
} = useGetContactsQuery(undefined, querySetting);
|
||||
const {
|
||||
isLoading: serverLoading,
|
||||
isSuccess: serverSuccess,
|
||||
isError: serverError,
|
||||
data: server,
|
||||
} = useGetServerQuery(undefined, querySetting);
|
||||
const [
|
||||
getContacts,
|
||||
{
|
||||
isLoading: contactsLoading,
|
||||
isSuccess: contactsSuccess,
|
||||
isError: contactsError,
|
||||
data: contacts,
|
||||
},
|
||||
] = useLazyGetContactsQuery();
|
||||
const [
|
||||
getServer,
|
||||
{
|
||||
isLoading: serverLoading,
|
||||
isSuccess: serverSuccess,
|
||||
isError: serverError,
|
||||
data: server,
|
||||
},
|
||||
] = useLazyGetServerQuery();
|
||||
useEffect(() => {
|
||||
initCache();
|
||||
rehydrate();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
getContacts();
|
||||
getServer();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const local_uid = localStorage.getItem(KEY_UID);
|
||||
if (contacts) {
|
||||
const matchedUser = contacts.find((c) => c.uid == loginedUser.uid);
|
||||
const matchedUser = contacts.find((c) => c.uid == local_uid);
|
||||
if (!matchedUser) {
|
||||
console.log("no matched user, redirect to login");
|
||||
dispatch(clearAuthData());
|
||||
@@ -41,13 +57,14 @@ export default function usePreload() {
|
||||
const markedContacts = contacts.map((u) => {
|
||||
return u.uid == matchedUser.uid ? { ...u, online: true } : u;
|
||||
});
|
||||
dispatch(setUserData(matchedUser));
|
||||
dispatch(setContacts(markedContacts));
|
||||
setChecked(true);
|
||||
}
|
||||
}
|
||||
}, [contacts]);
|
||||
return {
|
||||
loading: contactsLoading || serverLoading || !checked,
|
||||
loading: contactsLoading || serverLoading || !checked || !cacheFirst,
|
||||
error: contactsError && serverError,
|
||||
success: contactsSuccess && serverSuccess,
|
||||
data: {
|
||||
|
||||
+54
-62
@@ -1,72 +1,64 @@
|
||||
// import { useState } from "react";
|
||||
import { Route, Routes, HashRouter } from 'react-router-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Route, Routes, HashRouter } from "react-router-dom";
|
||||
import { Provider } from "react-redux";
|
||||
|
||||
import { persistStore } from 'redux-persist';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
// import { persistStore } from 'redux-persist';
|
||||
// import { PersistGate } from 'redux-persist/integration/react';
|
||||
// import Welcome from './Welcome'
|
||||
import NotFoundPage from './404';
|
||||
import LoginPage from './login';
|
||||
import HomePage from './home';
|
||||
import ChatPage from './chat';
|
||||
import ContactsPage from './contacts';
|
||||
import RequireAuth from '../common/component/RequireAuth';
|
||||
import NotFoundPage from "./404";
|
||||
import LoginPage from "./login";
|
||||
import HomePage from "./home";
|
||||
import ChatPage from "./chat";
|
||||
import ContactsPage from "./contacts";
|
||||
import RequireAuth from "../common/component/RequireAuth";
|
||||
|
||||
import store from '../app/store.with.persist';
|
||||
import InvitePage from './invite';
|
||||
// import getStore from "../app/store";
|
||||
import store from "../app/store";
|
||||
import InvitePage from "./invite";
|
||||
|
||||
const PageRoutes = () => {
|
||||
return (
|
||||
<HashRouter>
|
||||
<Routes>
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/invite" element={<InvitePage />} />
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<HomePage />
|
||||
</RequireAuth>
|
||||
}
|
||||
>
|
||||
<Route index element={<ChatPage />} />
|
||||
<Route path="chat">
|
||||
<Route index element={<ChatPage />} />
|
||||
<Route path="channel/:channel_id" element={<ChatPage />} />
|
||||
<Route path="dm/:user_id" element={<ChatPage />} />
|
||||
</Route>
|
||||
<Route path="contacts">
|
||||
<Route index element={<ContactsPage />} />
|
||||
<Route path=":user_id" element={<ContactsPage />} />
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
</HashRouter>
|
||||
);
|
||||
return (
|
||||
<HashRouter>
|
||||
<Routes>
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/invite" element={<InvitePage />} />
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<HomePage />
|
||||
</RequireAuth>
|
||||
}
|
||||
>
|
||||
<Route index element={<ChatPage />} />
|
||||
<Route path="chat">
|
||||
<Route index element={<ChatPage />} />
|
||||
<Route path="channel/:channel_id" element={<ChatPage />} />
|
||||
<Route path="dm/:user_id" element={<ChatPage />} />
|
||||
</Route>
|
||||
<Route path="contacts">
|
||||
<Route index element={<ContactsPage />} />
|
||||
<Route path=":user_id" element={<ContactsPage />} />
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
</HashRouter>
|
||||
);
|
||||
};
|
||||
// const local_key = "AUTH_DATA";
|
||||
export default function ReduxRoutes() {
|
||||
// const [authData, setAuthData] = useState(
|
||||
// JSON.parse(localStorage.getItem(local_key))
|
||||
// );
|
||||
// const updateAuthData = (data) => {
|
||||
// localStorage.setItem(local_key, JSON.stringify(data));
|
||||
// setAuthData(data);
|
||||
// };
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistStore(store)}>
|
||||
<PageRoutes />
|
||||
</PersistGate>
|
||||
{/* {authData ? (
|
||||
<PersistGate loading={null} persistor={persistStore(getPersistStore())}>
|
||||
<PageRoutes authData={authData} updateAuthData={updateAuthData} />
|
||||
</PersistGate>
|
||||
) : (
|
||||
<PageRoutes authData={authData} updateAuthData={updateAuthData} />
|
||||
)} */}
|
||||
</Provider>
|
||||
);
|
||||
// const [authData, setAuthData] = useState(
|
||||
// JSON.parse(localStorage.getItem(local_key))
|
||||
// );
|
||||
// const updateAuthData = (data) => {
|
||||
// localStorage.setItem(local_key, JSON.stringify(data));
|
||||
// setAuthData(data);
|
||||
// };
|
||||
return (
|
||||
<Provider store={store}>
|
||||
{/* <PersistGate loading={null} persistor={persistStore(store)}> */}
|
||||
<PageRoutes />
|
||||
{/* </PersistGate> */}
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ export default function SolidLoginButton() {
|
||||
|
||||
const handleSolidLogin = () => {
|
||||
getOpenId({
|
||||
issuer_url: "https://solidweb.org",
|
||||
// issuer: "solidweb.org",
|
||||
issuer: "broker.pod.inrupt.com",
|
||||
redirect_uri: `${location.origin}/#/login`,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5399,6 +5399,13 @@ levn@^0.4.1:
|
||||
prelude-ls "^1.2.1"
|
||||
type-check "~0.4.0"
|
||||
|
||||
lie@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
|
||||
integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
|
||||
dependencies:
|
||||
immediate "~3.0.5"
|
||||
|
||||
lie@~3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
|
||||
@@ -5491,6 +5498,13 @@ loader-utils@^3.2.0:
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.0.tgz#bcecc51a7898bee7473d4bc6b845b23af8304d4f"
|
||||
integrity sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==
|
||||
|
||||
localforage@^1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
|
||||
integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==
|
||||
dependencies:
|
||||
lie "3.1.1"
|
||||
|
||||
locate-path@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
|
||||
|
||||
Reference in New Issue
Block a user