diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..dd58241 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +engine/ \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..758509c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,12 @@ +{ + "semi": false, + "singleQuote": true, + "overrides": [ + { + "files": "src/browser/app/profile/*.js", + "options": { + "semi": true + } + } + ] +} diff --git a/melon.json b/melon.json index 1b5f866..cc51666 100644 --- a/melon.json +++ b/melon.json @@ -14,6 +14,10 @@ "ublock": { "id": "uBlock0@raymondhill.net", "url": "https://github.com/gorhill/uBlock/releases/download/1.39.0/uBlock0_1.39.0.firefox.xpi" + }, + "newtab": { + "id": "newtab@browser.fushra.com", + "url": "https://github.com/focus-browser/newtab/releases/download/v1.0.0-a.2/newtab.zip" } } } diff --git a/src/browser/app/profile/focus-browser.js b/src/browser/app/profile/focus-browser.js index e69de29..1ad346d 100644 --- a/src/browser/app/profile/focus-browser.js +++ b/src/browser/app/profile/focus-browser.js @@ -0,0 +1,6 @@ +// Note: You must have semicolons at the end of each line in user setting files + +pref('browser.aboutwelcome.enabled', false); + +pref('focus.welcome.enabled', true); +pref('focus.welcome.seen', false); diff --git a/src/browser/components/BrowserGlue-jsm.patch b/src/browser/components/BrowserGlue-jsm.patch new file mode 100644 index 0000000..c55a949 --- /dev/null +++ b/src/browser/components/BrowserGlue-jsm.patch @@ -0,0 +1,54 @@ +diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm +index 7bfc4ff7684931a656c0c1cd85a83082219e4f45..f88d4af15d7863d51b9f6894e24fa9ec6759a402 100644 +--- a/browser/components/BrowserGlue.jsm ++++ b/browser/components/BrowserGlue.jsm +@@ -3981,6 +3981,18 @@ BrowserGlue.prototype = { + ); + }, + ++ /** ++ * Displays the welcome dialog. Trigured `_maybeShowDefaultBrowserPrompt`. ++ * ++ * @todo Provide documentation for implementing custom popups ++ * @patch FocusBrowser ++ */ ++ _showWelcomeDialog() { ++ BrowserWindowTracker.getTopWindow().gDialogBox.open( ++ "chrome://focus/content/welcome.html" ++ ); ++ }, ++ + async _maybeShowDefaultBrowserPrompt() { + // Highest priority is the upgrade dialog, which can include a "primary + // browser" request and is limited in various ways, e.g., major upgrades. +@@ -4038,6 +4050,30 @@ BrowserGlue.prototype = { + return; + } + ++ ++ // ========================================================================= ++ // Focus browser welcome ++ ++ const welcomeEnabledPref = 'focus.welcome.enabled' ++ const welcomeSeenPref = 'focus.welcome.seen' ++ ++ // NOTE: Get bool prefs takes in two options, the pref string and a default ++ // value if it is not defined ++ ++ // TODO: Add pref information to melon docs ++ ++ const welcomeEnabled = Services.prefs.getBoolPref(welcomeEnabledPref, true) ++ const welcomeSeen = Services.prefs.getBoolPref(welcomeSeenPref, false) ++ ++ if (welcomeEnabled && !welcomeSeen) { ++ this._showWelcomeDialog() ++ // TODO: When welcome is fully implemented, the following line should run ++ // Services.prefs.setBoolPref(welcomeSeenPref, true) ++ } ++ ++ // ========================================================================= ++ ++ + const willPrompt = await DefaultBrowserCheck.willCheckDefaultBrowser( + /* isStartupCheck */ true + ); diff --git a/src/browser/components/moz-build.patch b/src/browser/components/moz-build.patch new file mode 100644 index 0000000..dbc9d99 --- /dev/null +++ b/src/browser/components/moz-build.patch @@ -0,0 +1,22 @@ +diff --git a/browser/components/moz.build b/browser/components/moz.build +index aaae39b52f397623ba8153e5fdd079e43a16e09e..34bb8c3445ef292ffcd4a27e974465786dc147e6 100644 +--- a/browser/components/moz.build ++++ b/browser/components/moz.build +@@ -25,7 +25,6 @@ with Files("safebrowsing/**"): + with Files("controlcenter/**"): + BUG_COMPONENT = ("Firefox", "General") + +- + DIRS += [ + "about", + "aboutlogins", +@@ -61,6 +60,9 @@ DIRS += [ + "urlbar", + ] + ++# Focus browser imports ++DIRS += ["welcome"] ++ + DIRS += ["build"] + + if CONFIG["NIGHTLY_BUILD"]: diff --git a/src/browser/components/welcome/jar.mn b/src/browser/components/welcome/jar.mn new file mode 100644 index 0000000..d0bb5be --- /dev/null +++ b/src/browser/components/welcome/jar.mn @@ -0,0 +1,5 @@ +focus.jar: +% content focus %content/focus/ contentaccessible=yes + content/focus/welcome.html + content/focus/welcome.css + content/focus/welcome.js \ No newline at end of file diff --git a/src/browser/components/welcome/moz.build b/src/browser/components/welcome/moz.build new file mode 100644 index 0000000..2661ad7 --- /dev/null +++ b/src/browser/components/welcome/moz.build @@ -0,0 +1 @@ +JAR_MANIFESTS += ["jar.mn"] diff --git a/src/browser/components/welcome/welcome.css b/src/browser/components/welcome/welcome.css new file mode 100644 index 0000000..b92de91 --- /dev/null +++ b/src/browser/components/welcome/welcome.css @@ -0,0 +1,54 @@ +body { + width: 504px; + height: 504px; + + display: flex; + position: relative; + flex-flow: row nowrap; + height: 100%; + min-height: 500px; + overflow: hidden; +} + +.page { + display: none; + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + align-content: center; + width: 100%; + + margin: 64px; +} + +.card h3 { + font-size: 0.75rem; +} + +#themeList { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: 8px; + margin-bottom: 8px; +} + +#themeList .card { + width: 140px; + display: flex; + flex-direction: column; + align-items: center; + border: 2px solid transparent; + transition: all 250ms ease-in-out; +} + +#themeList .card.selected { + border: 2px solid var(--in-content-item-selected); +} + +h2, +p { + margin: 8px 0; +} diff --git a/src/browser/components/welcome/welcome.html b/src/browser/components/welcome/welcome.html new file mode 100644 index 0000000..2a7786c --- /dev/null +++ b/src/browser/components/welcome/welcome.html @@ -0,0 +1,54 @@ + + + + + + + + + + + +
+

Welcome to Focus Browser

+ +

Lets get you setup

+ + +
+ +
+

Import your stuff

+

+ TODO: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. +

+ +
+ + + +
+ +
+

Chose a theme

+
+ +
+ + + + + + diff --git a/src/browser/components/welcome/welcome.js b/src/browser/components/welcome/welcome.js new file mode 100644 index 0000000..f42df26 --- /dev/null +++ b/src/browser/components/welcome/welcome.js @@ -0,0 +1,131 @@ +const { XPCOMUtils } = ChromeUtils.import( + 'resource://gre/modules/XPCOMUtils.jsm' +) + +XPCOMUtils.defineLazyModuleGetters(this, { + AddonManager: 'resource://gre/modules/AddonManager.jsm', +}) + +class Page { + /** + * A basic controller for individual pages + * @param {string} id The id of the element that represents this page. + */ + constructor(id) { + this.element = document.getElementById(id) + this.nextEl = document.getElementById(`${id}Next`) + + this.nextEl.addEventListener('click', () => { + this.pages.next() + }) + } + + /** + * + * @param {Pages} pages The pages wrapper + */ + setPages(pages) { + this.pages = pages + } + + hide() { + this.element.style.display = 'none' + } + + show() { + this.element.style.display = '' + } +} + +class Themes extends Page { + constructor(id) { + super(id) + + this.loadThemes() + } + + async loadThemes() { + const themes = (await AddonManager.getAddonsByTypes(['theme'])).filter( + (theme) => !theme.id.includes('colorway') + ) + const themeList = document.getElementById('themeList') + + const themeElements = [] + + let selectedTheme = '' + + console.log(themes) + + themes.forEach((theme) => { + const container = document.createElement('div') + container.classList.add('card') + + if (theme.isActive) { + container.classList.add('selected') + selectedTheme = theme.id + } + + container.addEventListener('click', () => { + themeElements.forEach((el) => el.classList.remove('selected')) + container.classList.add('selected') + selectedTheme = theme.id + theme.enable() + }) + + const img = document.createElement('img') + img.src = theme.icons['32'] + img.classList.add('card-heading-image') + + const name = document.createElement('h3') + name.textContent = theme.name + + container.appendChild(img) + container.appendChild(name) + + themeList.appendChild(container) + themeElements.push(container) + }) + } +} + +class Pages { + /** + * A wrapper around all pages + * @param {Page[]} pages The pages + */ + constructor(pages) { + this.pages = pages + this.currentPage = 0 + + this.pages.forEach((page) => page.setPages(this)) + + this._displayCurrentPage() + } + + next() { + this.currentPage++ + + if (this.currentPage >= this.pages.length) { + // We can use internal js apis to close the window + close() + return + } + + this._displayCurrentPage() + } + + _displayCurrentPage() { + for (const page of this.pages) { + page.hide() + } + + this.pages[this.currentPage].show() + } +} + +const pages = new Pages([ + new Page('welcome'), + new Page('import'), + new Themes('theme'), + new Page('search'), +])