Skip to content

Commit

Permalink
private file search
Browse files Browse the repository at this point in the history
  • Loading branch information
johan-lindell committed Sep 15, 2024
1 parent 03a4eda commit 9462182
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 6,899 deletions.
13 changes: 11 additions & 2 deletions components/header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Navbar from './navbar'
import SideMenu from './navbar/SideMenu'
import { NavbarLink } from '@lib/strapi/navbar'
import ExpandableNavbar from './navbar/ExpandableNavbar'
import SearchOverlay from './navbar/searchpage'

const Header = ({
navbarLinks,
Expand All @@ -14,6 +15,7 @@ const Header = ({
sessionToken?: string
}) => {
const [sideMenuOpen, setSideMenuOpen] = useState(false)
const [searchOpen, setSearchOpen] = useState(false)

return (
<header className="sticky top-0 z-10">
Expand All @@ -25,17 +27,24 @@ const Header = ({
<Navbar
navbarLinks={navbarLinks}
setSideMenuOpen={setSideMenuOpen}
sessionToken={sessionToken}
setSearchOpen={() => setSearchOpen(true)}
/>
<SideMenu open={sideMenuOpen}>
<Navbar
navbarLinks={navbarLinks}
position="side"
setSideMenuOpen={setSideMenuOpen}
sessionToken={sessionToken}
setSearchOpen={() => setSearchOpen(true)}
/>
</SideMenu>
</div>
{searchOpen && (
<SearchOverlay
sessionToken={sessionToken}
setSideMenuOpen={setSideMenuOpen}
onClose={() => setSearchOpen(false)}
/>
)}
</header>
)
}
Expand Down
17 changes: 4 additions & 13 deletions components/header/navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,21 @@ import { usePathname } from 'next/navigation'
import LoginButton from './LoginButton'
import HeaderLink from '@components/header/navbar/HeaderLink'
import { MdSearch } from 'react-icons/md'
import SearchOverlay from './searchpage/SearchOverlay'

type NavbarProps = {
navbarLinks: NavbarLink[]
position?: 'top' | 'side'
setSideMenuOpen: (state: boolean) => void
sessionToken?: string
setSearchOpen: () => void
}

const Navbar = ({
navbarLinks,
setSideMenuOpen,
position = 'top',
sessionToken,
setSearchOpen,
}: NavbarProps) => {
const path = usePathname()
const [searchOpen, setSearchOpen] = useState(false)

return (
<nav>
Expand Down Expand Up @@ -99,19 +97,12 @@ const Navbar = ({
</div>

<Row className={position === 'side' ? 'mt-6' : ''}>
<button onClick={() => setSearchOpen(!searchOpen)}>
<button onClick={setSearchOpen}>
<MdSearch color="white" size={32} />
</button>
<LoginButton />
</Row>
</div>
{searchOpen && (
<SearchOverlay
sessionToken={sessionToken}
setSideMenuOpen={setSideMenuOpen}
onClose={() => setSearchOpen(false)}
/>
)}
</nav>
)
}
Expand Down Expand Up @@ -143,7 +134,7 @@ const NavbarDropdown = ({
<div
className={classNames(
isTop ? 'peer' : '!m-0',
pathWithoutAnchor?.startsWith(`/${link.basePath}` ?? '') &&
pathWithoutAnchor?.startsWith(`/${link.basePath}`) &&
link.links.find((l) => l.link === pathWithoutAnchor) &&
'!text-teknologröd',
'link-text text-link'
Expand Down
29 changes: 29 additions & 0 deletions components/header/navbar/searchpage/FileCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react'
import Image from 'next/image'

type FileCardProps = {
id: string
name: string
thumbnailLink?: string | null
}
const FileCard = ({ id, name, thumbnailLink }: FileCardProps) => {
return (
<a
className="flex items-center p-2 border-b border-lightGray border-opacity-50 hover:bg-lightGray hover:bg-opacity-10"
href={`/api/drive/private/download?fileId=${id}&fileName=${name}`}
>
{thumbnailLink && (
<Image
src={thumbnailLink}
alt={name}
width="100"
height="100"
style={{ width: 130, height: 'auto' }}
/>
)}
<p className="text-white ml-2">{name}</p>
</a>
)
}

export default FileCard
31 changes: 10 additions & 21 deletions components/header/navbar/searchpage/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,31 @@
import ActivityIndicator from '@components/ActivityIndicator'
import React, { useEffect } from 'react'
import { SearchData, searchPublic } from '@lib/strapi/search'
import { MdCancel, MdSearch } from 'react-icons/md'

type SearchBarProps = {
query: string
setResults: (results: SearchData) => void
handleSearch: () => void
setQuery: (query: string) => void
clearResults: () => void
sessionToken?: string
searching: boolean
}

const SearchBar = ({
query,
setResults,
handleSearch,
setQuery,
sessionToken,
clearResults,
searching,
}: SearchBarProps) => {
useEffect(() => {
const delayDebounceFn = setTimeout(() => {
if (query) {
handleSearch()
}
}, 500) // 500ms delay

return () => clearTimeout(delayDebounceFn)
}, [query])
const handleSearch = async () => {
try {
const res = await searchPublic(query, sessionToken)
console.log(res)
setResults({
sectionData: res.sectionData,
pageData: res.pageData,
privateSectionData: res.privateSectionData,
privatePageData: res.privatePageData,
})
} catch (error) {
console.error(error)
}
}

return (
<div className="flex justify-center items-end w-full content-end">
Expand All @@ -50,8 +36,6 @@ const SearchBar = ({
onChange={(e) => setQuery(e.target.value)}
placeholder="Sök..."
className="w-full pl-10 pr-6 py-3 border border-gray-300 rounded-xl shadow-sm focus:outline-none focus:ring-2"
//onFocus={() => setIsFocused(true)}
//onBlur={() => setIsFocused(false)}
/>
<MdSearch
size={30}
Expand All @@ -67,6 +51,11 @@ const SearchBar = ({
size={30}
className="absolute right-3 top-4 h-5 w-5 text-gray-400"
/>
{searching && (
<div className="absolute right-9 top-3.5">
<ActivityIndicator width={25} height={25} stroke="black" />
</div>
)}
</button>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useState, useEffect } from 'react'

import Column from '@components/Column'
import { SearchData } from '@lib/strapi/search'
import { SearchData, searchDrive, searchPublic } from '@lib/strapi/search'
import { titleToAnchor } from '@utils/helpers'
import ListCard from './ListCard'
import SearchBar from './SearchBar'
import { MdCancel } from 'react-icons/md'
import { drive_v3 } from 'googleapis'
import FileCard from './FileCard'

type SearchOverlayProps = {
onClose: () => void
Expand All @@ -19,6 +20,9 @@ const SearchOverlay = ({
setSideMenuOpen,
}: SearchOverlayProps) => {
const [query, setQuery] = useState('')
const [fileSearch, setFileSearch] = useState(false)
const [searching, setSearching] = useState(false)
const [fileResults, setFileResults] = useState<drive_v3.Schema$File[]>([])
const [results, setResults] = useState<SearchData>({
sectionData: [],
pageData: [],
Expand All @@ -33,34 +37,101 @@ const SearchOverlay = ({
privateSectionData: [],
privatePageData: [],
})
setFileResults([])
}

// Add class to body to disable scrolling on main page
const handleSearch = async () => {
//do not search if query is less than 2 characters
if (query.length < 2) return
try {
setSearching(true)
if (fileSearch) {
const resDrive = await searchDrive(query)
setFileResults(resDrive || [])
setSearching(false)
} else {
const res = await searchPublic(query, sessionToken)
setResults({
sectionData: res.sectionData,
pageData: res.pageData,
privateSectionData: res.privateSectionData,
privatePageData: res.privatePageData,
})
setSearching(false)
}
} catch (error) {
setSearching(false)
console.error(error)
}
}

// Add class to body to disable interaction with main page
useEffect(() => {
document.body.classList.add('overflow-hidden')
document.body.style.pointerEvents = 'none'
return () => {
document.body.classList.remove('overflow-hidden')
document.body.style.pointerEvents = 'auto'
}
}, [])

//search with new param when search content changes
useEffect(() => {
handleSearch()
}, [fileSearch])

const contentReturned =
results.pageData.length > 0 ||
results.sectionData.length > 0 ||
results.privatePageData.length > 0 ||
results.privateSectionData.length > 0

const filesReturned = fileResults.length > 0

console.log(results)
return (
<div className="fixed inset-0 bg-gray-800 bg-opacity-80 flex items-center justify-center z-50 bg-black p-10">
<div className="z-50 fixed inset-0 bg-gray-800 bg-opacity-90 flex items-center justify-center bg-darkgray md:p-10 p-5 pt-12 pointer-events-auto">
<Column className="w-full h-full top-0 overflow-hidden">
<button onClick={onClose} className="absolute top-2 right-2 text-xl">
<MdCancel size={30} color="white" />
</button>
<SearchBar
sessionToken={sessionToken}
setQuery={setQuery}
setResults={setResults}
query={query}
clearResults={clearResults}
/>
<div className="mt-5 overflow-y-auto h-full p-4">
{(results.pageData.length > 0 ||
results.sectionData.length > 0 ||
results.privatePageData.length > 0 ||
results.privateSectionData.length > 0) && (
<div className="w-full items-start">
<SearchBar
sessionToken={sessionToken}
setQuery={setQuery}
handleSearch={handleSearch}
query={query}
clearResults={clearResults}
searching={searching}
/>
{sessionToken && (
<div className="flex text-white mt-4">
<button
className={`px-4 py-2 rounded-md ${!fileSearch ? 'bg-teknologröd' : 'bg-gray-500'}`}
onClick={() => setFileSearch(false)}
>
Sök nätsidan
</button>
<button
className={`px-4 py-2 rounded-md ${fileSearch ? 'bg-teknologröd' : 'bg-gray-500'}`}
onClick={() => setFileSearch(true)}
>
Sök filer
</button>
</div>
)}
{query.length < 2 &&
((!contentReturned && !fileSearch) ||
(!filesReturned && fileSearch)) &&
!searching && (
<p className="text-white mt-4 left-0 text-opacity-70">
Sök med minst två tecken...
</p>
)}
</div>

<div className="mt-3 overflow-y-auto h-full">
{!fileSearch && (
<div>
{results.pageData.map((page) => (
<ListCard
Expand Down Expand Up @@ -112,6 +183,22 @@ const SearchOverlay = ({
)}
</div>
)}
{fileSearch && (
<div className="overflow-y-auto overflow-x-hidden">
{fileResults.map(
(file) =>
file.id &&
file.name && (
<FileCard
key={file.id}
id={file.id}
name={file.name}
thumbnailLink={file.thumbnailLink}
/>
)
)}
</div>
)}
</div>
</Column>
</div>
Expand Down
13 changes: 12 additions & 1 deletion lib/google/drive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ export default class Drive {
}
return response.data
}

async searchFiles(searchParam: string) {
const searchResults = await this.drive.files.list({
q: `(name contains '${searchParam}' or fullText contains '${searchParam}') and mimeType != 'application/vnd.google-apps.folder'`,
fields: 'files(id, name, thumbnailLink)',
supportsAllDrives: true,
includeItemsFromAllDrives: true,
driveId: process.env.SHARED_GOOGLE_DRIVE_ID,
corpora: 'drive',
})
return searchResults.data.files ?? []
}
}

function createDrive(
Expand All @@ -48,7 +60,6 @@ function createDrive(
credentials.private_key,
['https://www.googleapis.com/auth/drive']
)

return new Drive(google.drive({ version: 'v3', auth }))
}

Expand Down
Loading

0 comments on commit 9462182

Please sign in to comment.