diff options
14 files changed, 262 insertions, 190 deletions
diff --git a/FrontEnd/src/app/App.tsx b/FrontEnd/src/app/App.tsx index 5c4b7eb2..39ef78f2 100644 --- a/FrontEnd/src/app/App.tsx +++ b/FrontEnd/src/app/App.tsx @@ -3,8 +3,8 @@ import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; import AppBar from "./views/common/AppBar"; import LoadingPage from "./views/common/LoadingPage"; +import Center from "./views/center"; import Home from "./views/home"; -import HomeV2 from "./views/home-v2"; import Login from "./views/login"; import Settings from "./views/settings"; import About from "./views/about"; @@ -40,13 +40,13 @@ const App: React.FC = () => { <div style={{ height: 56 }} /> <Switch> <Route exact path="/"> - <Home /> + {user == null ? <Home /> : <Center />} </Route> <Route exact path="/home"> <Home /> </Route> - <Route exact path="/home-new"> - <HomeV2 /> + <Route exact path="/center"> + <Center /> </Route> <Route exact path="/login"> <Login /> diff --git a/FrontEnd/src/app/index.sass b/FrontEnd/src/app/index.sass index 6527a65f..6f6bafb3 100644 --- a/FrontEnd/src/app/index.sass +++ b/FrontEnd/src/app/index.sass @@ -3,8 +3,8 @@ @import './views/common/common'
@import './views/common/alert/alert'
+@import './views/center/center'
@import './views/home/home'
-@import './views/home-v2/home-v2'
@import './views/about/about'
@import './views/login/login'
@import './views/settings/settings'
diff --git a/FrontEnd/src/app/views/home/BoardWithUser.tsx b/FrontEnd/src/app/views/center/BoardWithUser.tsx index 3263c745..3263c745 100644 --- a/FrontEnd/src/app/views/home/BoardWithUser.tsx +++ b/FrontEnd/src/app/views/center/BoardWithUser.tsx diff --git a/FrontEnd/src/app/views/home/BoardWithoutUser.tsx b/FrontEnd/src/app/views/center/BoardWithoutUser.tsx index d9c7fcf4..d9c7fcf4 100644 --- a/FrontEnd/src/app/views/home/BoardWithoutUser.tsx +++ b/FrontEnd/src/app/views/center/BoardWithoutUser.tsx diff --git a/FrontEnd/src/app/views/home/TimelineBoard.tsx b/FrontEnd/src/app/views/center/TimelineBoard.tsx index e0511422..e0511422 100644 --- a/FrontEnd/src/app/views/home/TimelineBoard.tsx +++ b/FrontEnd/src/app/views/center/TimelineBoard.tsx diff --git a/FrontEnd/src/app/views/home/TimelineCreateDialog.tsx b/FrontEnd/src/app/views/center/TimelineCreateDialog.tsx index b4e25ba1..b4e25ba1 100644 --- a/FrontEnd/src/app/views/home/TimelineCreateDialog.tsx +++ b/FrontEnd/src/app/views/center/TimelineCreateDialog.tsx diff --git a/FrontEnd/src/app/views/center/center.sass b/FrontEnd/src/app/views/center/center.sass new file mode 100644 index 00000000..4b86f241 --- /dev/null +++ b/FrontEnd/src/app/views/center/center.sass @@ -0,0 +1,36 @@ +.timeline-board
+ @extend .cru-card
+ @extend .d-flex
+ @extend .flex-column
+ @extend .py-3
+ min-height: 200px
+ height: 100%
+ position: relative
+
+.timeline-board-header
+ @extend .px-3
+ display: flex
+ align-items: center
+ justify-content: space-between
+
+.timeline-board-item
+ font-size: 1.1em
+ @extend .px-3
+ height: 48px
+ transition: background 0.3s
+ display: flex
+ align-items: center
+ .icon
+ height: 1.3em
+ color: black
+ @extend .mr-2
+ &:hover
+ background: $gray-300
+ .right
+ display: flex
+ align-items: center
+ flex-shrink: 0
+ .title
+ white-space: nowrap
+ overflow: hidden
+ text-overflow: ellipsis
diff --git a/FrontEnd/src/app/views/center/index.tsx b/FrontEnd/src/app/views/center/index.tsx new file mode 100644 index 00000000..bcf6ad6e --- /dev/null +++ b/FrontEnd/src/app/views/center/index.tsx @@ -0,0 +1,71 @@ +import React from "react"; +import { useHistory } from "react-router"; +import { useTranslation } from "react-i18next"; +import { Row, Container, Button, Col } from "react-bootstrap"; + +import { useUser } from "@/services/user"; +import SearchInput from "../common/SearchInput"; + +import BoardWithoutUser from "./BoardWithoutUser"; +import BoardWithUser from "./BoardWithUser"; +import TimelineCreateDialog from "./TimelineCreateDialog"; + +const HomePage: React.FC = () => { + const history = useHistory(); + + const { t } = useTranslation(); + + const user = useUser(); + + const [navText, setNavText] = React.useState<string>(""); + + const [dialog, setDialog] = React.useState<"create" | null>(null); + + return ( + <> + <Container> + <Row className="my-3 justify-content-center"> + <Col xs={12} sm={8} lg={6}> + <SearchInput + className="justify-content-center" + value={navText} + onChange={setNavText} + onButtonClick={() => { + history.push(`search?q=${navText}`); + }} + additionalButton={ + user != null && ( + <Button + variant="outline-success" + onClick={() => { + setDialog("create"); + }} + > + {t("home.createButton")} + </Button> + ) + } + /> + </Col> + </Row> + {(() => { + if (user == null) { + return <BoardWithoutUser />; + } else { + return <BoardWithUser user={user} />; + } + })()} + </Container> + {dialog === "create" && ( + <TimelineCreateDialog + open + close={() => { + setDialog(null); + }} + /> + )} + </> + ); +}; + +export default HomePage; diff --git a/FrontEnd/src/app/views/home-v2/home-v2.sass b/FrontEnd/src/app/views/home-v2/home-v2.sass deleted file mode 100644 index 56049994..00000000 --- a/FrontEnd/src/app/views/home-v2/home-v2.sass +++ /dev/null @@ -1,29 +0,0 @@ -.home-v2-timeline-list-item
- display: flex
- align-items: center
-
-.home-v2-timeline-list-item-timeline
- transition: background 0.8s
- animation: 0.8s home-v2-timeline-list-item-timeline-enter
- &:hover
- background: $gray-200
-
-@keyframes home-v2-timeline-list-item-timeline-enter
- from
- transform: translate(-100%,0)
- opacity: 0
-
-.home-v2-timeline-list-item-line
- width: 80px
- flex-shrink: 0
-
-@keyframes home-v2-timeline-list-loading-head-animation
- from
- transform: translate(0,-30px)
- opacity: 1
-
- to
- opacity: 0
-
-.home-v2-timeline-list-loading-head
- animation: 1s infinite home-v2-timeline-list-loading-head-animation
diff --git a/FrontEnd/src/app/views/home-v2/index.tsx b/FrontEnd/src/app/views/home-v2/index.tsx deleted file mode 100644 index cb3c1428..00000000 --- a/FrontEnd/src/app/views/home-v2/index.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React from "react"; -import { useHistory } from "react-router"; -import { useTranslation } from "react-i18next"; -import { Container, Button, Row, Col } from "react-bootstrap"; - -import { useUser } from "@/services/user"; -import SearchInput from "../common/SearchInput"; - -import TimelineListView from "./TimelineListView"; -import TimelineCreateDialog from "../home/TimelineCreateDialog"; -import { HttpTimelineInfo } from "@/http/timeline"; -import { getHttpHighlightClient } from "@/http/highlight"; - -const highlightTimelineMessageMap = { - loading: "home.loadingHighlightTimelines", - done: "home.loadedHighlightTimelines", - error: "home.errorHighlightTimelines", -} as const; - -const HomeV2: React.FC = () => { - const history = useHistory(); - - const { t } = useTranslation(); - - const user = useUser(); - - const [navText, setNavText] = React.useState<string>(""); - - const [dialog, setDialog] = React.useState<"create" | null>(null); - - const [highlightTimelineState, setHighlightTimelineState] = React.useState< - "loading" | "done" | "error" - >("loading"); - const [highlightTimelines, setHighlightTimelines] = React.useState< - HttpTimelineInfo[] | undefined - >(); - - React.useEffect(() => { - if (highlightTimelineState === "loading") { - let subscribe = true; - void getHttpHighlightClient() - .list() - .then( - (data) => { - if (subscribe) { - setHighlightTimelineState("done"); - setHighlightTimelines(data); - } - }, - () => { - if (subscribe) { - setHighlightTimelineState("error"); - setHighlightTimelines(undefined); - } - } - ); - return () => { - subscribe = false; - }; - } - }, [highlightTimelineState]); - - return ( - <> - <Container fluid className="px-0"> - <Row className="mx-0 my-3 px-2 justify-content-end"> - <Col xs="12" sm="auto"> - <SearchInput - value={navText} - onChange={setNavText} - onButtonClick={() => { - history.push(`search?q=${navText}`); - }} - additionalButton={ - user != null && ( - <Button - variant="outline-success" - onClick={() => { - setDialog("create"); - }} - > - {t("home.createButton")} - </Button> - ) - } - /> - </Col> - </Row> - <TimelineListView - headerText={highlightTimelineMessageMap[highlightTimelineState]} - timelines={highlightTimelines} - /> - </Container> - {dialog === "create" && ( - <TimelineCreateDialog open close={() => setDialog(null)} /> - )} - </> - ); -}; - -export default HomeV2; diff --git a/FrontEnd/src/app/views/home-v2/TimelineListView.tsx b/FrontEnd/src/app/views/home/TimelineListView.tsx index 9c44a0c2..9c44a0c2 100644 --- a/FrontEnd/src/app/views/home-v2/TimelineListView.tsx +++ b/FrontEnd/src/app/views/home/TimelineListView.tsx diff --git a/FrontEnd/src/app/views/home/WebsiteIntroduction.tsx b/FrontEnd/src/app/views/home/WebsiteIntroduction.tsx new file mode 100644 index 00000000..f4ceebcc --- /dev/null +++ b/FrontEnd/src/app/views/home/WebsiteIntroduction.tsx @@ -0,0 +1,69 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; + +const WebsiteIntroduction: React.FC<{ + className?: string; + style?: React.CSSProperties; +}> = ({ className, style }) => { + const { i18n } = useTranslation(); + + if (i18n.language.startsWith("zh")) { + return ( + <div className={className} style={style}> + <h2>欢迎来到时间线!🎉🎉🎉</h2> + <p> + 本网站由无数个独立的时间线构成,每一个时间线都是一个消息列表,类似于一个聊天软件(比如QQ)。 + </p> + <p> + 如果你拥有一个账号,登陆后你可以自由地在属于你的时间线中发送内容,支持markdown和上传图片哦!你可以创建一个新的时间线来开启一个新的话题。你也可以设置相关权限,只让一部分人能看到时间线的内容。 + </p> + <p> + 如果你没有账号,那么你可以去浏览一下公开的时间线,比如下面这些站长设置的高光时间线。 + </p> + <p> + 鉴于这个网站在我的小型服务器上部署,所以没有开放注册。如果你也想把这个服务部署到自己的服务器上,你可以在关于页面找到一些信息。 + </p> + <p> + <small className="text-secondary"> + 这一段介绍是我的对象抱怨多次我的网站他根本看不明白之后加的,希望你能顺利看懂这个网站的逻辑!😅 + </small> + </p> + </div> + ); + } else { + return ( + <div className={className} style={style}> + <h2>Welcome to Timeline!🎉🎉🎉</h2> + <p> + This website consists of many individual timelines. Each timeline is a + list of messages just like a chat app. + </p> + <p> + If you do have an account, you can post messages, which supports + Markdown and images, in your timelines after logging in. You can also + create a new timeline to open a new topic. You can set the permission + of a timeline to only allow specified people to see the content of the + timeline. + </p> + <p> + If you don't have an account, you can view some public timelines + like highlight timelines below set by website manager. + </p> + <p> + Since this website is hosted on my tiny server, so account registry is + not opened. If you want to host this service on your own server, you + can find some useful information on about page. + </p> + <p> + <small className="text-secondary"> + This introduction is added after my lover complained a lot of times + about the obscuration of my website. May you understand the logic of + it!😅 + </small> + </p> + </div> + ); + } +}; + +export default WebsiteIntroduction; diff --git a/FrontEnd/src/app/views/home/home.sass b/FrontEnd/src/app/views/home/home.sass index 4b86f241..56049994 100644 --- a/FrontEnd/src/app/views/home/home.sass +++ b/FrontEnd/src/app/views/home/home.sass @@ -1,36 +1,29 @@ -.timeline-board
- @extend .cru-card
- @extend .d-flex
- @extend .flex-column
- @extend .py-3
- min-height: 200px
- height: 100%
- position: relative
-
-.timeline-board-header
- @extend .px-3
+.home-v2-timeline-list-item
display: flex
align-items: center
- justify-content: space-between
-.timeline-board-item
- font-size: 1.1em
- @extend .px-3
- height: 48px
- transition: background 0.3s
- display: flex
- align-items: center
- .icon
- height: 1.3em
- color: black
- @extend .mr-2
+.home-v2-timeline-list-item-timeline
+ transition: background 0.8s
+ animation: 0.8s home-v2-timeline-list-item-timeline-enter
&:hover
- background: $gray-300
- .right
- display: flex
- align-items: center
- flex-shrink: 0
- .title
- white-space: nowrap
- overflow: hidden
- text-overflow: ellipsis
+ background: $gray-200
+
+@keyframes home-v2-timeline-list-item-timeline-enter
+ from
+ transform: translate(-100%,0)
+ opacity: 0
+
+.home-v2-timeline-list-item-line
+ width: 80px
+ flex-shrink: 0
+
+@keyframes home-v2-timeline-list-loading-head-animation
+ from
+ transform: translate(0,-30px)
+ opacity: 1
+
+ to
+ opacity: 0
+
+.home-v2-timeline-list-loading-head
+ animation: 1s infinite home-v2-timeline-list-loading-head-animation
diff --git a/FrontEnd/src/app/views/home/index.tsx b/FrontEnd/src/app/views/home/index.tsx index bcf6ad6e..a0df6a5a 100644 --- a/FrontEnd/src/app/views/home/index.tsx +++ b/FrontEnd/src/app/views/home/index.tsx @@ -1,16 +1,25 @@ import React from "react"; import { useHistory } from "react-router"; import { useTranslation } from "react-i18next"; -import { Row, Container, Button, Col } from "react-bootstrap"; +import { Container, Button, Row, Col } from "react-bootstrap"; + +import { HttpTimelineInfo } from "@/http/timeline"; +import { getHttpHighlightClient } from "@/http/highlight"; import { useUser } from "@/services/user"; + import SearchInput from "../common/SearchInput"; +import TimelineCreateDialog from "../center/TimelineCreateDialog"; +import TimelineListView from "./TimelineListView"; +import WebsiteIntroduction from "./WebsiteIntroduction"; -import BoardWithoutUser from "./BoardWithoutUser"; -import BoardWithUser from "./BoardWithUser"; -import TimelineCreateDialog from "./TimelineCreateDialog"; +const highlightTimelineMessageMap = { + loading: "home.loadingHighlightTimelines", + done: "home.loadedHighlightTimelines", + error: "home.errorHighlightTimelines", +} as const; -const HomePage: React.FC = () => { +const HomeV2: React.FC = () => { const history = useHistory(); const { t } = useTranslation(); @@ -21,13 +30,44 @@ const HomePage: React.FC = () => { const [dialog, setDialog] = React.useState<"create" | null>(null); + const [highlightTimelineState, setHighlightTimelineState] = React.useState< + "loading" | "done" | "error" + >("loading"); + const [highlightTimelines, setHighlightTimelines] = React.useState< + HttpTimelineInfo[] | undefined + >(); + + React.useEffect(() => { + if (highlightTimelineState === "loading") { + let subscribe = true; + void getHttpHighlightClient() + .list() + .then( + (data) => { + if (subscribe) { + setHighlightTimelineState("done"); + setHighlightTimelines(data); + } + }, + () => { + if (subscribe) { + setHighlightTimelineState("error"); + setHighlightTimelines(undefined); + } + } + ); + return () => { + subscribe = false; + }; + } + }, [highlightTimelineState]); + return ( <> - <Container> - <Row className="my-3 justify-content-center"> - <Col xs={12} sm={8} lg={6}> + <Container fluid className="px-0"> + <Row className="mx-0 my-3 px-2 justify-content-end"> + <Col xs="12" sm="auto"> <SearchInput - className="justify-content-center" value={navText} onChange={setNavText} onButtonClick={() => { @@ -48,24 +88,17 @@ const HomePage: React.FC = () => { /> </Col> </Row> - {(() => { - if (user == null) { - return <BoardWithoutUser />; - } else { - return <BoardWithUser user={user} />; - } - })()} + <WebsiteIntroduction className="p-2" /> + <TimelineListView + headerText={highlightTimelineMessageMap[highlightTimelineState]} + timelines={highlightTimelines} + /> </Container> {dialog === "create" && ( - <TimelineCreateDialog - open - close={() => { - setDialog(null); - }} - /> + <TimelineCreateDialog open close={() => setDialog(null)} /> )} </> ); }; -export default HomePage; +export default HomeV2; |