Files
log/oauth/src/components/ProfileRecordList.jsx
2025-06-25 20:27:43 +09:00

133 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react'
import { collections } from '../api/atproto.js'
import AvatarImage from './AvatarImage.jsx'
import LoadingSkeleton from './LoadingSkeleton.jsx'
const ProfileRecordList = ({ apiConfig, user, agent, onRecordDeleted }) => {
const [profiles, setProfiles] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
if (apiConfig?.admin && apiConfig?.collection) {
fetchProfiles()
}
}, [apiConfig])
const fetchProfiles = async () => {
try {
setLoading(true)
setError(null)
const adminProfiles = await collections.getProfiles(
apiConfig.pds,
apiConfig.admin,
apiConfig.collection
)
// Sort profiles: admin type first, then user type
const sortedProfiles = adminProfiles.sort((a, b) => {
if (a.value.type === 'admin' && b.value.type !== 'admin') return -1
if (a.value.type !== 'admin' && b.value.type === 'admin') return 1
return 0
})
setProfiles(sortedProfiles)
} catch (err) {
console.error('Failed to fetch profiles:', err)
setError('プロフィールの読み込みに失敗しました')
} finally {
setLoading(false)
}
}
const handleDelete = async (uri) => {
if (!user || !agent) return
if (!confirm('このプロフィールを削除しますか?')) return
try {
const rkey = uri.split('/').pop()
await agent.api.com.atproto.repo.deleteRecord({
repo: user.did,
collection: `${apiConfig.collection}.profile`,
rkey: rkey
})
// Invalidate cache and refresh
collections.invalidateCache(`${apiConfig.collection}.profile`)
await fetchProfiles()
if (onRecordDeleted) {
onRecordDeleted()
}
} catch (err) {
console.error('Failed to delete profile:', err)
setError('プロフィールの削除に失敗しました')
}
}
if (loading) {
return <LoadingSkeleton count={3} showTitle={true} />
}
if (error) {
return (
<div className="error-state">
<p>{error}</p>
<button onClick={fetchProfiles} className="retry-btn">再試行</button>
</div>
)
}
if (profiles.length === 0) {
return (
<div className="empty-state">
<p>プロフィールがありません</p>
</div>
)
}
return (
<div className="record-list profile-record-list">
{profiles.map((profile) => (
<div key={profile.uri} className={`record-item comment-style ${profile.value.type}`}>
<div className="message-header">
<div className="avatar">
<AvatarImage
src={profile.value.author.avatar}
alt={profile.value.author.displayName || profile.value.author.handle}
size={40}
/>
</div>
<div className="user-info">
<div className="display-name">
{profile.value.author.displayName || profile.value.author.handle}
{profile.value.type === 'admin' && (
<span className="admin-badge">Admin</span>
)}
</div>
<div className="handle">@{profile.value.author.handle}</div>
<div className="timestamp">
{new Date(profile.value.createdAt).toLocaleString()}
</div>
</div>
{user && (
<div className="record-actions">
<button
onClick={() => handleDelete(profile.uri)}
className="delete-btn"
title="削除"
>
×
</button>
</div>
)}
</div>
<div className="message-content">{profile.value.text}</div>
</div>
))}
</div>
)
}
export default ProfileRecordList