diff options
Diffstat (limited to 'web/src/index.js')
| -rw-r--r-- | web/src/index.js | 126 |
1 files changed, 82 insertions, 44 deletions
diff --git a/web/src/index.js b/web/src/index.js index a215c46..ff570da 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -13,9 +13,10 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. -import { html, render, Component } from "../lib/htm/preact.js" -import { Spinner } from "./spinner.js" -import { SearchBox } from "./search-box.js" +import {html, render, Component} from "../lib/htm/preact.js" +import {Spinner} from "./spinner.js" +import {SearchBox} from "./search-box.js" +import {giphyIsEnabled, GiphySearchTab, setGiphyAPIKey} from "./giphy.js" import * as widgetAPI from "./widget-api.js" import * as frequent from "./frequently-used.js" @@ -31,7 +32,7 @@ if (params.has('config')) { // This is updated from packs/index.json let HOMESERVER_URL = "https://matrix-client.matrix.org" -const makeThumbnailURL = mxc => `${HOMESERVER_URL}/_matrix/media/r0/thumbnail/${mxc.substr(6)}?height=128&width=128&method=scale` +const makeThumbnailURL = mxc => `${HOMESERVER_URL}/_matrix/media/v3/thumbnail/${mxc.slice(6)}?height=128&width=128&method=scale` // We need to detect iOS webkit because it has a bug related to scrolling non-fixed divs // This is also used to fix scrolling to sections on Element iOS @@ -52,6 +53,7 @@ class App extends Component { super(props) this.defaultTheme = params.get("theme") this.state = { + viewingGifs: false, packs: defaultState.packs, loading: true, error: null, @@ -118,7 +120,7 @@ class App extends Component { filtering: { ...this.state.filtering, searchTerm, - packs: packsWithFilteredStickers.filter(({ stickers }) => !!stickers.length), + packs: packsWithFilteredStickers.filter(({stickers}) => !!stickers.length), }, }) } @@ -135,10 +137,10 @@ class App extends Component { setTheme(theme) { if (theme === "default") { delete localStorage.mauStickerThemeOverride - this.setState({ theme: this.defaultTheme }) + this.setState({theme: this.defaultTheme}) } else { localStorage.mauStickerThemeOverride = theme - this.setState({ theme: theme }) + this.setState({theme: theme}) } } @@ -154,7 +156,7 @@ class App extends Component { _loadPacks(disableCache = false) { const cache = disableCache ? "no-cache" : undefined - fetch(INDEX, { cache }).then(async indexRes => { + fetch(INDEX, {cache}).then(async indexRes => { if (indexRes.status >= 400) { this.setState({ loading: false, @@ -164,13 +166,14 @@ class App extends Component { } const indexData = await indexRes.json() HOMESERVER_URL = indexData.homeserver_url || HOMESERVER_URL + setGiphyAPIKey(indexData.giphy_api_key, indexData.giphy_mxc_prefix) // TODO only load pack metadata when scrolled into view? for (const packFile of indexData.packs) { let packRes if (packFile.startsWith("https://") || packFile.startsWith("http://")) { - packRes = await fetch(packFile, { cache }) + packRes = await fetch(packFile, {cache}) } else { - packRes = await fetch(`${PACKS_BASE_URL}/${packFile}`, { cache }) + packRes = await fetch(`${PACKS_BASE_URL}/${packFile}`, {cache}) } const packData = await packRes.json() for (const sticker of packData.stickers) { @@ -182,7 +185,7 @@ class App extends Component { }) } this.updateFrequentlyUsed() - }, error => this.setState({ loading: false, error })) + }, error => this.setState({loading: false, error})) } componentDidMount() { @@ -214,6 +217,9 @@ class App extends Component { let maxXElem = null for (const entry of intersections) { const packID = entry.target.getAttribute("data-pack-id") + if (!packID) { + continue + } const navElement = document.getElementById(`nav-${packID}`) if (entry.isIntersecting) { navElement.classList.add("visible") @@ -230,9 +236,9 @@ class App extends Component { } } if (minXElem !== null) { - minXElem.scrollIntoView({ inline: "start" }) + minXElem.scrollIntoView({inline: "start"}) } else if (maxXElem !== null) { - maxXElem.scrollIntoView({ inline: "end" }) + maxXElem.scrollIntoView({inline: "end"}) } } @@ -268,36 +274,66 @@ class App extends Component { render() { const theme = `theme-${this.state.theme}` const filterActive = !!this.state.filtering.searchTerm - const packs = filterActive ? this.state.filtering.packs : [this.state.frequentlyUsed, ...this.state.packs] + const packs = filterActive + ? this.state.filtering.packs + : [this.state.frequentlyUsed, ...this.state.packs] if (this.state.loading) { - return html`<main class="spinner ${theme}"><${Spinner} size=${80} green /></main>` + return html` + <main class="spinner ${theme}"> + <${Spinner} size=${80} green/> + </main> + ` } else if (this.state.error) { - return html`<main class="error ${theme}"> - <h1>Failed to load packs</h1> - <p>${this.state.error}</p> - </main>` + return html` + <main class="error ${theme}"> + <h1>Failed to load packs</h1> + <p>${this.state.error}</p> + </main> + ` } else if (this.state.packs.length === 0) { - return html`<main class="empty ${theme}"><h1>No packs found 😿</h1></main>` + return html` + <main class="empty ${theme}"><h1>No packs found 😿</h1></main> + ` } - return html`<main class="has-content ${theme}"> - <nav onWheel=${this.navScroll} ref=${elem => this.navRef = elem}> - <${NavBarItem} pack=${this.state.frequentlyUsed} iconOverride="recent" /> - ${this.state.packs.map(pack => html`<${NavBarItem} id=${pack.id} pack=${pack}/>`)} - <${NavBarItem} pack=${{ id: "settings", title: "Settings" }} iconOverride="settings" /> - </nav> - <${SearchBox} onKeyUp=${this.searchStickers} /> - <div class="pack-list ${isMobileSafari ? "ios-safari-hack" : ""}" ref=${elem => this.packListRef = elem}> - ${filterActive && packs.length === 0 ? html`<div class="search-empty"><h1>No stickers match your search</h1></div>` : null} - ${packs.map(pack => html`<${Pack} id=${pack.id} pack=${pack} send=${this.sendSticker} />`)} - <${Settings} app=${this}/> - </div> - </main>` + const onClickOverride = this.state.viewingGifs + ? (evt, packID) => { + evt.preventDefault() + this.setState({viewingGifs: false}, () => { + scrollToSection(null, packID) + }) + } : null + const switchToGiphy = () => this.setState({viewingGifs: true, filtering: defaultState.filtering}) + + return html` + <main class="has-content ${theme}"> + <nav onWheel=${this.navScroll} ref=${elem => this.navRef = elem}> + ${giphyIsEnabled() && html` + <${NavBarItem} pack=${{id: "giphy", title: "GIPHY"}} iconOverride="giphy" onClickOverride=${switchToGiphy} extraClass=${this.state.viewingGifs ? "visible" : ""}/> + `} + <${NavBarItem} pack=${this.state.frequentlyUsed} iconOverride="recent" onClickOverride=${onClickOverride}/> + ${this.state.packs.map(pack => html`<${NavBarItem} id=${pack.id} pack=${pack} onClickOverride=${onClickOverride}/>`)} + <${NavBarItem} pack=${{id: "settings", title: "Settings"}} iconOverride="settings" onClickOverride=${onClickOverride}/> + </nav> + + ${this.state.viewingGifs ? html` + <${GiphySearchTab}/> + ` : html` + <${SearchBox} onInput=${this.searchStickers} value=${this.state.filtering.searchTerm ?? ""}/> + <div class="pack-list ${isMobileSafari ? "ios-safari-hack" : ""}" ref=${(elem) => (this.packListRef = elem)}> + ${filterActive && packs.length === 0 + ? html`<div class="search-empty"><h1>No stickers match your search</h1></div>` + : null} + ${packs.map((pack) => html`<${Pack} id=${pack.id} pack=${pack} send=${this.sendSticker}/>`)} + <${Settings} app=${this}/> + </div> + `} + </main>` } } -const Settings = ({ app }) => html` +const Settings = ({app}) => html` <section class="stickerpack settings" id="pack-settings" data-pack-id="settings"> <h1>Settings</h1> <div class="settings-list"> @@ -306,7 +342,7 @@ const Settings = ({ app }) => html` <label for="stickers-per-row">Stickers per row: ${app.state.stickersPerRow}</label> <input type="range" min=2 max=10 id="stickers-per-row" id="stickers-per-row" value=${app.state.stickersPerRow} - onInput=${evt => app.setStickersPerRow(evt.target.value)} /> + onInput=${evt => app.setStickersPerRow(evt.target.value)}/> </div> <div> <label for="theme">Theme: </label> @@ -325,13 +361,15 @@ const Settings = ({ app }) => html` // open the link in the browser instead of just scrolling there, so we need to scroll manually: const scrollToSection = (evt, id) => { const pack = document.getElementById(`pack-${id}`) - pack.scrollIntoView({ block: "start", behavior: "instant" }) - evt.preventDefault() + if (pack) { + pack.scrollIntoView({block: "start", behavior: "instant"}) + } + evt?.preventDefault() } -const NavBarItem = ({ pack, iconOverride = null }) => html` - <a href="#pack-${pack.id}" id="nav-${pack.id}" data-pack-id=${pack.id} title=${pack.title} - onClick=${isMobileSafari ? (evt => scrollToSection(evt, pack.id)) : undefined}> +const NavBarItem = ({pack, iconOverride = null, onClickOverride = null, extraClass = null}) => html` + <a href="#pack-${pack.id}" id="nav-${pack.id}" data-pack-id=${pack.id} title=${pack.title} class="${extraClass}" + onClick=${onClickOverride ? (evt => onClickOverride(evt, pack.id)) : (isMobileSafari ? (evt => scrollToSection(evt, pack.id)) : undefined)}> <div class="sticker"> ${iconOverride ? html` <span class="icon icon-${iconOverride}"/> @@ -343,7 +381,7 @@ const NavBarItem = ({ pack, iconOverride = null }) => html` </a> ` -const Pack = ({ pack, send }) => html` +const Pack = ({pack, send}) => html` <section class="stickerpack" id="pack-${pack.id}" data-pack-id=${pack.id}> <h1>${pack.title}</h1> <div class="sticker-list"> @@ -354,10 +392,10 @@ const Pack = ({ pack, send }) => html` </section> ` -const Sticker = ({ content, send }) => html` +const Sticker = ({content, send}) => html` <div class="sticker" onClick=${send} data-sticker-id=${content.id}> - <img data-src=${makeThumbnailURL(content.url)} alt=${content.body} title=${content.body} /> + <img data-src=${makeThumbnailURL(content.url)} alt=${content.body} title=${content.body}/> </div> ` -render(html`<${App} />`, document.body) +render(html`<${App}/>`, document.body) |
