docbrown/app/javascript/searchableItemList/searchableItemList.js
rhymes c8abbe9d60 Pro: memberships (#3461)
* Add ProMembership model

* Implement ProMembershipsController#create

* Implement basic ProMembershipsController#show

* Add ProMembership to ledger

* Populate user history after pro subscription is created

* Add fields for expiration notifications

* Add ProMemberships::ExpirationNotifier to notify users of expiring memberships

* Add tasks for recurring jobs to notify users of expiration

* Add auto_recharge column to ProMembership

* Add ProMemberships::Biller (incomplete)

* Fix specs

* Add ProMembership to Administrate

* Fix spec

* Add has_enough_credits? to User and Organization

* Add Payments::Customer class

* Finish ProMembership::Biller functionality

* Fix ProMemberships::Creator check for credits

* Disable destroy actions for ProMembershipsController

* Correctly authenticate ProMembershipsController actions

* Make sure only pro user's history can be indexed

* Add ProMembershipsController#update action for auto recharge

* Use regular AR to save new credits and add touch to the purchaser

* Clarify Pro membership create policy

* Display information about an existing pro membership

* Add UI to show page

* Add system test for Pro membership creation

* Implement edit membership

* Make sure users with pro memberships can access history and dashboard pro

* Fix padding issue

* Show a different text for a user that has credits but not enough for Pro

* Move Pro Membership functionality inside settings

* Update Pro Membership link in email notifications

* Bust all relevant caches

* Add the Pro checkmark around the website

* Use Users::ResaveArticlesJob instead of delay

* Add/remove user from pro-members chat channel

* Use the appropriate Pro checkmark

* Remove unfinished pro elements

* Remove checkmark JS
2019-09-24 10:38:54 -07:00

115 lines
3.1 KiB
JavaScript

// Shared behavior between the reading list and history pages
import setupAlgoliaIndex from '../src/utils/algolia';
// Provides the initial state for the component
export function defaultState(options) {
const state = {
query: '',
index: null,
page: 0,
hitsPerPage: 80,
totalCount: 0,
items: [],
itemsLoaded: false,
availableTags: [],
selectedTags: [],
showLoadMoreButton: false,
};
return Object.assign({}, state, options);
}
// Starts the search when the user types in the search box
export function onSearchBoxType(event) {
const component = this;
const query = event.target.value;
const { selectedTags, statusView } = component.state;
component.setState({ page: 0, items: [] });
component.search(query, { tags: selectedTags, statusView });
}
export function toggleTag(event, tag) {
event.preventDefault();
const component = this;
const { query, selectedTags, statusView } = component.state;
const newTags = selectedTags;
if (newTags.indexOf(tag) === -1) {
newTags.push(tag);
} else {
newTags.splice(newTags.indexOf(tag), 1);
}
component.setState({ selectedTags: newTags, page: 0, items: [] });
component.search(query, { tags: newTags, statusView });
}
// Perform the initial search
export function performInitialSearch({
containerId,
indexName,
searchOptions = {},
}) {
const component = this;
const { hitsPerPage } = component.state;
const index = setupAlgoliaIndex({ containerId, indexName });
index.search('', searchOptions).then(result => {
component.setState({
items: result.hits,
totalCount: result.nbHits,
index, // set the index in the component state, to be retrieved later
itemsLoaded: true,
// show the button if the number of total results is greater
// than the number of results for the current page
showLoadMoreButton: result.nbHits > hitsPerPage,
});
});
}
// Main search function
export function search(query, { page, tags, statusView }) {
const component = this;
// allow the page number to come from the calling function
// we check `undefined` because page can be 0
const newPage = page === undefined ? component.state.page : page;
const { index, hitsPerPage, items } = component.state;
const filters = { hitsPerPage, page: newPage };
if (tags && tags.length > 0) {
filters.tagFilters = tags;
}
if (statusView) {
filters.filters = `status:${statusView}`;
}
index.search(query, filters).then(result => {
// append new items at the end
const allItems = [...items, ...result.hits];
component.setState({
query,
page: newPage,
items: allItems,
totalCount: result.nbHits,
// show the button if the number of items is lower than the number
// of available results
showLoadMoreButton: allItems.length < result.nbHits,
});
});
}
// Retrieve the results in the next page
export function loadNextPage() {
const component = this;
const { query, selectedTags, page, statusView } = component.state;
component.setState({ page: page + 1 });
component.search(query, { tags: selectedTags, statusView });
}