Feat: The new login UI (#8865)

Built-in login UI instead of callback

---------

Co-authored-by: rcmerci <rcmerci@gmail.com>
Co-authored-by: Konstantinos Kaloutas <konstantinos@logseq.com>
Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
pull/8909/head
Charlie 2023-03-27 19:56:18 +08:00 committed by GitHub
parent 2b15702bea
commit 95c5cba9db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 6358 additions and 87 deletions

2
packages/amplify/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.parcel-cache
dist

View File

@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="./index.tsx"></script>
</body>
</html>

View File

@ -0,0 +1,87 @@
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import { Amplify } from 'aws-amplify'
import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'
function setupConfigure () {
Amplify.configure({
Auth: {
// REQUIRED only for Federated Authentication - Amazon Cognito Identity Pool ID
// identityPoolId: 'XX-XXXX-X:XXXXXXXX-XXXX-1234-abcd-1234567890ab',
// REQUIRED - Amazon Cognito Region
region: 'us-east-1',
// OPTIONAL - Amazon Cognito Federated Identity Pool Region
// Required only if it's different from Amazon Cognito Region
// identityPoolRegion: 'XX-XXXX-X',
// OPTIONAL - Amazon Cognito User Pool ID
userPoolId: 'us-east-1_ldvDmC9Fe',
// OPTIONAL - Amazon Cognito Web Client ID (26-char alphanumeric string)
userPoolWebClientId: '41m82unjghlea984vjpk887qcr',
// OPTIONAL - Enforce user authentication prior to accessing AWS resources or not
// mandatorySignIn: false,
// OPTIONAL - This is used when autoSignIn is enabled for Auth.signUp
// 'code' is used for Auth.confirmSignUp, 'link' is used for email link verification
// signUpVerificationMethod: 'code', // 'code' | 'link'
// OPTIONAL - Configuration for cookie storage
// Note: if the secure flag is set to true, then the cookie transmission requires a secure protocol
cookieStorage: {
domain: 'localhost',
path: '/',
expires: 365,
sameSite: 'strict',
secure: true,
},
// OPTIONAL - customized storage object
// storage: MyStorage,
// OPTIONAL - Manually set the authentication flow type. Default is 'USER_SRP_AUTH'
authenticationFlowType: 'USER_SRP_AUTH',
//
// // OPTIONAL - Manually set key value pairs that can be passed to Cognito Lambda Triggers
// clientMetadata: {myCustomKey: 'myCustomValue'},
//
// // OPTIONAL - Hosted UI configuration
// oauth: {
// domain: 'your_cognito_domain',
// scope: ['phone', 'email', 'profile', 'openid', 'aws.cognito.signin.user.admin'],
// redirectSignIn: 'http://localhost:3000/',
// redirectSignOut: 'http://localhost:3000/',
// responseType: 'code' // or 'token', note that REFRESH token will only be generated when the responseType is code
}
})
}
export default function App () {
return (
<div style={{ display: 'flex', justifyContent: 'center', height: '90vh', alignItems: 'center' }}>
<Authenticator signUpAttributes={['email']}
socialProviders={['google']}>
{({ signOut, user }) => (
<main>
<h1>Hello {user.username}</h1>
<button onClick={signOut}>Sign out</button>
</main>
)}
</Authenticator>
</div>)
}
function main () {
setupConfigure()
// mount
ReactDOM.render(<App/>, document.getElementById('app'))
}
// bootstrap
main()

View File

@ -0,0 +1,42 @@
{
"name": "logseq-amplify",
"version": "0.0.1",
"description": "Amplify components for Logseq",
"license": "MIT",
"scripts": {
"dev:amplify": "parcel watch ./src/amplify.ts --dist-dir ../src/main/frontend/ --no-hmr --no-source-maps",
"dev:examples": "parcel serve ./examples/index.html",
"build:amplify": "parcel build ./src/amplify.ts --dist-dir ../../resources/js --no-source-maps && mv ../../resources/js/amplify.css ../../resources/css/"
},
"devDependencies": {
"buffer": "^5.5.0",
"parcel": "^2.8.3",
"punycode": "^1.4.1"
},
"dependencies": {
"@aws-amplify/ui-react": "^4.3.8",
"aws-amplify": "^5.0.15",
"aws-amplify-react": "^5.1.43",
"react": "^17",
"react-dom": "^17"
},
"alias": {
"react": {
"global": "React"
},
"react-dom": {
"global": "ReactDOM"
},
"react/jsx-dev-runtime": "./node_modules/react/jsx-dev-runtime.js",
"react/jsx-runtime": "./node_modules/react/jsx-runtime.js"
},
"targets": {
"default": {
"outputFormat": "global",
"includeNodeModules": {
"react": false,
"react-dom": false
}
}
}
}

View File

@ -0,0 +1,60 @@
import { Authenticator, CheckboxField, useAuthenticator, AccountSettings } from '@aws-amplify/ui-react'
export function LSAuthenticator({ termsLink, children }: any) {
return (<div>
<Authenticator
formFields={{
signUp: {
email: { order: 1 },
username: { order: 2 },
password: { order: 3 },
confirm_password: { order: 4 },
}
}}
loginMechanisms={['username']}
socialProviders={['google']}
components={{
SignUp: {
FormFields() {
const { validationErrors } = useAuthenticator()
return (
<>
{/* Re-use default `Authenticator.SignUp.FormFields` */}
<Authenticator.SignUp.FormFields/>
{/* Append & require Terms & Conditions field to sign up */}
<CheckboxField
errorMessage={validationErrors.acknowledgement as string}
hasError={!!validationErrors.acknowledgement}
name="acknowledgement"
value="yes"
label={(<a href={termsLink}>I agree with the Terms & Conditions</a>)}
/>
</>
)
},
},
}}
services={{
async validateCustomSignUp(formData) {
if (!formData.acknowledgement) {
return {
acknowledgement: '',
}
}
}
}}
>
{children}
</Authenticator>
</div>)
}
export function LSAuthenticatorChangePassword(
{onSuccess, onError}
) {
return (
<AccountSettings.ChangePassword onSuccess={onSuccess} onError={onError}/>
)
}

View File

@ -0,0 +1,99 @@
import '@aws-amplify/ui-react/styles.css'
import { Amplify, Auth, Hub, I18n } from 'aws-amplify'
import { LSAuthenticator, LSAuthenticatorChangePassword } from './LSAuthenticator'
import { dict } from 'aws-amplify-react/lib-esm/AmplifyI18n'
// fix i18n
dict.zh['Reset Password'] = '重置密码'
dict.zh['Enter your username'] = '请输入用户名'
dict.zh['Enter your email'] = '请输入邮箱'
dict.zh['Enter your password'] = '请输入密码'
dict.zh['Confirm Password'] = '确认密码'
dict.zh['Please confirm your Password'] = '请确认密码'
dict.zh['Incorrect username or password.'] = '用户名或者密码不正确。如果您的邮箱未验证,请尝试使用用户名(非邮箱)登录,以保证再次邮箱验证流程。'
// @ts-ignore attach defaults
dict.en = {
'Incorrect username or password.': 'Incorrect username or password! ' +
'For unconfirmed users, please input your username instead of Email to receive the code.'
}
const fixesMapping = {
'Sign Up': ['Sign up', 'Create Account'],
'Sign In': ['Sign in'],
'Sign Out': 'Sign out',
'Send Code': 'Send code',
'Forgot Password': ['Forgot your password?'],
'Enter your email': ['Enter your Email'],
'Enter your password': ['Enter your Password'],
'Enter your username': ['Enter your Username']
}
Object.keys(dict).forEach((k) => {
const target = dict[k]
Object.entries(fixesMapping).forEach(([k1, v1]) => {
if (target?.hasOwnProperty(k1)) {
const vs = Array.isArray(v1) ? v1 : [v1]
vs.forEach(it => {
target[it] = target[k1]
})
}
})
})
I18n.putVocabularies(dict)
function setupAuthConfigure(config) {
const {
region,
userPoolId,
userPoolWebClientId,
identityPoolId,
oauthDomain,
oauthProviders
} = config
Amplify.configure({
'aws_project_region': region,
'aws_cognito_identity_pool_id': identityPoolId,
'aws_cognito_region': region,
'aws_user_pools_id': userPoolId,
'aws_user_pools_web_client_id': userPoolWebClientId,
'authenticationFlowType': 'USER_SRP_AUTH',
'oauth': {
'domain': oauthDomain,
'scope': [
'phone',
'email',
'openid',
'profile',
'aws.cognito.signin.user.admin'
],
'redirectSignIn': 'https://logseq.com/public/auth_callback.html',
'redirectSignOut': 'https://logseq.com/public/auth_callback.html',
'responseType': 'code'
},
'federationTarget': 'COGNITO_USER_POOLS',
'aws_cognito_social_providers': oauthProviders || [
'GOOGLE'
],
'aws_cognito_signup_attributes': [
'EMAIL'
],
'aws_cognito_password_protection_settings': {
'passwordPolicyMinLength': 8,
'passwordPolicyCharacters': []
},
'aws_cognito_verification_mechanisms': [
'EMAIL'
]
})
}
//@ts-ignore
window.LSAmplify = {
setupAuthConfigure,
LSAuthenticator, LSAuthenticatorChangePassword,
Auth, Amplify, Hub, I18n
}

5516
packages/amplify/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -51,6 +51,7 @@
<script defer src="/static/js/highlight.min.js"></script>
<script defer src="/static/js/interact.min.js"></script>
<script defer src="/static/js/main.js"></script>
<script defer src="/static/js/amplify.js"></script>
<script defer src="/static/js/tabler.min.js"></script>
<script defer src="/static/js/code-editor.js"></script>
<script defer src="/static/js/tldraw.js"></script>

File diff suppressed because one or more lines are too long

View File

@ -52,6 +52,7 @@ const portal = new MagicPortal(worker);
<script defer src="./js/interact.min.js"></script>
<script defer src="./js/lsplugin.core.js"></script>
<script defer src="./js/main.js"></script>
<script defer src="./js/amplify.js"></script>
<script defer src="./js/tabler.min.js"></script>
<script defer src="./js/code-editor.js"></script>
<script defer src="./js/excalidraw.js"></script>

View File

@ -51,6 +51,7 @@ const portal = new MagicPortal(worker);
<script defer src="./js/interact.min.js"></script>
<script defer src="./js/lsplugin.core.js"></script>
<script defer src="./js/main.js"></script>
<script defer src="./js/amplify.js"></script>
<script defer src="./js/tabler.min.js"></script>
<script defer src="./js/code-editor.js"></script>
<script defer src="./js/excalidraw.js"></script>

142
resources/js/amplify.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -90,9 +90,6 @@
[^js win parsed-url]
(let [url-host (.-host parsed-url)] ;; return "" when no pathname provided
(cond
(= "auth-callback" url-host)
(send-to-renderer win "loginCallback" (.get (.-searchParams parsed-url) "code"))
(= "x-callback-url" url-host)
(x-callback-url-handler win parsed-url)

View File

@ -46,10 +46,12 @@
(when-not (or config/publishing?
logged?
(not sync-enabled?))
[:a.button.text-sm.font-medium.block {:on-click #(js/window.open config/LOGIN-URL)}
[:span.flex.space-x-2
[:a.button.text-sm.font-medium.block
{:on-click #(state/pub-event! [:user/login])}
[:span (t :login)]
(when loading?
[:span.ml-2 (ui/loading "")])])))
[:span.ml-2 (ui/loading "")])]])))
(rum/defc left-menu-button < rum/reactive
< {:key-fn #(identity "left-menu-toggle-button")}

View File

@ -0,0 +1,56 @@
import {Amplify} from '@aws-amplify/core';
Amplify.configure({
Auth: {
// REQUIRED only for Federated Authentication - Amazon Cognito Identity Pool ID
// identityPoolId: 'XX-XXXX-X:XXXXXXXX-XXXX-1234-abcd-1234567890ab',
// REQUIRED - Amazon Cognito Region
region: 'us-east-1',
// OPTIONAL - Amazon Cognito Federated Identity Pool Region
// Required only if it's different from Amazon Cognito Region
// identityPoolRegion: 'XX-XXXX-X',
// OPTIONAL - Amazon Cognito User Pool ID
userPoolId: 'us-east-1_ldvDmC9Fe',
// OPTIONAL - Amazon Cognito Web Client ID (26-char alphanumeric string)
userPoolWebClientId: '41m82unjghlea984vjpk887qcr',
// OPTIONAL - Enforce user authentication prior to accessing AWS resources or not
// mandatorySignIn: false,
// OPTIONAL - This is used when autoSignIn is enabled for Auth.signUp
// 'code' is used for Auth.confirmSignUp, 'link' is used for email link verification
// signUpVerificationMethod: 'code', // 'code' | 'link'
// OPTIONAL - Configuration for cookie storage
// Note: if the secure flag is set to true, then the cookie transmission requires a secure protocol
cookieStorage: {
domain: "localhost",
path: "/",
expires: 365,
sameSite: "strict",
secure: true,
},
// OPTIONAL - customized storage object
// storage: MyStorage,
// OPTIONAL - Manually set the authentication flow type. Default is 'USER_SRP_AUTH'
authenticationFlowType: 'USER_SRP_AUTH',
//
// // OPTIONAL - Manually set key value pairs that can be passed to Cognito Lambda Triggers
// clientMetadata: {myCustomKey: 'myCustomValue'},
//
// // OPTIONAL - Hosted UI configuration
// oauth: {
// domain: 'your_cognito_domain',
// scope: ['phone', 'email', 'profile', 'openid', 'aws.cognito.signin.user.admin'],
// redirectSignIn: 'http://localhost:3000/',
// redirectSignOut: 'http://localhost:3000/',
// responseType: 'code' // or 'token', note that REFRESH token will only be generated when the responseType is code
}
});

View File

@ -0,0 +1,87 @@
(ns frontend.components.user.login
(:require [rum.core :as rum]
[frontend.rum :refer [adapt-class]]
[frontend.modules.shortcut.core :as shortcut]
[frontend.handler.user :as user]
[cljs-bean.core :as bean]
[frontend.handler.notification :as notification]
[frontend.state :as state]
[frontend.config :as config]))
(declare setupAuthConfigure! LSAuthenticator)
(defn sign-out!
[]
(try (.signOut js/LSAmplify.Auth)
(catch :default e (js/console.warn e))))
(defn- setup-configure!
[]
#_:clj-kondo/ignore
(def setupAuthConfigure! (.-setupAuthConfigure js/LSAmplify))
#_:clj-kondo/ignore
(def LSAuthenticator
(adapt-class (.-LSAuthenticator js/LSAmplify)))
(.setLanguage js/LSAmplify.I18n (or (:preferred-language @state/state) "en"))
(setupAuthConfigure!
#js {:region config/REGION,
:userPoolId config/USER-POOL-ID,
:userPoolWebClientId config/COGNITO-CLIENT-ID,
:identityPoolId config/IDENTITY-POOL-ID,
:oauthDomain config/OAUTH-DOMAIN}))
(rum/defc user-pane
[_sign-out! user]
(let [session (:signInUserSession user)
username (:username user)]
(rum/use-effect!
(fn []
(when session
(user/login-callback session)
(notification/show! (str "Hi, " username " :)") :success)
(state/close-modal!)))
[])
nil))
(rum/defc page-impl
[]
(let [[ready?, set-ready?] (rum/use-state false)
*ref-el (rum/use-ref nil)]
(rum/use-effect!
(fn [] (setup-configure!)
(set-ready? true)
(when-let [^js el (rum/deref *ref-el)]
(js/setTimeout #(some-> (.querySelector el "input[name=username]")
(.focus)) 100))) [])
[:div.cp__user-login
{:ref *ref-el}
(when ready?
(LSAuthenticator
{:termsLink "https://blog.logseq.com/terms/"}
(fn [^js op]
(let [sign-out! (.-signOut op)
^js user-proxy (.-user op)
^js user (try (js/JSON.parse (js/JSON.stringify user-proxy))
(catch js/Error e
(js/console.error "Error: Amplify user payload:" e)))
user' (bean/->clj user)]
(user-pane sign-out! user')))))]))
(rum/defcs page <
(shortcut/disable-all-shortcuts)
[_state]
(page-impl))
(defn open-login-modal!
[]
(state/set-modal!
(fn [_close] (page))
{:close-btn? true
:label "user-login"
:close-backdrop? false
:center? true}))

View File

@ -0,0 +1,135 @@
.cp__user-login {
[data-amplify-authenticator] [data-amplify-router] {
--amplify-components-authenticator-router-background-color: var(--ls-primary-background-color);
--amplify-components-field-label-color: var(--ls-primary-text-color);
--amplify-components-authenticator-router-border-color: var(--ls-border-color);
--amplify-components-tabs-item-color: var(--ls-primary-text-color);
--amplify-components-tabs-item-active-color: var(--ls-primary-text-color);
--amplify-components-tabs-item-hover-color: var(--ls-primary-text-color);
--amplify-components-tabs-item-active-border-color: var(--ls-tertiary-background-color);
--amplify-components-tabs-border-width: 0;
--amplify-components-authenticator-state-inactive-background-color: var(--ls-tertiary-background-color);
--amplify-components-tabs-item-active-background-color: var(--ls-primary-background-color);
--amplify-components-button-border-color: var(--ls-border-color);
--amplify-components-textfield-border-color: var(--ls-border-color);
--amplify-components-button-primary-background-color: var(--color-indigo-600);
--amplify-components-text-color: var(--ls-primary-text-color);
--amplify-components-button-hover-background-color: var(--ls-primary-background-color);
--amplify-components-button-border-width: 0;
--amplify-internal-button-loading-background-color: var(--ls-header-button-background);
--amplify-components-authenticator-router-border-width: 1px;
--amplify-components-button-color: var(--ls-primary-text-color);
--amplify-components-divider-label-background-color: var(--ls-primary-background-color);
--amplify-components-divider-label-color: var(--ls-primary-text-color);
--amplify-components-heading-color: var(--ls-primary-text-color);
--amplify-components-button-link-hover-background-color: transparent;
--amplify-components-button-link-active-background-color: transparent;
--amplify-components-textfield-color: var(--ls-primary-text-color);
--amplify-components-checkbox-icon-background-color: var(--color-indigo-600);
}
[data-amplify-authenticator] [data-amplify-router] {
@apply overflow-hidden rounded-[6px] shadow-2xl;
}
[data-amplify-authenticator] [data-amplify-container] {
place-self: unset;
}
[data-amplify-authenticator] [data-amplify-form] {
@apply px-4 py-2;
@screen sm {
@apply px-6 py-4;
}
}
}
.ui__modal[label=user-login] {
@apply flex items-center top-0;
.ui__modal-panel {
transition: transform .3s;
transform: translateY(-50px);
}
.panel-content {
@apply p-0 min-w-fit relative max-w-[600px] sm:max-w-[90vw] w-auto;
}
.ui__modal-close-wrap {
@apply z-10 top-[4px];
}
}
.cp__user {
&-login {
::placeholder {
color: var(--ls-primary-text-color);
opacity: .3;
}
[data-indicator-position=top] > .amplify-tabs-item {
margin-top: 0;
}
.amplify-tabs-item {
transition: none;
&:focus {
color: var(--ls-primary-text-color);
}
&:hover {
opacity: .9;
}
}
.amplify-field-group {
@apply relative;
.amplify-button {
color: var(--ls-primary-text-color);
&:active, &:hover, &:focus {
background-color: transparent;
}
}
}
.amplify-field-group__outer-end {
@apply absolute right-0 top-0 bottom-0;
}
.amplify-input {
border-radius: 4px !important;
}
.amplify-checkboxfield {
@apply text-sm;
.amplify-field__error-message {
color: var(--ls-primary-text-color);
opacity: .4;
}
}
.amplify-text--error {
color: var(--ls-error-text-color);
}
}
}
html {
&.is-mobile .ui__modal[label=user-login] .panel-content {
@apply pt-0;
}
&.has-mobile-keyboard .ui__modal[label=user-login] {
transform: translateY(calc(-50px - 6%));
}
}
.federated-sign-in-container {
display: none;
}

View File

@ -32,14 +32,24 @@
"https://logseq-prod.auth.us-east-1.amazoncognito.com/login?client_id=3c7np6bjtb4r1k1bi9i049ops5&response_type=code&scope=email+openid+phone&redirect_uri=logseq%3A%2F%2Fauth-callback")
(def API-DOMAIN "api.logseq.com")
(def WS-URL "wss://ws.logseq.com/file-sync?graphuuid=%s")
(def COGNITO-IDP "https://cognito-idp.us-east-1.amazonaws.com/"))
(def COGNITO-IDP "https://cognito-idp.us-east-1.amazonaws.com/")
(def COGNITO-CLIENT-ID "69cs1lgme7p8kbgld8n5kseii6")
(def REGION "us-east-1")
(def USER-POOL-ID "us-east-1_dtagLnju8")
(def IDENTITY-POOL-ID "us-east-1:d6d3b034-1631-402b-b838-b44513e93ee0")
(def OAUTH-DOMAIN "logseq-prod.auth.us-east-1.amazoncognito.com"))
(do (def FILE-SYNC-PROD? false)
(def LOGIN-URL
"https://logseq-test2.auth.us-east-2.amazoncognito.com/login?client_id=3ji1a0059hspovjq5fhed3uil8&response_type=code&scope=email+openid+phone&redirect_uri=logseq%3A%2F%2Fauth-callback")
(def API-DOMAIN "api-dev.logseq.com")
(def WS-URL "wss://ws-dev.logseq.com/file-sync?graphuuid=%s")
(def COGNITO-IDP "https://cognito-idp.us-east-2.amazonaws.com/")))
(def COGNITO-IDP "https://cognito-idp.us-east-2.amazonaws.com/")
(def COGNITO-CLIENT-ID "1qi1uijg8b6ra70nejvbptis0q")
(def REGION "us-east-2")
(def USER-POOL-ID "us-east-2_kAqZcxIeM")
(def IDENTITY-POOL-ID "us-east-2:cc7d2ad3-84d0-4faf-98fe-628f6b52c0a5")
(def OAUTH-DOMAIN "logseq-test2.auth.us-east-2.amazoncognito.com")))
;; Feature flags
;; =============

View File

@ -22,6 +22,7 @@
[frontend.components.search :as component-search]
[frontend.components.shell :as shell]
[frontend.components.whiteboard :as whiteboard]
[frontend.components.user.login :as login]
[frontend.config :as config]
[frontend.context.i18n :refer [t]]
[frontend.db :as db]
@ -121,7 +122,13 @@
(defmethod handle :user/logout [[_]]
(file-sync-handler/reset-session-graphs)
(sync/remove-all-pwd!)
(file-sync-handler/reset-user-state!))
(file-sync-handler/reset-user-state!)
(login/sign-out!))
(defmethod handle :user/login [[_ host-ui?]]
(if (or host-ui? (not util/electron?))
(js/window.open config/LOGIN-URL)
(login/open-login-modal!)))
(defmethod handle :graph/added [[_ repo {:keys [empty-graph?]}]]
(db/set-key-value repo :ast/version db-schema/ast-version)
@ -468,7 +475,8 @@
(reset! util/keyboard-height keyboard-height)
(set! (.. main-node -style -marginBottom) (str keyboard-height "px"))
(when-let [^js html (js/document.querySelector ":root")]
(.setProperty (.-style html) "--ls-native-kb-height" (str keyboard-height "px")))
(.setProperty (.-style html) "--ls-native-kb-height" (str keyboard-height "px"))
(.add (.-classList html) "has-mobile-keyboard"))
(when-let [left-sidebar-node (gdom/getElement "left-sidebar")]
(set! (.. left-sidebar-node -style -bottom) (str keyboard-height "px")))
(when-let [right-sidebar-node (gdom/getElementByClass "sidebar-item-list")]
@ -490,7 +498,8 @@
(state/set-state! :mobile/show-recording-bar? false))
(when (mobile-util/native-ios?)
(when-let [^js html (js/document.querySelector ":root")]
(.removeProperty (.-style html) "--ls-native-kb-height"))
(.removeProperty (.-style html) "--ls-native-kb-height")
(.remove (.-classList html) "has-mobile-keyboard"))
(when-let [card-preview-el (js/document.querySelector ".cards-review")]
(set! (.. card-preview-el -style -marginBottom) "0px"))
(when-let [card-preview-el (js/document.querySelector ".encryption-password")]

View File

@ -107,14 +107,20 @@
(state/set-auth-refresh-token refresh-token)
(set-token-to-localstorage! id-token access-token refresh-token)))
(defn- <refresh-tokens
"return refreshed id-token, access-token"
[refresh-token]
(http/post (str "https://" config/OAUTH-DOMAIN "/oauth2/token")
{:form-params {:grant_type "refresh_token"
:client_id config/COGNITO-CLIENT-ID
:refresh_token refresh-token}}))
(defn <refresh-id-token&access-token
"Refresh id-token and access-token"
[]
(go
(when-let [refresh-token (state/get-auth-refresh-token)]
(let [resp (<! (http/get (str "https://" config/API-DOMAIN "/auth_refresh_token?refresh_token=" refresh-token)
{:with-credentials? false}))]
(let [resp (<! (<refresh-tokens refresh-token))]
(cond
(and (<= 400 (:status resp))
(> 500 (:status resp)))
@ -146,17 +152,13 @@
;; refresh remote graph list by pub login event
(when (user-uuid) (state/pub-event! [:user/fetch-info-and-graphs]))))))
(defn ^:export login-callback [code]
(state/set-state! [:ui/loading? :login] true)
(go
(let [resp (<! (http/get (str "https://" config/API-DOMAIN "/auth_callback?code=" code)
{:with-credentials? false}))]
(if (= 200 (:status resp))
(-> resp
:body
(as-> $ (set-tokens! (:id_token $) (:access_token $) (:refresh_token $)))
(#(state/pub-event! [:user/fetch-info-and-graphs])))
(debug/pprint "login-callback" resp)))))
(defn login-callback
[session]
(set-tokens!
(:jwtToken (:idToken session))
(:jwtToken (:accessToken session))
(:token (:refreshToken session)))
(state/pub-event! [:user/fetch-info-and-graphs]))
(defn ^:export login-with-username-password-e2e
[username password client-id client-secret]

View File

@ -7,7 +7,6 @@
[frontend.handler.editor :as editor-handler]
[frontend.handler.notification :as notification]
[frontend.handler.route :as route-handler]
[frontend.handler.user :as user-handler]
[frontend.mobile.intent :as intent]
[frontend.state :as state]
[frontend.util.text :as text-util]))
@ -30,10 +29,6 @@
(map :url))
repo-names (map #(get-graph-name-fn %) repos)]
(cond
(= hostname "auth-callback")
(when-let [code (.get search-params "code")]
(user-handler/login-callback code))
(= hostname "graph")
(let [graph-name (some-> pathname
(string/replace "/" "")

View File

@ -12,7 +12,8 @@
[frontend.components.shortcut :as shortcut]
[frontend.components.whiteboard :as whiteboard]
[frontend.extensions.zotero :as zotero]
[frontend.components.bug-report :as bug-report]))
[frontend.components.bug-report :as bug-report]
[frontend.components.user.login :as login]))
;; http://localhost:3000/#?anchor=fn.1
(def routes
@ -94,4 +95,8 @@
["/plugins"
{:name :plugins
:view plugins/plugins-page}]])
:view plugins/plugins-page}]
["/login"
{:name :user-login
:view login/page}]])

View File

@ -563,9 +563,9 @@
"entered" "ease-out duration-300 opacity-100 translate-y-0 sm:scale-100"
"exiting" "ease-in duration-200 opacity-100 translate-y-0 sm:scale-100"
"exited" "ease-in duration-200 opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95")}
[:div.absolute.top-0.right-0.pt-2.pr-2
[:div.ui__modal-close-wrap
(when-not (false? close-btn?)
[:a.ui__modal-close.opacity-60.hover:opacity-100
[:a.ui__modal-close
{:aria-label "Close"
:type "button"
:on-click close-fn}

View File

@ -146,7 +146,12 @@
&-close {
@apply text-gray-400 hover:text-gray-500
focus:outline-none focus:text-gray-500
transition ease-in-out duration-150;
transition ease-in-out duration-150 opacity-60
hover:opacity-100;
&-wrap {
@apply absolute top-0 right-0 pt-2 pr-2;
}
}
&[label="ls-modal-align-center"] {
@ -200,8 +205,7 @@
html.is-native-android,
html.is-native-iphone,
html.is-native-iphone-without-notch
{
html.is-native-iphone-without-notch {
.references {
.blocks-container {
transform: translateX(-8px);

View File

@ -9,6 +9,7 @@
@import "resources/css/shepherd.css";
@import "resources/css/fonts.css";
@import "resources/css/excalidraw.min.css";
@import "resources/css/amplify.css";
@import "tldraw/apps/tldraw-logseq/src/styles.css";
@import "resources/css/katex.min.css";
@import "resources/css/codemirror.min.css";