* 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
115 lines
3.1 KiB
JavaScript
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 });
|
|
}
|