Merge branch 'main' into production
This commit is contained in:
commit
b3d0a8c6ad
30 changed files with 113 additions and 151 deletions
|
@ -110,7 +110,7 @@ GEM
|
|||
rubocop (>= 1.48.1, < 2.0)
|
||||
rubocop-ast (>= 1.30.0, < 2.0)
|
||||
ruby-progressbar (1.13.0)
|
||||
ryo.rb (0.4.7)
|
||||
ryo.rb (0.5.1)
|
||||
sass (3.7.4)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
|
|
66
Rules
66
Rules
|
@ -2,23 +2,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "ryo"
|
||||
require "ryo/json"
|
||||
require "nanoc-gzip"
|
||||
require "nanoc-webpack"
|
||||
require "nanoc-tidy"
|
||||
require_relative "nanoc/lib/require_rules"
|
||||
|
||||
locales = %w[ar en]
|
||||
slugs = Ryo.from(
|
||||
JSON.parse(
|
||||
File.read(File.join(Dir.getwd, "src", "json", "slugs.json"))
|
||||
)
|
||||
)
|
||||
i18n = Ryo.from(
|
||||
JSON.parse(
|
||||
File.read(File.join(Dir.getwd, "src", "json", "i18n.json"))
|
||||
)
|
||||
)
|
||||
|
||||
buildenv = ENV["buildenv"] || "development"
|
||||
##
|
||||
# Common vars
|
||||
locales = %w[ar en]
|
||||
json_dir = File.join(Dir.getwd, "src", "json")
|
||||
name_by_id = Ryo.from_json_file("#{json_dir}/nameById.json")
|
||||
i18n = Ryo.from_json_file("#{json_dir}/i18n.json")
|
||||
buildenv = ENV["buildenv"] || "development"
|
||||
Nanoc::Webpack.default_options.merge!(
|
||||
"--config" => "webpack.#{buildenv}.js"
|
||||
)
|
||||
|
@ -26,44 +22,8 @@ Nanoc::Tidy.default_options.merge!(
|
|||
"-upper" => true
|
||||
)
|
||||
|
||||
def require_rules(rules, locals = {}, target = binding)
|
||||
locals.each { target.local_variable_set(_1, _2) }
|
||||
path = File.join(Dir.getwd, rules)
|
||||
target.eval(
|
||||
if File.readable?(path)
|
||||
File.read(path)
|
||||
elsif File.readable?("#{path}.rb")
|
||||
File.read("#{path}.rb")
|
||||
elsif File.readable?("#{path}.rules")
|
||||
File.read("#{path}.rules")
|
||||
else
|
||||
raise LoadError, "#{path} is not readable"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
##
|
||||
# Inline CSS / JSON rules
|
||||
compile "/i18n.json" do
|
||||
filter(:minify_json)
|
||||
write(nil)
|
||||
end
|
||||
|
||||
compile "/surahs.json" do
|
||||
filter(:minify_json)
|
||||
write(nil)
|
||||
end
|
||||
|
||||
compile "/slugs.json" do
|
||||
filter(:minify_json)
|
||||
write(nil)
|
||||
end
|
||||
|
||||
compile "/recitations.json" do
|
||||
filter(:minify_json)
|
||||
write(nil)
|
||||
end
|
||||
|
||||
# See packages/typescript/postman
|
||||
compile "/css/postman.scss" do
|
||||
filter :sass, syntax: :scss, style: :compact
|
||||
filter :rainpress
|
||||
|
@ -73,7 +33,7 @@ end
|
|||
##
|
||||
# /sitemap.xml
|
||||
compile "/sitemap.xml.erb" do
|
||||
filter(:erb, locals: {locales:, slugs:})
|
||||
filter(:erb, locals: {locales:, name_by_id:})
|
||||
filter(:strip)
|
||||
write("/sitemap.xml")
|
||||
end
|
||||
|
@ -88,8 +48,8 @@ end
|
|||
# Require rules
|
||||
require_rules "nanoc/rules/assets"
|
||||
require_rules "nanoc/rules/redirect"
|
||||
require_rules "nanoc/rules/random", {locales:, i18n:, slugs:}
|
||||
require_rules "nanoc/rules/stream", {locales:, i18n:, slugs:}
|
||||
require_rules "nanoc/rules/random", {locales:}
|
||||
require_rules "nanoc/rules/stream", {locales:, i18n:, name_by_id:}
|
||||
require_rules "nanoc/rules/index", {locales:, i18n:}
|
||||
|
||||
##
|
||||
|
|
15
nanoc/lib/require_rules.rb
Normal file
15
nanoc/lib/require_rules.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
def require_rules(rules, locals = {}, target = binding)
|
||||
locals.each { target.local_variable_set(_1, _2) }
|
||||
path = File.join(Dir.getwd, rules)
|
||||
target.eval(
|
||||
if File.readable?(path)
|
||||
File.read(path)
|
||||
elsif File.readable?("#{path}.rb")
|
||||
File.read("#{path}.rb")
|
||||
elsif File.readable?("#{path}.rules")
|
||||
File.read("#{path}.rules")
|
||||
else
|
||||
raise LoadError, "#{path} is not readable"
|
||||
end
|
||||
)
|
||||
end
|
|
@ -15,6 +15,6 @@ locales.each do |locale|
|
|||
end
|
||||
|
||||
compile "/js/main/random.ts" do
|
||||
filter(:webpack)
|
||||
filter(:webpack, depend_on: ["/js/lib/"])
|
||||
write("/js/main/random.js")
|
||||
end
|
||||
|
|
|
@ -12,6 +12,6 @@ compile "/html/redirect.html.erb" do
|
|||
end
|
||||
|
||||
compile "/js/main/redirect.ts" do
|
||||
filter(:webpack)
|
||||
filter(:webpack, depend_on: ["/js/lib/"])
|
||||
write("/js/main/redirect.js")
|
||||
end
|
||||
|
|
|
@ -12,22 +12,22 @@ compile "/*/*/surah.json" do
|
|||
end
|
||||
end
|
||||
|
||||
Ryo.each(slugs) do |id, slug|
|
||||
Ryo.each(name_by_id) do |id, transliterated_name|
|
||||
writer = ->(locale, identifier:) do
|
||||
name = i18n[locale].surahs.names[id.to_i - 1]
|
||||
context = Ryo.from(
|
||||
filename: "stream.html.erb",
|
||||
locale:,
|
||||
locales:,
|
||||
surah: {id:, name:, slug:}
|
||||
surah: {id:, name:, transliterated_name:}
|
||||
)
|
||||
filter(:erb, locals: {context:})
|
||||
filter(:tidy)
|
||||
write "/#{locale}/#{identifier}/index.html"
|
||||
end
|
||||
locales.each do |locale|
|
||||
compile "/html/stream.html.erb", rep: "/#{locale}/#{slug}/index.html" do
|
||||
instance_exec(locale, identifier: slug, &writer)
|
||||
compile "/html/stream.html.erb", rep: "/#{locale}/#{transliterated_name}/index.html" do
|
||||
instance_exec(locale, identifier: transliterated_name, &writer)
|
||||
end
|
||||
compile "/html/stream.html.erb", rep: "/#{locale}/#{id}/index.html" do
|
||||
instance_exec(locale, identifier: id, &writer)
|
||||
|
@ -37,8 +37,7 @@ end
|
|||
|
||||
compile "/js/main/surah-stream.tsx" do
|
||||
filter :webpack,
|
||||
depend_on: ["/js/components", "/js/lib/", "/js/hooks"],
|
||||
reject: proc { _1.include?("WebPackage") }
|
||||
depend_on: ["/js/components", "/js/lib/", "/js/hooks"]
|
||||
write "/js/main/surah-stream.js"
|
||||
filter :gzip
|
||||
write "/js/main/surah-stream.js.gz"
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
<%= erb("partials/favicon.html.erb") %>
|
||||
</head>
|
||||
<body>
|
||||
<%= inline_json("/json/slugs.json") %>
|
||||
<script src="/js/main/random.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -10,14 +10,14 @@
|
|||
<link
|
||||
rel="canonical"
|
||||
hreflang="<%= context.locale %>"
|
||||
href="https://al-quran.reflectslight.io/<%= context.locale %>/<%= context.surah.slug %>/"
|
||||
href="https://al-quran.reflectslight.io/<%= context.locale %>/<%= context.surah.transliterated_name %>/"
|
||||
/>
|
||||
<% context.locales.each do |locale| %>
|
||||
<link
|
||||
rel="alternate"
|
||||
type="text/html"
|
||||
hreflang="<%= locale %>"
|
||||
href="https://al-quran.reflectslight.io/<%= locale %>/<%= context.surah.slug %>/"
|
||||
href="https://al-quran.reflectslight.io/<%= locale %>/<%= context.surah.transliterated_name %>/"
|
||||
/>
|
||||
<% end %>
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import url from "url";
|
||||
import * as Quran from "lib/Quran";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { SoundOnIcon, SoundOffIcon } from "components/Icon";
|
||||
import { SoundOnIcon, SoundOffIcon } from "~/components/Icon";
|
||||
|
||||
type Props = {
|
||||
recitation: Quran.Recitation;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { Select } from "components/Select";
|
||||
import { Select } from "~/components/Select";
|
||||
|
||||
interface Props {
|
||||
locale: string;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useEffect, useMemo, useRef } from "react";
|
||||
import * as Quran from "lib/Quran";
|
||||
import { AudioControl } from "components/AudioControl";
|
||||
import { formatNumber, TFunction } from "lib/i18n";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
import { AudioControl } from "~/components/AudioControl";
|
||||
import { formatNumber, TFunction } from "~/lib/i18n";
|
||||
import classNames from "classnames";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import * as Quran from "lib/Quran";
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import { useTheme } from "hooks/useTheme";
|
||||
import { ThemeSelect } from "components/ThemeSelect";
|
||||
import { LanguageSelect } from "components/LanguageSelect";
|
||||
import { formatNumber, TFunction } from "lib/i18n";
|
||||
import { RightArrow } from "components/Icon";
|
||||
import { SurahIndexFilter } from "components/SurahIndexFilter";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
import { useTheme } from "~/hooks/useTheme";
|
||||
import { ThemeSelect } from "~/components/ThemeSelect";
|
||||
import { LanguageSelect } from "~/components/LanguageSelect";
|
||||
import { formatNumber, TFunction } from "~/lib/i18n";
|
||||
import { RightArrow } from "~/components/Icon";
|
||||
import { SurahIndexFilter } from "~/components/SurahIndexFilter";
|
||||
import classNames from "classnames";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import { TFunction } from "lib/i18n";
|
||||
import * as Quran from "lib/Quran";
|
||||
import { TFunction } from "~/lib/i18n";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
|
||||
type Props = {
|
||||
t: TFunction;
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
import * as Quran from "lib/Quran";
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { useTheme } from "hooks/useTheme";
|
||||
import { Timer } from "components/Timer";
|
||||
import { Stream } from "components/Stream";
|
||||
import { ThemeSelect } from "components/ThemeSelect";
|
||||
import { LanguageSelect } from "components/LanguageSelect";
|
||||
import { AudioControl } from "components/AudioControl";
|
||||
import { PlayIcon, PauseIcon, RefreshIcon, StalledIcon } from "components/Icon";
|
||||
import classNames from "classnames";
|
||||
import { TFunction } from "lib/i18n";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
import { useTheme } from "~/hooks/useTheme";
|
||||
import { Timer } from "~/components/Timer";
|
||||
import { Stream } from "~/components/Stream";
|
||||
import { ThemeSelect } from "~/components/ThemeSelect";
|
||||
import { LanguageSelect } from "~/components/LanguageSelect";
|
||||
import { AudioControl } from "~/components/AudioControl";
|
||||
import {
|
||||
PlayIcon,
|
||||
PauseIcon,
|
||||
RefreshIcon,
|
||||
StalledIcon,
|
||||
} from "~/components/Icon";
|
||||
import { TFunction } from "~/lib/i18n";
|
||||
|
||||
interface Props {
|
||||
node: HTMLScriptElement;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import { Select } from "components/Select";
|
||||
import type { Theme } from "hooks/useTheme";
|
||||
import { Select } from "~/components/Select";
|
||||
import type { Theme } from "~/hooks/useTheme";
|
||||
|
||||
interface Props {
|
||||
theme: string;
|
||||
setTheme: (theme: Theme) => void;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect } from "react";
|
||||
import * as Quran from "lib/Quran";
|
||||
import { formatNumber } from "lib/i18n";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
import { formatNumber } from "~/lib/i18n";
|
||||
|
||||
interface Props {
|
||||
surah: Quran.Surah;
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
export interface Locale {
|
||||
fromBrowser: () => string;
|
||||
fromPath: () => string | undefined;
|
||||
}
|
||||
|
||||
export function Locale(window: Window): Locale {
|
||||
const self = Object.create(null);
|
||||
const { navigator, location } = window;
|
||||
const locales = ["ar", "en"];
|
||||
|
||||
self.fromBrowser = () => {
|
||||
return (
|
||||
navigator.languages
|
||||
.map(lang => lang.substr(0, 2))
|
||||
.find(locale => locales.includes(locale)) || "en"
|
||||
);
|
||||
};
|
||||
|
||||
self.fromPath = () => {
|
||||
return location.pathname
|
||||
.split("/")
|
||||
.filter(s => s.length)
|
||||
.at(0);
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import * as JSON from "lib/Quran/JSON";
|
||||
import { Ayah } from "lib/Quran/Ayah";
|
||||
import { Surah } from "lib/Quran/Surah";
|
||||
import * as JSON from "~/lib/Quran/JSON";
|
||||
import { Ayah } from "~/lib/Quran/Ayah";
|
||||
import { Surah } from "~/lib/Quran/Surah";
|
||||
|
||||
type Locale = "ar" | "en";
|
||||
type Ayat = Ayah[];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as Quran from "lib/Quran";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
|
||||
export type Ayah = {
|
||||
id: number;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as Quran from "lib/Quran";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
|
||||
type TimeSlot = [number, number];
|
||||
type TimeSlots = [TimeSlot];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as Quran from "lib/Quran";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
|
||||
type PhraseMap<T> = {
|
||||
[key: string]: undefined | string | PhraseMap<T>;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import postman, { item } from "postman";
|
||||
import * as Quran from "lib/Quran";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
|
||||
(function () {
|
||||
const parent: HTMLElement = document.querySelector(".postman.loader")!;
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { Locale } from "lib/Locale";
|
||||
|
||||
(function () {
|
||||
const nameById = require("@json/nameById.json");
|
||||
const surahId: number = Math.ceil(Math.random() * 114);
|
||||
const locale = Locale(window);
|
||||
const el: HTMLElement = document.querySelector(".json.slugs")!;
|
||||
const slugs = JSON.parse(el.innerText);
|
||||
location.replace(`/${locale.fromPath()}/${slugs[surahId]}`);
|
||||
const name = nameById[surahId];
|
||||
const locale = location.pathname.slice(1, 3);
|
||||
location.replace(["", locale, name, ""].join("/"));
|
||||
})();
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import { Locale } from "lib/Locale";
|
||||
|
||||
(function (window, location) {
|
||||
const locale = Locale(window).fromBrowser();
|
||||
location.replace(`/${locale}/`);
|
||||
})(window, location);
|
||||
(function () {
|
||||
const LOCALES = ["ar", "en"];
|
||||
const DEFAULT_LOCALE = "en";
|
||||
function getUserLocale() {
|
||||
return (
|
||||
navigator.languages
|
||||
.map(s => s.slice(0, 2))
|
||||
.find(s => LOCALES.includes(s)) || DEFAULT_LOCALE
|
||||
);
|
||||
}
|
||||
location.replace(["", getUserLocale(), ""].join("/"));
|
||||
})();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import * as Quran from "lib/Quran";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { i18n } from "lib/i18n";
|
||||
import { SurahIndex } from "components/SurahIndex";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
import { i18n } from "~/lib/i18n";
|
||||
import { SurahIndex } from "~/components/SurahIndex";
|
||||
|
||||
(function () {
|
||||
const root: HTMLElement = document.querySelector(".root")!;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import * as Quran from "lib/Quran";
|
||||
import * as Quran from "~/lib/Quran";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { i18n } from "lib/i18n";
|
||||
import { SurahStream } from "components/SurahStream";
|
||||
import { i18n } from "~/lib/i18n";
|
||||
import { SurahStream } from "~/components/SurahStream";
|
||||
|
||||
(function () {
|
||||
const root: HTMLElement = document.querySelector(".root")!;
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
<priority>1.0</priority>
|
||||
<changefreq>weekly</changefreq>
|
||||
</url>
|
||||
<% Ryo.each(slugs) do |_id, slug| %>
|
||||
<% Ryo.each(name_by_id) do |_id, transliterated_name| %>
|
||||
<url>
|
||||
<loc>https://al-quran.reflectslight.io/<%= locale %>/<%= slug %>/</loc>
|
||||
<loc>https://al-quran.reflectslight.io/<%= locale %>/<%= transliterated_name %>/</loc>
|
||||
<priority>0.7</priority>
|
||||
<changefreq>weekly</changefreq>
|
||||
</url>
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
"lib": [ "ES2020", "DOM" ],
|
||||
|
||||
"baseUrl": "src/",
|
||||
"paths": { "*": ["js/*"] },
|
||||
"paths": {
|
||||
"~/*": ["./js/*"],
|
||||
"@json/*": ["./json/*"]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,12 @@ const process = require("process");
|
|||
|
||||
module.exports = {
|
||||
resolve: {
|
||||
roots: [path.resolve("src/js"), path.resolve("node_modules")],
|
||||
modules: [path.resolve("src/js"), path.resolve("node_modules")],
|
||||
extensions: [".js", ".ts", ".tsx"],
|
||||
modules: [path.resolve(__dirname, "node_modules")],
|
||||
alias: {
|
||||
"@json": path.resolve(__dirname, "src/json"),
|
||||
"~": path.resolve(__dirname, "src/js"),
|
||||
},
|
||||
extensions: [".js", ".ts", ".tsx", ".json"],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
|
|
Loading…
Reference in a new issue