From 8643e5086ab5e522ff48c0187af94646bf2e3aa2 Mon Sep 17 00:00:00 2001 From: Tristan Yang Date: Mon, 29 Aug 2022 17:53:45 +0800 Subject: [PATCH] feat: add guest routes --- src/routes/guest/BlankPlaceholder.tsx | 20 +++ src/routes/guest/ChannelChat/index.tsx | 78 +++++++++++ src/routes/guest/ChannelChat/styled.tsx | 51 ++++++++ src/routes/guest/SessionList/Session.tsx | 63 +++++++++ src/routes/guest/SessionList/index.tsx | 54 ++++++++ src/routes/guest/SessionList/styled.tsx | 95 ++++++++++++++ src/routes/guest/index.tsx | 65 +++++++++ src/routes/guest/styled.tsx | 160 +++++++++++++++++++++++ src/routes/index.tsx | 7 + 9 files changed, 593 insertions(+) create mode 100644 src/routes/guest/BlankPlaceholder.tsx create mode 100644 src/routes/guest/ChannelChat/index.tsx create mode 100644 src/routes/guest/ChannelChat/styled.tsx create mode 100644 src/routes/guest/SessionList/Session.tsx create mode 100644 src/routes/guest/SessionList/index.tsx create mode 100644 src/routes/guest/SessionList/styled.tsx create mode 100644 src/routes/guest/index.tsx create mode 100644 src/routes/guest/styled.tsx diff --git a/src/routes/guest/BlankPlaceholder.tsx b/src/routes/guest/BlankPlaceholder.tsx new file mode 100644 index 00000000..ea2c6c86 --- /dev/null +++ b/src/routes/guest/BlankPlaceholder.tsx @@ -0,0 +1,20 @@ +// import React from "react"; +import styled from "styled-components"; +const Styled = styled.div` + display: flex; + align-items: center; + font-size: 28px; + font-weight: bold; + color: #ccc; + padding: 50px; + height: calc(100vh - 24px); + text-transform: uppercase; + letter-spacing: 2px; +`; +// type Props = {}; + +const BlankPlaceholder = () => { + return Guest Mode; +}; + +export default BlankPlaceholder; diff --git a/src/routes/guest/ChannelChat/index.tsx b/src/routes/guest/ChannelChat/index.tsx new file mode 100644 index 00000000..a04d2a78 --- /dev/null +++ b/src/routes/guest/ChannelChat/index.tsx @@ -0,0 +1,78 @@ +// import { useState, useEffect } from "react"; +// import { NavLink } from "react-router-dom"; +import useMessageFeed from "../../../common/hook/useMessageFeed"; +import ChannelIcon from "../../../common/component/ChannelIcon"; +import Layout from "../../chat/Layout"; +import { renderMessageFragment } from "../../chat/utils"; +import { StyledChannelChat, StyledHeader } from "./styled"; +import LoadMore from "../../chat/LoadMore"; +import { useAppSelector } from "../../../app/store"; +type Props = { + cid?: number; +}; +export default function GuestChannelChat({ cid = 0 }: Props) { + const { + list: msgIds, + appends, + hasMore, + pullUp + } = useMessageFeed({ + context: "channel", + id: cid + }); + const { data, messageData } = useAppSelector((store) => { + return { + footprint: store.footprint, + data: store.channels.byId[cid], + messageData: store.message || {} + }; + }); + if (!data) return null; + const { name, description, is_public } = data; + console.log("channel message list", msgIds); + const feeds = [...msgIds, ...appends]; + return ( + <> + +
+ + {name} + {description} +
+ + } + > + + {hasMore ? ( + + ) : ( +
+

Welcome to #{name} !

+

This is the start of the #{name} channel.

+
+ )} + {/*
*/} + {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 */} + + } /> + } /> +