Skip to content

Commit

Permalink
[πŸ₯ : feat] ν¬μŠ€νŒ… κ²€μƒ‰ν•˜κΈ°
Browse files Browse the repository at this point in the history
- Firebase 쿼리문을 μ΄μš©ν•˜λŠ” 경우 ν•œκ³„κ°€ μ‘΄μž¬ν•΄ ν‚€μ›Œλ“œλ‘œ μ‹œμž‘ν•˜λŠ” λ¬Έμžμ—΄λ§Œ 탐색 κ°€λŠ₯ (ex. "였늘" 검색 => "였늘 뭐해?" (O) "λ­ν•˜λƒ 였늘?" (X)
- νƒ€μž„λΌμΈμ„ μ΄μš©ν•΄ 검색어가 ν¬ν•¨λœ ν¬μŠ€νŒ…λ§Œ λ‚˜μ—΄ν•˜λŠ” search-timeline
  • Loading branch information
sryung1225 committed Dec 14, 2023
1 parent ed4848b commit f9136dd
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 18 deletions.
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -26,6 +27,10 @@ const router = createBrowserRouter([
path: '/profile',
element: <Profile />,
},
{
path: `/search/:searchKeyword`,
element: <SearchResult />,
},
],
},
{
Expand Down
3 changes: 3 additions & 0 deletions src/assets/images/i-search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/atoms.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { atom } from 'recoil';

export const searchKeywordAtom = atom({
key: 'searchKeyword',
default: '',
});

export const tmp = '';
55 changes: 55 additions & 0 deletions src/components/search-timeline.tsx
Original file line number Diff line number Diff line change
@@ -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<ITweet[]>([]);
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 ? (
<S.TimelineWrapper>
{tweets.map((tweet) => (
<Tweet key={tweet.id} {...tweet} />
))}
</S.TimelineWrapper>
) : (
<S.Text>검색 κ²°κ³Όκ°€ μ—†μŠ΅λ‹ˆλ‹€.</S.Text>
);
}
35 changes: 28 additions & 7 deletions src/components/search.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLInputElement>) => {
setSearchKeyword(e.target.value);
setSearchValue(e.target.value);
};
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (searchValue === '') return;
setSearchKeyword(searchValue);
setSearchValue('');
navigate(`/search/searchKeyword=${searchKeyword}`);
};
useEffect(() => {
if (searchKeyword) {
navigate(`/search/searchKeyword=${searchKeyword}`);
}
}, [searchKeyword, navigate]);
return (
<W.Window>
<WindowTop />
<S.Form>
<S.Form onSubmit={onSubmit}>
<S.FormInput
onChange={onChange}
name="searchKeyword"
value={searchKeyword}
placeholder="κ²€μƒ‰ν•˜κΈ°"
value={searchValue}
placeholder="검색어 μž…λ ₯"
type="text"
required
></S.FormInput>
<button type="submit">검색</button>
<S.FormButton type="submit">
<span className="a11yHidden">κ²€μƒ‰ν•˜κΈ°</span>
<IconSearch />
</S.FormButton>
</S.Form>
</W.Window>
);
Expand Down
6 changes: 3 additions & 3 deletions src/components/timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { Unsubscribe } from 'firebase/auth';
import { db } from '../firebase.ts';
import Tweet from './tweet.tsx';
import TimelineWrapper from '../styles/timeline.ts';
import * as S from '../styles/timeline.ts';
import ITweet from '../interfaces/ITweet.ts';

export default function Timeline() {
Expand Down Expand Up @@ -45,10 +45,10 @@ export default function Timeline() {
};
}, []);
return (
<TimelineWrapper>
<S.TimelineWrapper>
{tweets.map((tweet) => (
<Tweet key={tweet.id} {...tweet} />
))}
</TimelineWrapper>
</S.TimelineWrapper>
);
}
8 changes: 4 additions & 4 deletions src/components/user-timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from 'firebase/firestore';
import { auth, db } from '../firebase.ts';
import Tweet from './tweet.tsx';
import TimelineWrapper from '../styles/timeline.ts';
import * as S from '../styles/timeline.ts';
import ITweet from '../interfaces/ITweet.ts';

export default function UserTimeline() {
Expand Down Expand Up @@ -38,12 +38,12 @@ export default function UserTimeline() {
};
useEffect(() => {
fetchTweets();
}, []);
}, [tweets]);
return (
<TimelineWrapper>
<S.TimelineWrapper>
{tweets.map((tweet) => (
<Tweet key={tweet.id} {...tweet} />
))}
</TimelineWrapper>
</S.TimelineWrapper>
);
}
11 changes: 10 additions & 1 deletion src/routes/search-result.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<W.Window>
<WindowTop />
<SearchTimeline />
</W.Window>
);
}
21 changes: 20 additions & 1 deletion src/styles/search.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
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;
width: 100%;
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};
}
`;
7 changes: 5 additions & 2 deletions src/styles/timeline.ts
Original file line number Diff line number Diff line change
@@ -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;
`;

0 comments on commit f9136dd

Please sign in to comment.