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

Add initial evernote migration #1421

Open
wants to merge 1 commit 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
117 changes: 117 additions & 0 deletions src/cloud/api/migrations/EvernoteApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { callApi } from '../../lib/client'
import { NotebookMetadata } from '../../pages/migrations/EvernoteMigrate'
import { SerializedDocWithSupplemental } from '../../interfaces/db/doc'

export interface EvernoteNotebooks {
notebooks: NotebookMetadata[]
}

export interface EvernoteNoteData {
noteId: string
notebookId: string
title: string
}

interface EvernoteNotes {
notes: EvernoteNoteData[]
}

export interface AccessTokenRequestData {
oauthAccessToken: string
oauthAccessTokenSecret: string
edamShard: string
edamUserId: string
edamExpires: string
edamNoteStoreUrl: string
edamWebApiUrlPrefix: string
}

interface RequestTokenData {
oauthToken: string
oauthTokenSecret: string
redirectUrl: string
}

export const evernoteAccessTokenDataKey = 'evernote-migration-access-token-data'
export const evernoteOAuthVerifierKey = 'evernote-migration-oauth_verifier'
export const evernoteOAuthTokenKey = 'evernote-migration-oauth_token'
export const evernoteTempTokenKey = 'evernote-migration-temp-token'
export const evernoteTempTokenSecretKey = 'evernote-migration-temp-token-secret'

interface CreateEvernoteMigrationAccessTokenRequestBody {
oauthToken: string
oauthVerifier: string
oauthTokenSecret: string
// state: string // (Optional) Use it if you set `state`.
// todo: the state is oauthTokenSecret afaik
}

interface EvernoteDocImportResponseBody {
doc: SerializedDocWithSupplemental
}

export async function fetchEvernoteAccessToken(
oauthToken: string,
oauthVerifier: string,
oauthTokenSecret: string
): Promise<AccessTokenRequestData> {
const body: CreateEvernoteMigrationAccessTokenRequestBody = {
oauthToken,
oauthVerifier,
oauthTokenSecret,
}
return callApi<AccessTokenRequestData>(
`/api/migrations/evernote/access-token`,
{ method: 'post', json: body }
)
}

export async function evernoteAuthorize(): Promise<RequestTokenData> {
return callApi('api/migrations/evernote/authorize')
}

export async function fetchEvernoteNotebooks(
oauthAccessToken: string
): Promise<EvernoteNotebooks> {
return callApi<EvernoteNotebooks>(
`/api/migrations/evernote/notebooks?oauthAccessToken=${oauthAccessToken}`
)
}

export async function fetchEvernoteNotes(
oauthAccessToken: string,
notebookId: string
): Promise<EvernoteNotes> {
return callApi<EvernoteNotes>(
`/api/migrations/evernote/notes?oauthAccessToken=${oauthAccessToken}&notebookId=${notebookId}`
)
}

export async function importEvernoteNote(
oauthAccessToken: string,
noteId: string,
teamId: string,
parentFolderId: string,
workspaceId: string
): Promise<EvernoteDocImportResponseBody> {
return callApi<EvernoteDocImportResponseBody>(
`/api/migrations/evernote/${teamId}/${noteId}/import?oauthAccessToken=${oauthAccessToken}&parentFolderId=${parentFolderId}&workspaceId=${workspaceId}`
)
}

export function resetAccessToken() {
localStorage.removeItem(evernoteAccessTokenDataKey)
localStorage.removeItem(evernoteOAuthVerifierKey)
localStorage.removeItem(evernoteOAuthTokenKey)
localStorage.removeItem(evernoteTempTokenKey)
localStorage.removeItem(evernoteTempTokenSecretKey)
}

export function getAccessToken(): string | null {
const accessTokenData = localStorage.getItem(evernoteAccessTokenDataKey)
if (accessTokenData == null) {
return null
}
return (JSON.parse(accessTokenData) as AccessTokenRequestData)
.oauthAccessToken
}
6 changes: 3 additions & 3 deletions src/cloud/api/rest/doc.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { SerializedDoc } from '../../interfaces/db/doc'
import { SerializedDocWithSupplemental } from '../../interfaces/db/doc'
import { callApi } from '../../lib/client'

interface DocCreateRequestBody {
content: string
title: string
workspaceId: string
workspaceId?: string
path: string
tags: string[]
teamId: string
Expand All @@ -13,7 +13,7 @@ interface DocCreateRequestBody {
}

interface DocCreateResponseBody {
doc: SerializedDoc
doc: SerializedDocWithSupplemental
}

export function createDocREST(body: DocCreateRequestBody) {
Expand Down
4 changes: 3 additions & 1 deletion src/cloud/components/Application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ const Application = ({
return openSettingsTab('teamUpgrade')
case 'members':
return openSettingsTab('teamMembers')
case 'evernote-migration':
return openSettingsTab('import')
default:
return
}
Expand Down Expand Up @@ -359,7 +361,7 @@ const Application = ({
return (
<SidebarHeader
onSpaceClick={onSpaceClick}
spaceName={'...'}
spaceName={'Select space'}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this since it makes more sense from new evernote page

controls={sidebarHeaderControls}
/>
)
Expand Down
82 changes: 62 additions & 20 deletions src/cloud/components/ImportFlow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,22 @@ import { useModal } from '../../../design/lib/stores/modal'
import styled from '../../../design/lib/styled'
import Spinner from '../../../design/components/atoms/Spinner'
import ImportFlowSource from './molecules/ImportFlowSources'
import {
evernoteAccessTokenDataKey,
evernoteAuthorize,
evernoteTempTokenKey,
evernoteTempTokenSecretKey,
} from '../../api/migrations/EvernoteApi'
import { useRouter } from '../../lib/router'
import { useElectron } from '../../lib/stores/electron'

type ImportStep = 'destination' | 'source' | 'import'
type ImportStep = 'destination' | 'source' | 'import' | 'evernote-import'

const ImportFlow = () => {
const [step, setStep] = useState<ImportStep>('source')
const [selectedWorkspaceId, setSelectedWorkspaceId] = useState<string>()
const [selectedFolderId, setSelectedFolderId] = useState<string>()

const { closeLastModal: closeModal } = useModal()
const fileUploaderRef = useRef<HTMLInputElement>(null)
const [uploadType, setUploadType] = useState<
Expand All @@ -40,6 +49,7 @@ const ImportFlow = () => {

const navigateToTeam = useNavigateToTeam()
const navigateToWorkspace = useNavigateToWorkspace()
const { sendToElectron, usingElectron } = useElectron()

const onFileUpload = useCallback(
async (event: React.ChangeEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -142,27 +152,59 @@ const ImportFlow = () => {
navigateToFolder,
]
)
const { push } = useRouter()
const fetchEvernoteTempToken = useCallback(() => {
evernoteAuthorize()
.then((result) => {
localStorage.setItem(evernoteTempTokenKey, result.oauthToken)
localStorage.setItem(
evernoteTempTokenSecretKey,
result.oauthTokenSecret
)

const onsourceCallback = useCallback((val: string) => {
switch (val) {
case 'dropbox':
case 'gdocs':
case 'md':
setUploadType('md')
break
case 'evernote':
case 'confluence':
case 'html':
setUploadType('html')
break
case 'quip':
case 'notion':
default:
setUploadType('md|html')
break
if (usingElectron) {
sendToElectron('open-external-url', result.redirectUrl)
} else {
open(result.redirectUrl)
}
})
.catch((err) => console.log('Err' + err))
}, [sendToElectron, usingElectron])

const evernoteLoginOauth = useCallback(() => {
const accessTokenData = localStorage.getItem(evernoteAccessTokenDataKey)
if (accessTokenData != null) {
push('/migrations/evernote')
return
}
setStep('destination')
}, [])
fetchEvernoteTempToken()
}, [fetchEvernoteTempToken, push])

const onsourceCallback = useCallback(
(val: string) => {
switch (val) {
case 'dropbox':
case 'gdocs':
case 'md':
setUploadType('md')
setStep('destination')
break
case 'evernote':
case 'confluence':
case 'html':
setUploadType('html')
evernoteLoginOauth()
break
case 'quip':
case 'notion':
default:
setUploadType('md|html')
setStep('destination')
break
}
},
[evernoteLoginOauth]
)

const content = useMemo(() => {
switch (step) {
Expand Down
Loading