Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions app/features/search/components/MemoItem.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
@use 'styles/layout' as *;

.memoLink {
display: block;
text-decoration: none;
color: inherit;
}

.memoItem {
background: #fff;
border: 1px solid #eef0f3;
border-radius: 12px;
overflow: hidden;
}

.media {
position: relative;
width: 100%;
height: 180px;
overflow: hidden;
background: #f6f7f9;

@include mobile {
height: 140px;
}
}

.cover {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
align-items: center;
}

.overlay {
position: absolute;
left: 12px;
right: 12px;
top: 12px;
display: flex;
justify-content: flex-start;
align-items: center;
pointer-events: none;
}

.userRow {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 18px 6px 5px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.5);
}

.avatar {
width: 36px;
height: 36px;
border-radius: 999px;
object-fit: cover;
background: #f3f4f6;
flex: 0 0 40px;
border: 2px solid rgba(255, 255, 255, 0.9);

@include mobile {
width: 36px;
height: 36px;
}
}

.userInfo {
display: flex;
flex-direction: column;
line-height: 1;
pointer-events: none;
}

.handle {
font-weight: 700;
color: #0f172a;
font-size: 15px;
}

.timeShort {
font-size: 12px;
color: #6b7280;
}

.timeTop {
pointer-events: none;
display: flex;
align-items: center;
min-width: 0;
color: #6b7280;
font-size: 12px;
background: rgba(255, 255, 255, 0.6);
padding: 6px 8px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(2, 6, 23, 0.04);
}

.body {
padding: 12px 14px 16px;
}

.songMeta {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 12px;
}

.titleBlock {
min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}

.timeInBody {
color: #6b7280;
font-size: 12px;
white-space: nowrap;
pointer-events: none;
margin-left: 8px;
flex: 0 0 auto;
}

.songTitle {
font-weight: 700;
color: #0f172a;
font-size: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;

@include mobile {
font-size: 15px;
}
}

.artist {
font-size: 13px;
color: #6b7280;
}

.content {
margin-top: 10px;
color: #374151;
font-size: 14px;
line-height: 1.4;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;

@include mobile {
-webkit-line-clamp: 3;
line-clamp: 3;
}
}
48 changes: 48 additions & 0 deletions app/features/search/components/MemoItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Link } from '@remix-run/react';

import type { UserMusicMemoWithRelations } from 'app/features/search/services';

import styles from './MemoItem.module.scss';

const DEFAULT_AVATAR = '/images/features/profile/profile_default.png';
const DEFAULT_COVER = '/images/features/profile/album_default2.png';

export default function MemoItem({ memo }: { memo: UserMusicMemoWithRelations }) {
const cover = memo.song?.thumbnailUrl ?? DEFAULT_COVER;
const avatar = memo.user?.avatarUrl ?? DEFAULT_AVATAR;
const excerpt = memo.content.length > 120 ? `${memo.content.slice(0, 120)}…` : memo.content;
const createdISO = new Date(memo.createdAt).toISOString();
const created = new Date(memo.createdAt).toLocaleString();

return (
<Link to={`/music/${memo.songId}/user/${memo.user.id}`} className={styles.memoLink} aria-label="메모 보기">
<article className={styles.memoItem}>
<header className={styles.media}>
<img src={cover} alt={memo.song?.title ?? '앨범 커버'} className={styles.cover} />
<div className={styles.overlay}>
<div className={styles.userRow}>
<img className={styles.avatar} src={avatar} alt={memo.user?.handle ?? 'user'} />
<div className={styles.userInfo}>
<span className={styles.handle}>@{memo.user?.handle}</span>
</div>
</div>
</div>
</header>

<div className={styles.body}>
<div className={styles.songMeta}>
<div className={styles.titleBlock}>
<div className={styles.songTitle}>{memo.song?.title ?? '제목 없음'}</div>
<div className={styles.artist}>{memo.song?.artist}</div>
</div>
<div className={styles.timeInBody}>
<time dateTime={createdISO}>{created}</time>
</div>
</div>

{excerpt ? <p className={styles.content}>{excerpt}</p> : null}
</div>
</article>
</Link>
);
}
12 changes: 7 additions & 5 deletions app/features/search/loader.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import createLoader from 'app/utils/createLoader';

import { searchSongInputLoader } from './components/SearchSongInputloader';
import { findUserByHandleSim } from './services';
import { findMemosByContent, findUserByHandleSim } from './services';

export const searchLoader = createLoader(async ({ request, db }) => {
const url = new URL(request.url);
const query = url.searchParams.get('search') ?? '';
const type = url.searchParams.get('tab') ?? 'music';
const tab = url.searchParams.get('tab') ?? 'music';

const results = await (async () => {
if (type === 'music') {
if (tab === 'music') {
return await searchSongInputLoader({ searchQuery: query });
}
if (query) {
} else if (tab === 'handle') {
return await findUserByHandleSim(db)({ handle: query });
} else if (tab === 'memo') {
return await findMemosByContent(db)({ content: query });
}

return null;
})();

Expand Down
4 changes: 2 additions & 2 deletions app/features/search/pages/search.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
gap: 12px;
padding: 0;
margin: 0;
list-style: none;
}

.empty {
Expand Down Expand Up @@ -198,12 +199,11 @@
}

.profileGrid {
list-style: none;
display: grid;
gap: 8px;
width: 100%;
margin: 0;
grid-auto-rows: 1fr;
/* reset ul default inset so items align with content */
padding: 0;
list-style: none;
}
Loading