Skip to content

Commit

Permalink
Bluesky連携ページ仮実装 (#412)
Browse files Browse the repository at this point in the history
## Issue

## 変更内容
- Blueskyアカウントの連携ページを作成しました。
- デザインなどは仮です。

@coderabbitai: ignore
  • Loading branch information
takecchi authored Apr 18, 2024
1 parent 04a6d24 commit 50db764
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 3 deletions.
27 changes: 27 additions & 0 deletions src/app/(menu)/(private)/settings/_components/Settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client';

import { styled } from '@mui/material';
import Link from 'next/link';

const Root = styled('div')`
display: flex;
flex-direction: column;
padding: 0 20px;
`;

const H2 = styled('h2')`
font-size: 20px;
`;

/**
* FIXME デザインは仮です
* @constructor
*/
export default function Settings() {
return (
<Root>
<H2>外部連携</H2>
<Link href={'/settings/connect-bluesky'}>Blueskyと連携する</Link>
</Root>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
'use client';

import {
Alert,
Button,
IconButton,
InputAdornment,
LinearProgress,
OutlinedInput,
Snackbar,
styled,
} from '@mui/material';
import Link from 'next/link';
import { Visibility, VisibilityOff } from '@mui/icons-material';
import { useState } from 'react';
import { useConnectBsky } from '@/swr/client/bluesky';

const Root = styled('div')`
display: flex;
flex-direction: column;
padding: 20px;
gap: 40px;
`;

/**
* FIXME デザインは仮です
* @constructor
*/
export default function ConnectBluesky() {
const { isMutating, trigger, error } = useConnectBsky();

const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(true);
const [successMessage, setSuccessMessage] = useState('');
const handleClickShowPassword = () => setShowPassword((show) => !show);

return (
<Root>
<Link href={'https://bsky.app/settings/app-passwords'}>
アプリパスワードの発行はこちら
</Link>
<OutlinedInput
sx={{ width: '100%' }}
size="small"
name="handle"
autoComplete="off"
disabled={isMutating}
type="text"
onChange={(e) => setUsername(e.target.value)}
placeholder="example.bsky.social"
/>
<OutlinedInput
sx={{ width: '100%' }}
size="small"
disabled={isMutating}
name="app-password"
autoComplete="off"
type={showPassword ? 'text' : 'password'}
onChange={(e) => setPassword(e.target.value)}
placeholder="アプリパスワード"
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={(event) => {
event.preventDefault();
}}
edge="end"
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
}
/>
{isMutating && <LinearProgress />}
{!isMutating && error && <Alert severity="error">{error.message}</Alert>}
<Button
disabled={isMutating}
variant="contained"
sx={{ maxWidth: '200px' }}
onClick={() => {
void trigger({
connectAtProtocol: {
id: username,
appPassword: password,
},
}).then((result) => {
if (result) {
setSuccessMessage('連携しました。');
}
});
}}
>
連携
</Button>
<Snackbar
open={!!successMessage.length}
onClose={() => setSuccessMessage('')}
autoHideDuration={2_000}
>
<Alert severity="success">{successMessage}</Alert>
</Snackbar>
</Root>
);
}
15 changes: 15 additions & 0 deletions src/app/(menu)/(private)/settings/connect-bluesky/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Metadata } from 'next';
import PrimaryColumn from '@/app/(menu)/_components/main/PrimaryColumn';
import ConnectBluesky from '@/app/(menu)/(private)/settings/connect-bluesky/_components/ConnectBluesky';

export const metadata: Metadata = {
title: 'Bluesky連携',
};

export default function page() {
return (
<PrimaryColumn columnName={'Bluesky連携'} showBack={true}>
<ConnectBluesky />
</PrimaryColumn>
);
}
6 changes: 3 additions & 3 deletions src/app/(menu)/(private)/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import ComingSoon from '@/app/(menu)/_components/main/ComingSoon';
import { Metadata } from 'next';
import PrimaryColumn from '@/app/(menu)/_components/main/PrimaryColumn';
import Settings from '@/app/(menu)/(private)/settings/_components/Settings';

export const metadata: Metadata = {
title: '設定',
};

export default function page() {
return (
<PrimaryColumn hideHeader={true}>
<ComingSoon />
<PrimaryColumn columnName={'設定'}>
<Settings />
</PrimaryColumn>
);
}
3 changes: 3 additions & 0 deletions src/libs/cuculus-client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
AccountsApi,
AtProtocolApi,
AuthApi,
Configuration,
DefaultApi,
Expand All @@ -20,6 +21,7 @@ const invitationsApi = new InvitationsApi(config);
const timelinesApi = new TimelinesApi(config);
const postsApi = new PostsApi(config);
const accountsApi = new AccountsApi(config);
const atProtocolApi = new AtProtocolApi(config);

export {
authApi,
Expand All @@ -29,4 +31,5 @@ export {
timelinesApi,
postsApi,
accountsApi,
atProtocolApi,
};
45 changes: 45 additions & 0 deletions src/swr/client/bluesky.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import useSWRMutation from 'swr/mutation';
import { atProtocolApi } from '@/libs/cuculus-client';
import { useAuth } from '@/swr/client/auth';
import { ConnectAtProtocolRequest, ResponseError } from '@cuculus/cuculus-api';
import { getAuthorizationHeader } from '@/libs/auth';

/**
* Blueskyアカウントと連携する
*/
export const useConnectBsky = () => {
const { data: authId } = useAuth();
const key = authId ? { key: 'useConnectBsky', authId } : null;
return useSWRMutation<
boolean,
Error,
{ key: string; authId: number } | null,
ConnectAtProtocolRequest
>(
key,
async (_, { arg: request }) => {
// header上書きで消えてるので手動で設定
const headers = {
...(await getAuthorizationHeader(authId)),
accept: 'application/json',
'Content-Type': 'application/json',
};
try {
await atProtocolApi.connectAtProtocol(request, { headers });
return true;
} catch (error) {
if (error instanceof ResponseError) {
if (error.response.status === 400) {
const body = (await error.response.json()) as { message?: string };
throw new Error(body.message ?? 'エラーが発生しました。');
}
}
throw error;
}
},
{
throwOnError: false,
revalidate: false,
},
);
};

0 comments on commit 50db764

Please sign in to comment.