import { columnHasAnyFilter , eventActions , eventSubjectTypes , filterRecordHasAnyForcedValue , filterRecordWithThisValueCount , getEventActionMetadata , getFilterCountMetadata , getFilteredItems , getItemInbox , getItemsFilterMetadata , getNotificationReasonMetadata , getOwnerAndRepoFormattedFilter , getStateTypeMetadata , getSubjectTypeMetadata , GitHubEventSubjectType , GitHubIssueOrPullRequestSubjectType , GitHubNotificationSubjectType , GitHubStateType , isReadFilterChecked , issueOrPullRequestStateTypes , issueOrPullRequestSubjectTypes , isUnreadFilterChecked , itemPassesFilterRecord , notificationReasons , notificationSubjectTypes , ThemeColors , } from '@devhub/core' ; import _ from 'lodash' ; import React , { Fragment , useCallback , useMemo , useRef , useState } from 'react' ; import { ScrollView , View } from 'react-native' ; import { useDispatch } from 'react-redux' ; import { useColumn } from '../../hooks/use-column' ; import { useColumnData } from '../../hooks/use-column-data' ; import { useReduxState } from '../../hooks/use-redux-state' ; import * as actions from '../../redux/actions' ; import * as selectors from '../../redux/selectors' ; import { sharedStyles } from '../../styles/shared' ; import { contentPadding } from '../../styles/variables' ; import { vibrateHapticFeedback } from '../../utils/helpers/shared' ; import { columnHeaderItemContentSize } from '../columns/ColumnHeader' ; import { Avatar } from '../common/Avatar' ; import { Button } from '../common/Button' ; import { Checkbox , checkboxLabelSpacing , defaultCheckboxSize , } from '../common/Checkbox' ; import { CounterMetadata , CounterMetadataProps , } from '../common/CounterMetadata' ; import { Separator } from '../common/Separator' ; import { Spacer } from '../common/Spacer' ; import { ThemedView } from '../themed/ThemedView' ; import { getColumnHeaderThemeColors } from './ColumnHeader' ; import { ColumnOptionsInbox } from './ColumnOptionsInbox' ; import { ColumnOptionsRow } from './ColumnOptionsRow' ; import { sharedColumnOptionsStyles } from './options/shared' ; const metadataSortFn = ( a , b ) => a . label < b . label ? - 1 : a . label > b . label ? 1 : 0 ; const stateTypeOptions = issueOrPullRequestStateTypes . map ( getStateTypeMetadata ) ; const eventSubjectTypeOptions = eventSubjectTypes . map ( getSubjectTypeMetadata ) . sort ( metadataSortFn ) ; const eventActionOptions = eventActions . map ( getEventActionMetadata ) . sort ( metadataSortFn ) ; const issueOrPullRequestSubjectTypeOptions = issueOrPullRequestSubjectTypes . map ( getSubjectTypeMetadata ) . sort ( metadataSortFn ) ; const notificationSubjectTypeOptions = notificationSubjectTypes . map ( getSubjectTypeMetadata ) . sort ( metadataSortFn ) ; const notificationReasonOptions = notificationReasons . map ( getNotificationReasonMetadata ) . sort ( metadataSortFn ) ; export interface ColumnFiltersProps { columnId : string ; forceOpenAll ? : boolean ; startWithFiltersExpanded ? : boolean ; } export type ColumnFilterCategory = 'bot' | 'draft' | 'event_action' | 'inbox' | 'notification_reason' | 'privacy' | 'repos' | 'saved_for_later' | 'state' | 'subject_types' | 'unread' ; export const ColumnFilters = React . memo ( ( props ) => { const { columnId , forceOpenAll , startWithFiltersExpanded } = props ; const { column , dashboardFromUsername } = useColumn ( columnId ) ; const plan = useReduxState ( selectors . currentUserPlanSelector ) ; const getFilteredItemsOptions = useMemo < Parameters < typeof getFilteredItems > [ 3 ] > ( ( ) => ( { dashboardFromUsername , mergeSimilar : false , plan , } ) , [ dashboardFromUsername , plan ] ) ; const { allItems , filteredItems } = useColumnData ( columnId , getFilteredItemsOptions ) ; const { allForcedOwners , allForcedRepos , ownerFilters , ownerFiltersWithRepos , repoFilters , } = useMemo ( ( ) => getOwnerAndRepoFormattedFilter ( column && column . filters ) , [ column && column . filters ] ) ; const ownerOrRepoFilteredItemsMetadata = useMemo ( ( ) => column && getItemsFilterMetadata ( column . type , getFilteredItems ( column . type , allItems , { ... column . filters , owners : undefined , } , getFilteredItemsOptions ) , { forceIncludeTheseOwners : allForcedOwners , forceIncludeTheseRepos : allForcedRepos , dashboardFromUsername , plan , } ) , [ allForcedOwners , allForcedRepos , allItems , column && column . filters , column && column . type , dashboardFromUsername , plan , ] ) ; const _owners = Object . keys ( ( ownerOrRepoFilteredItemsMetadata && ownerOrRepoFilteredItemsMetadata . owners ) || { } ) ; const _shouldShowOwnerOrRepoFilters = ! ! ( _owners . length >= 1 || ( _owners . length === 1 && ownerOrRepoFilteredItemsMetadata && ownerOrRepoFilteredItemsMetadata . owners [ _owners [ 0 ] ] . repos && Object . keys ( ownerOrRepoFilteredItemsMetadata . owners [ _owners [ 0 ] ] . repos ) . length >= 1 ) ) ; const _allColumnOptionCategories = [ ! ! column && column . type === 'notifications' && 'inbox' , 'saved_for_later' , 'unread' , 'state' , 'draft' , 'bot' , 'subject_types' , ! ! column && column . type === 'activity' && 'event_action' , ! ! column && column . type === 'notifications' && 'notification_reason' , ! ! column && column . type === 'notifications' && 'privacy' , _shouldShowOwnerOrRepoFilters && 'repos' , ] ; const allColumnOptionCategories = _allColumnOptionCategories . filter ( Boolean ) as ColumnFilterCategory [ ] ; const [ openedOptionCategories , setOpenedOptionCategories ] = useState ( ( ) => new Set < ColumnFilterCategory > ( forceOpenAll || startWithFiltersExpanded ? allColumnOptionCategories : [ ] ) ) ; const lastColumnCategory = allColumnOptionCategories . slice ( - 1 ) [ 0 ] ; const allIsOpen = openedOptionCategories . size === allColumnOptionCategories . length ; const allowOnlyOneCategoryToBeOpenedRef = useRef ( ! allIsOpen ) ; const allowToggleCategories = ! forceOpenAll ; const dispatch = useDispatch ( ) ; const toggleOpenedOptionCategory = useCallback ( ( optionCategory ) => { setOpenedOptionCategories ( ( _set ) => { const set = new Set ( _set ) ; const isOpen = set . has ( optionCategory ) ; if ( allowOnlyOneCategoryToBeOpenedRef . current ) set . clear ( ) ; isOpen ? set . delete ( optionCategory ) : set . add ( optionCategory ) ; if ( set . size === 0 ) allowOnlyOneCategoryToBeOpenedRef . current = true ; return set ; } ) ; } , [ ] ) ; const allItemsMetadata = useMemo ( ( ) => getItemsFilterMetadata ( column ? column . type : 'activity' , allItems , { dashboardFromUsername , plan , } ) , [ column && column . type , allItems , dashboardFromUsername , plan ] ) ; const filteredItemsMetadata = useMemo ( ( ) => getItemsFilterMetadata ( column ? column . type : 'activity' , filteredItems , { dashboardFromUsername , plan , } ) , [ column && column . type , filteredItems , dashboardFromUsername , plan ] ) ; if ( ! column ) return null ; const inbox = getItemInbox ( column . type , column . filters ) ; function getCheckboxRight ( counterMetadataProps , { alwaysRenderANumber , backgroundColor , } = { } ) { return ( < CounterMetadata { ... counterMetadataProps } alwaysRenderANumber = { alwaysRenderANumber } backgroundColor = { backgroundColor } / > ) ; } return ( < ThemedView backgroundColor = { getColumnHeaderThemeColors ( ) . normal } style = { sharedStyles . flex } > < ScrollView alwaysBounceHorizontal = { false } alwaysBounceVertical bounces showsHorizontalScrollIndicator = { false } style = { sharedStyles . flex } > { allColumnOptionCategories . includes ( 'inbox' ) && column . type === 'notifications' && ( < ColumnOptionsInbox enableBackgroundHover = { allowToggleCategories } getCheckboxPropsFor = { ( i ) => ( { containerStyle : sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding , right : inbox === i ? i === 'all' ? getCheckboxRight ( allItemsMetadata . inbox . all ) : i === 'participating' ? getCheckboxRight ( allItemsMetadata . inbox . participating ) : undefined : undefined , } ) } inbox = { inbox } isOpen = { openedOptionCategories . has ( 'inbox' ) } onChange = { ( i ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColumnParticipatingFilter ( { columnId : column . id , participating : i === 'participating' , } ) ) ; } } onToggleRowVisibility = { allowToggleCategories ? ( ) => toggleOpenedOptionCategory ( 'inbox' ) : undefined } / > ) } { } { allColumnOptionCategories . includes ( 'saved_for_later' ) && ( ( ) => { const saved = column . filters && column . filters . saved ; return ( < ColumnOptionsRow enableBackgroundHover = { allowToggleCategories } hasChanged = { typeof saved === 'boolean' } headerItemFixedIconSize = { columnHeaderItemContentSize } hideSeparator = { lastColumnCategory === 'saved_for_later' } icon = { { family : 'octicon' , name : saved ? 'bookmark-fill' : saved === false ? 'bookmark-slash-fill' : 'bookmark' , } } isOpen = { openedOptionCategories . has ( 'saved_for_later' ) } onToggle = { allowToggleCategories ? ( ) => toggleOpenedOptionCategory ( 'saved_for_later' ) : undefined } right = { saved === true ? 'Only' : saved === false ? 'Excluded' : '' } title = "Bookmarks" > < Checkbox checked = { typeof saved === 'boolean' ? saved : null } containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } enableIndeterminateState label = "Bookmarks" onChange = { ( checked ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColumnSavedFilter ( { columnId : column . id , saved : checked , } ) ) ; } } right = { getCheckboxRight ( filteredItemsMetadata . saved ) } / > < /ColumnOptionsRow>); } ) ( ) } { allColumnOptionCategories . includes ( 'unread' ) && ( ( ) => { const isReadChecked = isReadFilterChecked ( column . filters ) ; const isUnreadChecked = isUnreadFilterChecked ( column . filters ) ; return ( < ColumnOptionsRow enableBackgroundHover = { allowToggleCategories } hasChanged = { ! ! ( column . filters && typeof column . filters . unread === 'boolean' ) } headerItemFixedIconSize = { columnHeaderItemContentSize } hideSeparator = { lastColumnCategory === 'unread' } icon = { { family : 'octicon' , name : column . filters && column . filters . unread === true ? 'eye-closed' : 'eye' , } } isOpen = { openedOptionCategories . has ( 'unread' ) } onToggle = { allowToggleCategories ? ( ) => toggleOpenedOptionCategory ( 'unread' ) : undefined } right = { isReadChecked && ! isUnreadChecked ? 'Read' : ! isReadChecked && isUnreadChecked ? 'Unread' : '' } title = "Read status" > < Checkbox checked = { isReadChecked && isUnreadChecked ? null : isReadChecked } containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue enableIndeterminateState = { isReadChecked && isUnreadChecked } label = "Read" squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } onChange = { ( ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColumnUnreadFilter ( { columnId : column . id , unread : isReadChecked && isUnreadChecked ? false : isReadChecked ? undefined : isUnreadChecked ? undefined : false , } ) ) ; } } right = { getCheckboxRight ( { read : filteredItemsMetadata . inbox [ inbox ] . read , } ) } /> < Checkbox checked = { isReadChecked && isUnreadChecked ? null : isUnreadChecked } containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue enableIndeterminateState = { isReadChecked && isUnreadChecked } label = "Unread" squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } onChange = { ( ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColumnUnreadFilter ( { columnId : column . id , unread : isReadChecked && isUnreadChecked ? true : isUnreadChecked ? undefined : isReadChecked ? undefined : true , } ) ) ; } } right = { getCheckboxRight ( { unread : filteredItemsMetadata . inbox [ inbox ] . unread , } ) } /> < /ColumnOptionsRow>); } ) ( ) } { allColumnOptionCategories . includes ( 'state' ) && ( ( ) => { const filters = column . filters && ( column . filters . state as Partial < Record < GitHubStateType , boolean > > ) ; const defaultBooleanValue = true ; const isFilterStrict = filterRecordWithThisValueCount ( filters , defaultBooleanValue ) ; const hasForcedValue = filterRecordHasAnyForcedValue ( filters ) ; const countMetadata = getFilterCountMetadata ( filters , stateTypeOptions . length , defaultBooleanValue ) ; const supportsOnlyOne = true ; return ( < ColumnOptionsRow enableBackgroundHover = { allowToggleCategories } hasChanged = { filterRecordHasAnyForcedValue ( filters ) } headerItemFixedIconSize = { columnHeaderItemContentSize } hideSeparator = { lastColumnCategory === 'state' } icon = { { family : 'octicon' , name : hasForcedValue ? itemPassesFilterRecord ( filters ! , 'merged' , defaultBooleanValue ) ? 'git-merge' : itemPassesFilterRecord ( filters ! , 'closed' , defaultBooleanValue ) ? 'issue-closed' : 'issue-opened' : 'issue-opened' , } } isOpen = { openedOptionCategories . has ( 'state' ) } onToggle = { allowToggleCategories ? ( ) => toggleOpenedOptionCategory ( 'state' ) : undefined } title = "State" right = { filterRecordHasAnyForcedValue ( filters ) ? `${ countMetadata . checked }/${ countMetadata . total }` : '' } > { stateTypeOptions . map ( ( item ) => { const checked = filters && typeof filters [ item . state ] === 'boolean' ? filters [ item . state ] : isFilterStrict ? ! defaultBooleanValue : null ; const enableIndeterminateState = ! isFilterStrict || checked === defaultBooleanValue ; return ( < Checkbox key = { `state-type-option-${ item . state }` } checked = { checked } checkedBackgroundThemeColor = { item . color } circle = { supportsOnlyOne } containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue = { defaultBooleanValue } squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } enableIndeterminateState = { enableIndeterminateState } label = { item . label } onChange = { ( value ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColummStateTypeFilter ( { columnId : column . id , state : item . state , supportsOnlyOne , value : supportsOnlyOne ? typeof value === 'boolean' ? true : null : isFilterStrict ? typeof value === 'boolean' ? defaultBooleanValue : null : hasForcedValue ? typeof value === 'boolean' ? ! defaultBooleanValue : null : value , } ) ) ; } } right = { getCheckboxRight ( filteredItemsMetadata . state [ item . state ] ) } / > ) ; } ) } < /ColumnOptionsRow>); } ) ( ) } { allColumnOptionCategories . includes ( 'draft' ) && ( ( ) => { const draft = column . filters && column . filters . draft ; const defaultBooleanValue = true ; return ( < ColumnOptionsRow enableBackgroundHover = { allowToggleCategories } hasChanged = { typeof draft === 'boolean' } headerItemFixedIconSize = { columnHeaderItemContentSize } hideSeparator = { lastColumnCategory === 'draft' } icon = { { family : 'octicon' , name : 'pencil' } } isOpen = { openedOptionCategories . has ( 'draft' ) } onToggle = { allowToggleCategories ? ( ) => toggleOpenedOptionCategory ( 'draft' ) : undefined } title = "Draft" right = { draft === true ? 'Only' : draft === false ? 'Excluded' : '' } > < Checkbox key = "draft-type-option" checked = { typeof draft === 'boolean' ? draft : null } checkedBackgroundThemeColor = "gray" containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue = { defaultBooleanValue } squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } enableIndeterminateState label = "Draft" onChange = { ( value ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColummDraftFilter ( { columnId : column . id , draft : typeof value === 'boolean' ? value : undefined , } ) ) ; } } right = { getCheckboxRight ( filteredItemsMetadata . draft ) } / > < /ColumnOptionsRow>); } ) ( ) } { allColumnOptionCategories . includes ( 'bot' ) && ( ( ) => { const bot = column . filters && column . filters . bot ; const defaultBooleanValue = true ; return ( < ColumnOptionsRow enableBackgroundHover = { allowToggleCategories } hasChanged = { typeof bot === 'boolean' } headerItemFixedIconSize = { columnHeaderItemContentSize } hideSeparator = { lastColumnCategory === 'bot' } icon = { { family : 'octicon' , name : 'hubot' } } isOpen = { openedOptionCategories . has ( 'bot' ) } onToggle = { allowToggleCategories ? ( ) => toggleOpenedOptionCategory ( 'bot' ) : undefined } title = "Bots" right = { bot === true ? 'Only' : bot === false ? 'Excluded' : '' } > < Checkbox key = "bot-type-option" checked = { typeof bot === 'boolean' ? bot : null } containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue = { defaultBooleanValue } squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } enableIndeterminateState label = "Bots" onChange = { ( value ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColummBotFilter ( { columnId : column . id , bot : typeof value === 'boolean' ? value : undefined , } ) ) ; } } right = { getCheckboxRight ( filteredItemsMetadata . bot ) } / > < /ColumnOptionsRow>); } ) ( ) } { allColumnOptionCategories . includes ( 'subject_types' ) && ( ( ) => { const filters = column . filters && ( column . filters . subjectTypes as Partial < Record < GitHubEventSubjectType | GitHubIssueOrPullRequestSubjectType | GitHubNotificationSubjectType , boolean > > ) ; const subjectTypeOptions = column . type === 'activity' ? eventSubjectTypeOptions : column . type === 'issue_or_pr' ? issueOrPullRequestSubjectTypeOptions : column . type === 'notifications' ? notificationSubjectTypeOptions : [ ] ; if ( ! ( subjectTypeOptions && subjectTypeOptions . length ) ) return null ; const defaultBooleanValue = true ; const isFilterStrict = filterRecordWithThisValueCount ( filters , defaultBooleanValue ) ; const hasForcedValue = filterRecordHasAnyForcedValue ( filters ) ; const countMetadata = getFilterCountMetadata ( filters , subjectTypeOptions . length , defaultBooleanValue ) ; return ( < ColumnOptionsRow enableBackgroundHover = { allowToggleCategories } hasChanged = { filterRecordHasAnyForcedValue ( filters ) } headerItemFixedIconSize = { columnHeaderItemContentSize } hideSeparator = { lastColumnCategory === 'subject_types' } icon = { { family : 'octicon' , name : 'file' } } isOpen = { openedOptionCategories . has ( 'subject_types' ) } onToggle = { allowToggleCategories ? ( ) => toggleOpenedOptionCategory ( 'subject_types' ) : undefined } title = "Subject type" right = { filterRecordHasAnyForcedValue ( filters ) ? `${ countMetadata . checked }/${ countMetadata . total }` : '' } > { subjectTypeOptions . map ( ( item ) => { const checked = filters && typeof filters [ item . subjectType ] === 'boolean' ? filters [ item . subjectType ] : isFilterStrict ? ! defaultBooleanValue : null ; const enableIndeterminateState = ! isFilterStrict || checked === defaultBooleanValue ; const counterMetadataProps = filteredItemsMetadata . subjectType [ item . subjectType ] ; return ( < Checkbox key = { `notification-subject-type-option-${ item . subjectType }` } checked = { checked } checkedBackgroundThemeColor = { item . color } containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue = { defaultBooleanValue } enableIndeterminateState = { enableIndeterminateState } label = { item . label } onChange = { ( value ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColummSubjectTypeFilter ( { columnId : column . id , subjectType : item . subjectType , value : isFilterStrict ? typeof value === 'boolean' ? defaultBooleanValue : null : hasForcedValue ? typeof value === 'boolean' ? ! defaultBooleanValue : null : value , } ) ) ; } } right = { getCheckboxRight ( counterMetadataProps || { } , { backgroundColor : item . subjectType === 'RepositoryVulnerabilityAlert' && counterMetadataProps && counterMetadataProps . unread ? 'red' : undefined , } ) } squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } / > ) ; } ) } < /ColumnOptionsRow>); } ) ( ) } { allColumnOptionCategories . includes ( 'notification_reason' ) && column . type === 'notifications' && ( ( ) => { const filters = column . filters && column . filters . notifications && column . filters . notifications . reasons ; const defaultBooleanValue = true ; const isFilterStrict = filterRecordWithThisValueCount ( filters , defaultBooleanValue ) ; const hasForcedValue = filterRecordHasAnyForcedValue ( filters ) ; const countMetadata = getFilterCountMetadata ( filters , notificationReasonOptions . length , defaultBooleanValue ) ; return ( < ColumnOptionsRow enableBackgroundHover = { allowToggleCategories } hasChanged = { filterRecordHasAnyForcedValue ( filters ) } headerItemFixedIconSize = { columnHeaderItemContentSize } hideSeparator = { lastColumnCategory === 'notification_reason' } icon = { { family : 'octicon' , name : 'rss' } } isOpen = { openedOptionCategories . has ( 'notification_reason' ) } onToggle = { allowToggleCategories ? ( ) => toggleOpenedOptionCategory ( 'notification_reason' ) : undefined } title = "Subscription reason" right = { filterRecordHasAnyForcedValue ( filters ) ? `${ countMetadata . checked }/${ countMetadata . total }` : '' } > { notificationReasonOptions . map ( ( item ) => { const checked = filters && typeof filters [ item . reason ] === 'boolean' ? filters [ item . reason ] : null ; const counterMetadataProps = filteredItemsMetadata . subscriptionReason [ item . reason ] ; return ( < Checkbox key = { `notification-reason-option-${ item . reason }` } checked = { checked } checkedBackgroundThemeColor = { item . color } containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue = { defaultBooleanValue } enableIndeterminateState = { ! isFilterStrict || checked === defaultBooleanValue } label = { item . label } labelTooltip = { item . fullDescription } onChange = { ( value ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColumnReasonFilter ( { columnId : column . id , reason : item . reason , value : isFilterStrict ? typeof value === 'boolean' ? defaultBooleanValue : null : hasForcedValue ? typeof value === 'boolean' ? ! defaultBooleanValue : null : value , } ) ) ; } } right = { getCheckboxRight ( counterMetadataProps || { } , { backgroundColor : item . reason === 'security_alert' && counterMetadataProps && counterMetadataProps . unread ? 'red' : undefined , } ) } squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } / > ) ; } ) } < /ColumnOptionsRow>); } ) ( ) } { allColumnOptionCategories . includes ( 'event_action' ) && column . type === 'activity' && ( ( ) => { const filters = column . filters && column . filters . activity && column . filters . activity . actions ; const defaultBooleanValue = true ; const isFilterStrict = filterRecordWithThisValueCount ( filters , defaultBooleanValue ) ; const hasForcedValue = filterRecordHasAnyForcedValue ( filters ) ; const countMetadata = getFilterCountMetadata ( filters , eventActionOptions . length , defaultBooleanValue ) ; return ( < ColumnOptionsRow enableBackgroundHover = { allowToggleCategories } hasChanged = { filterRecordHasAnyForcedValue ( filters ) } headerItemFixedIconSize = { columnHeaderItemContentSize } hideSeparator = { lastColumnCategory === 'event_action' } icon = { { family : 'octicon' , name : 'note' } } isOpen = { openedOptionCategories . has ( 'event_action' ) } onToggle = { allowToggleCategories ? ( ) => toggleOpenedOptionCategory ( 'event_action' ) : undefined } title = "Event action" right = { filterRecordHasAnyForcedValue ( filters ) ? `${ countMetadata . checked }/${ countMetadata . total }` : '' } > { eventActionOptions . map ( ( item ) => { const checked = filters && typeof filters [ item . action ] === 'boolean' ? filters [ item . action ] : isFilterStrict ? ! defaultBooleanValue : null ; const enableIndeterminateState = ! isFilterStrict || checked === defaultBooleanValue ; return ( < Checkbox key = { `event-type-option-${ item . action }` } checked = { checked } containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue = { defaultBooleanValue } enableIndeterminateState = { enableIndeterminateState } label = { item . label } onChange = { ( value ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColumnActivityActionFilter ( { columnId : column . id , type : item . action , value : isFilterStrict ? typeof value === 'boolean' ? defaultBooleanValue : null : hasForcedValue ? typeof value === 'boolean' ? ! defaultBooleanValue : null : value , } ) ) ; } } right = { getCheckboxRight ( filteredItemsMetadata . eventAction [ item . action ] || { } ) } squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } / > ) ; } ) } < /ColumnOptionsRow>); } ) ( ) } { allColumnOptionCategories . includes ( 'privacy' ) && ( ( ) => { const isPrivateChecked = ! ( column . filters && column . filters . private === false ) ; const isPublicChecked = ! ( column . filters && column . filters . private === true ) ; return ( < ColumnOptionsRow enableBackgroundHover = { allowToggleCategories } hasChanged = { ! ! column . filters && typeof column . filters . private === 'boolean' } headerItemFixedIconSize = { columnHeaderItemContentSize } hideSeparator = { lastColumnCategory === 'privacy' } icon = { { family : 'octicon' , name : column . filters && column . filters . private === false ? 'globe' : 'lock' , } } isOpen = { openedOptionCategories . has ( 'privacy' ) } onToggle = { allowToggleCategories ? ( ) => toggleOpenedOptionCategory ( 'privacy' ) : undefined } right = { isPrivateChecked && ! isPublicChecked ? 'Private' : ! isPrivateChecked && isPublicChecked ? 'Public' : '' } title = "Privacy" > < Checkbox checked = { isPublicChecked && isPrivateChecked ? null : isPublicChecked } containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue enableIndeterminateState = { isPublicChecked && isPrivateChecked } label = "Public" squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } onChange = { ( ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColumnPrivacyFilter ( { columnId : column . id , private : isPublicChecked && isPrivateChecked ? false : isPublicChecked ? undefined : isPrivateChecked ? undefined : false , } ) ) ; } } right = { getCheckboxRight ( filteredItemsMetadata . privacy . public ) } / > < Checkbox checked = { isPublicChecked && isPrivateChecked ? null : isPrivateChecked } containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue enableIndeterminateState = { isPublicChecked && isPrivateChecked } label = "Private" squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } onChange = { ( ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColumnPrivacyFilter ( { columnId : column . id , private : isPublicChecked && isPrivateChecked ? true : isPrivateChecked ? undefined : isPublicChecked ? undefined : true , } ) ) ; } } right = { getCheckboxRight ( filteredItemsMetadata . privacy . private ) } / > < /ColumnOptionsRow>); } ) ( ) } { allColumnOptionCategories . includes ( 'repos' ) && ( ( ) => { const defaultBooleanValue = true ; const isOwnerFilterStrict = filterRecordWithThisValueCount ( ownerFilters , true ) >= 1 ; const isRepoFilterStrict = filterRecordWithThisValueCount ( repoFilters , true ) >= 1 ; const ownerFilterHasForcedValue = filterRecordHasAnyForcedValue ( ownerFilters ) ; const repoFilterHasForcedValue = filterRecordHasAnyForcedValue ( repoFilters ) ; const owners = _ . sortBy ( Object . keys ( ( ownerOrRepoFilteredItemsMetadata && ownerOrRepoFilteredItemsMetadata . owners ) || { } ) ) ; const ownerCountMetadata = getFilterCountMetadata ( ownerFilters , owners . length , defaultBooleanValue ) ; return ( < ColumnOptionsRow enableBackgroundHover = { allowToggleCategories } hasChanged = { ownerFilterHasForcedValue || repoFilterHasForcedValue } headerItemFixedIconSize = { columnHeaderItemContentSize } hideSeparator = { lastColumnCategory === 'repos' } icon = { { family : 'octicon' , name : 'repo' } } isOpen = { openedOptionCategories . has ( 'repos' ) } onToggle = { allowToggleCategories ? ( ) => toggleOpenedOptionCategory ( 'repos' ) : undefined } title = "Repositories" right = { ownerFilterHasForcedValue || repoFilterHasForcedValue ? `${ ownerCountMetadata . checked }/${ ownerCountMetadata . total }` : '' } > { owners . map ( ( owner ) => { const ownerItem = ownerOrRepoFilteredItemsMetadata && ownerOrRepoFilteredItemsMetadata . owners [ owner ] ; if ( ! ownerItem ) return null ; const ownerFiltersWithRepo = ownerFiltersWithRepos && ownerFiltersWithRepos [ owner ] ? ownerFiltersWithRepos [ owner ] : null ; const ownerChecked = ownerFilters && typeof ownerFilters [ owner ] === 'boolean' ? ownerFilters [ owner ] : null ; const repos = _ . sortBy ( Object . keys ( ownerItem . repos ) ) ; const thisOwnerRepoFilters = ownerFiltersWithRepos && ownerFiltersWithRepos [ owner ] && ownerFiltersWithRepos [ owner ] ! . repos ; const thisOwnerHasStrictRepoFilter = filterRecordWithThisValueCount ( thisOwnerRepoFilters , defaultBooleanValue ) >= 1 ; const thisOwnerRepoFilterHasForcedValue = filterRecordHasAnyForcedValue ( thisOwnerRepoFilters ) ; return ( < Fragment key = { `owner-option-fragment-${ owner }` } > < Checkbox key = { `owner-option-${ owner }` } checked = { ownerChecked } containerStyle = { sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding } defaultValue = { defaultBooleanValue } enableIndeterminateState = { ! ( isOwnerFilterStrict || isRepoFilterStrict ) || thisOwnerHasStrictRepoFilter || ownerChecked === defaultBooleanValue } label = { owner } labelTooltip = { owner } left = { < Avatar size = { defaultCheckboxSize } shape = "circle" username = { owner } / > } onChange = { ( value ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColumnOwnerFilter ( { columnId : column . id , owner , value : isOwnerFilterStrict ? typeof value === 'boolean' ? defaultBooleanValue : null : ownerFilterHasForcedValue ? typeof value === 'boolean' ? ! defaultBooleanValue : null : value , } ) ) ; } } right = { getCheckboxRight ( ownerItem . metadata ! ) } squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } / > { repos . map ( ( repo ) => { const repoFullName = `${ owner }/${ repo }` ; const repoItem = ownerItem . repos [ repo ] ; if ( ! repoItem ) return null ; const repoChecked = ownerFiltersWithRepo && ownerFiltersWithRepo . repos && typeof ownerFiltersWithRepo . repos [ repo ] === 'boolean' ? ownerFiltersWithRepo . repos [ repo ] : null ; const disabled = ownerChecked === false ; return ( < Checkbox key = { `owner-repo-option-${ owner }/${ repo }` } checked = { disabled ? false : repoChecked } containerStyle = { [ sharedColumnOptionsStyles . fullWidthCheckboxContainerWithPadding , { marginLeft : defaultCheckboxSize + checkboxLabelSpacing , } , ] } defaultValue = { defaultBooleanValue } disabled = { disabled } enableIndeterminateState = { ! disabled && ( ! ( isOwnerFilterStrict || isRepoFilterStrict ) || ( ownerChecked === true && ! thisOwnerHasStrictRepoFilter ) || repoChecked === defaultBooleanValue ) } label = { repo } labelTooltip = { repoFullName } onChange = { ( value ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . setColumnRepoFilter ( { columnId : column . id , owner , repo , value : thisOwnerHasStrictRepoFilter ? typeof value === 'boolean' ? defaultBooleanValue : null : thisOwnerRepoFilterHasForcedValue ? isRepoFilterStrict && value !== ! defaultBooleanValue ? defaultBooleanValue : typeof value === 'boolean' ? ! defaultBooleanValue : null : value , } ) ) ; } } right = { getCheckboxRight ( repoItem ) } squareContainerStyle = { sharedColumnOptionsStyles . checkboxSquareContainer } / > ) ; } ) } < /Fragment>); } ) } < /ColumnOptionsRow>); } ) ( ) } < /ScrollView> < Separator horizontal / > < Spacer height = { contentPadding / 2 } /> < View style = { { paddingVertical : contentPadding / 2 , paddingHorizontal : contentPadding , } } > < Button disabled = { ! columnHasAnyFilter ( column . type , { ... column . filters , ... ( column . type === 'notifications' && { notifications : { ... ( column . filters && column . filters . notifications ) , participating : undefined , } , } ) , } ) } onPress = { ( ) => { vibrateHapticFeedback ( ) ; dispatch ( actions . clearColumnFilters ( { columnId : column . id } ) ) ; } } > Reset filters < /Button> < /View> < Separator horizontal / > < /ThemedView>); } ) ; ColumnFilters . displayName = 'ColumnFilters' ;