Refactor Plugin Library Logic (#884)

* Refactor Plugin Library Logic

* add kea router

* update types

* add dynamic routing

* upgrade kea

* kea SSR with location for router

* fix random undefined bug

* wip changes

* use selector

* fix build fail

* add netlify config

* fix back button

* don't redirect to root if still loading plugins

* refactor actions and logic

Co-authored-by: Marius Andra <marius.andra@gmail.com>
This commit is contained in:
Yakko Majuri
2021-02-01 10:06:12 +00:00
committed by GitHub
parent 2f6cb56dc3
commit b59918c8d9
13 changed files with 168 additions and 73 deletions

View File

@@ -4,9 +4,9 @@
* See: https://www.gatsbyjs.org/docs/browser-apis/
*/
// You can delete this file if you're not using it
import wrapWithProvider from './wrap-with-provider'
export const wrapRootElement = wrapWithProvider
import 'prismjs/themes/prism-okaidia.css'
import { wrapElement, initKea } from './kea'
// gatsby-browser.js
require('prismjs/themes/prism-okaidia.css')
initKea(false)
export const wrapRootElement = wrapElement

View File

@@ -8,3 +8,17 @@
exports.createPages = require('./gatsby/createPages')
exports.onCreateNode = require('./gatsby/onCreateNode')
// Implement the Gatsby API “onCreatePage”. This is
// called after every page is created.
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
// Only update the `/app` page.
if (page.path.match(/^\/plugins/)) {
// page.matchPath is a special key that's used for matching pages
// with corresponding routes only on the client.
page.matchPath = '/plugins/*'
// Update the page.
createPage(page)
}
}

View File

@@ -5,5 +5,9 @@
*/
// You can delete this file if you're not using it
import wrapWithProvider from './wrap-with-provider'
export const wrapRootElement = wrapWithProvider
import { wrapElement, initKea } from './kea'
export const wrapPageElement = ({ element, props }) => {
initKea(true, props.location)
return wrapElement({ element })
}

13
kea.js Normal file
View File

@@ -0,0 +1,13 @@
import React from 'react'
import { Provider } from 'react-redux'
import { getContext, resetContext } from 'kea'
import { loadersPlugin } from 'kea-loaders'
import { routerPlugin } from 'kea-router'
export function initKea(isServer = false, location = '') {
resetContext({
plugins: [loadersPlugin, routerPlugin(isServer ? { location } : {})],
})
}
export const wrapElement = ({ element }) => <Provider store={getContext().store}>{element}</Provider>

4
netlify.toml Normal file
View File

@@ -0,0 +1,4 @@
[[redirects]]
from = "/plugins/*"
to = "/plugins"
status = 200

View File

@@ -47,8 +47,9 @@
"gatsby-transformer-remark": "^2.8.7",
"gatsby-transformer-sharp": "^2.2.14",
"katex": "^0.12.0",
"kea": "^2.2.0",
"kea": "^2.2.2",
"kea-loaders": "^0.3.0",
"kea-router": "^0.5.1",
"node-sass": "^4.14.1",
"prismjs": "^1.21.0",
"query-string": "^6.13.1",
@@ -61,6 +62,7 @@
"react-modal": "^3.11.2",
"react-redux": "^7.2.1",
"react-responsive": "^6.1.2",
"redux": "^4.0.5",
"reselect": "^4.0.0",
"typescript": "^4.0.2"
},

View File

@@ -8,13 +8,13 @@ import { useActions, useValues } from 'kea'
import { pluginLibraryLogic } from '../../logic/pluginLibraryLogic'
export const PluginModal = () => {
const { activePlugin, modalOpen, pluginLoading } = useValues(pluginLibraryLogic)
const { setModalOpen } = useActions(pluginLibraryLogic)
const { activePlugin, activePluginName, pluginLoading } = useValues(pluginLibraryLogic)
const { openLibrary } = useActions(pluginLibraryLogic)
return (
<Modal
isOpen={modalOpen}
onRequestClose={() => setModalOpen(false)}
isOpen={!!activePluginName}
onRequestClose={openLibrary}
className="pluginModalContent"
overlayClassName="modalOverlay"
ariaHideApp={false}
@@ -32,7 +32,7 @@ export const PluginModal = () => {
Learn More <ExportOutlined />
</a>
</div>
<Button icon="close" onClick={() => setModalOpen(false)} className="modalClose" />
<Button icon="close" onClick={openLibrary} className="modalClose" />
</>
)}
</Modal>

View File

@@ -1,3 +1,5 @@
import { LibraryPluginType } from 'types'
export const unsafeHash = (str: string) => {
var a = 1,
c = 0,
@@ -12,3 +14,10 @@ export const unsafeHash = (str: string) => {
}
return String(a)
}
export const getPluginImageSrc = (plugin: LibraryPluginType) =>
plugin.imageLink
? plugin.imageLink
: plugin.url.includes('github')
? `https://raw.githubusercontent.com/${plugin.url.split('hub.com/')[1]}/main/logo.png`
: null

View File

@@ -1,38 +1,53 @@
import { kea } from 'kea'
import { pluginInstallationMd } from '../pages-content/plugin-installation'
import { getPluginImageSrc } from '../lib/utils'
const toPathName = (pluginName) => pluginName.toLowerCase().replaceAll(' ', '-')
export const pluginLibraryLogic = kea({
actions: {
setFilter: (filter) => ({ filter }),
setModalOpen: (open) => ({ open }),
setActivePlugin: (activePlugin) => ({ activePlugin }),
setPluginLoading: (pluginLoading) => ({ pluginLoading }),
openLibrary: true,
openPlugin: (pluginName) => ({ pluginName }),
openPluginPath: (pathname) => ({ pathname }),
},
reducers: {
reducers: () => ({
filter: [
'all',
{
setFilter: (_, { filter }) => filter,
},
],
modalOpen: [
false,
{
setModalOpen: (_, { open }) => open,
},
],
activePlugin: [
{},
{
setActivePlugin: (_, { activePlugin }) => activePlugin,
},
],
activePluginName: [
'',
{
openPlugin: (_, { pluginName }) => pluginName,
openLibrary: () => '',
},
],
pluginPathname: [
'',
{
openPluginPath: (_, { pathname }) => pathname,
openPlugin: (_, { pluginName }) => toPathName(pluginName),
openLibrary: () => '',
},
],
pluginLoading: [
false,
{
setPluginLoading: (_, { pluginLoading }) => pluginLoading,
openPlugin: () => true,
setActivePlugin: () => false,
},
],
},
}),
loaders: () => ({
plugins: [
[],
@@ -52,6 +67,11 @@ export const pluginLibraryLogic = kea({
(plugins, filter) =>
plugins.filter((p) => p.displayOnWebsiteLib && (filter === 'all' || filter === p.type)),
],
pluginMatch: [
(s) => [s.pluginPathname, s.filteredPlugins],
(pluginPathname, filteredPlugins) =>
filteredPlugins.find((plugin) => toPathName(plugin.name) === pluginPathname),
],
},
events: ({ actions }) => ({
afterMount: () => {
@@ -61,4 +81,54 @@ export const pluginLibraryLogic = kea({
}
},
}),
listeners: ({ values, actions }) => ({
openPlugin: async ({ pluginName }) => {
const { setActivePlugin } = actions
let plugin = values.filteredPlugins.filter((plugin) => plugin.name === pluginName)[0]
let markdown = `# ${plugin.name} \n ${plugin.description} \n ${pluginInstallationMd}`
if (plugin.url.includes('github.com/')) {
try {
const response = await window.fetch(
`https://raw.githubusercontent.com/${plugin.url.split('github.com/')[1]}/main/README.md`
)
if (response.status === 200) {
markdown = await response.text()
}
} catch (e) {
// can't load the readme, revert to default text
}
}
if (!markdown.includes('Installation')) {
markdown += pluginInstallationMd
}
plugin['markdown'] = markdown.split(/!\[.*\]\(.*\)/).join('')
plugin['imageSrc'] = getPluginImageSrc(plugin)
setActivePlugin(plugin)
},
loadPluginsSuccess: () => {
const { openPluginPath } = actions
openPluginPath(window.location.pathname.split('/plugins/')[1])
},
openPluginPath: () => {
const { pluginMatch, pluginsLoading } = values
if (pluginsLoading) {
return
}
if (pluginMatch) {
actions.openPlugin(pluginMatch.name)
} else {
actions.openLibrary()
}
},
}),
actionToUrl: () => ({
openLibrary: () => '/plugins/',
openPlugin: ({ pluginName }) => `/plugins/${toPathName(pluginName)}`,
openPluginPath: ({ pathname }) => `/plugins/${pathname}`,
}),
urlToAction: ({ actions }) => ({
'/plugins': () => actions.openLibrary(),
'/plugins/': () => actions.openLibrary(),
'/plugins/:pathname': ({ pathname }) => actions.openPluginPath(pathname),
}),
})

View File

@@ -7,43 +7,14 @@ import { useActions, useValues } from 'kea'
import { pluginLibraryLogic } from '../logic/pluginLibraryLogic'
import { Spin } from 'antd'
import { PluginModal } from '../components/PluginLibrary/PluginModal'
import { pluginInstallationMd } from '../pages-content/plugin-installation'
import { getPluginImageSrc } from '../lib/utils'
import './styles/plugin-library.scss'
const { TabPane } = Tabs
export const PluginLibraryPage = () => {
const { filteredPlugins, filter } = useValues(pluginLibraryLogic)
const { setFilter, setModalOpen, setActivePlugin, setPluginLoading } = useActions(pluginLibraryLogic)
const handlePluginClick = async (e, plugin) => {
setPluginLoading(true)
setModalOpen(true)
let markdown = `# ${plugin.name} \n ${plugin.description} \n ${pluginInstallationMd}`
if (plugin.url.includes('github')) {
e.stopPropagation()
const response = await window.fetch(
`https://raw.githubusercontent.com/${plugin.url.split('hub.com/')[1]}/main/README.md`
)
if (response.status === 200) {
markdown = await response.text()
}
}
if (!markdown.includes('Installation')) {
markdown += pluginInstallationMd
}
plugin['markdown'] = markdown.split(/!\[.*\]\(.*\)/).join('')
plugin['imageSrc'] = getPluginImageSrc(plugin)
setActivePlugin(plugin)
setPluginLoading(false)
}
const getPluginImageSrc = (plugin) =>
plugin.imageLink
? plugin.imageLink
: plugin.url.includes('github')
? `https://raw.githubusercontent.com/${plugin.url.split('hub.com/')[1]}/main/logo.png`
: null
const { setFilter, openPlugin } = useActions(pluginLibraryLogic)
return (
<Layout>
@@ -72,9 +43,7 @@ export const PluginLibraryPage = () => {
link={plugin.url}
imageSrc={getPluginImageSrc(plugin)}
isCommunityPlugin={plugin.maintainer === 'community'}
onClick={(e) => {
handlePluginClick(e, { ...plugin })
}}
onClick={() => openPlugin(plugin.name)}
/>
))
) : (

View File

@@ -12,3 +12,12 @@ export interface FeatureComparisonData {
mixpanel: boolean
heap: boolean
}
export interface LibraryPluginType {
id: number
name: string
description?: string
url: string
imageLink: string
maintainer?: string
}

View File

@@ -1,11 +0,0 @@
import React from 'react'
import { Provider } from 'react-redux'
import { getContext, resetContext } from 'kea'
import { loadersPlugin } from 'kea-loaders'
resetContext({
plugins: [loadersPlugin],
})
// eslint-disable-next-line react/display-name,react/prop-types
export default ({ element }) => <Provider store={getContext().store}>{element}</Provider>

View File

@@ -10617,10 +10617,17 @@ kea-loaders@^0.3.0:
resolved "https://registry.yarnpkg.com/kea-loaders/-/kea-loaders-0.3.0.tgz#876a838cd65da4547d2611d8e180e7d867e2a657"
integrity sha512-aXrUjQf/GdJVDQqLtTWoY4gF1F+dTXv5U1LfCLffPhgrWRYHGTnaBV/K6pWxy1Qh+vMbepy80Nx9hFiK1owtHw==
kea@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/kea/-/kea-2.2.0.tgz#1ba4a174a53880cca8002a67cf62b19b30d09702"
integrity sha512-IzgTC6SC89wTLfiBMPlduG4r4YanxONYK4werz8RMZxPvcYw4XEEK8xQJguwVYtLCEGm4x5YiLCubGqGfRcbEw==
kea-router@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/kea-router/-/kea-router-0.5.1.tgz#de6490628e16dd67702ee329e1912721b709efa2"
integrity sha512-qKAnT5TQxSWtkTifoZgRK+iQBtHCgKl11NoBldUmfunDo5eUtiYSK8rt7O123+fIZzXyg6PLQvJCRsSHf3Z3eA==
dependencies:
url-pattern "^1.0.3"
kea@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/kea/-/kea-2.2.2.tgz#2a58ca5089b95fcc463224e6d08d95f05fac7af7"
integrity sha512-9qtmm/J0kz+wSrST1ljsxuoKoIpAhynkV0Rht8OQkAHvX7KEtABNVVdX/r6AgK1BOsjACF4t6tcMnYXG51GEZQ==
keyv@3.0.0:
version "3.0.0"
@@ -17688,6 +17695,11 @@ url-parse@^1.1.8, url-parse@^1.4.3:
querystringify "^2.1.1"
requires-port "^1.0.0"
url-pattern@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/url-pattern/-/url-pattern-1.0.3.tgz#0409292471b24f23c50d65a47931793d2b5acfc1"
integrity sha1-BAkpJHGyTyPFDWWkeTF5PStaz8E=
url-to-options@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"