mirror of
https://github.com/anyproto/anytype-ts.git
synced 2025-06-07 21:47:02 +09:00
JS-5614: Webclipper auth rework
This commit is contained in:
parent
21eab5296a
commit
198988f19e
14 changed files with 229 additions and 27 deletions
10
dist/extension/auth/index.html
vendored
Normal file
10
dist/extension/auth/index.html
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>Anytype Web Clipper</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="../js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -41,6 +41,8 @@ class WindowManager {
|
|||
sandbox: false,
|
||||
}, param.webPreferences);
|
||||
|
||||
console.log(param);
|
||||
|
||||
let win = new BrowserWindow(param);
|
||||
|
||||
remote.enable(win.webContents);
|
||||
|
@ -142,10 +144,16 @@ class WindowManager {
|
|||
};
|
||||
|
||||
createChallenge (options) {
|
||||
const { screen } = require('electron');
|
||||
const primaryDisplay = screen.getPrimaryDisplay();
|
||||
const { width } = primaryDisplay.workAreaSize;
|
||||
|
||||
const win = this.create({}, {
|
||||
backgroundColor: '',
|
||||
width: 424,
|
||||
height: 232,
|
||||
x: Math.floor(width / 2 - 212),
|
||||
y: 100,
|
||||
titleBarStyle: 'hidden',
|
||||
});
|
||||
|
||||
|
@ -153,6 +161,7 @@ class WindowManager {
|
|||
win.setMenu(null);
|
||||
|
||||
is.windows || is.linux ? win.showInactive() : win.show();
|
||||
win.focus();
|
||||
|
||||
win.webContents.once('did-finish-load', () => {
|
||||
win.webContents.postMessage('challenge', options);
|
||||
|
|
82
extension/auth.tsx
Normal file
82
extension/auth.tsx
Normal file
|
@ -0,0 +1,82 @@
|
|||
import * as React from 'react';
|
||||
import * as hs from 'history';
|
||||
import { Router, Route, Switch } from 'react-router-dom';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { Provider } from 'mobx-react';
|
||||
import { configure } from 'mobx';
|
||||
import { S, U } from 'Lib';
|
||||
|
||||
import Index from './auth/index';
|
||||
import Success from './auth/success';
|
||||
import Util from './lib/util';
|
||||
|
||||
import './scss/auth.scss';
|
||||
|
||||
configure({ enforceActions: 'never' });
|
||||
|
||||
const Routes = [
|
||||
{ path: '/' },
|
||||
{ path: '/:page' },
|
||||
];
|
||||
|
||||
const Components = {
|
||||
index: Index,
|
||||
success: Success,
|
||||
};
|
||||
|
||||
const memoryHistory = hs.createMemoryHistory;
|
||||
const history = memoryHistory();
|
||||
|
||||
class RoutePage extends React.Component<RouteComponentProps> {
|
||||
|
||||
render () {
|
||||
const { match } = this.props;
|
||||
const params = match.params as any;
|
||||
const page = params.page || 'index';
|
||||
const Component = Components[page];
|
||||
|
||||
return Component ? <Component /> : null;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
class Auth extends React.Component {
|
||||
|
||||
render () {
|
||||
return (
|
||||
<Router history={history}>
|
||||
<Provider {...S}>
|
||||
<div>
|
||||
<Switch>
|
||||
{Routes.map((item: any, i: number) => (
|
||||
<Route path={item.path} exact={true} key={i} component={RoutePage} />
|
||||
))}
|
||||
</Switch>
|
||||
</div>
|
||||
</Provider>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
U.Router.init(history);
|
||||
|
||||
/* @ts-ignore */
|
||||
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
||||
switch (msg.type) {
|
||||
case 'initAuth':
|
||||
const { appKey, gatewayPort, serverPort } = msg;
|
||||
|
||||
Util.init(serverPort, gatewayPort);
|
||||
Util.authorize(appKey);
|
||||
|
||||
sendResponse({});
|
||||
break;
|
||||
};
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
export default Auth;
|
|
@ -8,7 +8,7 @@ interface State {
|
|||
error: string;
|
||||
};
|
||||
|
||||
const Challenge = observer(class Challenge extends React.Component<I.PageComponent, State> {
|
||||
const Index = observer(class Index extends React.Component<I.PageComponent, State> {
|
||||
|
||||
ref: any = null;
|
||||
state = {
|
||||
|
@ -19,21 +19,20 @@ const Challenge = observer(class Challenge extends React.Component<I.PageCompone
|
|||
super(props);
|
||||
|
||||
this.onSuccess = this.onSuccess.bind(this);
|
||||
this.onError = this.onError.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { error } = this.state;
|
||||
|
||||
return (
|
||||
<div className="page pageChallenge">
|
||||
<div className="page pageIndex">
|
||||
<Title text="Please enter the numbers from the app" />
|
||||
|
||||
<Pin
|
||||
ref={ref => this.ref = ref}
|
||||
pinLength={4}
|
||||
onSuccess={this.onSuccess}
|
||||
onError={this.onError}
|
||||
onError={() => {}}
|
||||
/>
|
||||
|
||||
<Error text={error} />
|
||||
|
@ -42,29 +41,35 @@ const Challenge = observer(class Challenge extends React.Component<I.PageCompone
|
|||
};
|
||||
|
||||
onSuccess () {
|
||||
C.AccountLocalLinkSolveChallenge(S.Extension.challengeId, this.ref?.getValue().trim(), (message: any) => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const data = JSON.parse(atob(urlParams.get('data') as string));
|
||||
|
||||
if (!data) {
|
||||
this.setState({ error: 'Invalid data' });
|
||||
return;
|
||||
};
|
||||
|
||||
Util.init(data.serverPort, data.gatewayPort);
|
||||
|
||||
C.AccountLocalLinkSolveChallenge(data.challengeId, this.ref?.getValue().trim(), (message: any) => {
|
||||
if (message.error.code) {
|
||||
this.setState({ error: message.error.description });
|
||||
return;
|
||||
};
|
||||
|
||||
const { appKey } = message;
|
||||
const { serverPort, gatewayPort } = S.Extension;
|
||||
|
||||
Storage.set('appKey', appKey);
|
||||
|
||||
Util.authorize(appKey, () => {
|
||||
Util.sendMessage({ type: 'initIframe', appKey, serverPort, gatewayPort }, () => {});
|
||||
Util.sendMessage({ type: 'initIframe', appKey, ...data }, () => {});
|
||||
Util.sendMessage({ type: 'initMenu' }, () => {});
|
||||
|
||||
U.Router.go('/create', {});
|
||||
U.Router.go('/success', {});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
onError () {
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
export default Challenge;
|
||||
export default Index;
|
21
extension/auth/success.tsx
Normal file
21
extension/auth/success.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { I } from 'Lib';
|
||||
|
||||
interface State {
|
||||
error: string;
|
||||
};
|
||||
|
||||
const Success = observer(class Success extends React.Component<I.PageComponent, State> {
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className="page pageSuccess">
|
||||
<div className="title">Successfully authorized!</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
export default Success;
|
|
@ -4,6 +4,7 @@ import $ from 'jquery';
|
|||
import { C, U, J, S } from 'Lib';
|
||||
import Popup from './popup';
|
||||
import Iframe from './iframe';
|
||||
import Auth from './auth';
|
||||
import Util from './lib/util';
|
||||
|
||||
import './scss/common.scss';
|
||||
|
@ -38,8 +39,8 @@ window.Anytype = {
|
|||
window.AnytypeGlobalConfig = {
|
||||
emojiUrl: J.Extension.clipper.emojiUrl,
|
||||
menuBorderTop: 16,
|
||||
menuBorderBottom: 16,
|
||||
flagsMw: { request: false },
|
||||
menuBorderBottom: 16,
|
||||
flagsMw: { request: true },
|
||||
};
|
||||
|
||||
let rootId = '';
|
||||
|
@ -52,6 +53,10 @@ if (Util.isPopup()) {
|
|||
if (Util.isIframe()) {
|
||||
rootId = `${J.Extension.clipper.prefix}-iframe`;
|
||||
component = <Iframe />;
|
||||
} else
|
||||
if (Util.isAuth()) {
|
||||
rootId = `${J.Extension.clipper.prefix}-auth`;
|
||||
component = <Auth />;
|
||||
};
|
||||
|
||||
if (!rootId) {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { S, U, J, C, dispatcher } from 'Lib';
|
||||
|
||||
const INDEX_POPUP = '/popup/index.html';
|
||||
const INDEX_IFRAME = '/iframe/index.html'
|
||||
const INDEX_IFRAME = '/iframe/index.html';
|
||||
const INDEX_AUTH = '/auth/index.html';
|
||||
|
||||
class Util {
|
||||
|
||||
|
@ -20,6 +21,10 @@ class Util {
|
|||
return this.isExtension() && (location.pathname == INDEX_IFRAME);
|
||||
};
|
||||
|
||||
isAuth () {
|
||||
return this.isExtension() && (location.pathname == INDEX_AUTH);
|
||||
};
|
||||
|
||||
fromPopup (url: string) {
|
||||
return url.match(INDEX_POPUP);
|
||||
};
|
||||
|
|
|
@ -8,7 +8,6 @@ import { ListMenu } from 'Component';
|
|||
import { S, U, C, J } from 'Lib';
|
||||
|
||||
import Index from './popup/index';
|
||||
import Challenge from './popup/challenge';
|
||||
import Create from './popup/create';
|
||||
import Success from './popup/success';
|
||||
|
||||
|
@ -23,7 +22,6 @@ const Routes = [
|
|||
|
||||
const Components = {
|
||||
index: Index,
|
||||
challenge: Challenge,
|
||||
create: Create,
|
||||
success: Success,
|
||||
};
|
||||
|
|
|
@ -81,6 +81,7 @@ const Index = observer(class Index extends React.Component<I.PageComponent, Stat
|
|||
} else {
|
||||
/* @ts-ignore */
|
||||
const manifest = chrome.runtime.getManifest();
|
||||
const { serverPort, gatewayPort } = S.Extension;
|
||||
|
||||
C.AccountLocalLinkNewChallenge(manifest.name, (message: any) => {
|
||||
if (message.error.code) {
|
||||
|
@ -89,7 +90,15 @@ const Index = observer(class Index extends React.Component<I.PageComponent, Stat
|
|||
};
|
||||
|
||||
S.Extension.challengeId = message.challengeId;
|
||||
U.Router.go('/challenge', {});
|
||||
|
||||
const data = {
|
||||
serverPort,
|
||||
gatewayPort,
|
||||
challengeId: message.challengeId,
|
||||
};
|
||||
|
||||
/* @ts-ignore */
|
||||
chrome.tabs.create({ url: chrome.runtime.getURL('auth/index.html') + `?data=${btoa(JSON.stringify(data))}` });
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
31
extension/scss/auth.scss
Normal file
31
extension/scss/auth.scss
Normal file
|
@ -0,0 +1,31 @@
|
|||
html.anytypeWebclipper-auth { width: 640px; position: absolute; left: 50%; top: 50%; transform: translate3d(-50%, -50%, 0px); }
|
||||
|
||||
#anytypeWebclipper-auth {
|
||||
@import "~scss/_mixins";
|
||||
|
||||
.input, .select, .textarea { @include text-small; border: 1px solid var(--color-shape-secondary); width: 100%; border-radius: 1px; }
|
||||
|
||||
.pin {
|
||||
.input { width: 32px; height: 40px; @include text-paragraph; border-radius: 6px; }
|
||||
}
|
||||
|
||||
.input, .select { height: 32px; padding: 0px 10px; }
|
||||
.select { display: flex; align-items: center; padding-right: 20px; }
|
||||
.textarea { padding: 10px; resize: none; height: 68px; display: block; }
|
||||
.buttonsWrapper { display: flex; flex-direction: column; justify-content: center; gap: 8px 0px; margin: 16px 0px 0px 0px; }
|
||||
.loaderWrapper { position: fixed; left: 0px; top: 0px; width: 100%; height: 100%; background: var(--color-bg-loader); z-index: 10; }
|
||||
.error { @include text-small; margin: 1em 0px 0px 0px; }
|
||||
|
||||
.isFocused { border-color: var(--color-ice) !important; box-shadow: 0px 0px 0px 1px var(--color-ice); }
|
||||
|
||||
.page.pageIndex,
|
||||
.page.pageSuccess { display: flex; flex-direction: column; align-items: center; }
|
||||
|
||||
.page.pageIndex {
|
||||
.title { @include text-header3; margin: 0px 0px 24px 0px; }
|
||||
}
|
||||
|
||||
.page.pageSuccess {
|
||||
.title { @include text-header3; }
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
html.anytypeWebclipper-iframe,
|
||||
html.anytypeWebclipper-popup {
|
||||
html.anytypeWebclipper-popup,
|
||||
html.anytypeWebclipper-auth {
|
||||
@import "~scss/font";
|
||||
@import "~scss/_mixins";
|
||||
|
||||
|
|
|
@ -33,11 +33,7 @@ html.anytypeWebclipper-popup { width: 268px; }
|
|||
|
||||
.isFocused { border-color: var(--color-ice) !important; box-shadow: 0px 0px 0px 1px var(--color-ice); }
|
||||
|
||||
.page.pageIndex, .page.pageChallenge { padding: 50px 16px; text-align: center; }
|
||||
|
||||
.page.pageChallenge {
|
||||
.title { @include text-common; font-weight: 600; margin: 0px 0px 24px 0px; }
|
||||
}
|
||||
.page.pageIndex { padding: 50px 16px; text-align: center; }
|
||||
|
||||
.page.pageCreate { padding: 16px; }
|
||||
.page.pageCreate {
|
||||
|
|
|
@ -93,11 +93,12 @@ const Pin = forwardRef<PinRefProps, Props>(({
|
|||
};
|
||||
|
||||
const onInputKeyDown = (e, index: number) => {
|
||||
const current = inputRefs.current[index];
|
||||
const prev = inputRefs.current[index - 1];
|
||||
|
||||
if (prev) {
|
||||
keyboard.shortcut('backspace', e, () => {
|
||||
prev.setValue('');
|
||||
current.setValue('');
|
||||
prev.setType('text');
|
||||
prev.focus();
|
||||
});
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { I, S, U, J } from 'Lib';
|
||||
|
||||
const electron = U.Common.getElectron();
|
||||
|
||||
const ACCOUNT_KEYS = [
|
||||
'spaceId',
|
||||
'spaceOrder',
|
||||
|
@ -18,6 +20,32 @@ const SPACE_KEYS = [
|
|||
'redirectInvite',
|
||||
];
|
||||
|
||||
const Api = {
|
||||
get: (key: string) => {
|
||||
if (electron.storeGet) {
|
||||
return electron.storeGet(key);
|
||||
} else {
|
||||
localStorage.getItem(key);
|
||||
};
|
||||
},
|
||||
|
||||
set: (key: string, obj: any) => {
|
||||
if (electron.storeSet) {
|
||||
electron.storeSet(key, obj);
|
||||
} else {
|
||||
localStorage.setItem(key, JSON.stringify(obj));
|
||||
};
|
||||
},
|
||||
|
||||
delete: (key: string) => {
|
||||
if (electron.storeDelete) {
|
||||
electron.storeDelete(key);
|
||||
} else {
|
||||
localStorage.removeItem(key);
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
class Storage {
|
||||
|
||||
storage: any = null;
|
||||
|
@ -28,7 +56,7 @@ class Storage {
|
|||
};
|
||||
|
||||
get (key: string): any {
|
||||
let o = U.Common.getElectron().storeGet(key);
|
||||
let o = Api.get(key);
|
||||
if (!o) {
|
||||
o = this.parse(String(this.storage[key] || ''));
|
||||
};
|
||||
|
@ -80,7 +108,7 @@ class Storage {
|
|||
if (this.isAccountKey(key)) {
|
||||
this.setAccountKey(key, o);
|
||||
} else {
|
||||
U.Common.getElectron().storeSet(key, o);
|
||||
Api.set(key, o);
|
||||
//delete(this.storage[key]);
|
||||
};
|
||||
};
|
||||
|
@ -93,6 +121,7 @@ class Storage {
|
|||
this.deleteAccountKey(key);
|
||||
} else {
|
||||
U.Common.getElectron().storeDelete(key);
|
||||
Api.delete(key);
|
||||
delete(this.storage[key]);
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue