|
| 1 | +import React, { createContext, useContext, useEffect, useMemo, useReducer, useState } from 'react' |
| 2 | +import { TemplateCategory, TemplateExplorerContextType, TemplateExplorerWizardAction, TemplateItem } from '../types/template-explorer-types' |
| 3 | +import { initialState, templateExplorerReducer } from '../reducers/template-explorer-reducer' |
| 4 | +import { metadata, templatesRepository } from '../src/utils/helpers' |
| 5 | +import { AppContext } from '@remix-ui/app' |
| 6 | + |
| 7 | +export const TemplateExplorerContext = createContext<TemplateExplorerContextType>({} as any) |
| 8 | + |
| 9 | +export const TemplateExplorerProvider = ({ children }: { children: React.ReactNode }) => { |
| 10 | + // const [templateRepository, setTemplateRepository] = useState<TemplateCategory[]>([]) |
| 11 | + // const [metadata, setMetadata] = useState<any[]>([]) |
| 12 | + // const [selectedTag, setSelectedTag] = useState<string | null>(null) |
| 13 | + // const [recentBump, setRecentBump] = useState<number>(0) |
| 14 | + const [state, dispatch] = useReducer(templateExplorerReducer, initialState) |
| 15 | + const appContext = useContext(AppContext) |
| 16 | + |
| 17 | + useEffect(() => { |
| 18 | + dispatch({ type: TemplateExplorerWizardAction.SET_METADATA, payload: metadata }) |
| 19 | + dispatch({ type: TemplateExplorerWizardAction.SET_TEMPLATE_REPOSITORY, payload: templatesRepository }) |
| 20 | + |
| 21 | + }, []) |
| 22 | + |
| 23 | + const allTags = useMemo((): string[] => { |
| 24 | + const tags: string[] = [] |
| 25 | + |
| 26 | + if (state.templateRepository && Array.isArray(state.templateRepository)) { |
| 27 | + state.templateRepository.forEach((template: any) => { |
| 28 | + if (template && template.items && Array.isArray(template.items)) { |
| 29 | + template.items.forEach((item: any) => { |
| 30 | + if (item && item.tagList && Array.isArray(item.tagList)) { |
| 31 | + item.tagList.forEach((tag: string) => { |
| 32 | + if (typeof tag === 'string' && !tags.includes(tag)) { |
| 33 | + tags.push(tag) |
| 34 | + } |
| 35 | + }) |
| 36 | + } |
| 37 | + }) |
| 38 | + } |
| 39 | + }) |
| 40 | + } |
| 41 | + |
| 42 | + return tags.sort() |
| 43 | + }, []) |
| 44 | + |
| 45 | + // Recent templates (before filteredTemplates so it can be referenced later) |
| 46 | + const recentTemplates = useMemo((): TemplateItem[] => { |
| 47 | + try { |
| 48 | + const raw = typeof window !== 'undefined' ? window.localStorage.getItem(RECENT_KEY) : null |
| 49 | + const list: string[] = raw ? JSON.parse(raw) : [] |
| 50 | + const items: TemplateItem[] = [] |
| 51 | + if (Array.isArray(state.templateRepository)) { |
| 52 | + list.forEach((val) => { |
| 53 | + for (const group of state.templateRepository as any[]) { |
| 54 | + if (group && Array.isArray(group.items)) { |
| 55 | + const found = group.items.find((it: any) => it && it.value === val) |
| 56 | + if (found) { |
| 57 | + items.push(found) |
| 58 | + break |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | + }) |
| 63 | + } |
| 64 | + //tag filter |
| 65 | + const filtered = state.selectedTag |
| 66 | + ? items.filter((it: any) => it && Array.isArray(it.tagList) && it.tagList.includes(state.selectedTag)) |
| 67 | + : items |
| 68 | + return filtered |
| 69 | + } catch (e) { |
| 70 | + return [] |
| 71 | + } |
| 72 | + }, [state.selectedTag, state.recentBump]) |
| 73 | + |
| 74 | + // Filter templates based on selected tag |
| 75 | + const filteredTemplates = useMemo((): TemplateCategory[] => { |
| 76 | + if (!state.selectedTag || !state.templateRepository || !Array.isArray(state.templateRepository)) { |
| 77 | + return state.templateRepository as TemplateCategory[] || [] |
| 78 | + } |
| 79 | + |
| 80 | + return (state.templateRepository as TemplateCategory[]).map((template: any) => ({ |
| 81 | + ...template, |
| 82 | + items: template.items.filter((item: any) => |
| 83 | + item && item.tagList && Array.isArray(item.tagList) && item.tagList.includes(state.selectedTag) |
| 84 | + ) |
| 85 | + })).filter((template: any) => template && template.items && template.items.length > 0) |
| 86 | + }, [state.selectedTag]) |
| 87 | + |
| 88 | + // Dedupe templates across the whole page and avoid showing ones already in recents |
| 89 | + const dedupedTemplates = useMemo((): TemplateCategory[] => { |
| 90 | + const recentSet = new Set<string>((recentTemplates || []).map((t: any) => t && t.value)) |
| 91 | + const seen = new Set<string>() |
| 92 | + const makeUniqueItems = (items: any[]) => { |
| 93 | + const unique: any[] = [] |
| 94 | + for (const it of items || []) { |
| 95 | + const val = it && it.value |
| 96 | + if (!val) continue |
| 97 | + if (recentSet.has(val)) continue |
| 98 | + if (seen.has(val)) continue |
| 99 | + seen.add(val) |
| 100 | + unique.push(it) |
| 101 | + } |
| 102 | + return unique |
| 103 | + } |
| 104 | + return (filteredTemplates || []).map((group: any) => ({ |
| 105 | + ...group, |
| 106 | + items: makeUniqueItems(group && group.items ? group.items : []) |
| 107 | + })).filter((g: any) => g && g.items && g.items.length > 0) |
| 108 | + }, [filteredTemplates, recentTemplates]) |
| 109 | + |
| 110 | + const handleTagClick = (tag: string) => { |
| 111 | + dispatch({ type: TemplateExplorerWizardAction.SET_SELECTED_TAG, payload: state.selectedTag === tag ? null : tag }) |
| 112 | + } |
| 113 | + |
| 114 | + const clearFilter = () => { |
| 115 | + dispatch({ type: TemplateExplorerWizardAction.SET_SELECTED_TAG, payload: null }) |
| 116 | + } |
| 117 | + |
| 118 | + const RECENT_KEY = 'remix.recentTemplates' |
| 119 | + |
| 120 | + const addRecentTemplate = (template: TemplateItem) => { |
| 121 | + try { |
| 122 | + const raw = typeof window !== 'undefined' ? window.localStorage.getItem(RECENT_KEY) : null |
| 123 | + const list: string[] = raw ? JSON.parse(raw) : [] |
| 124 | + const filtered = list.filter((v) => v !== template.value) |
| 125 | + filtered.unshift(template.value) |
| 126 | + const trimmed = filtered.slice(0, 4) |
| 127 | + if (typeof window !== 'undefined') window.localStorage.setItem(RECENT_KEY, JSON.stringify(trimmed)) |
| 128 | + dispatch({ type: TemplateExplorerWizardAction.SET_RECENT_BUMP, payload: state.recentBump + 1 }) |
| 129 | + } catch (e) { |
| 130 | + |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | + return ( |
| 135 | + <TemplateExplorerContext.Provider value={{ templateRepository: state.templateRepository, metadata: state.metadata, selectedTag: state.selectedTag, recentTemplates, filteredTemplates, dedupedTemplates, handleTagClick, clearFilter, addRecentTemplate, RECENT_KEY, allTags }}> |
| 136 | + {children} |
| 137 | + </TemplateExplorerContext.Provider> |
| 138 | + ) |
| 139 | +} |
0 commit comments