完善消息通知

This commit is contained in:
al 2024-12-29 16:20:17 +08:00
parent d1c2866f81
commit 2c75c47467
6 changed files with 214 additions and 115 deletions

29
App.jsx
View File

@ -69,7 +69,7 @@ import * as Clipboard from "expo-clipboard";
import PrivatyModal from "./components/PrivatyModal";
import * as Sentry from "@sentry/react-native";
import { ImageViewerProvider } from "./context/ImageViewProvider";
import WebSocketComponent from "./components/Websocket";
import store from "./store";
import { Provider as ReduxProvider } from "react-redux";
@ -122,7 +122,6 @@ const App = () => {
isSignin: false,
userToken: null,
});
const authContext = useMemo(
() => ({
signIn: async (data, mobilePhone, regionCode) => {
@ -158,6 +157,7 @@ const App = () => {
dispatch({ type: "SIGN_OUT" });
}
},
state,
inviterCode: inviterCode,
checked: checked,
setChecked: setChecked,
@ -228,7 +228,25 @@ const App = () => {
}
prepare();
}, []);
const handleGetWebsocketData = (data) => {
if (data.d.unread_cnt > 0) {
const { n_type, title } = data.d.notif;
Toast.show({
type: "info",
text1: `🔔 收到一条${
n_type === 0
? "系统"
: n_type === 1
? "审核"
: n_type === 2
? "付费"
: "活动"
}通知`,
text2: title,
topOffset: 60,
});
}
};
//
const getClipboardContent = useCallback(async () => {
const content = await Clipboard.getStringAsync();
@ -242,7 +260,6 @@ const App = () => {
}
setInviterCode(null);
}, []);
//
useEffect(() => {
if (!state.isSignin) return;
@ -911,6 +928,10 @@ const App = () => {
setVisible={setIsUpdateModalVisible}
data={versionData}
/>
<WebSocketComponent
getData={handleGetWebsocketData}
state={state}
/>
</SafeAreaProvider>
</ReduxProvider>
</AuthContext.Provider>

View File

@ -2,86 +2,116 @@ import React, { useEffect, useState } from "react";
import { View } from "react-native";
import baseRequest from "../../utils/baseRequest";
import { get } from "../../utils/storeInfo";
import { getNoticeCount } from "../../store/reducer";
import { connect } from "react-redux";
import "text-encoding";
const WebSocketComponent = ({ getData }) => {
var retryInterval = 1000;
const maxInterval = 60000;
let interval = null;
let timer = null;
var messageQueue = [];
let socket = null;
const WebSocketComponent = ({ getData, state, changeCount }) => {
const [messages, setMessages] = useState([]);
useEffect(() => {
let socket = null;
let interval = null;
async function connect_socket() {
const appInfo = await get("app_info");
const base = await baseRequest();
if (socket && socket.readyState === 1) return;
// WebSocket
if (!socket) {
socket = new WebSocket(
`${process.env.EXPO_PUBLIC_WEBSOCKET_URL}/ws?b_mid=${base.b_mid}&b_did=${base.b_did}&b_dt=1&b_token=${base.b_token}&b_ch${appInfo.b_ch}`
);
// console.log("------------++++++++++++++++", socket);
socket.onopen = () => {
console.log("WebSocket connected.");
// socket.send('Hello Server!');
socket.send(JSON.stringify({ t: 1 }));
};
//
socket.onmessage = (event) => {
if (
Object.prototype.toString.call(event.data) ===
"[object ArrayBuffer]"
) {
let enc = new TextDecoder("utf-8");
const view = new Uint8Array(event.data);
let temp = enc.decode(view);
try {
const data = JSON.parse(temp);
if (data.t === 2 && data.msg.ping_interval) {
socket.send("ping");
interval = setInterval(() => {
// ping
socket.send("ping");
// ping
// socket.on("ping", () => {
// socket.senrd("pong");
// });
}, data.msg.ping_interval * 1000);
}
if (data.t === 3) {
getData(data.msg);
setMessages((prevMessages) => [...prevMessages, data]);
}
} catch (error) {}
}
};
//
socket.onclose = () => {
clearInterval(interval);
// connect_socket();
console.log("WebSocket disconnected.");
};
//
socket.onerror = (error) => {
console.error("WebSocket error:", error);
};
}
// ping
// socket.on("ping", () => {
// socket.send("pong");
// });
}
connect_socket();
// WebSocket
return () => {
console.log("卸载了socket");
socket?.readyState === WebSocket.OPEN && socket?.close();
clearInterval(interval);
};
}, []); // effect
useEffect(() => {
socket?.close();
if (state.isSignin) {
connect_socket();
} else {
// changeCount(0);
clearInterval(interval);
clearTimeout(timer);
socket?.close();
socket = null;
}
}, [state]); // effect
async function connect_socket() {
const appInfo = await get("app_info");
const base = await baseRequest();
if (socket && socket.readyState === 1) return;
// WebSocket
socket = new WebSocket(
`${process.env.EXPO_PUBLIC_WEBSOCKET_URL}/ws?b_mid=${base.b_mid}&b_did=${base.b_did}&b_dt=1&b_token=${base.b_token}&b_ch${appInfo.b_ch}`
);
socket.onopen = () => {
console.log("WebSocket connected.");
retryInterval = 1000;
// socket.send('Hello Server!');
if (socket.readyState == WebSocket.OPEN)
socket.send(JSON.stringify({ t: 1 }));
};
//
socket.onmessage = (event) => {
if (
Object.prototype.toString.call(event.data) === "[object ArrayBuffer]"
) {
let enc = new TextDecoder("utf-8");
const view = new Uint8Array(event.data);
let temp = enc.decode(view);
try {
const data = JSON.parse(temp);
if (data.t === 2 && data.msg.ping_interval) {
if (socket.readyState == WebSocket.OPEN) socket.send("ping");
interval = setInterval(() => {
// ping
if (socket.readyState == WebSocket.OPEN) socket.send("ping");
// ping
// socket.on("ping", () => {
// socket.senrd("pong");
// });
}, data.msg.ping_interval * 1000);
}
if (data.t === 3) {
getData(data.msg);
changeCount(data.msg.d.unread_cnt);
setMessages((prevMessages) => [...prevMessages, data]);
}
} catch (error) {}
}
};
//
socket.onclose = async () => {
clearTimeout(timer);
const account = await get("account");
if (state.isSignin && typeof account?.mid === "number") {
timer = setTimeout(() => {
if (!state.isSignin) {
clearTimeout(timer);
return;
}
retryInterval = Math.min(retryInterval * 2, maxInterval);
try {
connect_socket();
} catch (error) {
console.log(error);
}
}, retryInterval);
}
// changeCount(0);
console.log("WebSocket disconnected.");
};
//
socket.onerror = (error) => {
clearTimeout(timer);
socket?.close();
console.error("WebSocket error:", error);
};
// ping
// socket.on("ping", () => {
// socket.send("pong");
// });
}
return (
<View>
{/* <Text style={{ fontSize: 24, fontWeight: 'bold' }}>WebSocket Messages</Text>
@ -94,4 +124,13 @@ const WebSocketComponent = ({ getData }) => {
);
};
export default WebSocketComponent;
const mapDispatchFromProps = (dispatch) => {
return {
changeCount: (num) => {
// dispatchactions
dispatch(getNoticeCount(num));
},
};
};
export default connect(null, mapDispatchFromProps)(WebSocketComponent);

View File

@ -9,18 +9,15 @@ import Posts from "../Posts";
import Space from "../Space";
import { get } from "../../utils/storeInfo";
import CreatePostModal from "../../components/CreatePostModal";
import WebSocketComponent from "../../components/Websocket";
import { connect } from "react-redux";
import Toast from "react-native-toast-message";
import baseRequest from "../../utils/baseRequest";
import { generateSignature } from "../../utils/crypto";
import { useSelector, useDispatch } from "react-redux";
import { getNoticeCount } from "../../store/reducer";
const Tab = createBottomTabNavigator();
export default function HomeTab({ navigation, route }) {
function HomeTab({ changeCount, noticeCount, authInfo }) {
const [isCreatePostTabVisible, setIsCreatePostTabVisible] = useState(false);
const noticeCount = useSelector((state) => state.noticeCount);
const dispatch = useDispatch();
useEffect(() => {
const checkRole = async () => {
const account = await get("account");
@ -51,10 +48,10 @@ export default function HomeTab({ navigation, route }) {
});
return;
}
dispatch(getNoticeCount(_data.data.total));
changeCount(_data.data.total);
};
checkRole();
}, [noticeCount]);
}, [noticeCount, authInfo]);
const CustomTabBarButton = useCallback(({ children }) => {
const [visible, setVisible] = useState(false);
return (
@ -80,26 +77,6 @@ export default function HomeTab({ navigation, route }) {
);
}, []);
const handleGetWebsocketData = (data) => {
if (data.d.unread_cnt > 0) {
dispatch(getNoticeCount(data.d.unread_cnt));
const { n_type, title } = data.d.notif;
Toast.show({
type: "info",
text1: `🔔 收到一条${
n_type === 0
? "系统"
: n_type === 1
? "审核"
: n_type === 2
? "付费"
: "活动"
}通知`,
text2: title,
topOffset: 60,
});
}
};
const EmptyComponent = () => <></>;
return (
@ -281,7 +258,26 @@ export default function HomeTab({ navigation, route }) {
}}
/>
</Tab.Navigator>
<WebSocketComponent getData={handleGetWebsocketData} />
</>
);
}
// store
const mapstateFromProps = (state) => {
// state: store
return {
noticeCount: state.noticeCount,
authInfo: state.authInfo,
};
};
// store
const mapDispatchFromProps = (dispatch) => {
return {
changeCount: (num) => {
// dispatchactions
dispatch(getNoticeCount(num));
},
};
};
export default connect(mapstateFromProps, mapDispatchFromProps)(HomeTab);

View File

@ -19,7 +19,8 @@ import { useDispatch } from "react-redux";
import { getNoticeCount } from "../../../../store/reducer";
import requireAPI from "../../../../utils/requireAPI";
import { Image } from "expo-image";
const MessageList = forwardRef(({ navigation }, ref) => {
import { connect } from "react-redux";
const MessageList = ({ navigation, noticeCount, refInstance }) => {
const dispatch = useDispatch();
const [data, setData] = useState([]);
const [scollNotice, setScollNotice] = useState("");
@ -59,7 +60,7 @@ const MessageList = forwardRef(({ navigation }, ref) => {
icon: require(`../../../../assets/icon/others/m_active.png`),
},
]);
useImperativeHandle(ref, () => ({
useImperativeHandle(refInstance, () => ({
readAllMsg: async (types) => {
try {
const body = {
@ -98,11 +99,9 @@ const MessageList = forwardRef(({ navigation }, ref) => {
},
}));
useEffect(() => {
navigation.addListener("focus", () => {
getData();
getActiveNotice();
});
}, []);
getData();
getActiveNotice();
}, [noticeCount]);
const getData = async () => {
try {
const base = await baseRequest();
@ -339,6 +338,17 @@ const MessageList = forwardRef(({ navigation }, ref) => {
</View>
</View>
);
});
};
export default MessageList;
// store
const mapstateFromProps = (state) => {
// state: store
return {
noticeCount: state.noticeCount,
};
};
const Component = connect(mapstateFromProps, null)(MessageList);
export default forwardRef((props, ref) => (
<Component {...props} refInstance={ref} />
));

View File

@ -25,8 +25,9 @@ import baseRequest from "../../../utils/baseRequest";
import { generateSignature } from "../../../utils/crypto";
import MyModal from "../../../components/MyModal";
import Toast from "react-native-toast-message";
export default function SwitchAccount({ navigation, route }) {
import { handleLogin } from "../../../store/reducer";
import { connect } from "react-redux";
function SwitchAccount({ changeLogin }) {
const blurhash = "LcKUTa%gOYWBYRt6xuoJo~s8V@fk";
const tailwind = useTailwind();
@ -115,6 +116,12 @@ export default function SwitchAccount({ navigation, route }) {
login_time: new Date().getTime(),
});
signIn(_data, data.mobile_phone_origion, data.region_code);
changeLogin({
isSignin: true,
userToken: data.token,
recommendMid: null,
inviter: null,
});
setAccountList((pre) =>
pre.map((item) => ({
...item,
@ -162,6 +169,12 @@ export default function SwitchAccount({ navigation, route }) {
await remove(key);
if (online) {
signOut();
changeLogin({
isSignin: false,
userToken: null,
recommendMid: null,
inviter: null,
});
return;
}
setAccountList((pre) => pre.filter((item) => item.key !== key));
@ -316,3 +329,14 @@ export default function SwitchAccount({ navigation, route }) {
</View>
);
}
const mapDispatchFromProps = (dispatch) => {
return {
changeLogin: (data) => {
// dispatchactions
dispatch(handleLogin(data));
},
};
};
export default connect(null, mapDispatchFromProps)(SwitchAccount);

View File

@ -5,14 +5,23 @@ export const appSlice = createSlice({
initialState: {
noticeCount: 0,
},
authInfo: {
isSignin: false,
userToken: null,
recommendMid: null,
inviter: null,
},
reducers: {
getNoticeCount: (state, { payload }) => {
state.noticeCount = payload;
},
handleLogin: (state, { payload }) => {
state.authInfo = payload;
},
},
});
// Action creators are generated for each case reducer function
export const { getNoticeCount } = appSlice.actions;
export const { getNoticeCount, handleLogin } = appSlice.actions;
export default appSlice.reducer;