diff --git a/src/actions/catalog.action.ts b/src/actions/catalog.action.ts index d6e7a79c..512544d9 100644 --- a/src/actions/catalog.action.ts +++ b/src/actions/catalog.action.ts @@ -41,6 +41,7 @@ export const CATALOG_SEARCH_UPDATE_KEYWORD = `${CATALOG_SEARCH_ACTIONS}/UpdateKe export const CATALOG_SEARCH_CLEAR_FEATURES = `${CATALOG_SEARCH_ACTIONS}/ClearFeatures`; export const CATALOG_SEARCH_RESET_FEATURES = `${CATALOG_SEARCH_ACTIONS}/ResetFeatures`; export const CATALOG_SEARCH_UPDATE_ORGANIZATION = `${CATALOG_SEARCH_ACTIONS}/UpdateOrganization`; +export const CATALOG_SEARCH_UPDATE_BUILD_SUPPORT = `${CATALOG_SEARCH_ACTIONS}/UpdateBuildSupport`; export const CatalogSearchActions = { updateFeatures: ( value: IKeyboardFeatures | IConditionNotSelected, @@ -77,6 +78,12 @@ export const CatalogSearchActions = { value: organizationId, }; }, + updateBuildSupport: (buildSupport: boolean) => { + return { + type: CATALOG_SEARCH_UPDATE_BUILD_SUPPORT, + value: buildSupport, + }; + }, }; export const CATALOG_KEYBOARD_ACTIONS = `@CatalogKeyboard`; diff --git a/src/actions/storage.action.ts b/src/actions/storage.action.ts index e628c6ca..0d73757c 100644 --- a/src/actions/storage.action.ts +++ b/src/actions/storage.action.ts @@ -1081,14 +1081,18 @@ export const storageActionsThunk = { const features = catalog.search.features; const keyword = catalog.search.keyword; const organizationId = catalog.search.organizationId; + const buildSupport = catalog.search.buildSupport; let searchKeyboardsByFeaturesResult = await storage.instance!.searchKeyboards(features, organizationId); if (isSuccessful(searchKeyboardsByFeaturesResult)) { + // Keyword search const definitionDocs = searchKeyboardsByFeaturesResult.value.documents.filter((doc) => doc.name.toLowerCase().includes(keyword.toLowerCase()) ); - const filteredDocs = definitionDocs.filter((doc) => { + + // Feature search + let filteredDocs = definitionDocs.filter((doc) => { if (features.length === 0) return true; let allMatch = true; @@ -1099,6 +1103,29 @@ export const storageActionsThunk = { return allMatch; }); + // Build support search + if (buildSupport) { + const buildableFirmwaresResult = + await storage.instance!.fetchAllBuildableFirmwares(); + if (isError(buildableFirmwaresResult)) { + console.error(buildableFirmwaresResult.cause); + dispatch( + NotificationActions.addError( + buildableFirmwaresResult.error, + buildableFirmwaresResult.cause + ) + ); + return; + } + const buildableFirmwareIds = buildableFirmwaresResult.value.map( + (buildableFirmware) => buildableFirmware.keyboardDefinitionId + ); + filteredDocs = filteredDocs.filter((doc) => { + return buildableFirmwareIds.includes(doc.id); + }); + } + + // Sort by image filteredDocs.sort((a, b) => { const countA = a.imageUrl ? 1 : 0; // sort higher with a image const countB = b.imageUrl ? 1 : 0; // sort higher with a image @@ -1110,6 +1137,7 @@ export const storageActionsThunk = { } }); + // Fetch related organizations const organizationIds: string[] = filteredDocs .filter((doc) => doc.authorType === 'organization') .map((doc) => doc.organizationId) @@ -1162,6 +1190,9 @@ export const storageActionsThunk = { if (organizationId) { query.organizationId = organizationId; } + if (buildSupport) { + query.buildSupport = 'true'; + } history.replaceState(null, 'Remap', `/catalog?${qs.stringify(query)}`); dispatch(CatalogAppActions.updatePhase('list')); }, diff --git a/src/components/catalog/Catalog.container.ts b/src/components/catalog/Catalog.container.ts index 15bcc268..b27861ba 100644 --- a/src/components/catalog/Catalog.container.ts +++ b/src/components/catalog/Catalog.container.ts @@ -78,6 +78,9 @@ const mapDispatchToProps = (_dispatch: any) => { ) ); } + if (params.buildSupport) { + _dispatch(CatalogSearchActions.updateBuildSupport(true)); + } if (params.features) { (params.features as string).split(',').forEach((feature: string) => { if (ALL_KEY_COUNT_TYPE.includes(feature as IKeyboardKeyCountType)) { diff --git a/src/components/catalog/search/CatalogSearch.container.ts b/src/components/catalog/search/CatalogSearch.container.ts index 14c2be84..58310f1d 100644 --- a/src/components/catalog/search/CatalogSearch.container.ts +++ b/src/components/catalog/search/CatalogSearch.container.ts @@ -15,6 +15,7 @@ const mapStateToProps = (state: RootState) => { searchResult: state.entities.searchResultKeyboardDocuments, keyword: state.catalog.search.keyword, organizationMap: state.entities.searchResultOrganizationMap, + buildSupport: state.catalog.search.buildSupport, }; }; export type CatalogSearchStateType = ReturnType; diff --git a/src/components/catalog/search/CatalogSearch.tsx b/src/components/catalog/search/CatalogSearch.tsx index a9de57e3..bac42797 100644 --- a/src/components/catalog/search/CatalogSearch.tsx +++ b/src/components/catalog/search/CatalogSearch.tsx @@ -122,6 +122,7 @@ class CatalogSearch extends React.Component< )} @@ -139,6 +140,7 @@ class CatalogSearch extends React.Component< open={this.state.showSearchDialog} keyword={this.props.keyword!} features={this.props.features!} + buildSupport={this.props.buildSupport!} onClose={this.closeSearchDialog.bind(this)} onSubmit={this.submitSearchDialog.bind(this)} /> diff --git a/src/components/catalog/search/CatalogSearchDialog.tsx b/src/components/catalog/search/CatalogSearchDialog.tsx index cc68ecac..1e82cc99 100644 --- a/src/components/catalog/search/CatalogSearchDialog.tsx +++ b/src/components/catalog/search/CatalogSearchDialog.tsx @@ -8,11 +8,13 @@ import { IKeyboardFeatures } from '../../../store/state'; type CatalogSearchDialogState = { features: IKeyboardFeatures[]; keyword: string; + buildSupport: boolean; }; type CatalogSearchDialogProps = { open: boolean; features: IKeyboardFeatures[]; keyword: string; + buildSupport: boolean; onClose: ( // eslint-disable-next-line no-unused-vars originalKeyword: string, @@ -67,6 +69,7 @@ export default class CatalogSearchDialog extends React.Component< onSubmit={this.props.onSubmit.bind(this)} keyword={this.props.keyword} features={this.props.features} + buildSupport={this.props.buildSupport} /> ); diff --git a/src/components/catalog/search/CatalogSearchForm.container.ts b/src/components/catalog/search/CatalogSearchForm.container.ts index d1b8aa52..b39a7d2f 100644 --- a/src/components/catalog/search/CatalogSearchForm.container.ts +++ b/src/components/catalog/search/CatalogSearchForm.container.ts @@ -40,6 +40,9 @@ const mapDispatchToProps = (_dispatch: any) => { updateOrganizationId: (organizationId: string | undefined) => { _dispatch(CatalogSearchActions.updateOrganizationId(organizationId)); }, + updateBuildSupport: (buildSupport: boolean) => { + _dispatch(CatalogSearchActions.updateBuildSupport(buildSupport)); + }, }; }; export type CatalogSearchFormActionsType = ReturnType< diff --git a/src/components/catalog/search/CatalogSearchForm.tsx b/src/components/catalog/search/CatalogSearchForm.tsx index eb803f89..7a98039b 100644 --- a/src/components/catalog/search/CatalogSearchForm.tsx +++ b/src/components/catalog/search/CatalogSearchForm.tsx @@ -42,6 +42,7 @@ type CatalogSearchFormState = {}; type OwnProps = { features: IKeyboardFeatures[]; keyword: string; + buildSupport: boolean; onSubmit?: () => void; }; @@ -138,6 +139,11 @@ export default class CatalogSearchForm extends React.Component< this.props.updateFeatures!(value, ALL_WIRELESS_TYPE); } + onChangeBuildSupport(event: SelectChangeEvent) { + const value = event.target.value === 'buildSupport'; + this.props.updateBuildSupport!(value); + } + // eslint-disable-next-line no-undef onChangeKeyword(event: React.ChangeEvent) { this.props.updateKeyword!(event.target.value); @@ -209,6 +215,23 @@ export default class CatalogSearchForm extends React.Component< +
+ + + Build Firmware Support + + + +
diff --git a/src/services/provider/Firebase.ts b/src/services/provider/Firebase.ts index f7b83caf..cb99d1bd 100644 --- a/src/services/provider/Firebase.ts +++ b/src/services/provider/Firebase.ts @@ -1652,6 +1652,37 @@ export class FirebaseProvider implements IStorage, IAuth { } } + async fetchAllBuildableFirmwares(): Promise> { + try { + const querySnapshot = await this.db + .collection('build') + .doc('v1') + .collection('firmwares') + .where('enabled', '==', true) + .get(); + return successResultOf( + querySnapshot.docs.map((doc) => { + return { + keyboardDefinitionId: doc.data()!.keyboardDefinitionId, + uid: doc.data()!.uid, + enabled: doc.data()!.enabled, + defaultBootloaderType: doc.data()!.defaultBootloaderType, + qmkFirmwareVersion: doc.data()!.qmkFirmwareVersion, + keyboardDirectoryName: doc.data()!.keyboardDirectoryName || '', + createdAt: doc.data()!.createdAt.toDate(), + updatedAt: doc.data()!.updatedAt.toDate(), + }; + }) + ); + } catch (error) { + console.error(error); + return errorResultOf( + `Fetching all buildable firmwares failed: ${error}`, + error + ); + } + } + async fetchBuildableFirmwareFiles( keyboardDefinitionId: string, fileType: IBuildableFirmwareFileType diff --git a/src/services/storage/Storage.ts b/src/services/storage/Storage.ts index 4bf2b0a3..849d40fb 100644 --- a/src/services/storage/Storage.ts +++ b/src/services/storage/Storage.ts @@ -527,5 +527,6 @@ export interface IStorage { taskId: string, description: string ): Promise; + fetchAllBuildableFirmwares(): Promise>; } /* eslint-enable no-unused-vars */ diff --git a/src/store/reducers.ts b/src/store/reducers.ts index f3d4ae98..1f7c7c7f 100644 --- a/src/store/reducers.ts +++ b/src/store/reducers.ts @@ -172,6 +172,7 @@ import { CATALOG_SEARCH_ACTIONS, CATALOG_SEARCH_CLEAR_FEATURES, CATALOG_SEARCH_RESET_FEATURES, + CATALOG_SEARCH_UPDATE_BUILD_SUPPORT, CATALOG_SEARCH_UPDATE_FEATURES, CATALOG_SEARCH_UPDATE_KEYWORD, CATALOG_SEARCH_UPDATE_ORGANIZATION, @@ -1080,6 +1081,10 @@ const catalogSearchReducer = ( draft.catalog.search.organizationId = action.value; break; } + case CATALOG_SEARCH_UPDATE_BUILD_SUPPORT: { + draft.catalog.search.buildSupport = action.value; + break; + } } }; diff --git a/src/store/state.ts b/src/store/state.ts index 2a30128d..507c4102 100644 --- a/src/store/state.ts +++ b/src/store/state.ts @@ -409,6 +409,7 @@ export type RootState = { features: IKeyboardFeatures[]; keyword: string; organizationId: string | undefined; + buildSupport: boolean; }; keyboard: { keymaps: { @@ -663,6 +664,7 @@ export const INIT_STATE: RootState = { features: [], keyword: '', organizationId: undefined, + buildSupport: false, }, keyboard: { keymaps: [],