Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: organice pwa as share target #795

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,17 @@
"start_url": absoluteUrl(pathname || "/index.html"),
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
"background_color": "#ffffff",
"share_target": {
"action": "/capture",
"method": "GET",
"params": {
"title": "captureVariable_v_title",
"text": "captureVariable_v_text",
"url": "captureVariable_v_url"
}
},
};

const manifestBlob = new Blob([JSON.stringify(manifest)], {type: 'application/json'});
const manifestURL = URL.createObjectURL(manifestBlob);
Expand Down
34 changes: 34 additions & 0 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"short_name": "organice",
"name": "organice",
"icons": [
{
"src": "organice-512x512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "organice-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "/index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff",
"share_target": {
"action": "/capture",
"method": "GET",
"params": {
"title": "captureVariable_v_title",
"text": "captureVariable_v_text",
"url": "captureVariable_v_url"
}
}
}
205 changes: 205 additions & 0 deletions src/components/Capture/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { useParams, Link, useHistory } from "react-router-dom";
// import './stylesheet.css';

import { List, Map } from 'immutable';
import { STATIC_FILE_PREFIX } from '../../lib/org_utils';

import parseQueryString from '../../util/parse_query_string';

const Capture = ({
captureTemplates,
loadedFilePaths,
state,
}) => {

/**
* Return a list of possible files for a capture template. Some templates
* force a certain file, some allow a specific whitelist and some allow all
* files
*/
const getPossibleFilesForTemplate = (template) => {
if (!template) {
return [];
}

if (template.get('file')) {
return [template.get('file')];
}

if (!template.get('isAvailableInAllOrgFiles')) {
return template.get('orgFilesWhereAvailable').toJS();
}

return loadedFilePaths.filter(path => !!path);
};

/**
* Render the template selector or the current template
*
* If no template is selected, show the list of all templates and let the user
* select one. If a template is selected, show the selected template and a
* button to unselect it
*/
const renderTemplateSelector = () => {

if (template) {
return (
<div>
<p>selected template: { template.get('description') }</p>

<Link
to={`/capture?${getQueryString()}`}
>select different template</Link>
</div>
);
}

return (
<div>
<p>select a template:</p>
<ul>
{captureTemplates.map(template => (
<li key={template.get('id')}>
<Link
to={`/capture/${template.get('description')}?${getQueryString()}`}
>{template.get('description')}</Link>
</li>
))}
</ul>
</div>
)
}

/**
* Render the file selector, if a template is selected. Depending on the
* template, show a dropdown with possible files or just an automatically
* selected file
*/
const renderFileSelector = () => {

if (!template) {
return null;
}

return (
<select onChange={(e) => setCaptureFile(e.target.value)} style={{ width: '90%' }} value={captureFile}>
{getPossibleFilesForTemplate(template).map((path) => (
<option key={path} value={path}>
{path}
</option>
))}
</select>
);
};

/**
* Render the submit button, if submitting is possible
*/
const renderSubmit = () => {
if (!template || !captureFile) {
return null;
}

return (
<button
onClick={submit}
>capture {template.get('description')} to {captureFile}</button>
);
};

// TODO just copied from App.js, make it a reusable function
const getCustomCaptureVariables = () => Map(
Object.entries(queryStringContents)
.map(([key, value]) => {
const CUSTOM_VARIABLE_PREFIX = 'captureVariable_';
if (key.startsWith(CUSTOM_VARIABLE_PREFIX)) {
return [key.substring(CUSTOM_VARIABLE_PREFIX.length), value];
}

return null;
})
.filter((item) => !!item)
);

/**
* Submit the capture. Update the state with target file, capture template &
* contents, and redirect to the target file. The actual capture is handled there
*/
const submit = () => {

let customCaptureVariables = getCustomCaptureVariables();

state.org.present = state.org.present.set(
'pendingCapture',
Map({
capturePath: captureFile,
captureTemplateName: template.get("description"),
captureContent: captureContent,
customCaptureVariables,
})
);

history.push(`/file${captureFile}`);
};

const history = useHistory();

// get the template from the router
const templateName = useParams().template;
const template = captureTemplates
.find((template) => template.get('description').trim() === templateName);

const queryStringContents = parseQueryString(window.location.search);

let captureFilename = queryStringContents.captureFile;
if (template && template.get('file')) {
captureFilename = template.get('file');
}

const [captureFile, setCaptureFile] = useState(captureFilename);
const captureContent = queryStringContents.captureContent;

const getQueryString = (additionalParams = {}) => new URLSearchParams({...queryStringContents, ...additionalParams});

// whenever the template changes (by router navigation), reset the capture
// file to the first possible one
useEffect(() => {
setCaptureFile(getPossibleFilesForTemplate(template)[0]);
}, [template]);

return (
<div>
<h1>Capture</h1>

<h2>Template</h2>
{ renderTemplateSelector() }

<h2>Target file</h2>
{ renderFileSelector() }

<p>
{ renderSubmit() }
</p>

</div>
);
};

const mapStateToProps = (state) => {

const loadedFilePaths = state.org.present
.get('files', List())
.keySeq()
.toJS()
.filter((path) => !path.startsWith(STATIC_FILE_PREFIX));
loadedFilePaths.unshift('');
return {
captureTemplates: state.capture.get('captureTemplates', List()),
loadedFilePaths,
state,
};
};

export default connect(mapStateToProps)(Capture);
2 changes: 2 additions & 0 deletions src/components/Entry/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import OrgFile from '../OrgFile';
import Settings from '../Settings';
import KeyboardShortcutsEditor from '../KeyboardShortcutsEditor';
import CaptureTemplatesEditor from '../CaptureTemplatesEditor';
import Capture from '../Capture';
import FileSettingsEditor from '../FileSettingsEditor';
import SyncServiceSignIn from '../SyncServiceSignIn';

Expand Down Expand Up @@ -194,6 +195,7 @@ class Entry extends PureComponent {
<Route path="/settings" exact={true}>
<Settings />
</Route>
<Route path="/capture/:template?" exact component={Capture} />
<Redirect to="/files" />
</Switch>
)
Expand Down