From 9ea7ea832b9306034710c7452c703cb23f31ea0a Mon Sep 17 00:00:00 2001 From: Aakash Goel Date: Sun, 30 Aug 2020 00:22:40 +0530 Subject: [PATCH 1/8] init plugin system now on selecting an email from left sidebar - the selected email object from the API is supplied to the "app". The app is then expected to define its own UI and APIs and to get its stuff going --- README.md | 9 + TODO.md | 6 + components/pageWrappers/AppWrapper.js | 197 ++++++++++++++++++ components/service-creator/action-bar.js | 8 +- .../service-creator/email-results-nav.js | 2 +- pages/[uid]/ft/auto-unlock/index.js | 39 ++++ pages/[uid]/ft/email-to-json/index.js | 0 7 files changed, 256 insertions(+), 5 deletions(-) create mode 100644 components/pageWrappers/AppWrapper.js create mode 100644 pages/[uid]/ft/auto-unlock/index.js create mode 100644 pages/[uid]/ft/email-to-json/index.js diff --git a/README.md b/README.md index fb4d5bc..117202c 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,12 @@ to start the development server on port `3000`. Your jsonbox instance will be ru ### LICENSE MIT + +Yet todo to complete the README: +- dockerize jsonbox + - enable IP whitelisting to hosted instance of jsonbox (open a PR) +- See if you can simplify the process of installing "emailapi" on a VM like DO + - nginx with certbot + - 2 docker images - `emailapi_app` and `jsonbox` +- Put up funding info and setup service to accept payments +- See if you'd like to offer hosted service in exchange for monthly committment??! (Too big an undertaking though) \ No newline at end of file diff --git a/TODO.md b/TODO.md index 5ac535b..a836ee6 100644 --- a/TODO.md +++ b/TODO.md @@ -26,3 +26,9 @@ - Remove `done` based callbacks from queue and move to Promise.resolve()s - Enable job success/failure notification system - Include all queues from filesystem instead of `require`ing them individually + + +### UI Plugin system +- Allow for email preview and config output bar to be an "app" +- the app receives the json props of email clicked in left sidebar +- it also receives some callbacks that allow for setting props (like selection state of email cards) or causing fetch/refresh in left sidebar \ No newline at end of file diff --git a/components/pageWrappers/AppWrapper.js b/components/pageWrappers/AppWrapper.js new file mode 100644 index 0000000..741e399 --- /dev/null +++ b/components/pageWrappers/AppWrapper.js @@ -0,0 +1,197 @@ +import React, { useState, useEffect } from 'react'; +import axios from 'axios'; +import { withRouter } from 'next/router'; +import Head from 'next/head'; +import styled, { createGlobalStyle } from 'styled-components'; +import Header from '~/components/service-creator/header'; +import ActionBar from '~/components/service-creator/action-bar'; + +import EmailResultsNav from '~/components/service-creator/email-results-nav'; + +const GlobalStyle = createGlobalStyle` + body { + overflow: hidden; + } + .mail-container-hover { + outline: 2px solid lightblue; + cursor: pointer; + } + .eaio-field { + outline: 4px solid lightblue; + } +`; + +const Container = styled.div` + display: grid; + grid-template-columns: 1fr; + grid-template-rows: 64px 64px 1fr; + + height: 100vh; + width: 100vw; +`; + +const ContainerBody = styled.div` + display: grid; + grid-template-columns: 400px 1fr 600px; + overflow: hidden; +`; + +function AppWrapper({ children, router, ...props }) { + const { + query: { uid }, + } = router; + + const { + AuthUserInfo: { token }, + } = props; + + const [searchInput, setSearchInput] = useState(''); + + const [triggerSearch, setTriggerSearch] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [searchResults, setSearchResults] = useState([]); + const [nextPageToken, setNextPageToken] = useState(null); + const [selectedSearchResultIndex, setSelectedSearchResultIndex] = useState( + null, + ); + + function resetData() { + setSearchResults([]); + setSelectedSearchResultIndex(null); + setNextPageToken(null); + } + + function handleChangeSearchInput(value) { + resetData(); + setSearchInput(value); + } + + async function handleSearchAction() { + window.history.pushState( + '', + '', + `?q=${searchInput ? encodeURIComponent(searchInput) : ''}`, + ); + setTriggerSearch(false); + try { + setIsLoading(true); + const reqParams = { + uid, + token, + query: searchInput, + }; + if (nextPageToken) { + reqParams.nextPageToken = nextPageToken; + } + try { + const response = await axios({ + method: 'post', + url: `/api/email-search`, + data: reqParams, + timeout: 15000, + }); + const { emails, nextPageToken: resNextPageToken } = response.data; + setSearchResults( + reqParams.nextPageToken ? [...searchResults, ...emails] : emails, + ); + setNextPageToken(resNextPageToken); + // if (emails.length) setSelectedSearchResultIndex(0); + setIsLoading(false); + } catch (e) { + if (!nextPageToken) { + // on first load, it's getting stuck for some reason + if (Number(e.statusCode) > 500) { + window.location.reload(); + } + } + } + } catch (error) { + console.log(error); + setIsLoading(false); + } + } + + function handleClickEmailSubject(idx) { + setSelectedSearchResultIndex(idx); + } + + function getEmailFromHeader(sender) { + const parts = sender.split('<'); + return parts.length === 2 ? parts[1].split('>')[0] : parts[0]; + } + + function handleFilterEmailsBySender(fromEmail) { + handleChangeSearchInput(`from:(${getEmailFromHeader(fromEmail)})`); + setTriggerSearch(true); + } + + function handleFilterEmailsBySubject({ fromEmail, subject }) { + handleChangeSearchInput( + `from:(${getEmailFromHeader(fromEmail)}) subject:(${subject})`, + ); + setTriggerSearch(true); + } + + function handleFetchMoreMails() { + setTriggerSearch(true); + } + + useEffect(() => { + if (triggerSearch) { + handleSearchAction(); + } + }, [triggerSearch]); + + return ( + <> + + emailapi.io | create new app + + + + + +
+ + + + {children({ + selectedEmail: searchResults[selectedSearchResultIndex] || {}, + })} + + + {/* <> + setOpen(false)}> +