diff --git a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-homepage-preview.js b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-homepage-preview.js index aa1edafff9a..a083415c2f8 100644 --- a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-homepage-preview.js +++ b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-homepage-preview.js @@ -83,7 +83,7 @@ export default class HomepagePreview extends PreviewBaseComponent { ] ); - ctx.font = `Bold ${bodyFontSize * 1.3}em '${font}'`; + ctx.font = `700 ${bodyFontSize * 1.3}em '${font}'`; ctx.fillStyle = colors.primary; ctx.textAlign = "center"; ctx.fillText(category.name, boxStartX + boxWidth / 2, boxStartY + 25); @@ -166,7 +166,7 @@ export default class HomepagePreview extends PreviewBaseComponent { // Categories this.categories().forEach((category, idx) => { const textPos = y + categoryHeight * 0.35; - ctx.font = `Bold ${bodyFontSize * 1.1}em '${font}'`; + ctx.font = `700 ${bodyFontSize * 1.1}em '${font}'`; ctx.fillStyle = colors.primary; ctx.fillText(category.name, cols[0], textPos); @@ -262,7 +262,7 @@ export default class HomepagePreview extends PreviewBaseComponent { // Categories this.categories().forEach((category, idx) => { const textPos = y + categoryHeight * 0.35; - ctx.font = `Bold ${bodyFontSize * 1.1}em '${font}'`; + ctx.font = `700 ${bodyFontSize * 1.1}em '${font}'`; ctx.fillStyle = colors.primary; ctx.fillText(category.name, cols[0], textPos); @@ -310,7 +310,7 @@ export default class HomepagePreview extends PreviewBaseComponent { ); ctx.fillText(title, cols[3], textPos); - ctx.font = `Bold ${bodyFontSize}em '${font}'`; + ctx.font = `700 ${bodyFontSize}em '${font}'`; ctx.fillText(Math.floor(Math.random() * 90) + 10, cols[4], textPos); ctx.font = `${bodyFontSize}em '${font}'`; ctx.fillText(`1h`, cols[4], textPos + topicHeight * 0.4); @@ -318,7 +318,7 @@ export default class HomepagePreview extends PreviewBaseComponent { ctx.beginPath(); ctx.fillStyle = category.color; const badgeSize = topicHeight * 0.1; - ctx.font = `Bold ${bodyFontSize * 0.5}em '${font}'`; + ctx.font = `700 ${bodyFontSize * 0.5}em '${font}'`; ctx.rect( cols[3] + margin * 0.25, y + topicHeight * 0.65, @@ -383,17 +383,33 @@ export default class HomepagePreview extends PreviewBaseComponent { ctx.stroke(); }; - const cols = [0.02, 0.66, 0.8, 0.87, 0.93].map((c) => c * width); + const cols = [0.02, 0.66, 0.75, 0.83, 0.9].map((c) => c * width); // Headings const headingY = height * 0.33; ctx.fillStyle = textColor; ctx.font = `${bodyFontSize * 0.9}em '${font}'`; - ctx.fillText("Topic", cols[0], headingY); - ctx.fillText("Replies", cols[2], headingY); - ctx.fillText("Views", cols[3], headingY); - ctx.fillText("Activity", cols[4], headingY); + ctx.fillText( + i18n("wizard.homepage_preview.table_headers.topic"), + cols[0], + headingY + ); + ctx.fillText( + i18n("wizard.homepage_preview.table_headers.replies"), + cols[2], + headingY + ); + ctx.fillText( + i18n("wizard.homepage_preview.table_headers.views"), + cols[3], + headingY + ); + ctx.fillText( + i18n("wizard.homepage_preview.table_headers.activity"), + cols[4], + headingY + ); // Topics let y = headingY + rowHeight / 2.6; @@ -412,7 +428,7 @@ export default class HomepagePreview extends PreviewBaseComponent { ctx.beginPath(); ctx.fillStyle = category.color; const badgeSize = rowHeight * 0.15; - ctx.font = `Bold ${bodyFontSize * 0.75}em '${font}'`; + ctx.font = `700 ${bodyFontSize * 0.75}em '${font}'`; ctx.rect(cols[0] + 4, y + rowHeight * 0.6, badgeSize, badgeSize); ctx.fill(); diff --git a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js index e6b8016821e..91ab091652c 100644 --- a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js +++ b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js @@ -116,7 +116,7 @@ export default class PreviewBase extends Component { }); }); - Promise.all( + return Promise.all( fontFaces.map((fontFace) => fontFace.load().then((loadedFont) => { document.fonts.add(loadedFont); @@ -135,7 +135,7 @@ export default class PreviewBase extends Component { this.loadingFontVariants = false; }); } else if (this.loadedFonts.has(font.id)) { - this.triggerRepaint(); + return Promise.resolve(this.triggerRepaint()); } } @@ -162,8 +162,14 @@ export default class PreviewBase extends Component { reload() { Promise.all([this.loadFonts(), this.loadImages()]).then(() => { - this.loaded = true; - this.triggerRepaint(); + // NOTE: This must be done otherwise the "bold" variant of the body font + // will not be loaded for some reason before rendering the canvas. + // + // The header font does not suffer from this issue. + this.loadFontVariants(this.wizard.font).then(() => { + this.loaded = true; + this.triggerRepaint(); + }); }); } @@ -283,17 +289,16 @@ export default class PreviewBase extends Component { avatarSize, avatarSize ); + // accounts for hard-set color variables in solarized themes ctx.fillStyle = colors.primary_low_mid || darkLightDiff(colors.primary, colors.secondary, 45, 55); const pathScale = this.headerHeight / 1200; - // search icon SVG path const searchIcon = new Path2D( "M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z" ); - // hamburger icon const hamburgerIcon = new Path2D( "M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z" ); @@ -321,100 +326,100 @@ export default class PreviewBase extends Component { const { ctx } = this; - const categoriesSize = headerHeight * 2; - const badgeHeight = categoriesSize * 0.25; + const badgeHeight = headerHeight * 2 * 0.25; const headerMargin = headerHeight * 0.2; + const fontSize = Math.round(badgeHeight * 0.5); + ctx.font = `${fontSize}px '${font}'`; + const allCategoriesText = i18n( + "wizard.homepage_preview.nav_buttons.all_categories" + ); + const categoriesWidth = ctx.measureText(allCategoriesText).width; + const categoriesBoxWidth = categoriesWidth + headerMargin * 2; + + // Box around "all categories >" ctx.beginPath(); ctx.strokeStyle = colors.primary; ctx.lineWidth = 0.5; ctx.rect( headerMargin, headerHeight + headerMargin, - categoriesSize, + categoriesBoxWidth, badgeHeight ); ctx.stroke(); - const fontSize = Math.round(badgeHeight * 0.5); - - ctx.font = `${fontSize}px '${font}'`; ctx.fillStyle = colors.primary; ctx.fillText( - "all categories", + allCategoriesText, headerMargin * 1.5, headerHeight + headerMargin * 1.4 + fontSize ); + // Caret (>) at the end of "all categories" box const pathScale = badgeHeight / 1000; - // caret icon const caretIcon = new Path2D( "M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z" ); ctx.save(); ctx.translate( - categoriesSize - headerMargin / 4, + categoriesBoxWidth, headerHeight + headerMargin + badgeHeight / 4 ); ctx.scale(pathScale, pathScale); ctx.fill(caretIcon); ctx.restore(); - const text = opts.categories ? "Categories" : "Latest"; + // First top menu item + const firstTopMenuItemText = opts.categories + ? i18n("wizard.homepage_preview.nav_buttons.categories") + : i18n("wizard.homepage_preview.nav_buttons.latest"); + const newText = i18n("wizard.homepage_preview.nav_buttons.new"); + const unreadText = i18n("wizard.homepage_preview.nav_buttons.unread"); + const topText = i18n("wizard.homepage_preview.nav_buttons.top"); + const hotText = i18n("wizard.homepage_preview.nav_buttons.hot"); - const activeWidth = categoriesSize * (opts.categories ? 0.8 : 0.55); ctx.beginPath(); ctx.fillStyle = colors.tertiary; ctx.rect( - headerMargin * 2 + categoriesSize, + categoriesBoxWidth + headerMargin * 2, headerHeight + headerMargin, - activeWidth, + ctx.measureText(firstTopMenuItemText).width + headerMargin * 2, badgeHeight ); ctx.fill(); ctx.font = `${fontSize}px '${font}'`; ctx.fillStyle = colors.secondary; - let x = headerMargin * 3.0 + categoriesSize; + const pillButtonTextY = headerHeight + headerMargin * 1.4 + fontSize; + const firstTopMenuItemX = headerMargin * 3.0 + categoriesBoxWidth; ctx.fillText( - text, - x - headerMargin * 0.1, - headerHeight + headerMargin * 1.5 + fontSize + firstTopMenuItemText, + firstTopMenuItemX, + pillButtonTextY, + ctx.measureText(firstTopMenuItemText).width ); ctx.fillStyle = colors.primary; - x += categoriesSize * (opts.categories ? 0.8 : 0.6); - ctx.fillText("New", x, headerHeight + headerMargin * 1.5 + fontSize); - x += categoriesSize * 0.4; - ctx.fillText("Unread", x, headerHeight + headerMargin * 1.5 + fontSize); + const newTextX = + firstTopMenuItemX + + ctx.measureText(firstTopMenuItemText).width + + headerMargin * 2.0; + ctx.fillText(newText, newTextX, pillButtonTextY); - x += categoriesSize * 0.6; - ctx.fillText("Top", x, headerHeight + headerMargin * 1.5 + fontSize); - } + const unreadTextX = + newTextX + ctx.measureText(newText).width + headerMargin * 2.0; + ctx.fillText(unreadText, unreadTextX, pillButtonTextY); - resizeTextLinesToFitRect( - textLines, - rectWidth, - ctx, - fontSize, - font, - renderCallback - ) { - const maxLengthLine = textLines.reduce((a, b) => - a.length > b.length ? a : b - ); + const topTextX = + unreadTextX + ctx.measureText(unreadText).width + headerMargin * 2.0; + ctx.fillText(topText, topTextX, pillButtonTextY); - let fontSizeDecreaseMultiplier = 1; - while (ctx.measureText(maxLengthLine).width > rectWidth) { - fontSizeDecreaseMultiplier -= 0.1; - ctx.font = `${fontSize * fontSizeDecreaseMultiplier}em '${font}'`; - } - - for (let i = 0; i < textLines.length; i++) { - renderCallback(textLines[i], i); - } + const hotTextX = + topTextX + ctx.measureText(topText).width + headerMargin * 2.0; + ctx.fillText(hotText, hotTextX, pillButtonTextY); } } diff --git a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/index.js b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/index.js index 10467c82eb0..8baf7cd67f6 100644 --- a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/index.js +++ b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/index.js @@ -2,7 +2,11 @@ import { action } from "@ember/object"; import { observes } from "@ember-decorators/object"; import { bind } from "discourse-common/utils/decorators"; import { i18n } from "discourse-i18n"; -import { chooseDarker, darkLightDiff } from "../../../lib/preview"; +import { + chooseDarker, + darkLightDiff, + resizeTextLinesToFitRect, +} from "../../../lib/preview"; import HomepagePreview from "./-homepage-preview"; import PreviewBaseComponent from "./-preview-base"; @@ -133,7 +137,7 @@ export default class Index extends PreviewBaseComponent { // Topic title ctx.beginPath(); ctx.fillStyle = colors.primary; - ctx.font = `bold ${titleFontSize}em '${headingFont}'`; + ctx.font = `700 ${titleFontSize}em '${headingFont}'`; ctx.fillText(i18n("wizard.previews.topic_title"), margin, height * 0.3); // Topic OP text @@ -144,7 +148,7 @@ export default class Index extends PreviewBaseComponent { const topicOp = i18n("wizard.homepage_preview.topic_ops.what_books"); const topicOpLines = topicOp.split("\n"); - this.resizeTextLinesToFitRect( + resizeTextLinesToFitRect( topicOpLines, timelineX - leftHandTextGutter, ctx, @@ -216,13 +220,14 @@ export default class Index extends PreviewBaseComponent { // Timeline post count const postCountY = height * 0.3 + margin + 10; - ctx.font = `Bold ${bodyFontSize}em ${font}`; + ctx.beginPath(); + ctx.font = `700 ${bodyFontSize}em '${font}'`; ctx.fillStyle = colors.primary; ctx.fillText("1 / 20", timelineX + margin / 2, postCountY); // Timeline post date ctx.beginPath(); - ctx.font = `${bodyFontSize * 0.9}em ${font}`; + ctx.font = `${bodyFontSize * 0.9}em '${font}'`; ctx.fillStyle = darkLightDiff(colors.primary, colors.secondary, 70, 65); ctx.fillText( "Nov 22", diff --git a/app/assets/javascripts/discourse/app/static/wizard/lib/preview.js b/app/assets/javascripts/discourse/app/static/wizard/lib/preview.js index 7f1cbb27788..90cfd4ec361 100644 --- a/app/assets/javascripts/discourse/app/static/wizard/lib/preview.js +++ b/app/assets/javascripts/discourse/app/static/wizard/lib/preview.js @@ -138,3 +138,26 @@ export function drawHeader(ctx, colors, width, headerHeight) { ctx.fill(); ctx.restore(); } + +export function resizeTextLinesToFitRect( + textLines, + rectWidth, + ctx, + fontSize, + font, + renderCallback +) { + const maxLengthLine = textLines.reduce((a, b) => + a.length > b.length ? a : b + ); + + let fontSizeDecreaseMultiplier = 1; + while (ctx.measureText(maxLengthLine).width > rectWidth) { + fontSizeDecreaseMultiplier -= 0.1; + ctx.font = `${fontSize * fontSizeDecreaseMultiplier}em '${font}'`; + } + + for (let i = 0; i < textLines.length; i++) { + renderCallback(textLines[i], i); + } +} diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index e525aa9fc8c..fb0d81ca9a2 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -72,7 +72,7 @@ class SiteSetting < ActiveRecord::Base end def self.top_menu_items - top_menu.split("|").map { |menu_item| TopMenuItem.new(menu_item) } + top_menu_map.map { |menu_item| TopMenuItem.new(menu_item) } end def self.homepage diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 06866fe8dcb..1eb5e714c4b 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -7390,6 +7390,14 @@ en: homepage_preview: "Homepage preview" homepage_preview: + nav_buttons: + all_categories: "all categories" + new: "New" + unread: "Unread" + top: "Top" + latest: "Latest" + hot: "Hot" + categories: "Categories" topic_titles: what_books: "What books are you reading?" what_movies: "What movies have you seen recently?" @@ -7409,3 +7417,8 @@ en: icebreakers: "Icebreakers" news: "News" site_feedback: "Site Feedback" + table_headers: + topic: "Topic" + replies: "Replies" + views: "Views" + activity: "Activity" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 6eec0e06a31..c5e16d9c072 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -5423,6 +5423,8 @@ en: choices: latest: label: "Latest Topics" + hot: + label: "Hot Topics" categories_only: label: "Categories Only" categories_with_featured_topics: diff --git a/lib/stylesheet/importer.rb b/lib/stylesheet/importer.rb index 9f35ee37df2..855ac522bc8 100644 --- a/lib/stylesheet/importer.rb +++ b/lib/stylesheet/importer.rb @@ -222,7 +222,7 @@ module Stylesheet ) contents << <<~CSS @font-face { - font-family: #{font[:name]}; + font-family: '#{font[:name]}'; src: #{src}; font-weight: #{variant[:weight]}; } diff --git a/lib/wizard/builder.rb b/lib/wizard/builder.rb index 6ffb989e370..102fb6f9051 100644 --- a/lib/wizard/builder.rb +++ b/lib/wizard/builder.rb @@ -191,7 +191,7 @@ class Wizard current = ( - if SiteSetting.top_menu.starts_with?("categories") + if SiteSetting.top_menu_map.first == "categories" SiteSetting.desktop_category_page_style else "latest" @@ -214,8 +214,8 @@ class Wizard updater.update_setting(:base_font, updater.fields[:body_font]) updater.update_setting(:heading_font, updater.fields[:heading_font]) - top_menu = SiteSetting.top_menu.split("|") - if updater.fields[:homepage_style] == "latest" && top_menu[0] != "latest" + top_menu = SiteSetting.top_menu_map + if updater.fields[:homepage_style] == "latest" && top_menu.first != "latest" top_menu.delete("latest") top_menu.insert(0, "latest") elsif updater.fields[:homepage_style] != "latest"