mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-04-25 15:38:49 +02:00
lazily load feeds
This commit is contained in:
parent
59c5d663f1
commit
c64a4593a6
5
dist/styles.css
vendored
5
dist/styles.css
vendored
@ -635,6 +635,11 @@ body.darwin .list-main .article-search {
|
|||||||
overflow: hidden scroll;
|
overflow: hidden scroll;
|
||||||
margin-top: var(--navHeight);
|
margin-top: var(--navHeight);
|
||||||
}
|
}
|
||||||
|
.cards-feed-container .ms-List-page {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
.cards-feed-container > div.load-more-wrapper, .flex-fix {
|
.cards-feed-container > div.load-more-wrapper, .flex-fix {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { ipcRenderer } from "electron"
|
import { ipcRenderer } from "electron"
|
||||||
|
|
||||||
const utilsBridge = {
|
const utilsBridge = {
|
||||||
|
platform: process.platform,
|
||||||
|
|
||||||
getVersion: (): string => {
|
getVersion: (): string => {
|
||||||
return ipcRenderer.sendSync("get-version")
|
return ipcRenderer.sendSync("get-version")
|
||||||
},
|
},
|
||||||
|
@ -129,7 +129,7 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||||||
})
|
})
|
||||||
this.webview = webview
|
this.webview = webview
|
||||||
webview.focus()
|
webview.focus()
|
||||||
let card = document.querySelector(`#refocus>div[data-iid="${this.props.item._id}"]`) as HTMLElement
|
let card = document.querySelector(`#refocus div[data-iid="${this.props.item._id}"]`) as HTMLElement
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (card) card.scrollIntoViewIfNeeded()
|
if (card) card.scrollIntoViewIfNeeded()
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount = () => {
|
componentWillUnmount = () => {
|
||||||
let refocus = document.querySelector(`#refocus>div[data-iid="${this.props.item._id}"]`) as HTMLElement
|
let refocus = document.querySelector(`#refocus div[data-iid="${this.props.item._id}"]`) as HTMLElement
|
||||||
if (refocus) refocus.focus()
|
if (refocus) refocus.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,52 +2,52 @@ import * as React from "react"
|
|||||||
import { RSSSource, SourceOpenTarget } from "../../scripts/models/source"
|
import { RSSSource, SourceOpenTarget } from "../../scripts/models/source"
|
||||||
import { RSSItem } from "../../scripts/models/item"
|
import { RSSItem } from "../../scripts/models/item"
|
||||||
|
|
||||||
export interface CardProps {
|
export namespace Card {
|
||||||
feedId: string
|
export type Props = {
|
||||||
item: RSSItem
|
feedId: string
|
||||||
source: RSSSource
|
item: RSSItem
|
||||||
shortcuts: (item: RSSItem, key: string) => void
|
source: RSSSource
|
||||||
markRead: (item: RSSItem) => void
|
shortcuts: (item: RSSItem, key: string) => void
|
||||||
contextMenu: (feedId: string, item: RSSItem, e) => void
|
markRead: (item: RSSItem) => void
|
||||||
showItem: (fid: string, item: RSSItem) => void
|
contextMenu: (feedId: string, item: RSSItem, e) => void
|
||||||
}
|
showItem: (fid: string, item: RSSItem) => void
|
||||||
|
|
||||||
export class Card extends React.Component<CardProps> {
|
|
||||||
openInBrowser = () => {
|
|
||||||
this.props.markRead(this.props.item)
|
|
||||||
window.utils.openExternal(this.props.item.link)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick = (e: React.MouseEvent) => {
|
export const openInBrowser = (props: Props) => {
|
||||||
|
props.markRead(props.item)
|
||||||
|
window.utils.openExternal(props.item.link)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const onClick = (props: Props, e: React.MouseEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
switch (this.props.source.openTarget) {
|
switch (props.source.openTarget) {
|
||||||
case SourceOpenTarget.Local:
|
case SourceOpenTarget.Local:
|
||||||
case SourceOpenTarget.Webpage: {
|
case SourceOpenTarget.Webpage: {
|
||||||
this.props.markRead(this.props.item)
|
props.markRead(props.item)
|
||||||
this.props.showItem(this.props.feedId, this.props.item)
|
props.showItem(props.feedId, props.item)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case SourceOpenTarget.External: {
|
case SourceOpenTarget.External: {
|
||||||
this.openInBrowser()
|
openInBrowser(props)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseUp = (e: React.MouseEvent) => {
|
export const onMouseUp = (props: Props, e: React.MouseEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
switch (e.button) {
|
switch (e.button) {
|
||||||
case 1:
|
case 1:
|
||||||
this.openInBrowser()
|
openInBrowser(props)
|
||||||
break
|
break
|
||||||
case 2:
|
case 2:
|
||||||
this.props.contextMenu(this.props.feedId, this.props.item, e)
|
props.contextMenu(props.feedId, props.item, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown = (e: React.KeyboardEvent) => {
|
export const onKeyDown = (props: Props, e: React.KeyboardEvent) => {
|
||||||
this.props.shortcuts(this.props.item, e.key)
|
props.shortcuts(props.item, e.key)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,38 +1,33 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Card } from "./card"
|
import { Card } from "./card"
|
||||||
import { AnimationClassNames } from "@fluentui/react"
|
|
||||||
import CardInfo from "./info"
|
import CardInfo from "./info"
|
||||||
|
|
||||||
class DefaultCard extends Card {
|
const className = (props: Card.Props) => {
|
||||||
className = () => {
|
let cn = ["card"]
|
||||||
let cn = ["card", AnimationClassNames.slideUpIn10]
|
if (props.item.snippet && props.item.thumb) cn.push("transform")
|
||||||
if (this.props.item.snippet && this.props.item.thumb) cn.push("transform")
|
if (props.item.hidden) cn.push("hidden")
|
||||||
if (this.props.item.hidden) cn.push("hidden")
|
return cn.join(" ")
|
||||||
return cn.join(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={this.className()}
|
|
||||||
onClick={this.onClick}
|
|
||||||
onMouseUp={this.onMouseUp}
|
|
||||||
onKeyDown={this.onKeyDown}
|
|
||||||
data-iid={this.props.item._id}
|
|
||||||
data-is-focusable>
|
|
||||||
{this.props.item.thumb ? (
|
|
||||||
<img className="bg" src={this.props.item.thumb} />
|
|
||||||
) : null}
|
|
||||||
<div className="bg"></div>
|
|
||||||
{this.props.item.thumb ? (
|
|
||||||
<img className="head" src={this.props.item.thumb} />
|
|
||||||
) : null}
|
|
||||||
<CardInfo source={this.props.source} item={this.props.item} />
|
|
||||||
<h3 className="title">{this.props.item.title}</h3>
|
|
||||||
<p className={"snippet"+(this.props.item.thumb?"":" show")}>{this.props.item.snippet.slice(0, 325)}</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DefaultCard: React.FunctionComponent<Card.Props> = (props) => (
|
||||||
|
<div
|
||||||
|
className={className(props)}
|
||||||
|
onClick={e => Card.onClick(props, e)}
|
||||||
|
onMouseUp={e => Card.onMouseUp(props, e)}
|
||||||
|
onKeyDown={e => Card.onKeyDown(props, e)}
|
||||||
|
data-iid={props.item._id}
|
||||||
|
data-is-focusable>
|
||||||
|
{props.item.thumb ? (
|
||||||
|
<img className="bg" src={props.item.thumb} />
|
||||||
|
) : null}
|
||||||
|
<div className="bg"></div>
|
||||||
|
{props.item.thumb ? (
|
||||||
|
<img className="head" src={props.item.thumb} />
|
||||||
|
) : null}
|
||||||
|
<CardInfo source={props.source} item={props.item} />
|
||||||
|
<h3 className="title">{props.item.title}</h3>
|
||||||
|
<p className={"snippet" + (props.item.thumb ? "" : " show")}>{props.item.snippet.slice(0, 325)}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
export default DefaultCard
|
export default DefaultCard
|
@ -8,7 +8,7 @@ type CardInfoProps = {
|
|||||||
item: RSSItem
|
item: RSSItem
|
||||||
}
|
}
|
||||||
|
|
||||||
const CardInfo = (props: CardInfoProps) => (
|
const CardInfo: React.FunctionComponent<CardInfoProps> = (props) => (
|
||||||
<p className="info">
|
<p className="info">
|
||||||
{props.source.iconurl ? <img src={props.source.iconurl} /> : null}
|
{props.source.iconurl ? <img src={props.source.iconurl} /> : null}
|
||||||
<span className="name">{props.source.name}</span>
|
<span className="name">{props.source.name}</span>
|
||||||
|
@ -1,34 +1,29 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Card } from "./card"
|
import { Card } from "./card"
|
||||||
import { AnimationClassNames } from "@fluentui/react"
|
|
||||||
import CardInfo from "./info"
|
import CardInfo from "./info"
|
||||||
|
|
||||||
class ListCard extends Card {
|
const className = (props: Card.Props) => {
|
||||||
className = () => {
|
let cn = ["list-card"]
|
||||||
let cn = ["list-card", AnimationClassNames.slideUpIn10]
|
if (props.item.hidden) cn.push("hidden")
|
||||||
if (this.props.item.hidden) cn.push("hidden")
|
return cn.join(" ")
|
||||||
return cn.join(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={this.className()}
|
|
||||||
onClick={this.onClick}
|
|
||||||
onMouseUp={this.onMouseUp}
|
|
||||||
onKeyDown={this.onKeyDown}
|
|
||||||
data-iid={this.props.item._id}
|
|
||||||
data-is-focusable>
|
|
||||||
{this.props.item.thumb ? (
|
|
||||||
<div className="head"><img src={this.props.item.thumb} /></div>
|
|
||||||
) : null}
|
|
||||||
<div className="data">
|
|
||||||
<CardInfo source={this.props.source} item={this.props.item} />
|
|
||||||
<h3 className="title">{this.props.item.title}</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ListCard: React.FunctionComponent<Card.Props> = (props) => (
|
||||||
|
<div
|
||||||
|
className={className(props)}
|
||||||
|
onClick={e => Card.onClick(props, e)}
|
||||||
|
onMouseUp={e => Card.onMouseUp(props, e)}
|
||||||
|
onKeyDown={e => Card.onKeyDown(props, e)}
|
||||||
|
data-iid={props.item._id}
|
||||||
|
data-is-focusable>
|
||||||
|
{props.item.thumb ? (
|
||||||
|
<div className="head"><img src={props.item.thumb} /></div>
|
||||||
|
) : null}
|
||||||
|
<div className="data">
|
||||||
|
<CardInfo source={props.source} item={props.item} />
|
||||||
|
<h3 className="title">{props.item.title}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
export default ListCard
|
export default ListCard
|
@ -3,49 +3,66 @@ import intl from "react-intl-universal"
|
|||||||
import { FeedProps } from "./feed"
|
import { FeedProps } from "./feed"
|
||||||
import DefaultCard from "../cards/default-card"
|
import DefaultCard from "../cards/default-card"
|
||||||
import { PrimaryButton, FocusZone } from 'office-ui-fabric-react';
|
import { PrimaryButton, FocusZone } from 'office-ui-fabric-react';
|
||||||
|
import { RSSItem } from "../../scripts/models/item";
|
||||||
|
import { List, AnimationClassNames } from "@fluentui/react";
|
||||||
|
|
||||||
class CardsFeed extends React.Component<FeedProps> {
|
class CardsFeed extends React.Component<FeedProps> {
|
||||||
state = { width: window.innerWidth - 12 }
|
observer: ResizeObserver
|
||||||
|
state = { width: window.innerWidth, height: window.innerHeight }
|
||||||
|
|
||||||
updateWidth = () => {
|
updateWindowSize = (entries: ResizeObserverEntry[]) => {
|
||||||
this.setState({ width: window.innerWidth - 12 });
|
if (entries) {
|
||||||
|
this.setState({ width: entries[0].contentRect.width - 40, height: window.innerHeight })
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
window.addEventListener('resize', this.updateWidth);
|
this.setState({ width: document.querySelector(".main").clientWidth - 40 })
|
||||||
|
this.observer = new ResizeObserver(this.updateWindowSize)
|
||||||
|
this.observer.observe(document.querySelector(".main"))
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('resize', this.updateWidth);
|
this.observer.disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
flexFix = () => {
|
getItemCountForPage = () => {
|
||||||
let elemPerRow = Math.floor(this.state.width / 280)
|
let elemPerRow = Math.floor(this.state.width / 280)
|
||||||
//let elemLastRow = this.props.items.length % elemPerRow
|
let rows = Math.ceil(this.state.height / 304)
|
||||||
let fixes = new Array<JSX.Element>()
|
return elemPerRow * rows
|
||||||
for (let i = 0; i < elemPerRow; i += 1) {
|
|
||||||
fixes.push(<div className="flex-fix" key={"f-"+i}></div>)
|
|
||||||
}
|
|
||||||
return fixes
|
|
||||||
}
|
}
|
||||||
|
getPageHeight = () => {
|
||||||
|
return this.state.height + (304 - this.state.height % 304)
|
||||||
|
}
|
||||||
|
|
||||||
|
flexFixItems = () => {
|
||||||
|
let elemPerRow = Math.floor(this.state.width / 280)
|
||||||
|
let elemLastRow = this.props.items.length % elemPerRow
|
||||||
|
let items = [ ...this.props.items ]
|
||||||
|
for (let i = 0; i < (elemPerRow - elemLastRow); i += 1) items.push(null)
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
onRenderItem = (item: RSSItem, index: number) => item ? (
|
||||||
|
<DefaultCard
|
||||||
|
feedId={this.props.feed._id}
|
||||||
|
key={item._id}
|
||||||
|
item={item}
|
||||||
|
source={this.props.sourceMap[item.source]}
|
||||||
|
shortcuts={this.props.shortcuts}
|
||||||
|
markRead={this.props.markRead}
|
||||||
|
contextMenu={this.props.contextMenu}
|
||||||
|
showItem={this.props.showItem} />
|
||||||
|
) : (<div className="flex-fix" key={"f-"+index}></div>)
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return this.props.feed.loaded && (
|
return this.props.feed.loaded && (
|
||||||
<FocusZone as="div" id="refocus" className="cards-feed-container">
|
<FocusZone as="div" id="refocus" className="cards-feed-container" data-is-scrollable>
|
||||||
{
|
<List
|
||||||
this.props.items.map((item) => (
|
className={AnimationClassNames.slideUpIn10}
|
||||||
<DefaultCard
|
items={this.flexFixItems()}
|
||||||
feedId={this.props.feed._id}
|
onRenderCell={this.onRenderItem}
|
||||||
key={item._id}
|
getItemCountForPage={this.getItemCountForPage}
|
||||||
item={item}
|
getPageHeight={this.getPageHeight}
|
||||||
source={this.props.sourceMap[item.source]}
|
usePageCache />
|
||||||
shortcuts={this.props.shortcuts}
|
|
||||||
markRead={this.props.markRead}
|
|
||||||
contextMenu={this.props.contextMenu}
|
|
||||||
showItem={this.props.showItem} />
|
|
||||||
))
|
|
||||||
}
|
|
||||||
{ this.flexFix() }
|
|
||||||
{
|
{
|
||||||
(this.props.feed.loaded && !this.props.feed.allLoaded)
|
(this.props.feed.loaded && !this.props.feed.allLoaded)
|
||||||
? <div className="load-more-wrapper"><PrimaryButton
|
? <div className="load-more-wrapper"><PrimaryButton
|
||||||
|
@ -1,26 +1,32 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import intl from "react-intl-universal"
|
import intl from "react-intl-universal"
|
||||||
import { FeedProps } from "./feed"
|
import { FeedProps } from "./feed"
|
||||||
import { DefaultButton, FocusZone, FocusZoneDirection } from 'office-ui-fabric-react';
|
import { DefaultButton, FocusZone, FocusZoneDirection, List } from 'office-ui-fabric-react';
|
||||||
import ListCard from "../cards/list-card";
|
import ListCard from "../cards/list-card";
|
||||||
|
import { RSSItem } from "../../scripts/models/item";
|
||||||
|
import { AnimationClassNames } from "@fluentui/react";
|
||||||
|
|
||||||
class ListFeed extends React.Component<FeedProps> {
|
class ListFeed extends React.Component<FeedProps> {
|
||||||
|
onRenderItem = (item: RSSItem) => (
|
||||||
|
<ListCard
|
||||||
|
feedId={this.props.feed._id}
|
||||||
|
key={item._id}
|
||||||
|
item={item}
|
||||||
|
source={this.props.sourceMap[item.source]}
|
||||||
|
shortcuts={this.props.shortcuts}
|
||||||
|
markRead={this.props.markRead}
|
||||||
|
contextMenu={this.props.contextMenu}
|
||||||
|
showItem={this.props.showItem} />
|
||||||
|
)
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return this.props.feed.loaded && (
|
return this.props.feed.loaded && (
|
||||||
<FocusZone as="div" id="refocus" direction={FocusZoneDirection.vertical} className="list-feed">
|
<FocusZone as="div" id="refocus" direction={FocusZoneDirection.vertical} className="list-feed" data-is-scrollable>
|
||||||
{
|
<List
|
||||||
this.props.items.map((item) => (
|
className={AnimationClassNames.slideUpIn10}
|
||||||
<ListCard
|
items={this.props.items}
|
||||||
feedId={this.props.feed._id}
|
onRenderCell={this.onRenderItem}
|
||||||
key={item._id}
|
usePageCache />
|
||||||
item={item}
|
|
||||||
source={this.props.sourceMap[item.source]}
|
|
||||||
shortcuts={this.props.shortcuts}
|
|
||||||
markRead={this.props.markRead}
|
|
||||||
contextMenu={this.props.contextMenu}
|
|
||||||
showItem={this.props.showItem} />
|
|
||||||
))
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
(this.props.feed.loaded && !this.props.feed.allLoaded)
|
(this.props.feed.loaded && !this.props.feed.allLoaded)
|
||||||
? <div className="load-more-wrapper"><DefaultButton
|
? <div className="load-more-wrapper"><DefaultButton
|
||||||
|
@ -120,7 +120,7 @@ export class Menu extends React.Component<MenuProps> {
|
|||||||
<div className="btn-group">
|
<div className="btn-group">
|
||||||
<a className="btn hide-wide" title={intl.get("menu.close")} onClick={this.props.toggleMenu}><Icon iconName="Back" /></a>
|
<a className="btn hide-wide" title={intl.get("menu.close")} onClick={this.props.toggleMenu}><Icon iconName="Back" /></a>
|
||||||
<a className="btn inline-block-wide" title={intl.get("menu.close")} onClick={this.props.toggleMenu}>
|
<a className="btn inline-block-wide" title={intl.get("menu.close")} onClick={this.props.toggleMenu}>
|
||||||
<Icon iconName={process.platform === "darwin" ? "SidePanel" : "GlobalNavButton"} />
|
<Icon iconName={window.utils.platform === "darwin" ? "SidePanel" : "GlobalNavButton"} />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="nav-wrapper">
|
<div className="nav-wrapper">
|
||||||
|
@ -111,7 +111,7 @@ class Nav extends React.Component<NavProps, NavState> {
|
|||||||
<a className="btn hide-wide"
|
<a className="btn hide-wide"
|
||||||
title={intl.get("nav.menu")}
|
title={intl.get("nav.menu")}
|
||||||
onClick={this.props.menu}>
|
onClick={this.props.menu}>
|
||||||
<Icon iconName={process.platform === "darwin" ? "SidePanel" : "GlobalNavButton"} />
|
<Icon iconName={window.utils.platform === "darwin" ? "SidePanel" : "GlobalNavButton"} />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<span className="title">{this.props.state.title}</span>
|
<span className="title">{this.props.state.title}</span>
|
||||||
|
242
src/components/utils/ResizeObserver.d.ts
vendored
Normal file
242
src/components/utils/ResizeObserver.d.ts
vendored
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
/**
|
||||||
|
* The **ResizeObserver** interface reports changes to the dimensions of an
|
||||||
|
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element)'s content
|
||||||
|
* or border box, or the bounding box of an
|
||||||
|
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement).
|
||||||
|
*
|
||||||
|
* > **Note**: The content box is the box in which content can be placed,
|
||||||
|
* > meaning the border box minus the padding and border width. The border box
|
||||||
|
* > encompasses the content, padding, and border. See
|
||||||
|
* > [The box model](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model)
|
||||||
|
* > for further explanation.
|
||||||
|
*
|
||||||
|
* `ResizeObserver` avoids infinite callback loops and cyclic dependencies that
|
||||||
|
* are often created when resizing via a callback function. It does this by only
|
||||||
|
* processing elements deeper in the DOM in subsequent frames. Implementations
|
||||||
|
* should, if they follow the specification, invoke resize events before paint
|
||||||
|
* and after layout.
|
||||||
|
*
|
||||||
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
|
||||||
|
*/
|
||||||
|
declare class ResizeObserver {
|
||||||
|
/**
|
||||||
|
* The **ResizeObserver** constructor creates a new `ResizeObserver` object,
|
||||||
|
* which can be used to report changes to the content or border box of an
|
||||||
|
* `Element` or the bounding box of an `SVGElement`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* var ResizeObserver = new ResizeObserver(callback)
|
||||||
|
*
|
||||||
|
* @param callback
|
||||||
|
* The function called whenever an observed resize occurs. The function is
|
||||||
|
* called with two parameters:
|
||||||
|
* * **entries**
|
||||||
|
* An array of
|
||||||
|
* [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
|
||||||
|
* objects that can be used to access the new dimensions of the element
|
||||||
|
* after each change.
|
||||||
|
* * **observer**
|
||||||
|
* A reference to the `ResizeObserver` itself, so it will definitely be
|
||||||
|
* accessible from inside the callback, should you need it. This could be
|
||||||
|
* used for example to automatically unobserve the observer when a certain
|
||||||
|
* condition is reached, but you can omit it if you don't need it.
|
||||||
|
*
|
||||||
|
* The callback will generally follow a pattern along the lines of:
|
||||||
|
* ```js
|
||||||
|
* function(entries, observer) {
|
||||||
|
* for (let entry of entries) {
|
||||||
|
* // Do something to each entry
|
||||||
|
* // and possibly something to the observer itself
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The following snippet is taken from the
|
||||||
|
* [resize-observer-text.html](https://mdn.github.io/dom-examples/resize-observer/resize-observer-text.html)
|
||||||
|
* ([see source](https://github.com/mdn/dom-examples/blob/master/resize-observer/resize-observer-text.html))
|
||||||
|
* example:
|
||||||
|
* @example
|
||||||
|
* const resizeObserver = new ResizeObserver(entries => {
|
||||||
|
* for (let entry of entries) {
|
||||||
|
* if(entry.contentBoxSize) {
|
||||||
|
* h1Elem.style.fontSize = Math.max(1.5, entry.contentBoxSize.inlineSize/200) + 'rem';
|
||||||
|
* pElem.style.fontSize = Math.max(1, entry.contentBoxSize.inlineSize/600) + 'rem';
|
||||||
|
* } else {
|
||||||
|
* h1Elem.style.fontSize = Math.max(1.5, entry.contentRect.width/200) + 'rem';
|
||||||
|
* pElem.style.fontSize = Math.max(1, entry.contentRect.width/600) + 'rem';
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* resizeObserver.observe(divElem);
|
||||||
|
*/
|
||||||
|
constructor(callback: ResizeObserverCallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The **disconnect()** method of the
|
||||||
|
* [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
|
||||||
|
* interface unobserves all observed
|
||||||
|
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
||||||
|
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
|
||||||
|
* targets.
|
||||||
|
*/
|
||||||
|
disconnect: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `observe()` method of the
|
||||||
|
* [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
|
||||||
|
* interface starts observing the specified
|
||||||
|
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
||||||
|
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement).
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* resizeObserver.observe(target, options);
|
||||||
|
*
|
||||||
|
* @param target
|
||||||
|
* A reference to an
|
||||||
|
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
||||||
|
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
|
||||||
|
* to be observed.
|
||||||
|
*
|
||||||
|
* @param options
|
||||||
|
* An options object allowing you to set options for the observation.
|
||||||
|
* Currently this only has one possible option that can be set.
|
||||||
|
*/
|
||||||
|
observe: (target: Element, options?: ResizeObserverObserveOptions) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The **unobserve()** method of the
|
||||||
|
* [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
|
||||||
|
* interface ends the observing of a specified
|
||||||
|
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
||||||
|
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement).
|
||||||
|
*/
|
||||||
|
unobserve: (target: Element) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResizeObserverObserveOptions {
|
||||||
|
/**
|
||||||
|
* Sets which box model the observer will observe changes to. Possible values
|
||||||
|
* are `content-box` (the default), and `border-box`.
|
||||||
|
*
|
||||||
|
* @default "content-box"
|
||||||
|
*/
|
||||||
|
box?: "content-box" | "border-box";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The function called whenever an observed resize occurs. The function is
|
||||||
|
* called with two parameters:
|
||||||
|
*
|
||||||
|
* @param entries
|
||||||
|
* An array of
|
||||||
|
* [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
|
||||||
|
* objects that can be used to access the new dimensions of the element after
|
||||||
|
* each change.
|
||||||
|
*
|
||||||
|
* @param observer
|
||||||
|
* A reference to the `ResizeObserver` itself, so it will definitely be
|
||||||
|
* accessible from inside the callback, should you need it. This could be used
|
||||||
|
* for example to automatically unobserve the observer when a certain condition
|
||||||
|
* is reached, but you can omit it if you don't need it.
|
||||||
|
*
|
||||||
|
* The callback will generally follow a pattern along the lines of:
|
||||||
|
* @example
|
||||||
|
* function(entries, observer) {
|
||||||
|
* for (let entry of entries) {
|
||||||
|
* // Do something to each entry
|
||||||
|
* // and possibly something to the observer itself
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const resizeObserver = new ResizeObserver(entries => {
|
||||||
|
* for (let entry of entries) {
|
||||||
|
* if(entry.contentBoxSize) {
|
||||||
|
* h1Elem.style.fontSize = Math.max(1.5, entry.contentBoxSize.inlineSize/200) + 'rem';
|
||||||
|
* pElem.style.fontSize = Math.max(1, entry.contentBoxSize.inlineSize/600) + 'rem';
|
||||||
|
* } else {
|
||||||
|
* h1Elem.style.fontSize = Math.max(1.5, entry.contentRect.width/200) + 'rem';
|
||||||
|
* pElem.style.fontSize = Math.max(1, entry.contentRect.width/600) + 'rem';
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* resizeObserver.observe(divElem);
|
||||||
|
*/
|
||||||
|
type ResizeObserverCallback = (
|
||||||
|
entries: ResizeObserverEntry[],
|
||||||
|
observer: ResizeObserver,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The **ResizeObserverEntry** interface represents the object passed to the
|
||||||
|
* [ResizeObserver()](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver)
|
||||||
|
* constructor's callback function, which allows you to access the new
|
||||||
|
* dimensions of the
|
||||||
|
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
||||||
|
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
|
||||||
|
* being observed.
|
||||||
|
*/
|
||||||
|
interface ResizeObserverEntry {
|
||||||
|
/**
|
||||||
|
* An object containing the new border box size of the observed element when
|
||||||
|
* the callback is run.
|
||||||
|
*/
|
||||||
|
readonly borderBoxSize: ResizeObserverEntryBoxSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object containing the new content box size of the observed element when
|
||||||
|
* the callback is run.
|
||||||
|
*/
|
||||||
|
readonly contentBoxSize: ResizeObserverEntryBoxSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [DOMRectReadOnly](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly)
|
||||||
|
* object containing the new size of the observed element when the callback is
|
||||||
|
* run. Note that this is better supported than the above two properties, but
|
||||||
|
* it is left over from an earlier implementation of the Resize Observer API,
|
||||||
|
* is still included in the spec for web compat reasons, and may be deprecated
|
||||||
|
* in future versions.
|
||||||
|
*/
|
||||||
|
// node_modules/typescript/lib/lib.dom.d.ts
|
||||||
|
readonly contentRect: DOMRectReadOnly;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reference to the
|
||||||
|
* [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or
|
||||||
|
* [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement)
|
||||||
|
* being observed.
|
||||||
|
*/
|
||||||
|
readonly target: Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The **borderBoxSize** read-only property of the
|
||||||
|
* [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
|
||||||
|
* interface returns an object containing the new border box size of the
|
||||||
|
* observed element when the callback is run.
|
||||||
|
*/
|
||||||
|
interface ResizeObserverEntryBoxSize {
|
||||||
|
/**
|
||||||
|
* The length of the observed element's border box in the block dimension. For
|
||||||
|
* boxes with a horizontal
|
||||||
|
* [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode),
|
||||||
|
* this is the vertical dimension, or height; if the writing-mode is vertical,
|
||||||
|
* this is the horizontal dimension, or width.
|
||||||
|
*/
|
||||||
|
blockSize: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of the observed element's border box in the inline dimension.
|
||||||
|
* For boxes with a horizontal
|
||||||
|
* [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode),
|
||||||
|
* this is the horizontal dimension, or width; if the writing-mode is
|
||||||
|
* vertical, this is the vertical dimension, or height.
|
||||||
|
*/
|
||||||
|
inlineSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Window {
|
||||||
|
ResizeObserver: typeof ResizeObserver;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import intl from "react-intl-universal"
|
import intl from "react-intl-universal"
|
||||||
import { RSSSource, INIT_SOURCES, SourceActionTypes, ADD_SOURCE, UPDATE_SOURCE, DELETE_SOURCE, initSources } from "./source"
|
import { INIT_SOURCES, SourceActionTypes, ADD_SOURCE, UPDATE_SOURCE, DELETE_SOURCE, initSources } from "./source"
|
||||||
import { RSSItem, ItemActionTypes, FETCH_ITEMS, fetchItems } from "./item"
|
import { RSSItem, ItemActionTypes, FETCH_ITEMS, fetchItems } from "./item"
|
||||||
import { ActionStatus, AppThunk, getWindowBreakpoint } from "../utils"
|
import { ActionStatus, AppThunk, getWindowBreakpoint } from "../utils"
|
||||||
import { INIT_FEEDS, FeedActionTypes, ALL, initFeeds } from "./feed"
|
import { INIT_FEEDS, FeedActionTypes, ALL, initFeeds } from "./feed"
|
||||||
@ -199,7 +199,7 @@ export function initIntl(): AppThunk<Promise<void>> {
|
|||||||
|
|
||||||
export function initApp(): AppThunk {
|
export function initApp(): AppThunk {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
document.body.classList.add(process.platform)
|
document.body.classList.add(window.utils.platform)
|
||||||
dispatch(initIntl()).then(() =>
|
dispatch(initIntl()).then(() =>
|
||||||
dispatch(initSources())
|
dispatch(initSources())
|
||||||
).then(() =>
|
).then(() =>
|
||||||
|
@ -39,7 +39,13 @@ export class RSSItem {
|
|||||||
item.snippet = htmlDecode(parsed.contentSnippet || "")
|
item.snippet = htmlDecode(parsed.contentSnippet || "")
|
||||||
}
|
}
|
||||||
if (parsed.thumb) item.thumb = parsed.thumb
|
if (parsed.thumb) item.thumb = parsed.thumb
|
||||||
else if (parsed.image) item.thumb = parsed.image
|
else if (parsed.image) {
|
||||||
|
if (parsed.image.$ && parsed.image.$.url) {
|
||||||
|
item.thumb = parsed.image.$.url
|
||||||
|
} else if (typeof parsed.image === "string") {
|
||||||
|
item.thumb = parsed.image
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (parsed.mediaContent) {
|
else if (parsed.mediaContent) {
|
||||||
let images = parsed.mediaContent.filter(c => c.$ && c.$.medium === "image" && c.$.url)
|
let images = parsed.mediaContent.filter(c => c.$ && c.$.medium === "image" && c.$.url)
|
||||||
if (images.length > 0) item.thumb = images[0].$.url
|
if (images.length > 0) item.thumb = images[0].$.url
|
||||||
|
Loading…
x
Reference in New Issue
Block a user