| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 | import { ReactiveCache } from '/imports/reactiveCache';import { TAPi18n } from '/imports/i18n';import Cards from '../../models/cards';import SessionData from '../../models/usersessiondata';import {QueryDebug} from "../../config/query-classes";import {OPERATOR_DEBUG} from "../../config/search-const";export class CardSearchPagedComponent extends BlazeComponent {  onCreated() {    this.searching = new ReactiveVar(false);    this.hasResults = new ReactiveVar(false);    this.hasQueryErrors = new ReactiveVar(false);    this.query = new ReactiveVar('');    this.resultsHeading = new ReactiveVar('');    this.searchLink = new ReactiveVar(null);    this.results = new ReactiveVar([]);    this.hasNextPage = new ReactiveVar(false);    this.hasPreviousPage = new ReactiveVar(false);    this.resultsCount = 0;    this.totalHits = 0;    this.queryErrors = null;    this.resultsPerPage = 25;    this.sessionId = SessionData.getSessionId();    this.subscriptionHandle = null;    this.serverError = new ReactiveVar(false);    this.sessionData = null;    this.debug = new ReactiveVar(new QueryDebug());    const that = this;    this.subscriptionCallbacks = {      onReady() {        if (process.env.DEBUG === 'true') {          console.log('Subscription ready, getting results...');          console.log('Subscription ready - sessionId:', that.sessionId);        }                // Wait for session data to be available (with timeout)        let waitCount = 0;        const maxWaitCount = 50; // 10 seconds max wait                const waitForSessionData = () => {          waitCount++;          const sessionData = that.getSessionData();          if (process.env.DEBUG === 'true') {            console.log('waitForSessionData - attempt', waitCount, 'session data:', sessionData);          }                    if (sessionData) {            const results = that.getResults();            if (process.env.DEBUG === 'true') {              console.log('Search results count:', results ? results.length : 0);            }                        // If no results and this is a due cards search, try to retry            if ((!results || results.length === 0) && that.searchRetryCount !== undefined && that.searchRetryCount < that.maxRetries) {              if (process.env.DEBUG === 'true') {                console.log('No results found, retrying search...');              }              that.searchRetryCount++;              Meteor.setTimeout(() => {                if (that.performSearch) {                  that.performSearch();                }              }, 500);              return;            }                        that.searching.set(false);            that.hasResults.set(true);            that.serverError.set(false);             } else if (waitCount < maxWaitCount) {               // Session data not available yet, wait a bit more               if (process.env.DEBUG === 'true') {                 console.log('Session data not available yet, waiting... (attempt', waitCount, 'of', maxWaitCount, ')');               }               Meteor.setTimeout(waitForSessionData, 200);             } else {               // Timeout reached, try fallback search               if (process.env.DEBUG === 'true') {                 console.log('Timeout reached waiting for session data, trying fallback search');               }               const results = that.getResults();               if (process.env.DEBUG === 'true') {                 console.log('Fallback search results count:', results ? results.length : 0);               }                              if (results && results.length > 0) {                 that.searching.set(false);                 that.hasResults.set(true);                 that.serverError.set(false);               } else {                 that.searching.set(false);                 that.hasResults.set(false);                 that.serverError.set(true);               }             }        };                // Start waiting for session data        Meteor.setTimeout(waitForSessionData, 100);      },      onError(error) {        if (process.env.DEBUG === 'true') {          console.log('Subscription error:', error);          console.log('Error.reason:', error.reason);          console.log('Error.message:', error.message);          console.log('Error.stack:', error.stack);        }        that.searching.set(false);        that.hasResults.set(false);        that.serverError.set(true);      },    };  }  resetSearch() {    this.searching.set(false);    this.results.set([]);    this.hasResults.set(false);    this.hasQueryErrors.set(false);    this.resultsHeading.set('');    this.serverError.set(false);    this.resultsCount = 0;    this.totalHits = 0;    this.queryErrors = null;    this.debug.set(new QueryDebug());  }  getSessionData(sessionId) {    const sessionIdToUse = sessionId || SessionData.getSessionId();    if (process.env.DEBUG === 'true') {      console.log('getSessionData - looking for sessionId:', sessionIdToUse);    }        // Try using the raw SessionData collection instead of ReactiveCache    const sessionData = SessionData.findOne({      sessionId: sessionIdToUse,    });    if (process.env.DEBUG === 'true') {      console.log('getSessionData - found session data (raw):', sessionData);    }        // Also try ReactiveCache for comparison    const reactiveSessionData = ReactiveCache.getSessionData({      sessionId: sessionIdToUse,    });    if (process.env.DEBUG === 'true') {      console.log('getSessionData - found session data (reactive):', reactiveSessionData);    }        return sessionData || reactiveSessionData;  }  getResults() {    // eslint-disable-next-line no-console    // console.log('getting results');    this.sessionData = this.getSessionData();    // eslint-disable-next-line no-console    if (process.env.DEBUG === 'true') {      console.log('getResults - sessionId:', this.sessionId);      console.log('getResults - session data:', this.sessionData);    }    const cards = [];        if (this.sessionData && this.sessionData.cards) {      if (process.env.DEBUG === 'true') {        console.log('getResults - cards array length:', this.sessionData.cards.length);      }      this.sessionData.cards.forEach(cardId => {        const card = ReactiveCache.getCard(cardId);        if (process.env.DEBUG === 'true') {          console.log('getResults - card:', cardId, card);        }        cards.push(card);      });      this.queryErrors = this.sessionData.errors || [];    } else {      if (process.env.DEBUG === 'true') {        console.log('getResults - no sessionData or no cards array, trying direct card search');      }      // Fallback: try to get cards directly from the client-side collection      const selector = {        type: 'cardType-card',        dueAt: { $exists: true, $nin: [null, ''] }      };      const allCards = Cards.find(selector).fetch();      if (process.env.DEBUG === 'true') {        console.log('getResults - direct card search found:', allCards ? allCards.length : 0, 'cards');      }            if (allCards && allCards.length > 0) {        allCards.forEach(card => {          if (card && card._id) {            if (process.env.DEBUG === 'true') {              console.log('getResults - direct card:', card._id, card.title);            }            cards.push(card);          }        });      }            this.queryErrors = [];    }    if (this.queryErrors.length) {      // console.log('queryErrors:', this.queryErrorMessages());      this.hasQueryErrors.set(true);      // return null;    }    this.debug.set(new QueryDebug(this.sessionData ? this.sessionData.debug : null));    if (process.env.DEBUG === 'true') {      console.log('debug:', this.debug.get().get());      console.log('debug.show():', this.debug.get().show());      console.log('debug.showSelector():', this.debug.get().showSelector());    }    if (cards) {      if (this.sessionData) {        this.totalHits = this.sessionData.totalHits || 0;        this.resultsCount = cards.length;        this.resultsStart = this.sessionData.lastHit - this.resultsCount + 1;        this.resultsEnd = this.sessionData.lastHit;        this.resultsHeading.set(this.getResultsHeading());        this.results.set(cards);        this.hasNextPage.set(this.sessionData.lastHit < this.sessionData.totalHits);        this.hasPreviousPage.set(          this.sessionData.lastHit - this.sessionData.resultsCount > 0,        );      } else {        this.totalHits = cards.length;        this.resultsCount = cards.length;        this.resultsStart = 1;        this.resultsEnd = cards.length;        this.resultsHeading.set(this.getResultsHeading());        this.results.set(cards);        this.hasNextPage.set(false);        this.hasPreviousPage.set(false);      }      return cards;    }    this.resultsCount = 0;    return null;  }  stopSubscription() {    if (this.subscriptionHandle) {      this.subscriptionHandle.stop();    }  }  getSubscription(queryParams) {    if (process.env.DEBUG === 'true') {      console.log('Subscribing to globalSearch with:', {        sessionId: this.sessionId,        params: queryParams.params,        text: queryParams.text      });    }    // Subscribe to both globalSearch and sessionData    const globalSearchHandle = Meteor.subscribe(      'globalSearch',      this.sessionId,      queryParams.params,      queryParams.text,      this.subscriptionCallbacks,    );        const sessionDataHandle = Meteor.subscribe('sessionData', this.sessionId);    if (process.env.DEBUG === 'true') {      console.log('Subscribed to sessionData with sessionId:', this.sessionId);    }    return globalSearchHandle;  }  runGlobalSearch(queryParams) {    this.searching.set(true);    this.debug.set(new QueryDebug());    this.stopSubscription();    this.subscriptionHandle = this.getSubscription(queryParams);  }  queryErrorMessages() {    const messages = [];    this.queryErrors.forEach(err => {      let value = err.color ? TAPi18n.__(`color-${err.value}`) : err.value;      if (!value) {        value = err.value;      }      messages.push(TAPi18n.__(err.tag, value));    });    return messages;  }  nextPage() {    this.searching.set(true);    this.stopSubscription();    this.subscriptionHandle = Meteor.subscribe(      'nextPage',      this.sessionId,      this.subscriptionCallbacks,    );  }  previousPage() {    this.searching.set(true);    this.stopSubscription();    this.subscriptionHandle = Meteor.subscribe(      'previousPage',      this.sessionId,      this.subscriptionCallbacks,    );  }  getResultsHeading() {    if (this.resultsCount === 0) {      return TAPi18n.__('no-cards-found');    } else if (this.resultsCount === 1) {      return TAPi18n.__('one-card-found');    } else if (this.resultsCount === this.totalHits) {      return TAPi18n.__('n-cards-found', {sprintf: [this.resultsCount]});    }    return TAPi18n.__('n-n-of-n-cards-found', {      start: this.resultsStart,      end: this.resultsEnd,      total: this.totalHits,    });  }  getSearchHref() {    const baseUrl = window.location.href.replace(/([?#].*$|\s*$)/, '');    return `${baseUrl}?q=${encodeURIComponent(this.query.get())}`;  }  events() {    return [      {        'click .js-next-page'(evt) {          evt.preventDefault();          this.nextPage();        },        'click .js-previous-page'(evt) {          evt.preventDefault();          this.previousPage();        },      },    ];  }}
 |