splashScreen.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import * as electron from "electron"
  2. import {EventEmitter} from "events"
  3. import * as fs from "fs"
  4. import * as path from "path"
  5. import * as url from "url"
  6. import * as moduleUpdater from "./common/moduleUpdater"
  7. import * as paths from "./common/paths"
  8. import * as ipcMain from "./ipcMain"
  9. // citron note: atom seems to add about 50px height to the frame on mac but not windows
  10. // TODO: see if we can eliminate fudge by using useContentSize BrowserWindow option
  11. const LOADING_WINDOW_WIDTH = 300;
  12. const LOADING_WINDOW_HEIGHT = process.platform == 'darwin' ? 300 : 350;
  13. // TODO: addModulesListener events should use Module's constants
  14. const UPDATE_CHECK_FINISHED = 'update-check-finished';
  15. const LAUNCHING = 'launching';
  16. export const APP_SHOULD_LAUNCH = 'APP_SHOULD_LAUNCH';
  17. export const APP_SHOULD_SHOW = 'APP_SHOULD_SHOW';
  18. export const events = new EventEmitter();
  19. function webContentsSend(win, event, ...args) {
  20. if(event === "SPLASH_UPDATE_STATE")lastStatus = args[0].status
  21. if (win != null && win.webContents != null) {
  22. win.webContents.send(`DISCORD_${event}`, ...args);
  23. }
  24. }
  25. let splashWindow:electron.BrowserWindow;
  26. let modulesListeners;
  27. let splashState;
  28. let launchedMainWindow;
  29. let quoteCachePath;
  30. let lastStatus
  31. export function setSplashState(state:any){
  32. splashState = state
  33. if (splashWindow != null && !splashWindow.isDestroyed() && !splashWindow.webContents.isDestroyed()) {
  34. webContentsSend(splashWindow, 'SPLASH_UPDATE_STATE', Object.assign({ status: lastStatus }, splashState));
  35. }
  36. }
  37. export function launchMainWindow(){
  38. launchMainWindowInternal();
  39. updateSplashState(LAUNCHING);
  40. }
  41. export function initSplash(startMinimized = false) {
  42. modulesListeners = {};
  43. splashState = {};
  44. launchedMainWindow = false;
  45. /*
  46. addModulesListener(UPDATE_CHECK_FINISHED, ({ succeeded, updateCount, manualRequired }) => {
  47. launchMainWindow();
  48. updateSplashState(LAUNCHING);
  49. });*/
  50. launchSplashWindow(startMinimized);
  51. quoteCachePath = path.join(paths.getUserData(), 'quotes.json');
  52. ipcMain.default.on('UPDATED_QUOTES', (_event, quotes) => cacheLatestQuotes(quotes));
  53. }
  54. function destroySplash() {
  55. if (splashWindow) {
  56. splashWindow.setSkipTaskbar(true);
  57. // defer the window hiding for a short moment so it gets covered by the main window
  58. const _nukeWindow = () => {
  59. splashWindow.hide();
  60. splashWindow.close();
  61. splashWindow = null;
  62. };
  63. setTimeout(_nukeWindow, 100);
  64. }
  65. }
  66. function addModulesListener(event, listener) {
  67. modulesListeners[event] = listener;
  68. moduleUpdater.events.addListener(event, listener);
  69. }
  70. function removeModulesListeners() {
  71. for (const event of Object.keys(modulesListeners)) {
  72. moduleUpdater.events.removeListener(event, modulesListeners[event]);
  73. }
  74. }
  75. export function updateSplashState(event) {
  76. if (splashWindow != null && !splashWindow.isDestroyed() && !splashWindow.webContents.isDestroyed()) {
  77. webContentsSend(splashWindow, 'SPLASH_UPDATE_STATE', Object.assign({ status: event }, splashState));
  78. }
  79. }
  80. function launchSplashWindow(startMinimized = false) {
  81. const windowConfig = {
  82. width: LOADING_WINDOW_WIDTH,
  83. height: LOADING_WINDOW_HEIGHT,
  84. transparent: false,
  85. frame: false,
  86. resizable: false,
  87. center: true,
  88. show: false,
  89. webPreferences: {
  90. nodeIntegration: true,
  91. },
  92. icon: path.join(__dirname, "..", "discord.png")
  93. };
  94. splashWindow = new electron.BrowserWindow(windowConfig);
  95. splashWindow.webContents.session.protocol.interceptFileProtocol('http', (request, callback) => {
  96. callback(path.join(__dirname, '..', "splash", request.url.replace('http://localhost/', '')));
  97. });
  98. // prevent users from dropping links to navigate in splash window
  99. splashWindow.webContents.on('will-navigate', e => e.preventDefault());
  100. splashWindow.webContents.on('new-window', (e, windowURL) => {
  101. e.preventDefault();
  102. electron.shell.openExternal(windowURL);
  103. // exit, but delay half a second because openExternal is about to fire
  104. // some events to things that are freed by app.quit.
  105. setTimeout(electron.app.quit, 500);
  106. });
  107. if (process.platform !== 'darwin') {
  108. // citron note: this causes a crash on quit while the window is open on osx
  109. splashWindow.on('closed', () => {
  110. splashWindow = null;
  111. if (!launchedMainWindow) {
  112. // user has closed this window before we launched the app, so let's quit
  113. electron.app.quit();
  114. }
  115. });
  116. }
  117. ipcMain.default.on('SPLASH_SCREEN_READY', () => {
  118. const cachedQuote = chooseCachedQuote();
  119. if (cachedQuote) {
  120. webContentsSend(splashWindow, 'SPLASH_SCREEN_QUOTE', cachedQuote);
  121. }
  122. if (splashWindow && !startMinimized) {
  123. splashWindow.show();
  124. }
  125. moduleUpdater.installPendingUpdates();
  126. events.emit("SPLASH_SCREEN_READY")
  127. });
  128. ipcMain.default.on('LAUNCH_ANYWAY', () => {
  129. launchMainWindowInternal()
  130. });
  131. splashWindow.loadURL("http://localhost/index.html");
  132. }
  133. function launchMainWindowInternal() {
  134. removeModulesListeners();
  135. if (!launchedMainWindow && splashWindow != null) {
  136. launchedMainWindow = true;
  137. events.emit(APP_SHOULD_LAUNCH);
  138. }
  139. }
  140. function scheduleUpdateCheck() {}
  141. export function focusWindow() {
  142. if (splashWindow != null) {
  143. splashWindow.focus();
  144. }
  145. }
  146. export function pageReady() {
  147. destroySplash();
  148. process.nextTick(() => events.emit(APP_SHOULD_SHOW));
  149. }
  150. function cacheLatestQuotes(quotes) {
  151. fs.writeFile(quoteCachePath, JSON.stringify(quotes), e => {
  152. if (e) {
  153. console.warn('Failed updating quote cache with error: ', e);
  154. }
  155. });
  156. }
  157. function chooseCachedQuote() {
  158. let cachedQuote = "Launching...";
  159. return cachedQuote;
  160. }