Some fixes
This commit is contained in:
		@@ -1,5 +1,5 @@
 | 
			
		||||
import { Link, Route, Routes, useLocation } from "react-router-dom";
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
import { Link, Route, Routes, useLocation } from "react-router-dom";
 | 
			
		||||
import EpisodeDetail from "./components/EpisodeDetail";
 | 
			
		||||
import EpisodeList from "./components/EpisodeList";
 | 
			
		||||
import FeedDetail from "./components/FeedDetail";
 | 
			
		||||
@@ -10,13 +10,16 @@ import RSSEndpoints from "./components/RSSEndpoints";
 | 
			
		||||
function App() {
 | 
			
		||||
  const location = useLocation();
 | 
			
		||||
  const [isDarkMode, setIsDarkMode] = useState(() => {
 | 
			
		||||
    const saved = localStorage.getItem('darkMode');
 | 
			
		||||
    const saved = localStorage.getItem("darkMode");
 | 
			
		||||
    return saved ? JSON.parse(saved) : false;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    document.documentElement.setAttribute('data-theme', isDarkMode ? 'dark' : 'light');
 | 
			
		||||
    localStorage.setItem('darkMode', JSON.stringify(isDarkMode));
 | 
			
		||||
    document.documentElement.setAttribute(
 | 
			
		||||
      "data-theme",
 | 
			
		||||
      isDarkMode ? "dark" : "light",
 | 
			
		||||
    );
 | 
			
		||||
    localStorage.setItem("darkMode", JSON.stringify(isDarkMode));
 | 
			
		||||
  }, [isDarkMode]);
 | 
			
		||||
 | 
			
		||||
  const toggleDarkMode = () => {
 | 
			
		||||
@@ -41,12 +44,12 @@ function App() {
 | 
			
		||||
                  RSS フィードから自動生成された音声ポッドキャスト
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <button 
 | 
			
		||||
              <button
 | 
			
		||||
                className="theme-toggle"
 | 
			
		||||
                onClick={toggleDarkMode}
 | 
			
		||||
                aria-label="テーマを切り替え"
 | 
			
		||||
              >
 | 
			
		||||
                {isDarkMode ? '☀️' : '🌙'}
 | 
			
		||||
                {isDarkMode ? "☀️" : "🌙"}
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ function EpisodeList() {
 | 
			
		||||
      filterEpisodesByCategory();
 | 
			
		||||
    }
 | 
			
		||||
  }, [episodes, selectedCategory, searchQuery]);
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  // Reset to page 1 when category changes (but don't trigger if already on page 1)
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (currentPage !== 1) {
 | 
			
		||||
@@ -85,28 +85,30 @@ function EpisodeList() {
 | 
			
		||||
          page: currentPage.toString(),
 | 
			
		||||
          limit: pageSize.toString(),
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if (selectedCategory) {
 | 
			
		||||
          searchParams.append("category", selectedCategory);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        const response = await fetch(`/api/episodes-with-feed-info?${searchParams}`);
 | 
			
		||||
 | 
			
		||||
        const response = await fetch(
 | 
			
		||||
          `/api/episodes-with-feed-info?${searchParams}`,
 | 
			
		||||
        );
 | 
			
		||||
        if (!response.ok) {
 | 
			
		||||
          throw new Error("データベースからの取得に失敗しました");
 | 
			
		||||
        }
 | 
			
		||||
        const data = await response.json();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // Handle paginated response
 | 
			
		||||
        if (data.episodes !== undefined) {
 | 
			
		||||
          const dbEpisodes = data.episodes || [];
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          if (dbEpisodes.length === 0 && data.total === 0) {
 | 
			
		||||
            // Database is empty, fallback to XML
 | 
			
		||||
            console.log("Database is empty, falling back to XML parsing...");
 | 
			
		||||
            setUseDatabase(false);
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          setEpisodes(dbEpisodes);
 | 
			
		||||
          setTotalEpisodes(data.total || 0);
 | 
			
		||||
          setTotalPages(data.totalPages || 1);
 | 
			
		||||
@@ -402,9 +404,7 @@ function EpisodeList() {
 | 
			
		||||
              ))}
 | 
			
		||||
            </select>
 | 
			
		||||
          )}
 | 
			
		||||
          {isSearching && (
 | 
			
		||||
            <span className="episode-meta-text">検索中...</span>
 | 
			
		||||
          )}
 | 
			
		||||
          {isSearching && <span className="episode-meta-text">検索中...</span>}
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -527,7 +527,7 @@ function EpisodeList() {
 | 
			
		||||
          ))}
 | 
			
		||||
        </tbody>
 | 
			
		||||
      </table>
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      {/* Pagination Controls - only show for database mode */}
 | 
			
		||||
      {useDatabase && totalPages > 1 && (
 | 
			
		||||
        <div className="pagination-container">
 | 
			
		||||
@@ -538,7 +538,7 @@ function EpisodeList() {
 | 
			
		||||
          >
 | 
			
		||||
            前へ
 | 
			
		||||
          </button>
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          {/* Page numbers */}
 | 
			
		||||
          <div className="pagination-pages">
 | 
			
		||||
            {/* First page */}
 | 
			
		||||
@@ -553,13 +553,13 @@ function EpisodeList() {
 | 
			
		||||
                {currentPage > 4 && <span>...</span>}
 | 
			
		||||
              </>
 | 
			
		||||
            )}
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            {/* Current page and nearby pages */}
 | 
			
		||||
            {Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
 | 
			
		||||
              const pageNum = Math.max(1, currentPage - 2) + i;
 | 
			
		||||
              if (pageNum > totalPages) return null;
 | 
			
		||||
              if (pageNum < Math.max(1, currentPage - 2)) return null;
 | 
			
		||||
              
 | 
			
		||||
 | 
			
		||||
              return (
 | 
			
		||||
                <button
 | 
			
		||||
                  key={pageNum}
 | 
			
		||||
@@ -570,7 +570,7 @@ function EpisodeList() {
 | 
			
		||||
                </button>
 | 
			
		||||
              );
 | 
			
		||||
            })}
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            {/* Last page */}
 | 
			
		||||
            {currentPage < totalPages - 2 && (
 | 
			
		||||
              <>
 | 
			
		||||
@@ -584,15 +584,17 @@ function EpisodeList() {
 | 
			
		||||
              </>
 | 
			
		||||
            )}
 | 
			
		||||
          </div>
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          <button
 | 
			
		||||
            className="btn btn-secondary"
 | 
			
		||||
            onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))}
 | 
			
		||||
            onClick={() =>
 | 
			
		||||
              setCurrentPage(Math.min(totalPages, currentPage + 1))
 | 
			
		||||
            }
 | 
			
		||||
            disabled={currentPage === totalPages}
 | 
			
		||||
          >
 | 
			
		||||
            次へ
 | 
			
		||||
          </button>
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          {/* Page size selector */}
 | 
			
		||||
          <div className="pagination-size-selector">
 | 
			
		||||
            <span style={{ fontSize: "14px" }}>表示件数:</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -222,7 +222,10 @@ function FeedDetail() {
 | 
			
		||||
                    <strong>
 | 
			
		||||
                      <Link
 | 
			
		||||
                        to={`/episode/${episode.id}`}
 | 
			
		||||
                        style={{ textDecoration: "none", color: "var(--accent-primary)" }}
 | 
			
		||||
                        style={{
 | 
			
		||||
                          textDecoration: "none",
 | 
			
		||||
                          color: "var(--accent-primary)",
 | 
			
		||||
                        }}
 | 
			
		||||
                      >
 | 
			
		||||
                        {episode.title}
 | 
			
		||||
                      </Link>
 | 
			
		||||
@@ -242,7 +245,10 @@ function FeedDetail() {
 | 
			
		||||
                      href={episode.articleLink}
 | 
			
		||||
                      target="_blank"
 | 
			
		||||
                      rel="noopener noreferrer"
 | 
			
		||||
                      style={{ fontSize: "12px", color: "var(--text-secondary)" }}
 | 
			
		||||
                      style={{
 | 
			
		||||
                        fontSize: "12px",
 | 
			
		||||
                        color: "var(--text-secondary)",
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                      元記事を見る
 | 
			
		||||
                    </a>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ function FeedList() {
 | 
			
		||||
  const [selectedCategory, setSelectedCategory] = useState<string>("");
 | 
			
		||||
  const [loading, setLoading] = useState(true);
 | 
			
		||||
  const [error, setError] = useState<string | null>(null);
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  // Pagination state
 | 
			
		||||
  const [currentPage, setCurrentPage] = useState(1);
 | 
			
		||||
  const [pageSize, setPageSize] = useState(15);
 | 
			
		||||
@@ -46,7 +46,7 @@ function FeedList() {
 | 
			
		||||
    try {
 | 
			
		||||
      setLoading(true);
 | 
			
		||||
      setError(null);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // Build query parameters
 | 
			
		||||
      const params = new URLSearchParams();
 | 
			
		||||
      params.append("page", currentPage.toString());
 | 
			
		||||
@@ -54,17 +54,17 @@ function FeedList() {
 | 
			
		||||
      if (selectedCategory) {
 | 
			
		||||
        params.append("category", selectedCategory);
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      const response = await fetch(`/api/feeds?${params.toString()}`);
 | 
			
		||||
      if (!response.ok) {
 | 
			
		||||
        const errorData = await response.json();
 | 
			
		||||
        throw new Error(errorData.error || "フィードの取得に失敗しました");
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      const data = await response.json();
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // Handle paginated response
 | 
			
		||||
      if (data.feeds && typeof data.total !== 'undefined') {
 | 
			
		||||
      if (data.feeds && typeof data.total !== "undefined") {
 | 
			
		||||
        setFeeds(data.feeds);
 | 
			
		||||
        setFilteredFeeds(data.feeds);
 | 
			
		||||
        setTotalFeeds(data.total);
 | 
			
		||||
@@ -96,7 +96,6 @@ function FeedList() {
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  const formatDate = (dateString: string) => {
 | 
			
		||||
    return new Date(dateString).toLocaleString("ja-JP");
 | 
			
		||||
  };
 | 
			
		||||
@@ -197,7 +196,11 @@ function FeedList() {
 | 
			
		||||
            <option value={45}>45件表示</option>
 | 
			
		||||
            <option value={60}>60件表示</option>
 | 
			
		||||
          </select>
 | 
			
		||||
          <button type="button" className="btn btn-secondary" onClick={fetchFeeds}>
 | 
			
		||||
          <button
 | 
			
		||||
            type="button"
 | 
			
		||||
            className="btn btn-secondary"
 | 
			
		||||
            onClick={fetchFeeds}
 | 
			
		||||
          >
 | 
			
		||||
            更新
 | 
			
		||||
          </button>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -268,7 +271,7 @@ function FeedList() {
 | 
			
		||||
            >
 | 
			
		||||
              前へ
 | 
			
		||||
            </button>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            {/* Page numbers */}
 | 
			
		||||
            <div className="page-numbers">
 | 
			
		||||
              {Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
 | 
			
		||||
@@ -282,12 +285,12 @@ function FeedList() {
 | 
			
		||||
                } else {
 | 
			
		||||
                  pageNum = currentPage - 2 + i;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                return (
 | 
			
		||||
                  <button
 | 
			
		||||
                    type="button"
 | 
			
		||||
                    key={pageNum}
 | 
			
		||||
                    className={`btn ${currentPage === pageNum ? 'btn-primary' : 'btn-secondary'}`}
 | 
			
		||||
                    className={`btn ${currentPage === pageNum ? "btn-primary" : "btn-secondary"}`}
 | 
			
		||||
                    onClick={() => setCurrentPage(pageNum)}
 | 
			
		||||
                  >
 | 
			
		||||
                    {pageNum}
 | 
			
		||||
@@ -295,7 +298,7 @@ function FeedList() {
 | 
			
		||||
                );
 | 
			
		||||
              })}
 | 
			
		||||
            </div>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <button
 | 
			
		||||
              type="button"
 | 
			
		||||
              className="btn btn-secondary"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user