Creating new config to set Title of Loklak Webpages

For a Search Engine and Progressive Web Application (PWA) like Loklak, it is important to change the title of each web page with the appropriate query term or configuration of the current active page (A great title can result in higher click-throughs and more traffic to the site, and hence its followed by search engines like Google). For solving this problem, I am writing this blog post which creates a new configuration to change the title dynamically across the whole project by using just one Action thereby maximizing reusability of the code and minimizing duplication. Create Action for title We would need to create a new separate title action following the architecture in loklak. Create a new title.ts file under Actions with the following code: import { Action } from ‘@ngrx/store’; export const ActionTypes = { SET_TITLE: ‘[Title] SET TITLE’ }; export class SetTitleAction implements Action { type = ActionTypes.SET_TITLE; constructor (public payload: string) { } } export type Actions = SetTitleAction; Create reducer for title For accessing the current title of the webpage, it is important to create a reducer function for the title following the pattern in loklak. import * as titleAction from ‘../actions/title’; export interface State { title: string; } export const initialState: State = { title: '', }; export function reducer(state: State = initialState, action: titleAction.Actions): State { switch (action.type) { case titleAction.ActionTypes.SET_TITLE: { const titleObt: string = action.payload; return Object.assign({}, state, { title: titleObt }); } default: { return state; } } } export const getTitle = (state: State) => state.title; Create selector and add reducer function to root reducer The title can be accessed from the current state from store when it would be added to the root state and reducer. Firstly import the title reducer created above and define the title property in root reducer and create a selector for the same. import * as fromTitle from ‘./title’; export interface State { … title: fromTitle.State; } export const reducers: ActionReducerMap<State> = { … title: fromTitle.reducer }; export const getTitleState = (state: State) => state.title; export const getTitle = createSelector(getTitleState, fromTitle.getTitle); Create Effect to change actual title Now comes the actual part which controls the changing of webpage title using ‘Title’ property of Platform-Browser. Create a new title.effects.ts file under effects using pattern in loklak. import { Injectable } from ‘@angular/core’; import { Title } from ‘@angular/platform-browser’; import { Effect, Actions, ofType } from ‘@ngrx/effects’; import { Observable } from ‘rxjs’; import { map } from ‘rxjs/operators’; import * as titleAction from ‘../actions/title’; @Injectable() export class SetTitleEffects { @Effect({ dispatch: false }) resetTitle$: Observable<void> = this.actions$ .pipe( ofType( titleAction.ActionTypes .SET_TITLE ), map((action) => { const title = action[‘payload’]; this.titleService .setTitle(title); }) ); constructor( private actions$: Actions, private titleService: Title ) { } }   Here the ‘payload’ actually contains the query searched by the user, which will be used update the title. Note: Before adding a new Effect, the old Effect needs to be removed. After creating the Effect, add it into app.module.ts and…

Continue ReadingCreating new config to set Title of Loklak Webpages

Dispatching a search action to get result for a query in Loklak Search

For a new comer in Loklak, it’s not really easy to understand the complete codebase to just get results for a query without explicitly using navigation route i.e. ‘/search’. In order to help newcomers to easily understand a simple way to get results for a query in the codebase, I am writing this blog post. Setting up a query A sample searchQuery will be created of type Query. And following discussion will provide a way to get the results for a sample query - ‘from:Fossasia’ (Last 30 days tweets from FOSSASIA). searchQuery: Query = { displayString: ‘from:FOSSASIA, queryString: ‘from:FOSSASIA’, routerString: ‘from:FOSSASIA’, filter: { video: false, image: false }, location: null, timeBound: { since: null, until: null }, from: true }   Process discussed here can be used to get results in any class file within Loklak Search. Using necessary imports First step would be to add all necessary imports. import { OnInit } from ‘@angular/core’; import { Store } from ‘@ngrx/store’; import { Observable } from ‘rxjs/Observable’; import * as fromRoot from ‘../../reducers’; import { Query } from ‘../../models/query’; import * as searchAction from ‘../../actions/api’; import { ApiResponseResult } from ‘../../models/api-response’;   Note: No need to import OnInit, if the class already implements it (OnInit is one of the Angular’s Lifecycle Hooks which controls the changes when the component containing it gets loaded initially). The Query is used to define the type of searchQuery variable created above. And ApiResponseResult is the type of result which will be obtained on searching the searchQuery. Declaring response variables and store object Next step would be to declare the variables with the type of response and create store object of type Store with the current State of the application. public isSearching$: Observable<boolean>; public apiResponseResults$: Observable<ApiResponseResult[]>; constructor( private store: Store<fromRoot.State> ) { }   isSearching$ holds an Observable which can be subscribed to get a boolean value which states the current status of searching of query. apiResponseResults$ holds the actual response of query. Dispatching a SearchAction The most crucial part comes here to dispatch a SearchAction with the created searchQuery as payload. A new method would be created with void as return type to dispatch the SearchAction. private queryFromURL(): void { this.store.dispatch( new searchAction.SearchAction(this.searchQuery) ); } Selecting and storing results from store A new method would be created to store the current searching status along with the response of input query in isSearching$ and apiResponseResults$ respectively. Now the selector for the reducer function corresponding to the search and api response entities would be called. private getDataFromStore(): void { this.isSearching$ = this.store.select(fromRoot.getSearchLoading); this.apiResponseResults$ = this.store.select( fromRoot.getApiResponseEntities ); } Dispatching and Selecting on view Init create ngOnInit() method, If you have not already done and call the respective dispatching and select method inside it respectively. ngOnInit() { this.queryFromURL(); this.getDataFromStore(); } How should one test the results? Call these lines inside any method and look for the results on browser console window. console.log(this.isSearching$); console.log(this.apiResponseResults$); Resources Loklak Search: Ngrx pattern Ngrx platform: Official Docs

Continue ReadingDispatching a search action to get result for a query in Loklak Search

Feeds Moderation in loklak Media Wall

Loklak Media Wall provides client side filters for entities received from loklak status.json API like blocking feeds from a particular user, removing duplicate feeds, hiding a particular feed post for moderating feeds. To implement it, we need pure functions which remove the requested type of feeds and returns a new array of feeds. Moreover, the original set of data must also be stored in an array so that if filters are removed, the requested data is provided to the user In this blog, I would be explaining how I implemented client side filters to filter out a particular type of feeds and provide the user with a cleaner data as requested. Types of filters There are four client-side filters currently provided by Loklak media wall: Profanity Filter: Checks for the feeds that might be offensive and removes it. Remove Duplicate: Removes duplicate feeds and the retweets from the original feeds Hide Feed: Removes a particular feed from the feeds Block User: Blocks a User and removes all the feeds from the particular user It is also important to ensure that on pagination, new feeds are filtered out based on the previous user requested moderation. Flow Chart The flow chart explains how different entities received from the server is filtered and how original set of entities is maintained so that if the user removes the filter, the original filtered entities are recovered. Working Profanity Filter To avoid any obscene language used in the feed status to be shown up on media wall and providing a rather clean data, profanity filter can be used. For this filter, loklak search.json APIs provide a field classifier_profanity which states if there is some swear word is used in the status. We can check for the value of this field and filter out the feed accordingly. export function profanityFilter(feeds: ApiResponseResult[]): ApiResponseResult[] { const filteredFeeds: ApiResponseResult[] = []; feeds.forEach((feed) => { if ( feed.classifier_language !== null && feed.classifier_profanity !== undefined ) { if (feed.classifier_profanity !== 'sex' &&  feed.classifier_profanity !== 'swear') { filteredFeeds.push(feed); } } else { filteredFeeds.push(feed); } }); return filteredFeeds || feeds; } Here, we check if the classifier_profanity field is either not ‘swear’ or ‘sex’ which clearly classifies the feeds and we can push the status accordingly. Moreover, if no classifier_profanity field is provided for a particular field, we can push the feed in the filtered feeds. Remove Duplicate Remove duplicate filter removes the tweets that are either retweets or even copy of some feed and return just one original feed. We need to compare field id_str which is the status id of the feed and remove the duplicate feeds. For this filter, we need to create a map and compare feeds on map object and remove the duplicate feeds iteratively and return the array of feeds with unique elements. export function removeDuplicateCheck(feeds: ApiResponseResult[]): ApiResponseResult[] { const map = { }; const filteredFeeds: ApiResponseResult[] = []; const newFeeds: ApiResponseResult[] = feeds; let v: string; for (let a = 0; a < feeds.length; a++) { v…

Continue ReadingFeeds Moderation in loklak Media Wall