Skip to content

Commit

Permalink
refactor: remove angular-algolia package
Browse files Browse the repository at this point in the history
  • Loading branch information
chihab committed May 19, 2023
1 parent 58a7eeb commit 0ae0842
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 453 deletions.
534 changes: 177 additions & 357 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@
"@nguniversal/express-engine": "^15.0.0",
"@ngx-loading-bar/core": "^6.0.2",
"@ngx-loading-bar/router": "^6.0.2",
"algoliasearch": "4.10.3",
"angular-instantsearch": "4.4.1",
"algoliasearch": "^4.17.0",
"core-js": "2.4.1",
"express": "^4.17.1",
"fast-glob": "^3.2.7",
Expand Down
51 changes: 51 additions & 0 deletions src/app/_core/services/search.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { createBrowserLocalStorageCache } from '@algolia/cache-browser-local-storage';
import { createFallbackableCache } from '@algolia/cache-common';
import { createInMemoryCache } from '@algolia/cache-in-memory';
import { SearchOptions, SearchResponse } from '@algolia/client-search';
import { Injectable } from '@angular/core';
import algoliasearch, { SearchClient, SearchIndex } from 'algoliasearch/lite';
import { environment } from 'src/environments/environment';

const staticSearchOptions: SearchOptions = Object.freeze({
attributesToSnippet: ['description:200'],
snippetEllipsisText: '…',
removeWordsIfNoResults: 'allOptional',
});

const version = 1

@Injectable({ providedIn: 'root' })
export class SearchService {

/**
* @description Connection to Algolia which caches requests and responses
* in localStorage when available, and in memory otherwise.
* @see https://tinyurl.com/2hfxbq3a
*/
private readonly client: SearchClient = algoliasearch(
environment.algolia.appId,
environment.algolia.apiKey,
{
responsesCache: createInMemoryCache(),
requestsCache: createInMemoryCache({ serializable: false }),
hostsCache: createFallbackableCache({
caches: [
createBrowserLocalStorageCache({
key: `${version}-${environment.algolia.appId}`,
}),
createInMemoryCache(),
],
}),
}
);

private readonly index: SearchIndex = this.client.initIndex(environment.algolia.indexName);

search<A>(
text: string,
options: SearchOptions = {}
): Promise<SearchResponse<A>> {
const finalOptions = { ...staticSearchOptions, ...options };
return this.index.search<A>(text, finalOptions);
}
}
1 change: 1 addition & 0 deletions src/app/_shared/components/input/input.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export class InputComponent implements OnChanges, OnInit, OnDestroy {
)
.subscribe(query => {
this.query.emit(query);
console.log(query);
})
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/_shared/components/navbar/navbar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ import { SearchComponent } from '../search/search.component';
})
export class NavbarComponent implements OnInit {
searchComponentClass: Promise<typeof SearchComponent> | null = null;
constructor(@Inject(PLATFORM_ID) private platformId: any) {}
constructor(@Inject(PLATFORM_ID) private platformId: any) { }
ngOnInit() {
if (isPlatformBrowser(this.platformId) && window.innerWidth > 440)
this.searchComponentClass = import('../search/search.component').then(
Expand Down
91 changes: 28 additions & 63 deletions src/app/_shared/components/search/search-widget.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,11 @@ import {
ChangeDetectionStrategy,
Component,
EventEmitter,
forwardRef,
Inject,
OnDestroy,
OnInit,
Output,
ViewEncapsulation
inject,
Output
} from '@angular/core';
import { SearchService } from '@core/services/search.service';
import { InputComponent } from '@shared/components/input/input.component';
import {
BaseWidget,
NgAisInstantSearch,
NgAisModule
} from 'angular-instantsearch';
import { connectSearchBox } from 'instantsearch.js/es/connectors';
import { EMPTY, filter, map, Observable, scan, tap } from 'rxjs';

// Generated by https://quicktype.io

export interface SearchHit {
objectID: string;
Expand All @@ -39,13 +27,12 @@ export interface SearchHit {
@Component({
selector: 'app-search-widget',
template: `<app-input
[value]="state.query"
[value]="query"
[delay]="200"
(query)="onQuery($event)"
></app-input>
<ng-container *ngIf="results$ | async as results">
<div class="drop-down" *ngIf="!!state.query">
<ng-container *ngIf="query && results">
<div class="drop-down">
<ng-template #noResults> No results. </ng-template>
<ng-container *ngIf="results.size; else noResults">
<table>
Expand All @@ -59,7 +46,7 @@ export interface SearchHit {
*ngFor="let hit of result.value"
(click)="onSearchHit(hit)"
>
<ais-highlight attribute="title" [hit]="hit"></ais-highlight>
{{ hit.title }}
</div>
</td>
</tr>
Expand Down Expand Up @@ -123,57 +110,35 @@ export interface SearchHit {
}
`
],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
// changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [InputComponent, NgAisModule, CommonModule]
imports: [InputComponent, CommonModule]
})
export class SearchWidgetComponent
extends BaseWidget
implements OnInit, OnDestroy
{
export class SearchWidgetComponent {
@Output() searchHit = new EventEmitter<SearchHit>();
parentIndex: any;
results$: Observable<Map<string, any[]>> = EMPTY;
state: {
query: string;
nbHits: number;
refine: (value: string) => void;
} = { query: '', nbHits: 0, refine: () => {} };
constructor(
@Inject(forwardRef(() => NgAisInstantSearch))
public instantSearchInstance: NgAisInstantSearch
) {
super('SearchBox');
this.createWidget(connectSearchBox as any, {});
}

ngOnInit() {
super.ngOnInit();
this.results$ = this.instantSearchInstance.change.pipe(
filter(() => !!this.state.query),
map(({ results }: { results: any }) => {
const groupedBySession: Map<string, any[]> = (
results?.hits || []
).reduce((grouped: Map<string, any[]>, hit: any) => {
const titles = grouped.get(hit.session.title) || [];
if (!titles.length) {
grouped.set(hit.session.title, titles);
}
titles.push(hit);
return grouped;
}, new Map<string, any[]>());
return groupedBySession;
})
);
}
searchService = inject(SearchService);
results!: Map<string, any[]>;
query = "";

onQuery(query: string) {
this.state.refine(query);
async onQuery(query: string) {
this.query = query;
const results = await this.searchService.search(query);
this.results = (
results?.hits || []
).reduce((grouped: Map<string, any[]>, hit: any) => {
const titles = grouped.get(hit.session.title) || [];
if (!titles.length) {
grouped.set(hit.session.title, titles);
}
titles.push(hit);
return grouped;
}, new Map<string, any[]>());
console.log(query, this.results);
}

onSearchHit(hit: SearchHit) {
this.query = '';
this.searchHit.emit(hit);
this.state.query = '';
}
}
26 changes: 2 additions & 24 deletions src/app/_shared/components/search/search.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,29 @@ import {
} from '@angular/core';
import { Router, RouterModule } from '@angular/router';
import { timeToSeconds } from '@helpers/time';
import algoliasearch from 'algoliasearch/lite';
import { NgAisModule } from 'angular-instantsearch';
import { environment } from 'src/environments/environment';
import { InputComponent } from '../input/input.component';
import { SearchHit, SearchWidgetComponent } from './search-widget.component';

@Component({
selector: 'app-search',
template: `
<ais-instantsearch *ngIf="isBrowser" [config]="config">
<ais-configure
[searchParameters]="{
attributesToSnippet: ['description:10'],
snippetEllipsisText: '…',
removeWordsIfNoResults: 'allOptional'
}"
>
</ais-configure>
<app-search-widget (searchHit)="onSearchHit($event)"></app-search-widget>
</ais-instantsearch>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [
CommonModule,
RouterModule,
InputComponent,
SearchWidgetComponent,
NgAisModule
SearchWidgetComponent
]
})
export class SearchComponent {
config: any = {
searchClient: algoliasearch(
environment.algolia.appId,
environment.algolia.apiKey
),
indexName: environment.algolia.indexName,
routing: false
};
public isBrowser = isPlatformBrowser(this.platformId);
constructor(
@Inject(PLATFORM_ID) private platformId: any,
private router: Router
) {}
) { }

onSearchHit(hit: SearchHit) {
this.router.navigate([`/playlist/${hit.session.videoId}`], {
Expand Down
22 changes: 16 additions & 6 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"compilerOptions": {
"baseUrl": "./",
"importHelpers": true,
"target": "es2020",
"target": "es2022",
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
Expand All @@ -13,18 +13,28 @@
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"useDefineForClassFields": false,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"skipLibCheck": true,
"module": "es2020",
"lib": ["es2018", "dom"],
"lib": [
"es2018",
"dom"
],
"paths": {
"@core/*": ["src/app/_core/*"],
"@shared/*": ["src/app/_shared/*"],
"@helpers/*": ["src/app/_helpers/*"]
"@core/*": [
"src/app/_core/*"
],
"@shared/*": [
"src/app/_shared/*"
],
"@helpers/*": [
"src/app/_helpers/*"
]
}
},
"angularCompilerOptions": {
Expand All @@ -33,4 +43,4 @@
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}
}

0 comments on commit 0ae0842

Please sign in to comment.