Compare commits

...

96 commits

Author SHA1 Message Date
76b58e0cc3 docs: update README
Some checks are pending
The Quran / ci (push) Waiting to run
2024-10-18 01:39:08 -03:00
e82c059098 Fix CI
Some checks failed
The Quran / ci (push) Has been cancelled
2024-10-16 21:41:09 -03:00
36a472a506 'ref' might be undefined
Some checks failed
The Quran / ci (push) Has been cancelled
2024-10-13 12:15:49 -03:00
ebd4bb36ca Revisit ThemeSelect.tsx
Some checks are pending
The Quran / ci (push) Waiting to run
2024-10-13 10:57:30 -03:00
8f409c4d68 Update .editorconfig 2024-10-13 03:42:36 -03:00
1b1d72246f Set width: 85% at <= $breakpoint-sm
Some checks failed
The Quran / ci (push) Has been cancelled
2024-10-09 19:24:37 -03:00
156a34dc9a Reduce default width
Some checks are pending
The Quran / ci (push) Waiting to run
2024-10-08 22:30:32 -03:00
900547c44e Add src/images/albumcover-600x600.png
Some checks failed
The Quran / ci (push) Has been cancelled
2024-10-07 00:29:55 -03:00
5c2bdea8b4 Update .editorconfig
Some checks are pending
The Quran / ci (push) Waiting to run
2024-10-07 00:03:21 -03:00
938c4f5fae Cache-bust MP3 content
Some checks are pending
The Quran / ci (push) Waiting to run
2024-10-06 23:59:36 -03:00
bfec7a8069 Hide SurahIndexFilter.tsx at <= $breakpoint-sm 2024-10-05 20:30:19 -03:00
8d2138468c Align StalledIcon
Some checks failed
The Quran / ci (push) Has been cancelled
2024-10-04 15:30:36 -03:00
026b5248c8 Fix layout on low-res devices
Some checks are pending
The Quran / ci (push) Waiting to run
2024-10-04 15:02:30 -03:00
74d444031c Fix build
Some checks are pending
The Quran / ci (push) Waiting to run
2024-10-04 12:53:48 -03:00
265b580a08 Apply Amiri font for Farsi
Some checks are pending
The Quran / ci (push) Waiting to run
2024-10-04 12:52:02 -03:00
4146973cf6 Center-align surah content
Some checks are pending
The Quran / ci (push) Waiting to run
2024-10-04 12:15:35 -03:00
3802de7df7 Update CHANGELOG.txt
Some checks failed
The Quran / ci (push) Has been cancelled
2024-10-02 11:09:48 -03:00
b9471c8421 s|Save time|Automation|g 2024-10-02 10:04:37 -03:00
d9cd9f680e Revisit howto_add_language.txt 2024-10-02 07:36:55 -03:00
2f349df4ec Apply async / await pattern 2024-10-02 07:13:48 -03:00
a28903c8b7 Revisit the loader implementation (again) 2024-10-02 06:44:54 -03:00
e24cc3d7fc Update postman.css 2024-10-02 06:44:19 -03:00
84b19edf04 Revisit the loader implementation 2024-10-02 01:09:49 -03:00
355a4c75b5 s|doc|docel|g 2024-10-02 00:40:31 -03:00
ed0d493a17 Apply font to Select.Option elements
Based on whether or not the element is for a RTL or LTR language
2024-10-02 00:32:55 -03:00
8518b5f491 Update loaders 2024-10-02 00:18:00 -03:00
b829068bd5 Update AnonymousFilter 2024-10-02 00:00:15 -03:00
a6973b439d mobile: revert from two columns to one
Some checks are pending
The Quran / ci (push) Waiting to run
2024-10-01 20:18:31 -03:00
69f8b0511a Replace 'Amiri Quran Regular' with 'Amiri Regular'
Some checks are pending
The Quran / ci (push) Waiting to run
2024-10-01 17:53:43 -03:00
111ae08175 Format CHANGELOG.txt 2024-09-30 22:50:04 -03:00
438cf9dab4 Fix CHANGELOG.txt
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-30 16:00:54 -03:00
4d44e72954 Load fonts on-demand
Some checks are pending
The Quran / ci (push) Waiting to run
The same set of fonts are not used across RTL and LTR languages. This
change only loads fonts that are being used
2024-09-30 15:50:08 -03:00
2324189992 Add fallback fonts
Some checks failed
The Quran / ci (push) Has been cancelled
2024-09-25 14:29:04 -03:00
de3fcdde1a Remove "Amiri Quran Regular" from SurahIndexLoader.ts 2024-09-25 14:25:55 -03:00
78dba7fe57 Apply "Cairo Bold" for bold hover text in footer
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-25 10:56:46 -03:00
bd4502cb63 Render two columns for RTL languages
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-24 21:24:20 -03:00
18a454075d Fix StalledIcon component
Some checks failed
The Quran / ci (push) Has been cancelled
2024-09-21 18:47:37 -03:00
3250fae3f3 Hoist classes to .body { ... }
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 17:32:13 -03:00
4951dada90 Scope styles within .content.theme
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 17:25:36 -03:00
d6d6b10be3 Apply classes regardless of locale direction
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 17:01:05 -03:00
743f326343 Optimize inline of postman.css
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 14:23:58 -03:00
5023b494b1 Update LICENSE.txt
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 14:10:17 -03:00
8adfca235a mv howto_add_language howto_add_language.txt
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 14:06:42 -03:00
9cce03e299 mv LICENSE LICENSE.txt
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 14:04:52 -03:00
cd2d320c6f mv CHANGELOG CHANGELOG.txt
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 14:01:21 -03:00
f5ba2b9388 Add border around LanguageSelect elements
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 13:56:04 -03:00
deb61e94bd Fix scrollbar color
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 13:44:19 -03:00
4ec69db283 Apply 'direction: rtl;' once
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 11:36:28 -03:00
8be703b261 Add sources
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 08:12:59 -03:00
df84efbda5 Fix 'rake ci'
Some checks are pending
The Quran / ci (push) Waiting to run
2024-09-21 07:50:16 -03:00
34c3fccd6c Reduce theme colors to three (primary, secondary, accent)
Removes --white-color & --black-color
2024-09-21 07:28:01 -03:00
1b3af0b6d7 Remove unused file 2024-09-20 22:08:37 -03:00
d31160e7f5 Update puma 2024-09-20 22:05:38 -03:00
3a2873b2cc Remove KaiOS breakpoints 2024-09-20 22:04:10 -03:00
188797bf58 Iterate on the theme system 2024-09-20 21:58:37 -03:00
3f662805e4 v0.10.0 2024-09-19 16:07:58 -03:00
5c0e26183c s|mt-1|mt-2| 2024-09-19 14:42:45 -03:00
058f423e4a Reduce padding between ayah number and ayah content 2024-09-19 14:39:00 -03:00
5cd13e28ab Small UI updates 2024-09-19 11:24:26 -03:00
685b1415ca Update CHANGELOG 2024-09-19 10:40:44 -03:00
22bb9301e2 Biggest UI change in a while 2024-09-19 10:37:48 -03:00
38224a7417 Apply bold font to Timer.tsx 2024-09-18 16:28:37 -03:00
92b0f4f6c1 Apply bold font to surah numbers (SurahIndex.tsx) 2024-09-18 16:21:01 -03:00
f66040965d Render three columns 2024-09-18 16:05:23 -03:00
44e143254e Add "Amiri Quran Regular" font to SurahIndex.tsx 2024-09-18 15:56:15 -03:00
07ce6cdf61 Update CHANGELOG 2024-09-18 15:32:03 -03:00
630592d71f Revisit SurahStream for RTL languages 2024-09-18 14:42:45 -03:00
7a9091fd6c Add font: "Amiri Quran Regular" 2024-09-18 10:53:36 -03:00
bf2d01c812 Replace custom z-index rule 2024-09-17 23:24:35 -03:00
03e33bdc37 tail.css v0.2.0 2024-09-17 23:15:18 -03:00
3e7f3271d5 Move text-2xl onto <a> element 2024-09-17 22:50:24 -03:00
024fc1c4ed Apply font-semibold in Head.tsx 2024-09-17 15:45:42 -03:00
4889d67157 Fix CI 2024-09-16 19:39:07 -03:00
9599c2ca5f Update CHANGELOG 2024-09-16 18:46:00 -03:00
78f7fc2076 Run prettier 2024-09-16 18:04:24 -03:00
590e912066 Add 'rake format:apply' 2024-09-16 18:02:46 -03:00
ca8fe77554 Bump the font size in Head component 2024-09-16 18:02:17 -03:00
26a989f6b7 Drop /robots.txt
Since /audio is now hosted on a separate domain, we don't need
robots.txt anymore
2024-09-15 06:26:04 -03:00
1b78d3c2c9 Update CHANGELOG 2024-09-15 05:41:11 -03:00
b66e66caee Add ability to set file mode on unix socket 2024-09-15 05:37:22 -03:00
06ae1173b6 Revisit CHANGELOG 2024-09-14 06:29:13 -03:00
4573fbae0e Remove submodules/ 2024-09-14 06:19:41 -03:00
195dad288a v0.9.1 2024-09-14 06:06:45 -03:00
e7d1241459 Fix render of _opengraph.html.erb 2024-09-14 05:55:14 -03:00
a5f66044e1 Update sort 2024-08-31 00:46:48 -03:00
cfce51e29c Avoid append of # to URL 2024-08-31 00:36:52 -03:00
735e8bcc20 Appease TypeScript and fix bug 2024-08-31 00:34:07 -03:00
d4588413e4 Remove unused import 2024-08-31 00:31:47 -03:00
21263d4574 Fix (most) TypeScript errors 2024-08-31 00:29:19 -03:00
105b7b18cb Replace 'font-size' rules with tail.css classes 2024-08-30 20:16:02 -03:00
fa47510dd2 Enhance RTL layouts 2024-08-30 15:16:19 -03:00
22fb6b87fa Replace mada-regular.ttf 2024-08-30 14:28:08 -03:00
07788b4e5e Inject vendor.js via loader 2024-08-30 14:07:23 -03:00
94aaa6e10c Gzip vendor.js (revert previous commit) 2024-08-30 11:00:34 -03:00
de96eccd7e Try plain JS (vendor.js) 2024-08-30 10:40:01 -03:00
a2a856cd45 Replace React with preact 2024-08-30 10:16:42 -03:00
86 changed files with 961 additions and 840 deletions

View file

@ -1,8 +1,14 @@
[*.rb,*.erb]
root = true
[*.html]
indent_style = space
indent_size = 2
[*.js,*.ts,*.tsx]
[*.rb, *.erb]
indent_style = space
indent_size = 2
[*.js, *.ts, *.tsx]
indent_style = space
indent_size = 2

9
.gitmodules vendored
View file

@ -1,9 +0,0 @@
[submodule "submodules/surah-name-glyphs"]
path = submodules/surah-name-glyphs
url = https://github.com/ReflectsLight/surah-name-glyphs
[submodule "submodules/quran-json"]
path = submodules/quran-json
url = https://github.com/ReflectsLight/quran-json
[submodule "submodules/tail.css"]
path = submodules/tail.css
url = https://github.com/0x1eef/tail.css

View file

@ -10,6 +10,7 @@ gem "nanoc", "~> 4.12"
gem "nanoc-gzip.rb"
gem "nanoc-webpack.rb", "~> 0.10"
gem "nanoc-tidy.rb", "~> 0.8.4"
gem "rainpress"
##
# dev

View file

@ -82,11 +82,12 @@ GEM
psych (5.1.2)
stringio
public_suffix (6.0.1)
puma (6.4.2)
puma (6.4.3)
nio4r (~> 2.0)
racc (1.8.1)
rack (3.1.7)
rainbow (3.1.1)
rainpress (1.0.1)
rake (13.2.1)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
@ -155,6 +156,7 @@ DEPENDENCIES
nanoc-tidy.rb (~> 0.8.4)
nanoc-webpack.rb (~> 0.10)
paint (~> 2.3)
rainpress
rake (~> 13.2)
rexml (~> 3.3.3)
ryo.rb

View file

@ -8,7 +8,9 @@ the build directory consists of HTML, CSS, JavaScript
and other static assets that can be hosted by
a web server such as nginx, apache, et cetera.
## Requirements
## Development
### Requirements
The following languages and tools have to be
installed to build the website from source:
@ -17,7 +19,7 @@ installed to build the website from source:
* NodeJS v18.15 (or later)
* [tidy-html5](https://github.com/htacg/tidy-html5)
## Development
### Examples
# Clone repository
git clone https://github.com/ReflectsLight/al-quran.reflectslight.io
@ -101,7 +103,13 @@ installed to build the website from source:
- Sahl Yassin
- Hani ar-Rifai
## Sources
* [GitHub](https://github.com/ReflectsLight/al-quran.reflectslight.io)
* [GitLab](https://gitlab.com/0x1eef/al-quran.reflectslight.io)
* [brew.bsd.cafe/@0x1eef](https://brew.bsd.cafe/0x1eef/al-quran.reflectslight.io)
## License
The "source code" is released under the terms of the GPL <br>
See [LICENSE](./share/al-quran.reflectslight.io/LICENSE) for details
See [LICENSE.txt](./share/al-quran.reflectslight.io/LICENSE.txt) for details

36
Rules
View file

@ -24,13 +24,10 @@ tdata = Ryo.from_json(path: File.join(dirs.content, "json", "t.json"))
surahs = Ryo.from_json(path: File.join(dirs.content, "json", "surahs.json"))
tidy = `which tidy || which tidy5`.chomp
buildenv = ENV["buildenv"] || "development"
etcdir = File.join(__dir__, "etc")
globals = {buildenv:, locales:, tidy:, tdata:, surahs:, name_by_id:}
##
# Filters
Nanoc::Webpack
.default_argv
.replace([*Nanoc::Webpack.default_argv, "--config", File.join(etcdir, "webpack.#{buildenv}.js")].uniq)
Nanoc::Tidy
.default_argv
.replace([*Nanoc::Tidy.default_argv, "-upper"].uniq)
@ -38,6 +35,7 @@ Nanoc::Tidy
##
# See packages/typescript/postman
compile "/css/vendor/postman.css" do
filter(:rainpress)
write("/css/vendor/postman.css")
end
@ -49,25 +47,21 @@ compile "/sitemap.xml.erb" do
write("/sitemap.xml")
end
##
# /robots.txt
compile "/robots.txt" do
write("/robots.txt")
end
##
# /json/durations/*.json
passthrough "/json/durations/*.json"
##
# Rules
require_rules "nanoc/rules/assets"
require_rules "nanoc/rules/redirect", {locales:, tidy:}
require_rules "nanoc/rules/random", {locales:, tdata:, tidy:}
require_rules "nanoc/rules/surah-stream", {locales:, tdata:, surahs:, name_by_id:, tidy:}
require_rules "nanoc/rules/surah-index", {locales:, tdata:, tidy:}
passthrough "/json/durations/*.json"
require_rules "nanoc/rules/assets", globals
require_rules "nanoc/rules/redirect", globals
require_rules "nanoc/rules/random", globals
require_rules "nanoc/rules/surah-stream", globals
require_rules "nanoc/rules/surah-index", globals
##
# Catch-all
compile "/js/main/vendor.ts" do
filter :webpack,
argv: %w[--config etc/webpack.vendor.js]
write("/js/main/vendor.js")
filter :gzip
write("/js/main/vendor.js.gz")
end
compile("/**/*") { write(nil) }
layout("**/*", :erb)

View file

@ -4,6 +4,7 @@ AllCops:
TargetRubyVersion: 3.2
Exclude:
- submodules/**/*
- source/**/*
Include:
- Rakefile.rb
- rake/tasks/*.rake

View file

@ -1,16 +1,19 @@
{
"include": ["../src/**/*.ts", "../src/**/*.tsx"],
"exclude": ["../node_modules"],
"include": [
"../src/**/*.ts",
"../src/**/*.tsx",
],
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"module": "commonjs",
"target": "ES2020",
"noImplicitAny": true,
"moduleResolution": "node",
"esModuleInterop": true,
"jsx": "react",
"jsx": "react-jsx",
"lib": [ "ES2020", "DOM" ],
"typeRoots": ["../src/js/types/globals.d.ts", "../node_modules/@types"],
"skipLibCheck": true,
"baseUrl": "../src/",
"paths": {
"~/*": ["./js/*"],

View file

@ -7,6 +7,10 @@ module.exports = {
"@css": path.resolve(__dirname, "..", "src", "css"),
"@json": path.resolve(__dirname, "..", "src", "json"),
"~": path.resolve(__dirname, "..", "src", "js"),
"react": "preact/compat",
"react-dom/test-utils": "preact/test-utils",
"react-dom": "preact/compat",
"react/jsx-runtime": "preact/jsx-runtime",
},
extensions: [".js", ".ts", ".tsx", ".json", ".css", ".scss"],
},

14
etc/webpack.vendor.js Normal file
View file

@ -0,0 +1,14 @@
const path = require('path');
const common = require('./webpack.common.js');
const { merge } = require('webpack-merge');
module.exports = (env, argv) => {
return merge(
common,
{
output: { library: { type: 'umd' } },
resolve: { extensions: ['.ts', '.tsx'] },
optimization: { minimize: true }
}
)
}

View file

@ -27,6 +27,7 @@ server:
base_url: https://al-quran.reflectslight.io
unix:
path:
mode: ug=rw,o=
tcp:
host: 127.0.0.1
port: 7777

View file

@ -9,9 +9,8 @@ module Nanoc::Extension
require "securerandom"
##
# @example
# compile "/sitemap.xml.erb" do
# filter(:erb)
# filter Proc.new { _1.each_line.reject { |s| s.strip.empty? }.join }
# compile "/sitemap.xml" do
# filter proc { _1.chomp }
# write("/sitemap.xml")
# end
# @param [Proc, Symbol] fn
@ -21,9 +20,9 @@ module Nanoc::Extension
# @return [void]
def filter(fn, options = {})
if Proc === fn
id = anonymous_id
Nanoc::Filter.define(id) { fn.call(_1, _2) }
super(id, options)
anonid = anonymous_id
Nanoc::Filter.define(anonid) { fn.call(_1, _2) }
super(anonid, options)
else
super(fn, options)
end

View file

@ -15,7 +15,9 @@ locales.each do |locale|
end
compile "/js/main/random.ts" do
filter(:webpack, depend_on: ["/js/lib/"])
filter :webpack,
argv: %W[--config etc/webpack.#{buildenv}.js],
depend_on: ["/js/lib/"]
write("/js/main/random.js")
filter(:gzip)
write("/js/main/random.js.gz")

View file

@ -12,7 +12,9 @@ compile "/html/main/redirect.html.erb" do
end
compile "/js/main/redirect.ts" do
filter(:webpack, depend_on: ["/js/lib/"])
filter :webpack,
argv: %W[--config etc/webpack.#{buildenv}.js],
depend_on: ["/js/lib/"]
write("/js/main/redirect.js")
filter(:gzip)
write("/js/main/redirect.js.gz")

View file

@ -15,19 +15,22 @@ locales.each do |locale|
end
compile "/js/main/surah-index.tsx" do
filter :webpack, depend_on: [
"/js/components",
"/js/lib",
"/js/hooks",
"/css"
]
filter :webpack,
argv: %W[--config etc/webpack.#{buildenv}.js],
depend_on: [
"/js/components",
"/js/lib",
"/js/hooks",
"/css"
]
write "/js/main/surah-index.js"
filter :gzip
write "/js/main/surah-index.js.gz"
end
compile "/js/loaders/SurahIndexLoader.ts" do
filter :webpack
filter :webpack,
argv: %W[--config etc/webpack.#{buildenv}.js]
write "/js/loaders/surah-index-loader.js"
filter :gzip
write "/js/loaders/surah-index-loader.js.gz"

View file

@ -32,19 +32,22 @@ Ryo.each(name_by_id) do |id, slug|
end
compile "/js/main/surah-stream.tsx" do
filter :webpack, depend_on: [
"/js/components",
"/js/lib",
"/js/hooks",
"/css"
]
filter :webpack,
argv: %W[--config etc/webpack.#{buildenv}.js],
depend_on: [
"/js/components",
"/js/lib",
"/js/hooks",
"/css"
]
write "/js/main/surah-stream.js"
filter :gzip
write "/js/main/surah-stream.js.gz"
end
compile "/js/loaders/SurahStreamLoader.ts" do
filter :webpack
filter :webpack,
argv: %W[--config etc/webpack.#{buildenv}.js]
write "/js/loaders/surah-stream-loader.js"
filter :gzip
write "/js/loaders/surah-stream-loader.js.gz"

99
package-lock.json generated
View file

@ -10,13 +10,14 @@
],
"dependencies": {
"classnames": "^2.3",
"react": "^18.2",
"react-dom": "^18.2"
"preact": "^10.23.2",
"react": "npm:@preact/compat",
"react-dom": "npm:@preact/compat",
"react-jsxruntime": "npm:@preact/compat"
},
"devDependencies": {
"@types/css-font-loading-module": "^0.0.13",
"@types/react": "^18.0",
"@types/react-dom": "^18.0",
"@types/react": "^18.3.5",
"css-loader": "^7.1",
"esbuild-loader": "^4.1",
"eslint": "^9.8",
@ -715,9 +716,9 @@
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.3.4",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz",
"integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==",
"version": "18.3.5",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz",
"integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -725,16 +726,6 @@
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
"version": "18.3.0",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
"integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz",
@ -2638,12 +2629,6 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@ -2780,18 +2765,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@ -3245,6 +3218,16 @@
"resolved": "packages/typescript/postman",
"link": true
},
"node_modules/preact": {
"version": "10.23.2",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.23.2.tgz",
"integrity": "sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -3346,28 +3329,33 @@
}
},
"node_modules/react": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"name": "@preact/compat",
"version": "17.1.2",
"resolved": "https://registry.npmjs.org/@preact/compat/-/compat-17.1.2.tgz",
"integrity": "sha512-7pOZN9lMDDRQ+6aWvjwTp483KR8/zOpfS83wmOo3zfuLKdngS8/5RLbsFWzFZMGdYlotAhX980hJ75bjOHTwWg==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
},
"engines": {
"node": ">=0.10.0"
"peerDependencies": {
"preact": "*"
}
},
"node_modules/react-dom": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"name": "@preact/compat",
"version": "17.1.2",
"resolved": "https://registry.npmjs.org/@preact/compat/-/compat-17.1.2.tgz",
"integrity": "sha512-7pOZN9lMDDRQ+6aWvjwTp483KR8/zOpfS83wmOo3zfuLKdngS8/5RLbsFWzFZMGdYlotAhX980hJ75bjOHTwWg==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
},
"peerDependencies": {
"react": "^18.3.1"
"preact": "*"
}
},
"node_modules/react-jsxruntime": {
"name": "@preact/compat",
"version": "17.1.2",
"resolved": "https://registry.npmjs.org/@preact/compat/-/compat-17.1.2.tgz",
"integrity": "sha512-7pOZN9lMDDRQ+6aWvjwTp483KR8/zOpfS83wmOo3zfuLKdngS8/5RLbsFWzFZMGdYlotAhX980hJ75bjOHTwWg==",
"license": "MIT",
"peerDependencies": {
"preact": "*"
}
},
"node_modules/readdirp": {
@ -3572,15 +3560,6 @@
}
}
},
"node_modules/scheduler": {
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
}
},
"node_modules/schema-utils": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",

View file

@ -10,13 +10,14 @@
},
"dependencies": {
"classnames": "^2.3",
"react": "^18.2",
"react-dom": "^18.2"
"preact": "^10.23.2",
"react": "npm:@preact/compat",
"react-dom": "npm:@preact/compat",
"react-jsxruntime": "npm:@preact/compat"
},
"devDependencies": {
"@types/css-font-loading-module": "^0.0.13",
"@types/react": "^18.0",
"@types/react-dom": "^18.0",
"@types/react": "^18.3.5",
"css-loader": "^7.1",
"esbuild-loader": "^4.1",
"eslint": "^9.8",

View file

@ -2,10 +2,10 @@ import type { Item, FontItem } from './postman/item';
import item from './postman/item';
import request from './postman/request';
type Postman = { fetch: () => Promise<Package> };
type Postman = { deliver: () => Promise<Parcel> };
type Args = Array<Item | FontItem | Function>
type Items = Array<Item | FontItem>;
type Package = {
export type Parcel = {
fonts: FontFace[]
images: HTMLElement[]
css: HTMLElement[]
@ -30,7 +30,7 @@ export { item };
export default function (...args: Args) {
const self: Postman = Object.create(null);
const result: Package = { fonts: [], images: [], css: [], scripts: [], json: [] };
const result: Parcel = { fonts: [], images: [], css: [], scripts: [], json: [] };
const [items, callback] = parseArgs(args);
items.sort((i1, i2) => i1.priority >= i2.priority ? 1 : -1);
@ -62,11 +62,11 @@ export default function (...args: Args) {
/* unreachable */
return null;
});
return reqs as Array<Promise<Package>>;
return reqs as Array<Promise<Parcel>>;
};
self.fetch = async () => {
await Promise.all<Package>(spawnRequests());
self.deliver = async () => {
await Promise.all<Parcel>(spawnRequests());
return result;
};

View file

@ -10,7 +10,7 @@ export default {
const { href } = item;
return fetch(href, options)
.then((res) => res.text())
.then((text) => ({ type: 'application/javascript', text }))
.then((text) => ({ type: 'application/javascript', text, ...item.props }))
.then((props) => Object.assign(document.createElement('script'), props));
},

View file

@ -1,6 +1,9 @@
# frozen_string_literal: true
namespace :format do
desc "Apply all formats"
task apply: %i[format:rubocop:apply format:eslint:apply]
desc "Run rubocop (Ruby)"
task :rubocop do
sh "bundle exec rubocop --config etc/rubocop.yml"

View file

@ -5,7 +5,7 @@ namespace :nanoc do
task :clean do
Dir.chdir(dirs.root) do
sh "rm -rf node_modules/.cache/"
sh "rm -rf build/"
sh "rm -rf #{nanoc.output_dir}/*"
sh "rm -rf tmp/"
end
end

View file

@ -1,19 +1,28 @@
# frozen_string_literal: true
cwd = File.realpath File.join(__dir__, "..", "..", ".")
desc "Start web server"
task :server, [:protocol] do |_t, args|
require "server"
nanoc = Ryo.from_yaml(path: File.join(cwd, "nanoc.yaml"))
h = args.to_h
p = h[:protocol] || "tcp"
n = File.basename(dirs.root)
n = File.basename(cwd)
o = if p == "unix"
{unix: nanoc.server.unix.path}
else
{host: nanoc.server.tcp.host, port: nanoc.server.tcp.port}
end
Process.setproctitle "rake server[#{p}] [#{n}]"
s = Server.dir(nanoc.output_dir, o)
s.start(block: true)
Process.setproctitle "rake server[#{p}] [#{n}]"
if p == "unix"
t = s.start(block: false)
unix = nanoc.server.unix
chmod(unix.mode, unix.path, verbose: false)
t.join
else
s.start(block: true)
end
rescue Interrupt
s.stop
end

View file

@ -1,40 +0,0 @@
# -*- mode: org -*-
** vNEXT
**** Change default ~audio.base_url~
This change sets the default audio URL to
https://audio1.al-quran.reflectslight.io/rifai
**** Improve KaiOS support
This change optimizes the layout for KaiOS devices through
specialized media queries
**** Add ~etc/~
This change moves a large portion of the website's configuration
files to the ~/etc~ directory
** v0.9.0
**** Add ~share/al-quran.reflectslight.io/documentation/~
This change replaces ~share/doc/al-quran.reflectslight.io~
**** Add new recitation
A new recitation by Hani ar-Rifai has been added
**** Replace ~opengraph.rb~ with ~_opengraph.html.erb~
This change simplifies how we render opengraph-related meta tags
**** Move to nodejs for scss compiler
This change replaces the deprecated Ruby scss compiler with the
nodejs compiler
**** Add ~audio.base_url~ to nanoc.yaml
This change provides increased flexibility in how the website
serve audio files
**** Rename packages/typescript/Quran/ properties
This change introduces urlName, translitName to Surah objects
**** eslint upgrade
This change migrates to the most recent version of eslint (^9.8)

View file

@ -0,0 +1,156 @@
# -*- mode: org -*-
* vNEXT
** Revisit ~ThemeSelect.tsx~ for touch devices
On touch devices it is now easier to change themes thanks to
a container that has a larger height and width than the theme
icon it contains. Along with this change, the theme icons have
become larger and less circular
** Reduce default width
The look and feel of the website has changed to be more like
a book that could fit in the palm of your hand
** Add src/images/albumcover-600x600.jpg
A copy of the album art that has been embedded as metadata
in the MP3 files served from audio.al-quran.reflectslight.io
** Apply Amiri font for Farsi
Similar to Arabic - apply the Amiri Regular font for the content
of a surah. With this change the distinction is more so between a
RTL language vs a LTR language rather than between Arabic vs English
vs Farsi
** Render ~LanguageSelect.tsx~ items in an optimal font
RTL languages have ~Cairo Regular~ applied.
LTR languages have ~Kanit Regular~ applied
** Fix the render of Amiri font on iOS
On modern versions of iOS the "Amiri Quran Regular" font rendered
as invisible text. This change reverts to the "Amiri Regular" font
instead, which appears to render without any issues
** Download fonts based on language direction
RTL and LTR languages use a different set of fonts, and before this
change we would download all fonts regardless of whether the font
would be used or not
** Optimize inline of ~postman.css~
The inlined CSS is minified before being inserted into HTML documents
** Remove KaiOS breakpoints
There is a separate branch (~kaios/main~) for KaiOS support
** Rewrite theme implementation
The theme implementation has been reduced to a set of colors
that are applied when the theme is active, and in the process
we made the creation of new themes easier
* v0.10.0
** Add Cairo fonts
Two new fonts for RTL languages: Cairo Regular, Cairo Bold
** Render three columns on ~SurahIndex.tsx~
The ~SurahIndex.tsx~ component renders three columns for RTL
languages (compared with two columns before)
** Revisit color scheme
Redo the website color scheme with a focus on improving the
Arabic / Farsi variants
** Revisit ~SurahStream.tsx~ for RTL languages
The appearance of the ~SurahStream.tsx~ component was revisted
for RTL languages, especially Arabic
** Add "Amiri Quran Regular" to ~SurahIndex.tsx~
The name of each surah is rendered in the Amiri Quran Regular
font in the Arabic locale / language
** Add "Amiri Quran Regular" font
This new font is used specifically for the contents of a surah
in the Arabic locale / language
** Replace custom z-index rule
Replace custom z-index rule with the CSS class ~.z-10~
** Update tail.css
The vendored copy of ~tail.css~ has been updated
** Add ~rake format:apply~
This new rake task tries to auto-correct typescript and
ruby lint errors
** Apply larger font size in ~Head.tsx~
The text "The Noble Quran" has a larger size (~.text-2xl~)
** Remove ~/robots.txt~
We don't need this file anymore. It was used to block crawlers
indexing audio content at /audio but audio content is now served
on a separate domain
** Add file mode option to ~nanoc.yaml~
The file mode option can be set on the server's unix socket.
Default: ~ug=rw,o=~
* v0.9.1
** Fix ~_opengraph.html.erb~ typos
Fix multiple issues with the render of ~_opengraph.html.erb~
** Replace font-size rules with tail.css classes
Replace custom 'font-size' rules with standard tail.css
classes (eg text-lg)
** Remove ~mada-regular.ttf~
The Arabic font is now based on standard web fonts rather than
a custom fonts.google.com font
** Add ~src/js/main/vendor.ts~
The new vendor entry point bundles preact, and other
third party dependencies in a single file. This change
is an improvement imported from the ~kaios/main~ branch
** Replace React with preact
The preact library is a lightweight alternative to React,
with a smaller footprint. This change is an improvement
imported from the ~kaios/main~ branch
** Change default ~audio.base_url~
Set the default audio URL to
https://audio.al-quran.reflectslight.io/rifai
**** Improve KaiOS support
Optimize the layout for KaiOS devices through specialized
media queries. A new branch (~kaios/main~) will focus on
KaiOS support
**** Add ~etc/~
Move a large portion of the website's configuration files to
the ~/etc~ directory
* v0.9.0
** Add ~share/al-quran.reflectslight.io/documentation/~
Replace ~share/doc/al-quran.reflectslight.io~
** Add new recitation
Add a new recitation by Hani ar-Rifai
** Replace ~opengraph.rb~ with ~_opengraph.html.erb~
Simplify how we render opengraph meta tags
** Move to nodejs for scss compiler
Replace the deprecated Ruby scss compiler with the nodejs compiler
** Add ~audio.base_url~ to nanoc.yaml
Provide extra flexibility for audio content
** Rename packages/typescript/Quran/ properties
Introduce urlName, translitName to Surah objects
** Upgrade eslint
Upgrade to the most recent version of eslint (^9.8)

View file

@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
al-quran (ebook) Copyright (C) 2024 ReflectsLight
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

View file

@ -1 +1 @@
v0.9.0
v0.10.0

View file

@ -1,31 +0,0 @@
# -*- mode: org -*-
** Introduction
This document explains the process that should be
followed when adding a new locale or language
to the website. Ideally the process would require
a minimal amount of manual work and be fast to complete
but we're not quite there yet.
** Steps
**** Add new locale to file ~/Rules~
The ~/Rules~ file has to be updated
**** Add new locale to array ~Quran.locales~
The ~/packages/Quran/src/index.ts~ file has to be updated
**** Add new locale (+ translations)
The following files have to updated:
+ ~/src/json/t.json~
+ ~/src/json/surahs.json~
+ ~/src/json/<locale>/<surahId>/{info,surah}.json~
**** Save time
The ~/src/json/<locale>/<surahId>/info.json~ files can
be generated from the contents of ~/src/json/surahs.json~ -
and that leaves us with one less step to complete:
#+BEGIN_SRC sh
user@localhost$ bundle exec rake t:surahs.json
#+END_SRC

View file

@ -0,0 +1,44 @@
# -*- mode: org -*-
* Introduction
This document explains the process that should be
followed when adding a new locale or language
to the website.
Ideally the process would require a minimal amount
of manual work and be fast to complete but we're not
quite there yet.
** Note
Experience has taught me that there's more work than
you might think in maintaining support for a language,
and that starts with finding a good translation.
But it does not end there: the pros and cons of adopting
a new language have to be carefully considered. Going
forward I don't plan to add another language unless a native
speaker can help maintain its support.
* Steps
** Add new locale to file ~/Rules~
The ~/Rules~ file has to be updated
** Add new locale to array ~Quran.locales~
The ~/packages/Quran/src/index.ts~ file has to be updated
** Add new locale (+ translations)
The following files have to updated:
- /src/json/t.json
- /src/json/surahs.json
- /src/json/<locale>/<surahId>/{info,surah}.json
** Automation
The ~/src/json/<locale>/<surahId>/info.json~ files can be
generated from the contents of ~/src/json/surahs.json~:
#+BEGIN_SRC sh
user@localhost$ bundle exec rake t:surahs.json
#+END_SRC

View file

@ -1,4 +1,3 @@
@import "base/colors";
@import "base/breakpoints";
@import "base/icon";
@import "base/select";
@ -8,57 +7,96 @@ html {
height: 100%;
body {
height: 100%;
color: $black;
margin: 0;
.root {
height: 100%;
}
.ltr {
font-family: "Kanit Regular";
direction: ltr;
}
.rtl {
font-family: "Mada Regular";
font-family: "Cairo Regular", "Arial", "Tahoma", sans-serif;
direction: rtl;
}
.invisible, .hidden {
display: none;
}
.outline-0 {
outline: 0;
}
.scroll-y {
overflow-y: auto;
}
.font-cairo {
font-family: "Cairo Regular";
}
.font-cairo-bold {
font-family: "Cairo Bold";
}
.font-amiri {
font-family: "Amiri Regular", "Scheherazade", "Arial", sans-serif;
}
}
}
body .root .content.theme {
margin: 0 auto;
max-width: $breakpoint-md;
width: 85%;
max-width: $breakpoint-sm;
width: 100%;
color: var(--color-accent);
/* <= $breakpoint-sm */
@media (max-width: $breakpoint-sm) {
width: 85%;
}
header {
a[data-testid="h1"] {
font-size: x-large;
@media screen and
(max-width: $breakpoint-kaiOS-portrait) and
(orientation: portrait) {
font-size: larger;
}
background: var(--primary-color);
color: var(--secondary-color);
}
}
header {
nav, div {
font-size: large;
}
}
ul.body {
font-size: large;
scrollbar-color: var(--primary-color) var(--secondary-color);
}
footer {
font-size: large;
.color-primary {
color: var(--primary-color);
}
.color-secondary {
color: var(--secondary-color) !important;
}
.color-accent {
color: var(--accent-color);
}
.background-primary {
background: var(--primary-color);
}
.background-secondary {
background: var(--secondary-color);
}
.background-accent {
background: var(--accent-color);
}
.border-accent {
border: 1px solid var(--accent-color);
}
}
@ -66,21 +104,14 @@ body .root .content.theme {
* RTL languages
*/
body .root .content.theme.rtl {
header h1 {
font-size: xx-large;
}
/* <= $breakpoint-sm */
@media (max-width: $breakpoint-sm) {
header h1 {
font-size: x-large;
}
}
/* <= $breakpoint-xxl */
@media (max-width: $breakpoint-xxl) {
ul.body {
font-size: larger;
}
direction: rtl;
header a[data-testid="h1"] {
font-family: "Cairo Bold";
}
}
html[dir="rtl"] {
.font-extrabold {
font-family: "Cairo Bold" !important;
}
}

View file

@ -1,8 +1,4 @@
/* KaiOS: max widths */
$breakpoint-kaiOS-portrait: 240px;
$breakpoint-kaiOS-landscape: 320px;
/* Standard max widths */
/* max-width */
$breakpoint-sm: 576px;
$breakpoint-md: 768px;
$breakpoint-lg: 992px;

View file

@ -1,2 +0,0 @@
$black: #454545;
$white: #FFF;

View file

@ -57,6 +57,7 @@
.refresh.icon,
.right-arrow.icon,
.left-arrow.icon {
fill: var(--primary-color);
height: 16px;
width: 16px;
}
@ -69,7 +70,7 @@
g rect {
width: 15px;
height: 40px;
fill: #FFF;
fill: var(--primary-color);
}
}
@ -100,7 +101,7 @@
.root .content.theme.rtl {
.stalled.icon {
left: 0px;
right: 7px;
right: 12px;
}
ul.body.stream {
@ -120,3 +121,42 @@
}
}
}
.content.theme {
ul.body.stream li, footer {
.play.icon {
fill: var(--primary-color);
stroke: var(--secondary-color);
stroke-width: 2px;
}
.pause.icon {
rect {
fill: var(--primary-color);
stroke: var(--secondary-color);
stroke-width: 1px;
}
}
.refresh.icon {
fill: var(--primary-color);
}
.sound-on.icon, .sound-off.icon {
polygon {
fill: var(--primary-color);
stroke-width: 2;
}
}
.right-arrow.icon,
.left-arrow.icon {
g {
fill: var(--secondary--color);
}
}
}
.stalled.icon {
div { background: var(--secondary-color); }
}
}

View file

@ -1,22 +1,37 @@
.root .content.theme {
.green {
background: #6d765b !important;
}
.blue {
background: #3383C3 !important;
}
.react-select {
z-index: 2;
.active {
cursor: pointer;
}
}
.react-select.theme-select {
ul li,
ul li a {
-webkit-tap-highlight-color: transparent;
}
.circle {
width: 16px;
height: 16px;
border-radius: 12px;
background: var(--primary-color);
}
}
.react-select.language-select {
li a {
border: 1px solid $black;
background: var(--primary-color);
color: var(--secondary-color);
&:active, &:visited, &:link {
color: var(--secondary-color);
}
&:hover {
font-weight: bold;
}
}
}
}

View file

@ -4,8 +4,9 @@
body .root .content.theme {
ul.body.index {
@media (max-width: $breakpoint-sm) {
li, li a {
li, li a {
color: var(--accent-color);
@media (max-width: $breakpoint-sm) {
width: 100%;
}
}
@ -30,11 +31,22 @@ body .root .content.theme {
}
footer {
@media screen and (max-width: $breakpoint-kaiOS-landscape) {
display: none;
a {
color: var(--accent-color);
&:active, &:link, &:visited {
color: var(--accent-color);
}
&:hover {
font-weight: bold;
}
}
@media screen and (max-width: $breakpoint-kaiOS-portrait) {
display: none;
input {
color: var(--accent-color);
border: 1px solid var(--primary-color);
&:focus {
outline-color: var(--primary-color);
}
}
@media(max-width: $breakpoint-sm) {
@ -49,6 +61,12 @@ body .root .content.theme {
}
}
@media(max-width: $breakpoint-sm) {
input[data-testid="SurahIndex/Filter"] {
display: none;
}
}
@media(hover: none) {
input[data-testid="SurahIndex/Filter"] {
display: none;
@ -62,23 +80,19 @@ body .root .content.theme {
*/
body .root .content.theme.rtl {
ul.body.index {
li a {
span:first-child {
border: 1px solid $black;
}
}
li.surah {
@media (max-width: $breakpoint-sm) {
width: 100%;
}
span.transliterated { display: none; }
}
}
/* >= $breakpoint-xxl */
@media (min-width: $breakpoint-xxl) {
ul.body.index {
li.surah a {
span:last-child {
font-size: larger;
@media (hover: hover) {
footer {
a {
&:hover {
font-weight: normal;
font-family: "Cairo Bold";
}
}
}

View file

@ -21,32 +21,8 @@ body .root .content.theme {
body .root .content.theme.rtl {
ul.body.stream {
li.ayah p {
line-height: 1.7;
max-width: 470px;
}
}
/* <= $breakpoint-sm */
@media (max-width: $breakpoint-sm) {
ul.body.stream {
li.ayah p {
width: 100%;
}
}
}
/* >= $breakpoint-xxl */
@media (min-width: $breakpoint-xxl) {
ul.body.stream {
$gap: 2rem;
margin-top: $gap;
li.ayah {
font-size: larger;
margin-bottom: $gap;
}
}
footer {
.timer {
font-size: larger;
}
width: 100%;
@extend .font-amiri;
}
}
}

View file

@ -1,39 +1,5 @@
.root .content.theme.blue.rtl {
direction: rtl;
}
.root .content.theme.blue {
@import "blue/base/colors";
.color-white {
color: $white;
}
.color-primary {
color: $primary-color;
}
.color-secondary {
color: $secondary-color;
}
.color-accent {
color: $accent-color;
}
.background-primary {
background: $primary-color;
}
.background-secondary {
background: $secondary-color;
}
.background-accent {
background: $accent-color;
}
--primary-color: #3383C3;
--secondary-color: #FFF;
--accent-color: #444;
}
@import "blue/base";
@import "blue/main/SurahIndex";
@import "blue/main/SurahStream";

View file

@ -1,15 +0,0 @@
@import "base/icon";
@import "base/select";
.root .content.theme.blue {
@import "base/colors";
scrollbar-color: $primary-color #FFF;
header {
a[data-testid="h1"] {
background: $primary-color;
border: 1px solid $black;
color: #FFF;
}
}
}

View file

@ -1,3 +0,0 @@
$primary-color: #3383C3;
$secondary-color: #3383C3;
$accent-color: lighten($primary-color, 38%);

View file

@ -1,40 +0,0 @@
.content.theme.blue {
@import "themes/blue/base/colors";
ul.body.stream li, footer {
.play.icon {
fill: $primary-color;
stroke: $secondary-color;
stroke-width: 2px;
}
.pause.icon {
rect {
fill: $primary-color;
stroke: $secondary-color;
stroke-width: 1px;
}
}
.refresh.icon {
fill: $primary-color;
}
.sound-on.icon, .sound-off.icon {
polygon {
fill: $primary-color;
stroke-width: 2;
}
}
.right-arrow.icon,
.left-arrow.icon {
g {
fill: $secondary-color;
}
}
}
.stalled.icon {
div { background: $secondary-color; }
}
}

View file

@ -1,32 +0,0 @@
.content.theme {
@import "themes/blue/base/colors";
.blue {
background: $secondary-color;
}
}
.content.theme.blue {
@import "themes/blue/base/colors";
.react-select.theme-select {
.active .circle {
background: $secondary-color;
}
ul li.blue .circle {
background: $secondary-color;
}
}
.react-select.language-select {
li a {
background: $secondary-color;
color: $white;
&:active, &:visited, &:link {
color: $white;
}
&:hover {
font-weight: bold;
}
}
}
}

View file

@ -1,57 +0,0 @@
.root .surah-index.content.theme.blue {
@import "themes/blue/base/colors";
@import "base/breakpoints";
ul.body.index {
li.surah a {
&:active, &:link, &:visited {
color: $primary-color;
}
span.id {
color: $secondary-color;
}
}
}
footer {
a {
&:active, &:link, &:visited {
color: $primary-color;
text-decoration: none;
}
&:hover {
font-weight: bold;
}
}
input {
border: 1px solid $secondary-color;
&:focus {
outline-color: $secondary-color;
}
}
}
}
.root .content.theme.blue.en {
@import "themes/blue/base/colors";
header {
div {
color: $secondary-color;
}
}
}
.root .content.theme.blue.rtl {
@import "themes/blue/base/colors";
ul.body.index {
li.surah a {
span.id {
color: $secondary-color;
}
span.name {
color: $primary-color;
}
}
}
}

View file

@ -1,19 +0,0 @@
.root .content.theme.blue {
@import "themes/blue/base/colors";
@import "base/breakpoints";
ul.body.stream {
li.ayah {
span span {
color: $secondary-color;
}
p { }
}
}
footer {
.timer {
color: $primary-color;
}
}
}

View file

@ -1,40 +1,5 @@
.root .content.theme.green.rtl {
@import "green/base/colors";
direction: rtl;
}
.root .content.theme.green {
@import "green/base/colors";
.color-primary {
color: $primary-color;
}
.color-secondary {
color: $secondary-color;
}
.color-accent {
color: $accent-color;
}
.color-white {
color: #FFF;
}
.background-primary {
background: $primary-color;
}
.background-secondary {
background: $secondary-color;
}
.background-accent {
background: $accent-color;
}
--primary-color: #6d765b;
--secondary-color: #FFF;
--accent-color: #444;
}
@import "green/base";
@import "green/main/SurahIndex";
@import "green/main/SurahStream";

View file

@ -1,15 +0,0 @@
@import "base/icon";
@import "base/select";
.root .content.theme.green {
@import "base/colors";
scrollbar-color: $primary-color #FFF;
header {
a[data-testid="h1"] {
background: $primary-color;
border: 1px solid $black;
color: #FFF;
}
}
}

View file

@ -1,3 +0,0 @@
$primary-color: #6d765b;
$secondary-color: #6C755A;
$accent-color: #FFF;

View file

@ -1,41 +0,0 @@
.content.theme.green {
@import "themes/green/base/colors";
ul.body.stream li, footer {
.play.icon {
g path {
fill: $primary-color;
stroke: $primary-color;
stroke-width: 3px;
}
}
.pause.icon {
g rect {
fill: $primary-color;
stroke: $primary-color;
stroke-width: 1px;
}
}
.refresh.icon {
fill: $primary-color;
}
.sound-on.icon, .sound-off.icon {
g polygon {
fill: $primary-color;
}
}
.right-arrow.icon,
.left-arrow.icon {
g {
fill: $primary-color;
}
}
}
.stalled.icon {
div { background: $primary-color; }
}
}

View file

@ -1,32 +0,0 @@
.content.theme {
@import "themes/green/base/colors";
.green {
background: $primary-color;
}
}
.content.theme.green {
@import "themes/green/base/colors";
.react-select.theme-select {
.active {
.circle {
background: $primary-color;
border-radius: 10px;
}
}
}
.react-select.language-select {
li a {
background: $primary-color;
color: $white;
&:active, &:visited, &:link {
color: $white;
}
&:hover {
font-weight: bold;
}
}
}
}

View file

@ -1,27 +0,0 @@
.root .surah-index.content.theme.green {
@import "base/breakpoints";
@import "themes/green/base/colors";
ul.body.index a {
&:active, &:link, &:visited {
color: $primary-color;
}
}
footer {
a {
&:active, &:link, &:visited {
color: $primary-color;
}
&:hover {
font-weight: bold;
}
}
input {
border: 1px solid $primary-color;
&:focus {
outline-color: $primary-color;
}
}
}
}

View file

@ -1,29 +0,0 @@
.root .content.theme.green {
@import "themes/green/base/colors";
@import "base/breakpoints";
header {
h1, h1 a { color: $primary-color; }
color: $primary-color;
}
ul.body.stream {
li.ayah {
color: $primary-color;
p { color: $black; }
}
}
footer {
.timer {
color: $primary-color;
}
}
.sound-on.icon, .svg.sound-off.icon {
polygon {
fill: $primary-color;
stroke-width: 2;
}
}
}

View file

@ -8,11 +8,11 @@ body {
padding: 0;
}
.postman.loader {
.postman.main {
height: 100%;
}
.postman.loader div:first-child {
.postman.main div:first-child {
width: 200px;
margin: 0 auto;
display: flex;
@ -21,11 +21,11 @@ body {
top: 250px;
}
.postman.loader progress {
.postman.main progress {
width: 100%;
}
.postman.loader .percentage {
.postman.main .percentage {
font-family: monospace;
text-align: center;
width: 100%;

File diff suppressed because one or more lines are too long

BIN
src/fonts/amiri-regular.ttf Normal file

Binary file not shown.

BIN
src/fonts/cairo-bold.ttf Normal file

Binary file not shown.

BIN
src/fonts/cairo-regular.ttf Normal file

Binary file not shown.

Binary file not shown.

View file

@ -9,7 +9,7 @@
<meta property="og:type" content="article"/>
<meta property="og:title" content="<%= t(context.locale, 'TheNobleQuran') %>"/>
<meta property="og:description" content="<%= context.surah.name %>"/>
<meta property="og:url" content="<%= base_url %>/<%= context.locale %>/<%= context.surah.urlname %>/"/>
<meta property="og:url" content="<%= base_url %>/<%= context.locale %>/<%= context.surah.urlName %>/"/>
<meta property="og:image" content="<%= base_url %>/images/og/<%= context.surah.id %>.png?v=<%= commit %>"/>
<meta property="og:image:type" content="image/png"/>
<% elsif file == "random.html.erb" %>
@ -19,4 +19,6 @@
<meta property="og:url" content="<%= base_url %>/<%= context.locale %>/random/"/>
<meta property="og:image" content="<%= base_url %>/images/og/0.png?v=<%= commit %>"/>
<meta property="og:image:type" content="image/png"/>
<% else %>
<% error! "unknown file: #{file}" %>
<% end %>

View file

@ -1,4 +1,4 @@
<div class="postman loader" dir="<%= dir %>">
<div class="postman main" dir="<%= dir %>">
<div>
<progress value="0" max="100"></progress>
<span class="percentage"><%= t(locale, "loading") %></span>

View file

@ -4,7 +4,7 @@
<title><%= t("en", "TheNobleQuran") %></title>
<meta name="description" content="<%= t('en', 'meta.index.description') %>">
<%= erb("_version.html.erb") %>
<%= erb("_opengraph.html.erb", {page: "redirect.html.erb", context:}) %>
<%= erb("_opengraph.html.erb", {file: "redirect.html.erb", context:}) %>
<link rel="canonical" href="<%= base_url %>/en/" />
<% locales.each do |locale| %>
<link

View file

@ -21,7 +21,7 @@
</head>
<body>
<%= erb("_postman.html.erb", {locale: context.locale, dir: context.dir}) %>
<div class="root h-full"></div>
<div class="app mount root h-full"></div>
<script src="/js/loaders/surah-index-loader.js?v=<%= commit %>"></script>
</body>
</html>

View file

@ -7,7 +7,7 @@
<meta charset="UTF-8">
<meta name="description" content="<%= t(context.locale, 'meta.stream.description') %>">
<%= erb("_version.html.erb") %>
<%= erb("_opengraph.html.erb", {file: "stream-stream.html.erb", context:}) %>
<%= erb("_opengraph.html.erb", {file: "surah-stream.html.erb", context:}) %>
<link
rel="canonical"
href="<%= base_url %>/<%= context.locale %>/<%= context.surah.urlName %>/"
@ -21,9 +21,10 @@
</head>
<body>
<%= erb("_postman.html.erb", {locale: context.locale, dir: context.dir}) %>
<div class="root"
<div class="app mount root"
data-surah-id="<%= context.surah.id %>"
data-audio-base-url="<%= audio_base_url %>">
data-audio-base-url="<%= audio_base_url %>"
data-commit-id="<%= commit %>">
</div>
<script src="/js/loaders/surah-stream-loader.js?v=<%= commit %>"></script>
</body>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -1,4 +1,3 @@
import React, { useEffect, useState } from "react";
import type { Surah, Ayah } from "Quran";
import { SoundOnIcon, SoundOffIcon } from "~/components/Icon";
@ -24,6 +23,7 @@ export function AudioControl({
const [enabled, setEnabled] = useState<boolean>(false);
const [audioStatus, setAudioStatus] = useState<Maybe<TAudioStatus>>(null);
const [audioBaseUrl, setAudioBaseUrl] = useState<Maybe<string>>(null);
const [commitId, setCommitId] = useState<Maybe<string>>(null);
const play = (audio: HTMLAudioElement) => audio.play().catch(() => null);
const pause = (audio: HTMLAudioElement) => audio.pause();
@ -32,7 +32,9 @@ export function AudioControl({
return;
}
if (enabled) {
audio.src = [audioBaseUrl, surah.id, `${ayah.id}.mp3`].join("/");
audio.src = [audioBaseUrl, surah.id, `${ayah.id}.mp3?v=${commitId}`].join(
"/",
);
play(audio);
}
}, [hidden, enabled, ayah?.id, audioBaseUrl]);
@ -42,8 +44,10 @@ export function AudioControl({
"[data-audio-base-url]",
);
const url = el?.dataset?.audioBaseUrl;
const commit = el?.dataset?.commitId;
if (url?.length) {
setAudioBaseUrl(url);
setCommitId(commit);
} else {
console.warn("audio.base_url is not set");
}

View file

@ -1,4 +1,3 @@
import React from "react";
import type { ReactNode } from "react";
import { LanguageSelect, ThemeSelect } from "~/components/Select";
import type { TLocale } from "Quran";
@ -16,8 +15,8 @@ export function Head({ locale, theme, setTheme, children }: Props) {
return (
<header
className={classNames("flex flex-col h-20 mt-4", {
"mb-4": locale.direction === "ltr",
"mb-6": locale.direction === "rtl",
"mb-6": locale.direction === "ltr",
"mb-8": locale.direction === "rtl",
})}
>
<div className="flex flex-col">
@ -25,12 +24,12 @@ export function Head({ locale, theme, setTheme, children }: Props) {
<a
data-testid="h1"
href={`/${locale.name}/`}
className="flex rounded justify-center p-3 m-0 mb-4 w-full no-underline"
className="flex rounded justify-center p-3 m-0 mb-4 w-full no-underline font-semibold color-secondary text-2xl"
>
{children}
</a>
</div>
<nav className="flex flex-row justify-between">
<nav className="flex flex-row justify-between text-lg">
<LanguageSelect locale={locale} />
<ThemeSelect theme={theme} setTheme={setTheme} />
</nav>

View file

@ -1,6 +1,3 @@
import React from "react";
import classNames from "classnames";
type Props = {
onClick: () => void;
};
@ -153,9 +150,9 @@ export function RefreshIcon({ onClick }: Props) {
export function StalledIcon() {
return (
<div className="stalled icon flex justify-end w-10">
<div />
<div />
<div />
<div className="background-primary" />
<div className="background-primary" />
<div className="background-primary" />
</div>
);
}

View file

@ -1,4 +1,3 @@
import React from "react";
import { Quran, TLocale } from "Quran";
import { Select } from "~/components/Select";
import classNames from "classnames";
@ -17,7 +16,8 @@ export function LanguageSelect({ locale }: Props) {
<Select.Option
key={i}
className={classNames(
"flex h-6 w-full justify-center no-underline mb-1 rounded",
"flex h-6 w-full justify-center no-underline mb-1 rounded border-accent",
l.direction === "rtl" ? "font-cairo" : "font-kanit",
l.direction,
l.name === locale.name ? "active" : undefined,
)}

View file

@ -1,11 +1,18 @@
import React, { ReactNode, AnchorHTMLAttributes } from "react";
import type { ReactNode } from "preact/compat";
type Rest = AnchorHTMLAttributes<HTMLAnchorElement>;
type Props = {
value: string;
href?: string | undefined;
className?: string;
onClick?: () => void;
children: ReactNode;
} & Rest;
};
export function Option({ children, ...rest }: Props) {
return <a {...rest}>{children}</a>;
export function Option({ children, href, className, onClick }: Props) {
const ref = createRef();
return (
<a href={href} className={className} onClick={onClick} ref={ref}>
{children as string}
</a>
);
}

View file

@ -1,7 +1,5 @@
import React from "react";
import { Select } from "~/components/Select";
import type { Theme } from "~/hooks/useTheme";
import classNames from "classnames";
type Props = {
theme: string;
@ -17,10 +15,10 @@ export function ThemeSelect({ theme, setTheme }: Props) {
<Select.Option
key={i}
onClick={() => setTheme(t)}
className={classNames("block circle mb-1", t)}
className="flex justify-end w-10 h-6"
value={t}
>
<span className="block w-full h-full" />
<span className={classNames("rounded w-5 h-5", t)} />
</Select.Option>
);
})}

View file

@ -1,5 +1,3 @@
import React, { useState, useEffect } from "react";
import classNames from "classnames";
import { Option } from "./Option";
import { ThemeSelect } from "./ThemeSelect";
import { LanguageSelect } from "./LanguageSelect";
@ -28,7 +26,7 @@ function Select({ value, children: options, className }: Props) {
return (
<div
className={classNames(
"react-select flex flex-col h-full relative",
"react-select flex flex-col h-full relative z-10",
className,
)}
>
@ -39,7 +37,12 @@ function Select({ value, children: options, className }: Props) {
<li
key={key}
className={classNames({ hidden: isHidden })}
onClick={(e) => [e.stopPropagation(), setOpen(!isOpen)]}
onClick={(e) => {
e.stopPropagation();
const { ref } = n.props;
setOpen(!isOpen);
ref?.current?.click();
}}
>
{n}
</li>

View file

@ -1,4 +1,3 @@
import React from "react";
import { TFunction, formatNumber } from "~/lib/t";
import type { Surah, TLocale } from "Quran";

View file

@ -1,11 +1,9 @@
import React, { useRef, useState, useEffect } from "react";
import type { Surah, TLocale } from "Quran";
import { useTheme } from "~/hooks/useTheme";
import { formatNumber, TFunction } from "~/lib/t";
import { Arrow } from "~/components/Icon";
import { Head } from "~/components/Head";
import { Filter } from "./Filter";
import classNames from "classnames";
import "@css/main/SurahIndex.scss";
type Props = {
@ -39,7 +37,10 @@ export function SurahIndex({ locale, surahs, t }: Props) {
<Head locale={locale} theme={theme} setTheme={setTheme}>
{t(locale, "TheNobleQuran")}
</Head>
<ul className="flex flex-wrap body index scroll-y list-none m-0 p-0 pt-4 m-auto w-full h-5/6">
<ul
lang={locale.name}
className="flex flex-wrap body index scroll-y list-none m-0 p-0 pt-4 m-auto w-full h-5/6 text-lg"
>
{index.map((surah, key) => (
<li
className={classNames("flex justify-center surah", {
@ -53,11 +54,11 @@ export function SurahIndex({ locale, surahs, t }: Props) {
href={`/${locale.name}/${surah.urlName}/`}
>
{locale.direction === "ltr" ? (
<span className="color-secondary font-extrabold w-10 text-center">
<span className="background-primary color-secondary ml-2 mr-3 font-extrabold w-10 text-center">
{formatNumber(locale, surah.id)}
</span>
) : (
<span className="flex items-center justify-center color-white background-secondary w-8 h-8 p-1 ml-5 rounded">
<span className="flex items-center font-extrabold justify-center color-secondary background-primary w-8 h-8 p-1 ml-5 rounded">
{formatNumber(locale, surah.id)}
</span>
)}
@ -79,9 +80,9 @@ export function SurahIndex({ locale, surahs, t }: Props) {
</li>
))}
</ul>
<footer className="flex flex-row justify-between mb-5 h-12">
<footer className="flex flex-row justify-between mb-5 h-12 text-lg">
<a
className="flex flex-row items-center no-underline"
className="flex flex-row items-center color-black no-underline"
href={`/${locale.name}/random/`}
>
{locale.direction === "ltr" ? (

View file

@ -1,4 +1,3 @@
import React, { useEffect, useMemo, useRef } from "react";
import type { Surah, Ayah, TAyat, TLocale } from "Quran";
import { AudioControl } from "~/components/AudioControl";
import { formatNumber, TFunction } from "~/lib/t";
@ -22,17 +21,15 @@ export function Stream({
t,
}: Props) {
const className = endOfStream || isPaused ? ["scroll-y"] : [];
const isRTL = locale.direction === "rtl";
const ref = useRef<HTMLUListElement>(null);
const ul = useMemo<JSX.Element>(() => {
const ltr = locale.direction === "ltr";
const rtl = locale.direction === "rtl";
return (
<ul
lang={locale.name}
className={classNames(
"body stream scroll-y list-none p-0 m-0 h-5/6",
"body stream scroll-y text-lg list-none p-0 m-0 h-5/6 mt-4",
...className,
{ "mt-6": ltr || rtl },
)}
ref={ref}
>
@ -40,9 +37,12 @@ export function Stream({
return (
<li
key={ayah.id}
className={classNames("ayah fade", { "mb-5": rtl, "mb-4": ltr })}
className={classNames("ayah fade", {
"mb-8": isRTL,
"mb-5": !isRTL,
})}
>
<span className="flex h-8 items-center">
<span className="flex h-8 items-center color-primary">
<AudioControl
hidden={!(isPaused || endOfStream)}
audio={new Audio()}
@ -54,14 +54,20 @@ export function Stream({
}
}}
/>
<span>
<span className="color-primary font-extrabold">
{t(locale, "surah")} {formatNumber(locale, surah.id)}
{t(locale, "comma")} {t(locale, "ayah")}{" "}
{formatNumber(locale, ayah.id)} {t(locale, "of")}{" "}
{formatNumber(locale, surah.ayat.length)}
</span>
</span>
<p className="m-0">{ayah.body}</p>
<p
className={classNames("m-0 color-accent", {
"text-2xl mt-5": isRTL,
})}
>
{ayah.body}
</p>
</li>
);
})}

View file

@ -1,5 +1,3 @@
import React, { useState, useEffect, useMemo, useRef } from "react";
import classNames from "classnames";
import type { Surah, Ayah, TAyat, TLocale } from "Quran";
import { useTheme } from "~/hooks/useTheme";
import { AudioControl, TAudioStatus } from "~/components/AudioControl";
@ -69,7 +67,7 @@ export function SurahStream({ surah, locale, t }: Props) {
isPaused={isPaused}
t={t}
/>
<footer className="flex justify-between items-center h-16">
<footer className="flex justify-between items-center h-16 text-lg">
{!endOfStream && isPaused && (
<PlayIcon onClick={() => setIsPaused(false)} />
)}

View file

@ -1,4 +1,3 @@
import React, { useEffect, useState } from "react";
import type { Surah, Ayah, TLocale } from "Quran";
import { formatNumber } from "~/lib/t";
@ -67,7 +66,7 @@ export function Timer({
}
return (
<div className="timer text-base w-10 flex justify-end">
<div className="timer font-extrabold text-base w-10 flex justify-end color-primary">
{!ms || ms / 1000 <= 0
? formatNumber(locale, 0)
: formatNumber(locale, ms / 1000, { maximumFractionDigits: 0 })}

View file

@ -1,5 +1,3 @@
import { useState } from "react";
export type Theme = "blue" | "green";
type Result = [Theme, (t: Theme) => void];
const THEMES: Theme[] = ["blue", "green"];

View file

@ -18,11 +18,18 @@ export function T(phrases: PhraseMap<string>): TFunction {
}
export function formatNumber(
locale: TLocale,
locale: TLocale | string,
num: number,
options = {},
): string {
const numl = locale.name === "ar" ? "ar-SA" : locale.name;
const name = (() => {
if (typeof locale === "string") {
return locale;
} else {
return locale.name;
}
})();
const numl = name === "ar" ? "ar-SA" : name;
return new Intl.NumberFormat(numl, {
maximumFractionDigits: 1,
...options,

View file

@ -1,41 +1,41 @@
import postman, { item } from "postman";
import { formatNumber } from "~/lib/t";
(function () {
const doc = document.documentElement;
const rev = doc
(async function () {
const docel = document.documentElement;
const main: HTMLElement = docel.querySelector(".postman.main")!;
const css: HTMLStyleElement = docel.querySelector(".postman.css")!;
const rev = docel
.querySelector("meta[name='revision']")!
.getAttribute("content")!;
const locale = {
name: doc.lang,
direction: doc.dir as "rtl" | "ltr",
displayName: "",
};
const fonts = (() => {
const f = [
item.font("Cairo Regular", "url(/fonts/cairo-regular.ttf)"),
item.font("Kanit Regular", "url(/fonts/kanit-regular.ttf)"),
];
if (docel.dir === "rtl") {
f.push(item.font("Cairo Bold", "url(/fonts/cairo-bold.ttf)"));
}
return f;
})();
/* Postman */
const loader: HTMLElement = doc.querySelector(".postman.loader")!;
const style: HTMLStyleElement = doc.querySelector(".css.postman")!;
const progressBar: HTMLProgressElement = loader.querySelector("progress")!;
const progressNumber: HTMLSpanElement = loader.querySelector(".percentage")!;
postman(
item.script(`/js/main/surah-index.js?v=${rev}`),
item.font("Kanit Regular", "url(/fonts/kanit-regular.ttf)"),
item.font("Mada Regular", "url(/fonts/mada-regular.ttf"),
const parcel = await postman(
item.script(`/js/main/vendor.js?v=${rev}`, { id: "0" }),
item.script(`/js/main/surah-index.js?v=${rev}`, { id: "1" }),
...fonts,
item.progress((percent: number) => {
progressBar.value = percent;
progressNumber.innerText = formatNumber(
locale,
Number(percent.toFixed(0)),
);
const bar: HTMLProgressElement = main.querySelector("progress")!;
const num: HTMLSpanElement = main.querySelector(".percentage")!;
bar.value = percent;
num.innerText = formatNumber(docel.lang, Number(percent.toFixed(0)));
}),
)
.fetch()
.then((pkg) => {
[loader, style].forEach((el) => el.remove());
pkg.fonts.forEach((f) => document.fonts.add(f));
pkg.css.forEach((s) => document.head.appendChild(s));
pkg.scripts.forEach((s) =>
document.body.removeChild(document.body.appendChild(s)),
);
).deliver();
[main, css].forEach((el) => el.remove());
parcel.fonts.forEach((f) => document.fonts.add(f));
parcel.css.forEach((s: HTMLElement) => document.head.appendChild(s));
parcel.scripts
.sort((a, b) => Number(a.id) - Number(b.id))
.forEach((s) => {
document.body.removeChild(document.body.appendChild(s));
});
})();

View file

@ -1,48 +1,49 @@
import postman, { item } from "postman";
import { formatNumber } from "~/lib/t";
(function () {
const doc = document.documentElement;
const rev = doc
(async function () {
const docel = document.documentElement;
const main = docel.querySelector(".postman.main")!;
const css = docel.querySelector(".postman.css")!;
const { surahId } = docel.querySelector<HTMLElement>(".app.mount")!.dataset;
const rev = docel
.querySelector("meta[name='revision']")!
.getAttribute("content")!;
const { surahId } = document.querySelector<HTMLElement>(".root")!.dataset;
const locale = {
name: doc.lang,
direction: doc.dir as "rtl" | "ltr",
displayName: "",
};
const fonts = (() => {
const f = [
item.font("Cairo Regular", "url(/fonts/cairo-regular.ttf)"),
item.font("Kanit Regular", "url(/fonts/kanit-regular.ttf)"),
];
if (docel.dir === "rtl") {
f.push(item.font("Cairo Bold", "url(/fonts/cairo-bold.ttf)"));
f.push(item.font("Amiri Regular", "url(/fonts/amiri-regular.ttf)"));
}
return f;
})();
/* Postman */
const loader = doc.querySelector(".postman.loader")!;
const style = doc.querySelector(".css.postman")!;
const progressBar = loader.querySelector("progress")!;
const progressNumber: HTMLSpanElement = loader.querySelector(".percentage")!;
postman(
item.script(`/js/main/surah-stream.js?v=${rev}`),
item.font("Kanit Regular", "url(/fonts/kanit-regular.ttf)"),
item.font("Mada Regular", "url(/fonts/mada-regular.ttf"),
const parcel = await postman(
item.script(`/js/main/vendor.js?v=${rev}`, { id: "0" }),
item.script(`/js/main/surah-stream.js?v=${rev}`, { id: "1" }),
...fonts,
/* eslint-disable */
item.json(`/json/${doc.lang}/${surahId}/info.json?v=${rev}`, { className: "json surahinfo" }),
item.json(`/json/${doc.lang}/${surahId}/surah.json?v=${rev}`, { className: "json surah" }),
item.json(`/json/${docel.lang}/${surahId}/info.json?v=${rev}`, { className: "json surahinfo" }),
item.json(`/json/${docel.lang}/${surahId}/surah.json?v=${rev}`, { className: "json surah" }),
item.json(`/json/durations/${surahId}.json?v=${rev}`, { className: "json durations" }),
/* eslint-enable */
item.progress((percent: number) => {
progressBar.value = percent;
progressNumber.innerText = formatNumber(
locale,
Number(percent.toFixed(0)),
);
const bar = main.querySelector("progress")!;
const num: HTMLSpanElement = main.querySelector(".percentage")!;
bar.value = percent;
num.innerText = formatNumber(docel.lang, Number(percent.toFixed(0)));
}),
)
.fetch()
.then((pkg) => {
[loader, style].forEach((el) => el.remove());
pkg.fonts.forEach((f) => document.fonts.add(f));
pkg.css.forEach((s) => document.head.appendChild(s));
pkg.json.forEach((o) => document.body.appendChild(o));
pkg.scripts.forEach((s) =>
document.body.removeChild(document.body.appendChild(s)),
);
).deliver();
[main, css].forEach((el) => el.remove());
parcel.fonts.forEach((f) => document.fonts.add(f));
parcel.css.forEach((s) => document.head.appendChild(s));
parcel.json.forEach((o) => document.body.appendChild(o));
parcel.scripts
.sort((a, b) => Number(a.id) - Number(b.id))
.forEach((s) => {
document.body.removeChild(document.body.appendChild(s));
});
})();

View file

@ -1,6 +1,4 @@
import { Surah, TSurah, Quran } from "Quran";
import React from "react";
import ReactDOM from "react-dom/client";
import { T } from "~/lib/t";
import { SurahIndex } from "~/components/SurahIndex";
@ -16,7 +14,5 @@ import { SurahIndex } from "~/components/SurahIndex";
(e: TSurah) => new Surah(e),
);
ReactDOM.createRoot(root).render(
<SurahIndex locale={locale} surahs={surahs} t={t} />,
);
render(<SurahIndex locale={locale} surahs={surahs} t={t} />, root);
})();

View file

@ -1,6 +1,4 @@
import { Quran, Surah, Ayah, TSurah } from "Quran";
import React from "react";
import ReactDOM from "react-dom/client";
import { T } from "~/lib/t";
import { SurahStream } from "~/components/SurahStream";
@ -32,7 +30,5 @@ import { SurahStream } from "~/components/SurahStream";
ayah.ms = ms * 1000;
}
ReactDOM.createRoot(root).render(
<SurahStream surah={surah} locale={locale} t={t} />,
);
render(<SurahStream surah={surah} locale={locale} t={t} />, root);
})();

16
src/js/main/vendor.ts Normal file
View file

@ -0,0 +1,16 @@
import { render, createRef } from "preact";
import { useState, useEffect, useMemo, useRef } from "preact/hooks";
import * as React from "preact/compat";
import classNames from "classnames";
const exports = {
React,
render,
useState,
useEffect,
useMemo,
useRef,
createRef,
classNames,
};
Object.assign(window, exports);

13
src/js/types/globals.d.ts vendored Normal file
View file

@ -0,0 +1,13 @@
import * as preact from "preact";
import * as hooks from "preact/hooks";
import classn from "classnames";
declare global {
const render: typeof preact.render;
const useState: typeof hooks.useState;
const useEffect: typeof hooks.useEffect;
const useRef: typeof hooks.useRef;
const useMemo: typeof hooks.useMemo;
const createRef: typeof preact.createRef;
const classNames: typeof classn;
}

View file

@ -1,2 +0,0 @@
user-agent: *
disallow: /audio/

@ -1 +0,0 @@
Subproject commit f44a820680efcf4a1756c83f7d63e08595b7a643

@ -1 +0,0 @@
Subproject commit c4c4c5079c534c71f9566434458ed3ad9bcd8b19

@ -1 +0,0 @@
Subproject commit c2c43ec193426653e704de90c83d4094d802a7bd