mirror of
https://github.com/logto-io/logto.git
synced 2025-02-24 22:05:56 -05:00
Merge pull request #941 from logto-io/charles-log-2588-add-integration-guide-for-native-sdks
refactor(console): refactor integration guide to support native SDKs
This commit is contained in:
commit
95e394ba1c
13 changed files with 521 additions and 110 deletions
|
@ -1,3 +1,4 @@
|
|||
import MultiTextInputField from '@mdx/components/MultiTextInputField';
|
||||
import Step from '@mdx/components/Step';
|
||||
import Tabs from '@mdx/components/Tabs';
|
||||
import TabItem from '@mdx/components/TabItem';
|
||||
|
@ -12,8 +13,16 @@ import TabItem from '@mdx/components/TabItem';
|
|||
>
|
||||
|
||||
### Prerequisite
|
||||
* Minimal Android SDK: Level 24 (TBD)
|
||||
* Minimal Android SDK: Level 24
|
||||
|
||||
Add the `mavenCentral()` repository to your Gradle project build file:
|
||||
```kotlin
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
```
|
||||
|
||||
Add the Logto Android SDK to your dependencies:
|
||||
<Tabs>
|
||||
|
||||
<TabItem value="kotlin" label="Kotlin">
|
||||
|
@ -55,7 +64,7 @@ The `Redirect URI` indicates the endpoint that the application used to receive a
|
|||
|
||||
### Configure Redirect URI in Admin Console
|
||||
|
||||
Add the following value to Redirect URIs on the application settings page.
|
||||
Add the following value to Redirect URIs input field
|
||||
|
||||
```bash
|
||||
$(LOGTO_REDIRECT_SCHEME)://$(YOUR_APP_PACKAGE)/callback
|
||||
|
@ -67,6 +76,8 @@ Notes:
|
|||
|
||||
e.g. `io.logto.android://io.logto.sample/callback`
|
||||
|
||||
<MultiTextInputField name="redirectUris" title="Redirect URI" onError={() => props.onError(2)} />
|
||||
|
||||
### Configure Logto Android SDK
|
||||
|
||||
<Tabs>
|
||||
|
@ -132,7 +143,7 @@ Notes:
|
|||
|
||||
<Step
|
||||
title="Sign In"
|
||||
subtitle="Sin In to your application by Logto and do some extra works"
|
||||
subtitle="Sign In to your application by Logto and do some extra works"
|
||||
index={2}
|
||||
activeIndex={props.activeStepIndex}
|
||||
invalidIndex={props.invalidStepIndex}
|
||||
|
|
|
@ -0,0 +1,284 @@
|
|||
import MultiTextInputField from '@mdx/components/MultiTextInputField';
|
||||
import Step from '@mdx/components/Step';
|
||||
import Tabs from '@mdx/components/Tabs';
|
||||
import TabItem from '@mdx/components/TabItem';
|
||||
|
||||
<Step
|
||||
title="安装 SDK"
|
||||
subtitle="从 Gradle 安装 Logto Android SDK"
|
||||
index={0}
|
||||
activeIndex={props.activeStepIndex}
|
||||
invalidIndex={props.invalidStepIndex}
|
||||
onNext={() => props.onNext(1)}
|
||||
>
|
||||
|
||||
### 前提条件
|
||||
* 最小 Android SDK 版本: Level 24
|
||||
|
||||
将 `mavenCentral()` 添加到构建脚本中:
|
||||
```kotlin
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
```
|
||||
|
||||
添加 Logto Android SDK 依赖:
|
||||
<Tabs>
|
||||
|
||||
<TabItem value="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
implementation("io.logto.sdk:android:1.0.0")
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="groovy" label="Groovy">
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation 'io.logto.sdk:android:1.0.0'
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="配置"
|
||||
subtitle="Configure your application and LogtoClient"
|
||||
index={1}
|
||||
activeIndex={props.activeStepIndex}
|
||||
invalidIndex={props.invalidStepIndex}
|
||||
onNext={() => props.onNext(2)}
|
||||
>
|
||||
|
||||
### 配置 Redirect URI
|
||||
|
||||
Redirect URI 指定了应用用来接受授权结果的端口,Logto Android SDK 内部实现了该重定向的功能
|
||||
|
||||
### 在管理员控制台中配置 Redirect URI
|
||||
|
||||
将以下值添加到下面的 Redirect URI 输入框中:
|
||||
|
||||
```bash
|
||||
$(LOGTO_REDIRECT_SCHEME)://$(YOUR_APP_PACKAGE)/callback
|
||||
```
|
||||
|
||||
注意:
|
||||
- `LOGTO_REDIRECT_SCHEME` 应为自定义的反向域名格式的一串字符。
|
||||
- 将上述的 `$(LOGTO_REDIRECT_SCHEME)` 替换成你定义的值。
|
||||
|
||||
例: `io.logto.android://io.logto.sample/callback`
|
||||
|
||||
<MultiTextInputField name="redirectUris" title="Redirect URI" onError={() => props.onError(1)} />
|
||||
|
||||
### 配置 Logto Android SDK
|
||||
|
||||
<Tabs>
|
||||
|
||||
<TabItem value="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
import io.logto.sdk.android.LogtoClient
|
||||
import io.logto.sdk.android.type.LogtoConfig
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
val logtoConfig = LogtoConfig(
|
||||
endpoint = "<your-logto-endpoint>",
|
||||
appId = "<your-app-id>",
|
||||
scopes = null,
|
||||
resources = null,
|
||||
usingPersistStorage = true,
|
||||
)
|
||||
|
||||
val logtoClient = LogtoClient(logtoConfig, application)
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="java" label="Java">
|
||||
|
||||
```java
|
||||
import io.logto.sdk.android.LogtoClient;
|
||||
import io.logto.sdk.android.type.LogtoConfig;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private LogtoClient logtoClient;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
LogtoConfig logtoConfig = new LogtoConfig(
|
||||
"<your-logto-endpoint>",
|
||||
"<your-app-id>",
|
||||
null,
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
logtoClient = new LogtoClient(logtoConfig, getApplication());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
Notes:
|
||||
|
||||
- `<your-logto-endpoint>` 是你运行 Logto 服务所在的地址. 若你的 Logto 服务运行在 `http://localhost:300`,则 `<your-logto-endpoint` 为 `http://localhost:300`
|
||||
- `<your-app-id>` 是你为自己的应用在管理员控制台中所创建的客户端ID.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="登录"
|
||||
subtitle="Sign In to your application by Logto and do some extra works"
|
||||
index={2}
|
||||
activeIndex={props.activeStepIndex}
|
||||
invalidIndex={props.invalidStepIndex}
|
||||
onNext={() => props.onNext(3)}
|
||||
>
|
||||
|
||||
### 执行登录
|
||||
|
||||
<Tabs>
|
||||
|
||||
<TabItem value="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
logtoClient.signInWithBrowser(
|
||||
this,
|
||||
"io.logto.android://io.logto.sample/callback",
|
||||
) { logtoException: LogtoException? ->
|
||||
// 后续处理逻辑
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="java" label="Java">
|
||||
|
||||
```java
|
||||
logtoClient.signInWithBrowser(
|
||||
this,
|
||||
"io.logto.android://io.logto.sample/callback",
|
||||
logtoException -> {
|
||||
// 后续处理逻辑
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### 登录成功后,你可以使用 SDK 提供的一些 API 来实现自己的业务逻辑
|
||||
|
||||
<Tabs>
|
||||
|
||||
<TabItem value="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
logtoClient.getAccessToken { logtoException: LogtoException?, result: AccessToken? ->
|
||||
// 后续处理逻辑
|
||||
}
|
||||
|
||||
logtoClient.getIdTokenClaims { logtoException: LogtoException?, result: IdTokenClaims? ->
|
||||
// 后续处理逻辑
|
||||
}
|
||||
|
||||
logtoClient.fetchUserInfo { logtoException: LogtoException?, userInfoResponse: UserInfoResponse? ->
|
||||
// 后续处理逻辑
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="java" label="Java">
|
||||
|
||||
```java
|
||||
logtoClient.getAccessToken((logtoException, accessToken) -> {
|
||||
// 后续处理逻辑
|
||||
});
|
||||
|
||||
logtoClient.getIdTokenClaims((logtoException, idTokenClaims) -> {
|
||||
// 后续处理逻辑
|
||||
});
|
||||
|
||||
logtoClient.fetchUserInfo((logtoException, userInfoResponse) -> {
|
||||
// 后续处理逻辑
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="登出"
|
||||
index={3}
|
||||
activeIndex={props.activeStepIndex}
|
||||
invalidIndex={props.invalidStepIndex}
|
||||
onNext={() => props.onNext(4)}
|
||||
>
|
||||
|
||||
### 执行登出
|
||||
|
||||
<Tabs>
|
||||
|
||||
<TabItem value="kotlin" label="Kotlin">
|
||||
|
||||
```kotlin
|
||||
logtoClient.signOut { logtoException: LogtoException? ->
|
||||
// 后续处理逻辑
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="java" label="Java">
|
||||
|
||||
```java
|
||||
logtoClient.signOut(logtoException -> {
|
||||
// 后续处理逻辑
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
注意:
|
||||
|
||||
- 登出操作会清除本地存储的用户相关凭据,即使在登出过程中发生了异常。
|
||||
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="延伸阅读"
|
||||
subtitle="3 steps"
|
||||
index={4}
|
||||
activeIndex={props.activeStepIndex}
|
||||
invalidIndex={props.invalidStepIndex}
|
||||
buttonText="general.done"
|
||||
buttonHtmlType="submit"
|
||||
>
|
||||
|
||||
- 获取用户信息
|
||||
- 配置社会化登录
|
||||
- 访问受保护的 API 资源
|
||||
|
||||
</Step>
|
118
packages/console/src/assets/docs/tutorial/integrate-sdk/ios.mdx
Normal file
118
packages/console/src/assets/docs/tutorial/integrate-sdk/ios.mdx
Normal file
|
@ -0,0 +1,118 @@
|
|||
import MultiTextInputField from '@mdx/components/MultiTextInputField';
|
||||
import Step from '@mdx/components/Step';
|
||||
import Tabs from '@mdx/components/Tabs';
|
||||
import TabItem from '@mdx/components/TabItem';
|
||||
|
||||
<Step
|
||||
title="Install SDK"
|
||||
subtitle="Add Logto SDK as a Dependency"
|
||||
index={0}
|
||||
activeIndex={props.activeStepIndex}
|
||||
invalidIndex={props.invalidStepIndex}
|
||||
onNext={() => props.onNext(1)}
|
||||
>
|
||||
|
||||
Use the following URL to add Logto SDK as a dependency in Swift Package Manager.
|
||||
|
||||
```bash
|
||||
https://github.com/logto-io/swift.git
|
||||
```
|
||||
|
||||
Since Xcode 11, you can [directly import a swift package](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app) w/o any additional tool.
|
||||
|
||||
We do not support **Carthage** and **CocoaPods** at the time due to some technical issues.
|
||||
|
||||
<details>
|
||||
<summary>Carthage</summary>
|
||||
Carthage <a href="https://github.com/Carthage/Carthage/issues/1226#issuecomment-290931385">needs a `xcodeproj` file to build</a>, but `swift package generate-xcodeproj` will report a failure since we are using binary targets for native social plugins. We will try to find a workaround later.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>CocoaPods</summary>
|
||||
CocoaPods <a href="https://github.com/CocoaPods/CocoaPods/issues/3276">does not support local dependency</a> and monorepo, thus it's hard to create a `.podspec` for this repo.
|
||||
</details>
|
||||
|
||||
</Step>
|
||||
<Step
|
||||
title="Init LogtoClient"
|
||||
index={1}
|
||||
activeIndex={props.activeStepIndex}
|
||||
invalidIndex={props.invalidStepIndex}
|
||||
onNext={() => props.onNext(2)}
|
||||
>
|
||||
|
||||
```swift
|
||||
import Logto
|
||||
|
||||
let config = try? LogtoConfig(
|
||||
endpoint: "<your-logto-endpoint>",
|
||||
appId: "<your-application-id>"
|
||||
)
|
||||
let logtoClient = LogtoClient(useConfig: config)
|
||||
```
|
||||
|
||||
By default, we store credentials like ID Token and Refresh Token in Keychain. Thus the user doesn't need to sign in again when he returns.
|
||||
|
||||
To turn off this behavior, set `usingPersistStorage` to `false`:
|
||||
|
||||
```swift
|
||||
let config = try? LogtoConfig(
|
||||
// ...
|
||||
usingPersistStorage: false
|
||||
)
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step
|
||||
title="Sign In"
|
||||
index={2}
|
||||
activeIndex={props.activeStepIndex}
|
||||
invalidIndex={props.invalidStepIndex}
|
||||
onNext={() => props.onNext(3)}
|
||||
>
|
||||
|
||||
First, let’s configure your redirect URI
|
||||
|
||||
<MultiTextInputField name="redirectUris" title="Redirect URI" onError={() => props.onError(2)} />
|
||||
|
||||
```swift
|
||||
do {
|
||||
try await client.signInWithBrowser(redirectUri: "<your-redirect-uri>")
|
||||
print(client.isAuthenticated) // true
|
||||
} catch let error as LogtoClientErrors.SignIn {
|
||||
// error occured during sign in
|
||||
}
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step
|
||||
title="Sign Out"
|
||||
subtitle="1 step"
|
||||
index={3}
|
||||
activeIndex={props.activeStepIndex}
|
||||
invalidIndex={props.invalidStepIndex}
|
||||
onNext={() => props.onNext(4)}
|
||||
>
|
||||
|
||||
Calling `.signOut()` will clean all the Logto data in Keychain, if it has.
|
||||
|
||||
```swift
|
||||
await client.signOut()
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step
|
||||
title="Further Readings"
|
||||
subtitle="3 steps"
|
||||
index={4}
|
||||
activeIndex={props.activeStepIndex}
|
||||
invalidIndex={props.invalidStepIndex}
|
||||
buttonText="general.done"
|
||||
buttonHtmlType="submit"
|
||||
>
|
||||
|
||||
- [SDK Documentation](https://link-url-here.org)
|
||||
- [OIDC Documentation](https://link-url-here.org)
|
||||
- [Calling API to fetch accessToken](https://link-url-here.org)
|
||||
|
||||
</Step>
|
|
@ -43,7 +43,6 @@ pnpm add @logto/vue
|
|||
</TabItem>
|
||||
</Tabs>
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="Initiate LogtoClient"
|
||||
subtitle="1 step"
|
||||
|
@ -70,7 +69,6 @@ app.mount("#app");
|
|||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="Sign In"
|
||||
subtitle="2 steps"
|
||||
|
@ -149,7 +147,6 @@ const { isAuthenticated } = useLogto();
|
|||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="Sign Out"
|
||||
subtitle="1 step"
|
||||
|
@ -178,7 +175,6 @@ const onClickSignOut = () => signOut('http://localhost:1234');
|
|||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="Further Readings"
|
||||
subtitle="3 steps"
|
||||
|
|
|
@ -43,7 +43,6 @@ pnpm add @logto/vue
|
|||
</TabItem>
|
||||
</Tabs>
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="初始化 LogtoClient"
|
||||
subtitle="1 step"
|
||||
|
@ -70,7 +69,6 @@ app.mount("#app");
|
|||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="Sign In"
|
||||
subtitle="2 steps"
|
||||
|
@ -150,7 +148,6 @@ const { isAuthenticated } = useLogto();
|
|||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="Sign Out"
|
||||
subtitle="1 step"
|
||||
|
@ -178,7 +175,6 @@ const onClickSignOut = () => signOut('http://localhost:1234');
|
|||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step
|
||||
title="延伸阅读"
|
||||
subtitle="3 steps"
|
||||
|
|
|
@ -130,6 +130,7 @@ const CreateForm = ({ onClose }: Props) => {
|
|||
{createdApp && (
|
||||
<GuideModal
|
||||
appName={createdApp.name}
|
||||
appType={createdApp.type}
|
||||
isOpen={isGetStartedModalOpen}
|
||||
onClose={closeModal}
|
||||
onComplete={onComplete}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { ApplicationType } from '@logto/schemas';
|
||||
import { MDXProvider } from '@mdx-js/react';
|
||||
import i18next from 'i18next';
|
||||
import { MDXProps } from 'mdx/types';
|
||||
|
@ -13,23 +14,29 @@ import IconButton from '@/components/IconButton';
|
|||
import Spacer from '@/components/Spacer';
|
||||
import Close from '@/icons/Close';
|
||||
import * as modalStyles from '@/scss/modal.module.scss';
|
||||
import { SupportedJavascriptLibraries } from '@/types/applications';
|
||||
import { applicationTypeAndSdkTypeMappings, SupportedSdk } from '@/types/applications';
|
||||
import { GuideForm } from '@/types/guide';
|
||||
|
||||
import LibrarySelector from '../LibrarySelector';
|
||||
import SdkSelector from '../SdkSelector';
|
||||
import StepsSkeleton from '../StepsSkeleton';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
appName: string;
|
||||
appType: ApplicationType;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onComplete: (data: GuideForm) => Promise<void>;
|
||||
};
|
||||
|
||||
const Guides: Record<string, LazyExoticComponent<(props: MDXProps) => JSX.Element>> = {
|
||||
ios: lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/ios.mdx')),
|
||||
android: lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/android.mdx')),
|
||||
react: lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/react.mdx')),
|
||||
vue: lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/vue.mdx')),
|
||||
'android_zh-cn': lazy(
|
||||
async () => import('@/assets/docs/tutorial/integrate-sdk/android_zh-cn.mdx')
|
||||
),
|
||||
'react_zh-cn': lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/react_zh-cn.mdx')),
|
||||
'vue_zh-cn': lazy(async () => import('@/assets/docs/tutorial/integrate-sdk/vue_zh-cn.mdx')),
|
||||
};
|
||||
|
@ -39,16 +46,15 @@ const onClickFetchSampleProject = (projectName: string) => {
|
|||
window.open(sampleUrl, '_blank');
|
||||
};
|
||||
|
||||
const GuideModal = ({ appName, isOpen, onClose, onComplete }: Props) => {
|
||||
const [subtype, setSubtype] = useState<SupportedJavascriptLibraries>(
|
||||
SupportedJavascriptLibraries.React
|
||||
);
|
||||
const GuideModal = ({ appName, appType, isOpen, onClose, onComplete }: Props) => {
|
||||
const sdks = applicationTypeAndSdkTypeMappings[appType];
|
||||
const [selectedSdk, setSelectedSdk] = useState<SupportedSdk>(sdks[0]);
|
||||
const [activeStepIndex, setActiveStepIndex] = useState(-1);
|
||||
const [invalidStepIndex, setInvalidStepIndex] = useState(-1);
|
||||
|
||||
const locale = i18next.language;
|
||||
const guideI18nKey = `${subtype}_${locale}`.toLowerCase();
|
||||
const GuideComponent = Guides[guideI18nKey] ?? Guides[subtype];
|
||||
const guideI18nKey = `${selectedSdk}_${locale}`.toLowerCase();
|
||||
const GuideComponent = Guides[guideI18nKey] ?? Guides[selectedSdk.toLowerCase()];
|
||||
|
||||
const methods = useForm<GuideForm>({ mode: 'onSubmit', reValidateMode: 'onChange' });
|
||||
const {
|
||||
|
@ -82,16 +88,16 @@ const GuideModal = ({ appName, isOpen, onClose, onComplete }: Props) => {
|
|||
type="outline"
|
||||
title="admin_console.applications.guide.get_sample_file"
|
||||
onClick={() => {
|
||||
onClickFetchSampleProject(subtype);
|
||||
onClickFetchSampleProject(selectedSdk);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<FormProvider {...methods}>
|
||||
<form onSubmit={onSubmit}>
|
||||
{cloneElement(<LibrarySelector libraryName={subtype} />, {
|
||||
{cloneElement(<SdkSelector sdks={sdks} selectedSdk={selectedSdk} />, {
|
||||
className: styles.banner,
|
||||
onChange: setSubtype,
|
||||
onChange: setSelectedSdk,
|
||||
onToggle: () => {
|
||||
setActiveStepIndex(0);
|
||||
},
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import congrats from '@/assets/images/congrats.svg';
|
||||
import tada from '@/assets/images/tada.svg';
|
||||
import Button from '@/components/Button';
|
||||
import Card from '@/components/Card';
|
||||
import RadioGroup, { Radio } from '@/components/RadioGroup';
|
||||
import { SupportedJavascriptLibraries } from '@/types/applications';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
libraryName?: SupportedJavascriptLibraries;
|
||||
onChange?: (value: string) => void;
|
||||
onToggle?: () => void;
|
||||
};
|
||||
|
||||
const LibrarySelector = ({
|
||||
className,
|
||||
libraryName = SupportedJavascriptLibraries.React,
|
||||
onChange,
|
||||
onToggle,
|
||||
}: Props) => {
|
||||
const [isFolded, setIsFolded] = useState(false);
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const librarySelector = useMemo(
|
||||
() => (
|
||||
<Card className={classNames(styles.card, className)}>
|
||||
<img src={congrats} alt="success" />
|
||||
<div>
|
||||
<div className={styles.title}>{t('applications.guide.title')}</div>
|
||||
<div className={styles.subtitle}>{t('applications.guide.subtitle')}</div>
|
||||
</div>
|
||||
<RadioGroup
|
||||
className={styles.radioGroup}
|
||||
name="libraryName"
|
||||
value={libraryName}
|
||||
type="card"
|
||||
onChange={onChange}
|
||||
>
|
||||
{Object.values(SupportedJavascriptLibraries).map((library) => (
|
||||
<Radio key={library} className={styles.radio} value={library}>
|
||||
{library}
|
||||
</Radio>
|
||||
))}
|
||||
</RadioGroup>
|
||||
<div className={styles.buttonWrapper}>
|
||||
<Button
|
||||
type="primary"
|
||||
title="general.next"
|
||||
onClick={() => {
|
||||
setIsFolded(true);
|
||||
onToggle?.();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
),
|
||||
[className, libraryName, onChange, onToggle, t]
|
||||
);
|
||||
|
||||
const librarySelectorFolded = useMemo(
|
||||
() => (
|
||||
<div className={classNames(styles.card, styles.folded, className)}>
|
||||
<img src={tada} alt="Tada!" />
|
||||
<span>{t('applications.guide.description_by_library', { library: libraryName })}</span>
|
||||
</div>
|
||||
),
|
||||
[className, libraryName, t]
|
||||
);
|
||||
|
||||
return isFolded ? librarySelectorFolded : librarySelector;
|
||||
};
|
||||
|
||||
export default LibrarySelector;
|
|
@ -0,0 +1,70 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import congrats from '@/assets/images/congrats.svg';
|
||||
import tada from '@/assets/images/tada.svg';
|
||||
import Button from '@/components/Button';
|
||||
import Card from '@/components/Card';
|
||||
import RadioGroup, { Radio } from '@/components/RadioGroup';
|
||||
import { SupportedSdk } from '@/types/applications';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
sdks: readonly SupportedSdk[];
|
||||
selectedSdk: SupportedSdk;
|
||||
onChange?: (value: string) => void;
|
||||
onToggle?: () => void;
|
||||
};
|
||||
|
||||
const SdkSelector = ({ className, sdks, selectedSdk, onChange, onToggle }: Props) => {
|
||||
const [isFolded, setIsFolded] = useState(false);
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
if (isFolded) {
|
||||
return (
|
||||
<div className={classNames(styles.card, styles.folded, className)}>
|
||||
<img src={tada} alt="Tada!" />
|
||||
<span>{t('applications.guide.description_by_sdk', { sdk: selectedSdk })}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={classNames(styles.card, className)}>
|
||||
<img src={congrats} alt="success" />
|
||||
<div>
|
||||
<div className={styles.title}>{t('applications.guide.title')}</div>
|
||||
<div className={styles.subtitle}>{t('applications.guide.subtitle')}</div>
|
||||
</div>
|
||||
<RadioGroup
|
||||
className={styles.radioGroup}
|
||||
name="selectedSdk"
|
||||
value={selectedSdk}
|
||||
type="card"
|
||||
onChange={onChange}
|
||||
>
|
||||
{sdks.length > 1 &&
|
||||
sdks.map((sdk) => (
|
||||
<Radio key={sdk} className={styles.radio} value={sdk}>
|
||||
{sdk}
|
||||
</Radio>
|
||||
))}
|
||||
</RadioGroup>
|
||||
<div className={styles.buttonWrapper}>
|
||||
<Button
|
||||
type="primary"
|
||||
title="general.next"
|
||||
onClick={() => {
|
||||
setIsFolded(true);
|
||||
onToggle?.();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default SdkSelector;
|
|
@ -6,8 +6,17 @@ export const applicationTypeI18nKey = Object.freeze({
|
|||
[ApplicationType.Traditional]: 'applications.type.traditional',
|
||||
} as const);
|
||||
|
||||
export enum SupportedJavascriptLibraries {
|
||||
Angular = 'angular',
|
||||
React = 'react',
|
||||
Vue = 'vue',
|
||||
export enum SupportedSdk {
|
||||
iOS = 'iOS',
|
||||
Android = 'Android',
|
||||
Angular = 'Angular',
|
||||
React = 'React',
|
||||
Vue = 'Vue',
|
||||
Traditional = 'Traditional',
|
||||
}
|
||||
|
||||
export const applicationTypeAndSdkTypeMappings = Object.freeze({
|
||||
[ApplicationType.Native]: [SupportedSdk.iOS, SupportedSdk.Android],
|
||||
[ApplicationType.SPA]: [SupportedSdk.Angular, SupportedSdk.React, SupportedSdk.Vue],
|
||||
[ApplicationType.Traditional]: [SupportedSdk.Traditional],
|
||||
} as const);
|
||||
|
|
|
@ -169,9 +169,8 @@ const translation = {
|
|||
'Follow a step by step guide to integrate your application or get a sample configured with your account settings',
|
||||
title: 'Congratulations! The application has been created successfully.',
|
||||
subtitle:
|
||||
'Now follow the steps below to finish your app settings. Please select the JS library to continue.',
|
||||
description_by_library:
|
||||
'This quickstart demonstrates how to add Logto to {{library}} application.',
|
||||
'Now follow the steps below to finish your app settings. Please select the SDK type to continue.',
|
||||
description_by_sdk: 'This quickstart demonstrates how to add Logto to {{sdk}} application.',
|
||||
},
|
||||
},
|
||||
application_details: {
|
||||
|
|
|
@ -168,8 +168,8 @@ const translation = {
|
|||
header_description:
|
||||
'参考如下教程,将 Logto 集成到您的应用中。您也可以点击右侧链接,获取我们为您准备好的示范工程。',
|
||||
title: '恭喜!您的应用已成功创建。',
|
||||
subtitle: '请参考以下步骤完成您的应用设置。首先,请选择您要使用的 Javascript 框架:',
|
||||
description_by_library: '本教程向您演示如何在 {{library}} 应用中集成 Logto 登录功能',
|
||||
subtitle: '请参考以下步骤完成您的应用设置。首先,请选择您要使用的 SDK 类型:',
|
||||
description_by_sdk: '本教程向您演示如何在 {{sdk}} 应用中集成 Logto 登录功能',
|
||||
},
|
||||
},
|
||||
application_details: {
|
||||
|
|
Loading…
Add table
Reference in a new issue