<commit_before>import moment from 'moment'

import {
    StandardSearchResponse,
    AnnotationsSearchResponse,
    AnnotsByPageUrl,
    AnnotPage,
} from 'src/search/background/types'
import {
    PageData,
    NormalizedState,
    PageResult,
    PageResultsByDay,
    NoteData,
    SearchResultToState,
    NoteResult,
    NoteFormState,
    RootState,
    NestedResults,
    NotesType,
} from './types'
import { Annotation } from 'src/annotations/types'
import { PAGE_SEARCH_DUMMY_DAY } from '../constants'

export const notesTypeToString = (type: NotesType): string => {
    if (type === 'user') {
        return 'Your notes'
    }
    if (type === 'followed') {
        return 'Shared with you'
    }
    return 'Results'
}

export const stringToNotesType = (str: string): NotesType => {
    if (str === 'Your notes') {
        return 'user'
    }
    if (str === 'Shared with you') {
        return 'followed'
    }
    return 'search'
}

export const formatDayGroupTime = (day: number) =>
    moment(day).calendar(null, {
        sameDay: '[Today]',
        lastDay: '[Yesterday]',
        lastWeek: '[Last] dddd',
        sameElse: 'dddd, DD MMMM, YYYY',
    })

export const initNormalizedState = <T>(): NormalizedState<T> => ({
    allIds: [],
    byId: {},
})

export const getInitialFormState = (): NoteFormState => ({
    tags: [],
    inputValue: '',
    isTagPickerShown: false,
})

export const areAllNotesShown = ({ results }: RootState): boolean => {
    for (const { pages } of Object.values(results)) {
        for (const { areNotesShown } of Object.values(pages.byId)) {
            if (!areNotesShown) {
                return false
            }
        }
    }
    return true
}

export const bindFunctionalProps = <
    Props = { [key: string]: (...args: any) => any },
    BoundProps = { [key: string]: (...args: any) => any }
>(
    props: Props,
    ...args
): BoundProps => {
    const boundProps = {} as BoundProps

    for (const [key, fn] of Object.entries(props)) {
        boundProps[key] = fn(...args)
    }

    return boundProps
}

export const getInitialPageResultState = (
    id: string,
    noteIds: string[] = [],
): PageResult => ({
    id,
    notesType: 'user',
    areNotesShown: false,
    isTagPickerShown: false,
    isCopyPasterShown: false,
    isListPickerShown: false,
    sortingFn: (a, b) => 1,
    loadNotesState: 'pristine',
    newNoteForm: getInitialFormState(),
    noteIds: { user: noteIds, followed: [], search: [] },
    hoverState: null,
})

export const getInitialNoteResultState = (): NoteResult => ({
    isEditing: false,
    areRepliesShown: false,
    isTagPickerShown: false,
    isShareMenuShown: false,
    isCopyPasterShown: false,
    editNoteForm: getInitialFormState(),
    hoverState: null,
})

const pageResultToPageData = (pageResult: AnnotPage): PageData => ({
    tags: pageResult.tags,
    lists: pageResult.lists,
    fullUrl: pageResult.fullUrl,
    fullTitle: pageResult.title,
    normalizedUrl: pageResult.url,
    favIconURI: pageResult.favIcon,
    displayTime: pageResult.displayTime,
    hasNotes: pageResult.annotsCount > 0,
})

const annotationToNoteData = (
    annotation: Annotation,
): NoteData & NoteResult => ({
    url: annotation.url,
    pageUrl: annotation.pageUrl,
    highlight: annotation.body,
    comment: annotation.comment,
    tags: annotation.tags ?? [],
    displayTime: new Date(
        annotation.lastEdited ?? annotation.createdWhen,
    ).getTime(),
    isEdited: annotation.lastEdited != null,
    ...getInitialNoteResultState(),
    editNoteForm: {
        inputValue: annotation.comment ?? '',
        tags: annotation.tags ?? [],
        isTagPickerShown: false,
    },
})

export const annotationSearchResultToState: SearchResultToState = (
    result: AnnotationsSearchResponse,
) => {
    if (!result.isAnnotsSearch) {
        return pageSearchResultToState(result)
    }

    const pageData = initNormalizedState<PageData>()
    const noteData = initNormalizedState<NoteData & NoteResult>()

    const resultState: { [day: number]: PageResultsByDay } = {}
    const dayEntries: [number, AnnotsByPageUrl][] = Object.entries(
        result.annotsByDay,
    ) as any

    for (const [day, annotsByPageUrl] of dayEntries) {
        const dayNumber = +day // Make sure to cast to `number`, as Object.entries will auto-cast it to a `string`
        const pageResults = initNormalizedState<PageResult>()

        for (const [pageUrl, annotations] of Object.entries(annotsByPageUrl)) {
            const noteIds = annotations.map((a) => a.url)

            pageResults.allIds.push(pageUrl)
            pageResults.byId[pageUrl] = getInitialPageResultState(
                pageUrl,
                noteIds,
            )

            for (const annotation of annotations) {
                pageResults.byId[pageUrl].noteIds.search.push(annotation.url)

                noteData.allIds.push(annotation.url)
                noteData.byId[annotation.url] = annotationToNoteData(annotation)
            }
        }

        resultState[dayNumber] = {
            day: dayNumber,
            pages: pageResults,
        }
    }

    for (const pageResult of result.docs) {
        const id = pageResult.url

        pageData.allIds.push(id)
        pageData.byId[id] = pageResultToPageData(pageResult)
    }<commit_msg>Open page notes by default on highlights search<commit_after>import moment from 'moment'

import {
    StandardSearchResponse,
    AnnotationsSearchResponse,
    AnnotsByPageUrl,
    AnnotPage,
} from 'src/search/background/types'
import {
    PageData,
    NormalizedState,
    PageResult,
    PageResultsByDay,
    NoteData,
    SearchResultToState,
    NoteResult,
    NoteFormState,
    RootState,
    NestedResults,
    NotesType,
} from './types'
import { Annotation } from 'src/annotations/types'
import { PAGE_SEARCH_DUMMY_DAY } from '../constants'

export const notesTypeToString = (type: NotesType): string => {
    if (type === 'user') {
        return 'Your notes'
    }
    if (type === 'followed') {
        return 'Shared with you'
    }
    return 'Results'
}

export const stringToNotesType = (str: string): NotesType => {
    if (str === 'Your notes') {
        return 'user'
    }
    if (str === 'Shared with you') {
        return 'followed'
    }
    return 'search'
}

export const formatDayGroupTime = (day: number) =>
    moment(day).calendar(null, {
        sameDay: '[Today]',
        lastDay: '[Yesterday]',
        lastWeek: '[Last] dddd',
        sameElse: 'dddd, DD MMMM, YYYY',
    })

export const initNormalizedState = <T>(): NormalizedState<T> => ({
    allIds: [],
    byId: {},
})

export const getInitialFormState = (): NoteFormState => ({
    tags: [],
    inputValue: '',
    isTagPickerShown: false,
})

export const areAllNotesShown = ({ results }: RootState): boolean => {
    for (const { pages } of Object.values(results)) {
        for (const { areNotesShown } of Object.values(pages.byId)) {
            if (!areNotesShown) {
                return false
            }
        }
    }
    return true
}

export const bindFunctionalProps = <
    Props = { [key: string]: (...args: any) => any },
    BoundProps = { [key: string]: (...args: any) => any }
>(
    props: Props,
    ...args
): BoundProps => {
    const boundProps = {} as BoundProps

    for (const [key, fn] of Object.entries(props)) {
        boundProps[key] = fn(...args)
    }

    return boundProps
}

export const getInitialPageResultState = (
    id: string,
    noteIds: string[] = [],
    extra: Partial<PageResult> = {},
): PageResult => ({
    id,
    notesType: 'user',
    areNotesShown: false,
    isTagPickerShown: false,
    isCopyPasterShown: false,
    isListPickerShown: false,
    sortingFn: (a, b) => 1,
    loadNotesState: 'pristine',
    newNoteForm: getInitialFormState(),
    noteIds: { user: noteIds, followed: [], search: [] },
    hoverState: null,
    ...extra,
})

export const getInitialNoteResultState = (): NoteResult => ({
    isEditing: false,
    areRepliesShown: false,
    isTagPickerShown: false,
    isShareMenuShown: false,
    isCopyPasterShown: false,
    editNoteForm: getInitialFormState(),
    hoverState: null,
})

const pageResultToPageData = (pageResult: AnnotPage): PageData => ({
    tags: pageResult.tags,
    lists: pageResult.lists,
    fullUrl: pageResult.fullUrl,
    fullTitle: pageResult.title,
    normalizedUrl: pageResult.url,
    favIconURI: pageResult.favIcon,
    displayTime: pageResult.displayTime,
    hasNotes: pageResult.annotsCount > 0,
})

const annotationToNoteData = (
    annotation: Annotation,
): NoteData & NoteResult => ({
    url: annotation.url,
    pageUrl: annotation.pageUrl,
    highlight: annotation.body,
    comment: annotation.comment,
    tags: annotation.tags ?? [],
    displayTime: new Date(
        annotation.lastEdited ?? annotation.createdWhen,
    ).getTime(),
    isEdited: annotation.lastEdited != null,
    ...getInitialNoteResultState(),
    editNoteForm: {
        inputValue: annotation.comment ?? '',
        tags: annotation.tags ?? [],
        isTagPickerShown: false,
    },
})

export const annotationSearchResultToState: SearchResultToState = (
    result: AnnotationsSearchResponse,
) => {
    if (!result.isAnnotsSearch) {
        return pageSearchResultToState(result)
    }

    const pageData = initNormalizedState<PageData>()
    const noteData = initNormalizedState<NoteData & NoteResult>()

    const resultState: { [day: number]: PageResultsByDay } = {}
    const dayEntries: [number, AnnotsByPageUrl][] = Object.entries(
        result.annotsByDay,
    ) as any

    for (const [day, annotsByPageUrl] of dayEntries) {
        const dayNumber = +day // Make sure to cast to `number`, as Object.entries will auto-cast it to a `string`
        const pageResults = initNormalizedState<PageResult>()

        for (const [pageUrl, annotations] of Object.entries(annotsByPageUrl)) {
            const noteIds = annotations.map((a) => a.url)

            pageResults.allIds.push(pageUrl)
            pageResults.byId[pageUrl] = getInitialPageResultState(
                pageUrl,
                noteIds,
                { areNotesShown: true },
            )

            for (const annotation of annotations) {
                pageResults.byId[pageUrl].noteIds.search.push(annotation.url)

                noteData.allIds.push(annotation.url)
                noteData.byId[annotation.url] = annotationToNoteData(annotation)
            }
        }

        resultState[dayNumber] = {
            day: dayNumber,
            pages: pageResults,
        }
    }

    for (const pageResult of result.docs) {
        const id = pageResult.url

        pageData.allIds.push(id)
        pageData.byId[id] = pageResultToPageData(pageResult)
    }
{'fraction_non_alphanumeric': 0.12143386439422008, 'fraction_numerical': 0.0003705075954057058, 'mean_word_length': 2.720537560303239, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 50, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4220671', 'n_tokens_mistral': 3350, 'n_tokens_neox': 3240, 'n_words': 744}