- import Ember from 'ember';
- import layout from './template';
-
- import config from 'ember-get-config';
- import { task } from 'ember-concurrency';
-
- import Analytics from '../../mixins/analytics';
- import hostAppName from '../../mixins/host-app-name';
-
- /**
- * @module ember-osf
- * @submodule components
- */
-
- /**
- * Discover-page component. Builds a search interface utilizing SHARE.
- * See retraction-watch, registries, and preprints discover pages for working examples.
- *
- * Majority adapted from Ember-SHARE https://github.com/CenterForOpenScience/ember-share, with additions from PREPRINTS
- * and REGISTRIES discover pages. Original Ember-SHARE facets and PREPRINTS/REGISTRIES facets behave differently at this time.
- * You can build a discover-page that uses Ember-SHARE type facets -OR- PREPRINTS/REGISTRIES type facets. Would not recommend
- * mixing until code is combined.
- *
- * How to Use:
- * Pass in custom text like searchPlaceholder. The facets property will enable you to customize the filters
- * on the left-hand side of the discover page. Sort options are the sort dropdown options. Each query parameter must be passed in individually,
- * so they are reflected in the URL. Logo and custom colors must be placed in the consuming application's stylesheet. Individual components
- * can additionally be overridden in your application.
- *
- * Sample usage:
- * ```handlebars
- *{{discover-page
- * consumingService=consumingService
- * searchPlaceholder=searchPlaceholder
- * detailRoute=detailRoute
- * discoverHeader=discoverHeader
- * themeProvider=themeProvider
- *
- * sortOptions=sortOptions
- * filterReplace=filterReplace
- * whiteListedProviders=whiteListedProviders
- * fetchedProviders=externalProviders
- *
- * facets=facets
- * results=results
- * numberOfResults=numberOfResults
- * aggregations=aggregations
- * queryParamsState=queryParamsState
- *
- * showActiveFilters=showActiveFilters
- * loading=fetchData.isRunning
- *
- * clearFilters=(action 'clearFilters')
- * search=(action 'search')
- *
- * size=size
- * page=page
- * q=q
- * sort=sort
- * }}
- * {{!-- plus any query params (e.g. provider=provider) --}}
- * ```
- * @class discover-page
- */
-
- const MAX_SOURCES = 500;
-
- export default Ember.Component.extend(Analytics, hostAppName, {
- layout,
- currentUser: Ember.inject.service('current-user'),
- theme: Ember.inject.service(),
- i18n: Ember.inject.service(),
-
- classNames: ['discover-page'],
-
- // ************************************************************
- // PROPERTIES
- // ************************************************************
-
- /**
- * Size query parameter. If "size" is one of your query params, it must be passed to the component so it can be updated.
- * @property {Integer} size
- * @default 10
- */
- size: 10,
-
- /**
- * Sort query parameter. If "sort" is one of your query params, it must be passed to the component so it can be updated.
- * @property {String} sort
- * @default ''
- */
- sort: '',
-
- /**
- * Page query parameter. If "page" is one of your query params, it must be passed to the component so it can be updated.
- * @property {Integer} page
- * @default 1
- */
- page: 1,
-
- /**
- * q query parameter. If "q" is one of your query params, it must be passed to the component so it can be updated.
- * @property {String} q
- * @default ''
- */
- q: '',
-
- /**
- * Name of detail route for consuming application, like "content" or "detail". Override if search result title should link to detail route.
- * @property {String} detailRoute
- */
- detailRoute: null,
-
- /**
- * Text header for top of discover page.
- * @property {String} discoverHeader
- */
- discoverHeader: null,
-
-
- /**
- * For PREPRINTS ONLY. Pass in the providers fetched in preprints app so they can be used in the provider carousel
- * @property {Object} fetchedProviders
- */
- fetchedProviders: null,
-
- /**
- * For PREPRINTS and REGISTRIES. A mapping of filter names for front-end display. Ex. {OSF: 'OSF Preprints'}.
- * @property {Object} filterReplace
- */
- filterReplace: {},
-
- /**
- * For PREPRINTS and REGISTRIES. Displays activeFilters box above search facets.
- * @property {boolean} showActiveFilters
- */
- showActiveFilters: false,
-
- showLuceneHelp: false,
-
- numberOfResults: 0,
- numberOfSources: 0,
-
- results: Ember.ArrayProxy.create({ content: [] }), // Results from SHARE query
-
- /**
- * Sort dropdown options - Array of dictionaries. Each dictionary should have display and sortBy keys.
- * @property {Array} sortOptions
- * @default [{
- display: 'Relevance',
- sortBy: ''
- }]
- */
- sortOptions: [{
- display: 'Relevance',
- sortBy: ''
- }, {
- display: 'Date Updated (Desc)',
- sortBy: '-date_updated'
- }, {
- display: 'Date Updated (Asc)',
- sortBy: 'date_updated'
- }, {
- display: 'Ingest Date (Asc)',
- sortBy: 'date_created'
- }, {
- display: 'Ingest Date (Desc)',
- sortBy: '-date_created'
- }],
-
- /**
- * themeProvider
- * @property {Object} Preprint provider loaded from theme service. Should be passed from consuming service so it is loaded before SHARE is queried.
- * @default ''
- */
- themeProvider: null,
-
-
- // ************************************************************
- // COMPUTED PROPERTIES
- // ************************************************************
-
- domainRedirectProviders: Ember.computed(function() {
- let providerDomains = [];
- let providers = this.get('fetchedProviders');
- for (let providerVal of providers) {
- if (providerVal.get('domain') && providerVal.get('domainRedirectEnabled') === true) {
- providerDomains.push(providerVal.get('domain'));
- }
- }
- return providerDomains;
- }),
-
- clampedPages: Ember.computed('totalPages', 'size', function() {
- // requesting over 10000 will error due to elastic limitations
- // https://www.elastic.co/guide/en/elasticsearch/guide/current/pagination.html
- let maxPages = Math.ceil(10000 / this.get('size'));
- let totalPages = this.get('totalPages');
-
- return totalPages < maxPages ? totalPages : maxPages;
- }),
-
- totalPages: Ember.computed('numberOfResults', 'size', function() {
- // Total pages of search results
- return Math.ceil(this.get('numberOfResults') / this.get('size'));
- }),
-
- actions: {
- clearFilters() {
- this.get('metrics').trackEvent({
- category: 'button',
- action: 'click',
- label: 'Discover - Clear Filters',
- });
-
- this.get('clearFilters')();
- },
-
- search() {
- this.get('metrics').trackEvent({
- category: 'button',
- action: 'click',
- label: 'Discover - Search',
- extra: this.get('q'),
- });
-
- this.get('search')();
- },
-
- selectSortOption(option) {
- // Runs when sort option changed in dropdown
- this.get('metrics').trackEvent({
- category: 'dropdown',
- action: 'select',
- label: `Sort by: ${option || 'relevance'}`
- });
-
- this.set('sort', option);
- },
-
- selectPage(pageNumber) {
- // When paginating, sets page and scrolls to top of results.
- this.set('page', pageNumber);
- if (scroll) {
- this.scrollToResults();
- }
- },
-
- toggleShowLuceneHelp() {
- this.toggleProperty('showLuceneHelp');
- },
-
- updateFilters(filterType, item) {
- item = typeof item === 'object' ? item.text : item;
- const currentState = this.get(`queryParamsState.${filterType}.value`).slice(0);
- const hasItem = currentState.includes(item);
-
- if (hasItem) {
- this.set(filterType, currentState.filter(x => !item.includes(x)));
- } else {
- currentState.pushObject(item)
- this.set(filterType, currentState);
- }
-
- this.get('metrics').trackEvent({
- category: 'filter',
- action: hasItem ? 'remove' : 'add',
- label: `Discover - ${filterType} ${item}`
- });
- },
-
- updateParams(key, value) {
- this.set(key, value);
- },
- },
-
- // ************************************************************
- // Discover-page METHODS and HOOKS
- // ************************************************************
-
- scrollToResults() {
- // Scrolls to top of search results
- Ember.$('html, body').scrollTop(this.$('.results-top').position().top);
- },
-
- isTypeFacet(obj) {
- return obj.key === 'type';
- },
-
- getTypes: task(function* () {
- const response = yield Ember.$.ajax({
- url: `${config.OSF.shareApiUrl}/schema/creativework/hierarchy/`,
- crossDomain: true,
- type: 'GET',
- contentType: 'application/vnd.api+json',
- });
- if (response.data) {
- const types = response.data.CreativeWork ? response.data.CreativeWork.children : {};
- this.get('facets').find(this.isTypeFacet).data = this.transformTypes(types);
- }
- }),
-
- transformTypes(types) {
- const tmpTypes = types;
- if (typeof (tmpTypes) !== 'object') {
- return tmpTypes;
- }
-
- for (const key of Object.keys(tmpTypes)) {
- const lowKey = key.replace(/([A-Z])/g, ' $1').trim().toLowerCase();
- tmpTypes[lowKey] = this.transformTypes(tmpTypes[key]);
- if (key !== lowKey) {
- delete tmpTypes[key];
- }
- }
-
- return tmpTypes;
- },
-
- getCounts: task(function* () {
- const searchUrl = `${config.OSF.shareSearchUrl}?preference=${this.get('currentUser.sessionKey')}`;
- const queryBody = JSON.stringify({
- size: 0,
- aggregations: {
- sources: {
- cardinality: {
- field: 'sources',
- precision_threshold: MAX_SOURCES,
- },
- },
- },
- });
- const response = yield Ember.$.ajax({
- url: searchUrl,
- crossDomain: true,
- type: 'POST',
- contentType: 'application/json',
- data: queryBody,
- });
- this.setProperties({
- numberOfEvents: response.hits.total,
- numberOfSources: response.aggregations.sources.value,
- });
- }),
-
- init() {
- // Runs on initial render.
- this._super(...arguments);
- if (this.get('facets').find(this.isTypeFacet)) {
- this.get('getTypes').perform();
- }
- this.get('getCounts').perform();
- },
- });
-
-