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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "daily-helper",
"version": "2.3.0",
"version": "2.4.0",
"private": true,
"scripts": {
"start": "vite",
Expand Down
6 changes: 6 additions & 0 deletions src/components/AppBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import IconButton from '@mui/material/IconButton'
import LinearProgress from '@mui/material/LinearProgress'
import MenuIcon from '@mui/icons-material/Menu'
import ReloadIcon from '@mui/icons-material/Replay'
import SearchBar from './SearchBar'
import Settings from './settings/Settings'
import SettingsIcon from '@mui/icons-material/Settings'
import Tab from '@mui/material/Tab'
Expand All @@ -35,6 +36,8 @@ type AppBarElementProps = {
drawbarName: string
isDrawbarOpen: boolean
setIsDrawbarOpen: (newState: boolean) => void
onSearch?: (query: string) => void
showSearch?: boolean
}
export default function AppBarElement({
handleReload: initialHandleReload,
Expand All @@ -45,6 +48,8 @@ export default function AppBarElement({
drawbarName,
isDrawbarOpen,
setIsDrawbarOpen,
onSearch,
showSearch = true,
}: AppBarElementProps) {
const relativeTime = useRelativeTime(lastUpdated)
const [teamTabValue, setTeamTabValue] = useState(0)
Expand Down Expand Up @@ -118,6 +123,7 @@ export default function AppBarElement({
</Tabs>

<Box sx={{ flexGrow: 1 }} />
{onSearch && showSearch && <SearchBar onSearch={onSearch} />}
{showDrawbar && (
<ClickAwayListener onClickAway={handleHideDrawbar}>
<Box>
Expand Down
50 changes: 28 additions & 22 deletions src/components/PullRequestFilterBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,34 @@ function FilterButton({
onClick,
}: FilterButtonProps) {
return (
<Stack
direction="row"
alignItems="center"
gap={0.5}
onClick={onClick}
sx={{
cursor: 'pointer',
px: 1,
py: 0.5,
borderRadius: 1,
color: active ? color : 'text.secondary',
'&:hover': {
color: active ? color : 'text.primary',
bgcolor: 'action.hover',
},
}}
>
<Icon sx={{ fontSize: 16 }} />
<Typography variant="body2" fontWeight={active ? 600 : 400}>
{label}
</Typography>
</Stack>
<Tooltip title={label}>
<Stack
direction="row"
alignItems="center"
gap={0.5}
onClick={onClick}
sx={{
cursor: 'pointer',
px: 1,
py: 0.5,
borderRadius: 1,
color: active ? color : 'text.secondary',
'&:hover': {
color: active ? color : 'text.primary',
bgcolor: 'action.hover',
},
}}
>
<Icon sx={{ fontSize: 16 }} />
<Typography
variant="body2"
fontWeight={active ? 600 : 400}
sx={{ display: { xs: 'none', md: 'block' }, whiteSpace: 'nowrap' }}
>
{label}
</Typography>
</Stack>
</Tooltip>
)
}

Expand Down
103 changes: 103 additions & 0 deletions src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { useEffect, useState } from 'react'

import CloseIcon from '@mui/icons-material/Close'
import IconButton from '@mui/material/IconButton'
import InputAdornment from '@mui/material/InputAdornment'
import SearchIcon from '@mui/icons-material/Search'
import TextField from '@mui/material/TextField'
import Tooltip from '@mui/material/Tooltip'
import { useDebounce } from '../hooks/useDebounce'

const DEBOUNCE_MS = 120

type SearchBarProps = {
onSearch: (query: string) => void
}

export default function SearchBar({ onSearch }: SearchBarProps) {
const [expanded, setExpanded] = useState(false)
const [value, setValue] = useState('')
const debouncedValue = useDebounce(value, DEBOUNCE_MS)

useEffect(() => {
onSearch(debouncedValue)
}, [debouncedValue, onSearch])

const collapseIfEmpty = () => {
if (!value) setExpanded(false)
}

const clear = () => {
setValue('')
setExpanded(false)
}

if (!expanded) {
return (
<Tooltip title="Search">
<IconButton
size="large"
color="inherit"
aria-label="Search"
onClick={() => setExpanded(true)}
>
<SearchIcon />
</IconButton>
</Tooltip>
)
}

return (
<TextField
autoFocus
size="small"
placeholder="Search…"
value={value}
onChange={e => setValue(e.target.value)}
onBlur={collapseIfEmpty}
onKeyDown={e => {
if (e.key === 'Escape') clear()
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon
fontSize="small"
sx={{ color: 'inherit', opacity: 0.8 }}
/>
</InputAdornment>
),
endAdornment: (
<InputAdornment position="end">
<IconButton
size="small"
color="inherit"
aria-label="Clear search"
// onMouseDown so the field's onBlur doesn't fire first and swallow the click
onMouseDown={e => e.preventDefault()}
onClick={clear}
>
<CloseIcon fontSize="small" />
</IconButton>
</InputAdornment>
),
}}
sx={{
width: { xs: 150, sm: 240 },
'& .MuiOutlinedInput-root': {
color: 'inherit',
bgcolor: 'rgba(255, 255, 255, 0.12)',
'& fieldset': { borderColor: 'rgba(255, 255, 255, 0.3)' },
'&:hover fieldset': { borderColor: 'rgba(255, 255, 255, 0.5)' },
'&.Mui-focused fieldset': {
borderColor: 'rgba(255, 255, 255, 0.7)',
},
},
'& .MuiInputBase-input::placeholder': {
color: 'inherit',
opacity: 0.7,
},
}}
/>
)
}
18 changes: 18 additions & 0 deletions src/helpers/prFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ function isTitleWhitelisted(title: string, filters: Settings_Filters): boolean {
)
}

export function matchesSearch(pr: PullRequest, rawQuery: string): boolean {
const query = rawQuery.trim().toLowerCase()
if (!query) return true

const userMatches = (user: User) =>
getDisplayName(user).toLowerCase().includes(query) ||
user.login.toLowerCase().includes(query)

return (
pr.title.toLowerCase().includes(query) ||
`#${pr.number}`.includes(query) ||
pr.repositoryName.toLowerCase().includes(query) ||
userMatches(pr.author) ||
pr.assignees.some(userMatches) ||
pr.labels.some(label => label.name.toLowerCase().includes(query))
)
}

export function applyReviewRequiredFilter(
prs: PullRequest[],
viewerLogin: string,
Expand Down
12 changes: 12 additions & 0 deletions src/hooks/useDebounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useEffect, useState } from 'react'

export function useDebounce<T>(value: T, delayMs: number): T {
const [debounced, setDebounced] = useState(value)

useEffect(() => {
const id = setTimeout(() => setDebounced(value), delayMs)
return () => clearTimeout(id)
}, [value, delayMs])

return debounced
}
12 changes: 10 additions & 2 deletions src/views/DailyHelper/DailyHelper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import Stack from '@mui/material/Stack'
import Tooltip from '@mui/material/Tooltip'
import Typography from '@mui/material/Typography'
import VisibleIcon from '@mui/icons-material/Visibility'
import { applyReviewRequiredFilter } from '../../helpers/prFilters'
import {
applyReviewRequiredFilter,
matchesSearch,
} from '../../helpers/prFilters'
import { getDisplayName } from '../../helpers/getDisplayName'
import { getStateRank } from '../../helpers/getStateRank'
import { dataFetcher } from '../../helpers/dataFetcher'
Expand Down Expand Up @@ -68,6 +71,7 @@ export default function DailyHelper() {
const isMyPrsFilterActive = activeFilter === 'myPrs'
const isMyWorkFilterActive = activeFilter === 'myWork'
const [viewerLogin, setViewerLogin] = useState<string | null>(null)
const [searchQuery, setSearchQuery] = useState('')
const [activeView, setActiveView] = useState<'list' | 'kanban' | 'notes'>(
settingsHandler.loadView(),
)
Expand Down Expand Up @@ -191,7 +195,8 @@ export default function DailyHelper() {
(!reviewRequiredFilteredIds ||
reviewRequiredFilteredIds.has(pr.id)) &&
(!isMyPrsFilterActive || pr.author.login === viewerLogin) &&
(!isMyWorkFilterActive || isMyWork(pr)),
(!isMyWorkFilterActive || isMyWork(pr)) &&
matchesSearch(pr, searchQuery),
),
[
sortedPullRequests,
Expand All @@ -202,6 +207,7 @@ export default function DailyHelper() {
viewerLogin,
hiddenLabels,
isPullRequestsWithoutLabelsHidden,
searchQuery,
],
)

Expand Down Expand Up @@ -275,6 +281,8 @@ export default function DailyHelper() {
drawbarName="Search filters"
isDrawbarOpen={isDrawbarOpen}
setIsDrawbarOpen={setIsDrawbarOpen}
onSearch={setSearchQuery}
showSearch={activeView !== 'notes'}
/>
<Box
sx={{
Expand Down
Loading