diff --git a/src/App.tsx b/src/App.tsx
index e2d0a5b..8bc3121 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -4,6 +4,7 @@ import { auth } from './firebase.ts';
import ProtectedRoute from './components/protected-route.tsx';
import Home from './routes/home.tsx';
import Profile from './routes/profile.tsx';
+import SearchResult from './routes/search-result.tsx';
import Auth from './routes/auth.tsx';
import Layout from './components/layout.tsx';
import LoadingScreen from './components/loading-screen.tsx';
@@ -26,6 +27,10 @@ const router = createBrowserRouter([
path: '/profile',
element: ,
},
+ {
+ path: `/search/:searchKeyword`,
+ element: ,
+ },
],
},
{
diff --git a/src/assets/images/i-search.svg b/src/assets/images/i-search.svg
new file mode 100644
index 0000000..d7cafa1
--- /dev/null
+++ b/src/assets/images/i-search.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/atoms.tsx b/src/atoms.tsx
new file mode 100644
index 0000000..d34eab9
--- /dev/null
+++ b/src/atoms.tsx
@@ -0,0 +1,8 @@
+import { atom } from 'recoil';
+
+export const searchKeywordAtom = atom({
+ key: 'searchKeyword',
+ default: '',
+});
+
+export const tmp = '';
diff --git a/src/components/search-timeline.tsx b/src/components/search-timeline.tsx
new file mode 100644
index 0000000..d88f4d4
--- /dev/null
+++ b/src/components/search-timeline.tsx
@@ -0,0 +1,55 @@
+import { useEffect, useState } from 'react';
+import {
+ collection,
+ getDocs,
+ limit,
+ orderBy,
+ query,
+ where,
+} from 'firebase/firestore';
+import { useRecoilValue } from 'recoil';
+import { db } from '../firebase.ts';
+import { searchKeywordAtom } from '../atoms.tsx';
+import Tweet from './tweet.tsx';
+import * as S from '../styles/timeline.ts';
+import ITweet from '../interfaces/ITweet.ts';
+
+export default function SearchTimeline() {
+ const [tweets, setTweets] = useState([]);
+ const searchKeyword = useRecoilValue(searchKeywordAtom);
+ const fetchTweets = async () => {
+ const tweetsQuery = query(
+ collection(db, 'tweets'),
+ orderBy('tweet'),
+ where('tweet', '>=', searchKeyword),
+ where('tweet', '<=', `${searchKeyword}\uf8ff`),
+ limit(25),
+ );
+ const snapshot = await getDocs(tweetsQuery);
+ const tweetList = snapshot.docs.map((doc) => {
+ const { userId, userName, tweet, createdAt, photo } = doc.data();
+ return {
+ userId,
+ userName,
+ tweet,
+ createdAt,
+ photo,
+ id: doc.id,
+ };
+ });
+ tweetList.sort((a, b) => b.createdAt - a.createdAt);
+ setTweets(tweetList);
+ };
+ useEffect(() => {
+ fetchTweets();
+ }, [searchKeyword]);
+ return tweets.length !== 0 ? (
+
+ {tweets.map((tweet) => (
+
+ ))}
+
+ ) : (
+ 검색 결과가 없습니다.
+ );
+}
diff --git a/src/components/search.tsx b/src/components/search.tsx
index 865e07e..b68f610 100644
--- a/src/components/search.tsx
+++ b/src/components/search.tsx
@@ -1,26 +1,47 @@
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
+import { useRecoilState } from 'recoil';
+import { useNavigate } from 'react-router-dom';
+import { searchKeywordAtom } from '../atoms.tsx';
import WindowTop from './window-top.tsx';
import * as W from '../styles/window.ts';
import * as S from '../styles/search.ts';
+import { ReactComponent as IconSearch } from '../assets/images/i-search.svg';
export default function Search() {
- const [searchKeyword, setSearchKeyword] = useState('');
+ const navigate = useNavigate();
+ const [searchValue, setSearchValue] = useState('');
+ const [searchKeyword, setSearchKeyword] = useRecoilState(searchKeywordAtom);
const onChange = (e: React.ChangeEvent) => {
- setSearchKeyword(e.target.value);
+ setSearchValue(e.target.value);
};
+ const onSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (searchValue === '') return;
+ setSearchKeyword(searchValue);
+ setSearchValue('');
+ navigate(`/search/searchKeyword=${searchKeyword}`);
+ };
+ useEffect(() => {
+ if (searchKeyword) {
+ navigate(`/search/searchKeyword=${searchKeyword}`);
+ }
+ }, [searchKeyword, navigate]);
return (
-
+
-
+
+ 검색하기
+
+
);
diff --git a/src/routes/search-result.tsx b/src/routes/search-result.tsx
index 78cfb5b..0c856b5 100644
--- a/src/routes/search-result.tsx
+++ b/src/routes/search-result.tsx
@@ -1,3 +1,12 @@
+import SearchTimeline from '../components/search-timeline.tsx';
+import WindowTop from '../components/window-top.tsx';
+import * as W from '../styles/window.ts';
+
export default function SearchResult() {
- return null;
+ return (
+
+
+
+
+ );
}
diff --git a/src/styles/search.ts b/src/styles/search.ts
index b173ac1..db3d287 100644
--- a/src/styles/search.ts
+++ b/src/styles/search.ts
@@ -1,7 +1,9 @@
import { styled } from 'styled-components';
import { Input } from './button.ts';
+import { grayColor } from './global.ts';
export const Form = styled.form`
+ position: relative;
display: flex;
flex-direction: column;
gap: 10px;
@@ -9,4 +11,21 @@ export const Form = styled.form`
max-width: 500px;
`;
-export const FormInput = styled(Input)``;
+export const FormInput = styled(Input)`
+ padding-right: 50px;
+`;
+
+export const FormButton = styled.button`
+ position: absolute;
+ top: 3px;
+ right: 3px;
+ bottom: 3px;
+ height: 37px;
+ width: 37px;
+ background-color: transparent;
+ border-radius: 50%;
+ border: none;
+ svg {
+ stroke: ${grayColor};
+ }
+`;
diff --git a/src/styles/timeline.ts b/src/styles/timeline.ts
index 241ad4b..5ecd51b 100644
--- a/src/styles/timeline.ts
+++ b/src/styles/timeline.ts
@@ -1,8 +1,11 @@
import styled from 'styled-components';
-const TimelineWrapper = styled.ul`
+export const TimelineWrapper = styled.ul`
display: block;
overflow-y: auto;
`;
-export default TimelineWrapper;
+export const Text = styled.p`
+ margin: 20px 0;
+ text-align: center;
+`;