diff --git a/public/icons/static/shape/about.svg b/public/icons/static/shape/about.svg
new file mode 100644
index 000000000..30a25636b
--- /dev/null
+++ b/public/icons/static/shape/about.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/icons/static/shape/question.svg b/public/icons/static/shape/question.svg
new file mode 100644
index 000000000..14cf8acf0
--- /dev/null
+++ b/public/icons/static/shape/question.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/PromotionList/Holder.js b/src/components/PromotionList/Holder.js
deleted file mode 100755
index fb6656c10..000000000
--- a/src/components/PromotionList/Holder.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react'
-
-import { ICON_CMD } from '@/config'
-
-import {
- Wrapper,
- BannerWrapper,
- Logo,
- PartnerInfo,
- Title,
- Desc,
-} from './styles/holder'
-
-const Holder = () => (
-
-
-
- 空缺中
- 联系我们
-
-
-
-
-)
-
-export default React.memo(Holder)
diff --git a/src/components/PromotionList/PartnerBanner.js b/src/components/PromotionList/PartnerBanner.js
deleted file mode 100755
index 872b730e0..000000000
--- a/src/components/PromotionList/PartnerBanner.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react'
-
-import { ICON_CMD } from '@/config'
-import {
- Wrapper,
- ItemWrapper,
- PartnerInfo,
- Logo,
- Title,
- Desc,
-} from './styles/partner_banner'
-
-const PartnerBanner = () => (
-
-
-
-
- 拉勾网
- 一个找工作的网站
-
-
-
-
-
-
- xaudiopro
- 音频剪辑,格式转换
-
-
-
-)
-
-export default React.memo(PartnerBanner)
diff --git a/src/components/PromotionList/Spotlight.tsx b/src/components/PromotionList/Spotlight.tsx
new file mode 100755
index 000000000..72e4cf763
--- /dev/null
+++ b/src/components/PromotionList/Spotlight.tsx
@@ -0,0 +1,29 @@
+import React from 'react'
+
+import type { TItem } from './spec'
+import {
+ Wrapper,
+ ItemWrapper,
+ Header,
+ Logo,
+ Title,
+ Desc,
+} from './styles/spotlight'
+
+type TProps = {
+ item: TItem
+}
+
+const Spotlight: React.FC = ({ item }) => (
+
+
+
+ {item.desc}
+
+
+)
+
+export default React.memo(Spotlight)
diff --git a/src/components/PromotionList/WaitList.tsx b/src/components/PromotionList/WaitList.tsx
new file mode 100644
index 000000000..2c28df092
--- /dev/null
+++ b/src/components/PromotionList/WaitList.tsx
@@ -0,0 +1,28 @@
+import React from 'react'
+
+import type { TItem } from './spec'
+import { Wrapper, Item } from './styles/wait_list'
+
+type TProps = {
+ items: TItem[]
+ activeId: string
+ setActiveId: (id: string) => void
+}
+
+const WaitList: React.FC = ({ items, activeId, setActiveId }) => {
+ return (
+
+ {items.map((item) => (
+ - setActiveId(item.id)}
+ >
+ {item.title}
+
+ ))}
+
+ )
+}
+
+export default React.memo(WaitList)
diff --git a/src/components/PromotionList/fakeItems.ts b/src/components/PromotionList/fakeItems.ts
new file mode 100644
index 000000000..9cfe6d4d2
--- /dev/null
+++ b/src/components/PromotionList/fakeItems.ts
@@ -0,0 +1,34 @@
+import { ICON_CMD } from '@/config'
+
+const items = [
+ {
+ id: '1',
+ title: 'xaudiopro',
+ link: 'https://xxxx',
+ desc: '音频剪辑,格式转换, 音频剪辑,格式转换 音频剪辑',
+ cover: `${ICON_CMD}/test_ad.jpg`,
+ },
+ {
+ id: '2',
+ title: 'github',
+ link: 'https://xxxx',
+ desc: 'github 就不用多介绍了吧',
+ cover: `${ICON_CMD}/test_ad.jpg`,
+ },
+ {
+ id: '3',
+ title: 'xxoo',
+ link: 'https://xxxx',
+ desc: 'xxx 就不用多介绍了吧',
+ cover: `${ICON_CMD}/test_ad.jpg`,
+ },
+ {
+ id: '4',
+ title: 'xxoo2',
+ link: 'https://xxxx',
+ desc: 'xxx2 就不用多介绍了吧',
+ cover: `${ICON_CMD}/test_ad.jpg`,
+ },
+]
+
+export default items
diff --git a/src/components/PromotionList/index.js b/src/components/PromotionList/index.js
deleted file mode 100755
index 4c5740793..000000000
--- a/src/components/PromotionList/index.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *
- * PromotionList
- *
- */
-
-import React from 'react'
-import T from 'prop-types'
-
-import { ICON_CMD } from '@/config'
-import { buildLog } from '@/utils'
-import PartnerBanner from './PartnerBanner'
-// import Holder from './Holder'
-
-import { Wrapper, Header, Title, AboutIcon } from './styles'
-
-/* eslint-disable-next-line */
-const log = buildLog('c:PromotionList:index')
-
-const PromotionList = ({ show, onAbout }) => {
- return (
-
- {show && (
- <>
-
-
- >
- )}
-
- )
-}
-
-PromotionList.propTypes = {
- show: T.bool,
- onAbout: T.func,
-}
-
-PromotionList.defaultProps = {
- show: true,
- onAbout: log,
-}
-
-export default React.memo(PromotionList)
diff --git a/src/components/PromotionList/index.tsx b/src/components/PromotionList/index.tsx
new file mode 100755
index 000000000..f76a76a50
--- /dev/null
+++ b/src/components/PromotionList/index.tsx
@@ -0,0 +1,76 @@
+/*
+ *
+ * PromotionList
+ *
+ */
+
+import React, { useState, useRef } from 'react'
+import { findIndex } from 'ramda'
+
+import { ICON } from '@/config'
+import { buildLog } from '@/utils'
+import { useInterval } from '@/hooks'
+
+import useHoverDirty from 'react-use/lib/useHoverDirty'
+
+import type { TItem } from './spec'
+import Spotlight from './Spotlight'
+import WaitList from './WaitList'
+
+import { Wrapper, Header, Title, AboutIcon } from './styles'
+import fakeItems from './fakeItems'
+
+/* eslint-disable-next-line */
+const log = buildLog('c:PromotionList:index')
+
+type TProps = {
+ show?: boolean
+ onAbout?: () => void
+ items: TItem[]
+ intervalSec?: number
+}
+
+const PromotionList: React.FC = ({
+ show = true,
+ onAbout = log,
+ items = fakeItems,
+ intervalSec = 5000,
+}) => {
+ const [activeId, setActiveId] = useState(items[0].id)
+ const activeItemIndex = findIndex((item) => item.id === activeId, items)
+ const activeItem = items[activeItemIndex]
+
+ const ref = useRef(null)
+
+ const isHovering = useHoverDirty(ref)
+
+ useInterval(() => {
+ if (isHovering) return
+ const nextIndex =
+ activeItemIndex < items.length - 1 ? activeItemIndex + 1 : 0
+ setActiveId(items[nextIndex].id)
+ }, intervalSec)
+
+ return (
+
+ {show && (
+ <>
+
+
+
+ >
+ )}
+
+ )
+}
+
+export default React.memo(PromotionList)
diff --git a/src/components/PromotionList/spec.ts b/src/components/PromotionList/spec.ts
new file mode 100644
index 000000000..9cc1f7d44
--- /dev/null
+++ b/src/components/PromotionList/spec.ts
@@ -0,0 +1,7 @@
+export type TItem = {
+ id: string
+ title: string
+ link: string
+ desc: string
+ cover?: string
+}
diff --git a/src/components/PromotionList/styles/holder.ts b/src/components/PromotionList/styles/holder.ts
deleted file mode 100755
index c7720ed7f..000000000
--- a/src/components/PromotionList/styles/holder.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import styled from 'styled-components'
-
-import { theme, css } from '@/utils'
-import Img from '@/Img'
-
-export const Wrapper = styled.div`
- ${css.flex()};
-`
-export const BannerWrapper = styled.div`
- ${css.flex()};
- margin-bottom: 12px;
-`
-export const Logo = styled(Img)`
- fill: ${theme('thread.articleDigest')};
- ${css.size(30)};
- opacity: 0;
- ${BannerWrapper}:hover & {
- opacity: 1;
- }
- transition: all 0.2s;
- transition-delay: 0.2s;
-`
-export const PartnerInfo = styled.div`
- ${css.flexColumn('justify-center', 'align-center')};
- margin-right: 40px;
-`
-export const Title = styled.div`
- color: ${theme('thread.articleTitle')};
- font-size: 0.9rem;
-`
-export const Desc = styled.div`
- color: ${theme('thread.articleDigest')};
- font-size: 0.8rem;
- opacity: 0;
- ${BannerWrapper}:hover & {
- opacity: 1;
- }
- transition: all 0.2s;
-`
diff --git a/src/components/PromotionList/styles/index.ts b/src/components/PromotionList/styles/index.ts
index 13d01afef..0d52c6c9f 100755
--- a/src/components/PromotionList/styles/index.ts
+++ b/src/components/PromotionList/styles/index.ts
@@ -5,30 +5,25 @@ import { css, theme } from '@/utils'
export const Wrapper = styled.div`
margin-left: 20px;
- margin-top: 40px;
+ margin-top: 30px;
min-height: 150px;
max-width: 180px;
`
export const Header = styled.div`
${css.flex('justify-between', 'align-center')};
+ padding-top: 20px;
margin-bottom: 12px;
+ border-top: 1px solid;
+ border-top-color: ${theme('thread.articleSpliter')};
+ width: 80%;
`
export const Title = styled.div`
+ font-size: 12px;
color: ${theme('thread.articleDigest')};
- border-top: 1px solid;
- border-top-color: ${theme('thread.articleSpliter')};
padding-top: 15px;
- width: 80px;
-
- ${Wrapper}:hover & {
- width: 100%;
- }
-
- transition: width 0.3s;
- transition-delay: 0.2s;
`
export const AboutIcon = styled(Img)`
- ${css.size(14)};
+ ${css.size(15)};
fill: ${theme('thread.articleDigest')};
opacity: 0;
margin-top: 16px;
@@ -41,6 +36,5 @@ export const AboutIcon = styled(Img)`
${Wrapper}:hover & {
opacity: 1;
}
- transition: all 0.2s;
- transition-delay: 0.2s;
+ transition: all 0.25s;
`
diff --git a/src/components/PromotionList/styles/partner_banner.ts b/src/components/PromotionList/styles/partner_banner.ts
deleted file mode 100755
index 0f79c8550..000000000
--- a/src/components/PromotionList/styles/partner_banner.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import styled from 'styled-components'
-
-import { css, theme } from '@/utils'
-import Img from '@/Img'
-
-export const Wrapper = styled.div`
- ${css.flexColumn()};
-`
-export const ItemWrapper = styled.div`
- ${css.flex('align-center')};
- padding: 8px 0;
-
- &:hover {
- cursor: pointer;
- padding: 8px 5px;
- border-radius: 4px;
- }
- transition: all 0.25s;
- transition-delay: 0.2s;
-`
-export const Logo = styled(Img)`
- ${css.size(20)};
- border-radius: 5px;
- /* TODO: for dark themes */
- filter: saturate(0.8);
- transition: all 0.25s;
- transition-delay: 0.2s;
-
- ${ItemWrapper}:hover & {
- width: 24px;
- height: 24px;
- }
-`
-export const PartnerInfo = styled.div`
- ${css.flexColumn('justify-center')};
- margin-left: 10px;
-`
-export const Title = styled.div`
- color: ${theme('thread.articleTitle')};
- font-size: 13px;
-
- ${ItemWrapper}:hover & {
- font-weight: bold;
- }
-`
-export const Desc = styled.div`
- color: ${theme('thread.articleDigest')};
- font-size: 12px;
- height: 1px;
- overflow-y: hidden;
-
- ${ItemWrapper}:hover & {
- height: 14px;
- }
- transition: all 0.25s;
- transition-delay: 0.2s;
-`
diff --git a/src/components/PromotionList/styles/spotlight.ts b/src/components/PromotionList/styles/spotlight.ts
new file mode 100755
index 000000000..31aab17f8
--- /dev/null
+++ b/src/components/PromotionList/styles/spotlight.ts
@@ -0,0 +1,37 @@
+import styled from 'styled-components'
+
+import { css, theme } from '@/utils'
+import Img from '@/Img'
+
+export const Wrapper = styled.div`
+ ${css.flexColumn()};
+ background: #06303b;
+ padding: 15px 10px;
+ width: 100%;
+ min-height: 120px;
+ border-radius: 5px;
+ margin-left: -12px;
+`
+export const ItemWrapper = styled.div`
+ ${css.flexColumn()};
+`
+export const Header = styled.div`
+ ${css.flex('align-center', 'justify-between')};
+ margin-left: 2px;
+`
+export const Logo = styled(Img)`
+ ${css.size(25)};
+ border-radius: 5px;
+ margin-right: 10px;
+ /* TODO: for dark themes */
+ filter: saturate(0.8);
+`
+export const Title = styled.div`
+ color: ${theme('thread.articleTitle')};
+ font-size: 15px;
+`
+export const Desc = styled.div`
+ color: ${theme('thread.articleDigest')};
+ font-size: 12px;
+ margin-top: 12px;
+`
diff --git a/src/components/PromotionList/styles/wait_list.ts b/src/components/PromotionList/styles/wait_list.ts
new file mode 100644
index 000000000..aa5c0c9a0
--- /dev/null
+++ b/src/components/PromotionList/styles/wait_list.ts
@@ -0,0 +1,27 @@
+import styled from 'styled-components'
+
+import type { TActive } from '@/spec'
+import { css, theme } from '@/utils'
+
+export const Wrapper = styled.div`
+ ${css.flex('align-center')};
+ flex-wrap: wrap;
+ width: 100%;
+ margin-top: 10px;
+ margin-left: -2px;
+`
+export const Item = styled.div`
+ ${css.cutRest('50px')};
+ color: ${({ active }) =>
+ active ? theme('thread.articleTitle') : theme('thread.articleDigest')};
+ font-size: 12px;
+ margin-right: 8px;
+ margin-bottom: 2px;
+
+ &:hover {
+ cursor: pointer;
+ color: ${theme('thread.articleTitle')};
+ }
+
+ transition: all 0.3s;
+`
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
index 1c0b6637c..44f925356 100755
--- a/src/hooks/index.ts
+++ b/src/hooks/index.ts
@@ -11,5 +11,6 @@ export { default as useDevice } from './useDevice'
export { default as useNetwork } from 'react-use/lib/useNetwork'
export { default as useCopyToClipboard } from 'react-use/lib/useCopyToClipboard'
+export { default as useInterval } from 'react-use/lib/useInterval'
export { useSwipeable as useSwipe } from 'react-swipeable'