From 53a280cd723aa37554ff06d49e6841945af6fdcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B5=A9=E8=BF=9C?= Date: Mon, 8 Jun 2020 16:54:53 +0800 Subject: [PATCH] list view --- dist/article/article.css | 4 + dist/icons/fabric-icons-15-3807251b.woff | Bin 0 -> 13592 bytes dist/styles.css | 121 +++++++++++++++++++++- package.json | 1 + src/components/article.tsx | 2 +- src/components/cards/list-card.tsx | 28 +++++ src/components/context-menu.tsx | 23 +++- src/components/feeds/cards-feed.tsx | 4 +- src/components/feeds/feed.tsx | 19 +++- src/components/feeds/list-feed.tsx | 35 +++++++ src/components/nav.tsx | 11 +- src/components/page.tsx | 24 ++++- src/components/settings/groups.tsx | 1 + src/containers/context-menu-container.tsx | 17 ++- src/containers/feed-container.tsx | 15 +-- src/containers/nav-container.tsx | 6 +- src/containers/page-container.tsx | 3 +- src/scripts/models/app.ts | 18 +++- src/scripts/models/page.ts | 33 +++++- src/scripts/utils.ts | 10 ++ 20 files changed, 343 insertions(+), 32 deletions(-) create mode 100644 dist/icons/fabric-icons-15-3807251b.woff create mode 100644 src/components/cards/list-card.tsx create mode 100644 src/components/feeds/list-feed.tsx diff --git a/dist/article/article.css b/dist/article/article.css index 1351e00..ac8a255 100644 --- a/dist/article/article.css +++ b/dist/article/article.css @@ -17,6 +17,10 @@ a:hover, a:active { text-decoration: underline; } +#main { + max-width: 700px; + margin: 0 auto; +} #main > p.title { font-size: 1.25rem; line-height: 1.75rem; diff --git a/dist/icons/fabric-icons-15-3807251b.woff b/dist/icons/fabric-icons-15-3807251b.woff new file mode 100644 index 0000000000000000000000000000000000000000..7ddc4865a6bd5ddcf7b7b221d608971a6dee26e7 GIT binary patch literal 13592 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 F{{j7I8;Ae^ literal 0 HcmV?d00001 diff --git a/dist/styles.css b/dist/styles.css index dd10423..2d3b238 100644 --- a/dist/styles.css +++ b/dist/styles.css @@ -59,6 +59,10 @@ i.ms-Nav-chevron { user-select: none; overflow: hidden; } +#root > nav .btn, #root > nav span { + z-index: 1; + position: relative; +} nav .progress { position: fixed; top: 0; @@ -113,7 +117,7 @@ nav .progress { font-size: 14px; vertical-align: top; } -.btn-group .btn.system { +#root > nav .btn-group .btn.system { position: relative; z-index: 10; } @@ -301,11 +305,11 @@ img.favicon { height: 120%; box-shadow: inset 5px 0 20px #0004; } - .main.menu-on { + .main.menu-on, .list-main.menu-on { padding-left: 280px; } - nav.menu-on .btn-group .btn.hide-wide, .menu .btn-group .btn.hide-wide { + nav.hide-btns .btn-group .btn, nav.menu-on .btn-group .btn.hide-wide, .menu .btn-group .btn.hide-wide { display: none; } .btn-group .btn.inline-block-wide { @@ -335,9 +339,12 @@ img.favicon { .article-container .btn-group.next { right: calc(50% - 486px); } +.article { + height: 100%; +} .article webview { width: 100%; - height: calc(100vh - 86px); + height: calc(100% - 36px); border: none; } .article i.ms-Icon { @@ -358,6 +365,63 @@ img.favicon { white-space: nowrap; display: inline-block; } +.side-article-wrapper { + flex-grow: 1; + padding-top: 32px; + height: calc(100% - 32px); + background: #fff; +} +.side-article-wrapper .article { + display: flex; + flex-direction: column-reverse; +} +.side-article-wrapper .article .actions { + border-bottom: none; +} +.side-article-wrapper .article > .ms-Stack { + border-top: 1px solid #e1dfdd; +} +.list-feed-container::before, .side-article-wrapper::before { + content: ""; + display: block; + width: 100%; + border-bottom: 1px solid #e1dfdd; + position: absolute; + top: 31px; +} + +.list-main { + display: flex; + flex-wrap: wrap; + height: 100%; + position: relative; + top: -32px; + overflow: hidden; + background: #fff; +} +.list-feed-container { + width: 350px; + background-color: #faf9f8; + height: 100%; + position: relative; +} +.list-feed-container::after { + content: ""; + display: block; + pointer-events: none; + position: absolute; + top: -10%; + right: 0; + width: 120%; + height: 120%; + box-shadow: inset 5px 0 20px #0004; +} +.list-feed { + margin-top: 32px; + height: calc(100% - 32px); + overflow: hidden scroll; + position: relative; +} .cards-feed-container { display: inline-flex; @@ -496,4 +560,51 @@ img.favicon { } .card p.snippet.show { transform: none; -} \ No newline at end of file +} + +.list-card { + display: flex; + position: relative; + overflow: hidden; + color: #161514; + user-select: none; + transition: box-shadow linear .08s; + border-bottom: 1px solid #e1dfdd; + transform: scale(1); + cursor: pointer; + box-shadow: #0000 0px 5px 15px; +} +.list-card:hover { + box-shadow: #0004 0px 5px 15px; +} +.list-card:active { + box-shadow: #0000 0px 5px 15px, inset #0004 0px 0px 15px; +} +.list-card div.head { + width: 80px; + height: 80px; + margin: 8px 0 8px 10px; +} +.list-card div.head img { + width: 80px; + height: 80px; + object-fit: cover; + -webkit-user-drag: none; +} +.list-card .data { + flex-grow: 1; +} +.list-card .info { + margin: 8px 10px; +} +.list-card h3.title { + font-size: 14px; + line-height: 18px; + font-weight: 600; + margin: 8px 10px; + position: relative; + -webkit-line-clamp: 3; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; +} diff --git a/package.json b/package.json index 5d6d14d..331f46e 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "redux-devtools": "^3.5.0", "redux-thunk": "^2.3.0", "reselect": "^4.0.0", + "simplebar-react": "^2.2.0", "ts-loader": "^7.0.4", "typescript": "^3.9.2", "webpack": "^4.43.0", diff --git a/src/components/article.tsx b/src/components/article.tsx index d031f78..a9fc52e 100644 --- a/src/components/article.tsx +++ b/src/components/article.tsx @@ -152,7 +152,7 @@ class Article extends React.Component { diff --git a/src/components/cards/list-card.tsx b/src/components/cards/list-card.tsx new file mode 100644 index 0000000..80580f1 --- /dev/null +++ b/src/components/cards/list-card.tsx @@ -0,0 +1,28 @@ +import * as React from "react" +import { Card } from "./card" +import Time from "../utils/time" +import { AnimationClassNames } from "@fluentui/react" + +class ListCard extends Card { + render() { + return ( +
+ {this.props.item.thumb ? ( +
+ ) : null} +
+

+ {this.props.source.iconurl ? : null} + {this.props.source.name} +

+

{this.props.item.title}

+
+
+ ) + } +} + +export default ListCard \ No newline at end of file diff --git a/src/components/context-menu.tsx b/src/components/context-menu.tsx index f6072f1..bd9031d 100644 --- a/src/components/context-menu.tsx +++ b/src/components/context-menu.tsx @@ -6,17 +6,20 @@ import { ContextMenuType } from "../scripts/models/app" import { RSSItem } from "../scripts/models/item" import { ContextReduxProps } from "../containers/context-menu-container" import { FeedIdType } from "../scripts/models/feed" +import { ViewType } from "../scripts/models/page" export type ContextMenuProps = ContextReduxProps & { type: ContextMenuType - event?: MouseEvent + event?: MouseEvent | string position?: [number, number] item?: RSSItem feedId?: FeedIdType text?: string + viewType: ViewType showItem: (feedId: FeedIdType, item: RSSItem) => void markRead: (item: RSSItem) => void markUnread: (item: RSSItem) => void + switchView: (viewType: ViewType) => void close: () => void } @@ -88,6 +91,24 @@ export class ContextMenu extends React.Component { onClick: () => { googleSearch(this.props.text) } } ] + case ContextMenuType.View: return [ + { + key: "cardView", + text: "卡片视图", + iconProps: { iconName: "GridViewMedium" }, + canCheck: true, + checked: this.props.viewType === ViewType.Cards, + onClick: () => this.props.switchView(ViewType.Cards) + }, + { + key: "listView", + text: "列表视图", + iconProps: { iconName: "BacklogList" }, + canCheck: true, + checked: this.props.viewType === ViewType.List, + onClick: () => this.props.switchView(ViewType.List) + } + ] default: return [] } } diff --git a/src/components/feeds/cards-feed.tsx b/src/components/feeds/cards-feed.tsx index b5ddb71..854ba11 100644 --- a/src/components/feeds/cards-feed.tsx +++ b/src/components/feeds/cards-feed.tsx @@ -1,9 +1,9 @@ import * as React from "react" -import { Feed } from "./feed" +import { Feed, FeedProps } from "./feed" import DefaultCard from "../cards/default-card" import { PrimaryButton } from 'office-ui-fabric-react'; -class CardsFeed extends Feed { +class CardsFeed extends React.Component { state = { width: window.innerWidth - 12 } updateWidth = () => { diff --git a/src/components/feeds/feed.tsx b/src/components/feeds/feed.tsx index 024697d..8251805 100644 --- a/src/components/feeds/feed.tsx +++ b/src/components/feeds/feed.tsx @@ -2,9 +2,13 @@ import * as React from "react" import { RSSItem } from "../../scripts/models/item" import { FeedReduxProps } from "../../containers/feed-container" import { RSSFeed, FeedIdType } from "../../scripts/models/feed" +import { ViewType } from "../../scripts/models/page" +import CardsFeed from "./cards-feed" +import ListFeed from "./list-feed" -type FeedProps = FeedReduxProps & { +export type FeedProps = FeedReduxProps & { feed: RSSFeed + viewType: ViewType items: RSSItem[] sourceMap: Object markRead: (item: RSSItem) => void @@ -13,4 +17,15 @@ type FeedProps = FeedReduxProps & { showItem: (fid: FeedIdType, item: RSSItem) => void } -export class Feed extends React.Component { } \ No newline at end of file +export class Feed extends React.Component { + render() { + switch (this.props.viewType) { + case (ViewType.Cards): return ( + + ) + case (ViewType.List): return ( + + ) + } + } +} \ No newline at end of file diff --git a/src/components/feeds/list-feed.tsx b/src/components/feeds/list-feed.tsx new file mode 100644 index 0000000..b1b5ecb --- /dev/null +++ b/src/components/feeds/list-feed.tsx @@ -0,0 +1,35 @@ +import * as React from "react" +import { FeedProps } from "./feed" +import { PrimaryButton } from 'office-ui-fabric-react'; +import ListCard from "../cards/list-card"; + +class ListFeed extends React.Component { + render() { + return this.props.feed.loaded && ( +
+ { + this.props.items.map((item) => ( + + )) + } + { + (this.props.feed.loaded && !this.props.feed.allLoaded) + ?
this.props.loadMore(this.props.feed)} />
+ : null + } +
+ ) + } +} + +export default ListFeed \ No newline at end of file diff --git a/src/components/nav.tsx b/src/components/nav.tsx index 92223e5..0a1cabb 100644 --- a/src/components/nav.tsx +++ b/src/components/nav.tsx @@ -10,6 +10,7 @@ type NavProps = { fetch: () => void, menu: () => void, logs: () => void, + views: () => void, settings: () => void } @@ -59,6 +60,12 @@ class Nav extends React.Component { if (this.canFetch()) this.props.fetch() } + views = () => { + if (this.props.state.contextMenu.event !== "#view-toggle") { + this.props.views() + } + } + getProgress = () => { return this.props.state.fetchingTotal > 0 ? this.props.state.fetchingProgress / this.props.state.fetchingTotal @@ -78,7 +85,9 @@ class Nav extends React.Component { {this.props.state.logMenu.notify ? : } - + {if (this.props.state.contextMenu.event === "#view-toggle") e.stopPropagation()}}> + diff --git a/src/components/page.tsx b/src/components/page.tsx index 13ad4ce..02bb92e 100644 --- a/src/components/page.tsx +++ b/src/components/page.tsx @@ -3,12 +3,14 @@ import { FeedIdType } from "../scripts/models/feed" import { FeedContainer } from "../containers/feed-container" import { AnimationClassNames, Icon } from "@fluentui/react" import ArticleContainer from "../containers/article-container" +import { ViewType } from "../scripts/models/page" type PageProps = { menuOn: boolean settingsOn: boolean feeds: FeedIdType[] itemId: number + viewType: ViewType dismissItem: () => void offsetItem: (offset: number) => void } @@ -23,12 +25,13 @@ class Page extends React.Component { this.props.offsetItem(1) } - render = () => ( + render = () => this.props.viewType == ViewType.Cards + ? ( <> {this.props.settingsOn ? null :
{this.props.feeds.map(fid => ( - + ))}
} {this.props.itemId >= 0 && ( @@ -42,6 +45,23 @@ class Page extends React.Component { )} ) + : ( + <> + {this.props.settingsOn ? null : +
+
+ {this.props.feeds.map(fid => ( + + ))} +
+ {this.props.itemId >= 0 && ( +
+ +
+ )} +
} + + ) } export default Page \ No newline at end of file diff --git a/src/components/settings/groups.tsx b/src/components/settings/groups.tsx index df9c80c..7090697 100644 --- a/src/components/settings/groups.tsx +++ b/src/components/settings/groups.tsx @@ -272,6 +272,7 @@ class GroupsTab extends React.Component { selectionMode={SelectionMode.multiple} /> + 拖拽订阅源以排序 } {(!this.state.manageGroup || !this.state.selectedGroup) ?<> diff --git a/src/containers/context-menu-container.tsx b/src/containers/context-menu-container.tsx index 1a1bbba..206fbdc 100644 --- a/src/containers/context-menu-container.tsx +++ b/src/containers/context-menu-container.tsx @@ -4,14 +4,16 @@ import { RootState } from "../scripts/reducer" import { ContextMenuType, closeContextMenu } from "../scripts/models/app" import { ContextMenu } from "../components/context-menu" import { RSSItem, markRead, markUnread } from "../scripts/models/item" -import { showItem } from "../scripts/models/page" +import { showItem, switchView, ViewType } from "../scripts/models/page" import { FeedIdType } from "../scripts/models/feed" +import { setDefaultView } from "../scripts/utils" const getContext = (state: RootState) => state.app.contextMenu +const getViewType = (state: RootState) => state.page.viewType const mapStateToProps = createSelector( - [getContext], - (context) => { + [getContext, getViewType], + (context, viewType) => { switch (context.type) { case ContextMenuType.Item: return { type: context.type, @@ -24,6 +26,11 @@ const mapStateToProps = createSelector( position: context.position, text: context.target as string } + case ContextMenuType.View: return { + type: context.type, + event: context.event, + viewType: viewType + } default: return { type: ContextMenuType.Hidden } } } @@ -34,6 +41,10 @@ const mapDispatchToProps = dispatch => { showItem: (feedId: FeedIdType, item: RSSItem) => dispatch(showItem(feedId, item)), markRead: (item: RSSItem) => dispatch(markRead(item)), markUnread: (item: RSSItem) => dispatch(markUnread(item)), + switchView: (viewType: ViewType) => { + setDefaultView(viewType) + dispatch(switchView(viewType)) + }, close: () => dispatch(closeContextMenu()) } } diff --git a/src/containers/feed-container.tsx b/src/containers/feed-container.tsx index cef66d4..755801f 100644 --- a/src/containers/feed-container.tsx +++ b/src/containers/feed-container.tsx @@ -1,27 +1,30 @@ import { connect } from "react-redux" import { createSelector } from "reselect" import { RootState } from "../scripts/reducer" -import CardsFeed from "../components/feeds/cards-feed" import { markRead, RSSItem } from "../scripts/models/item" import { openItemMenu } from "../scripts/models/app" import { FeedIdType, loadMore, RSSFeed } from "../scripts/models/feed" -import { showItem } from "../scripts/models/page" +import { showItem, ViewType } from "../scripts/models/page" +import { Feed } from "../components/feeds/feed" interface FeedContainerProps { feedId: FeedIdType + viewType: ViewType } const getSources = (state: RootState) => state.sources const getItems = (state: RootState) => state.items const getFeed = (state: RootState, props: FeedContainerProps) => state.feeds[props.feedId] +const getView = (_, props: FeedContainerProps) => props.viewType const makeMapStateToProps = () => { return createSelector( - [getSources, getItems, getFeed], - (sources, items, feed) => ({ + [getSources, getItems, getFeed, getView], + (sources, items, feed, viewType) => ({ feed: feed, items: feed.iids.map(iid => items[iid]), - sourceMap: sources + sourceMap: sources, + viewType: viewType }) ) } @@ -36,4 +39,4 @@ const mapDispatchToProps = dispatch => { const connector = connect(makeMapStateToProps, mapDispatchToProps) export type FeedReduxProps = typeof connector -export const FeedContainer = connector(CardsFeed) \ No newline at end of file +export const FeedContainer = connector(Feed) \ No newline at end of file diff --git a/src/containers/nav-container.tsx b/src/containers/nav-container.tsx index c2b7a2f..2515526 100644 --- a/src/containers/nav-container.tsx +++ b/src/containers/nav-container.tsx @@ -2,11 +2,12 @@ import { connect } from "react-redux" import { createSelector } from "reselect" import { RootState } from "../scripts/reducer" import { fetchItems } from "../scripts/models/item" -import { toggleMenu, toggleLogMenu, toggleSettings } from "../scripts/models/app" +import { toggleMenu, toggleLogMenu, toggleSettings, openViewMenu } from "../scripts/models/app" +import { ViewType } from "../scripts/models/page" import Nav from "../components/nav" const getState = (state: RootState) => state.app -const getItemShown = (state: RootState) => state.page.itemId >= 0 +const getItemShown = (state: RootState) => (state.page.itemId >= 0) && state.page.viewType !== ViewType.List const mapStateToProps = createSelector( [getState, getItemShown], @@ -20,6 +21,7 @@ const mapDispatchToProps = (dispatch) => ({ fetch: () => dispatch(fetchItems()), menu: () => dispatch(toggleMenu()), logs: () => dispatch(toggleLogMenu()), + views: () => dispatch(openViewMenu()), settings: () => dispatch(toggleSettings()) }) diff --git a/src/containers/page-container.tsx b/src/containers/page-container.tsx index e411201..15cfeed 100644 --- a/src/containers/page-container.tsx +++ b/src/containers/page-container.tsx @@ -15,7 +15,8 @@ const mapStateToProps = createSelector( feeds: [page.feedId], settingsOn: settingsOn, menuOn: menuOn, - itemId: page.itemId + itemId: page.itemId, + viewType: page.viewType }) ) diff --git a/src/scripts/models/app.ts b/src/scripts/models/app.ts index f9276c8..6d56200 100644 --- a/src/scripts/models/app.ts +++ b/src/scripts/models/app.ts @@ -6,7 +6,7 @@ import { SourceGroupActionTypes, UPDATE_SOURCE_GROUP, ADD_SOURCE_TO_GROUP, DELET import { PageActionTypes, SELECT_PAGE, PageType, selectAllArticles } from "./page" export enum ContextMenuType { - Hidden, Item, Text + Hidden, Item, Text, View } export enum AppLogType { @@ -64,6 +64,7 @@ export class AppState { export const CLOSE_CONTEXT_MENU = "CLOSE_CONTEXT_MENU" export const OPEN_ITEM_MENU = "OPEN_ITEM_MENU" export const OPEN_TEXT_MENU = "OPEN_TEXT_MENU" +export const OPEN_VIEW_MENU = "OPEN_VIEW_MENU" interface CloseContextMenuAction { type: typeof CLOSE_CONTEXT_MENU @@ -82,7 +83,11 @@ interface OpenTextMenuAction { item: string } -export type ContextMenuActionTypes = CloseContextMenuAction | OpenItemMenuAction | OpenTextMenuAction +interface OpenViewMenuAction { + type: typeof OPEN_VIEW_MENU +} + +export type ContextMenuActionTypes = CloseContextMenuAction | OpenItemMenuAction | OpenTextMenuAction | OpenViewMenuAction export const TOGGLE_LOGS = "TOGGLE_LOGS" export interface LogMenuActionType { type: typeof TOGGLE_LOGS } @@ -121,6 +126,8 @@ export function openTextMenu(text: string, position: [number, number]): ContextM } } +export const openViewMenu = (): ContextMenuActionTypes => ({ type: OPEN_VIEW_MENU }) + export const toggleMenu = () => ({ type: TOGGLE_MENU }) export const toggleLogMenu = () => ({ type: TOGGLE_LOGS }) export const toggleSettings = () => ({ type: TOGGLE_SETTINGS }) @@ -274,6 +281,13 @@ export function appReducer( target: action.item } } + case OPEN_VIEW_MENU: return { + ...state, + contextMenu: { + type: ContextMenuType.View, + event: "#view-toggle" + } + } case TOGGLE_MENU: return { ...state, menu: !state.menu diff --git a/src/scripts/models/page.ts b/src/scripts/models/page.ts index 654d5ed..c01cbc8 100644 --- a/src/scripts/models/page.ts +++ b/src/scripts/models/page.ts @@ -1,8 +1,10 @@ import { ALL, SOURCE, FeedIdType, loadMore } from "./feed" -import { getWindowBreakpoint, AppThunk } from "../utils" -import { RSSItem, ItemActionTypes, MARK_READ, MARK_UNREAD, markRead } from "./item" +import { getWindowBreakpoint, AppThunk, getDefaultView } from "../utils" +import { RSSItem, markRead } from "./item" +import { SourceActionTypes, DELETE_SOURCE } from "./source" export const SELECT_PAGE = "SELECT_PAGE" +export const SWITCH_VIEW = "SWITCH_VIEW" export const SHOW_ITEM = "SHOW_ITEM" export const SHOW_OFFSET_ITEM = "SHOW_OFFSET_ITEM" export const DISMISS_ITEM = "DISMISS_ITEM" @@ -11,6 +13,10 @@ export enum PageType { AllArticles, Sources, Page } +export enum ViewType { + Cards, List, Customized +} + interface SelectPageAction { type: typeof SELECT_PAGE pageType: PageType @@ -21,6 +27,11 @@ interface SelectPageAction { title?: string } +interface SwitchViewAction { + type: typeof SWITCH_VIEW + viewType: ViewType +} + interface ShowItemAction { type: typeof SHOW_ITEM feedId: FeedIdType @@ -29,7 +40,7 @@ interface ShowItemAction { interface DismissItemAction { type: typeof DISMISS_ITEM } -export type PageActionTypes = SelectPageAction | ShowItemAction | DismissItemAction +export type PageActionTypes = SelectPageAction | SwitchViewAction | ShowItemAction | DismissItemAction export function selectAllArticles(init = false): PageActionTypes { return { @@ -52,6 +63,13 @@ export function selectSources(sids: number[], menuKey: string, title: string): P } } +export function switchView(viewType: ViewType): PageActionTypes { + return { + type: SWITCH_VIEW, + viewType: viewType + } +} + export function showItem(feedId: FeedIdType, item: RSSItem): PageActionTypes { return { type: SHOW_ITEM, @@ -90,13 +108,14 @@ export function showOffsetItem(offset: number): AppThunk { } export class PageState { + viewType = getDefaultView() feedId = ALL as FeedIdType itemId = -1 } export function pageReducer( state = new PageState(), - action: PageActionTypes + action: PageActionTypes | SourceActionTypes ): PageState { switch (action.type) { case SELECT_PAGE: @@ -111,10 +130,16 @@ export function pageReducer( } default: return state } + case SWITCH_VIEW: return { + ...state, + viewType: action.viewType, + itemId: action.viewType === ViewType.List ? state.itemId : -1 + } case SHOW_ITEM: return { ...state, itemId: action.item.id } + case DELETE_SOURCE: case DISMISS_ITEM: return { ...state, itemId: -1 diff --git a/src/scripts/utils.ts b/src/scripts/utils.ts index 6c69d7d..152d71e 100644 --- a/src/scripts/utils.ts +++ b/src/scripts/utils.ts @@ -48,6 +48,7 @@ export function setProxy(address = null) { } import ElectronProxyAgent = require("@yang991178/electron-proxy-agent") +import { ViewType } from "./models/page" let agent = new ElectronProxyAgent(remote.getCurrentWebContents().session) export const rssParser = new Parser({ customFields: customFields, @@ -92,3 +93,12 @@ export const cutText = (s: string, length: number) => { } export const googleSearch = (text: string) => openExternal("https://www.google.com/search?q=" + encodeURIComponent(text)) + +const VIEW_STORE_KEY = "view" +export const getDefaultView = () => { + let view = localStorage.getItem(VIEW_STORE_KEY) + return view ? parseInt(view) as ViewType : ViewType.Cards +} +export const setDefaultView = (viewType: ViewType) => { + localStorage.setItem(VIEW_STORE_KEY, String(viewType)) +} \ No newline at end of file