From 9f03438f0b7f27ae3073ffe5e794d65e20f79ec6 Mon Sep 17 00:00:00 2001
From: tgsmdww <389007832@qq.com>
Date: Sun, 23 Dec 2018 20:40:41 +0800
Subject: [PATCH 01/13] feat: favorite topic
---
src/@types/@cc98/IConfig.d.ts | 2 +-
src/@types/@cc98/ITopic.d.ts | 4 +++
src/pages/Topic/FixButtons.tsx | 41 ++++++++++++++++++++++++++++
src/pages/Topic/PostItem/Actions.tsx | 2 +-
src/services/post.ts | 16 ++++++++++-
src/services/topic.ts | 7 +++++
src/services/utils/errorHandler.ts | 4 +++
7 files changed, 73 insertions(+), 3 deletions(-)
diff --git a/src/@types/@cc98/IConfig.d.ts b/src/@types/@cc98/IConfig.d.ts
index 0531c7d..7a5663e 100644
--- a/src/@types/@cc98/IConfig.d.ts
+++ b/src/@types/@cc98/IConfig.d.ts
@@ -37,7 +37,7 @@ declare module '@cc98/api' {
/**
* 推荐功能
*/
- recommendationFunction: Array
+ recommendationFunction: any[]
/**
* 推荐阅读
*/
diff --git a/src/@types/@cc98/ITopic.d.ts b/src/@types/@cc98/ITopic.d.ts
index b6bb493..64f26df 100644
--- a/src/@types/@cc98/ITopic.d.ts
+++ b/src/@types/@cc98/ITopic.d.ts
@@ -85,5 +85,9 @@ declare module '@cc98/api' {
tag2: number
isInternalOnly: boolean
+ /**
+ * 是否收藏
+ */
+ isFavorite: boolean
}
}
diff --git a/src/pages/Topic/FixButtons.tsx b/src/pages/Topic/FixButtons.tsx
index 1e41cb8..bc2c681 100644
--- a/src/pages/Topic/FixButtons.tsx
+++ b/src/pages/Topic/FixButtons.tsx
@@ -7,9 +7,16 @@ import SwapVertIcon from '@material-ui/icons/SwapVert'
import EditIcon from '@material-ui/icons/Edit'
import AddIcon from '@material-ui/icons/Add'
import RemoveIcon from '@material-ui/icons/Remove'
+import FavoriteIcon from '@material-ui/icons/Favorite'
+import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder'
import { navigate } from '@/utils/history'
import { ITopic } from '@cc98/api'
+import { putFavorite, deleteFavorite } from '@/services/post'
+import { getTopicFavorite } from '../../services/topic'
+import useFetcher from '@/hooks/useFetcher'
+import { favoriteHandler } from '../../services/utils/errorHandler'
+import snackbar from '@/utils/snackbar'
interface Props {
/**
@@ -29,11 +36,45 @@ interface Props {
export default ({ topicInfo, isReverse, refreshFunc }: Props) => {
// 控制按钮是否展开
const [expand, setExpand] = useState(false)
+ const [isFavorite, setIsFavorite] = useFetcher(() => getTopicFavorite(topicInfo.id))
+
+ const toggleFavorite = async () => {
+ if (isFavorite) {
+ const res = await deleteFavorite(topicInfo.id)
+ res.fail(favoriteHandler).succeed(() => {
+ snackbar.success('取消关注')
+ setIsFavorite(false)
+ })
+ } else {
+ const res = await putFavorite(topicInfo.id)
+ res.fail(favoriteHandler).succeed(() => {
+ snackbar.success('关注成功')
+ setIsFavorite(true)
+ })
+ }
+ }
return (
<>
{expand && (
<>
+
+ {isFavorite ? (
+ {
+ toggleFavorite()
+ setExpand(false)
+ }}
+ />
+ ) : (
+ {
+ toggleFavorite()
+ setExpand(false)
+ }}
+ />
+ )}
+
diff --git a/src/pages/Topic/PostItem/Actions.tsx b/src/pages/Topic/PostItem/Actions.tsx
index 983ce28..ad1a5da 100644
--- a/src/pages/Topic/PostItem/Actions.tsx
+++ b/src/pages/Topic/PostItem/Actions.tsx
@@ -194,7 +194,7 @@ const MoreActions = ({ postInfo, isTrace, refreshPost, userInfo }: Props) => {
)
},
})
- const board = childBoards.filter(b => b.id === postInfo.boardId)[0]
+ const board = childBoards.find(b => b.id === postInfo.boardId)
function isManager() {
// 本人是管理员允许修改任何帖子
diff --git a/src/services/post.ts b/src/services/post.ts
index f12e0b4..9b43a93 100644
--- a/src/services/post.ts
+++ b/src/services/post.ts
@@ -1,4 +1,4 @@
-import { GET, PUT } from '@/utils/fetch'
+import { GET, PUT, DELETE } from '@/utils/fetch'
import { IPost, ILike } from '@cc98/api'
/**
@@ -129,3 +129,17 @@ export function rate(id: number, value: 1 | -1, reason: string) {
},
})
}
+
+/**
+ * 收藏帖子
+ */
+export function putFavorite(topicId: number) {
+ return PUT(`me/favorite/${topicId}`)
+}
+
+/**
+ * 取消收藏帖子
+ */
+export function deleteFavorite(topicId: number) {
+ return DELETE(`me/favorite/${topicId}`)
+}
diff --git a/src/services/topic.ts b/src/services/topic.ts
index e6b36de..2c591f0 100644
--- a/src/services/topic.ts
+++ b/src/services/topic.ts
@@ -158,3 +158,10 @@ export function getMyRecentTopics(from: number) {
},
})
}
+
+/**
+ * 获取帖子是否收藏
+ */
+export function getTopicFavorite(id: number | string) {
+ return GET(`topic/${id}/isfavorite`)
+}
diff --git a/src/services/utils/errorHandler.ts b/src/services/utils/errorHandler.ts
index 130ca37..0ddd6d2 100644
--- a/src/services/utils/errorHandler.ts
+++ b/src/services/utils/errorHandler.ts
@@ -60,3 +60,7 @@ export function manageHandler(err: FetchError) {
snackbar.error('服务器内部错误')
}
}
+
+export function favoriteHandler(err: FetchError) {
+ snackbar.error('操作失败')
+}
From ffaf65d1589ef6ecf545362951b55707256e581c Mon Sep 17 00:00:00 2001
From: tgsmdww <389007832@qq.com>
Date: Sun, 23 Dec 2018 20:58:28 +0800
Subject: [PATCH 02/13] feat: add favorite entrance
---
src/pages/MyFollow/index.tsx | 4 +++-
src/services/topic.ts | 12 ++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/pages/MyFollow/index.tsx b/src/pages/MyFollow/index.tsx
index 1404bfd..e9fbe6b 100644
--- a/src/pages/MyFollow/index.tsx
+++ b/src/pages/MyFollow/index.tsx
@@ -4,7 +4,7 @@ import { InfTopicList } from '@/components/TopicList'
import { Tab, Tabs } from '@material-ui/core'
-import { getFollowBoardsTopics, getFollowUsersTopics } from '@/services/topic'
+import { getFollowBoardsTopics, getFollowUsersTopics, getFavoriteTopics } from '@/services/topic'
export default () => {
const [current, setCurrent] = useState('board')
@@ -24,10 +24,12 @@ export default () => {
>
+
{current === 'board' && }
{current === 'user' && }
+ {current === 'topic' && }
>
)
}
diff --git a/src/services/topic.ts b/src/services/topic.ts
index 2c591f0..aecec7b 100644
--- a/src/services/topic.ts
+++ b/src/services/topic.ts
@@ -94,6 +94,18 @@ export function getFollowUsersTopics(from: number) {
})
}
+/**
+ * 获取收藏的帖子
+ */
+export function getFavoriteTopics(from: number) {
+ return GET('topic/me/favorite', {
+ params: {
+ from,
+ size: 20,
+ },
+ })
+}
+
/**
* 搜索
*/
From bf39eeaec48dd42d9a1721bc50e1ab4d8a6348e2 Mon Sep 17 00:00:00 2001
From: tgsmdww <389007832@qq.com>
Date: Mon, 24 Dec 2018 20:38:17 +0800
Subject: [PATCH 03/13] feat: add topic menu; fix post action's judge
---
src/pages/Help/About/index.tsx | 112 ---------------------------
src/pages/Topic/Actions.tsx | 83 ++++++++++++++++++++
src/pages/Topic/FixButtons.tsx | 41 ----------
src/pages/Topic/PostHead.tsx | 12 ++-
src/pages/Topic/PostItem/Actions.tsx | 22 +++++-
src/services/utils/errorHandler.ts | 6 +-
src/version.ts | 2 +-
7 files changed, 118 insertions(+), 160 deletions(-)
delete mode 100644 src/pages/Help/About/index.tsx
create mode 100644 src/pages/Topic/Actions.tsx
diff --git a/src/pages/Help/About/index.tsx b/src/pages/Help/About/index.tsx
deleted file mode 100644
index d84b3d1..0000000
--- a/src/pages/Help/About/index.tsx
+++ /dev/null
@@ -1,112 +0,0 @@
-import React from 'react'
-import styled from 'styled-components'
-
-import useFetcher from '@/hooks/useFetcher'
-
-import { navigate } from '@/utils/history'
-
-import {
- Table,
- TableRow,
- TableBody,
- TableCell,
- Avatar,
- CardHeader,
- Divider,
- Typography,
-} from '@material-ui/core'
-
-import { getSiteInfo } from '@/services/global'
-
-const Title = styled(Typography).attrs({
- align: 'center',
- variant: 'h6',
-})`
- && {
- margin-top: 16px;
- margin-bottom: 16px;
- }
-`
-
-const SiteInfo = () => {
- const [info] = useFetcher(getSiteInfo)
-
- if (info === null) {
- return null
- }
-
- const rows = [
- { name: '今日帖数', data: info.todayCount },
- { name: '论坛总主题数', data: info.maxPostCount },
- { name: '论坛总回复数', data: info.postCount },
- { name: '总用户数', data: info.userCount },
- { name: '最新加入用户', data: info.lastUserName },
- ]
-
- return (
- <>
- 论坛统计
-
-
-
-
- {rows.map(row => (
-
- {row.name}
- {row.data}
-
- ))}
-
-
- >
- )
-}
-
-interface DevCardProps {
- name: string
- description: string
- userId: number
-}
-
-const CardHeaderS = styled(CardHeader)`
- && {
- width: 48%;
- }
-`
-
-const DevCard = ({ name, description, userId }: DevCardProps) => (
- }
- title={name}
- subheader={description}
- onClick={() => navigate(`/user/${userId}`)}
- />
-)
-
-const CardFlexDiv = styled.div`
- display: flex;
- flex-wrap: wrap;
-`
-
-const DevTeam = () => (
- <>
- 开发组
-
-
-
-
-
-
-
-
-
-
- >
-)
-
-export default () => (
- <>
-
-
- >
-)
diff --git a/src/pages/Topic/Actions.tsx b/src/pages/Topic/Actions.tsx
new file mode 100644
index 0000000..1b6537b
--- /dev/null
+++ b/src/pages/Topic/Actions.tsx
@@ -0,0 +1,83 @@
+import React, { useState } from 'react'
+
+import useFetcher from '@/hooks/useFetcher'
+
+import { IconButton, Menu, MenuItem, ListItemIcon, Typography } from '@material-ui/core'
+import MoreVertIcon from '@material-ui/icons/MoreVert'
+import FavoriteIcon from '@material-ui/icons/Favorite'
+import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder'
+
+import snackbar from '@/utils/snackbar'
+import { ITopic } from '@cc98/api'
+
+import { getTopicFavorite } from '@/services/topic'
+import { favoriteHandler } from '@/services/utils/errorHandler'
+import { putFavorite, deleteFavorite } from '@/services/post'
+
+interface Props {
+ /**
+ * 帖子信息
+ */
+ topicInfo: ITopic
+}
+
+export default ({ topicInfo }: Props) => {
+ const [anchorEl, setAnchorEl] = useState(null)
+ const [isFavorite, setIsFavorite] = useFetcher(() => getTopicFavorite(topicInfo.id))
+
+ const handleOpen = (event: React.MouseEvent) => {
+ setAnchorEl(event.currentTarget)
+ }
+
+ const handleClose = () => {
+ setAnchorEl(null)
+ }
+
+ const handleFavorite = async () => {
+ if (isFavorite) {
+ const res = await deleteFavorite(topicInfo.id)
+ res.fail(favoriteHandler).succeed(() => {
+ snackbar.success('取消收藏')
+ setIsFavorite(false)
+ })
+ } else {
+ const res = await putFavorite(topicInfo.id)
+ res.fail(favoriteHandler).succeed(() => {
+ snackbar.success('收藏成功')
+ setIsFavorite(true)
+ })
+ }
+ }
+
+ return (
+ <>
+
+
+
+
+ >
+ )
+}
diff --git a/src/pages/Topic/FixButtons.tsx b/src/pages/Topic/FixButtons.tsx
index bc2c681..1e41cb8 100644
--- a/src/pages/Topic/FixButtons.tsx
+++ b/src/pages/Topic/FixButtons.tsx
@@ -7,16 +7,9 @@ import SwapVertIcon from '@material-ui/icons/SwapVert'
import EditIcon from '@material-ui/icons/Edit'
import AddIcon from '@material-ui/icons/Add'
import RemoveIcon from '@material-ui/icons/Remove'
-import FavoriteIcon from '@material-ui/icons/Favorite'
-import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder'
import { navigate } from '@/utils/history'
import { ITopic } from '@cc98/api'
-import { putFavorite, deleteFavorite } from '@/services/post'
-import { getTopicFavorite } from '../../services/topic'
-import useFetcher from '@/hooks/useFetcher'
-import { favoriteHandler } from '../../services/utils/errorHandler'
-import snackbar from '@/utils/snackbar'
interface Props {
/**
@@ -36,45 +29,11 @@ interface Props {
export default ({ topicInfo, isReverse, refreshFunc }: Props) => {
// 控制按钮是否展开
const [expand, setExpand] = useState(false)
- const [isFavorite, setIsFavorite] = useFetcher(() => getTopicFavorite(topicInfo.id))
-
- const toggleFavorite = async () => {
- if (isFavorite) {
- const res = await deleteFavorite(topicInfo.id)
- res.fail(favoriteHandler).succeed(() => {
- snackbar.success('取消关注')
- setIsFavorite(false)
- })
- } else {
- const res = await putFavorite(topicInfo.id)
- res.fail(favoriteHandler).succeed(() => {
- snackbar.success('关注成功')
- setIsFavorite(true)
- })
- }
- }
return (
<>
{expand && (
<>
-
- {isFavorite ? (
- {
- toggleFavorite()
- setExpand(false)
- }}
- />
- ) : (
- {
- toggleFavorite()
- setExpand(false)
- }}
- />
- )}
-
diff --git a/src/pages/Topic/PostHead.tsx b/src/pages/Topic/PostHead.tsx
index 11842b3..f5e5c51 100644
--- a/src/pages/Topic/PostHead.tsx
+++ b/src/pages/Topic/PostHead.tsx
@@ -10,6 +10,8 @@ import { getBoardNameById } from '@/services/board'
import { navigate, goback } from '@/utils/history'
+import TopicMenu from './Actions'
+
const Wrapper = styled(Paper).attrs({
square: true,
elevation: 1,
@@ -21,6 +23,7 @@ const Wrapper = styled(Paper).attrs({
top: 0;
min-height: 56px;
padding: 0 16px;
+ padding-right: 0;
/* z-index of TopBar is 1100 and DrawerMenu is 1200 */
z-index: 1105;
@@ -43,6 +46,7 @@ const Title = styled(Typography).attrs({
&& {
margin: 4px 0;
flex-grow: 2;
+ flex-shrink: 1;
}
`
@@ -50,10 +54,15 @@ const SubTitle = styled(Typography)`
&& {
margin-left: 8px;
margin-right: -5px;
- flex-shrink: 0;
+ flex-shrink: 1.2;
opacity: 0.5;
}
`
+const MenuIcon = styled(IconButton)`
+ && {
+ margin-right: 5px;
+ }
+`
interface Props {
topicInfo: ITopic
@@ -73,6 +82,7 @@ const PostHead: React.FunctionComponent = ({ topicInfo }) => {
{topicInfo.title}
navigate(`/board/${topicInfo.boardId}`)}>{boardName}
+
)
}
diff --git a/src/pages/Topic/PostItem/Actions.tsx b/src/pages/Topic/PostItem/Actions.tsx
index ad1a5da..a0e5547 100644
--- a/src/pages/Topic/PostItem/Actions.tsx
+++ b/src/pages/Topic/PostItem/Actions.tsx
@@ -196,18 +196,32 @@ const MoreActions = ({ postInfo, isTrace, refreshPost, userInfo }: Props) => {
})
const board = childBoards.find(b => b.id === postInfo.boardId)
- function isManager() {
+ function judgeEdit() {
// 本人是管理员允许修改任何帖子
if (myInfo && myInfo.privilege === '管理员') return true
// 不是管理员包括版主不允许修改管理员的帖子
if (userInfo && userInfo.privilege === '管理员') return false
// 本人是版主可以修改其他帖子
if (myInfo && board && board.boardMasters.indexOf(myInfo.name) !== -1) return true
+
+ return false
+ }
+
+ function judgeManage() {
+ // 本人是管理员允许管理任何帖子
+ if (myInfo && myInfo.privilege === '管理员') return true
+ // 本人是版主可以管理本版帖子
+ if (myInfo && board && board.boardMasters.indexOf(myInfo.name) !== -1) return true
+
+ return false
}
const myInfo = userInstance.state.myInfo
- const isMaster = isManager()
- const canEdit = postInfo.userId === (myInfo && myInfo.id) || postInfo.isAnonymous || isMaster
+ const myEdit = judgeEdit()
+ // 编辑操作
+ const canEdit = postInfo.userId === (myInfo && myInfo.id) || postInfo.isAnonymous || myEdit
+ // 管理操作
+ const canManage = judgeManage()
return (
<>
@@ -234,7 +248,7 @@ const MoreActions = ({ postInfo, isTrace, refreshPost, userInfo }: Props) => {
)}
- {isMaster && }
+ {canManage && }
>
)
diff --git a/src/services/utils/errorHandler.ts b/src/services/utils/errorHandler.ts
index 0ddd6d2..717798f 100644
--- a/src/services/utils/errorHandler.ts
+++ b/src/services/utils/errorHandler.ts
@@ -62,5 +62,9 @@ export function manageHandler(err: FetchError) {
}
export function favoriteHandler(err: FetchError) {
- snackbar.error('操作失败')
+ if (err.status === 401) {
+ snackbar.error('请先登录')
+ } else {
+ snackbar.error('操作失败')
+ }
}
diff --git a/src/version.ts b/src/version.ts
index 441e9a7..cd5ab3a 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -2,4 +2,4 @@
* 版本号
*/
-export default 'v1.1.0-beta'
+export default 'v1.1.1-beta'
From 100dbae87024002d4d778dea28be561db4423d88 Mon Sep 17 00:00:00 2001
From: tgsmdww <389007832@qq.com>
Date: Tue, 25 Dec 2018 12:19:13 +0800
Subject: [PATCH 04/13] fix
---
src/@types/@cc98/ITopic.d.ts | 4 ----
src/version.ts | 2 +-
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/@types/@cc98/ITopic.d.ts b/src/@types/@cc98/ITopic.d.ts
index 64f26df..b6bb493 100644
--- a/src/@types/@cc98/ITopic.d.ts
+++ b/src/@types/@cc98/ITopic.d.ts
@@ -85,9 +85,5 @@ declare module '@cc98/api' {
tag2: number
isInternalOnly: boolean
- /**
- * 是否收藏
- */
- isFavorite: boolean
}
}
diff --git a/src/version.ts b/src/version.ts
index cd5ab3a..c6a6681 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -2,4 +2,4 @@
* 版本号
*/
-export default 'v1.1.1-beta'
+export default 'v1.2.0-beta'
From 44dcd01930bd05fc23e638ce26825d18d0749bd9 Mon Sep 17 00:00:00 2001
From: tgsmdww <389007832@qq.com>
Date: Tue, 25 Dec 2018 12:21:14 +0800
Subject: [PATCH 05/13] Revert "fix"
This reverts commit 100dbae87024002d4d778dea28be561db4423d88.
---
src/@types/@cc98/ITopic.d.ts | 4 ++++
src/version.ts | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/@types/@cc98/ITopic.d.ts b/src/@types/@cc98/ITopic.d.ts
index b6bb493..64f26df 100644
--- a/src/@types/@cc98/ITopic.d.ts
+++ b/src/@types/@cc98/ITopic.d.ts
@@ -85,5 +85,9 @@ declare module '@cc98/api' {
tag2: number
isInternalOnly: boolean
+ /**
+ * 是否收藏
+ */
+ isFavorite: boolean
}
}
diff --git a/src/version.ts b/src/version.ts
index c6a6681..cd5ab3a 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -2,4 +2,4 @@
* 版本号
*/
-export default 'v1.2.0-beta'
+export default 'v1.1.1-beta'
From ea6fb3d75d40a9034bce6c9875197f0d5b12052d Mon Sep 17 00:00:00 2001
From: tgsmdww <389007832@qq.com>
Date: Tue, 25 Dec 2018 12:23:05 +0800
Subject: [PATCH 06/13] fix
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
误添isFavorite字段;版本号;疑似lint修改Array为any[]
---
src/@types/@cc98/ITopic.d.ts | 4 ----
src/version.ts | 2 +-
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/@types/@cc98/ITopic.d.ts b/src/@types/@cc98/ITopic.d.ts
index 64f26df..b6bb493 100644
--- a/src/@types/@cc98/ITopic.d.ts
+++ b/src/@types/@cc98/ITopic.d.ts
@@ -85,9 +85,5 @@ declare module '@cc98/api' {
tag2: number
isInternalOnly: boolean
- /**
- * 是否收藏
- */
- isFavorite: boolean
}
}
diff --git a/src/version.ts b/src/version.ts
index cd5ab3a..c6a6681 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -2,4 +2,4 @@
* 版本号
*/
-export default 'v1.1.1-beta'
+export default 'v1.2.0-beta'
From db541a5f72e5db0a624cbe74d0bb20b23d7d1820 Mon Sep 17 00:00:00 2001
From: AsukaSong
Date: Tue, 25 Dec 2018 16:04:03 +0800
Subject: [PATCH 07/13] fix: add dotenv for host
---
src/config/host.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/config/host.ts b/src/config/host.ts
index 085e0f3..4aec293 100644
--- a/src/config/host.ts
+++ b/src/config/host.ts
@@ -4,8 +4,8 @@ interface IHost {
}
const host: IHost = {
- oauth: 'https://openid.cc98.org/connect/token',
- api: 'https://api-v2.cc98.org',
+ oauth: process.env.oauth || 'https://openid.cc98.org/connect/token',
+ api: process.env.api || 'https://api-v2.cc98.org',
}
export default host
From c73ead2e4b8dcd236f2dde798490cf6e6c27c241 Mon Sep 17 00:00:00 2001
From: Hydrogen
Date: Tue, 25 Dec 2018 18:46:59 +0800
Subject: [PATCH 08/13] chore: babel
---
.babelrc | 18 ------------------
.browserslistrc | 10 ++++++++++
babel.config.js | 36 ++++++++++++++++++++++++++++++++++++
package-lock.json | 20 ++++++++++++++++----
package.json | 3 ++-
5 files changed, 64 insertions(+), 23 deletions(-)
delete mode 100644 .babelrc
create mode 100644 .browserslistrc
create mode 100644 babel.config.js
diff --git a/.babelrc b/.babelrc
deleted file mode 100644
index 8f4cafb..0000000
--- a/.babelrc
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "presets": [
- ["@babel/env", {
- "modules": false,
- "targets": { "esmodules": true }
- }],
- "@babel/react",
- "@babel/typescript"
- ],
- "plugins": [
- ["@babel/plugin-proposal-class-properties", { "loose": true }],
- ["transform-imports", {
- "@material-ui/core": { "transform": "@material-ui/core/${member}" },
- "lodash-es": { "transform": "lodash-es/${member}" }
- }],
- ["babel-plugin-styled-components", { "ssr": false }]
- ]
-}
diff --git a/.browserslistrc b/.browserslistrc
new file mode 100644
index 0000000..6ed2e33
--- /dev/null
+++ b/.browserslistrc
@@ -0,0 +1,10 @@
+# Browsers that we support
+
+ios 10.3
+Android > 67
+
+last 2 version
+> 1% in CN
+
+not dead
+not IE > 0
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 0000000..9aa0d1e
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,36 @@
+const presets = [
+ ["@babel/env", {
+ "debug": process.env.NODE_ENV === "production",
+ "modules": false,
+ }],
+ "@babel/react",
+ "@babel/typescript",
+]
+
+const plugins = [
+ ["@babel/plugin-proposal-class-properties", {
+ "loose": true
+ }],
+ ["transform-imports", {
+ "@material-ui/core": {
+ "transform": "@material-ui/core/es/${member}"
+ },
+ "lodash-es": {
+ "transform": "lodash-es/${member}"
+ }
+ }],
+ ["babel-plugin-styled-components", {
+ "ssr": false
+ }],
+]
+
+if (process.env.NODE_ENV === "production") {
+ plugins.unshift(...[
+ ["@babel/plugin-transform-runtime"],
+ ])
+}
+
+module.exports = {
+ presets,
+ plugins,
+}
diff --git a/package-lock.json b/package-lock.json
index 6a9721c..9be17ab 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -682,6 +682,18 @@
"regenerator-transform": "^0.13.3"
}
},
+ "@babel/plugin-transform-runtime": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.2.0.tgz",
+ "integrity": "sha512-jIgkljDdq4RYDnJyQsiWbdvGeei/0MOTtSHKO/rfbd/mXBxNpdlulMx49L0HQ4pug1fXannxoqCI+fYSle9eSw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "resolve": "^1.8.1",
+ "semver": "^5.5.1"
+ }
+ },
"@babel/plugin-transform-shorthand-properties": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz",
@@ -992,9 +1004,9 @@
}
},
"@material-ui/core": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-3.7.0.tgz",
- "integrity": "sha512-Eac2JlwL8oG6ns2ay5L5mvqx8HUmbSnLj2zqsakdi/7h52wuJj6u+Zz0UCHe2UCowiEcmjJ2Tz9iVpLrKrp2sw==",
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-3.7.1.tgz",
+ "integrity": "sha512-CjIGwvzn84BgzXWzC9M/Tz2gDI7AfUe3G1JXkZQAVy+ddPikh+iZwn5snnElfcjuC+ahXxaIyK49ARt3NM49vQ==",
"requires": {
"@babel/runtime": "7.2.0",
"@material-ui/utils": "^3.0.0-alpha.1",
@@ -1008,7 +1020,7 @@
"dom-helpers": "^3.2.1",
"hoist-non-react-statics": "^3.2.1",
"is-plain-object": "^2.0.4",
- "jss": "^9.3.3",
+ "jss": "^9.8.7",
"jss-camel-case": "^6.0.0",
"jss-default-unit": "^8.0.2",
"jss-global": "^3.0.0",
diff --git a/package.json b/package.json
index 1178918..1629050 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"dependencies": {
"@aspnet/signalr": "^1.1.0",
"@cc98/ubb-core": "1.1.0",
- "@material-ui/core": "^3.7.0",
+ "@material-ui/core": "^3.7.1",
"@material-ui/icons": "^3.0.1",
"@reach/router": "^1.2.1",
"@sentry/browser": "^4.4.2",
@@ -34,6 +34,7 @@
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-proposal-class-properties": "^7.2.3",
+ "@babel/plugin-transform-runtime": "^7.2.0",
"@babel/preset-env": "^7.2.3",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.1.0",
From 2cef9dbe39b41b8a69fdd48ce14fbf184d176a3c Mon Sep 17 00:00:00 2001
From: Hydrogen
Date: Tue, 25 Dec 2018 19:34:04 +0800
Subject: [PATCH 09/13] fix: babel dev conifg
---
babel.config.js | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/babel.config.js b/babel.config.js
index 9aa0d1e..a02f2ca 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -8,6 +8,7 @@ const presets = [
]
const plugins = [
+ ["@babel/plugin-transform-runtime"],
["@babel/plugin-proposal-class-properties", {
"loose": true
}],
@@ -24,12 +25,6 @@ const plugins = [
}],
]
-if (process.env.NODE_ENV === "production") {
- plugins.unshift(...[
- ["@babel/plugin-transform-runtime"],
- ])
-}
-
module.exports = {
presets,
plugins,
From f7011507b6fd79f85f3d2d3d4fe66490388f262b Mon Sep 17 00:00:00 2001
From: AsukaSong
Date: Tue, 25 Dec 2018 20:08:11 +0800
Subject: [PATCH 10/13] =?UTF-8?q?fix=20#22=20=E7=83=AD=E9=97=A8=E5=88=87?=
=?UTF-8?q?=E6=8D=A2=E6=A0=87=E7=AD=BE=E5=8D=A1=E9=A1=BF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* 加载热门列表时延时300ms
* 添加了一个useDelay
---
src/components/TopicList/index.tsx | 12 ++++++++++--
src/hooks/useDelay.ts | 20 ++++++++++++++++++++
src/pages/HotTopic/index.tsx | 20 +++++++++++++-------
3 files changed, 43 insertions(+), 9 deletions(-)
create mode 100644 src/hooks/useDelay.ts
diff --git a/src/components/TopicList/index.tsx b/src/components/TopicList/index.tsx
index eacbb22..ee93e4e 100644
--- a/src/components/TopicList/index.tsx
+++ b/src/components/TopicList/index.tsx
@@ -3,6 +3,7 @@ import styled from 'styled-components'
import useInfList, { Service as InfService } from '@/hooks/useInfList'
import useFetcher, { Service as FinService } from '@/hooks/useFetcher'
+import useDelay from '@/hooks/useDelay'
import TopicList from './TopicList'
import { Place } from './TopicListItem'
@@ -58,12 +59,19 @@ interface FinProps {
service: FinService
noLoading?: boolean
place: Place
+ delay?: number
}
-const FinTopicList: React.FunctionComponent = ({ service, noLoading, place }) => {
+const FinTopicList: React.FunctionComponent = ({
+ service,
+ noLoading,
+ place,
+ delay = 0,
+}) => {
const [topics] = useFetcher(service, { fail: navigateHandler })
+ const isResolve = useDelay(delay)
- if (topics === null) {
+ if (topics === null || !isResolve) {
return noLoading ? null :
}
diff --git a/src/hooks/useDelay.ts b/src/hooks/useDelay.ts
new file mode 100644
index 0000000..d16afcd
--- /dev/null
+++ b/src/hooks/useDelay.ts
@@ -0,0 +1,20 @@
+import { useState, useEffect } from 'react'
+
+/**
+ * 返回一个boolean
+ * 在指定的时间后其值为true
+ * @returns {boolean} 是否超过了延迟时间
+ * @param time 延迟时间
+ */
+export default function useDelay(time: number): boolean {
+ const [isResolve, setIsResolve] = useState(time <= 0)
+
+ useEffect(() => {
+ if (time <= 0) return
+ const timer = setTimeout(() => setIsResolve(true), time)
+
+ return () => clearTimeout(timer)
+ }, [])
+
+ return isResolve
+}
diff --git a/src/pages/HotTopic/index.tsx b/src/pages/HotTopic/index.tsx
index 7f39d1c..580bb5b 100644
--- a/src/pages/HotTopic/index.tsx
+++ b/src/pages/HotTopic/index.tsx
@@ -1,6 +1,7 @@
import React, { useState } from 'react'
import useFetcher from '@/hooks/useFetcher'
+import useDelay from '@/hooks/useDelay'
import { FinTopicList } from '@/components/TopicList'
import { List, Tab, Tabs } from '@material-ui/core'
@@ -18,14 +19,16 @@ import { notificationHandler } from '@/services/utils/errorHandler'
interface Props {
service: typeof getHotTopics
+ delay?: number
}
-export const HotTopicList: React.FunctionComponent = ({ service }) => {
+export const HotTopicList: React.FunctionComponent = ({ service, delay = 0 }) => {
const [topics] = useFetcher(service, {
fail: notificationHandler,
})
+ const isResolve = useDelay(delay)
- if (topics === null) {
+ if (topics === null || !isResolve) {
return
}
@@ -60,11 +63,14 @@ export default () => {
- {current === 'day' && }
-
- {current === 'week' && }
- {current === 'month' && }
- {current === 'history' && }
+ {current === 'day' && }
+ {current === 'week' && }
+ {current === 'month' && (
+
+ )}
+ {current === 'history' && (
+
+ )}
>
)
}
From 9d65f0e9c0938ec64b403af8f5c59cce47fee21b Mon Sep 17 00:00:00 2001
From: Hydrogen
Date: Wed, 26 Dec 2018 01:07:06 +0800
Subject: [PATCH 11/13] feat: router animate
fix: markdown pre overflow
fix: scroll when goback
---
config/webpack.dev.js | 1 +
package-lock.json | 8 ++
package.json | 1 +
src/pages/Editor/Editor/MainContent.tsx | 2 +-
src/pages/Topic/PostItem/Content.tsx | 5 ++
src/router/index.tsx | 102 +++++++++++++++++++-----
src/version.ts | 2 +-
7 files changed, 98 insertions(+), 23 deletions(-)
diff --git a/config/webpack.dev.js b/config/webpack.dev.js
index 3f815ea..8b8d317 100644
--- a/config/webpack.dev.js
+++ b/config/webpack.dev.js
@@ -17,6 +17,7 @@ module.exports = merge(common, {
devtool: 'eval-source-map',
devServer: {
+ disableHostCheck: true,
historyApiFallback: true,
port: 9898,
host: '0.0.0.0',
diff --git a/package-lock.json b/package-lock.json
index 9be17ab..654cf50 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7894,6 +7894,14 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
+ "react-spring": {
+ "version": "7.2.5",
+ "resolved": "https://registry.npmjs.org/react-spring/-/react-spring-7.2.5.tgz",
+ "integrity": "sha512-J14PEJmkTOKLqN03+y7nM9szUrnQhZnapyQu4Tr8Ncq5wpKUB4ASZ4pRU/amNVKf0WVPkYeD62FqZFIrDjELJg==",
+ "requires": {
+ "@babel/runtime": "^7.0.0"
+ }
+ },
"react-transition-group": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.2.tgz",
diff --git a/package.json b/package.json
index 1629050..aa822ba 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"lodash-es": "^4.17.11",
"react": "16.7.0-alpha.2",
"react-dom": "16.7.0-alpha.2",
+ "react-spring": "^7.2.5",
"remark": "^10.0.1",
"remark-react": "^5.0.1",
"styled-components": "^4.1.3",
diff --git a/src/pages/Editor/Editor/MainContent.tsx b/src/pages/Editor/Editor/MainContent.tsx
index 8b00b7b..7d84263 100644
--- a/src/pages/Editor/Editor/MainContent.tsx
+++ b/src/pages/Editor/Editor/MainContent.tsx
@@ -10,7 +10,7 @@ const InputArea = styled(InputBase).attrs({
multiline: true,
autoFocus: true,
rows: 6,
- rowsMax: 12,
+ rowsMax: 10,
})`
&& {
margin-top: 8px;
diff --git a/src/pages/Topic/PostItem/Content.tsx b/src/pages/Topic/PostItem/Content.tsx
index ece3e2e..9b5acfe 100644
--- a/src/pages/Topic/PostItem/Content.tsx
+++ b/src/pages/Topic/PostItem/Content.tsx
@@ -24,6 +24,11 @@ const TypographyS = styled(Typography)`
img {
max-width: 100%;
}
+
+ pre,
+ code {
+ white-space: pre-wrap;
+ }
}
`
diff --git a/src/router/index.tsx b/src/router/index.tsx
index 5dc91b9..fb8d735 100644
--- a/src/router/index.tsx
+++ b/src/router/index.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from 'react'
+import React, { useEffect, useRef } from 'react'
// https://reach.tech/router/api/Router
import { Location, WindowLocation } from '@reach/router'
import Router, { ILocation } from './Router'
@@ -8,8 +8,14 @@ import settingInstance from '@/containers/setting'
import './gesture'
+interface LocationState {
+ href: string
+ location: WindowLocation
+ scrollTop: number
+}
+
interface State {
- locations: WindowLocation[]
+ locationStates: LocationState[]
MAX_CACHE_SIZE: number
}
@@ -18,7 +24,7 @@ interface State {
*/
class RouterCacheContainer extends Container {
state: State = {
- locations: [],
+ locationStates: [],
MAX_CACHE_SIZE: settingInstance.state.routerCacheSize,
}
@@ -27,22 +33,26 @@ class RouterCacheContainer extends Container {
* @param location
*/
push(location: WindowLocation) {
- const { locations, MAX_CACHE_SIZE } = this.state
- const index = locations.findIndex(backLoc => backLoc.href === location.href)
+ const { locationStates, MAX_CACHE_SIZE } = this.state
+ const index = locationStates.findIndex(locState => locState.href === location.href)
if (index !== -1) {
- const loc = locations[index]
- locations.splice(index, 1)
- locations.push(loc)
+ const loc = locationStates[index]
+ locationStates.splice(index, 1)
+ locationStates.push(loc)
} else {
- locations.push({ ...location })
+ locationStates.push({
+ href: location.href,
+ location: { ...location },
+ scrollTop: 0,
+ })
// 超过最大缓存数
- if (locations.length > MAX_CACHE_SIZE) {
- locations.shift()
+ if (locationStates.length > MAX_CACHE_SIZE) {
+ locationStates.shift()
}
}
- this.setState({ locations })
+ this.setState({ locationStates })
}
}
@@ -65,8 +75,63 @@ export function bindURL(func: Function, href: string) {
}
}
-const CacheRouter: React.FunctionComponent = ({ location }) => {
- const { locations } = useContainer(ROUTER_CACHE).state
+// https://majido.github.io/scroll-restoration-proposal/history-based-api.html#web-idl
+history.scrollRestoration = 'manual'
+
+// @ts-ignore FIXME: no animated export from d.ts
+import { useSpring, animated } from 'react-spring/hooks'
+import { config } from 'react-spring'
+
+interface ScrollDivProps {
+ show: boolean
+ locState: LocationState
+}
+
+const ScrollDiv = ({ show, locState }: ScrollDivProps) => {
+ const lastShow = useRef(false)
+ const [props, set] = useSpring(() => ({
+ opacity: 0,
+ config: config.gentle,
+ }))
+
+ if (lastShow.current !== show) {
+ if (show) {
+ // @ts-ignore
+ set({ opacity: 1 })
+
+ setTimeout(() => {
+ if (!locState.scrollTop) {
+ return
+ }
+ window.scrollTo({
+ left: 0,
+ top: locState.scrollTop,
+ // behavior: 'smooth',
+ })
+ // FIXME: choose a better delay
+ }, 300)
+ }
+
+ if (!show) {
+ // @ts-ignore
+ set({ opacity: 0 })
+ locState.scrollTop = window.scrollY
+ }
+
+ lastShow.current = show
+ }
+
+ const style = show ? props : { display: 'none' }
+
+ return (
+
+
+
+ )
+}
+
+const CacheRouter: React.FC = ({ location }) => {
+ const { locationStates } = useContainer(ROUTER_CACHE).state
useEffect(
() => {
@@ -77,13 +142,8 @@ const CacheRouter: React.FunctionComponent = ({ location }) => {
return (
<>
- {locations.map(backLoc => (
-
-
-
+ {locationStates.map(locState => (
+
))}
>
)
diff --git a/src/version.ts b/src/version.ts
index c6a6681..0404c53 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -2,4 +2,4 @@
* 版本号
*/
-export default 'v1.2.0-beta'
+export default 'v1.2.1-beta'
From 4b07a9eb69c7945ef309f5e5d33baa354fa80d89 Mon Sep 17 00:00:00 2001
From: Hydrogen
Date: Wed, 26 Dec 2018 17:31:14 +0800
Subject: [PATCH 12/13] style: Topic SubTitle
---
.../handlerHub/specificTagHandlers/bili.tsx | 11 +-------
src/UBB/style.css | 4 +--
src/components/FixFab/index.tsx | 9 ++++---
src/components/TopicList/TopicListItem.tsx | 27 +++++++++----------
src/pages/Topic/FixButtons.tsx | 6 ++---
.../Topic/{Actions.tsx => PostActions.tsx} | 20 +++-----------
src/pages/Topic/PostHead.tsx | 14 +++++-----
src/router/index.tsx | 2 +-
src/services/board.ts | 2 +-
9 files changed, 36 insertions(+), 59 deletions(-)
rename src/pages/Topic/{Actions.tsx => PostActions.tsx} (77%)
diff --git a/src/UBB/handlerHub/specificTagHandlers/bili.tsx b/src/UBB/handlerHub/specificTagHandlers/bili.tsx
index e8ee400..046d93e 100644
--- a/src/UBB/handlerHub/specificTagHandlers/bili.tsx
+++ b/src/UBB/handlerHub/specificTagHandlers/bili.tsx
@@ -12,20 +12,11 @@ const handler: ITagHandler = {
const { bili } = node.tagData
const partNumber = parseInt(bili, 10) || 1
- const props = {
- border: 0,
- frameborder: 'no',
- framespacing: 0,
- allowfullscreen: true,
- }
-
return (
)
},
diff --git a/src/UBB/style.css b/src/UBB/style.css
index 91d3562..9267a97 100644
--- a/src/UBB/style.css
+++ b/src/UBB/style.css
@@ -11,8 +11,8 @@
.ubb-tag-bili {
- width: 80%;
- margin-left: 10%;
+ width: 90%;
+ margin-left: 5%;
border: none;
}
diff --git a/src/components/FixFab/index.tsx b/src/components/FixFab/index.tsx
index e66be13..e1116e8 100644
--- a/src/components/FixFab/index.tsx
+++ b/src/components/FixFab/index.tsx
@@ -9,7 +9,7 @@ const FabS = styled(Fab).attrs({
})<{ bottom: number }>`
&& {
position: fixed;
- bottom: ${props => (props.bottom ? `${props.bottom}px` : '15px')};
+ bottom: ${props => `${props.bottom}px`};
right: 15px;
z-index: 20;
}
@@ -17,13 +17,14 @@ const FabS = styled(Fab).attrs({
interface Props {
onClick?: () => void
- bottom?: number
+ /** 次序,从 1 计数 */
+ order?: number
}
-const FixFab: React.FunctionComponent = ({ onClick, bottom, children }) => (
+const FixFab: React.FunctionComponent = ({ onClick, order = 1, children }) => (
// FIXME: waiting @types/styled-components to upgrade
// @ts-ignore https://www.styled-components.com/docs/advanced#refs
-
+
{children}
)
diff --git a/src/components/TopicList/TopicListItem.tsx b/src/components/TopicList/TopicListItem.tsx
index f34d332..e78564d 100644
--- a/src/components/TopicList/TopicListItem.tsx
+++ b/src/components/TopicList/TopicListItem.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react'
+import React, { useState, useEffect } from 'react'
import styled from 'styled-components'
import { navigate } from '@/utils/history'
@@ -91,10 +91,18 @@ interface Props {
export default ({ data, place }: Props) => {
const [boardName, setBoardName] = useState('')
-
- function getBoardName() {
- getBoardNameById(data.boardId).then(boardName => setBoardName(boardName))
- }
+ useEffect(
+ () => {
+ switch (place) {
+ case 'usercenter':
+ case 'hot':
+ case 'follow':
+ case 'search':
+ getBoardNameById(data.boardId).then(boardName => setBoardName(boardName))
+ }
+ },
+ [place]
+ )
const title = data.title
let subtitle = data.userName ? data.userName : '[匿名]'
@@ -103,16 +111,10 @@ export default ({ data, place }: Props) => {
switch (place) {
case 'usercenter':
- if (!boardName) {
- getBoardName()
- }
subtitle = boardName
break
case 'hot':
- if (!boardName) {
- getBoardName()
- }
info1 = boardName
break
@@ -120,9 +122,6 @@ export default ({ data, place }: Props) => {
info1 = dayjs(data.time).fromNow()
case 'follow':
case 'search':
- if (!boardName) {
- getBoardName()
- }
info2 = boardName
break
diff --git a/src/pages/Topic/FixButtons.tsx b/src/pages/Topic/FixButtons.tsx
index 1e41cb8..7f25620 100644
--- a/src/pages/Topic/FixButtons.tsx
+++ b/src/pages/Topic/FixButtons.tsx
@@ -34,10 +34,10 @@ export default ({ topicInfo, isReverse, refreshFunc }: Props) => {
<>
{expand && (
<>
-
+
-
+
isReverse
@@ -46,7 +46,7 @@ export default ({ topicInfo, isReverse, refreshFunc }: Props) => {
}
/>
-
+
navigate(`/editor/replyTopic/${topicInfo.id}`)} />
>
diff --git a/src/pages/Topic/Actions.tsx b/src/pages/Topic/PostActions.tsx
similarity index 77%
rename from src/pages/Topic/Actions.tsx
rename to src/pages/Topic/PostActions.tsx
index 1b6537b..6c4112b 100644
--- a/src/pages/Topic/Actions.tsx
+++ b/src/pages/Topic/PostActions.tsx
@@ -5,7 +5,6 @@ import useFetcher from '@/hooks/useFetcher'
import { IconButton, Menu, MenuItem, ListItemIcon, Typography } from '@material-ui/core'
import MoreVertIcon from '@material-ui/icons/MoreVert'
import FavoriteIcon from '@material-ui/icons/Favorite'
-import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder'
import snackbar from '@/utils/snackbar'
import { ITopic } from '@cc98/api'
@@ -61,21 +60,10 @@ export default ({ topicInfo }: Props) => {
handleClose()
}}
>
- {isFavorite ? (
- <>
-
-
-
- 取消收藏
- >
- ) : (
- <>
-
-
-
- 收藏主题
- >
- )}
+
+
+
+ {isFavorite ? '取消收藏' : '收藏主题'}
>
diff --git a/src/pages/Topic/PostHead.tsx b/src/pages/Topic/PostHead.tsx
index f5e5c51..60434f5 100644
--- a/src/pages/Topic/PostHead.tsx
+++ b/src/pages/Topic/PostHead.tsx
@@ -10,7 +10,7 @@ import { getBoardNameById } from '@/services/board'
import { navigate, goback } from '@/utils/history'
-import TopicMenu from './Actions'
+import PostActions from './PostActions'
const Wrapper = styled(Paper).attrs({
square: true,
@@ -52,17 +52,15 @@ const Title = styled(Typography).attrs({
const SubTitle = styled(Typography)`
&& {
- margin-left: 8px;
+ display: inline-block;
+ min-width: 4rem;
+ max-width: 6rem;
+ text-align: center;
margin-right: -5px;
flex-shrink: 1.2;
opacity: 0.5;
}
`
-const MenuIcon = styled(IconButton)`
- && {
- margin-right: 5px;
- }
-`
interface Props {
topicInfo: ITopic
@@ -82,7 +80,7 @@ const PostHead: React.FunctionComponent = ({ topicInfo }) => {
{topicInfo.title}
navigate(`/board/${topicInfo.boardId}`)}>{boardName}
-
+
)
}
diff --git a/src/router/index.tsx b/src/router/index.tsx
index fb8d735..a201b93 100644
--- a/src/router/index.tsx
+++ b/src/router/index.tsx
@@ -76,7 +76,7 @@ export function bindURL(func: Function, href: string) {
}
// https://majido.github.io/scroll-restoration-proposal/history-based-api.html#web-idl
-history.scrollRestoration = 'manual'
+// history.scrollRestoration = 'manual'
// @ts-ignore FIXME: no animated export from d.ts
import { useSpring, animated } from 'react-spring/hooks'
diff --git a/src/services/board.ts b/src/services/board.ts
index 870d8b2..51aa97c 100644
--- a/src/services/board.ts
+++ b/src/services/board.ts
@@ -14,7 +14,7 @@ export const getBoardsInfo = cacheService(
)
/**
- * 通过版面Id获取版面名称
+ * 通过版面Id获取版面名称(返回值不是 Try)
*/
export const getBoardNameById = (() => {
// cache
From 38f14ffc660ece647acc0e06350b0841c02bfba0 Mon Sep 17 00:00:00 2001
From: Hydrogen
Date: Wed, 26 Dec 2018 23:16:23 +0800
Subject: [PATCH 13/13] refactor: Actions
---
src/pages/Topic/PostItem/Actions.tsx | 257 +-----------------
.../Topic/PostItem/Actions/IconActions.tsx | 119 ++++++++
.../Topic/PostItem/Actions/MemuActions.tsx | 160 +++++++++++
src/pages/Topic/PostItem/Content.tsx | 7 +-
.../Topic/PostItem/{ => Dialog}/Judge.tsx | 0
.../Topic/PostItem/{ => Dialog}/Manage.tsx | 0
src/services/board.ts | 60 ++--
src/version.ts | 2 +-
8 files changed, 338 insertions(+), 267 deletions(-)
create mode 100644 src/pages/Topic/PostItem/Actions/IconActions.tsx
create mode 100644 src/pages/Topic/PostItem/Actions/MemuActions.tsx
rename src/pages/Topic/PostItem/{ => Dialog}/Judge.tsx (100%)
rename src/pages/Topic/PostItem/{ => Dialog}/Manage.tsx (100%)
diff --git a/src/pages/Topic/PostItem/Actions.tsx b/src/pages/Topic/PostItem/Actions.tsx
index a0e5547..545edc2 100644
--- a/src/pages/Topic/PostItem/Actions.tsx
+++ b/src/pages/Topic/PostItem/Actions.tsx
@@ -1,34 +1,16 @@
-import React, { useState } from 'react'
+import React from 'react'
import styled from 'styled-components'
-import useFetcher from '@/hooks/useFetcher'
+import IconActions from './Actions/IconActions'
+import MemuActions from './Actions/MemuActions'
-import { IconButton, Typography, Menu, MenuItem } from '@material-ui/core'
+import { IPost, IUser } from '@cc98/api'
-import ThumbUpIcon from '@material-ui/icons/ThumbUp'
-import ThumbDownIcon from '@material-ui/icons/ThumbDown'
-import FormatQuoteIcon from '@material-ui/icons/FormatQuote'
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
-
-import { IPost, ILikeState, IUser, IBoard } from '@cc98/api'
-import { putLike, putDislike } from '@/services/post'
-
-import userInstance from '@/containers/user'
-
-import { navigate } from '@/utils/history'
-import snackbar from '@/utils/snackbar'
-import { getBoardsInfo } from '@/services/board'
-import copy2Clipboard from 'copy-to-clipboard'
-
-import Judge from './Judge'
-import Manage from './Manage'
-
-// @babel/plugin-transform-typescript does not support const enums
-enum LikeState {
- NONE = 0,
- LIKE = 1,
- DISLIKE = 2,
-}
+const FlexDiv = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+`
interface Props {
/**
@@ -38,232 +20,21 @@ interface Props {
/**
* 用户信息
*/
- userInfo?: IUser
+ userInfo: IUser | undefined
/**
* 是否追踪
*/
- isTrace?: boolean
+ isTrace: boolean
/**
* 更新 Post 信息
*/
refreshPost: () => void
}
-const ActionDiv = styled.div`
- display: flex;
- align-items: center;
- margin-left: 8px;
-`
-
-const Count = styled(Typography).attrs({
- color: 'textSecondary',
-})`
- && {
- margin-left: -2px;
- margin-right: 12px;
- }
-`
-
-const DividerCol = styled.span`
- margin: 0 4px;
- /* FIXME: remove hardcode color */
- border: solid thin rgba(0, 0, 0, 0.54);
- height: 1rem;
-`
-
-/**
- * 检查是否登录
- */
-function checkLogIn() {
- if (!userInstance.state.isLogIn) {
- snackbar.error('请先登录')
-
- return false
- }
-
- return true
-}
-
-const IconActions: React.FunctionComponent = ({ postInfo, refreshPost }) => {
- const { likeState } = postInfo
-
- const handleLike = (newLikeState: ILikeState) => () => {
- if (!checkLogIn()) return
-
- const putService = newLikeState === 1 ? putLike : putDislike
-
- putService(postInfo.id).then(res =>
- res.fail().succeed(_ => {
- refreshPost()
- })
- )
- }
-
- const handleQuote = () => {
- if (!checkLogIn()) return
-
- if (postInfo.isDeleted) {
- snackbar.error('不能引用已删除的帖子')
-
- return
- }
- navigate(`/editor/replyTopic/${postInfo.topicId}/quote/${postInfo.floor}`)
- }
-
- return (
-
-
-
-
- {postInfo.likeCount}
-
-
-
-
-
-
- {postInfo.dislikeCount}
-
-
-
-
-
-
- )
-}
-
-const MoreActions = ({ postInfo, isTrace, refreshPost, userInfo }: Props) => {
- // 控制 Menu 的显示
- const [anchorEl, setAnchorEl] = useState(null)
- const handleOpen = (event: React.MouseEvent) => {
- setAnchorEl(event.currentTarget)
- }
- const handleClose = () => {
- setAnchorEl(null)
- }
-
- const handleTrace = () => {
- if (isTrace) {
- navigate(`/topic/${postInfo.topicId}`)
- } else {
- if (postInfo.isAnonymous) {
- navigate(`/topic/${postInfo.topicId}/anonymous/trace/${postInfo.id}`)
- } else {
- navigate(`/topic/${postInfo.topicId}/trace/${postInfo.userId}`)
- }
- }
- handleClose()
- }
-
- const handleShare = () => {
- if (document.location) {
- copy2Clipboard(
- `https://${document.location.host}/topic/${postInfo.topicId}#${postInfo.floor}`
- )
- }
- snackbar.success('分享链接已经成功复制到剪切板')
- handleClose()
- }
-
- // 控制 评分 的显示
- const [showJudge, setShowJudge] = useState(false)
- const [showManage, setShowManage] = useState(false)
-
- const judgeOpen = () => setShowJudge(true)
- const judgeClose = () => setShowJudge(false)
-
- const manageOpen = () => setShowManage(true)
- const manageClose = () => setShowManage(false)
-
- const handleJudge = () => {
- judgeOpen()
- handleClose()
- }
-
- const handleManage = () => {
- manageOpen()
- handleClose()
- }
-
- // FIXME: 不接受每次都这样请求,写一个新的 service
- const [childBoards, setChildBoards] = useState([])
- useFetcher(getBoardsInfo, {
- success: boards => {
- setChildBoards(
- boards.map(baseBoard => baseBoard.boards).reduce((prev, cur) => cur.concat(prev))
- )
- },
- })
- const board = childBoards.find(b => b.id === postInfo.boardId)
-
- function judgeEdit() {
- // 本人是管理员允许修改任何帖子
- if (myInfo && myInfo.privilege === '管理员') return true
- // 不是管理员包括版主不允许修改管理员的帖子
- if (userInfo && userInfo.privilege === '管理员') return false
- // 本人是版主可以修改其他帖子
- if (myInfo && board && board.boardMasters.indexOf(myInfo.name) !== -1) return true
-
- return false
- }
-
- function judgeManage() {
- // 本人是管理员允许管理任何帖子
- if (myInfo && myInfo.privilege === '管理员') return true
- // 本人是版主可以管理本版帖子
- if (myInfo && board && board.boardMasters.indexOf(myInfo.name) !== -1) return true
-
- return false
- }
-
- const myInfo = userInstance.state.myInfo
- const myEdit = judgeEdit()
- // 编辑操作
- const canEdit = postInfo.userId === (myInfo && myInfo.id) || postInfo.isAnonymous || myEdit
- // 管理操作
- const canManage = judgeManage()
-
- return (
- <>
- {showJudge && (
-
- )}
- {showManage && (
-
- )}
-
-
-
-
- >
- )
-}
-
-const FlexDiv = styled.div`
- display: flex;
- justify-content: space-between;
- align-items: center;
-`
-
-export default ({ postInfo, isTrace, refreshPost, userInfo }: Props) => (
+const Actions: React.FC = ({ postInfo, isTrace, refreshPost, userInfo }) => (
- (
/>
)
+
+export default Actions
diff --git a/src/pages/Topic/PostItem/Actions/IconActions.tsx b/src/pages/Topic/PostItem/Actions/IconActions.tsx
new file mode 100644
index 0000000..ab15414
--- /dev/null
+++ b/src/pages/Topic/PostItem/Actions/IconActions.tsx
@@ -0,0 +1,119 @@
+import React from 'react'
+import styled from 'styled-components'
+
+import { IconButton, Typography } from '@material-ui/core'
+
+import ThumbUpIcon from '@material-ui/icons/ThumbUp'
+import ThumbDownIcon from '@material-ui/icons/ThumbDown'
+import FormatQuoteIcon from '@material-ui/icons/FormatQuote'
+
+import { IPost, ILikeState } from '@cc98/api'
+import { putLike, putDislike } from '@/services/post'
+
+import userInstance from '@/containers/user'
+
+import { navigate } from '@/utils/history'
+import snackbar from '@/utils/snackbar'
+
+// @babel/plugin-transform-typescript does not support const enums
+enum LikeState {
+ NONE = 0,
+ LIKE = 1,
+ DISLIKE = 2,
+}
+
+interface Props {
+ /**
+ * 帖子信息
+ */
+ postInfo: IPost
+ /**
+ * 更新 Post 信息
+ */
+ refreshPost: () => void
+}
+
+const ActionDiv = styled.div`
+ display: flex;
+ align-items: center;
+ margin-left: 8px;
+`
+
+const Count = styled(Typography).attrs({
+ color: 'textSecondary',
+})`
+ && {
+ margin-left: -2px;
+ margin-right: 12px;
+ }
+`
+
+const DividerCol = styled.span`
+ margin: 0 4px;
+ /* FIXME: remove hardcode color */
+ border: solid thin rgba(0, 0, 0, 0.54);
+ height: 1rem;
+`
+
+/**
+ * 检查是否登录
+ */
+function checkLogIn() {
+ if (!userInstance.state.isLogIn) {
+ snackbar.error('请先登录')
+
+ return false
+ }
+
+ return true
+}
+
+const IconActions: React.FC = ({ postInfo, refreshPost }) => {
+ const { likeState } = postInfo
+
+ const handleLike = (newLikeState: ILikeState) => () => {
+ if (!checkLogIn()) return
+
+ const putService = newLikeState === 1 ? putLike : putDislike
+
+ putService(postInfo.id).then(res =>
+ res.fail().succeed(_ => {
+ refreshPost()
+ })
+ )
+ }
+
+ const handleQuote = () => {
+ if (!checkLogIn()) return
+
+ if (postInfo.isDeleted) {
+ snackbar.error('不能引用已删除的帖子')
+
+ return
+ }
+ navigate(`/editor/replyTopic/${postInfo.topicId}/quote/${postInfo.floor}`)
+ }
+
+ return (
+
+
+
+
+ {postInfo.likeCount}
+
+
+
+
+
+
+ {postInfo.dislikeCount}
+
+
+
+
+
+
+ )
+}
+
+export default IconActions
diff --git a/src/pages/Topic/PostItem/Actions/MemuActions.tsx b/src/pages/Topic/PostItem/Actions/MemuActions.tsx
new file mode 100644
index 0000000..d2e2c24
--- /dev/null
+++ b/src/pages/Topic/PostItem/Actions/MemuActions.tsx
@@ -0,0 +1,160 @@
+import React, { useState, useEffect } from 'react'
+
+import { IconButton, Menu, MenuItem } from '@material-ui/core'
+
+import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
+
+import { IPost, IUser } from '@cc98/api'
+
+import userInstance from '@/containers/user'
+
+import { navigate } from '@/utils/history'
+import snackbar from '@/utils/snackbar'
+import { getBoardMastersById } from '@/services/board'
+import copy2Clipboard from 'copy-to-clipboard'
+
+// TODO: fix
+import Judge from '../Dialog/Judge'
+import Manage from '../Dialog/Manage'
+
+interface Props {
+ /**
+ * 帖子信息
+ */
+ postInfo: IPost
+ /**
+ * 用户信息
+ */
+ userInfo: IUser | undefined
+ /**
+ * 是否追踪
+ */
+ isTrace: boolean
+ /**
+ * 更新 Post 信息
+ */
+ refreshPost: () => void
+}
+
+const MenuActions: React.FC = ({ postInfo, isTrace, refreshPost, userInfo }) => {
+ // 控制 Menu 的显示
+ const [anchorEl, setAnchorEl] = useState(null)
+ const handleOpen = (event: React.MouseEvent) => {
+ setAnchorEl(event.currentTarget)
+ }
+ const handleClose = () => {
+ setAnchorEl(null)
+ }
+
+ const handleTrace = () => {
+ if (isTrace) {
+ navigate(`/topic/${postInfo.topicId}`)
+ } else {
+ if (postInfo.isAnonymous) {
+ navigate(`/topic/${postInfo.topicId}/anonymous/trace/${postInfo.id}`)
+ } else {
+ navigate(`/topic/${postInfo.topicId}/trace/${postInfo.userId}`)
+ }
+ }
+ handleClose()
+ }
+
+ const handleShare = () => {
+ if (document.location) {
+ copy2Clipboard(
+ `https://${document.location.host}/topic/${postInfo.topicId}#${postInfo.floor}`
+ )
+ }
+ snackbar.success('分享链接已经成功复制到剪切板')
+ handleClose()
+ }
+
+ // 控制 评分 的显示
+ const [showJudge, setShowJudge] = useState(false)
+ const [showManage, setShowManage] = useState(false)
+
+ const judgeOpen = () => setShowJudge(true)
+ const judgeClose = () => setShowJudge(false)
+
+ const manageOpen = () => setShowManage(true)
+ const manageClose = () => setShowManage(false)
+
+ const handleJudge = () => {
+ judgeOpen()
+ handleClose()
+ }
+
+ const handleManage = () => {
+ manageOpen()
+ handleClose()
+ }
+
+ // FIXME: 显然组件的组织有问题,数据或许可以集中化处理
+
+ // 获取该版面版主信息
+ const [boardMasters, setBoardMasters] = useState([])
+ useEffect(
+ () => {
+ getBoardMastersById(postInfo.boardId).then(boardMasters => setBoardMasters(boardMasters))
+ },
+ [postInfo.boardId]
+ )
+
+ function judgeEdit() {
+ // 本人是管理员允许修改任何帖子
+ if (myInfo && myInfo.privilege === '管理员') return true
+ // 不是管理员包括版主不允许修改管理员的帖子
+ if (userInfo && userInfo.privilege === '管理员') return false
+ // 本人是版主可以修改其他帖子
+ if (myInfo && boardMasters.indexOf(myInfo.name) !== -1) return true
+
+ return false
+ }
+
+ function judgeManage() {
+ // 本人是管理员允许管理任何帖子
+ if (myInfo && myInfo.privilege === '管理员') return true
+ // 本人是版主可以管理本版帖子
+ if (myInfo && boardMasters.indexOf(myInfo.name) !== -1) return true
+
+ return false
+ }
+
+ const myInfo = userInstance.state.myInfo
+ // 编辑操作
+ const canEdit = postInfo.userId === (myInfo && myInfo.id) || postInfo.isAnonymous || judgeEdit()
+ // 管理操作
+ const canManage = judgeManage()
+
+ return (
+ <>
+ {showJudge && (
+
+ )}
+ {showManage && (
+
+ )}
+
+
+
+
+ >
+ )
+}
+
+export default MenuActions
diff --git a/src/pages/Topic/PostItem/Content.tsx b/src/pages/Topic/PostItem/Content.tsx
index 9b5acfe..905ae61 100644
--- a/src/pages/Topic/PostItem/Content.tsx
+++ b/src/pages/Topic/PostItem/Content.tsx
@@ -20,14 +20,15 @@ const TypographyS = styled(Typography)`
margin: 12px 16px;
margin-bottom: 4px;
- /* for in markdown */
+ /* for markdown */
img {
max-width: 100%;
}
-
pre,
- code {
+ code,
+ a {
white-space: pre-wrap;
+ word-break: break-all;
}
}
`
diff --git a/src/pages/Topic/PostItem/Judge.tsx b/src/pages/Topic/PostItem/Dialog/Judge.tsx
similarity index 100%
rename from src/pages/Topic/PostItem/Judge.tsx
rename to src/pages/Topic/PostItem/Dialog/Judge.tsx
diff --git a/src/pages/Topic/PostItem/Manage.tsx b/src/pages/Topic/PostItem/Dialog/Manage.tsx
similarity index 100%
rename from src/pages/Topic/PostItem/Manage.tsx
rename to src/pages/Topic/PostItem/Dialog/Manage.tsx
diff --git a/src/services/board.ts b/src/services/board.ts
index 51aa97c..63ddad7 100644
--- a/src/services/board.ts
+++ b/src/services/board.ts
@@ -13,32 +13,50 @@ export const getBoardsInfo = cacheService(
3600 * 24 * 7
)
+// Cache Map for Board
+let HAS_MAP = false
+const BOARD_MAP: {
+ [id: number]: IBoard
+} = {}
+
+/** 创建 BOARD_MAP */
+async function buildBoardMap() {
+ const res = await getBoardsInfo()
+ res.fail().succeed(boards => {
+ // 防止重复创建
+ if (HAS_MAP) {
+ return
+ }
+ for (const baseBoard of boards) {
+ for (const childBoard of baseBoard.boards) {
+ BOARD_MAP[childBoard.id] = childBoard
+ }
+ }
+ HAS_MAP = true
+ })
+}
+
/**
- * 通过版面Id获取版面名称(返回值不是 Try)
+ * 获取版面的版主信息(返回值不是 Try)
*/
-export const getBoardNameById = (() => {
- // cache
- let _hasMap = false
- const _BoardNameCacheMap: {
- [key: number]: string
- } = {}
+export async function getBoardMastersById(boardId: number) {
+ if (!HAS_MAP) {
+ await buildBoardMap()
+ }
- return async (id: number) => {
- if (!_hasMap) {
- const res = await getBoardsInfo()
- res.fail().succeed(boards => {
- for (const baseBoard of boards) {
- for (const childBoard of baseBoard.boards) {
- _BoardNameCacheMap[childBoard.id] = childBoard.name
- }
- }
- _hasMap = true
- })
- }
+ return BOARD_MAP[boardId].boardMasters || []
+}
- return _BoardNameCacheMap[id] || '版面不存在'
+/**
+ * 通过版面Id获取版面名称(返回值不是 Try)
+ */
+export async function getBoardNameById(boardId: number) {
+ if (!HAS_MAP) {
+ await buildBoardMap()
}
-})()
+
+ return BOARD_MAP[boardId].name || '版面不存在'
+}
/**
* 获取单个版面信息
diff --git a/src/version.ts b/src/version.ts
index 0404c53..f5fbf2e 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -2,4 +2,4 @@
* 版本号
*/
-export default 'v1.2.1-beta'
+export default 'v1.2.2-beta'