Skip to content

Commit

Permalink
[Improve code]: utils/atproto_api/* 手動formatter・Linter対応 (#81)
Browse files Browse the repository at this point in the history
* 🚨 astro/src/utils/atproto_apiに`eslint . --fix`を実行

* 🎨 astro/src/utils/atproto_api/models/*にPrettierを適用

* 🎨 astro/src/utils/atproto_api/*にPrettierを適用

* ✏️ searchの誤字を修正

* 🏷️ imageの$typeを"blob"のみに限定

* 🚨 暫定的に、astro/src/utils/atproto_api以下のレスポンスを、as構文で型付け

* 🎨 astro/src/utils/atproto_api/*.tsにPrettierを適用

* 🏷️ uploadBlobの戻り値の型情報を分割

* [Improve code]: astro/src/components/Client/bsky/* 手動formatter・Linter対応 (#83)

* 🚨 astro/src/components/Client/bsky/にESLintの--fixオプションを適用

* 🔒️ target="_blank"付のタグの脆弱性対策を追加

詳細は下記を確認してください。
https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-target-blank.md

* 🚨 readDrafts()が常にtruthyな値を返すようなので、OR演算子を削除

* 🏷️ atproto_apiの型定義変更に伴い、既存の実装の型情報を更新

* 🚨 Promise<void>を返す関数にvoidキーワードを追加

* 🚨 バッククォートで囲われたダブルクォートのエスケープを解除

* ✏️ identifierの綴りを修正

* 🚨 map()で繰り返し出力されるコンポーネントに、key属性を追加

* 🚨 使用されないエラー変数にアンダースコアを追加

* 🏷️ atproto_apiの型定義変更に伴い、既存の実装の型情報を更新

* 🎨 astro/src/components/Client/bskyにPrettierを適用

* ✏️ `LoginForm`の注意文言に、環境変数の`servicename`を用いるよう変更

* 💬 エラー処理後の型アサーションを解説するコメントを追記

* 🥅 使用されていないエラー変数名をアンダースコアのみに変更

* 🚨 エラー型の判定を`"error" in val`形式で統一

* ♻️ 変数定義と値の代入を凝集化

* 🚨 エラー型の判定を`"error" in val`形式で統一

* 🏷️ 型アサーションを用いない実装に変更

* 🔥 不要になったimport文を削除

* 🎨 astro/src/components/Client/bsky/*にPrettierを適用

* ESLint修正漏れの修正

---------

Co-authored-by: nekono <91360587+nkte8@users.noreply.github.com>
  • Loading branch information
ZEKE320 and nkte8 committed Apr 12, 2024
1 parent 771960b commit 4e6f352
Show file tree
Hide file tree
Showing 46 changed files with 732 additions and 632 deletions.
101 changes: 61 additions & 40 deletions astro/src/components/Client/bsky/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
import { useState, useEffect, useContext, Dispatch, SetStateAction } from "react"
import {
useState,
useEffect,
useContext,
Dispatch,
SetStateAction,
} from "react"
import { inputtext_base, link } from "../common/tailwindVariants"
import { Session_context, Profile_context } from "../common/contexts"
import { type msgInfo } from "../common/types"
import createSession from "@/utils/atproto_api/createSession";
import loadProfile from "./lib/loadProfile";
import createSession from "@/utils/atproto_api/createSession"
import loadProfile from "./lib/loadProfile"

import { writeJwt } from "@/utils/useLocalStorage"
import ProcButton from "../common/ProcButton"
import Tooltip from "../common/Tooltip"
import SavePasswordToggle from "./optionToggles/SavePasswordToggle"
import { readLogininfo, setLogininfo } from "@/utils/useLocalStorage"
import { writeJwt, readLogininfo, setLogininfo } from "@/utils/useLocalStorage"
import { servicename } from "@/env/vars"

export const Component = ({
setMsgInfo,
}: {
setMsgInfo: Dispatch<SetStateAction<msgInfo>>,
setMsgInfo: Dispatch<SetStateAction<msgInfo>>
}) => {
const [loading, setLoad] = useState<boolean>(false)
const [savePassword, setSavePassword] = useState<boolean>(false)
const [identifier, setIdentifer] = useState<string>("")
const [identifier, setIdentifier] = useState<string>("")
const [password, setPassword] = useState<string>("")
const { setSession } = useContext(Session_context)
const { setProfile } = useContext(Profile_context)
Expand All @@ -32,10 +38,10 @@ export const Component = ({
}
const res = await createSession({
identifier: id,
password: pw
password: pw,
})
if (typeof res.error !== "undefined") {
let e: Error = new Error(res.message)
if ("error" in res) {
const e: Error = new Error(res.message)
e.name = res.error
throw e
} else {
Expand All @@ -50,13 +56,13 @@ export const Component = ({
if (savePassword === true) {
setLogininfo({
id: identifier,
pw: password
pw: password,
})
}
// プロフィールを読み込み
await loadProfile({
session: res,
setProfile: setProfile
setProfile: setProfile,
})
}
} catch (error: unknown) {
Expand All @@ -66,7 +72,7 @@ export const Component = ({
}
setMsgInfo({
msg: msg,
isError: true
isError: true,
})
}
setLoad(false)
Expand All @@ -78,77 +84,92 @@ export const Component = ({
msg: "ブラウザに保存されたID/APWでログイン中...",
isError: false,
})
await handleLogin(
loginInfo.id,
loginInfo.pw)
await handleLogin(loginInfo.id, loginInfo.pw)
}
}
useEffect(() => {
handleOnLoad()
void handleOnLoad()
}, [])

return (
<>
<div>
<div className="mt-16">
<div className="align-middle mb-0">
<label className="w-32 inline-block my-auto">
Email or ID:
</label>
<input onChange={(event) => setIdentifer(event.target.value)}
<input
onChange={event => setIdentifier(event.target.value)}
placeholder="example.bsky.social"
disabled={loading}
className={inputtext_base({
class: "max-w-52 w-full px-2",
kind: "outbound",
disabled: loading
})} type="text" />
disabled: loading,
})}
type="text"
/>
</div>
<div className="align-middle">
<label className="w-32 inline-block my-auto">
AppPassword※:
</label>
<input onChange={(event) => setPassword(event.target.value)}
<input
onChange={event => setPassword(event.target.value)}
placeholder="this-isex-ampl-epwd"
disabled={loading}
className={inputtext_base({
class: "max-w-52 w-full px-2",
kind: "outbound",
disabled: loading
})} type="password" />
disabled: loading,
})}
type="password"
/>
</div>
<div className="my-2">
<ProcButton
handler={handleLogin}
isProcessing={loading}
context="Blueskyアカウントへログイン"
disabled={!(identifier.length > 0 && password.length > 0)}
showAnimation={true} />
disabled={
!(identifier.length > 0 && password.length > 0)
}
showAnimation={true}
/>
</div>
<div className="mx-auto w-fit">
<SavePasswordToggle
labeltext={"ID/AppPasswordをブラウザへ保存する"}
prop={savePassword}
setProp={setSavePassword} />
setProp={setSavePassword}
/>
</div>
<Tooltip tooltip={
<div className="flex flex-col sm:flex-row">
<div className="inline-block px-4 py-2 text-left">
(BskyLinXに限らず)非公式のアプリを使う際はAppPasswordの利用が推奨されています。
<a className={link()}
target="_blank"
href="https://bsky.app/settings/app-passwords">
<b>bsky.appの⚙設定</b><b>🔒高度な設定(新規タブが開きます)</b>
</a>から生成してください。
<Tooltip
tooltip={
<div className="flex flex-col sm:flex-row">
<div className="inline-block px-4 py-2 text-left">
{`(${servicename}に限らず)非公式のアプリを使う際はAppPasswordの利用が推奨されています。`}
<a
className={link()}
target="_blank"
href="https://bsky.app/settings/app-passwords"
rel="noopener noreferrer"
>
<b>bsky.appの⚙設定</b>
<b>🔒高度な設定(新規タブが開きます)</b>
</a>
から生成してください。
</div>
</div>
</div>
}>
}
>
<span className="text-sky-400">
※AppPasswordとは?(タップで説明を表示)
</span>
</Tooltip>
</div>
</>
</div>
)
}

export default Component
export default Component
86 changes: 49 additions & 37 deletions astro/src/components/Client/bsky/MediaPreview/AltDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// utils
import React, { memo, useRef, useCallback, useState, useEffect } from "react";
import React, { memo, useRef, useCallback, useState, useEffect } from "react"

// component
import OverlayDialog from "../../common/OverlayDialog"
Expand All @@ -9,50 +9,54 @@ import { inputtext_base, link } from "../../common/tailwindVariants"
import { MediaData } from "../../common/types"

const Img = ({
mediaDataItem
mediaDataItem,
}: {
mediaDataItem: {
alt: string
blob: Blob
}
}) => {
return (
<img src={URL.createObjectURL(mediaDataItem.blob)} className="max-w-md w-full mx-auto" />
<img
src={URL.createObjectURL(mediaDataItem.blob)}
className="max-w-md w-full mx-auto"
/>
)
}
const MemoImg = memo(Img)

export const Component = ({
itemId,
mediaData
mediaData,
}: {
itemId: number,
itemId: number
mediaData: MediaData
}) => {
// mediaDataがimageではない場合はコンポーネントを無効に
if (mediaData === null || mediaData.type !== "images") {
return
}
let mediaDataItem = mediaData.images[itemId]
const textref = useRef<HTMLTextAreaElement | null>(null);
const textref = useRef<HTMLTextAreaElement | null>(null)
const [altBoxText, setAltBoxText] = useState<string>(mediaDataItem.alt)
const maxShowAltLength = 10

const handleOpenDialog = () => {
if (textref.current) {
textref.current.value = mediaDataItem.alt
}
};
}

const handleCloseDialog = () => {
mediaDataItem.alt = altBoxText
};
}

const handleClickInDialog = useCallback(
(e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
e.stopPropagation();
}, []
);
e.stopPropagation()
},
[],
)

const handleOnChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
// メディアがimagesの場合のみ処理
Expand Down Expand Up @@ -81,43 +85,51 @@ export const Component = ({
callbackOpenDialog={handleOpenDialog}
callbackCloseDialog={handleCloseDialog}
buttonOption={{
className: [
"rounded-lg", "border-1",
"bg-opacity-70", "px-2",
"bg-black", "border-white",
"text-white", "absolute",
"left-1", "bottom-1"].join(" ") + (
altBoxText !== "" ? (" bg-blue-500") : ("")
),
className:
[
"rounded-lg",
"border-1",
"bg-opacity-70",
"px-2",
"bg-black",
"border-white",
"text-white",
"absolute",
"left-1",
"bottom-1",
].join(" ") + (altBoxText !== "" ? " bg-blue-500" : ""),
content:
altBoxText !== "" ? (
`${altBoxText.slice(0, maxShowAltLength)
}${altBoxText.length >= maxShowAltLength
? ("...") : ("")}`
) : (
"Altを設定"
)
}}>
<div onClick={handleClickInDialog} >
<MemoImg
mediaDataItem={mediaDataItem} />
<label>画像に設定するAltを入力(ダイアログを閉じると反映)</label>
<textarea onChange={handleOnChange}
altBoxText !== ""
? `${altBoxText.slice(0, maxShowAltLength)}${
altBoxText.length >= maxShowAltLength ? "..." : ""
}`
: "Altを設定",
}}
>
<div onClick={handleClickInDialog}>
<MemoImg mediaDataItem={mediaDataItem} />
<label>
画像に設定するAltを入力(ダイアログを閉じると反映)
</label>
<textarea
onChange={handleOnChange}
autoFocus={true}
ref={textref}
placeholder="上記に設定するAltを入力してください"
className={inputtext_base({
kind: "outbound",
class: "p-4 w-full md:w-lg resize-y overflow-y-auto h-32"
class: "p-4 w-full md:w-lg resize-y overflow-y-auto h-32",
})}
/>
<button
className={link({ enabled: !(altBoxText === "") })}
disabled={altBoxText === ""}
onClick={handleClick}
>内容をクリア</button>
>
内容をクリア
</button>
</div>
</OverlayDialog>
);
};
export default Component
)
}
export default Component
Loading

0 comments on commit 4e6f352

Please sign in to comment.