diff --git a/assets/ts/client/VitrineClient.ts b/assets/ts/client/VitrineClient.ts new file mode 100644 index 00000000..9486a66d --- /dev/null +++ b/assets/ts/client/VitrineClient.ts @@ -0,0 +1,146 @@ +import { ipcRenderer } from 'electron'; + +import { GamesCollection } from '../models/GamesCollection'; +import { PotentialGame } from '../models/PotentialGame'; +import { PlayableGame } from '../models/PlayableGame'; +import { beforeCss } from './helpers'; + +export class VitrineClient { + private potentialGames: GamesCollection; + private playableGames: GamesCollection; + private selectedGameId: string; + + constructor() { + this.potentialGames = new GamesCollection(); + this.playableGames = new GamesCollection(); + } + + public run() { + this.selectedGameId = ''; + ipcRenderer.send('client.ready'); + } + + public launchEvents() { + ipcRenderer.on('server.send-game', (event, game) => { + $('#game-cover-container').css({ + 'display': 'block' + }); + $('#game-title').html(game.name); + $('#game-desc').addClass('game-desc').html(game.summary); + $('#game-cover-image').css({ + 'background-image': 'url(' + game.cover + ')', + 'background-repeat': 'no-repeat', + 'background-size': '100% 100%', + }); + if (game.screenshots.length) { + beforeCss('#game-background', { + 'background-image': 'url(' + game.screenshots[0] + ')' + }); + } + }); + + ipcRenderer.on('server.send-game-error', (event, error) => { + $('#game-title').html(error); + throw new Error(error); + }); + + ipcRenderer.on('server.add-potential-games', (event, games) => { + this.potentialGames.games = games; + this.renderPotentialGames(); + }); + + ipcRenderer.on('server.add-playable-games', (event, games) => { + this.playableGames.games = games; + this.renderPlayableGames(); + }); + + ipcRenderer.on('server.remove-potential-game', (event, gameId) => { + this.potentialGames.removeGame(gameId, (error, game: PotentialGame) => { + if (error) + throw new Error(error); + else if (game) { + this.renderPotentialGames(); + } + }) + }); + + ipcRenderer.on('server.add-playable-game', (event, playableGame) => { + this.playableGames.addGame(playableGame); + this.renderPlayableGames(); + }); + } + + private renderPotentialGames() { + $('#potential-games-list').html(''); + + this.potentialGames.sort(); + + let counter: number = 0; + this.potentialGames.forEach((potentialGame: PotentialGame) => { + let html: string = '
  • ' + + //'' + potentialGame.name + '' + + '' + + '
  • '; + $('#potential-games-list').append(html); + counter++; + if (counter == this.potentialGames.games.length) + this.createGameClickEvents(false); + }); + } + + private renderPlayableGames() { + $('#playable-games-list').html(''); + + this.playableGames.sort(); + + let counter: number = 0; + this.playableGames.forEach((playableGame: PlayableGame) => { + let html: string = '
  • ' + playableGame.name + '
  • '; + $('#playable-games-list').append(html); + counter++; + if (counter == this.playableGames.games.length) + this.createGameClickEvents(true); + }); + } + + private createGameClickEvents(treatingPlayableGames: boolean) { + if (treatingPlayableGames) { + $('a.play-game-link[game-id]').each((index, value) => { + $(value).click(() => { + let gameId: string = $(value).attr('game-id'); + this.playableGames.getGame(gameId, (error, game) => { + if (error) + throw new Error(error); + if (this.selectedGameId === gameId) + return; + let gameCover: string = 'url(' + game.details.cover.split('\\').join('\\\\') + ')'; + let gameBgScreen: string = 'url(' + game.details.backgroundScreen.split('\\').join('\\\\') + ')'; + $('#game-cover-container').css({ + 'display': 'block' + }); + $('#game-title').html(game.name); + $('#game-desc').addClass('game-desc').html(game.details.summary); + $('#game-cover-image').css({ + 'background-image': gameCover, + 'background-repeat': 'no-repeat', + 'background-size': '100% 100%', + }); + beforeCss('#game-background', { + 'background-image': gameBgScreen + }); + this.selectedGameId = gameId; + }); + // ipcRenderer.send('client.launch-game', gameId); + }); + }); + } + else { + $('button.add-game-btn[game-id]').each((index, value) => { + $(value).click(() => { + let gameId: string = $(value).attr('game-id'); + ipcRenderer.send('client.add-game', gameId); + }); + }); + } + } +} diff --git a/assets/ts/client/dom.ts b/assets/ts/client/dom.ts index 317d0785..8d007414 100644 --- a/assets/ts/client/dom.ts +++ b/assets/ts/client/dom.ts @@ -1,14 +1,14 @@ import { ipcRenderer } from 'electron'; import * as formToObject from 'form-to-object'; -import './helpers'; + +import { extendJQuery } from './helpers'; function clickGameCover() { ($('#game-cover-component')).animateCss('pulse', 120); } export function launchDom() { - /* TODO: Remove this */ - // ipcRenderer.send('client.get-game', 'The Witcher'); + extendJQuery(); $(document.body).on('submit', '#game-name-form', function(event) { event.preventDefault(); diff --git a/assets/ts/client/events.ts b/assets/ts/client/events.ts deleted file mode 100644 index 4d4c9e26..00000000 --- a/assets/ts/client/events.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { ipcRenderer } from 'electron'; - -import { PotentialGame } from '../server/games/PotentialGame'; -import { PlayableGame } from '../server/games/PlayableGame'; -import { beforeCss, alphabeticSort } from './helpers'; - -let potentialGames: PotentialGame[]; -let playableGames: PlayableGame[]; - -function createGameClickEvents(treatingPlayableGames: boolean) { - if (treatingPlayableGames) { - $('a.play-game-link[game-id]').each(function() { - $(this).click(() => { - let gameId: string = $(this).attr('game-id'); - ipcRenderer.send('client.launch-game', gameId); - }); - }); - } - else { - $('button.add-game-btn[game-id]').each(function() { - $(this).click(() => { - let gameId: string = $(this).attr('game-id'); - ipcRenderer.send('client.add-game', gameId); - }); - }); - } -} - -function renderPotentialGames() { - $('#potential-games-list').html(''); - - if (potentialGames.length) - (potentialGames).sort(alphabeticSort); - - let counter: number = 0; - potentialGames.forEach((potentialGame: PotentialGame) => { - let html: string = '
  • ' + - //'' + potentialGame.name + '' + - '' + - '
  • '; - $('#potential-games-list').append(html); - counter++; - if (counter == potentialGames.length) - createGameClickEvents(false); - }); -} - -function renderPlayableGames() { - $('#playable-games-list').html(''); - - if (playableGames.length) - (playableGames).sort(alphabeticSort); - - let counter: number = 0; - playableGames.forEach((playableGame: PlayableGame) => { - let html: string = '
  • ' + playableGame.name + '
  • '; - $('#playable-games-list').append(html); - counter++; - if (counter == playableGames.length) - createGameClickEvents(true); - }); -} - -export function setClientReady() { - ipcRenderer.send('client.ready'); -} - -export function launchEvents() { - ipcRenderer.on('server.send-game', (event, game) => { - console.log(game); - $('#game-cover-container').css({ - 'display': 'block' - }); - $('#game-title').html(game.name); - $('#game-desc').addClass('game-desc').html(game.summary); - $('#game-cover-image').css({ - 'background-image': 'url(' + game.cover + ')', - 'background-repeat': 'no-repeat', - 'background-size': '100% 100%', - }); - if (game.screenshots.length) { - beforeCss('#game-background', { - 'background-image': 'url(' + game.screenshots[0] + ')' - }); - } - }); - - ipcRenderer.on('server.send-game-error', (event, error) => { - $('#game-title').html(error); - throw new Error(error); - }); - - ipcRenderer.on('server.add-potential-games', (event, games) => { - potentialGames = games; - renderPotentialGames(); - }); - - ipcRenderer.on('server.add-playable-games', (event, games) => { - playableGames = games; - renderPlayableGames(); - }); - - ipcRenderer.on('server.remove-potential-game', (event, gameId) => { - potentialGames.forEach((potentialGame: PotentialGame) => { - if (potentialGame.uuid == gameId) { - let index: number = potentialGames.indexOf(potentialGame); - potentialGames.splice(index, 1); - renderPotentialGames(); - } - }); - }); -} diff --git a/assets/ts/client/helpers.ts b/assets/ts/client/helpers.ts index 6256ee69..1c638d7e 100644 --- a/assets/ts/client/helpers.ts +++ b/assets/ts/client/helpers.ts @@ -1,24 +1,23 @@ -$.fn.extend({ - animateCss(animationName: string, animationDuration?: number) { - let animationEnd: string = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend'; - if (animationDuration !== undefined) - this.css('animation-duration', animationDuration + 'ms'); - this.addClass('animated ' + animationName).one(animationEnd, function() { - $(this).removeClass('animated ' + animationName); - }); - return this; - } -}); +export function extendJQuery() { + $.fn.extend({ + animateCss(animationName: string, animationDuration?: number) { + if (animationDuration !== undefined) + this.css('animation-duration', animationDuration + 'ms'); + + let animationEnd: string = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend'; + this.addClass('animated ' + animationName).one(animationEnd, function() { + $(this).removeClass('animated ' + animationName); + }); + return this; + } + }); +} export function beforeCss(selector: any, styling: object) { + $('head style').remove(); let rawStyling: string = ''; Object.keys(styling).forEach((key) => { rawStyling += key + ': ' + styling[key] + ';'; }); - $('head').append(''); } - -export function alphabeticSort(nodeA: any, nodeB: any) { - return nodeA.name > nodeB.name; -} diff --git a/assets/ts/client/main.ts b/assets/ts/client/main.ts index a29d466d..c9eca595 100644 --- a/assets/ts/client/main.ts +++ b/assets/ts/client/main.ts @@ -3,12 +3,13 @@ import * as jQuery from 'jquery'; import 'bootstrap-sass'; import { clientBootstrap, loadTitleBar } from './bootstrap'; -import { launchEvents, setClientReady } from './events'; +import { VitrineClient } from './VitrineClient'; import { launchDom } from './dom'; clientBootstrap(() => { // loadTitleBar(); - launchEvents(); + let vitrineClient: VitrineClient = new VitrineClient(); + vitrineClient.launchEvents(); launchDom(); - setClientReady(); + vitrineClient.run(); }); diff --git a/assets/ts/models/GamesCollection.ts b/assets/ts/models/GamesCollection.ts new file mode 100644 index 00000000..869e5681 --- /dev/null +++ b/assets/ts/models/GamesCollection.ts @@ -0,0 +1,68 @@ +function alphabeticSort(nodeA: any, nodeB: any) { + return nodeA.name > nodeB.name; +} + +export class GamesCollection { + private _games: T[]; + + constructor() { + this._games = []; + } + + get games(): T[] { + return this._games; + } + + set games(games: T[]) { + this._games = games; + } + + public getGame(gameId: string, callback) { + let counter: number = 0; + let found: boolean = false; + + this._games.forEach((game: any) => { + if (game.uuid == gameId) { + found = true; + callback(null, game); + } + counter++; + if (counter == this._games.length && !found) + callback('Game not found.', null); + + }); + } + + public addGame(game: T) { + this._games.push(game); + } + + public removeGame(gameId: string, callback) { + let counter: number = 0; + let found: boolean = false; + + this._games.forEach((game: any) => { + if (game.uuid == gameId) { + found = true; + let index: number = this._games.indexOf(game); + this._games.splice(index, 1); + callback(null, game); + } + counter++; + if (counter == this._games.length && !found) + callback('Game not found.', null); + + }); + } + + public sort() { + if (this._games.length) + (this._games).sort(alphabeticSort); + } + + public forEach(callback) { + this._games.forEach(function(game: any) { + callback(game); + }) + } +} diff --git a/assets/ts/models/PlayableGame.ts b/assets/ts/models/PlayableGame.ts new file mode 100644 index 00000000..1d0dad9b --- /dev/null +++ b/assets/ts/models/PlayableGame.ts @@ -0,0 +1,11 @@ +import { PotentialGame } from './PotentialGame'; + +export class PlayableGame extends PotentialGame { + public timePlayed: number; + + public static toPlayableGame(game: PotentialGame) { + let playableGame: PlayableGame = game; + playableGame.timePlayed = 0; + return playableGame; + } +} diff --git a/assets/ts/server/games/PotentialGame.ts b/assets/ts/models/PotentialGame.ts similarity index 57% rename from assets/ts/server/games/PotentialGame.ts rename to assets/ts/models/PotentialGame.ts index 536240f2..8be28525 100644 --- a/assets/ts/server/games/PotentialGame.ts +++ b/assets/ts/models/PotentialGame.ts @@ -2,7 +2,5 @@ export class PotentialGame { public commandLine: string[]; public uuid: string; - constructor(public name: string, public details?: any) { - } - + constructor(public name: string, public details?: any) {} } diff --git a/assets/ts/server/events.ts b/assets/ts/server/events.ts index 51f2aab7..8f1db08f 100644 --- a/assets/ts/server/events.ts +++ b/assets/ts/server/events.ts @@ -3,28 +3,32 @@ import * as fs from 'fs'; import { execFile } from 'child_process'; import { IgdbWrapper } from './api/IgdbWrapper'; +import { GamesCollection } from '../models/GamesCollection'; +import { PotentialGame } from '../models/PotentialGame'; +import { PlayableGame } from '../models/PlayableGame'; import { getSteamCrawlerPromise } from './games/SteamGamesCrawler'; -import { PotentialGame } from './games/PotentialGame'; -import { PlayableGame } from './games/PlayableGame'; -import { uuidV5, downloadFile } from './helpers'; import { getPlayableGamesCrawlerPromise } from './games/PlayableGamesCrawler'; +import { uuidV5, downloadFile } from './helpers'; let igdbWrapper: IgdbWrapper = new IgdbWrapper(); -let potentialGames: PotentialGame[]; -let playableGames: PlayableGame[]; +let potentialGames: GamesCollection; +let playableGames: GamesCollection; export const events = { 'client.ready': (event) => { - getSteamCrawlerPromise().then((games: PotentialGame[]) => { + potentialGames = new GamesCollection(); + playableGames = new GamesCollection(); + + getSteamCrawlerPromise().then((games: GamesCollection) => { potentialGames = games; - event.sender.send('server.add-potential-games', potentialGames); + event.sender.send('server.add-potential-games', potentialGames.games); }).catch((error) => { throw error; }); - getPlayableGamesCrawlerPromise().then((games: PlayableGame[]) => { + getPlayableGamesCrawlerPromise().then((games: GamesCollection) => { playableGames = games; - event.sender.send('server.add-playable-games', playableGames); + event.sender.send('server.add-playable-games', playableGames.games); }).catch((error) => { throw error; }); @@ -66,7 +70,7 @@ export const events = { }); } counter++; - if (counter == potentialGames.length && !gameFound) + if (counter == potentialGames.games.length && !gameFound) throw Error('Game not found'); }); }, @@ -80,19 +84,20 @@ export const events = { return; fs.mkdirSync(gameDirectory); - let addedGame: any = potentialSteamGame; + let addedGame: any = PlayableGame.toPlayableGame(potentialSteamGame); let screenPath = path.join(gameDirectory, 'background.jpg'); let coverPath = path.join(gameDirectory, 'cover.jpg'); - downloadFile(addedGame.details.cover, coverPath, () => { + downloadFile(addedGame.details.cover, coverPath, true, () => { addedGame.details.cover = coverPath; - downloadFile(addedGame.details.screenshots[0].replace('t_screenshot_med', 't_screenshot_huge'), screenPath, () => { + downloadFile(addedGame.details.screenshots[0].replace('t_screenshot_med', 't_screenshot_huge'), screenPath, true,() => { addedGame.details.backgroundScreen = screenPath; delete addedGame.details.screenshots; fs.writeFile(configFilePath, JSON.stringify(addedGame, null, 2), (err) => { if (err) throw err; event.sender.send('server.remove-potential-game', potentialSteamGame.uuid); + event.sender.send('server.add-playable-game', addedGame); }); }); }); diff --git a/assets/ts/server/games/PlayableGame.ts b/assets/ts/server/games/PlayableGame.ts deleted file mode 100644 index 7259b130..00000000 --- a/assets/ts/server/games/PlayableGame.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { PotentialGame } from './PotentialGame'; - -export class PlayableGame extends PotentialGame { - public timePlayed: number; -} \ No newline at end of file diff --git a/assets/ts/server/games/PlayableGamesCrawler.ts b/assets/ts/server/games/PlayableGamesCrawler.ts index 05e0eb3b..c1ffcd7d 100644 --- a/assets/ts/server/games/PlayableGamesCrawler.ts +++ b/assets/ts/server/games/PlayableGamesCrawler.ts @@ -1,7 +1,8 @@ import * as fs from 'fs'; import * as path from 'path'; -import { PlayableGame } from './PlayableGame'; +import { PlayableGame } from '../../models/PlayableGame'; +import { GamesCollection } from '../../models/GamesCollection'; class PlayableGamesCrawler { private playableGames: PlayableGame[]; @@ -15,14 +16,16 @@ class PlayableGamesCrawler { public search(callback) { this.callback = callback; - console.log(this.gamesDirectory); - fs.readdir(this.gamesDirectory, (err, files) => { - if (err) { - this.callback(err, null); + fs.readdir(this.gamesDirectory, (error, files) => { + if (error) { + this.callback(error, null); + return; + } + if (!files.length) { + let playableGames: GamesCollection = new GamesCollection(); + this.callback(null, playableGames); return; } - if (!files.length) - this.callback(null, []); let counter: number = 0; files.forEach((gameId) => { let configFilePath: any = path.join(this.gamesDirectory, gameId, 'config.json'); @@ -31,8 +34,12 @@ class PlayableGamesCrawler { this.playableGames.push(playableGame); } counter++; - if (counter == files.length) - this.callback(null, this.playableGames); + if (counter === files.length) { + let playableGames: GamesCollection = new GamesCollection(); + playableGames.games = this.playableGames; + this.callback(null, playableGames); + delete this.callback; + } }); }); } diff --git a/assets/ts/server/games/SteamGamesCrawler.ts b/assets/ts/server/games/SteamGamesCrawler.ts index 693347a2..3106ceb8 100644 --- a/assets/ts/server/games/SteamGamesCrawler.ts +++ b/assets/ts/server/games/SteamGamesCrawler.ts @@ -4,8 +4,9 @@ import * as glob from 'glob'; import { uuidV5 } from '../helpers'; import { AcfParser } from '../api/AcfParser'; -import { PotentialGame } from './PotentialGame'; +import { PotentialGame } from '../../models/PotentialGame'; import { IgdbWrapper } from '../api/IgdbWrapper'; +import { GamesCollection } from '../../models/GamesCollection'; class SteamGamesCrawler { private configFilePath: string; @@ -37,17 +38,15 @@ class SteamGamesCrawler { private processGames(error, files): void { if (!files.length) { - /* TODO: Remove this */ - let blankGame: PotentialGame = new PotentialGame('PuTTY', null); - blankGame.commandLine = ['C:/Users/P.ROMAN/Desktop/putty.exe']; - this.callback(null, [blankGame]); + let potentialGames: GamesCollection = new GamesCollection(); + this.callback(null, potentialGames); return; } let counter: number = 0; files.forEach((appManifest, index, array) => { let gameManifest: any = new AcfParser(appManifest).toObject().AppState; - if (this.isGameAlreadyAdded(gameManifest.name)) { + if (SteamGamesCrawler.isGameAlreadyAdded(gameManifest.name)) { counter++; return; } @@ -69,14 +68,16 @@ class SteamGamesCrawler { counter++; if (counter === array.length) { - this.callback(null, this.potentialGames); + let potentialGames: GamesCollection = new GamesCollection(); + potentialGames.games = this.potentialGames; + this.callback(null, potentialGames); delete this.callback; } }); }); } - private isGameAlreadyAdded(name: string) { + private static isGameAlreadyAdded(name: string) { let gameId: string = uuidV5(name); let gameDirectory = path.join(__dirname, 'games', gameId); diff --git a/assets/ts/server/helpers.ts b/assets/ts/server/helpers.ts index 686bab4a..64c123eb 100644 --- a/assets/ts/server/helpers.ts +++ b/assets/ts/server/helpers.ts @@ -1,3 +1,4 @@ +import * as http from 'http'; import * as https from 'https'; import * as fs from 'fs'; import * as uuid from 'uuid/v5'; @@ -8,10 +9,11 @@ export function uuidV5(name: string) { return uuid(name, dnsNamespace); } -export function downloadFile(url, path, callback) { +export function downloadFile(url, path, isHttps, callback) { let file = fs.createWriteStream(path); + let protocol: any = (isHttps) ? (https) : (http); - https.get(url, (response) => { + protocol.get(url, (response) => { response.pipe(file); callback(); }); diff --git a/package.json b/package.json index a8b9eb0c..a967aa75 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vitrine", - "version": "0.0.7", + "version": "0.0.8", "description": "A modern game library using Electron, NodeJS and IGDB.", "main": "main.js", "author": "Paul Roman",