*/}
+ {feeds.map((mid, idx) => {
+ const curr = messageData[mid];
+ if (!curr) return null;
+ const isFirst = idx == 0;
+ const prev = isFirst ? null : messageData[feeds[idx - 1]];
+ return renderMessageFragment({
+ readonly: true,
+ selectMode: false,
+ prev,
+ curr,
+ contextId: cid,
+ context: "channel"
+ });
+ })}
+
+
+ >
+ );
+}
diff --git a/src/routes/guest/ChannelChat/styled.tsx b/src/routes/guest/ChannelChat/styled.tsx
new file mode 100644
index 00000000..d8691476
--- /dev/null
+++ b/src/routes/guest/ChannelChat/styled.tsx
@@ -0,0 +1,51 @@
+import styled from "styled-components";
+export const StyledHeader = styled.header`
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ .txt {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ .title {
+ font-size: 16px;
+ line-height: 24px;
+ color: #1c1c1e;
+ }
+ .desc {
+ margin-left: 8px;
+ font-weight: normal;
+ font-size: 16px;
+ line-height: 24px;
+ color: #616161;
+ }
+ }
+`;
+export const StyledChannelChat = styled.article`
+ padding: 18px 16px;
+ width: 100%;
+ height: 100%;
+ height: -webkit-fill-available;
+ overflow-x: hidden;
+ overflow-y: auto;
+ overflow-anchor: auto;
+ > .info {
+ padding-top: 62px;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 8px;
+ .title {
+ font-weight: bold;
+ font-size: 36px;
+ line-height: 44px;
+ }
+ .desc {
+ color: #78787c;
+ font-weight: 500;
+ font-size: 16px;
+ line-height: 24px;
+ }
+ }
+`;
diff --git a/src/routes/guest/SessionList/Session.tsx b/src/routes/guest/SessionList/Session.tsx
new file mode 100644
index 00000000..09c4375e
--- /dev/null
+++ b/src/routes/guest/SessionList/Session.tsx
@@ -0,0 +1,63 @@
+// @ts-nocheck
+import { useState, useEffect, FC } from "react";
+import dayjs from "dayjs";
+import { renderPreviewMessage } from "../../chat/utils";
+import Avatar from "../../../common/component/Avatar";
+import IconLock from "../../../assets/icons/lock.svg";
+import { NavLink } from "react-router-dom";
+import { useAppSelector } from "../../../app/store";
+
+interface IProps {
+ id: number;
+ mid: number;
+}
+const Session: FC
= ({ id, mid }) => {
+ const [data, setData] = useState<{
+ name: string;
+ icon: string;
+ mid: number;
+ is_public: boolean;
+ }>();
+ const { messageData, userData, channelData } = useAppSelector((store) => {
+ return {
+ messageData: store.message,
+ userData: store.users.byId,
+ channelData: store.channels.byId
+ };
+ });
+
+ useEffect(() => {
+ const tmp = channelData[id];
+ if (!tmp) return;
+ // channel
+ const { name, icon = "", is_public } = tmp;
+ setData({ name, icon, mid, is_public });
+ }, [id, mid, userData, channelData]);
+ if (!data) return null;
+ const previewMsg = messageData[mid] || {};
+ const { name, icon, is_public } = data;
+
+ return (
+
+
+
+
+
+
+ {name} {!is_public && }
+
+
+ {previewMsg.created_at ? dayjs(previewMsg.created_at).fromNow() : null}
+
+
+
+ {renderPreviewMessage(previewMsg)}
+
+
+
+
+ );
+};
+export default Session;
diff --git a/src/routes/guest/SessionList/index.tsx b/src/routes/guest/SessionList/index.tsx
new file mode 100644
index 00000000..647b736b
--- /dev/null
+++ b/src/routes/guest/SessionList/index.tsx
@@ -0,0 +1,54 @@
+import { FC } from "react";
+import { useState, useEffect } from "react";
+import Styled from "./styled";
+import Session from "./Session";
+import { useAppSelector } from "../../../app/store";
+export interface ChatSession {
+ key: string;
+ type: "user" | "channel";
+ id: number;
+ mid?: number;
+ unread?: number;
+}
+type Props = {};
+const SessionList: FC = () => {
+ const [sessions, setSessions] = useState([]);
+ const { channelIDs, readChannels, readUsers, channelMessage, userMessage, loginUid } =
+ useAppSelector((store) => {
+ return {
+ loginUid: store.authData.user?.uid,
+ channelIDs: store.channels.ids,
+ userMessage: store.userMessage.byId,
+ channelMessage: store.channelMessage,
+ readChannels: store.footprint.readChannels,
+ readUsers: store.footprint.readUsers
+ };
+ });
+
+ useEffect(() => {
+ const cSessions = channelIDs.map((id) => {
+ const mids = channelMessage[id];
+ if (!mids || mids.length == 0) {
+ return { key: `channel_${id}`, unreads: 0, id, type: "channel" };
+ }
+ const mid = [...mids].pop();
+ return { key: `channel_${id}`, id, mid, type: "channel" };
+ });
+ const tmps = [...(cSessions as ChatSession[])].sort((a, b) => {
+ const { mid: aMid = 0 } = a;
+ const { mid: bMid = 0 } = b;
+ return bMid - aMid;
+ });
+ setSessions(tmps);
+ }, [channelIDs, channelMessage, readChannels, readUsers, loginUid, userMessage]);
+
+ return (
+
+ {sessions.map((s) => {
+ const { key, id, mid = 0 } = s;
+ return ;
+ })}
+
+ );
+};
+export default SessionList;
diff --git a/src/routes/guest/SessionList/styled.tsx b/src/routes/guest/SessionList/styled.tsx
new file mode 100644
index 00000000..90b9abfd
--- /dev/null
+++ b/src/routes/guest/SessionList/styled.tsx
@@ -0,0 +1,95 @@
+import styled from "styled-components";
+const Styled = styled.ul`
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ padding: 8px;
+ height: calc(100vh - 56px - 16px - 8px);
+ overflow: auto;
+ > .session {
+ > a {
+ display: flex;
+ gap: 8px;
+ border-radius: 8px;
+ padding: 8px;
+ width: 100%;
+ &.active,
+ &:hover {
+ background: rgba(116, 127, 141, 0.2);
+ }
+ .icon {
+ display: flex;
+ background-color: #eee;
+ border-radius: 50%;
+ img {
+ width: 40px;
+ height: 40px;
+ &.channel_default {
+ padding: 5px;
+ }
+ }
+ }
+ .details {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ .up {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ .name {
+ font-weight: 600;
+ font-size: 14px;
+ line-height: 20px;
+ color: #52525b;
+ max-width: 112px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ &.only_title {
+ max-width: 190px;
+ }
+ }
+ .time {
+ font-weight: 500;
+ font-size: 12px;
+ line-height: 18px;
+ color: #78787c;
+ white-space: nowrap;
+ overflow: hidden;
+ max-width: 80px;
+ text-overflow: ellipsis;
+ }
+ }
+ .down {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ > .msg {
+ font-weight: 400;
+ font-size: 12px;
+ line-height: 18px;
+ color: #78787c;
+ white-space: nowrap;
+ overflow: hidden;
+ width: 140px;
+ text-overflow: ellipsis;
+ }
+ }
+ }
+ &.muted {
+ .up .name,
+ .up .time,
+ .down .msg {
+ color: #d0d5dd;
+ }
+ .down .badge {
+ background: #bfbfbf;
+ }
+ }
+ }
+ }
+`;
+
+export default Styled;
diff --git a/src/routes/guest/index.tsx b/src/routes/guest/index.tsx
new file mode 100644
index 00000000..71e5ccda
--- /dev/null
+++ b/src/routes/guest/index.tsx
@@ -0,0 +1,65 @@
+// import React from 'react';
+import { NavLink, useParams } from "react-router-dom";
+import StyledWrapper from "./styled";
+import Loading from "../../common/component/Loading";
+import Manifest from "../../common/component/Manifest";
+import Server from "../../common/component/Server";
+import BlankPlaceholder from "./BlankPlaceholder";
+import { useAppSelector } from "../../app/store";
+import usePreload from "../../common/hook/usePreload";
+import SessionList from "./SessionList";
+import ChannelChat from "./ChannelChat";
+// const routes = ["/setting", "/setting/channel/:cid"];
+export default function GuestHomePage() {
+ const { cid = 0 } = useParams();
+ const placeholderVisible = cid == 0;
+ const {
+ ui: { ready }
+ } = useAppSelector((store) => {
+ return {
+ ui: store.ui
+ };
+ });
+ const { loading } = usePreload();
+ if (loading || !ready) {
+ return ;
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+ {placeholderVisible && }
+ {cid !== 0 && }
+
+
+ >
+ );
+}
+
+// import Server from "../../common/component/Server";
+// import ChannelChat from "./ChannelChat";
+// import SessionList from "./SessionList";
+// export default function ChatPage() {
+// const { channel_id = 0 } = useParams();
+// const placeholderVisible = channel_id == 0 ;
+// return (
+// <>
+//
+//
+//
+//
+//
+//
+// {placeholderVisible && }
+// {channel_id !== 0 && }
+//
+//
+// >
+// );
+// }
diff --git a/src/routes/guest/styled.tsx b/src/routes/guest/styled.tsx
new file mode 100644
index 00000000..8ac5d64d
--- /dev/null
+++ b/src/routes/guest/styled.tsx
@@ -0,0 +1,160 @@
+import styled from "styled-components";
+const StyledWrapper = styled.div`
+ display: flex;
+ height: 100%;
+ padding: 8px;
+ background: var(---navs-bg);
+ > .left {
+ background-color: #fff;
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ min-width: 268px;
+ box-shadow: inset -1px 0px 0px rgba(0, 0, 0, 0.05);
+ height: 100%;
+ overflow: auto;
+ border-radius: 16px 0 0 16px;
+ .list {
+ margin: 12px 8px;
+ .title {
+ padding: 0 8px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 4px;
+ cursor: pointer;
+ > .txt {
+ user-select: none;
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ font-weight: bold;
+ font-size: 12px;
+ line-height: 20px;
+ color: #78787c;
+ }
+ .icon {
+ transition: transform 0.5s ease;
+ transform-origin: center;
+ }
+ .add_icon {
+ width: 18px;
+ height: 18px;
+ }
+ }
+ > .nav {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ a {
+ text-decoration: none;
+ }
+ .session {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ padding: 4px 8px;
+ border-radius: 4px;
+ &:hover,
+ &.active {
+ background: rgba(116, 127, 141, 0.1);
+ }
+
+ .avatar {
+ /* todo */
+ }
+ .details {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ .up {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ .name {
+ font-weight: 600;
+ font-size: 14px;
+ line-height: 20px;
+ color: #52525b;
+ max-width: 112px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ time {
+ white-space: nowrap;
+ font-weight: 500;
+ font-size: 12px;
+ line-height: 18px;
+ color: #78787c;
+ }
+ }
+ .down {
+ display: flex;
+ justify-content: space-between;
+ .msg {
+ min-height: 18px;
+ font-weight: normal;
+ font-size: 12px;
+ line-height: 18px;
+ color: #78787c;
+ white-space: nowrap;
+ overflow: hidden;
+ width: 140px;
+ text-overflow: ellipsis;
+ }
+ > .badge {
+ color: #fff;
+ height: 20px;
+ min-width: 20px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 10px;
+ background: #1fe1f9;
+ font-weight: 900;
+ font-size: 10px;
+ line-height: 10px;
+ &.dot {
+ min-width: unset;
+ width: 6px;
+ height: 6px;
+ padding: 0;
+ }
+ &.mute {
+ background: #bfbfbf;
+ }
+ }
+ }
+ }
+ }
+ /* drop files effect */
+ .drop_over {
+ box-shadow: inset 0 0 0 2px #52edff;
+ }
+ }
+ &.collapse {
+ .title .icon {
+ transform: rotate(-90deg);
+ }
+ > .nav > .link:not(.active) {
+ display: none;
+ }
+ }
+ }
+ }
+ > .right {
+ border-radius: 0 16px 16px 0;
+ width: 100%;
+ &.placeholder {
+ background-color: #fff;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+ }
+`;
+
+export default StyledWrapper;
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
index 8b69e0ac..8498ee4e 100644
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -23,6 +23,8 @@ import RequireAuth from "../common/component/RequireAuth";
import RequireNoAuth from "../common/component/RequireNoAuth";
import Meta from "../common/component/Meta";
import HomePage from "./home";
+import GeustPage from "./guest";
+// import GuestChannelChatPage from "./guest/ChannelChat";
import ChatPage from "./chat";
import Loading from "../common/component/Loading";
import store, { useAppSelector } from "../app/store";
@@ -89,6 +91,11 @@ const PageRoutes = () => {
/>
} />
} />
+ {/* guest mode */}
+
+ } />
+ } />
+