From 3606c48f02b71872e5134a57b5c7741e8043509f Mon Sep 17 00:00:00 2001 From: Tomek Zawadzki Date: Mon, 24 Aug 2026 19:33:57 +0200 Subject: [PATCH] Migrate feed and tab bar icons to react-native-nano-icons react-native-svg materialises three native views per icon (SvgView + Group + Path). Feed screens render around a thousand icons, so that is ~2400 views and ~2400 shadow nodes of pure overhead per profile load. Move the 28 icons that repeat on feed screens - the post action bar, feed row reason, post embeds, the loading skeleton and the bottom tab bar - to react-native-nano-icons, which draws each icon as a single native text glyph. The SVG sources are generated from the existing icon path data so the geometry is unchanged, and `createNanoIcon` mirrors the `useCommonSVGProps` contract so call sites are one-line import swaps. Measured on a Samsung Galaxy A16 (SM-A165F, Android 16) with release builds, 10 runs per condition: native views -20.6%, icon construction -45.3%, JS-thread CPU -6.1%, main-thread CPU -3.3%. Co-Authored-By: Claude Opus 5 (1M context) --- app.config.js | 4 + .../nano-icons/app-icons/arrow-top-right.svg | 1 + assets/nano-icons/app-icons/bell-filled.svg | 1 + assets/nano-icons/app-icons/bell.svg | 1 + .../nano-icons/app-icons/bookmark-filled.svg | 1 + assets/nano-icons/app-icons/bookmark.svg | 1 + assets/nano-icons/app-icons/bubble.svg | 1 + assets/nano-icons/app-icons/circle-check.svg | 1 + assets/nano-icons/app-icons/clock.svg | 1 + assets/nano-icons/app-icons/earth.svg | 1 + assets/nano-icons/app-icons/heart-filled.svg | 1 + assets/nano-icons/app-icons/heart.svg | 1 + assets/nano-icons/app-icons/home-filled.svg | 1 + assets/nano-icons/app-icons/home.svg | 1 + assets/nano-icons/app-icons/inbox.svg | 1 + assets/nano-icons/app-icons/menu.svg | 1 + .../nano-icons/app-icons/message-filled.svg | 1 + assets/nano-icons/app-icons/message.svg | 1 + assets/nano-icons/app-icons/mute.svg | 1 + assets/nano-icons/app-icons/pause.svg | 1 + assets/nano-icons/app-icons/pin.svg | 1 + assets/nano-icons/app-icons/play.svg | 1 + assets/nano-icons/app-icons/reply.svg | 1 + .../nano-icons/app-icons/repost-corner2.svg | 1 + assets/nano-icons/app-icons/repost.svg | 1 + assets/nano-icons/app-icons/search-filled.svg | 1 + assets/nano-icons/app-icons/search.svg | 1 + assets/nano-icons/app-icons/share.svg | 1 + assets/nano-icons/app-icons/unmute.svg | 1 + .../nanoicons/app-icons.glyphmap.json | 1 + assets/nano-icons/nanoicons/app-icons.ttf | Bin 0 -> 5976 bytes package.json | 1 + pnpm-lock.yaml | 152 ++++++++++++++++++ .../Post/Embed/ExternalEmbed/index.tsx | 2 +- .../Post/Embed/StandardSiteEmbed/index.tsx | 3 +- .../VideoEmbedInner/VideoEmbedInnerNative.tsx | 10 +- .../PostControls/BookmarkButton.tsx | 5 +- .../PostControls/PostMenu/index.tsx | 2 +- src/components/PostControls/RepostButton.tsx | 2 +- .../PostControls/ShareMenu/index.tsx | 2 +- src/components/PostControls/index.tsx | 2 +- src/components/icons/nano.tsx | 109 +++++++++++++ src/lib/custom-animations/LikeIcon.tsx | 6 +- src/style.css | 16 ++ src/view/com/posts/PostFeedReason.tsx | 3 +- src/view/com/util/LoadingPlaceholder.tsx | 10 +- src/view/shell/bottom-bar/BottomBar.tsx | 28 ++-- 47 files changed, 347 insertions(+), 39 deletions(-) create mode 100644 assets/nano-icons/app-icons/arrow-top-right.svg create mode 100644 assets/nano-icons/app-icons/bell-filled.svg create mode 100644 assets/nano-icons/app-icons/bell.svg create mode 100644 assets/nano-icons/app-icons/bookmark-filled.svg create mode 100644 assets/nano-icons/app-icons/bookmark.svg create mode 100644 assets/nano-icons/app-icons/bubble.svg create mode 100644 assets/nano-icons/app-icons/circle-check.svg create mode 100644 assets/nano-icons/app-icons/clock.svg create mode 100644 assets/nano-icons/app-icons/earth.svg create mode 100644 assets/nano-icons/app-icons/heart-filled.svg create mode 100644 assets/nano-icons/app-icons/heart.svg create mode 100644 assets/nano-icons/app-icons/home-filled.svg create mode 100644 assets/nano-icons/app-icons/home.svg create mode 100644 assets/nano-icons/app-icons/inbox.svg create mode 100644 assets/nano-icons/app-icons/menu.svg create mode 100644 assets/nano-icons/app-icons/message-filled.svg create mode 100644 assets/nano-icons/app-icons/message.svg create mode 100644 assets/nano-icons/app-icons/mute.svg create mode 100644 assets/nano-icons/app-icons/pause.svg create mode 100644 assets/nano-icons/app-icons/pin.svg create mode 100644 assets/nano-icons/app-icons/play.svg create mode 100644 assets/nano-icons/app-icons/reply.svg create mode 100644 assets/nano-icons/app-icons/repost-corner2.svg create mode 100644 assets/nano-icons/app-icons/repost.svg create mode 100644 assets/nano-icons/app-icons/search-filled.svg create mode 100644 assets/nano-icons/app-icons/search.svg create mode 100644 assets/nano-icons/app-icons/share.svg create mode 100644 assets/nano-icons/app-icons/unmute.svg create mode 100644 assets/nano-icons/nanoicons/app-icons.glyphmap.json create mode 100644 assets/nano-icons/nanoicons/app-icons.ttf create mode 100644 src/components/icons/nano.tsx diff --git a/app.config.js b/app.config.js index 22787090bb..df5324f13e 100644 --- a/app.config.js +++ b/app.config.js @@ -237,6 +237,10 @@ module.exports = function (_config) { 'expo-video', 'expo-localization', 'expo-web-browser', + [ + 'react-native-nano-icons', + {iconSets: [{inputDir: './assets/nano-icons/app-icons'}]}, + ], [ 'react-native-edge-to-edge', {android: {enforceNavigationBarContrast: false}}, diff --git a/assets/nano-icons/app-icons/arrow-top-right.svg b/assets/nano-icons/app-icons/arrow-top-right.svg new file mode 100644 index 0000000000..e91825d5f3 --- /dev/null +++ b/assets/nano-icons/app-icons/arrow-top-right.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/bell-filled.svg b/assets/nano-icons/app-icons/bell-filled.svg new file mode 100644 index 0000000000..fe75d77198 --- /dev/null +++ b/assets/nano-icons/app-icons/bell-filled.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/bell.svg b/assets/nano-icons/app-icons/bell.svg new file mode 100644 index 0000000000..69a07f2270 --- /dev/null +++ b/assets/nano-icons/app-icons/bell.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/bookmark-filled.svg b/assets/nano-icons/app-icons/bookmark-filled.svg new file mode 100644 index 0000000000..809970c48d --- /dev/null +++ b/assets/nano-icons/app-icons/bookmark-filled.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/bookmark.svg b/assets/nano-icons/app-icons/bookmark.svg new file mode 100644 index 0000000000..676174b8cf --- /dev/null +++ b/assets/nano-icons/app-icons/bookmark.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/bubble.svg b/assets/nano-icons/app-icons/bubble.svg new file mode 100644 index 0000000000..7dd254546e --- /dev/null +++ b/assets/nano-icons/app-icons/bubble.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/circle-check.svg b/assets/nano-icons/app-icons/circle-check.svg new file mode 100644 index 0000000000..b555a74d6e --- /dev/null +++ b/assets/nano-icons/app-icons/circle-check.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/clock.svg b/assets/nano-icons/app-icons/clock.svg new file mode 100644 index 0000000000..4ee902bf5a --- /dev/null +++ b/assets/nano-icons/app-icons/clock.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/earth.svg b/assets/nano-icons/app-icons/earth.svg new file mode 100644 index 0000000000..22f39e3955 --- /dev/null +++ b/assets/nano-icons/app-icons/earth.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/heart-filled.svg b/assets/nano-icons/app-icons/heart-filled.svg new file mode 100644 index 0000000000..a6e0777f05 --- /dev/null +++ b/assets/nano-icons/app-icons/heart-filled.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/heart.svg b/assets/nano-icons/app-icons/heart.svg new file mode 100644 index 0000000000..c11c6dc833 --- /dev/null +++ b/assets/nano-icons/app-icons/heart.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/home-filled.svg b/assets/nano-icons/app-icons/home-filled.svg new file mode 100644 index 0000000000..3d532c294c --- /dev/null +++ b/assets/nano-icons/app-icons/home-filled.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/home.svg b/assets/nano-icons/app-icons/home.svg new file mode 100644 index 0000000000..917f9fac0f --- /dev/null +++ b/assets/nano-icons/app-icons/home.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/inbox.svg b/assets/nano-icons/app-icons/inbox.svg new file mode 100644 index 0000000000..461ec3cc82 --- /dev/null +++ b/assets/nano-icons/app-icons/inbox.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/menu.svg b/assets/nano-icons/app-icons/menu.svg new file mode 100644 index 0000000000..71a1ec0fc9 --- /dev/null +++ b/assets/nano-icons/app-icons/menu.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/message-filled.svg b/assets/nano-icons/app-icons/message-filled.svg new file mode 100644 index 0000000000..2b355bb2ac --- /dev/null +++ b/assets/nano-icons/app-icons/message-filled.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/message.svg b/assets/nano-icons/app-icons/message.svg new file mode 100644 index 0000000000..41efcbd4fb --- /dev/null +++ b/assets/nano-icons/app-icons/message.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/mute.svg b/assets/nano-icons/app-icons/mute.svg new file mode 100644 index 0000000000..90262ea7d9 --- /dev/null +++ b/assets/nano-icons/app-icons/mute.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/pause.svg b/assets/nano-icons/app-icons/pause.svg new file mode 100644 index 0000000000..e88ade5c07 --- /dev/null +++ b/assets/nano-icons/app-icons/pause.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/pin.svg b/assets/nano-icons/app-icons/pin.svg new file mode 100644 index 0000000000..d1de5f2cdf --- /dev/null +++ b/assets/nano-icons/app-icons/pin.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/play.svg b/assets/nano-icons/app-icons/play.svg new file mode 100644 index 0000000000..239f4f8e77 --- /dev/null +++ b/assets/nano-icons/app-icons/play.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/reply.svg b/assets/nano-icons/app-icons/reply.svg new file mode 100644 index 0000000000..c0397689b9 --- /dev/null +++ b/assets/nano-icons/app-icons/reply.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/repost-corner2.svg b/assets/nano-icons/app-icons/repost-corner2.svg new file mode 100644 index 0000000000..a1efb8b17e --- /dev/null +++ b/assets/nano-icons/app-icons/repost-corner2.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/repost.svg b/assets/nano-icons/app-icons/repost.svg new file mode 100644 index 0000000000..250c216649 --- /dev/null +++ b/assets/nano-icons/app-icons/repost.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/search-filled.svg b/assets/nano-icons/app-icons/search-filled.svg new file mode 100644 index 0000000000..b3364d577e --- /dev/null +++ b/assets/nano-icons/app-icons/search-filled.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/search.svg b/assets/nano-icons/app-icons/search.svg new file mode 100644 index 0000000000..b007141551 --- /dev/null +++ b/assets/nano-icons/app-icons/search.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/share.svg b/assets/nano-icons/app-icons/share.svg new file mode 100644 index 0000000000..6b003cb531 --- /dev/null +++ b/assets/nano-icons/app-icons/share.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/app-icons/unmute.svg b/assets/nano-icons/app-icons/unmute.svg new file mode 100644 index 0000000000..6bdbc95589 --- /dev/null +++ b/assets/nano-icons/app-icons/unmute.svg @@ -0,0 +1 @@ + diff --git a/assets/nano-icons/nanoicons/app-icons.glyphmap.json b/assets/nano-icons/nanoicons/app-icons.glyphmap.json new file mode 100644 index 0000000000..46db6aaa86 --- /dev/null +++ b/assets/nano-icons/nanoicons/app-icons.glyphmap.json @@ -0,0 +1 @@ +{"m":{"f":"app-icons","u":1024,"z":1020,"s":59648,"h":"f31796d2f6d13414a394ceff67db6d504f46e260cf1e305618733472f96c181d"},"i":{"arrow-top-right":[1024,[[59648,"#000"]]],"bell-filled":[1024,[[59649,"#000"]]],"bell":[1024,[[59650,"#000"]]],"bookmark-filled":[1024,[[59651,"#000"]]],"bookmark":[1024,[[59652,"#000"]]],"bubble":[1024,[[59653,"#000"]]],"circle-check":[1024,[[59654,"#000"]]],"clock":[1024,[[59655,"#000"]]],"earth":[1024,[[59656,"#000"]]],"heart-filled":[1024,[[59657,"#000"]]],"heart":[1024,[[59658,"#000"]]],"home-filled":[1024,[[59659,"#000"]]],"home":[1024,[[59660,"#000"]]],"inbox":[1024,[[59661,"#000"]]],"menu":[1024,[[59662,"#000"]]],"message-filled":[1024,[[59663,"#000"]]],"message":[1024,[[59664,"#000"]]],"mute":[1024,[[59665,"#000"]]],"pause":[1024,[[59666,"#000"]]],"pin":[1024,[[59667,"#000"]]],"play":[1024,[[59668,"#000"]]],"reply":[1024,[[59669,"#000"]]],"repost-corner2":[1024,[[59670,"#000"]]],"repost":[1024,[[59671,"#000"]]],"search-filled":[1024,[[59672,"#000"]]],"search":[1024,[[59673,"#000"]]],"share":[1024,[[59674,"#000"]]],"unmute":[1024,[[59675,"#000"]]]}} \ No newline at end of file diff --git a/assets/nano-icons/nanoicons/app-icons.ttf b/assets/nano-icons/nanoicons/app-icons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c07c52449ba84c2f46a9960f1f39ef7054917719 GIT binary patch literal 5976 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'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} @@ -4722,6 +4729,10 @@ packages: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} + cli-spinners@3.4.0: + resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} + engines: {node: '>=18.20'} + cli-table@0.3.11: resolution: {integrity: sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==} engines: {node: '>= 0.2.0'} @@ -5013,6 +5024,9 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + cubic2quad@1.2.1: + resolution: {integrity: sha512-wT5Y7mO8abrV16gnssKdmIhIbA9wSkeMzhh27jAguKrV82i24wER0vL5TGhUJ9dbJNDcigoRZ0IAHFEEEI4THQ==} + data-urls@3.0.2: resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} engines: {node: '>=12'} @@ -5913,6 +5927,9 @@ packages: debug: optional: true + fonteditor-core@2.6.3: + resolution: {integrity: sha512-YUryIKjkenjZ41E7JvM3V+02Ak4mTHDDTwBWgs9KBzypzHqLZHuua1UDRevZNTKawmnq1dbBAa70Jddl2+F4FQ==} + fontfaceobserver@2.3.0: resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==} @@ -6405,6 +6422,10 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -6484,6 +6505,10 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -6987,6 +7012,10 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + engines: {node: '>=18'} + log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} @@ -7146,6 +7175,9 @@ packages: engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true + microbuffer@1.0.0: + resolution: {integrity: sha512-O/SUXauVN4x6RaEJFqSPcXNtLFL+QzJHKZlyDVYFwcDDRVca3Fa/37QXXC+4zAGGa4YhHrHxKXuuHvLDIQECtA==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -7542,6 +7574,10 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} + ora@9.4.1: + resolution: {integrity: sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==} + engines: {node: '>=20'} + orderedmap@2.1.1: resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} @@ -7666,6 +7702,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pathkit-wasm@1.0.0: + resolution: {integrity: sha512-oREIcGpwnBBto6PfB3C20aIw9jBPLcSNVF1AlL5tGlXsoL2zqWVuD0mi8WMJQHHNemm6aR1hHH2lz+TO551Fgw==} + pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -8068,6 +8107,10 @@ packages: pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + pyodide@0.29.4: + resolution: {integrity: sha512-tCseTsqU3kSxZIjkue5zXxTMNEwrKZwOIIEQRBA/VzHxFN1hoCxe4w41phfCdHd9it9RcCNQb5K/Re0InqMgvA==} + engines: {node: '>=18.0.0'} + qrcode@1.5.4: resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} engines: {node: '>=10.13.0'} @@ -8233,6 +8276,20 @@ packages: react: '*' react-native: '*' + react-native-nano-icons@0.2.1: + resolution: {integrity: sha512-aQD7MdOLJzP/dBtuzEZbkdsVWrPec47UTfFc1Y/TveS/dXWhumA0ujlT4shFiY5GcL2s35z7NA8ZbxZWibggQQ==} + hasBin: true + peerDependencies: + '@expo/config': '>=9.0.0' + expo: '*' + react: '*' + react-native: '*' + peerDependenciesMeta: + '@expo/config': + optional: true + expo: + optional: true + react-native-pager-view@6.8.0: resolution: {integrity: sha512-/wgFV8nB4TLnQ6j9e3TvNrHbPF5TINMZHaXt86GOV0NSJNMVGkWguniJVKrYLm85LL8KVhRkgdh43Rdu7PvW1A==} peerDependencies: @@ -8844,6 +8901,10 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + stdin-discarder@0.3.2: + resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} + engines: {node: '>=18'} + stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} @@ -8972,6 +9033,10 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svg2ttf@6.1.0: + resolution: {integrity: sha512-EjxgcmhKcBpx/3fR1hPwVtJAbUc/ZsDpwOTF74SI3PbzCg4pDHnxVmoSuqgEqxVJGqqkSCI6+82cucpn2D5aOw==} + hasBin: true + svgo@2.8.2: resolution: {integrity: sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA==} engines: {node: '>=10.13.0'} @@ -8982,6 +9047,9 @@ packages: engines: {node: '>=16'} hasBin: true + svgpath@2.6.0: + resolution: {integrity: sha512-OIWR6bKzXvdXYyO4DK/UWa1VA1JeKq8E+0ug2DG98Y/vOmMpfZNj+TIG988HjfYSqtcy/hFOtZq/n/j5GSESNg==} + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -9647,6 +9715,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoctocolors@2.2.0: + resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==} + engines: {node: '>=18'} + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -14166,6 +14238,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.6.2: {} + char-regex@1.0.2: {} char-regex@2.0.2: {} @@ -14248,6 +14322,8 @@ snapshots: cli-spinners@2.9.2: {} + cli-spinners@3.4.0: {} + cli-table@0.3.11: dependencies: colors: 1.0.3 @@ -14570,6 +14646,8 @@ snapshots: csstype@3.2.3: {} + cubic2quad@1.2.1: {} + data-urls@3.0.2: dependencies: abab: 2.0.6 @@ -15641,6 +15719,10 @@ snapshots: optionalDependencies: debug: 4.4.3(supports-color@8.1.1) + fonteditor-core@2.6.3: + dependencies: + '@xmldom/xmldom': 0.8.13 + fontfaceobserver@2.3.0: {} for-each@0.3.5: @@ -16151,6 +16233,8 @@ snapshots: is-interactive@1.0.0: {} + is-interactive@2.0.0: {} + is-map@2.0.3: {} is-negative-zero@2.0.3: {} @@ -16215,6 +16299,8 @@ snapshots: is-unicode-supported@0.1.0: {} + is-unicode-supported@2.1.0: {} + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -16958,6 +17044,11 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.2.0 + log-update@6.1.0: dependencies: ansi-escapes: 7.3.0 @@ -17228,6 +17319,8 @@ snapshots: - supports-color - utf-8-validate + microbuffer@1.0.0: {} + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -17471,6 +17564,17 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 + ora@9.4.1: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 3.4.0 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 7.0.1 + stdin-discarder: 0.3.2 + string-width: 8.2.1 + orderedmap@2.1.1: {} own-keys@1.0.1: @@ -17601,6 +17705,8 @@ snapshots: path-type@4.0.0: {} + pathkit-wasm@1.0.0: {} + pend@1.2.0: {} picocolors@1.1.1: {} @@ -18022,6 +18128,14 @@ snapshots: pure-rand@6.1.0: {} + pyodide@0.29.4: + dependencies: + '@types/emscripten': 1.41.5 + ws: 8.20.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + qrcode@1.5.4: dependencies: dijkstrajs: 1.0.3 @@ -18236,6 +18350,29 @@ snapshots: react: 19.2.3 react-native: 0.86.0(patch_hash=dd549527bb84c88acc7b0b1d521c9f4b666fda484c75cd0b282f8e9838524909)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) + react-native-nano-icons@0.2.1(@expo/config@57.0.8(@typescript/typescript6@6.0.2)(supports-color@8.1.1))(@typescript/typescript6@6.0.2)(expo@57.0.8)(react-native@0.86.0(patch_hash=dd549527bb84c88acc7b0b1d521c9f4b666fda484c75cd0b282f8e9838524909)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): + dependencies: + '@expo/config-plugins': 57.0.8(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + '@xmldom/xmldom': 0.9.10 + chalk: 5.6.2 + fonteditor-core: 2.6.3 + ora: 9.4.1 + pathkit-wasm: 1.0.0 + plist: 3.1.1 + pyodide: 0.29.4 + react: 19.2.3 + react-native: 0.86.0(patch_hash=dd549527bb84c88acc7b0b1d521c9f4b666fda484c75cd0b282f8e9838524909)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) + svg2ttf: 6.1.0 + xcode: 3.0.1 + optionalDependencies: + '@expo/config': 57.0.8(@typescript/typescript6@6.0.2)(supports-color@8.1.1) + expo: 57.0.8(patch_hash=1722861d12907a2d412d04230545b2fcbfa5547a79f99d763cb3e8de6f3a6f2a)(b32afa45429c882ad113a248f683620a) + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + react-native-pager-view@6.8.0(patch_hash=c8316acd33c9aef6531e8c272c2bfab7bd02af1255969eeaab2bad5ab48531cc)(react-native@0.86.0(patch_hash=dd549527bb84c88acc7b0b1d521c9f4b666fda484c75cd0b282f8e9838524909)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: react: 19.2.3 @@ -18947,6 +19084,8 @@ snapshots: statuses@2.0.2: {} + stdin-discarder@0.3.2: {} + stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 @@ -19079,6 +19218,15 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svg2ttf@6.1.0: + dependencies: + '@xmldom/xmldom': 0.9.10 + argparse: 2.0.1 + cubic2quad: 1.2.1 + lodash: 4.18.1 + microbuffer: 1.0.0 + svgpath: 2.6.0 + svgo@2.8.2: dependencies: commander: 7.2.0 @@ -19099,6 +19247,8 @@ snapshots: picocolors: 1.1.1 sax: 1.6.0 + svgpath@2.6.0: {} + symbol-tree@3.2.4: {} tagged-tag@1.0.0: {} @@ -19809,6 +19959,8 @@ snapshots: yocto-queue@0.1.0: {} + yoctocolors@2.2.0: {} + zod@3.25.76: {} zxing-wasm@3.1.3(@types/emscripten@1.41.5): diff --git a/src/components/Post/Embed/ExternalEmbed/index.tsx b/src/components/Post/Embed/ExternalEmbed/index.tsx index a496780524..b2b92d88e9 100644 --- a/src/components/Post/Embed/ExternalEmbed/index.tsx +++ b/src/components/Post/Embed/ExternalEmbed/index.tsx @@ -15,7 +15,7 @@ import {toNiceDomain} from '#/lib/strings/url-helpers' import {useExternalEmbedsPrefs} from '#/state/preferences' import {atoms as a, useTheme} from '#/alf' import {Divider} from '#/components/Divider' -import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' +import {EarthIcon as Globe} from '#/components/icons/nano' import {Link} from '#/components/Link' import {Text} from '#/components/Typography' import {IS_NATIVE} from '#/env' diff --git a/src/components/Post/Embed/StandardSiteEmbed/index.tsx b/src/components/Post/Embed/StandardSiteEmbed/index.tsx index 0c50723a15..9527e96b80 100644 --- a/src/components/Post/Embed/StandardSiteEmbed/index.tsx +++ b/src/components/Post/Embed/StandardSiteEmbed/index.tsx @@ -13,9 +13,8 @@ import {atoms as a, useBreakpoints, useTheme, utils, web} from '#/alf' import {ButtonIcon, ButtonText} from '#/components/Button' import {Divider} from '#/components/Divider' import {useInteractionState} from '#/components/hooks/useInteractionState' -import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRightIcon} from '#/components/icons/Arrow' -import {Clock_Stroke2_Corner0_Rounded as Clock} from '#/components/icons/Clock' import {StandardSite} from '#/components/icons/community/StandardSite' +import {ArrowTopRightIcon, ClockIcon as Clock} from '#/components/icons/nano' import {Link} from '#/components/Link' import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {matchStandardSitePublisher} from '#/components/Post/Embed/StandardSiteEmbed/publishers' diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx index a8ea65a43b..340e029144 100644 --- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerNative.tsx @@ -8,10 +8,12 @@ import {useAutoplayDisabled} from '#/state/preferences' import {atoms as a, useTheme} from '#/alf' import {AltBadgeWithDialog} from '#/components/AltBadgeWithDialog' import {useIsWithinMessage} from '#/components/dms/MessageContext' -import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' -import {Pause_Filled_Corner0_Rounded as PauseIcon} from '#/components/icons/Pause' -import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play' -import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' +import { + MuteIcon, + PauseIcon, + PlayIcon, + UnmuteIcon, +} from '#/components/icons/nano' import {KeepAwake} from '#/components/KeepAwake' import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {useReportDialogMetadataContext} from '#/components/moderation/ReportDialog/ReportDialogMetadataContext' diff --git a/src/components/PostControls/BookmarkButton.tsx b/src/components/PostControls/BookmarkButton.tsx index 8d43947823..37bfd25bd9 100644 --- a/src/components/PostControls/BookmarkButton.tsx +++ b/src/components/PostControls/BookmarkButton.tsx @@ -10,7 +10,10 @@ import {useFeedFeedbackContext} from '#/state/feed-feedback' import {useBookmarkMutation} from '#/state/queries/bookmarks/useBookmarkMutation' import {useRequireAuth} from '#/state/session' import {useTheme} from '#/alf' -import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark' +import { + BookmarkFilledIcon as BookmarkFilled, + BookmarkIcon as Bookmark, +} from '#/components/icons/nano' import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash' import * as toast from '#/components/Toast' import {useAnalytics} from '#/analytics' diff --git a/src/components/PostControls/PostMenu/index.tsx b/src/components/PostControls/PostMenu/index.tsx index 23a3e52401..c7ef7d2c48 100644 --- a/src/components/PostControls/PostMenu/index.tsx +++ b/src/components/PostControls/PostMenu/index.tsx @@ -5,7 +5,7 @@ import {useLingui} from '@lingui/react/macro' import {type Shadow} from '#/state/cache/post-shadow' import {EventStopper} from '#/view/com/util/EventStopper' -import {DotGrid3x1_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid' +import {MenuIcon as DotsHorizontal} from '#/components/icons/nano' import * as Menu from '#/components/Menu' import {useMenuControl} from '#/components/Menu' import {type app} from '#/lexicons' diff --git a/src/components/PostControls/RepostButton.tsx b/src/components/PostControls/RepostButton.tsx index 734cafa3c8..fa8296125a 100644 --- a/src/components/PostControls/RepostButton.tsx +++ b/src/components/PostControls/RepostButton.tsx @@ -9,8 +9,8 @@ import {useRequireAuth} from '#/state/session' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' +import {RepostIcon} from '#/components/icons/nano' import {CloseQuote_Stroke2_Corner1_Rounded as QuoteIcon} from '#/components/icons/Quote' -import {Repost_Stroke2_Corner3_Rounded as RepostIcon} from '#/components/icons/Repost' import {useFormatPostStatCount} from '#/components/PostControls/util' import {Text} from '#/components/Typography' import { diff --git a/src/components/PostControls/ShareMenu/index.tsx b/src/components/PostControls/ShareMenu/index.tsx index 6c2188b612..ffdbff8735 100644 --- a/src/components/PostControls/ShareMenu/index.tsx +++ b/src/components/PostControls/ShareMenu/index.tsx @@ -12,7 +12,7 @@ import {type Shadow} from '#/state/cache/post-shadow' import {useFeedFeedbackContext} from '#/state/feed-feedback' import {EventStopper} from '#/view/com/util/EventStopper' import {native} from '#/alf' -import {ArrowShareRight_Stroke2_Corner2_Rounded as ArrowShareRightIcon} from '#/components/icons/ArrowShareRight' +import {ShareIcon as ArrowShareRightIcon} from '#/components/icons/nano' import * as Menu from '#/components/Menu' import {useMenuControl} from '#/components/Menu' import {useAnalytics} from '#/analytics' diff --git a/src/components/PostControls/index.tsx b/src/components/PostControls/index.tsx index 6457267780..15797b88bc 100644 --- a/src/components/PostControls/index.tsx +++ b/src/components/PostControls/index.tsx @@ -19,7 +19,7 @@ import { useProgressGuideControls, } from '#/state/shell/progress-guide' import {atoms as a, useBreakpoints, useTheme} from '#/alf' -import {Reply as Bubble} from '#/components/icons/Reply' +import {ReplyIcon as Bubble} from '#/components/icons/nano' import {useFormatPostStatCount} from '#/components/PostControls/util' import * as Skele from '#/components/Skeleton' import * as Toast from '#/components/Toast' diff --git a/src/components/icons/nano.tsx b/src/components/icons/nano.tsx new file mode 100644 index 0000000000..0538fee53a --- /dev/null +++ b/src/components/icons/nano.tsx @@ -0,0 +1,109 @@ +import {type ColorValue, StyleSheet} from 'react-native' +import {createNanoIconSet} from 'react-native-nano-icons' + +import {useTheme} from '#/alf' +import {type Props, sizes} from '#/components/icons/common' +import glyphMap from '../../../assets/nano-icons/nanoicons/app-icons.glyphmap.json' + +const NanoIcon = createNanoIconSet(glyphMap) + +type IconName = keyof (typeof glyphMap)['i'] + +/** + * Drop-in replacement for `createSinglePathSVG` backed by a font glyph instead + * of a react-native-svg subtree. Each icon renders as one native text glyph + * rather than SvgView + Group + Path, which is three native views per icon. + * + * The prop contract deliberately mirrors `useCommonSVGProps` so call sites and + * wrappers such as `PostControlButtonIcon` do not have to change. + * + * Only icons that are a single filled path can move here. Anything using the + * `gradient` prop (`StarterPackIcon`) or several fill colours (`VerifiedCheck`) + * must stay on react-native-svg. + */ +export function createNanoIcon(name: IconName) { + return function Icon({fill, size, style, width, testID}: Props) { + const t = useTheme() + const flattened = StyleSheet.flatten(style) + + /* + * Mirrors useCommonSVGProps: an explicit `size` token wins, then a raw + * `width`, then the default. `fill` wins over a color inherited via style. + */ + const resolvedSize = Number(size ? sizes[size] : width || sizes.md) + const color = (fill || flattened?.color || t.palette.primary_500) as + ColorValue | ColorValue[] + + return ( + /* + * The a11y rule wants an accessibilityHint alongside accessibilityLabel, + * but a hint would be exactly wrong here: the label is empty precisely to + * remove this glyph from the accessibility tree, and a hint would put + * content back into it. See the accessibilityLabel comment below. + */ + // oxlint-disable-next-line react-native-a11y/has-accessibility-hint + + ) + } +} + +/* Post action bar */ +export const ReplyIcon = createNanoIcon('reply') +export const RepostIcon = createNanoIcon('repost') +export const HeartIcon = createNanoIcon('heart') +export const HeartFilledIcon = createNanoIcon('heart-filled') +export const BookmarkIcon = createNanoIcon('bookmark') +export const BookmarkFilledIcon = createNanoIcon('bookmark-filled') +export const ShareIcon = createNanoIcon('share') +export const MenuIcon = createNanoIcon('menu') + +/* Feed rows and post embeds */ +export const PinIcon = createNanoIcon('pin') +export const EarthIcon = createNanoIcon('earth') +export const PlayIcon = createNanoIcon('play') +export const PauseIcon = createNanoIcon('pause') +export const MuteIcon = createNanoIcon('mute') +export const UnmuteIcon = createNanoIcon('unmute') +export const ArrowTopRightIcon = createNanoIcon('arrow-top-right') +export const ClockIcon = createNanoIcon('clock') + +/* Feed loading skeletons - 8 rows x 3 icons per placeholder, one per profile tab */ +export const BubbleIcon = createNanoIcon('bubble') +export const RepostCorner2Icon = createNanoIcon('repost-corner2') + +/* Shell chrome, mounted for the whole session */ +export const HomeIcon = createNanoIcon('home') +export const HomeFilledIcon = createNanoIcon('home-filled') +export const SearchIcon = createNanoIcon('search') +export const SearchFilledIcon = createNanoIcon('search-filled') +export const MessageIcon = createNanoIcon('message') +export const MessageFilledIcon = createNanoIcon('message-filled') +export const BellIcon = createNanoIcon('bell') +export const BellFilledIcon = createNanoIcon('bell-filled') +export const InboxIcon = createNanoIcon('inbox') +export const CircleCheckIcon = createNanoIcon('circle-check') diff --git a/src/lib/custom-animations/LikeIcon.tsx b/src/lib/custom-animations/LikeIcon.tsx index 4ab9582aae..21d4e43db7 100644 --- a/src/lib/custom-animations/LikeIcon.tsx +++ b/src/lib/custom-animations/LikeIcon.tsx @@ -7,9 +7,9 @@ import Animated, { import {useTheme} from '#/alf' import { - Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled, - Heart2_Stroke2_Corner0_Rounded as HeartIconOutline, -} from '#/components/icons/Heart2' + HeartFilledIcon as HeartIconFilled, + HeartIcon as HeartIconOutline, +} from '#/components/icons/nano' const keyframe = new Keyframe({ 0: { diff --git a/src/style.css b/src/style.css index 48bacf81b9..46b24fdfc6 100644 --- a/src/style.css +++ b/src/style.css @@ -12,6 +12,22 @@ * HTML & BODY STYLES IN `web/index.html` and `bskyweb/templates/base.html` */ +/* + * Nano Icons font, used by the icons in `#/components/icons/nano`. + * + * On native the Expo config plugin links the TTF into the app bundle, but on + * web the library reads the family straight off CSS and never registers it at + * runtime - without this the icons render as tofu. + */ +@font-face { + font-family: 'app-icons'; + src: url('../assets/nano-icons/nanoicons/app-icons.ttf') format('truetype'); + font-weight: normal; + font-style: normal; + /* Icon glyphs have no sensible fallback, so never swap in one. */ + font-display: block; +} + /* Buttons and inputs have a font set by UA, so we'll have to reset that */ button, input, diff --git a/src/view/com/posts/PostFeedReason.tsx b/src/view/com/posts/PostFeedReason.tsx index 54fdbfe465..2a87fcf126 100644 --- a/src/view/com/posts/PostFeedReason.tsx +++ b/src/view/com/posts/PostFeedReason.tsx @@ -9,8 +9,7 @@ import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-disp import {makeProfileLink} from '#/lib/routes/links' import {useSession} from '#/state/session' import {atoms as a, useTheme} from '#/alf' -import {Pin_Stroke2_Corner0_Rounded as PinIcon} from '#/components/icons/Pin' -import {Repost_Stroke2_Corner3_Rounded as RepostIcon} from '#/components/icons/Repost' +import {PinIcon, RepostIcon} from '#/components/icons/nano' import {Link} from '#/components/Link' import {ProfileHoverCard} from '#/components/ProfileHoverCard' import {Text} from '#/components/Typography' diff --git a/src/view/com/util/LoadingPlaceholder.tsx b/src/view/com/util/LoadingPlaceholder.tsx index 97b8587933..e3d9f9123c 100644 --- a/src/view/com/util/LoadingPlaceholder.tsx +++ b/src/view/com/util/LoadingPlaceholder.tsx @@ -9,12 +9,12 @@ import { import {s} from '#/lib/styles' import {atoms as a, useTheme} from '#/alf' -import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble' import { - Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled, - Heart2_Stroke2_Corner0_Rounded as HeartIconOutline, -} from '#/components/icons/Heart2' -import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost' + BubbleIcon as Bubble, + HeartFilledIcon as HeartIconFilled, + HeartIcon as HeartIconOutline, + RepostCorner2Icon as Repost, +} from '#/components/icons/nano' export function LoadingPlaceholder({ width, diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index c33d2b7b8e..10dc3cae9a 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -34,23 +34,17 @@ import {Button, ButtonText} from '#/components/Button' import {useDialogControl} from '#/components/Dialog' import {SwitchAccountDialog} from '#/components/dialogs/SwitchAccount' import { - Bell_Filled_Corner0_Rounded as BellFilled, - Bell_Stroke2_Corner0_Rounded as Bell, -} from '#/components/icons/Bell' -import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheckIcon} from '#/components/icons/CircleCheck' -import { - HomeOpen_Filled_Corner0_Rounded as HomeFilled, - HomeOpen_Stoke2_Corner0_Rounded as Home, -} from '#/components/icons/HomeOpen' -import {Inbox_Stroke2_Corner2_Rounded as InboxIcon} from '#/components/icons/Inbox' -import { - MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled, - MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass, -} from '#/components/icons/MagnifyingGlass' -import { - Message_Stroke2_Corner0_Rounded as Message, - Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, -} from '#/components/icons/Message' + BellFilledIcon as BellFilled, + BellIcon as Bell, + CircleCheckIcon, + HomeFilledIcon as HomeFilled, + HomeIcon as Home, + InboxIcon, + MessageFilledIcon as MessageFilled, + MessageIcon as Message, + SearchFilledIcon as MagnifyingGlassFilled, + SearchIcon as MagnifyingGlass, +} from '#/components/icons/nano' import * as Menu from '#/components/Menu' import * as Toast from '#/components/Toast' import {Text} from '#/components/Typography'