Skip to content

Commit

Permalink
feat: Default opening page config option
Browse files Browse the repository at this point in the history
  • Loading branch information
colin969 committed Jul 31, 2024
1 parent 8c523cb commit 94fd310
Show file tree
Hide file tree
Showing 17 changed files with 125 additions and 17 deletions.
2 changes: 2 additions & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"searchLimitValue": "{0} Results",
"useCustomViews": "Use Custom Search Views",
"useCustomViewsDesc": "Replaces the Library search tabs with custom named search tabs and adds a Library option to the advanced filters.",
"defaultOpeningPage": "Default Opening Page",
"defaultOpeningPageDesc": "Default page to open to, either the Home Page or a chosen Search Page",
"restoreSearchViews": "Restore Search Results",
"restoreSearchViewsDesc": "Saves and restore a copy of each search page (text, filters, selected game and playlist) whenever you open the application.",
"enableEditing": "Enable Editing",
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/Util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Game, Playlist, TagFilterGroup } from 'flashpoint-launcher';
import * as fs from 'fs';
import * as path from 'path';
import { GameOrderChangeEvent } from './components/GameOrder';
import { Paths } from './Paths';
import { Paths } from '@shared/Paths';
import { GameDragEventData } from './components/pages/BrowsePage';
import { GameGridItem } from './components/GameGridItem';
import { GameListItem } from './components/GameListItem';
Expand Down
33 changes: 31 additions & 2 deletions src/renderer/components/GameGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { memoizeOne } from '@shared/memoize';
import * as React from 'react';
import { ArrowKeyStepper, AutoSizer, Grid, GridCellProps, ScrollIndices } from 'react-virtualized-reactv17';
import { UpdateView, ViewGameSet } from '../interfaces';
import { findElementAncestor, getExtremeIconURL, getGameImageURL } from '../Util';
import { findElementAncestor, gameDragDataType, getExtremeIconURL, getGameImageURL } from '../Util';
import { GameGridItem } from './GameGridItem';
import { GameItemContainer } from './GameItemContainer';
import { GameDragEventData } from './pages/BrowsePage';
import { GameDragData, GameDragEventData } from './pages/BrowsePage';
import { ScreenshotPreviewMode } from '@shared/BrowsePageLayout';

const RENDERER_OVERSCAN = 5;
Expand All @@ -26,6 +26,8 @@ export type GameGridProps = {
games: ViewGameSet;
/** Total number of games in the results view there are. */
resultsTotal?: number;
/** Are we in a playlist view? */
insideOrderedPlaylist: boolean;
/** Currently selected game (if any). */
selectedGameId?: string;
/** Currently dragged game index (if any). */
Expand All @@ -46,6 +48,8 @@ export type GameGridProps = {
onGameDragStart?: (event: React.DragEvent, dragEventData: GameDragEventData) => void;
/** Called when the user stops dragging a game (when they release it). */
onGameDragEnd?: (event: React.DragEvent) => void;
/** Moves a game at the specified index above the other game at the destination index, inside the playlist */
onMovePlaylistGame: (sourceGameId: string, destGameId: string) => void;
updateView: UpdateView;
/** Function for getting a reference to grid element. Called whenever the reference could change. */
gridRef?: RefFunc<HTMLDivElement>;
Expand Down Expand Up @@ -106,6 +110,29 @@ export class GameGrid extends React.Component<GameGridProps> {
window.Shared.back.unregisterAny(this.onResponse);
}

onGameDrop = (event: React.DragEvent) => {
const rawData = event.dataTransfer.getData(gameDragDataType);
if (rawData) {
const dragData = JSON.parse(rawData) as GameDragData;
console.log(`source: ${dragData.index}`);
const destData = this.findGameDragEventData(event.target);
if (destData) {
console.log(`dest: ${destData.index}`);
// Move the dropped game above the target game in the playlist
this.props.onMovePlaylistGame(dragData.gameId, destData.gameId);
}
}
};

onGameDragOver = (event: React.DragEvent): void => {
const types = event.dataTransfer.types;
if (types.length === 1 && types[0] === gameDragDataType) {
// Show the "You can drop here" cursor while dragging something droppable over this element
event.dataTransfer.dropEffect = 'copy';
event.preventDefault();
}
};

render() {
const games = this.props.games || [];
// @HACK: Check if the games array changed
Expand All @@ -124,6 +151,8 @@ export class GameGrid extends React.Component<GameGridProps> {
onGameContextMenu={this.onGameContextMenu}
onGameDragStart={this.onGameDragStart}
onGameDragEnd={this.onGameDragEnd}
onGameDrop={this.props.insideOrderedPlaylist ? this.onGameDrop : undefined}
onGameDragOver={this.props.insideOrderedPlaylist ? this.onGameDragOver : undefined}
findGameDragEventData={this.findGameDragEventData}
realRef={this.wrapperRef}
onKeyPress={this.onKeyPress}>
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/GameList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export type OwnProps = {
onGameDragStart: (event: React.DragEvent, dragEventData: GameDragEventData) => void;
/** Called when the user stops dragging a game (when they release it). */
onGameDragEnd: (event: React.DragEvent) => void;
/** Moves a game at the specified index above the other game at the destination index, inside tha playlist */
/** Moves a game at the specified index above the other game at the destination index, inside the playlist */
onMovePlaylistGame: (sourceGameId: string, destGameId: string) => void;
updateView: UpdateView;
/** Function for getting a reference to grid element. Called whenever the reference could change. */
Expand Down
13 changes: 12 additions & 1 deletion src/renderer/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { MenuItemConstructorOptions } from 'electron';
import * as React from 'react';
import { Link, RouteComponentProps, useLocation } from 'react-router-dom';
import { WithPreferencesProps } from '../containers/withPreferences';
import { Paths } from '../Paths';
import { Paths } from '@shared/Paths';
import { joinLibraryRoute } from '../Util';
import { LangContext } from '../util/lang';
import { OpenIcon } from './OpenIcon';
Expand Down Expand Up @@ -73,6 +73,11 @@ export class Header extends React.Component<HeaderProps, HeaderState> {
view: name
});
}
if (this.props.preferencesData.defaultOpeningPage === joinLibraryRoute(view)) {
updatePreferencesData({
defaultOpeningPage: joinLibraryRoute(name)
});
}
updatePreferencesData({
customViews,
storedViews,
Expand Down Expand Up @@ -170,6 +175,12 @@ export class Header extends React.Component<HeaderProps, HeaderState> {
}
const customViews = this.props.preferencesData.customViews.filter(v => v !== view);
const storedViews = this.props.preferencesData.storedViews.filter(v => v.view !== view);
// Make sure the default page is always valid
if (this.props.preferencesData.defaultOpeningPage === joinLibraryRoute(view)) {
updatePreferencesData({
defaultOpeningPage: Paths.HOME,
});
}
updatePreferencesData({
customViews: customViews,
storedViews: storedViews,
Expand Down
21 changes: 20 additions & 1 deletion src/renderer/components/RightBrowseSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export class RightBrowseSidebar extends React.Component<RightBrowseSidebarProps,
onOriginalDescriptionChange = this.wrapOnTextChange((game, text) => this.props.onEditGame({ originalDescription: text }));
// Bound "on click" callbacks for game fields
onDeveloperClick = this.wrapOnTextClick('developer');
onSeriesClick = this.wrapOnTextClick('series');
onSeriesClick = this.wrapOnExactTextClick('series');
onSourceClick = this.wrapOnTextClick('source');
onPublisherClick = this.wrapOnTextClick('publisher');
onPlayModeClick = this.wrapOnTextClick('playMode');
Expand Down Expand Up @@ -1403,6 +1403,25 @@ export class RightBrowseSidebar extends React.Component<RightBrowseSidebarProps,
};
}

/**
* Create a callback for when a game field is clicked.
*
* @param field Name of metadata field that was clicked
*/
wrapOnExactTextClick<T extends PickType<Game, string>>(field: T): () => void {
return () => {
const { isEditing } = this.props;
if (!isEditing && this.props.currentGame) {
this.props.onDeselectPlaylist();
const value = this.props.currentGame[field as keyof Game]?.toString();
const search = (value)
? `${field}=${wrapSearchTerm(value)}`
: `${field}=""`;
this.props.onSearch(search);
}
};
}

/**
* Create a wrapper for a EditableTextWrap's onChange callback (this is to reduce redundancy).
*
Expand Down
3 changes: 0 additions & 3 deletions src/renderer/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,6 @@ export function SearchBar(props: SearchBarProps) {
const onToggleLibrary = onToggleFactory('libraries');
const onClearLibraries = onClearFactory('libraries');

console.log('libraries adv');
console.log(view.advancedFilter.libraries);

return (
<div className={`search-bar-wrapper ${expanded ? 'search-bar-wrapper--expanded-simple' : ''}`}>
<div className="search-bar">
Expand Down
6 changes: 4 additions & 2 deletions src/renderer/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import { WithTagCategoriesProps } from '../containers/withTagCategories';
import { WithTasksProps } from '../containers/withTasks';
import { CreditsFile } from '../credits/CreditsFile';
import { fpfssLogin } from '../fpfss';
import { Paths } from '../Paths';
import { Paths } from '@shared/Paths';
import { AppRouter, AppRouterProps } from '../router';
import { getViewName, getGameImagePath, getGameImageURL, getGamePath, joinLibraryRoute } from '../Util';
import { LangContext } from '../util/lang';
Expand Down Expand Up @@ -298,6 +298,7 @@ export class App extends React.Component<AppProps> {
views: customViews,
areLibraries: false,
});

}
} else {
if (this.props.preferencesData.useStoredViews) {
Expand All @@ -315,6 +316,8 @@ export class App extends React.Component<AppProps> {
}

this.props.setTagCategories(data.tagCategories);
console.log('navigating to ' + this.props.preferencesData.defaultOpeningPage);
this.props.history.push(this.props.preferencesData.defaultOpeningPage);
})
.then(() => {
this.props.mainActions.addLoaded([BackInit.DATABASE]);
Expand Down Expand Up @@ -361,7 +364,6 @@ export class App extends React.Component<AppProps> {
registerWebsocketListeners() {
window.Shared.back.register(BackOut.INIT_EVENT, (event, data) => {
for (const index of data.done) {
console.log('new ' + index);
switch (+index) { // DO NOT REMOVE - Fails to convert to enum without explicitint conversion
case BackInit.DATABASE: {
this.onDatabaseLoaded();
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/components/pages/BrowsePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export class BrowsePage extends React.Component<BrowsePageProps, BrowsePageState
loadView() {
// Force the first search if view hasn't been used yet
if (this.props.currentView.data.metaState === RequestState.WAITING) {
// console.log('loading view ' + this.props.currentView.id);
this.props.searchActions.forceSearch({
view: this.props.currentView.id
});
Expand Down Expand Up @@ -167,6 +168,7 @@ export class BrowsePage extends React.Component<BrowsePageProps, BrowsePageState
<GameGrid
games={currentView.data.games}
resultsTotal={currentView.data.total !== undefined ? currentView.data.total : Object.keys(currentView.data.games).length}
insideOrderedPlaylist={currentView.selectedPlaylist !== undefined && currentView.advancedFilter.playlistOrder}
selectedGameId={currentView.selectedGame?.id}
draggedGameIndex={draggedGameIndex}
extremeTags={extremeTags}
Expand All @@ -176,6 +178,7 @@ export class BrowsePage extends React.Component<BrowsePageProps, BrowsePageState
onContextMenu={this.props.onGameContextMenu}
onGameDragStart={this.onGameDragStart}
onGameDragEnd={this.onGameDragEnd}
onMovePlaylistGame={this.onMovePlaylistGame}
cellWidth={width}
cellHeight={height}
logoVersion={this.props.logoVersion}
Expand Down
43 changes: 41 additions & 2 deletions src/renderer/components/pages/ConfigPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ import { deepCopy } from '@shared/Util';
import { formatString } from '@shared/utils/StringFormatter';
import { AppPathOverride, TagFilterGroup } from 'flashpoint-launcher';
import * as React from 'react';
import { getExtIconURL, getExtremeIconURL, getPlatformIconURL, isFlashpointValidCheck } from '../../Util';
import {
getExtIconURL,
getExtremeIconURL,
getPlatformIconURL,
isFlashpointValidCheck,
joinLibraryRoute
} from '../../Util';
import { LangContext } from '../../util/lang';
import { CheckBox } from '../CheckBox';
import { ConfigBox, ConfigBoxInner } from '../ConfigBox';
Expand All @@ -36,6 +42,8 @@ import { Spinner } from '../Spinner';
import { SimpleButton } from '../SimpleButton';
import { ScreenshotPreviewMode } from '@shared/BrowsePageLayout';
import { WithSearchProps } from '@renderer/containers/withSearch';
import { Paths } from '@shared/Paths';
import { GENERAL_VIEW_ID } from '@renderer/store/search/slice';

const { num } = Coerce;

Expand Down Expand Up @@ -110,11 +118,13 @@ export class ConfigPage extends React.Component<ConfigPageProps, ConfigPageState
const serverOptions = this.itemizeServerOptionsMemo(this.props.serverNames);
const libraryOptions = this.itemizeLibraryOptionsMemo(this.props.libraries, this.props.preferencesData.excludedRandomLibraries, this.context.libraries);
const platformOptions = this.itemizePlatformOptionsMemo(this.props.platforms, this.props.preferencesData.nativePlatforms);
const defaultOpeningPageOptions = this.itemizeDefaultOpeningPageOptionsMemo(Object.keys(this.props.search.views), !this.props.preferencesData.useCustomViews, allStrings['libraries']);
const appPathOverrides = this.renderAppPathOverridesMemo(this.props.preferencesData.appPathOverrides);
const tagFilters = this.renderTagFiltersMemo(this.props.preferencesData.tagFilters, this.props.preferencesData.browsePageShowExtreme, this.context, this.props.logoVersion);
const logoSetPreviewRows = this.renderLogoSetMemo(this.props.platforms, this.props.logoVersion);
const extensions = this.renderExtensionsMemo(this.props.extensions, strings);
const extConfigSections = this.renderExtensionConfigs(this.props.extConfigs, this.props.extConfig);

return (
<div className='config-page simple-scroll'>
<div className='config-page__inner'>
Expand All @@ -135,6 +145,12 @@ export class ConfigPage extends React.Component<ConfigPageProps, ConfigPageState
description={strings.useCustomViewsDesc}
checked={this.props.preferencesData.useCustomViews}
onToggle={this.onUseCustomViews} />
<ConfigBoxSelect
title={strings.defaultOpeningPage}
description={strings.defaultOpeningPageDesc}
value={this.props.preferencesData.defaultOpeningPage}
onChange={this.onDefaultOpeningPageSelect}
items={defaultOpeningPageOptions} />
{/* Enable Editing */}
<ConfigBoxCheckbox
title={strings.enableEditing}
Expand Down Expand Up @@ -545,6 +561,21 @@ export class ConfigPage extends React.Component<ConfigPageProps, ConfigPageState
});
});

itemizeDefaultOpeningPageOptionsMemo = memoizeOne((views: string[], areLibraries: boolean, strings: LangContainer['libraries']): SelectItem<string>[] => {
return [
{
value: Paths.HOME,
display: 'Home Page'
},
...views.filter((view) => view !== GENERAL_VIEW_ID).map((view) => {
return {
value: joinLibraryRoute(view),
display: areLibraries ? strings[view] || view : view,
};
})
];
});

renderClearPlaytimeButton = ({ confirm, extra }: ConfirmElementArgs<[LangContainer['config']]>) => {
return (
<SimpleButton
Expand Down Expand Up @@ -828,7 +859,10 @@ export class ConfigPage extends React.Component<ConfigPageProps, ConfigPageState
};

onUseCustomViews = (isChecked: boolean): void => {
updatePreferencesData({ useCustomViews: isChecked });
updatePreferencesData({
useCustomViews: isChecked,
defaultOpeningPage: Paths.HOME,
});
if (isChecked) {
const customViews = this.props.preferencesData.customViews;
if (customViews.length === 0) {
Expand Down Expand Up @@ -909,6 +943,11 @@ export class ConfigPage extends React.Component<ConfigPageProps, ConfigPageState
updatePreferencesData({ fallbackLanguage: event.target.value });
};

onDefaultOpeningPageSelect = (event: React.ChangeEvent<HTMLSelectElement>): void => {
console.log(event.target.value);
updatePreferencesData({ defaultOpeningPage: event.target.value });
};

onExcludedLibraryCheckboxChange = (library: string): void => {
const excludedRandomLibraries = [ ...this.props.preferencesData.excludedRandomLibraries ];

Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/pages/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import ReactDatePicker from 'react-datepicker';
import ReactMarkdown from 'react-markdown';
import { Link } from 'react-router-dom';
import remarkGfm from 'remark-gfm';
import { Paths } from '../../Paths';
import { Paths } from '@shared/Paths';
import { findGameDragEventDataGrid, getExtremeIconURL, getGameImageURL, getPlatformIconURL, joinLibraryRoute } from '../../Util';
import { WithPreferencesProps } from '../../containers/withPreferences';
import { WithSearchProps } from '../../containers/withSearch';
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/pages/NotFoundPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Paths } from '@renderer/Paths';
import { Paths } from '@shared/Paths';
import { Link } from 'react-router-dom';

/** Page shown when the current URL does not point to an existing page. */
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { UpdateInfo } from 'electron-updater';
import { Playlist, ViewGame } from 'flashpoint-launcher';
import * as React from 'react';
import { Route, Switch } from 'react-router-dom';
import { Paths } from './Paths';
import { Paths } from '@shared/Paths';
import { AboutPage, AboutPageProps } from './components/pages/AboutPage';
import { DeveloperPage, DeveloperPageProps } from './components/pages/DeveloperPage';
import { IFramePage, IFramePageProps } from './components/pages/IFramePage';
Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions src/shared/lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const langTemplate = {
'searchLimitValue',
'useCustomViews',
'useCustomViewsDesc',
'defaultOpeningPage',
'defaultOpeningPageDesc',
'restoreSearchViews',
'restoreSearchViewsDesc',
'enableEditing',
Expand Down
4 changes: 3 additions & 1 deletion src/shared/preferences/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ import * as Coerce from '@shared/utils/Coerce';
import { IObjectParserProp, ObjectParser } from '../utils/ObjectParser';
import { CurateGroup } from '@renderer/store/curate/slice';
import { getDefaultAdvancedFilter } from '@shared/search/util';
import { Paths } from '@shared/Paths';

export function updatePreferencesData(data: DeepPartial<AppPreferencesData>, send = true) {
const preferences = window.Shared.preferences;
// @TODO Figure out the delta change of the object tree, and only send the changes
preferences.data = overwritePreferenceData(deepCopy(preferences.data), data);
console.log(preferences.data);
if (send) {
sendPrefs();
}
Expand Down Expand Up @@ -169,6 +169,7 @@ export const defaultPreferencesData: Readonly<AppPreferencesData> = Object.freez
storedViews: [],
useCustomViews: false,
customViews: [],
defaultOpeningPage: Paths.HOME,
});

/**
Expand Down Expand Up @@ -248,6 +249,7 @@ export function overwritePreferenceData(
parser.prop('useStoredViews', v => source.useStoredViews = !!v, true);
parser.prop('useCustomViews', v => source.useCustomViews = !!v, true);
parser.prop('customViews', v => source.customViews = strArray(v), true);
parser.prop('defaultOpeningPage', v => source.defaultOpeningPage = str(v), true);

// Can't have a negative delay!
if (source.screenshotPreviewDelay < 0) {
Expand Down
Loading

0 comments on commit 94fd310

Please sign in to comment.