feat(sub): redesign the subscription page around usage, tabs and app imports

The info page was a long key/value table followed by every link and two
app dropdowns, and it rendered left-to-right even for Persian and Arabic.
It now leads with a usage ring, the remaining quota and a stats grid, and
splits the rest into Subscription / Apps / Configs tabs.

- Status tells expired, data-used-up and disabled apart instead of one
  "Inactive", replacing the hard-coded English expiry chip.
- The Apps tab keeps every Android and iOS app with its existing deep
  link, preselects the visitor's platform and adds Windows: Hiddify and
  Clash Verge Rev import directly, v2rayN copies the link.
- fa-IR and ar-EG render right-to-left; URLs, IDs and sizes stay LTR.
- The footer shows the support link and the client refresh interval, so
  subPageContext now carries subUpdates (also in ?format=info).
- Status, days-left and app deep-link logic lives in subPageModel.ts,
  with unit tests pinning the deep links the page already shipped.
This commit is contained in:
Sanaei
2026-09-16 01:17:04 +02:00
parent 01ce2bcecb
commit e26cf1d3ed
42 changed files with 1869 additions and 877 deletions
@@ -0,0 +1,25 @@
# Subscription page app icons
## Line icons — Arcticons (CC BY-SA 4.0)
`happ.svg`, `sing-box.svg`, `v2rayng.svg` and `v2raytun.svg` come from
[Arcticons](https://github.com/Arcticons-Team/Arcticons) by Donnnno and the
Arcticons contributors (as listed on svgicons.com), licensed under
[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/).
Changes from the originals: the stroke colour is `currentColor` so the page can
tint them to its theme, the stroke width is raised from 1 to 2 so they stay
legible at tile size, and the style classes and ids were removed. These modified
files are distributed under CC BY-SA 4.0 as well.
## App icons
The remaining files are each app's own icon, downscaled to 96 px, used only to
identify the app a button opens. They remain the trademarks of their owners.
| File | Source |
| ------------------- | ------------------------------------------------------------------------- |
| `shadowrocket.webp` | App Store listing [id932747118](https://apps.apple.com/app/id932747118) |
| `streisand.webp` | App Store listing [id6450534064](https://apps.apple.com/app/id6450534064) |
| `v2box.webp` | App Store listing [id6446814690](https://apps.apple.com/app/id6446814690) |
| `incy.webp` | App Store listing [id6756943388](https://apps.apple.com/app/id6756943388) |
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><polyline points="11.9387 18.636 11.2805 19.2322 13.2164 6.3148 21.6618 6.3148 21.082 10.1846"/><polyline points="20.2585 28.1999 18.113 42.5 9.6674 42.5 10.3198 38.1088"/><polyline points="28.1728 36.5585 27.2773 42.5 35.7228 42.5 37.9603 27.5876 36.0158 29.2999"/><polyline points="21.0144 27.3598 27.8555 27.3598 26.2122 38.3731 36.0158 29.2999 39.1998 8.0038 29.2593 17.99 28.8029 21.0714 27.2773 21.0714 26.4784 21.8658"/><polygon points="26.4784 21.8658 20.2243 28.1543 18.9689 28.1543 18.752 29.6379 8.8002 39.6355 11.9387 18.636 21.7423 9.5743 19.9047 21.8658 26.4784 21.8658"/><polyline points="38.0794 9.1294 38.6261 5.5 30.1808 5.5 28.1701 18.9112 29.2593 17.99"/></svg>

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M40.7241,15.6976l-15.4896-10.8095c-.7416-.5175-1.7273-.5175-2.4689,0L7.2759,15.6976c-.9141.6379-.9141,1.9909,0,2.6288l15.4896,10.8095c.7416.5175,1.7273.5175,2.4689,0l15.4896-10.8095c.9141-.6379.9141-1.9909,0-2.6288Z"/><path d="M41.4096,17.012v13.976c0,.4977-.2285.9954-.6855,1.3144l-15.4896,10.8095c-.7416.5175-1.7273.5175-2.4689,0l-15.4896-10.8095c-.457-.3189-.6855-.8167-.6855-1.3144h0s0-13.976,0-13.976"/><line x1="24" y1="29.524" x2="24" y2="43.5"/><path d="M11.8734,12.4893l18.3951,13.1335v3.8047c0,.6869.7673,1.0951,1.3369.7111l4.3991-2.9651c.2961-.1996.4735-.5332.4735-.8903v-4.9939l-18.3951-13.1335"/></svg>

After

Width:  |  Height:  |  Size: 783 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m9.6185,41.4866V10.6142h-4.1185v-4.0722h10.2641v14.3936c.6957-.6404,1.2872-1.1794,1.8728-1.7248,3.5152-3.2745,7.0291-6.5504,10.5432-9.826.9416-.8777,1.8767-1.7624,2.8301-2.6271.1437-.1303.3712-.2375.5603-.2382,3.5668-.0136,10.9295,0,10.9295,0-10.9555,11.6366-21.8724,23.2736-32.8815,34.9672Z"/></svg>

After

Width:  |  Height:  |  Size: 468 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><rect x="5.5" y="5.5" width="37" height="37" rx="4" ry="4"/><polyline points="28.9436 11.2023 20.4652 36.7977 11.9867 11.2023"/><path d="M28.3091,29.0207c0-2.3774,2.1537-4.2518,4.6165-3.7785,1.6155.3105,2.9055,1.7076,3.0663,3.3447.1195,1.2178-.2658,2.4194-1.1069,3.1576-1.5583,1.3675-6.5759,5.053-6.5759,5.053h7.7042"/></svg>

After

Width:  |  Height:  |  Size: 484 B