Skip to content

Commit

Permalink
Break up ResultRow #8 - Split ShowResultRow
Browse files Browse the repository at this point in the history
To avoid circular dependencies between Show and Season result rows, split
ShowResultRow into three classes:
* ShowResultRowBase - Common show result row functionality
* ShowResultRow - A "real" row that will load seasons when clicked.
* ShowTitleResultRow - A placeholder row displayed when a show/season is already
  active.

Splitting the class up this way allows SeasonResultRow to only need to know
about ShowTitleResultRow, not the "real" ShowResultRow.
  • Loading branch information
danrahn committed Mar 10, 2024
1 parent 9ebcdba commit d91525d
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 61 deletions.
10 changes: 5 additions & 5 deletions Client/Script/SeasonResultRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { PlexClientState } from './PlexClientState.js';
import { PurgedMarkers } from './PurgedMarkerManager.js';
import SectionOptionsResultRow from './SectionOptionsResultRow.js';
import { ServerCommands } from './Commands.js';
import ShowResultRow from './ShowResultRow.js';
import ShowTitleResultRow from './ShowTitleResultRow.js';
import Tooltip from './Tooltip.js';

/**
Expand All @@ -33,8 +33,8 @@ export default class SeasonResultRow extends ResultRow {
#sectionTitle;

/**
* The placeholder {@linkcode ShowResultRow} that displays the show name/stats when in episode view.
* @type {ShowResultRow} */
* The placeholder {@linkcode ShowTitleResultRow} that displays the show name/stats when in episode view.
* @type {ShowTitleResultRow} */
#showTitle;

/**
Expand Down Expand Up @@ -250,8 +250,8 @@ export default class SeasonResultRow extends ResultRow {
addRow(this.#sectionTitle.buildRow());
}

this.#showTitle = new ShowResultRow(PlexClientState.getActiveShow());
addRow(this.#showTitle.buildRow(true));
this.#showTitle = new ShowTitleResultRow(PlexClientState.getActiveShow());
addRow(this.#showTitle.buildRow());
addRow(buildNode('hr'));
this.#seasonTitle = new SeasonResultRow(PlexClientState.getActiveSeason());
addRow(this.#seasonTitle.buildRow(true));
Expand Down
71 changes: 15 additions & 56 deletions Client/Script/ShowResultRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,22 @@ import { SeasonData } from '../../Shared/PlexTypes.js';
import SeasonResultRow from './SeasonResultRow.js';
import SectionOptionsResultRow from './SectionOptionsResultRow.js';
import { ServerCommands } from './Commands.js';
import ShowResultRowBase from './ShowResultRowBase.js';
import ShowTitleResultRow from './ShowTitleResultRow.js';
import Tooltip from './Tooltip.js';

/** @typedef {!import('./PurgedMarkerCache').PurgedShow} PurgedShow */
/** @typedef {!import('../../Shared/PlexTypes').MarkerData} MarkerData */
/** @typedef {!import('../../Shared/PlexTypes').SerializedSeasonData} SerializedSeasonData */
/** @typedef {!import('../../Shared/PlexTypes').ShowData} ShowData */


const Log = new ContextualLog('ShowRow');

/**
* A result row for a single show in the library.
*/
export default class ShowResultRow extends ResultRow {
export default class ShowResultRow extends ShowResultRowBase {
/**
* When this show is active, holds a map of season metadata ids to its corresponding SeasonResultRow
* @type {{[metadataId: number]: SeasonResultRow}} */
Expand All @@ -36,15 +43,10 @@ export default class ShowResultRow extends ResultRow {
#sectionTitle;

/**
* The placeholder {@linkcode ShowResultRow} that displays the show name/stats when in season view.
* @type {ShowResultRow} */
* The placeholder {@linkcode ShowTitleResultRow} that displays the show name/stats when in season view.
* @type {ShowTitleResultRow} */
#showTitle;

/**
* Whether this is a "dummy" row when displaying seasons/episodes
* @type {boolean} */
#selected;

/** @param {ShowData} show */
constructor(show) {
super(show, 'topLevelResult showResult');
Expand All @@ -56,52 +58,9 @@ export default class ShowResultRow extends ResultRow {
show() { return this.mediaItem(); }

/**
* Creates a DOM element for this show.
* Each entry contains three columns - the show name, the number of seasons, and the number of episodes.
* @param {boolean} [selected=false] True if this row is selected and should be treated like
* a header opposed to a clickable entry. */
buildRow(selected = false) {
this.#selected = selected;
if (this.html()) {
Log.warn('buildRow has already been called for this SeasonResultRow, that shouldn\'t happen');
return this.html();
}

const show = this.show();
const titleNode = buildNode('div', {}, show.title);
if (show.originalTitle) {
titleNode.appendChild(buildNode('span', { class : 'resultRowAltTitle' }, ` (${show.originalTitle})`));
}

const customColumn = buildNode('div', { class : 'showResultSeasons' }, plural(show.seasonCount, 'Season'));
const row = this.buildRowColumns(titleNode, customColumn, selected ? null : this.#showClick.bind(this));
if (selected) {
this.addBackButton(row, 'Back to results', async () => {
UISections.clearSections(UISection.Seasons | UISection.Episodes);
await UISections.hideSections(UISection.Seasons | UISection.Episodes);
UISections.showSections(UISection.MoviesOrShows);
});

row.classList.add('dynamicText');
}

this.setHtml(row);
return row;
}

/**
* Returns the callback invoked when clicking on the marker count when purged markers are present. */
getPurgeEventListener() {
return this.#onShowPurgeClick.bind(this);
}

/**
* Launches the purge overlay for this show. */
#onShowPurgeClick() {
// For dummy rows, set focus back to the first tabbable row, as the purged icon might not exist anymore
const focusBack = this.#selected ? $$('.tabbableRow', this.html().parentElement) : this.html();
PurgedMarkers.showSingleShow(this.show().metadataId, focusBack);
}
* Callback to invoke when the row is clicked.
* @returns {(e: MouseEvent) => any} */
onClick() { return this.#showClick.bind(this); }

/**
* Updates various UI states after purged markers are restored/ignored.
Expand Down Expand Up @@ -188,8 +147,8 @@ export default class ShowResultRow extends ResultRow {
addRow(this.#sectionTitle.buildRow());
}

this.#showTitle = new ShowResultRow(this.show());
addRow(this.#showTitle.buildRow(true /*selected*/));
this.#showTitle = new ShowTitleResultRow(this.show());
addRow(this.#showTitle.buildRow());
addRow(new BulkActionResultRow(this.show()).buildRow());
addRow(buildNode('hr', { style : 'margin-top: 0' }));
this.#seasonsFiltered = 0;
Expand Down
66 changes: 66 additions & 0 deletions Client/Script/ShowResultRowBase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { $$, buildNode, plural } from './Common.js';
import { ContextualLog } from '../../Shared/ConsoleLog.js';
import { PurgedMarkers } from './PurgedMarkerManager.js';
import { ResultRow } from './ResultRow.js';

const Log = new ContextualLog('ShowRowBase');

/**
* Base class for a show result row, either a "real" one or a title placeholder.
*/
export default class ShowResultRowBase extends ResultRow {

/** @param {ShowData} show */
constructor(show) {
super(show, 'topLevelResult showResult');
}

/** Whether this row is a placeholder title row, used when a specific show/season is selected. */
titleRow() { return false; }

/**
* Return the underlying show data associated with this result row.
* @returns {ShowData} */
show() { return this.mediaItem(); }

/**
* Callback to invoke when the row is clicked.
* @returns {(e: MouseEvent) => any|null} */
onClick() { return null; }

/**
* Creates a DOM element for this show.
* Each entry contains three columns - the show name, the number of seasons, and the number of episodes. */
buildRow() {
if (this.html()) {
Log.warn('buildRow has already been called for this ShowResultRow, that shouldn\'t happen');
return this.html();
}

const show = this.show();
const titleNode = buildNode('div', {}, show.title);
if (show.originalTitle) {
titleNode.appendChild(buildNode('span', { class : 'resultRowAltTitle' }, ` (${show.originalTitle})`));
}

const customColumn = buildNode('div', { class : 'showResultSeasons' }, plural(show.seasonCount, 'Season'));
const row = this.buildRowColumns(titleNode, customColumn, this.onClick());

this.setHtml(row);
return row;
}

/**
* Returns the callback invoked when clicking on the marker count when purged markers are present. */
getPurgeEventListener() {
return this.#onShowPurgeClick.bind(this);
}

/**
* Launches the purge overlay for this show. */
#onShowPurgeClick() {
// For dummy rows, set focus back to the first tabbable row, as the purged icon might not exist anymore
const focusBack = this.titleRow() ? $$('.tabbableRow', this.html().parentElement) : this.html();
PurgedMarkers.showSingleShow(this.show().metadataId, focusBack);
}
}
51 changes: 51 additions & 0 deletions Client/Script/ShowTitleResultRow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { UISection, UISections } from './ResultSections.js';
import { PlexClientState } from './PlexClientState.js';
import ShowResultRowBase from './ShowResultRowBase.js';

/** @typedef {!import('../../Shared/PlexTypes').ShowData} ShowData */

/**
* A show result row that's used as a placeholder when a specific show/season is active.
*/
export default class ShowTitleResultRow extends ShowResultRowBase {
/**
* @param {ShowData} show */
constructor(show) {
super(show, 'topLevelResult showResult');
}

titleRow() { return true; }

/**
* Build this placeholder row. Takes the base row and adds a 'back' button. */
buildRow() {
if (this.html()) {
// Extra data has already been added, and super.buildRow accounts for this, and gives us some warning logging.
return super.buildRow();
}

const row = super.buildRow();
this.addBackButton(row, 'Back to results', async () => {
UISections.clearSections(UISection.Seasons | UISection.Episodes);
await UISections.hideSections(UISection.Seasons | UISection.Episodes);
UISections.showSections(UISection.MoviesOrShows);
});

row.classList.add('dynamicText');
return row;
}

/**
* Updates various UI states after purged markers are restored/ignored.
* @param {PurgedShow} _unpurged */
notifyPurgeChange(_unpurged) {
/*async*/ PlexClientState.updateNonActiveBreakdown(this, []);
}

/**
* Update marker breakdown data after a bulk update.
* @param {{[seasonId: number]: MarkerData[]}} _changedMarkers */
notifyBulkAction(_changedMarkers) {
return PlexClientState.updateNonActiveBreakdown(this, []);
}
}

0 comments on commit d91525d

Please sign in to comment.