Skip to content
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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,9 @@ you can import/export with json file.
you can cutomize shortcut and other settings fron icon-menu.

- whether or not confirm before delete

### Sync

To sync your notes in the cloud (and share them across several browsers automatically) you will need to register and login from the menu.

This setup can be customized from icon-menu: icon-menu where you can specify a custom url. The url must point to a running backend instance. You can learn how to self host one using the [dedicated project](https://github.com/kumabook/stickynotes-backend/).
6 changes: 5 additions & 1 deletion src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ function handlePopupMessage(msg) {
break;
}
case 'reset-password':
browser.tabs.create({ url: api.resetPasswordUrl });
browser.tabs.create({ url: api.getResetPasswordUrl() });
break;
default:
break;
Expand Down Expand Up @@ -348,6 +348,10 @@ function handleOptionsUIMessage(msg) {
})));
break;
}
case 'updated-server-url': {
browser.storage.local.get().then(payload => api.setOnPremiseUrl(payload));
break;
}
default:
break;
}
Expand Down
52 changes: 52 additions & 0 deletions src/containers/OptionsUI.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* global confirm: false */
/* global chrome */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
Expand All @@ -21,6 +22,43 @@ class OptionsUI extends React.Component {
);
}

renderSetOnPremise() {
return (
<div>
<h4 className="optionsLabel">On Premise</h4>
<input
className="optionsValueInput"
type="checkbox"
checked={this.props.hasCustomBaseUrl}
onChange={e => {
this.props.hasCustomBaseUrl = e.target.checked;
this.props.handleUseCustomerBackupServer(this.props);
}}
/>
Use a custom backend server
{this.props.hasCustomBaseUrl && ['BASE_URL', 'CLIENT_ID', 'CLIENT_SECRET'].map((key)=> <div key={key}>
<input
className="optionsValueTextInput"
type="text"
name={key}
value={this.props[key]}
onChange={e => {
this.props[key] = e.target.value;
this.props.handleUseCustomerBackupServer(this.props);
}}
/>
&nbsp;{key}
</div>)}
{this.props.hasCustomBaseUrl && this.props.BASE_URL && this.props.BASE_URL != "" && (
<div>
<div className="optionsDescription">You can get a client id and a client secret <a href={`${this.props.BASE_URL}/oauth/applications`}>here</a></div>
<input class="optionsDescription" type="button" value="Authorize" onClick={()=>{chrome.permissions.request({origins: [this.props['BASE_URL']]})}} /> Allow the web-extension to backup data on this domain
</div>
)}
</div>
);
}

renderFiles() {
return (
<div>
Expand All @@ -43,6 +81,7 @@ class OptionsUI extends React.Component {
<div>
{this.renderFiles()}
{this.renderCanMoveFocusByTab()}
{this.renderSetOnPremise()}
</div>
);
}
Expand All @@ -54,6 +93,11 @@ OptionsUI.propTypes = {
handleInputFiles: PropTypes.func.isRequired,
handleCanMoveFocusByTabChange: PropTypes.func.isRequired,
canMoveFocusByTab: PropTypes.bool.isRequired,
handleUseCustomerBackupServer: PropTypes.func.isRequired,
hasCustomBaseUrl: PropTypes.func.isRequired,
BASE_URL: PropTypes.string,
CLIENT_ID: PropTypes.string,
CLIENT_SECRET: PropTypes.string,
};

OptionsUI.defaultProps = {
Expand All @@ -62,6 +106,10 @@ OptionsUI.defaultProps = {
function mapStateToProps(state) {
return {
canMoveFocusByTab: state.canMoveFocusByTab,
hasCustomBaseUrl: state.hasCustomBaseUrl,
BASE_URL: state.BASE_URL,
CLIENT_ID: state.CLIENT_ID,
CLIENT_SECRET: state.CLIENT_SECRET,
};
}

Expand Down Expand Up @@ -90,6 +138,10 @@ function mapDispatchToProps(dispatch) {
type: 'UPDATE_CAN_MOVE_FOCUS_BY_TAB',
payload,
}),
handleUseCustomerBackupServer: payload => dispatch({
type: 'UPDATE_USE_CUSTOM_BACKUP_SERVER',
payload,
}),
};
}

Expand Down
40 changes: 40 additions & 0 deletions src/reducers/options_ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,49 @@ const canMoveFocusByTab = (state = true, action) => {
}
};

const hasCustomBaseUrl = (state = false, action) => {
switch (action.type) {
case 'UPDATE_USE_CUSTOM_BACKUP_SERVER':
return action.payload.hasCustomBaseUrl;
default:
return state;
}
};

const BASE_URL = (state = "", action) => {
switch (action.type) {
case 'UPDATE_USE_CUSTOM_BACKUP_SERVER':
return action.payload.BASE_URL;
default:
return state;
}
};

const CLIENT_ID = (state = "", action) => {
switch (action.type) {
case 'UPDATE_USE_CUSTOM_BACKUP_SERVER':
return action.payload.CLIENT_ID;
default:
return state;
}
};

const CLIENT_SECRET = (state = "", action) => {
switch (action.type) {
case 'UPDATE_USE_CUSTOM_BACKUP_SERVER':
return action.payload.CLIENT_SECRET;
default:
return state;
}
};


const rootReducer = combineReducers({
canMoveFocusByTab,
hasCustomBaseUrl,
BASE_URL,
CLIENT_ID,
CLIENT_SECRET,
});

export default rootReducer;
9 changes: 9 additions & 0 deletions src/sagas/options_ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,21 @@ function* watchCanMoveFocusByTab() {
});
}

function* watchUseCustomerBackupServer() {
yield takeEvery('UPDATE_USE_CUSTOM_BACKUP_SERVER', function* update() {
const { BASE_URL, CLIENT_ID, CLIENT_SECRET, hasCustomBaseUrl } = yield select(state => state);
yield browser.storage.local.set({ BASE_URL, CLIENT_ID, CLIENT_SECRET, hasCustomBaseUrl });
port.postMessage({ type: 'updated-server-url', portName });
});
}


export default function* root() {
yield [
fork(watchPort),
fork(watchImport),
fork(watchExport),
fork(watchCanMoveFocusByTab),
fork(watchUseCustomerBackupServer),
];
}
42 changes: 39 additions & 3 deletions src/utils/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,40 @@
import browser from 'webextension-polyfill';
import logger from './logger';

const { BASE_URL, CLIENT_ID, CLIENT_SECRET } = process.env;
let { BASE_URL, CLIENT_ID, CLIENT_SECRET } = process.env;

browser.storage.local.get('hasCustomBaseUrl').then((hasCustomBaseUrl) => {
if (hasCustomBaseUrl) {
['BASE_URL', 'CLIENT_ID', 'CLIENT_SECRET'].forEach(key=>browser.storage.local.get(key).then((v) => {
if (v && v[key]) {
if (key=='BASE_URL')
BASE_URL = v[key];
if (key=='CLIENT_ID')
CLIENT_ID = v[key];
if (key=='CLIENT_SECRET')
CLIENT_SECRET = v[key];
}
}));
}
});


function setOnPremiseUrl(payload) {
let server = {};
['BASE_URL', 'CLIENT_ID', 'CLIENT_SECRET'].forEach(key=>{
if (!payload.hasCustomBaseUrl || typeof(payload[key]) == "undefined" || payload[key] == "") {
server[key] = process.env[key];
} else {
server[key] = payload[key];
}
});
({ BASE_URL, CLIENT_ID, CLIENT_SECRET } = server);
}


function getResetPasswordUrl() {
return `${BASE_URL}/password_resets/new`;
}

function getAccessToken() {
return browser.storage.local.get('accessToken').then((v) => {
Expand Down Expand Up @@ -84,6 +117,9 @@ function sendRequest(method, url, params) {
logger.trace(JSON.stringify(params));

return fetch(u, { method, headers, body })
.catch(function(e) {
return {ok: false, status: e.code, exception: e}
})
.then((response) => {
logger.trace(response);
if (response.ok) {
Expand Down Expand Up @@ -158,6 +194,6 @@ export default {
isLoggedIn,
getUser,
setUser,
signUpUrl: `${BASE_URL}/users/new`,
resetPasswordUrl: `${BASE_URL}/password_resets/new`,
getResetPasswordUrl,
setOnPremiseUrl,
};