diff --git a/.eslintrc.js b/.eslintrc.js index f6407fa6fa..2d5f2822ac 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -33,7 +33,6 @@ module.exports = { ], 'bsky-internal/use-exact-imports': 'error', 'bsky-internal/use-typed-gates': 'error', - 'bsky-internal/use-prefixed-imports': 'warn', 'simple-import-sort/imports': [ 'warn', { diff --git a/.github/workflows/golang-test-lint.yml b/.github/workflows/golang-test-lint.yml index c8124dbade..36e28841d5 100644 --- a/.github/workflows/golang-test-lint.yml +++ b/.github/workflows/golang-test-lint.yml @@ -21,7 +21,7 @@ jobs: with: go-version: '1.22' - name: Dummy Static Files - run: touch bskyweb/static/js/blah.js && touch bskyweb/static/css/blah.txt && touch bskyweb/static/media/blah.txt + run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt - name: Check run: cd bskyweb/ && make check - name: Build (binary) @@ -38,6 +38,6 @@ jobs: with: go-version: '1.22' - name: Dummy Static Files - run: touch bskyweb/static/js/blah.js && touch bskyweb/static/css/blah.txt && touch bskyweb/static/media/blah.txt + run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt - name: Lint run: cd bskyweb/ && make lint diff --git a/.prettierignore b/.prettierignore index 8ccbae2148..641a1b8bfc 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,4 @@ -# Ignore everything except JS-ey or CSS code. +# Ignore everything except JS-ey code. # Based on https://stackoverflow.com/a/70715829/458193 * !**/*.js @@ -7,8 +7,6 @@ !**/*.tsx !*/ -!**/*.css - # More specific ignores go below. .expo android diff --git a/Dockerfile.embedr b/Dockerfile.embedr index 663cbcfc51..9ff04aa5c7 100644 --- a/Dockerfile.embedr +++ b/Dockerfile.embedr @@ -40,7 +40,6 @@ RUN find ./bskyweb/embedr-static && find ./bskyweb/embedr-templates && find ./bs # hack around issue with empty directory and go:embed RUN touch bskyweb/static/js/empty.txt -RUN touch bskyweb/static/css/empty.txt RUN touch bskyweb/static/media/empty.txt # diff --git a/__tests__/lib/images.test.ts b/__tests__/lib/images.test.ts index a5acad25f6..595f566c47 100644 --- a/__tests__/lib/images.test.ts +++ b/__tests__/lib/images.test.ts @@ -1,30 +1,26 @@ -import {deleteAsync} from 'expo-file-system' -import {manipulateAsync, SaveFormat} from 'expo-image-manipulator' +import ImageResizer from '@bam.tech/react-native-image-resizer' import RNFetchBlob from 'rn-fetch-blob' import { downloadAndResize, DownloadAndResizeOpts, - getResizedDimensions, } from '../../src/lib/media/manip' -const mockResizedImage = { - path: 'file://resized-image.jpg', - size: 100, - width: 100, - height: 100, - mime: 'image/jpeg', -} - describe('downloadAndResize', () => { const errorSpy = jest.spyOn(global.console, 'error') + const mockResizedImage = { + path: jest.fn().mockReturnValue('file://resized-image.jpg'), + size: 100, + width: 50, + height: 50, + mime: 'image/jpeg', + } + beforeEach(() => { - const mockedCreateResizedImage = manipulateAsync as jest.Mock - mockedCreateResizedImage.mockResolvedValue({ - uri: 'file://resized-image.jpg', - ...mockResizedImage, - }) + const mockedCreateResizedImage = + ImageResizer.createResizedImage as jest.Mock + mockedCreateResizedImage.mockResolvedValue(mockResizedImage) }) afterEach(() => { @@ -58,17 +54,17 @@ describe('downloadAndResize', () => { 'GET', 'https://example.com/image.jpg', ) - - // First time it gets called is to get dimensions - expect(manipulateAsync).toHaveBeenCalledWith(expect.any(String), [], {}) - expect(manipulateAsync).toHaveBeenCalledWith( - expect.any(String), - [{resize: {height: opts.height, width: opts.width}}], - {format: SaveFormat.JPEG, compress: 1.0}, + expect(ImageResizer.createResizedImage).toHaveBeenCalledWith( + 'file://downloaded-image.jpg', + 100, + 100, + 'JPEG', + 100, + undefined, + undefined, + undefined, + {mode: 'cover'}, ) - expect(deleteAsync).toHaveBeenCalledWith(expect.any(String), { - idempotent: true, - }) }) it('should return undefined for invalid URI', async () => { @@ -86,6 +82,46 @@ describe('downloadAndResize', () => { expect(result).toBeUndefined() }) + it('should return undefined for unsupported file type', async () => { + const mockedFetch = RNFetchBlob.fetch as jest.Mock + mockedFetch.mockResolvedValueOnce({ + path: jest.fn().mockReturnValue('file://downloaded-image'), + info: jest.fn().mockReturnValue({status: 200}), + flush: jest.fn(), + }) + + const opts: DownloadAndResizeOpts = { + uri: 'https://example.com/image', + width: 100, + height: 100, + maxSize: 500000, + mode: 'cover', + timeout: 10000, + } + + const result = await downloadAndResize(opts) + expect(result).toEqual(mockResizedImage) + expect(RNFetchBlob.config).toHaveBeenCalledWith({ + fileCache: true, + appendExt: 'jpeg', + }) + expect(RNFetchBlob.fetch).toHaveBeenCalledWith( + 'GET', + 'https://example.com/image', + ) + expect(ImageResizer.createResizedImage).toHaveBeenCalledWith( + 'file://downloaded-image', + 100, + 100, + 'JPEG', + 100, + undefined, + undefined, + undefined, + {mode: 'cover'}, + ) + }) + it('should return undefined for non-200 response', async () => { const mockedFetch = RNFetchBlob.fetch as jest.Mock mockedFetch.mockResolvedValueOnce({ @@ -107,44 +143,4 @@ describe('downloadAndResize', () => { expect(errorSpy).not.toHaveBeenCalled() expect(result).toBeUndefined() }) - - it('should not downsize whenever dimensions are below the max dimensions', () => { - const initialDimensionsOne = { - width: 1200, - height: 1000, - } - const resizedDimensionsOne = getResizedDimensions(initialDimensionsOne) - - const initialDimensionsTwo = { - width: 1000, - height: 1200, - } - const resizedDimensionsTwo = getResizedDimensions(initialDimensionsTwo) - - expect(resizedDimensionsOne).toEqual(initialDimensionsOne) - expect(resizedDimensionsTwo).toEqual(initialDimensionsTwo) - }) - - it('should resize dimensions and maintain aspect ratio if they are above the max dimensons', () => { - const initialDimensionsOne = { - width: 3000, - height: 1500, - } - const resizedDimensionsOne = getResizedDimensions(initialDimensionsOne) - - const initialDimensionsTwo = { - width: 2000, - height: 4000, - } - const resizedDimensionsTwo = getResizedDimensions(initialDimensionsTwo) - - expect(resizedDimensionsOne).toEqual({ - width: 2000, - height: 1000, - }) - expect(resizedDimensionsTwo).toEqual({ - width: 1000, - height: 2000, - }) - }) }) diff --git a/assets/icons/accessibility_stroke2_corner2_rounded.svg b/assets/icons/accessibility_stroke2_corner2_rounded.svg deleted file mode 100644 index 62184bd8d9..0000000000 --- a/assets/icons/accessibility_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/alien_stroke2_corner0_rounded.svg b/assets/icons/alien_stroke2_corner0_rounded.svg index c4dcc350a5..595308c97b 100644 --- a/assets/icons/alien_stroke2_corner0_rounded.svg +++ b/assets/icons/alien_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/apple_stroke2_corner0_rounded.svg b/assets/icons/apple_stroke2_corner0_rounded.svg index 300b1396af..3c7f051a3c 100644 --- a/assets/icons/apple_stroke2_corner0_rounded.svg +++ b/assets/icons/apple_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/arrowBoxLeft_stroke2_corner2_rounded.svg b/assets/icons/arrowBoxLeft_stroke2_corner2_rounded.svg deleted file mode 100644 index ea9afbc60f..0000000000 --- a/assets/icons/arrowBoxLeft_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/arrowTriangleBottom_stroke2_corner1_rounded.svg b/assets/icons/arrowTriangleBottom_stroke2_corner1_rounded.svg index e8bd913893..f40546f7cd 100644 --- a/assets/icons/arrowTriangleBottom_stroke2_corner1_rounded.svg +++ b/assets/icons/arrowTriangleBottom_stroke2_corner1_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/arrowsDiagonalIn_stroke2_corner0_rounded.svg b/assets/icons/arrowsDiagonalIn_stroke2_corner0_rounded.svg index 84e0d1e53c..a9532cd9c6 100644 --- a/assets/icons/arrowsDiagonalIn_stroke2_corner0_rounded.svg +++ b/assets/icons/arrowsDiagonalIn_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/arrowsDiagonalIn_stroke2_corner2_rounded.svg b/assets/icons/arrowsDiagonalIn_stroke2_corner2_rounded.svg index 16b7f7db11..9b92e533eb 100644 --- a/assets/icons/arrowsDiagonalIn_stroke2_corner2_rounded.svg +++ b/assets/icons/arrowsDiagonalIn_stroke2_corner2_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/arrowsDiagonalOut_stroke2_corner0_rounded.svg b/assets/icons/arrowsDiagonalOut_stroke2_corner0_rounded.svg index ef8268de1f..9987b34406 100644 --- a/assets/icons/arrowsDiagonalOut_stroke2_corner0_rounded.svg +++ b/assets/icons/arrowsDiagonalOut_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/arrowsDiagonalOut_stroke2_corner2_rounded.svg b/assets/icons/arrowsDiagonalOut_stroke2_corner2_rounded.svg index 56a6a6165a..36d8e1d67c 100644 --- a/assets/icons/arrowsDiagonalOut_stroke2_corner2_rounded.svg +++ b/assets/icons/arrowsDiagonalOut_stroke2_corner2_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/aspectRatio11_stroke2_corner0_rounded.svg b/assets/icons/aspectRatio11_stroke2_corner0_rounded.svg deleted file mode 100644 index 949eba697b..0000000000 --- a/assets/icons/aspectRatio11_stroke2_corner0_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/aspectRatio34_stroke2_corner0_rounded.svg b/assets/icons/aspectRatio34_stroke2_corner0_rounded.svg deleted file mode 100644 index 50761a05c5..0000000000 --- a/assets/icons/aspectRatio34_stroke2_corner0_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/aspectRatio43_stroke2_corner0_rounded.svg b/assets/icons/aspectRatio43_stroke2_corner0_rounded.svg deleted file mode 100644 index 4ca8362e94..0000000000 --- a/assets/icons/aspectRatio43_stroke2_corner0_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/at_stroke2_corner0_rounded.svg b/assets/icons/at_stroke2_corner0_rounded.svg index e43b7f77c3..8d30d7c8c5 100644 --- a/assets/icons/at_stroke2_corner0_rounded.svg +++ b/assets/icons/at_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/at_stroke2_corner2_rounded.svg b/assets/icons/at_stroke2_corner2_rounded.svg deleted file mode 100644 index 37ccbda238..0000000000 --- a/assets/icons/at_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/atom_stroke2_corner0_rounded.svg b/assets/icons/atom_stroke2_corner0_rounded.svg index a2965fd3be..723fdeab6d 100644 --- a/assets/icons/atom_stroke2_corner0_rounded.svg +++ b/assets/icons/atom_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/bell2_filled_corner0_rounded.svg b/assets/icons/bell2_filled_corner0_rounded.svg index 556854d35a..9c66129b11 100644 --- a/assets/icons/bell2_filled_corner0_rounded.svg +++ b/assets/icons/bell2_filled_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/bell2_stroke2_corner0_rounded.svg b/assets/icons/bell2_stroke2_corner0_rounded.svg index 5290906ab8..577bc5eaa1 100644 --- a/assets/icons/bell2_stroke2_corner0_rounded.svg +++ b/assets/icons/bell2_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/bellOff_filled_corner0_rounded.svg b/assets/icons/bellOff_filled_corner0_rounded.svg index 80b48c4c93..4c6997a709 100644 --- a/assets/icons/bellOff_filled_corner0_rounded.svg +++ b/assets/icons/bellOff_filled_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/bellOff_stroke2_corner0_rounded.svg b/assets/icons/bellOff_stroke2_corner0_rounded.svg index 07b30755e9..0ed4910d4b 100644 --- a/assets/icons/bellOff_stroke2_corner0_rounded.svg +++ b/assets/icons/bellOff_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/bell_filled_corner0_rounded.svg b/assets/icons/bell_filled_corner0_rounded.svg index 249e8bc19b..3f21b7e9bd 100644 --- a/assets/icons/bell_filled_corner0_rounded.svg +++ b/assets/icons/bell_filled_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/bell_stroke2_corner0_rounded.svg b/assets/icons/bell_stroke2_corner0_rounded.svg index 8b50bb2eef..a31f1bd152 100644 --- a/assets/icons/bell_stroke2_corner0_rounded.svg +++ b/assets/icons/bell_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/birthdayCake_stroke2_corner2_rounded.svg b/assets/icons/birthdayCake_stroke2_corner2_rounded.svg deleted file mode 100644 index 542e1552f4..0000000000 --- a/assets/icons/birthdayCake_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/bubbleInfo_stroke2_corner2_rounded.svg b/assets/icons/bubbleInfo_stroke2_corner2_rounded.svg deleted file mode 100644 index 2cc08924f8..0000000000 --- a/assets/icons/bubbleInfo_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/bubbleQuestion_stroke2_corner0_rounded.svg b/assets/icons/bubbleQuestion_stroke2_corner0_rounded.svg index fa37c14332..0bfcc48a0e 100644 --- a/assets/icons/bubbleQuestion_stroke2_corner0_rounded.svg +++ b/assets/icons/bubbleQuestion_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/calendar_stroke2_corner0_rounded.svg b/assets/icons/calendar_stroke2_corner0_rounded.svg index a8180a22fb..703f389dba 100644 --- a/assets/icons/calendar_stroke2_corner0_rounded.svg +++ b/assets/icons/calendar_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/camera_filled_stroke2_corner0_rounded.svg b/assets/icons/camera_filled_stroke2_corner0_rounded.svg index a6baed32c6..fa0101cf0d 100644 --- a/assets/icons/camera_filled_stroke2_corner0_rounded.svg +++ b/assets/icons/camera_filled_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/camera_stroke2_corner0_rounded.svg b/assets/icons/camera_stroke2_corner0_rounded.svg index 789bc14a84..ce0c29ae50 100644 --- a/assets/icons/camera_stroke2_corner0_rounded.svg +++ b/assets/icons/camera_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/cc_filled_stroke2_corner0_rounded.svg b/assets/icons/cc_filled_stroke2_corner0_rounded.svg index ae8466e67e..58823ca80d 100644 --- a/assets/icons/cc_filled_stroke2_corner0_rounded.svg +++ b/assets/icons/cc_filled_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/cc_stroke2_corner0_rounded.svg b/assets/icons/cc_stroke2_corner0_rounded.svg index 4d711d85be..fcda1570f9 100644 --- a/assets/icons/cc_stroke2_corner0_rounded.svg +++ b/assets/icons/cc_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/celebrate_stroke2_corner0_rounded.svg b/assets/icons/celebrate_stroke2_corner0_rounded.svg index f2ea3c44d8..3ea7bc8d2a 100644 --- a/assets/icons/celebrate_stroke2_corner0_rounded.svg +++ b/assets/icons/celebrate_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/chevronBottom_stroke2_corner0_rounded.svg b/assets/icons/chevronBottom_stroke2_corner0_rounded.svg index 085a28bb8c..705c1c5139 100644 --- a/assets/icons/chevronBottom_stroke2_corner0_rounded.svg +++ b/assets/icons/chevronBottom_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/chevronTop_stroke2_corner0_rounded.svg b/assets/icons/chevronTop_stroke2_corner0_rounded.svg index 2012da128f..da94ba911f 100644 --- a/assets/icons/chevronTop_stroke2_corner0_rounded.svg +++ b/assets/icons/chevronTop_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/circleBanSign_stroke2_corner0_rounded.svg b/assets/icons/circleBanSign_stroke2_corner0_rounded.svg index 349e9c32de..73251477fa 100644 --- a/assets/icons/circleBanSign_stroke2_corner0_rounded.svg +++ b/assets/icons/circleBanSign_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/circleQuestion_stroke2_corner2_rounded.svg b/assets/icons/circleQuestion_stroke2_corner2_rounded.svg deleted file mode 100644 index a534f98716..0000000000 --- a/assets/icons/circleQuestion_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/clipboard_stroke2_corner2_rounded.svg b/assets/icons/clipboard_stroke2_corner2_rounded.svg index bcda20dd69..f403cfb929 100644 --- a/assets/icons/clipboard_stroke2_corner2_rounded.svg +++ b/assets/icons/clipboard_stroke2_corner2_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/closeQuote_filled_stroke2_corner0_rounded.svg b/assets/icons/closeQuote_filled_stroke2_corner0_rounded.svg index 3eddcbfacc..41e75887c0 100644 --- a/assets/icons/closeQuote_filled_stroke2_corner0_rounded.svg +++ b/assets/icons/closeQuote_filled_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/closeQuote_stroke2_corner0_rounded.svg b/assets/icons/closeQuote_stroke2_corner0_rounded.svg index 304d3c59db..3c76c73920 100644 --- a/assets/icons/closeQuote_stroke2_corner0_rounded.svg +++ b/assets/icons/closeQuote_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/closeQuote_stroke2_corner1_rounded.svg b/assets/icons/closeQuote_stroke2_corner1_rounded.svg index 357ede850e..b27eb94f23 100644 --- a/assets/icons/closeQuote_stroke2_corner1_rounded.svg +++ b/assets/icons/closeQuote_stroke2_corner1_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/coffee_stroke2_corner0_rounded.svg b/assets/icons/coffee_stroke2_corner0_rounded.svg index 90bd5b6ae1..b734ef606a 100644 --- a/assets/icons/coffee_stroke2_corner0_rounded.svg +++ b/assets/icons/coffee_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/colorPalette_stroke2_corner0_rounded.svg b/assets/icons/colorPalette_stroke2_corner0_rounded.svg index db94cb6208..b1056e1a96 100644 --- a/assets/icons/colorPalette_stroke2_corner0_rounded.svg +++ b/assets/icons/colorPalette_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/earth_stroke2_corner0_rounded.svg b/assets/icons/earth_stroke2_corner0_rounded.svg index e130a2e819..02d2fe0b0e 100644 --- a/assets/icons/earth_stroke2_corner0_rounded.svg +++ b/assets/icons/earth_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/emojiArc_stroke2_corner0_rounded.svg b/assets/icons/emojiArc_stroke2_corner0_rounded.svg index 33a1e208f2..3d09228e71 100644 --- a/assets/icons/emojiArc_stroke2_corner0_rounded.svg +++ b/assets/icons/emojiArc_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/emojiHeartEyes_stroke2_corner0_rounded.svg b/assets/icons/emojiHeartEyes_stroke2_corner0_rounded.svg index 9cebeb17c5..4aecb86c54 100644 --- a/assets/icons/emojiHeartEyes_stroke2_corner0_rounded.svg +++ b/assets/icons/emojiHeartEyes_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/emojiSad_stroke2_corner0_rounded.svg b/assets/icons/emojiSad_stroke2_corner0_rounded.svg index fbbfd366d9..0a5a43cd0b 100644 --- a/assets/icons/emojiSad_stroke2_corner0_rounded.svg +++ b/assets/icons/emojiSad_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/emojiSmile_stroke2_corner0_rounded.svg b/assets/icons/emojiSmile_stroke2_corner0_rounded.svg index fac12d4c79..fd329b5098 100644 --- a/assets/icons/emojiSmile_stroke2_corner0_rounded.svg +++ b/assets/icons/emojiSmile_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/envelope_filled_stroke2_corner0_rounded.svg b/assets/icons/envelope_filled_stroke2_corner0_rounded.svg index 6c3ea83020..3810bf334f 100644 --- a/assets/icons/envelope_filled_stroke2_corner0_rounded.svg +++ b/assets/icons/envelope_filled_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/envelope_stroke2_corner0_rounded.svg b/assets/icons/envelope_stroke2_corner0_rounded.svg index 4de98cca03..c3ab45980b 100644 --- a/assets/icons/envelope_stroke2_corner0_rounded.svg +++ b/assets/icons/envelope_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/envelope_stroke2_corner2_rounded.svg b/assets/icons/envelope_stroke2_corner2_rounded.svg deleted file mode 100644 index 39331f8a12..0000000000 --- a/assets/icons/envelope_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/explosion_stroke2_corner0_rounded.svg b/assets/icons/explosion_stroke2_corner0_rounded.svg index e661ec3511..11544bf79c 100644 --- a/assets/icons/explosion_stroke2_corner0_rounded.svg +++ b/assets/icons/explosion_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/eyeSlash_stroke2_corner0_rounded.svg b/assets/icons/eyeSlash_stroke2_corner0_rounded.svg index 6de34c5f3d..f11bdd937f 100644 --- a/assets/icons/eyeSlash_stroke2_corner0_rounded.svg +++ b/assets/icons/eyeSlash_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/eye_stroke2_corner0_rounded.svg b/assets/icons/eye_stroke2_corner0_rounded.svg index 604b3ff79e..035daa6e1c 100644 --- a/assets/icons/eye_stroke2_corner0_rounded.svg +++ b/assets/icons/eye_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/eye_stroke2_corner2_rounded.svg b/assets/icons/eye_stroke2_corner2_rounded.svg deleted file mode 100644 index 81e31ba032..0000000000 --- a/assets/icons/eye_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/flag_stroke2_corner0_rounded.svg b/assets/icons/flag_stroke2_corner0_rounded.svg index d1de1d0075..9f9cc5cdd1 100644 --- a/assets/icons/flag_stroke2_corner0_rounded.svg +++ b/assets/icons/flag_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/flipHorizontal_stroke2_corner0_rounded.svg b/assets/icons/flipHorizontal_stroke2_corner0_rounded.svg deleted file mode 100644 index 0d4c81d2c1..0000000000 --- a/assets/icons/flipHorizontal_stroke2_corner0_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/flipVertical_stroke2_corner0_rounded.svg b/assets/icons/flipVertical_stroke2_corner0_rounded.svg deleted file mode 100644 index 42fca985c5..0000000000 --- a/assets/icons/flipVertical_stroke2_corner0_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/gameController_stroke2_corner0_rounded.svg b/assets/icons/gameController_stroke2_corner0_rounded.svg index a3584d158c..e530c802b0 100644 --- a/assets/icons/gameController_stroke2_corner0_rounded.svg +++ b/assets/icons/gameController_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/gifSquare_stroke2_corner0_rounded.svg b/assets/icons/gifSquare_stroke2_corner0_rounded.svg index 7d44106dd5..47b9df9846 100644 --- a/assets/icons/gifSquare_stroke2_corner0_rounded.svg +++ b/assets/icons/gifSquare_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/gif_stroke2_corner0_rounded.svg b/assets/icons/gif_stroke2_corner0_rounded.svg index 019900b593..519acfd4d2 100644 --- a/assets/icons/gif_stroke2_corner0_rounded.svg +++ b/assets/icons/gif_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/globe_stroke2_corner0_rounded.svg b/assets/icons/globe_stroke2_corner0_rounded.svg index fe0142abcb..83cb88d136 100644 --- a/assets/icons/globe_stroke2_corner0_rounded.svg +++ b/assets/icons/globe_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/group3_stroke2_corner0_rounded.svg b/assets/icons/group3_stroke2_corner0_rounded.svg index 4a7cc0c2e4..2a8f43a8a4 100644 --- a/assets/icons/group3_stroke2_corner0_rounded.svg +++ b/assets/icons/group3_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/growth_stroke2_corner0_rounded.svg b/assets/icons/growth_stroke2_corner0_rounded.svg index f5d931cf9c..ec9083fb1e 100644 --- a/assets/icons/growth_stroke2_corner0_rounded.svg +++ b/assets/icons/growth_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/lab_stroke2_corner0_rounded.svg b/assets/icons/lab_stroke2_corner0_rounded.svg index cb1e017b9c..466809194e 100644 --- a/assets/icons/lab_stroke2_corner0_rounded.svg +++ b/assets/icons/lab_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/leaf_stroke2_corner0_rounded.svg b/assets/icons/leaf_stroke2_corner0_rounded.svg index f5d931cf9c..16b379f98c 100644 --- a/assets/icons/leaf_stroke2_corner0_rounded.svg +++ b/assets/icons/leaf_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/lock_stroke2_corner0_rounded.svg b/assets/icons/lock_stroke2_corner0_rounded.svg index 6a2717fae9..8b094ba5eb 100644 --- a/assets/icons/lock_stroke2_corner0_rounded.svg +++ b/assets/icons/lock_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/lock_stroke2_corner2_rounded.svg b/assets/icons/lock_stroke2_corner2_rounded.svg deleted file mode 100644 index 8e34c3b05c..0000000000 --- a/assets/icons/lock_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/message_stroke2_corner0_rounded.svg b/assets/icons/message_stroke2_corner0_rounded.svg index 1cfdb51250..2cbaa3e628 100644 --- a/assets/icons/message_stroke2_corner0_rounded.svg +++ b/assets/icons/message_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/message_stroke2_corner0_rounded_filled.svg b/assets/icons/message_stroke2_corner0_rounded_filled.svg index 8e012013a2..0de0246727 100644 --- a/assets/icons/message_stroke2_corner0_rounded_filled.svg +++ b/assets/icons/message_stroke2_corner0_rounded_filled.svg @@ -1 +1 @@ - + diff --git a/assets/icons/moon_stroke2_corner2_rounded.svg b/assets/icons/moon_stroke2_corner2_rounded.svg index 06758ee3ff..8f5c03699b 100644 --- a/assets/icons/moon_stroke2_corner2_rounded.svg +++ b/assets/icons/moon_stroke2_corner2_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/musicNote_stroke2_corner0_rounded.svg b/assets/icons/musicNote_stroke2_corner0_rounded.svg index 031ba810ef..2dcc2e3b29 100644 --- a/assets/icons/musicNote_stroke2_corner0_rounded.svg +++ b/assets/icons/musicNote_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/mute_stroke2_corner0_rounded.svg b/assets/icons/mute_stroke2_corner0_rounded.svg index ab73567563..8ebecb3920 100644 --- a/assets/icons/mute_stroke2_corner0_rounded.svg +++ b/assets/icons/mute_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/news2_stroke2_corner0_rounded.svg b/assets/icons/news2_stroke2_corner0_rounded.svg index a298ad69a9..66e4c373a0 100644 --- a/assets/icons/news2_stroke2_corner0_rounded.svg +++ b/assets/icons/news2_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/newskie.svg b/assets/icons/newskie.svg index 308049f586..e3a9d83c80 100644 --- a/assets/icons/newskie.svg +++ b/assets/icons/newskie.svg @@ -1 +1 @@ - + diff --git a/assets/icons/openQuote_filled_stroke2_corner0_rounded.svg b/assets/icons/openQuote_filled_stroke2_corner0_rounded.svg index 97db191f3b..e8141a1128 100644 --- a/assets/icons/openQuote_filled_stroke2_corner0_rounded.svg +++ b/assets/icons/openQuote_filled_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/openQuote_stroke2_corner0_rounded.svg b/assets/icons/openQuote_stroke2_corner0_rounded.svg index 10cd33d20e..eee6344cee 100644 --- a/assets/icons/openQuote_stroke2_corner0_rounded.svg +++ b/assets/icons/openQuote_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/paintRoller_stroke2_corner2_rounded.svg b/assets/icons/paintRoller_stroke2_corner2_rounded.svg deleted file mode 100644 index 3ebb36aa82..0000000000 --- a/assets/icons/paintRoller_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/pause_filled_corner0_rounded.svg b/assets/icons/pause_filled_corner0_rounded.svg index b901dc7c51..0037701f90 100644 --- a/assets/icons/pause_filled_corner0_rounded.svg +++ b/assets/icons/pause_filled_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/pause_filled_corner2_rounded.svg b/assets/icons/pause_filled_corner2_rounded.svg index 3eebad4453..98726d873e 100644 --- a/assets/icons/pause_filled_corner2_rounded.svg +++ b/assets/icons/pause_filled_corner2_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/pause_stroke2_corner0_rounded.svg b/assets/icons/pause_stroke2_corner0_rounded.svg index ee1c978f8e..d2735ed2bd 100644 --- a/assets/icons/pause_stroke2_corner0_rounded.svg +++ b/assets/icons/pause_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/pause_stroke2_corner2_rounded.svg b/assets/icons/pause_stroke2_corner2_rounded.svg index 6ce60ecf9a..3a8c0b4379 100644 --- a/assets/icons/pause_stroke2_corner2_rounded.svg +++ b/assets/icons/pause_stroke2_corner2_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/pencil_stroke2_corner0_rounded.svg b/assets/icons/pencil_stroke2_corner0_rounded.svg index 098b180763..7341989894 100644 --- a/assets/icons/pencil_stroke2_corner0_rounded.svg +++ b/assets/icons/pencil_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/peopleRemove2_stroke2_corner0_rounded.svg b/assets/icons/peopleRemove2_stroke2_corner0_rounded.svg index b8d7437caa..daec6f5579 100644 --- a/assets/icons/peopleRemove2_stroke2_corner0_rounded.svg +++ b/assets/icons/peopleRemove2_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/personCheck_stroke2_corner0_rounded.svg b/assets/icons/personCheck_stroke2_corner0_rounded.svg index 212b166b15..b3231c2780 100644 --- a/assets/icons/personCheck_stroke2_corner0_rounded.svg +++ b/assets/icons/personCheck_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/personPlus_stroke2_corner0_rounded.svg b/assets/icons/personPlus_stroke2_corner0_rounded.svg index 2de70426cb..118268bf97 100644 --- a/assets/icons/personPlus_stroke2_corner0_rounded.svg +++ b/assets/icons/personPlus_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/personX_stroke2_corner0_rounded.svg b/assets/icons/personX_stroke2_corner0_rounded.svg index 248e6ca76a..073015bc54 100644 --- a/assets/icons/personX_stroke2_corner0_rounded.svg +++ b/assets/icons/personX_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/person_stroke2_corner0_rounded.svg b/assets/icons/person_stroke2_corner0_rounded.svg index 01037371d3..a23ad76071 100644 --- a/assets/icons/person_stroke2_corner0_rounded.svg +++ b/assets/icons/person_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/person_stroke2_corner2_rounded.svg b/assets/icons/person_stroke2_corner2_rounded.svg deleted file mode 100644 index 7088c2880c..0000000000 --- a/assets/icons/person_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/phone_stroke2_corner2_rounded.svg b/assets/icons/phone_stroke2_corner2_rounded.svg index 7e555c37a4..4f44f08e52 100644 --- a/assets/icons/phone_stroke2_corner2_rounded.svg +++ b/assets/icons/phone_stroke2_corner2_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/piggyBank_stroke2_corner0_rounded.svg b/assets/icons/piggyBank_stroke2_corner0_rounded.svg index 0ec432635b..36d3060102 100644 --- a/assets/icons/piggyBank_stroke2_corner0_rounded.svg +++ b/assets/icons/piggyBank_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/pizza_stroke2_corner0_rounded.svg b/assets/icons/pizza_stroke2_corner0_rounded.svg index 5f809453b3..e63351b897 100644 --- a/assets/icons/pizza_stroke2_corner0_rounded.svg +++ b/assets/icons/pizza_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/play_filled_corner0_rounded.svg b/assets/icons/play_filled_corner0_rounded.svg index 2b030208fb..7bee1ae9a3 100644 --- a/assets/icons/play_filled_corner0_rounded.svg +++ b/assets/icons/play_filled_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/play_stroke2_corner0_rounded.svg b/assets/icons/play_stroke2_corner0_rounded.svg index 0aecbfabb7..d7321b9b7b 100644 --- a/assets/icons/play_stroke2_corner0_rounded.svg +++ b/assets/icons/play_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/poop_stroke2_corner0_rounded.svg b/assets/icons/poop_stroke2_corner0_rounded.svg index d6342739ff..daa6c11126 100644 --- a/assets/icons/poop_stroke2_corner0_rounded.svg +++ b/assets/icons/poop_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/qrCode_stroke2_corner0_rounded.svg b/assets/icons/qrCode_stroke2_corner0_rounded.svg index 680354f802..b17db39533 100644 --- a/assets/icons/qrCode_stroke2_corner0_rounded.svg +++ b/assets/icons/qrCode_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/raisingHand4Finger_stroke2_corner0_rounded.svg b/assets/icons/raisingHand4Finger_stroke2_corner0_rounded.svg index e032738579..aed3d9e7ec 100644 --- a/assets/icons/raisingHand4Finger_stroke2_corner0_rounded.svg +++ b/assets/icons/raisingHand4Finger_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/rose_stroke2_corner0_rounded.svg b/assets/icons/rose_stroke2_corner0_rounded.svg index 2b5ed9ab79..2d269855bd 100644 --- a/assets/icons/rose_stroke2_corner0_rounded.svg +++ b/assets/icons/rose_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/settingsGear2_filled_corner0_rounded.svg b/assets/icons/settingsGear2_filled_corner0_rounded.svg index b01874bde2..dfc89ff507 100644 --- a/assets/icons/settingsGear2_filled_corner0_rounded.svg +++ b/assets/icons/settingsGear2_filled_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/shaka_stroke2_corner0_rounded.svg b/assets/icons/shaka_stroke2_corner0_rounded.svg index 63f6c222aa..32af469e4a 100644 --- a/assets/icons/shaka_stroke2_corner0_rounded.svg +++ b/assets/icons/shaka_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/shield_stroke2_corner0_rounded.svg b/assets/icons/shield_stroke2_corner0_rounded.svg index 36057176ff..c4ef98e5ad 100644 --- a/assets/icons/shield_stroke2_corner0_rounded.svg +++ b/assets/icons/shield_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/speakerVolumeFull_stroke2_corner0_rounded.svg b/assets/icons/speakerVolumeFull_stroke2_corner0_rounded.svg index 9e0c2cfda3..81357a12e3 100644 --- a/assets/icons/speakerVolumeFull_stroke2_corner0_rounded.svg +++ b/assets/icons/speakerVolumeFull_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/starterPack.svg b/assets/icons/starterPack.svg index cefbab50cb..7f0df55952 100644 --- a/assets/icons/starterPack.svg +++ b/assets/icons/starterPack.svg @@ -1 +1 @@ - + diff --git a/assets/icons/starter_pack_icon.svg b/assets/icons/starter_pack_icon.svg index 3d604b76dd..47a2f49b64 100644 --- a/assets/icons/starter_pack_icon.svg +++ b/assets/icons/starter_pack_icon.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/textSize_stroke2_corner0_rounded.svg b/assets/icons/textSize_stroke2_corner0_rounded.svg index 27665627d4..6c7537d100 100644 --- a/assets/icons/textSize_stroke2_corner0_rounded.svg +++ b/assets/icons/textSize_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/ticket_stroke2_corner0_rounded.svg b/assets/icons/ticket_stroke2_corner0_rounded.svg index 0edb01eedf..a45a90ae5f 100644 --- a/assets/icons/ticket_stroke2_corner0_rounded.svg +++ b/assets/icons/ticket_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/timesLarge_stroke2_corner0_rounded.svg b/assets/icons/timesLarge_stroke2_corner0_rounded.svg index 8b0f526bfb..68403f5984 100644 --- a/assets/icons/timesLarge_stroke2_corner0_rounded.svg +++ b/assets/icons/timesLarge_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/trash_stroke2_corner0_rounded.svg b/assets/icons/trash_stroke2_corner0_rounded.svg index e7cbf50be8..d4b32f81fe 100644 --- a/assets/icons/trash_stroke2_corner0_rounded.svg +++ b/assets/icons/trash_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/trash_stroke2_corner2_rounded.svg b/assets/icons/trash_stroke2_corner2_rounded.svg deleted file mode 100644 index e97dfe90c4..0000000000 --- a/assets/icons/trash_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/trending2_stroke2_corner2_rounded.svg b/assets/icons/trending2_stroke2_corner2_rounded.svg index e5c2db79a9..cc806b0eb6 100644 --- a/assets/icons/trending2_stroke2_corner2_rounded.svg +++ b/assets/icons/trending2_stroke2_corner2_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/triangleExclamation_stroke2_corner2_rounded.svg b/assets/icons/triangleExclamation_stroke2_corner2_rounded.svg index ac71eab37a..aa56404457 100644 --- a/assets/icons/triangleExclamation_stroke2_corner2_rounded.svg +++ b/assets/icons/triangleExclamation_stroke2_corner2_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/ufo_stroke2_corner0_rounded.svg b/assets/icons/ufo_stroke2_corner0_rounded.svg index ae86aa7bef..115c589e0a 100644 --- a/assets/icons/ufo_stroke2_corner0_rounded.svg +++ b/assets/icons/ufo_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/userCircle_filled_corner0_rounded.svg b/assets/icons/userCircle_filled_corner0_rounded.svg index 984205b020..67bb6eac77 100644 --- a/assets/icons/userCircle_filled_corner0_rounded.svg +++ b/assets/icons/userCircle_filled_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/userCircle_stroke2_corner0_rounded.svg b/assets/icons/userCircle_stroke2_corner0_rounded.svg index 7a3747e28c..ffad04f2b7 100644 --- a/assets/icons/userCircle_stroke2_corner0_rounded.svg +++ b/assets/icons/userCircle_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + diff --git a/assets/icons/verified_stroke2_corner2_rounded.svg b/assets/icons/verified_stroke2_corner2_rounded.svg deleted file mode 100644 index 048b2816e3..0000000000 --- a/assets/icons/verified_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/window_stroke2_corner2_rounded.svg b/assets/icons/window_stroke2_corner2_rounded.svg deleted file mode 100644 index 859c00c4a5..0000000000 --- a/assets/icons/window_stroke2_corner2_rounded.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/zap_stroke2_corner0_rounded.svg b/assets/icons/zap_stroke2_corner0_rounded.svg index 979649fddb..06a88ad8d2 100644 --- a/assets/icons/zap_stroke2_corner0_rounded.svg +++ b/assets/icons/zap_stroke2_corner0_rounded.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/bskyembed/src/components/embed.tsx b/bskyembed/src/components/embed.tsx index 82c3fd60a0..1ed107b592 100644 --- a/bskyembed/src/components/embed.tsx +++ b/bskyembed/src/components/embed.tsx @@ -158,12 +158,6 @@ export function Embed({ return The quoted post is blocked. } - // Case 3.8: Detached quote post - if (AppBskyEmbedRecord.isViewDetached(record)) { - // Just don't show anything - return null - } - // Unknown embed type return null } diff --git a/bskyembed/src/index.css b/bskyembed/src/index.css index 22b2b8be5c..23457ec28d 100644 --- a/bskyembed/src/index.css +++ b/bskyembed/src/index.css @@ -4,4 +4,4 @@ .break-word { word-break: break-word; -} +} \ No newline at end of file diff --git a/bskyweb/.gitignore b/bskyweb/.gitignore index a63a381f94..05b3ad7ab5 100644 --- a/bskyweb/.gitignore +++ b/bskyweb/.gitignore @@ -10,10 +10,6 @@ static/js/*.js static/js/*.map static/js/*.js.LICENSE.txt static/js/empty.txt -static/css/*.css -static/css/*.map -static/css/*.css.LICENSE.txt -static/css/empty.txt static/media/*.png static/media/empty.txt templates/scripts.html diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index fd80a5ed14..2d75a2b723 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -210,11 +210,6 @@ func serve(cctx *cli.Context) error { maxAge = 7 * (60 * 60 * 24) // 1 week } - // fonts can be cached for a year - if strings.HasSuffix(path, ".otf") { - maxAge = 365 * (60 * 60 * 24) // 1 year - } - c.Response().Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", maxAge)) return next(c) } diff --git a/bskyweb/static/css/.gitkeep b/bskyweb/static/css/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/bskyweb/static/media/Inter-Black.66e9a87f1c921e844ed4.otf b/bskyweb/static/media/Inter-Black.66e9a87f1c921e844ed4.otf deleted file mode 100644 index 44d1779af6..0000000000 Binary files a/bskyweb/static/media/Inter-Black.66e9a87f1c921e844ed4.otf and /dev/null differ diff --git a/bskyweb/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf b/bskyweb/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf deleted file mode 100644 index 6fc475e415..0000000000 Binary files a/bskyweb/static/media/Inter-BlackItalic.27b9f0ad06fd13a7b9da.otf and /dev/null differ diff --git a/bskyweb/static/media/Inter-Bold.8d330503e1d034ad68de.otf b/bskyweb/static/media/Inter-Bold.8d330503e1d034ad68de.otf deleted file mode 100644 index 58a38073e8..0000000000 Binary files a/bskyweb/static/media/Inter-Bold.8d330503e1d034ad68de.otf and /dev/null differ diff --git a/bskyweb/static/media/Inter-BoldItalic.bb17e63f9baa0d861a20.otf b/bskyweb/static/media/Inter-BoldItalic.bb17e63f9baa0d861a20.otf deleted file mode 100644 index e67935aa5a..0000000000 Binary files a/bskyweb/static/media/Inter-BoldItalic.bb17e63f9baa0d861a20.otf and /dev/null differ diff --git a/bskyweb/static/media/Inter-ExtraBold.ff2581a193bf6b7e0b06.otf b/bskyweb/static/media/Inter-ExtraBold.ff2581a193bf6b7e0b06.otf deleted file mode 100644 index 66cd95228c..0000000000 Binary files a/bskyweb/static/media/Inter-ExtraBold.ff2581a193bf6b7e0b06.otf and /dev/null differ diff --git a/bskyweb/static/media/Inter-ExtraBoldItalic.0e50b40728d24d40fdf4.otf b/bskyweb/static/media/Inter-ExtraBoldItalic.0e50b40728d24d40fdf4.otf deleted file mode 100644 index f269814a64..0000000000 Binary files a/bskyweb/static/media/Inter-ExtraBoldItalic.0e50b40728d24d40fdf4.otf and /dev/null differ diff --git a/bskyweb/static/media/Inter-Italic.95778eb0c75dc956257e.otf b/bskyweb/static/media/Inter-Italic.95778eb0c75dc956257e.otf deleted file mode 100644 index f78848b987..0000000000 Binary files a/bskyweb/static/media/Inter-Italic.95778eb0c75dc956257e.otf and /dev/null differ diff --git a/bskyweb/static/media/Inter-Medium.296aa2d65964269836b3.otf b/bskyweb/static/media/Inter-Medium.296aa2d65964269836b3.otf deleted file mode 100644 index f44f89adac..0000000000 Binary files a/bskyweb/static/media/Inter-Medium.296aa2d65964269836b3.otf and /dev/null differ diff --git a/bskyweb/static/media/Inter-MediumItalic.0e57e17a6311368e2114.otf b/bskyweb/static/media/Inter-MediumItalic.0e57e17a6311368e2114.otf deleted file mode 100644 index 1970f57214..0000000000 Binary files a/bskyweb/static/media/Inter-MediumItalic.0e57e17a6311368e2114.otf and /dev/null differ diff --git a/bskyweb/static/media/Inter-Regular.1f5ed03b6dd9fd1f9982.otf b/bskyweb/static/media/Inter-Regular.1f5ed03b6dd9fd1f9982.otf deleted file mode 100644 index 2d0bd1d645..0000000000 Binary files a/bskyweb/static/media/Inter-Regular.1f5ed03b6dd9fd1f9982.otf and /dev/null differ diff --git a/bskyweb/static/media/Inter-SemiBold.2277990330981b8409bb.otf b/bskyweb/static/media/Inter-SemiBold.2277990330981b8409bb.otf deleted file mode 100644 index 52c84550ba..0000000000 Binary files a/bskyweb/static/media/Inter-SemiBold.2277990330981b8409bb.otf and /dev/null differ diff --git a/bskyweb/static/media/Inter-SemiBoldItalic.f62fea3df3a521d6c8a7.otf b/bskyweb/static/media/Inter-SemiBoldItalic.f62fea3df3a521d6c8a7.otf deleted file mode 100644 index b725bfc883..0000000000 Binary files a/bskyweb/static/media/Inter-SemiBoldItalic.f62fea3df3a521d6c8a7.otf and /dev/null differ diff --git a/bskyweb/static/media/MaterialIcons.f20305dee9d396fea5c7.ttf b/bskyweb/static/media/MaterialIcons.f20305dee9d396fea5c7.ttf deleted file mode 100644 index 9d09b0feb8..0000000000 Binary files a/bskyweb/static/media/MaterialIcons.f20305dee9d396fea5c7.ttf and /dev/null differ diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html index 03686ef5c4..5dc5a9e8a2 100644 --- a/bskyweb/templates/base.html +++ b/bskyweb/templates/base.html @@ -13,72 +13,297 @@ - - - - - - - - - - - {% include "scripts.html" %} @@ -95,7 +320,7 @@ {%- block body_all %}
-
+
diff --git a/eslint/index.js b/eslint/index.js index 6f75f1bc34..cf5d41225d 100644 --- a/eslint/index.js +++ b/eslint/index.js @@ -5,6 +5,5 @@ module.exports = { 'avoid-unwrapped-text': require('./avoid-unwrapped-text'), 'use-exact-imports': require('./use-exact-imports'), 'use-typed-gates': require('./use-typed-gates'), - 'use-prefixed-imports': require('./use-prefixed-imports'), }, } diff --git a/eslint/use-exact-imports.js b/eslint/use-exact-imports.js index 26e688563e..06723043fe 100644 --- a/eslint/use-exact-imports.js +++ b/eslint/use-exact-imports.js @@ -1,3 +1,4 @@ +/* eslint-disable bsky-internal/use-exact-imports */ const BANNED_IMPORTS = [ '@fortawesome/free-regular-svg-icons', '@fortawesome/free-solid-svg-icons', @@ -5,12 +6,11 @@ const BANNED_IMPORTS = [ exports.create = function create(context) { return { - ImportDeclaration(node) { - const source = node.source - if (typeof source.value !== 'string') { + Literal(node) { + if (typeof node.value !== 'string') { return } - if (BANNED_IMPORTS.includes(source.value)) { + if (BANNED_IMPORTS.includes(node.value)) { context.report({ node, message: diff --git a/eslint/use-prefixed-imports.js b/eslint/use-prefixed-imports.js deleted file mode 100644 index 141d536484..0000000000 --- a/eslint/use-prefixed-imports.js +++ /dev/null @@ -1,39 +0,0 @@ -const BANNED_IMPORT_PREFIXES = [ - 'alf/', - 'components/', - 'lib/', - 'locale/', - 'logger/', - 'platform/', - 'state/', - 'storage/', - 'view/', -] - -module.exports = { - meta: { - type: 'suggestion', - fixable: 'code', - }, - create(context) { - return { - ImportDeclaration(node) { - const source = node.source - if (typeof source.value !== 'string') { - return - } - if ( - BANNED_IMPORT_PREFIXES.some(banned => source.value.startsWith(banned)) - ) { - context.report({ - node: source, - message: `Use '#/${source.value}'`, - fix(fixer) { - return fixer.replaceText(source, `'#/${source.value}'`) - }, - }) - } - }, - } - }, -} diff --git a/jest/jestSetup.js b/jest/jestSetup.js index 50a33589ea..a68c1dc4bf 100644 --- a/jest/jestSetup.js +++ b/jest/jestSetup.js @@ -42,16 +42,8 @@ jest.mock('rn-fetch-blob', () => ({ fetch: jest.fn(), })) -jest.mock('expo-file-system', () => ({ - getInfoAsync: jest.fn().mockResolvedValue({exists: true, size: 100}), - deleteAsync: jest.fn(), -})) - -jest.mock('expo-image-manipulator', () => ({ - manipulateAsync: jest.fn().mockResolvedValue({ - uri: 'file://resized-image', - }), - SaveFormat: jest.requireActual('expo-image-manipulator').SaveFormat, +jest.mock('@bam.tech/react-native-image-resizer', () => ({ + createResizedImage: jest.fn(), })) jest.mock('@segment/analytics-react-native', () => ({ diff --git a/modules/BlueskyNSE/NotificationService.swift b/modules/BlueskyNSE/NotificationService.swift index 481402890f..f863eaf223 100644 --- a/modules/BlueskyNSE/NotificationService.swift +++ b/modules/BlueskyNSE/NotificationService.swift @@ -2,80 +2,46 @@ import UserNotifications import UIKit let APP_GROUP = "group.app.bsky" -typealias ContentHandler = (UNNotificationContent) -> Void - -// This extension allows us to do some processing of the received notification -// data before displaying the notification to the user. In our use case, there -// are a few particular things that we want to do: -// -// - Determine whether we should play a sound for the notification -// - Download and display any images for the notification -// - Update the badge count accordingly -// -// The extension may or may not create a new process to handle a notification. -// It is also possible that multiple notifications will be processed by the -// same instance of `NotificationService`, though these will happen in -// parallel. -// -// Because multiple instances of `NotificationService` may exist, we should -// be careful in accessing preferences that will be mutated _by the -// extension itself_. For example, we should not worry about `playChatSound` -// changing, since we never mutate that value within the extension itself. -// However, since we mutate `badgeCount` frequently, we should ensure that -// these updates always run sync with each other and that the have access -// to the most recent values. class NotificationService: UNNotificationServiceExtension { - private var contentHandler: ContentHandler? - private var bestAttempt: UNMutableNotificationContent? + var prefs = UserDefaults(suiteName: APP_GROUP) override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { - self.contentHandler = contentHandler - - guard let bestAttempt = NSEUtil.createCopy(request.content), + guard let bestAttempt = createCopy(request.content), let reason = request.content.userInfo["reason"] as? String else { contentHandler(request.content) return } - self.bestAttempt = bestAttempt if reason == "chat-message" { mutateWithChatMessage(bestAttempt) } else { mutateWithBadge(bestAttempt) } - // Any image downloading (or other network tasks) should be handled at the end - // of this block. Otherwise, if there is a timeout and serviceExtensionTimeWillExpire - // gets called, we might not have all the needed mutations completed in time. - contentHandler(bestAttempt) } override func serviceExtensionTimeWillExpire() { - guard let contentHandler = self.contentHandler, - let bestAttempt = self.bestAttempt else { - return - } - contentHandler(bestAttempt) + // If for some reason the alloted time expires, we don't actually want to display a notification } - // MARK: Mutations + func createCopy(_ content: UNNotificationContent) -> UNMutableNotificationContent? { + return content.mutableCopy() as? UNMutableNotificationContent + } func mutateWithBadge(_ content: UNMutableNotificationContent) { - NSEUtil.shared.prefsQueue.sync { - var count = NSEUtil.shared.prefs?.integer(forKey: "badgeCount") ?? 0 - count += 1 + var count = prefs?.integer(forKey: "badgeCount") ?? 0 + count += 1 - // Set the new badge number for the notification, then store that value for using later - content.badge = NSNumber(value: count) - NSEUtil.shared.prefs?.setValue(count, forKey: "badgeCount") - } + // Set the new badge number for the notification, then store that value for using later + content.badge = NSNumber(value: count) + prefs?.setValue(count, forKey: "badgeCount") } func mutateWithChatMessage(_ content: UNMutableNotificationContent) { - if NSEUtil.shared.prefs?.bool(forKey: "playSoundChat") == true { + if self.prefs?.bool(forKey: "playSoundChat") == true { mutateWithDmSound(content) } } @@ -88,18 +54,3 @@ class NotificationService: UNNotificationServiceExtension { content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "dm.aiff")) } } - -// NSEUtil's purpose is to create a shared instance of `UserDefaults` across -// `NotificationService` instances. It also includes a queue so that we can process -// updates to `UserDefaults` in parallel. - -private class NSEUtil { - static let shared = NSEUtil() - - var prefs = UserDefaults(suiteName: APP_GROUP) - var prefsQueue = DispatchQueue(label: "NSEPrefsQueue") - - static func createCopy(_ content: UNNotificationContent) -> UNMutableNotificationContent? { - return content.mutableCopy() as? UNMutableNotificationContent - } -} diff --git a/modules/Share-with-Bluesky/Info.plist b/modules/Share-with-Bluesky/Info.plist index 43f46a5e56..421abb3c41 100644 --- a/modules/Share-with-Bluesky/Info.plist +++ b/modules/Share-with-Bluesky/Info.plist @@ -16,8 +16,6 @@ 1 NSExtensionActivationSupportsImageWithMaxCount 10 - NSExtensionActivationSupportsMovieWithMaxCount - 1 NSExtensionPointIdentifier @@ -40,4 +38,4 @@ CFBundleShortVersionString $(MARKETING_VERSION) - + \ No newline at end of file diff --git a/modules/Share-with-Bluesky/ShareViewController.swift b/modules/Share-with-Bluesky/ShareViewController.swift index 63143277a5..c045d578fe 100644 --- a/modules/Share-with-Bluesky/ShareViewController.swift +++ b/modules/Share-with-Bluesky/ShareViewController.swift @@ -5,6 +5,7 @@ class ShareViewController: UIViewController { // scheme. let appScheme = Bundle.main.object(forInfoDictionaryKey: "MainAppScheme") as? String ?? "bluesky" + // override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) @@ -23,8 +24,6 @@ class ShareViewController: UIViewController { await self.handleUrl(item: firstAttachment) } else if firstAttachment.hasItemConformingToTypeIdentifier("public.image") { await self.handleImages(items: attachments) - } else if firstAttachment.hasItemConformingToTypeIdentifier("public.video") { - await self.handleVideos(items: attachments) } else { self.completeRequest() } @@ -32,23 +31,31 @@ class ShareViewController: UIViewController { } private func handleText(item: NSItemProvider) async { - if let data = try? await item.loadItem(forTypeIdentifier: "public.text") as? String { - if let encoded = data.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed), - let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)") { - _ = self.openURL(url) + do { + if let data = try await item.loadItem(forTypeIdentifier: "public.text") as? String { + if let encoded = data.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed), + let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)") { + _ = self.openURL(url) + } } + self.completeRequest() + } catch { + self.completeRequest() } - self.completeRequest() } private func handleUrl(item: NSItemProvider) async { - if let data = try? await item.loadItem(forTypeIdentifier: "public.url") as? URL { - if let encoded = data.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed), - let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)") { - _ = self.openURL(url) + do { + if let data = try await item.loadItem(forTypeIdentifier: "public.url") as? URL { + if let encoded = data.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed), + let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)") { + _ = self.openURL(url) + } } + self.completeRequest() + } catch { + self.completeRequest() } - self.completeRequest() } private func handleImages(items: [NSItemProvider]) async { @@ -98,25 +105,6 @@ class ShareViewController: UIViewController { self.completeRequest() } - private func handleVideos(items: [NSItemProvider]) async { - let firstItem = items.first - - if let dataUri = try? await firstItem?.loadItem(forTypeIdentifier: "public.video") as? URL { - let ext = String(dataUri.lastPathComponent.split(separator: ".").last ?? "mp4") - if let tempUrl = getTempUrl(ext: ext) { - let data = try? Data(contentsOf: dataUri) - try? data?.write(to: tempUrl) - - if let encoded = dataUri.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed), - let url = URL(string: "\(self.appScheme)://intent/compose?videoUri=\(encoded)") { - _ = self.openURL(url) - } - } - } - - self.completeRequest() - } - private func saveImageWithInfo(_ image: UIImage?) -> String? { guard let image = image else { return nil @@ -126,26 +114,27 @@ class ShareViewController: UIViewController { // Saving this file to the bundle group's directory lets us access it from // inside of the app. Otherwise, we wouldn't have access even though the // extension does. - if let tempUrl = getTempUrl(ext: "jpeg"), - let jpegData = image.jpegData(compressionQuality: 1) { - try jpegData.write(to: tempUrl) - return "\(tempUrl.absoluteString)|\(image.size.width)|\(image.size.height)" + if let dir = FileManager() + .containerURL( + forSecurityApplicationGroupIdentifier: "group.app.bsky") { + let filePath = "\(dir.absoluteString)\(ProcessInfo.processInfo.globallyUniqueString).jpeg" + + if let newUri = URL(string: filePath), + let jpegData = image.jpegData(compressionQuality: 1) { + try jpegData.write(to: newUri) + return "\(newUri.absoluteString)|\(image.size.width)|\(image.size.height)" + } } - } catch {} - return nil + return nil + } catch { + return nil + } } private func completeRequest() { self.extensionContext?.completeRequest(returningItems: nil) } - private func getTempUrl(ext: String) -> URL? { - if let dir = FileManager().containerURL(forSecurityApplicationGroupIdentifier: "group.app.bsky") { - return URL(string: "\(dir.absoluteString)\(ProcessInfo.processInfo.globallyUniqueString).\(ext)")! - } - return nil - } - @objc func openURL(_ url: URL) -> Bool { var responder: UIResponder? = self while responder != nil { diff --git a/package.json b/package.json index 4b3486545e..ba7882902c 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "web": "expo start --web", "use-build-number": "./scripts/useBuildNumberEnv.sh", "use-build-number-with-bump": "./scripts/useBuildNumberEnvWithBump.sh", - "build-web": "expo export:web && node ./scripts/post-web-build.js", + "build-web": "expo export:web && node ./scripts/post-web-build.js && cp -v ./web-build/static/js/*.* ./bskyweb/static/js/ && cp -v ./web-build/static/media/*.png ./bskyweb/static/media/", "build-all": "yarn intl:build && yarn use-build-number-with-bump eas build --platform all", "build-ios": "yarn use-build-number-with-bump eas build -p ios", "build-android": "yarn use-build-number-with-bump eas build -p android", @@ -49,11 +49,11 @@ "export": "npx expo export", "make-deploy-bundle": "bash scripts/bundleUpdate.sh", "generate-webpack-stats-file": "EXPO_PUBLIC_GENERATE_STATS=1 yarn build-web", - "open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web", - "icons:optimize": "svgo -f ./assets/icons" + "open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web" }, "dependencies": { "@atproto/api": "^0.13.7", + "@bam.tech/react-native-image-resizer": "^3.0.4", "@braintree/sanitize-url": "^6.0.2", "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet", "@emoji-mart/react": "^1.1.1", @@ -110,13 +110,11 @@ "await-lock": "^2.2.2", "babel-plugin-transform-remove-console": "^6.9.4", "base64-js": "^1.5.1", - "bcp-47": "^2.1.0", "bcp-47-match": "^2.0.3", "date-fns": "^2.30.0", "deprecated-react-native-prop-types": "^5.0.0", "email-validator": "^2.0.4", "emoji-mart": "^5.5.2", - "emoji-regex": "^10.4.0", "eventemitter3": "^5.0.1", "expo": "^51.0.8", "expo-application": "^5.9.1", @@ -160,20 +158,24 @@ "lodash.set": "^4.3.2", "lodash.shuffle": "^4.2.0", "lodash.throttle": "^4.1.1", + "mobx": "^6.6.1", + "mobx-react-lite": "^3.4.0", + "mobx-utils": "^6.0.6", "nanoid": "^5.0.5", "normalize-url": "^8.0.0", "patch-package": "^6.5.1", "postinstall-postinstall": "^2.1.0", "psl": "^1.9.0", "react": "18.2.0", + "react-avatar-editor": "^13.0.0", "react-compiler-runtime": "file:./lib/react-compiler-runtime", "react-dom": "^18.2.0", - "react-image-crop": "^11.0.7", "react-keyed-flatten-children": "^3.0.0", "react-native": "0.74.1", "react-native-compressor": "^1.8.24", "react-native-date-picker": "^4.4.2", "react-native-drawer-layout": "^4.0.0-alpha.3", + "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.16.2", "react-native-get-random-values": "~1.11.0", "react-native-image-crop-picker": "0.41.2", @@ -202,7 +204,6 @@ "statsig-react-native-expo": "^4.6.1", "tippy.js": "^6.3.7", "tlds": "^1.234.0", - "tldts": "^6.1.46", "zeego": "^1.6.2", "zod": "^3.20.2" }, @@ -234,6 +235,7 @@ "@types/lodash.set": "^4.3.7", "@types/lodash.shuffle": "^4.2.7", "@types/psl": "^1.1.1", + "@types/react-avatar-editor": "^13.0.0", "@types/react-dom": "^18.2.18", "@types/react-responsive": "^8.0.5", "@types/react-test-renderer": "^17.0.1", @@ -267,7 +269,6 @@ "react-refresh": "^0.14.0", "react-scripts": "^5.0.1", "react-test-renderer": "18.2.0", - "svgo": "^3.3.2", "ts-node": "^10.9.1", "typescript": "^5.5.4", "url-loader": "^4.1.1", @@ -335,13 +336,8 @@ }, "lint-staged": { "*{.js,.jsx,.ts,.tsx}": [ - "eslint --cache --fix" - ], - "*{.js,.jsx,.ts,.tsx,.css}": [ + "eslint --cache --fix", "prettier --cache --write --ignore-unknown" - ], - "assets/icons/*.svg": [ - "svgo" ] } } diff --git a/patches/expo-modules-core+1.12.11.patch b/patches/expo-modules-core+1.12.11.patch index ea26b821da..4878bb9f7e 100644 --- a/patches/expo-modules-core+1.12.11.patch +++ b/patches/expo-modules-core+1.12.11.patch @@ -4,23 +4,11 @@ index bb74e80..0aa0202 100644 +++ b/node_modules/expo-modules-core/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java @@ -90,8 +90,8 @@ public class NativeModulesProxy extends ReactContextBaseJavaModule { mModuleRegistry.ensureIsInitialized(); - + KotlinInteropModuleRegistry kotlinModuleRegistry = getKotlinInteropModuleRegistry(); - kotlinModuleRegistry.emitOnCreate(); kotlinModuleRegistry.installJSIInterop(); + kotlinModuleRegistry.emitOnCreate(); - + Map constants = new HashMap<>(3); constants.put(MODULES_CONSTANTS_KEY, new HashMap<>()); -diff --git a/node_modules/expo-modules-core/build/uuid/uuid.js b/node_modules/expo-modules-core/build/uuid/uuid.js -index 109d3fe..c7fce9e 100644 ---- a/node_modules/expo-modules-core/build/uuid/uuid.js -+++ b/node_modules/expo-modules-core/build/uuid/uuid.js -@@ -1,5 +1,7 @@ - import bytesToUuid from './lib/bytesToUuid'; - import { Uuidv5Namespace } from './uuid.types'; -+import { ensureNativeModulesAreInstalled } from '../ensureNativeModulesAreInstalled'; -+ensureNativeModulesAreInstalled(); - const nativeUuidv4 = globalThis?.expo?.uuidv4; - const nativeUuidv5 = globalThis?.expo?.uuidv5; - function uuidv4() { diff --git a/patches/react-native+0.74.1.patch b/patches/react-native+0.74.1.patch index aee3da1ecc..789ba84ace 100644 --- a/patches/react-native+0.74.1.patch +++ b/patches/react-native+0.74.1.patch @@ -1,18 +1,5 @@ -diff --git a/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm b/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm -index caa5540..c5d4e67 100644 ---- a/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm -+++ b/node_modules/react-native/Libraries/Blob/RCTFileReaderModule.mm -@@ -73,7 +73,7 @@ @implementation RCTFileReaderModule - } else { - NSString *type = [RCTConvert NSString:blob[@"type"]]; - NSString *text = [NSString stringWithFormat:@"data:%@;base64,%@", -- type != nil && [type length] > 0 ? type : @"application/octet-stream", -+ ![type isEqual:[NSNull null]] && [type length] > 0 ? type : @"application/octet-stream", - [data base64EncodedStringWithOptions:0]]; - - resolve(text); diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm -index b0d71dc..41b9a0e 100644 +index b0d71dc..9974932 100644 --- a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm +++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm @@ -377,10 +377,6 @@ - (void)textInputDidBeginEditing @@ -49,7 +36,7 @@ index e9b330f..1ecdf0a 100644 + @end diff --git a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m -index b09e653..f93cb46 100644 +index b09e653..4c32b31 100644 --- a/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m +++ b/node_modules/react-native/React/Views/RefreshControl/RCTRefreshControl.m @@ -198,9 +198,53 @@ - (void)refreshControlValueChanged diff --git a/scripts/post-web-build.js b/scripts/post-web-build.js index 7bbee38554..baaa7cb8b7 100644 --- a/scripts/post-web-build.js +++ b/scripts/post-web-build.js @@ -20,30 +20,7 @@ console.log(`Writing ${templateFile}`) const outputFile = entrypoints .map(name => { const file = path.basename(name) - const ext = path.extname(file) - - if (ext === '.js') { - return `` - } - if (ext === '.css') { - return `` - } - - return '' + return `` }) .join('\n') fs.writeFileSync(templateFile, outputFile) - -function copyFiles(sourceDir, targetDir) { - const files = fs.readdirSync(path.join(projectRoot, sourceDir)) - files.forEach(file => { - const sourcePath = path.join(projectRoot, sourceDir, file) - const targetPath = path.join(projectRoot, targetDir, file) - fs.copyFileSync(sourcePath, targetPath) - console.log(`Copied ${sourcePath} to ${targetPath}`) - }) -} - -copyFiles('web-build/static/js', 'bskyweb/static/js') -copyFiles('web-build/static/css', 'bskyweb/static/css') -copyFiles('web-build/static/media', 'bskyweb/static/media') diff --git a/src/App.native.tsx b/src/App.native.tsx index c6334379f7..9214253aca 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -1,6 +1,6 @@ import 'react-native-url-polyfill/auto' -import '#/lib/sentry' // must be near top -import '#/view/icons' +import 'lib/sentry' // must be near top +import 'view/icons' import React, {useEffect, useState} from 'react' import {GestureHandlerRootView} from 'react-native-gesture-handler' @@ -29,11 +29,6 @@ import {Provider as A11yProvider} from '#/state/a11y' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as DialogStateProvider} from '#/state/dialogs' import {listenSessionDropped} from '#/state/events' -import { - beginResolveGeolocation, - ensureGeolocationResolved, - Provider as GeolocationProvider, -} from '#/state/geolocation' import {Provider as InvitesStateProvider} from '#/state/invites' import {Provider as LightboxStateProvider} from '#/state/lightbox' import {MessagesProvider} from '#/state/messages' @@ -60,7 +55,7 @@ import {TestCtrls} from '#/view/com/testing/TestCtrls' import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext' import * as Toast from '#/view/com/util/Toast' import {Shell} from '#/view/shell' -import {ThemeProvider as Alf} from '#/alf' +import {ThemeProvider as Alf, useFonts} from '#/alf' import {useColorModeTheme} from '#/alf/util/useColorModeTheme' import {NuxDialogs} from '#/components/dialogs/nuxs' import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry' @@ -71,11 +66,6 @@ import {BackgroundNotificationPreferencesProvider} from '../modules/expo-backgro SplashScreen.preventAutoHideAsync() -/** - * Begin geolocation ASAP - */ -beginResolveGeolocation() - function InnerApp() { const [isReady, setIsReady] = React.useState(false) const {currentAccount} = useSession() @@ -116,64 +106,60 @@ function InnerApp() { }, [_]) return ( - - - - - - + + + + + + - - - {/* LabelDefsProvider MUST come before ModerationOptsProvider */} - - - - - - - - - - - - - - - - - - - - - - - - - + + {/* LabelDefsProvider MUST come before ModerationOptsProvider */} + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + ) } function App() { const [isReady, setReady] = useState(false) + const [loaded] = useFonts() React.useEffect(() => { - Promise.all([initPersistedState(), ensureGeolocationResolved()]).then(() => - setReady(true), - ) + initPersistedState().then(() => setReady(true)) }, []) - if (!isReady) { + if (!isReady || !loaded) { return null } @@ -182,38 +168,36 @@ function App() { * that is set up in the InnerApp component above. */ return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) } diff --git a/src/App.web.tsx b/src/App.web.tsx index 1664812d08..1c66507336 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -1,6 +1,5 @@ -import '#/lib/sentry' // must be near top -import '#/view/icons' -import './style.css' +import 'lib/sentry' // must be near top +import 'view/icons' import React, {useEffect, useState} from 'react' import {KeyboardProvider} from 'react-native-keyboard-controller' @@ -19,11 +18,6 @@ import {Provider as A11yProvider} from '#/state/a11y' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as DialogStateProvider} from '#/state/dialogs' import {listenSessionDropped} from '#/state/events' -import { - beginResolveGeolocation, - ensureGeolocationResolved, - Provider as GeolocationProvider, -} from '#/state/geolocation' import {Provider as InvitesStateProvider} from '#/state/invites' import {Provider as LightboxStateProvider} from '#/state/lightbox' import {MessagesProvider} from '#/state/messages' @@ -52,7 +46,7 @@ import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/Video import * as Toast from '#/view/com/util/Toast' import {ToastContainer} from '#/view/com/util/Toast.web' import {Shell} from '#/view/shell/index' -import {ThemeProvider as Alf} from '#/alf' +import {ThemeProvider as Alf, useFonts} from '#/alf' import {useColorModeTheme} from '#/alf/util/useColorModeTheme' import {NuxDialogs} from '#/components/dialogs/nuxs' import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry' @@ -60,11 +54,6 @@ import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialo import {Provider as PortalProvider} from '#/components/Portal' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' -/** - * Begin geolocation ASAP - */ -beginResolveGeolocation() - function InnerApp() { const [isReady, setIsReady] = React.useState(false) const {currentAccount} = useSession() @@ -107,64 +96,61 @@ function InnerApp() { return ( - - - - - - + + + + + + - - - {/* LabelDefsProvider MUST come before ModerationOptsProvider */} - - - - - - - - - - - - - - - - - - - - - - - - + + {/* LabelDefsProvider MUST come before ModerationOptsProvider */} + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + ) } function App() { const [isReady, setReady] = useState(false) + const [loaded] = useFonts() React.useEffect(() => { - Promise.all([initPersistedState(), ensureGeolocationResolved()]).then(() => - setReady(true), - ) + initPersistedState().then(() => setReady(true)) }, []) - if (!isReady) { + if (!isReady || !loaded) { return null } @@ -173,33 +159,31 @@ function App() { * that is set up in the InnerApp component above. */ return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + ) } diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts index 0c8eb330d7..9f75d305ae 100644 --- a/src/alf/atoms.ts +++ b/src/alf/atoms.ts @@ -276,13 +276,16 @@ export const atoms = { letterSpacing: tokens.TRACKING, }, font_normal: { - fontWeight: tokens.fontWeight.regular, + fontWeight: tokens.fontWeight.normal, }, - font_bold: { + font_semibold: { fontWeight: tokens.fontWeight.semibold, }, + font_bold: { + fontWeight: tokens.fontWeight.bold, + }, font_heavy: { - fontWeight: tokens.fontWeight.extrabold, + fontWeight: tokens.fontWeight.heavy, }, italic: { fontStyle: 'italic', diff --git a/src/alf/fonts.ts b/src/alf/fonts.ts index b11ce939f8..ce658fa05b 100644 --- a/src/alf/fonts.ts +++ b/src/alf/fonts.ts @@ -1,4 +1,6 @@ -import {isWeb} from '#/platform/detection' +import {useFonts as defaultUseFonts} from 'expo-font' + +import {isNative, isWeb} from '#/platform/detection' import {Device, device} from '#/storage' const FAMILIES = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif` @@ -32,6 +34,38 @@ export function setFontFamily(fontFamily: Device['fontFamily']) { device.set(['fontFamily'], fontFamily) } +/* + * Unused fonts are commented out, but the files are there if we need them. + */ +export function useFonts() { + /** + * For native, the `expo-font` config plugin embeds the fonts in the + * application binary. But `expo-font` isn't supported on web, so we fall + * back to async loading here. + */ + if (isNative) return [true, null] + return defaultUseFonts({ + // 'Inter-Thin': require('../../assets/fonts/inter/Inter-Thin.otf'), + // 'Inter-ThinItalic': require('../../assets/fonts/inter/Inter-ThinItalic.otf'), + // 'Inter-ExtraLight': require('../../assets/fonts/inter/Inter-ExtraLight.otf'), + // 'Inter-ExtraLightItalic': require('../../assets/fonts/inter/Inter-ExtraLightItalic.otf'), + // 'Inter-Light': require('../../assets/fonts/inter/Inter-Light.otf'), + // 'Inter-LightItalic': require('../../assets/fonts/inter/Inter-LightItalic.otf'), + 'Inter-Regular': require('../../assets/fonts/inter/Inter-Regular.otf'), + 'Inter-Italic': require('../../assets/fonts/inter/Inter-Italic.otf'), + 'Inter-Medium': require('../../assets/fonts/inter/Inter-Medium.otf'), + 'Inter-MediumItalic': require('../../assets/fonts/inter/Inter-MediumItalic.otf'), + 'Inter-SemiBold': require('../../assets/fonts/inter/Inter-SemiBold.otf'), + 'Inter-SemiBoldItalic': require('../../assets/fonts/inter/Inter-SemiBoldItalic.otf'), + 'Inter-Bold': require('../../assets/fonts/inter/Inter-Bold.otf'), + 'Inter-BoldItalic': require('../../assets/fonts/inter/Inter-BoldItalic.otf'), + 'Inter-ExtraBold': require('../../assets/fonts/inter/Inter-ExtraBold.otf'), + 'Inter-ExtraBoldItalic': require('../../assets/fonts/inter/Inter-ExtraBoldItalic.otf'), + 'Inter-Black': require('../../assets/fonts/inter/Inter-Black.otf'), + 'Inter-BlackItalic': require('../../assets/fonts/inter/Inter-BlackItalic.otf'), + }) +} + /* * Unused fonts are commented out, but the files are there if we need them. */ @@ -74,10 +108,4 @@ export function applyFonts( style.fontFamily = style.fontFamily || FAMILIES } } - - /** - * Disable contextual ligatures - * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant} - */ - style.fontVariant = ['no-contextual'] } diff --git a/src/alf/themes.ts b/src/alf/themes.ts index 9f7ec5c673..f5d2247f9f 100644 --- a/src/alf/themes.ts +++ b/src/alf/themes.ts @@ -183,7 +183,7 @@ export function createThemes({ } as const const darkPalette: Palette = { - white: color.gray_25, + white: color.gray_0, black: color.trueBlack, contrast_25: color.gray_975, diff --git a/src/alf/tokens.ts b/src/alf/tokens.ts index 3f30702e85..d43d2b67dd 100644 --- a/src/alf/tokens.ts +++ b/src/alf/tokens.ts @@ -47,16 +47,11 @@ export const borderRadius = { full: 999, } as const -/** - * These correspond to Inter font files we actually load. - */ export const fontWeight = { - regular: '400', - // medium: '500', - semibold: '600', - // bold: '700', - extrabold: '800', - // black: '900', + normal: '400', + semibold: '500', + bold: '600', + heavy: '700', } as const export const gradients = { diff --git a/src/components/AppLanguageDropdown.tsx b/src/components/AppLanguageDropdown.tsx index 6170ab2e20..02cd0ce2d4 100644 --- a/src/components/AppLanguageDropdown.tsx +++ b/src/components/AppLanguageDropdown.tsx @@ -24,6 +24,8 @@ export function AppLanguageDropdown() { if (sanitizedLang !== value) { setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value)) } + setLangPrefs.setPrimaryLanguage(value) + setLangPrefs.setContentLanguage(value) // reset feeds to refetch content resetPostsFeedQueries(queryClient) diff --git a/src/components/AppLanguageDropdown.web.tsx b/src/components/AppLanguageDropdown.web.tsx index 00a7b53011..a106d99663 100644 --- a/src/components/AppLanguageDropdown.web.tsx +++ b/src/components/AppLanguageDropdown.web.tsx @@ -27,6 +27,8 @@ export function AppLanguageDropdown() { if (sanitizedLang !== value) { setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value)) } + setLangPrefs.setPrimaryLanguage(value) + setLangPrefs.setContentLanguage(value) // reset feeds to refetch content resetPostsFeedQueries(queryClient) diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 8728b88c2c..704aa9d987 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -14,7 +14,7 @@ import { } from 'react-native' import {LinearGradient} from 'expo-linear-gradient' -import {atoms as a, flatten, select, tokens, useTheme, web} from '#/alf' +import {android, atoms as a, flatten, select, tokens, useTheme} from '#/alf' import {Props as SVGIconProps} from '#/components/icons/common' import {Text} from '#/components/Typography' @@ -30,7 +30,7 @@ export type ButtonColor = | 'gradient_sunset' | 'gradient_nordic' | 'gradient_bonfire' -export type ButtonSize = 'tiny' | 'small' | 'large' +export type ButtonSize = 'tiny' | 'xsmall' | 'small' | 'medium' | 'large' export type ButtonShape = 'round' | 'square' | 'default' export type VariantProps = { /** @@ -343,46 +343,39 @@ export const Button = React.forwardRef( if (shape === 'default') { if (size === 'large') { - baseStyles.push({ - paddingVertical: 13, - paddingHorizontal: 20, - borderRadius: 8, - gap: 8, - }) + baseStyles.push( + {paddingVertical: 15}, + a.px_2xl, + a.rounded_sm, + a.gap_md, + ) + } else if (size === 'medium') { + baseStyles.push( + {paddingVertical: 12}, + a.px_2xl, + a.rounded_sm, + a.gap_md, + ) } else if (size === 'small') { - baseStyles.push({ - paddingVertical: 8, - paddingHorizontal: 12, - borderRadius: 6, - gap: 6, - }) + baseStyles.push({paddingVertical: 9}, a.px_lg, a.rounded_sm, a.gap_sm) + } else if (size === 'xsmall') { + baseStyles.push({paddingVertical: 6}, a.px_sm, a.rounded_sm, a.gap_sm) } else if (size === 'tiny') { - baseStyles.push({ - paddingVertical: 4, - paddingHorizontal: 8, - borderRadius: 4, - gap: 4, - }) + baseStyles.push({paddingVertical: 4}, a.px_sm, a.rounded_xs, a.gap_xs) } } else if (shape === 'round' || shape === 'square') { if (size === 'large') { if (shape === 'round') { - baseStyles.push({height: 46, width: 46}) + baseStyles.push({height: 54, width: 54}) } else { - baseStyles.push({height: 44, width: 44}) + baseStyles.push({height: 50, width: 50}) } } else if (size === 'small') { - if (shape === 'round') { - baseStyles.push({height: 36, width: 36}) - } else { - baseStyles.push({height: 34, width: 34}) - } + baseStyles.push({height: 34, width: 34}) + } else if (size === 'xsmall') { + baseStyles.push({height: 28, width: 28}) } else if (size === 'tiny') { - if (shape === 'round') { - baseStyles.push({height: 22, width: 22}) - } else { - baseStyles.push({height: 21, width: 21}) - } + baseStyles.push({height: 20, width: 20}) } if (shape === 'round') { @@ -626,11 +619,11 @@ export function useSharedButtonTextStyles() { } if (size === 'large') { - baseStyles.push(a.text_md, a.leading_tight, web({paddingTop: 1})) - } else if (size === 'small') { - baseStyles.push(a.text_sm, a.leading_tight, web({paddingTop: 1})) + baseStyles.push(a.text_md, android({paddingBottom: 1})) } else if (size === 'tiny') { - baseStyles.push(a.text_xs, a.leading_tight) + baseStyles.push(a.text_xs, android({paddingBottom: 1})) + } else { + baseStyles.push(a.text_sm, android({paddingBottom: 1})) } return StyleSheet.flatten(baseStyles) @@ -650,98 +643,31 @@ export function ButtonText({children, style, ...rest}: ButtonTextProps) { export function ButtonIcon({ icon: Comp, position, - size, + size: iconSize, }: { icon: React.ComponentType position?: 'left' | 'right' size?: SVGIconProps['size'] }) { - const {size: buttonSize, disabled} = useButtonContext() + const {size, disabled} = useButtonContext() const textStyles = useSharedButtonTextStyles() - const {iconSize, iconContainerSize} = React.useMemo(() => { - /** - * Pre-set icon sizes for different button sizes - */ - const iconSizeShorthand = - size ?? - (({ - large: 'sm', - small: 'xs', - tiny: 'xs', - }[buttonSize || 'small'] || 'sm') as Exclude< - SVGIconProps['size'], - undefined - >) - - /* - * Copied here from icons/common.tsx so we can tweak if we need to, but - * also so that we can calculate transforms. - */ - const iconSize = { - xs: 12, - sm: 16, - md: 20, - lg: 24, - xl: 28, - '2xl': 32, - }[iconSizeShorthand] - - /* - * Goal here is to match rendered text size so that different size icons - * don't increase button size - */ - const iconContainerSize = { - large: 18, - small: 16, - tiny: 13, - }[buttonSize || 'small'] - - return { - iconSize, - iconContainerSize, - } - }, [buttonSize, size]) return ( - - - + ) } diff --git a/src/components/FeedCard.tsx b/src/components/FeedCard.tsx index b28f66f839..e6d664cfda 100644 --- a/src/components/FeedCard.tsx +++ b/src/components/FeedCard.tsx @@ -11,17 +11,17 @@ import {msg, plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' -import {sanitizeHandle} from '#/lib/strings/handles' import {logger} from '#/logger' -import {precacheFeedFromGeneratorView} from '#/state/queries/feed' import { useAddSavedFeedsMutation, usePreferencesQuery, useRemoveFeedMutation, } from '#/state/queries/preferences' -import {useSession} from '#/state/session' -import * as Toast from '#/view/com/util/Toast' +import {sanitizeHandle} from 'lib/strings/handles' +import {precacheFeedFromGeneratorView} from 'state/queries/feed' +import {useSession} from 'state/session' import {UserAvatar} from '#/view/com/util/UserAvatar' +import * as Toast from 'view/com/util/Toast' import {useTheme} from '#/alf' import {atoms as a} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' @@ -121,10 +121,7 @@ export function TitleAndByline({ return ( - + {title} {creator && ( diff --git a/src/components/KnownFollowers.tsx b/src/components/KnownFollowers.tsx index 35a346c3a5..4017a7b0be 100644 --- a/src/components/KnownFollowers.tsx +++ b/src/components/KnownFollowers.tsx @@ -5,7 +5,7 @@ import {msg, Plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {makeProfileLink} from '#/lib/routes/links' -import {sanitizeDisplayName} from '#/lib/strings/display-names' +import {sanitizeDisplayName} from 'lib/strings/display-names' import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useTheme} from '#/alf' import {Link, LinkProps} from '#/components/Link' @@ -185,11 +185,11 @@ function KnownFollowersInner({ serverCount > 2 ? ( Followed by{' '} - + {slice[0].profile.displayName} ,{' '} - + {slice[1].profile.displayName} , and{' '} @@ -203,11 +203,11 @@ function KnownFollowersInner({ // only 2 Followed by{' '} - + {slice[0].profile.displayName} {' '} and{' '} - + {slice[1].profile.displayName} @@ -216,7 +216,7 @@ function KnownFollowersInner({ // 1-n followers, including blocks Followed by{' '} - + {slice[0].profile.displayName} {' '} and{' '} @@ -230,7 +230,7 @@ function KnownFollowersInner({ // only 1 Followed by{' '} - + {slice[0].profile.displayName} diff --git a/src/components/LabelingServiceCard/index.tsx b/src/components/LabelingServiceCard/index.tsx index 03b8ece6b1..542f2d2993 100644 --- a/src/components/LabelingServiceCard/index.tsx +++ b/src/components/LabelingServiceCard/index.tsx @@ -9,7 +9,6 @@ import {sanitizeHandle} from '#/lib/strings/handles' import {useLabelerInfoQuery} from '#/state/queries/labeler' import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useTheme, ViewStyleProp} from '#/alf' -import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag' import {Link as InternalLink, LinkProps} from '#/components/Link' import {RichText} from '#/components/RichText' import {Text} from '#/components/Typography' @@ -44,45 +43,21 @@ export function Avatar({avatar}: {avatar?: string}) { } export function Title({value}: {value: string}) { - return ( - - {value} - - ) + return {value} } export function Description({value, handle}: {value?: string; handle: string}) { - const {_} = useLingui() return value ? ( - + ) : ( - - {_(msg`By ${sanitizeHandle(handle, '@')}`)} + + By {sanitizeHandle(handle, '@')} ) } -export function RegionalNotice() { - const t = useTheme() - return ( - - - - Required in your region - - - ) -} - export function LikeCount({count}: {count: number}) { const t = useTheme() return ( @@ -91,7 +66,7 @@ export function LikeCount({count}: {count: number}) { a.mt_sm, a.text_sm, t.atoms.text_contrast_medium, - {fontWeight: '600'}, + {fontWeight: '500'}, ]}> @@ -110,7 +85,7 @@ export function Content({children}: React.PropsWithChildren<{}>) { a.align_center, a.justify_between, ]}> - {children} + {children} diff --git a/src/components/Link.tsx b/src/components/Link.tsx index c80b9f3707..6c25faffb8 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -9,7 +9,6 @@ import {sanitizeUrl} from '@braintree/sanitize-url' import {StackActions, useLinkProps} from '@react-navigation/native' import {BSKY_DOWNLOAD_URL} from '#/lib/constants' -import {useNavigationDeduped} from '#/lib/hooks/useNavigationDeduped' import {AllNavigatorParams} from '#/lib/routes/types' import {shareUrl} from '#/lib/sharing' import { @@ -18,10 +17,11 @@ import { isExternalUrl, linkRequiresWarning, } from '#/lib/strings/url-helpers' -import {isNative, isWeb} from '#/platform/detection' +import {isNative} from '#/platform/detection' import {shouldClickOpenNewTab} from '#/platform/urls' import {useModalControls} from '#/state/modals' import {useOpenLink} from '#/state/preferences/in-app-browser' +import {useNavigationDeduped} from 'lib/hooks/useNavigationDeduped' import {atoms as a, flatten, TextStyleProp, useTheme, web} from '#/alf' import {Button, ButtonProps} from '#/components/Button' import {useInteractionState} from '#/components/hooks/useInteractionState' @@ -244,10 +244,7 @@ export function Link({ export type InlineLinkProps = React.PropsWithChildren< BaseLinkProps & TextStyleProp & Pick > & - Pick & { - disableUnderline?: boolean - title?: TextProps['title'] - } + Pick export function InlineLinkText({ children, @@ -260,7 +257,6 @@ export function InlineLinkText({ selectable, label, shareOnLongPress, - disableUnderline, ...rest }: InlineLinkProps) { const t = useTheme() @@ -294,12 +290,11 @@ export function InlineLinkText({ {...rest} style={[ {color: t.palette.primary_500}, - (hovered || focused || pressed) && - !disableUnderline && { - ...web({outline: 0}), - textDecorationLine: 'underline', - textDecorationColor: flattenedStyle.color ?? t.palette.primary_500, - }, + (hovered || focused || pressed) && { + ...web({outline: 0}), + textDecorationLine: 'underline', + textDecorationColor: flattenedStyle.color ?? t.palette.primary_500, + }, flattenedStyle, ]} role="link" @@ -370,18 +365,3 @@ export function BaseLink({ ) } - -export function WebOnlyInlineLinkText({ - children, - to, - onPress, - ...props -}: InlineLinkProps) { - return isWeb ? ( - - {children} - - ) : ( - {children} - ) -} diff --git a/src/components/ListCard.tsx b/src/components/ListCard.tsx index ed5838fb04..829f36d471 100644 --- a/src/components/ListCard.tsx +++ b/src/components/ListCard.tsx @@ -7,14 +7,13 @@ import { moderateUserList, ModerationUI, } from '@atproto/api' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' +import {Trans} from '@lingui/macro' import {useQueryClient} from '@tanstack/react-query' -import {sanitizeHandle} from '#/lib/strings/handles' -import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {precacheList} from '#/state/queries/feed' -import {useSession} from '#/state/session' +import {sanitizeHandle} from 'lib/strings/handles' +import {useModerationOpts} from 'state/preferences/moderation-opts' +import {precacheList} from 'state/queries/feed' +import {useSession} from 'state/session' import {atoms as a, useTheme} from '#/alf' import { Avatar, @@ -112,7 +111,6 @@ export function TitleAndByline({ modUi?: ModerationUI }) { const t = useTheme() - const {_} = useLingui() const {currentAccount} = useSession() return ( @@ -132,7 +130,6 @@ export function TitleAndByline({ {title} @@ -142,12 +139,15 @@ export function TitleAndByline({ {creator && ( - {purpose === MODLIST - ? _(msg`Moderation list by ${sanitizeHandle(creator.handle, '@')}`) - : _(msg`List by ${sanitizeHandle(creator.handle, '@')}`)} + {purpose === MODLIST ? ( + + Moderation list by {sanitizeHandle(creator.handle, '@')} + + ) : ( + List by {sanitizeHandle(creator.handle, '@')} + )} )} diff --git a/src/components/MediaInsetBorder.tsx b/src/components/MediaInsetBorder.tsx index ed89880f40..ef8b00e2e0 100644 --- a/src/components/MediaInsetBorder.tsx +++ b/src/components/MediaInsetBorder.tsx @@ -24,7 +24,7 @@ export function MediaInsetBorder({ return ( {children} diff --git a/src/components/Pills.tsx b/src/components/Pills.tsx index 974d83593f..742a11667c 100644 --- a/src/components/Pills.tsx +++ b/src/components/Pills.tsx @@ -130,10 +130,9 @@ export function Label({ )} {name} {handle} diff --git a/src/components/ProfileHoverCard/index.web.tsx b/src/components/ProfileHoverCard/index.web.tsx index 4cda42fdbe..3890790dbe 100644 --- a/src/components/ProfileHoverCard/index.web.tsx +++ b/src/components/ProfileHoverCard/index.web.tsx @@ -5,15 +5,15 @@ import {flip, offset, shift, size, useFloating} from '@floating-ui/react-dom' import {msg, plural} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {isTouchDevice} from '#/lib/browser' import {getModerationCauseKey} from '#/lib/moderation' import {makeProfileLink} from '#/lib/routes/links' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' -import {useProfileShadow} from '#/state/cache/profile-shadow' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {usePrefetchProfileQuery, useProfileQuery} from '#/state/queries/profile' import {useSession} from '#/state/session' +import {isTouchDevice} from 'lib/browser' +import {useProfileShadow} from 'state/cache/profile-shadow' import {formatCount} from '#/view/com/util/numeric/format' import {UserAvatar} from '#/view/com/util/UserAvatar' import {ProfileHeaderHandle} from '#/screens/Profile/Header/Handle' @@ -411,7 +411,6 @@ function Inner({ () => currentAccount?.did === profile.did, [currentAccount, profile], ) - const isLabeler = profile.associated?.labeler return ( @@ -420,13 +419,11 @@ function Inner({ {!isMe && - !isLabeler && (isBlockedUser ? ( }) { diff --git a/src/components/ProgressGuide/Task.tsx b/src/components/ProgressGuide/Task.tsx index f2ceba52ac..a83715a425 100644 --- a/src/components/ProgressGuide/Task.tsx +++ b/src/components/ProgressGuide/Task.tsx @@ -35,7 +35,9 @@ export function ProgressGuideTask({ )} - {title} + + {title} + {subtitle && ( diff --git a/src/components/ProgressGuide/Toast.tsx b/src/components/ProgressGuide/Toast.tsx index 69e0082606..346312af51 100644 --- a/src/components/ProgressGuide/Toast.tsx +++ b/src/components/ProgressGuide/Toast.tsx @@ -154,7 +154,7 @@ export const ProgressGuideToast = React.forwardRef< ref={animatedCheckRef} /> - {title} + {title} {subtitle && ( {subtitle} diff --git a/src/components/Prompt.tsx b/src/components/Prompt.tsx index 8765cdee31..7836bbef95 100644 --- a/src/components/Prompt.tsx +++ b/src/components/Prompt.tsx @@ -120,7 +120,7 @@ export function Cancel({ diff --git a/src/screens/Login/ForgotPasswordForm.tsx b/src/screens/Login/ForgotPasswordForm.tsx index 7acaae5101..8588888b87 100644 --- a/src/screens/Login/ForgotPasswordForm.tsx +++ b/src/screens/Login/ForgotPasswordForm.tsx @@ -129,7 +129,7 @@ export const ForgotPasswordForm = ({ label={_(msg`Back`)} variant="solid" color="secondary" - size="large" + size="medium" onPress={onPressBack}> Back @@ -143,7 +143,7 @@ export const ForgotPasswordForm = ({ label={_(msg`Next`)} variant="solid" color={'primary'} - size="large" + size="medium" onPress={onPressNext}> Next @@ -170,7 +170,7 @@ export const ForgotPasswordForm = ({ onPress={onEmailSent} label={_(msg`Go to next`)} accessibilityHint={_(msg`Navigates to the next screen`)} - size="large" + size="medium" variant="ghost" color="secondary"> diff --git a/src/screens/Login/LoginForm.tsx b/src/screens/Login/LoginForm.tsx index 9c2237214b..9a01c04990 100644 --- a/src/screens/Login/LoginForm.tsx +++ b/src/screens/Login/LoginForm.tsx @@ -14,14 +14,14 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useAnalytics} from '#/lib/analytics/analytics' -import {useRequestNotificationsPermission} from '#/lib/notifications/notifications' import {isNetworkError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors' import {createFullHandle} from '#/lib/strings/handles' import {logger} from '#/logger' -import {useSetHasCheckedForStarterPack} from '#/state/preferences/used-starter-packs' import {useSessionApi} from '#/state/session' import {useLoggedOutViewControls} from '#/state/shell/logged-out' +import {useRequestNotificationsPermission} from 'lib/notifications/notifications' +import {useSetHasCheckedForStarterPack} from 'state/preferences/used-starter-packs' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {FormError} from '#/components/forms/FormError' @@ -285,7 +285,7 @@ export const LoginForm = ({ label={_(msg`Back`)} variant="solid" color="secondary" - size="large" + size="medium" onPress={onPressBack}> Back @@ -299,7 +299,7 @@ export const LoginForm = ({ accessibilityHint={_(msg`Retries login`)} variant="solid" color="secondary" - size="large" + size="medium" onPress={onPressRetryConnect}> Retry @@ -319,7 +319,7 @@ export const LoginForm = ({ accessibilityHint={_(msg`Navigates to the next screen`)} variant="solid" color="primary" - size="large" + size="medium" onPress={onPressNext}> Next diff --git a/src/screens/Login/PasswordUpdatedForm.tsx b/src/screens/Login/PasswordUpdatedForm.tsx index 03e7d86696..5407f3f1e3 100644 --- a/src/screens/Login/PasswordUpdatedForm.tsx +++ b/src/screens/Login/PasswordUpdatedForm.tsx @@ -39,7 +39,7 @@ export const PasswordUpdatedForm = ({ accessibilityHint={_(msg`Closes password update alert`)} variant="solid" color="primary" - size="large"> + size="medium"> Okay diff --git a/src/screens/Login/SetNewPasswordForm.tsx b/src/screens/Login/SetNewPasswordForm.tsx index a6658621cc..88f7ec5416 100644 --- a/src/screens/Login/SetNewPasswordForm.tsx +++ b/src/screens/Login/SetNewPasswordForm.tsx @@ -160,7 +160,7 @@ export const SetNewPasswordForm = ({ label={_(msg`Back`)} variant="solid" color="secondary" - size="large" + size="medium" onPress={onPressBack}> Back @@ -174,7 +174,7 @@ export const SetNewPasswordForm = ({ label={_(msg`Next`)} variant="solid" color="primary" - size="large" + size="medium" onPress={onPressNext}> Next diff --git a/src/screens/Messages/Conversation/ChatDisabled.tsx b/src/screens/Messages/Conversation/ChatDisabled.tsx index c768d2504b..23acc41cde 100644 --- a/src/screens/Messages/Conversation/ChatDisabled.tsx +++ b/src/screens/Messages/Conversation/ChatDisabled.tsx @@ -128,7 +128,7 @@ function DialogInner() { testID="backBtn" variant="solid" color="secondary" - size="large" + size="medium" onPress={onBack} label={_(msg`Back`)}> {_(msg`Back`)} @@ -137,7 +137,7 @@ function DialogInner() { testID="submitBtn" variant="solid" color="primary" - size="large" + size="medium" onPress={onSubmit} label={_(msg`Submit`)}> {_(msg`Submit`)} diff --git a/src/screens/Messages/Conversation/MessageInputEmbed.tsx b/src/screens/Messages/Conversation/MessageInputEmbed.tsx index 2d1551019e..bf28ed4fe9 100644 --- a/src/screens/Messages/Conversation/MessageInputEmbed.tsx +++ b/src/screens/Messages/Conversation/MessageInputEmbed.tsx @@ -174,6 +174,7 @@ export function MessageInputEmbed({ showAvatar author={post.author} moderation={moderation} + authorHasWarning={!!post.author.labels?.length} timestamp={post.indexedAt} postHref={itemHref} style={a.flex_0} diff --git a/src/screens/Messages/List/ChatListItem.tsx b/src/screens/Messages/List/ChatListItem.tsx index e9668b4e11..c45cc28d7a 100644 --- a/src/screens/Messages/List/ChatListItem.tsx +++ b/src/screens/Messages/List/ChatListItem.tsx @@ -10,10 +10,6 @@ import { import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useHaptics} from '#/lib/haptics' -import {decrementBadgeCount} from '#/lib/notifications/notifications' -import {logEvent} from '#/lib/statsig/statsig' -import {sanitizeDisplayName} from '#/lib/strings/display-names' import { postUriToRelativePath, toBskyAppUrl, @@ -23,6 +19,10 @@ import {isNative} from '#/platform/detection' import {useProfileShadow} from '#/state/cache/profile-shadow' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useSession} from '#/state/session' +import {useHaptics} from 'lib/haptics' +import {decrementBadgeCount} from 'lib/notifications/notifications' +import {logEvent} from 'lib/statsig/statsig' +import {sanitizeDisplayName} from 'lib/strings/display-names' import {TimeElapsed} from '#/view/com/util/TimeElapsed' import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' @@ -248,7 +248,6 @@ function ChatListItemReady({ numberOfLines={1} style={[{maxWidth: '85%'}, web([a.leading_normal])]}> refetch()}> diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx index 9bfe6c3fac..cd3179674c 100644 --- a/src/screens/Moderation/index.tsx +++ b/src/screens/Moderation/index.tsx @@ -7,7 +7,6 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useFocusEffect} from '@react-navigation/native' -import {useAnalytics} from '#/lib/analytics/analytics' import {getLabelingServiceTitle} from '#/lib/moderation' import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' import {logger} from '#/logger' @@ -23,8 +22,8 @@ import { useProfileUpdateMutation, } from '#/state/queries/profile' import {useSession} from '#/state/session' -import {isNonConfigurableModerationAuthority} from '#/state/session/additional-moderation-authorities' import {useSetMinimalShellMode} from '#/state/shell' +import {useAnalytics} from 'lib/analytics/analytics' import {ViewHeader} from '#/view/com/util/ViewHeader' import {CenteredView} from '#/view/com/util/Views' import {ScrollView} from '#/view/com/util/Views' @@ -339,7 +338,7 @@ export function ModerationScreenInner({ a.justify_between, disabledOnIOS && {opacity: 0.5}, ]}> - + Enable adult content - {isNonConfigurableModerationAuthority( - labeler.creator.did, - ) && } )} diff --git a/src/screens/Profile/Header/DisplayName.tsx b/src/screens/Profile/Header/DisplayName.tsx index e30162c3af..955e3d72c8 100644 --- a/src/screens/Profile/Header/DisplayName.tsx +++ b/src/screens/Profile/Header/DisplayName.tsx @@ -2,9 +2,9 @@ import React from 'react' import {View} from 'react-native' import {AppBskyActorDefs, ModerationDecision} from '@atproto/api' -import {sanitizeDisplayName} from '#/lib/strings/display-names' -import {sanitizeHandle} from '#/lib/strings/handles' import {Shadow} from '#/state/cache/types' +import {sanitizeDisplayName} from 'lib/strings/display-names' +import {sanitizeHandle} from 'lib/strings/handles' import {atoms as a, useTheme} from '#/alf' import {Text} from '#/components/Typography' @@ -19,9 +19,8 @@ export function ProfileHeaderDisplayName({ return ( + style={[t.atoms.text, a.text_4xl, a.self_start, {fontWeight: '500'}]}> {sanitizeDisplayName( profile.displayName || sanitizeHandle(profile.handle), moderation.ui('displayName'), diff --git a/src/screens/Profile/Header/Handle.tsx b/src/screens/Profile/Header/Handle.tsx index ba869b6626..0344f1a234 100644 --- a/src/screens/Profile/Header/Handle.tsx +++ b/src/screens/Profile/Header/Handle.tsx @@ -1,12 +1,11 @@ import React from 'react' import {View} from 'react-native' import {AppBskyActorDefs} from '@atproto/api' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' +import {Trans} from '@lingui/macro' -import {isInvalidHandle} from '#/lib/strings/handles' -import {isIOS} from '#/platform/detection' import {Shadow} from '#/state/cache/types' +import {isInvalidHandle} from 'lib/strings/handles' +import {isIOS} from 'platform/detection' import {atoms as a, useTheme, web} from '#/alf' import {NewskieDialog} from '#/components/NewskieDialog' import {Text} from '#/components/Typography' @@ -19,7 +18,6 @@ export function ProfileHeaderHandle({ disableTaps?: boolean }) { const t = useTheme() - const {_} = useLingui() const invalidHandle = isInvalidHandle(profile.handle) const blockHide = profile.viewer?.blocking || profile.viewer?.blockedBy return ( @@ -35,7 +33,6 @@ export function ProfileHeaderHandle({ ) : undefined} - {invalidHandle ? _(msg`⚠Invalid Handle`) : `@${profile.handle}`} + {invalidHandle ? ⚠Invalid Handle : `@${profile.handle}`} ) diff --git a/src/screens/Settings/AppearanceSettings.tsx b/src/screens/Settings/AppearanceSettings.tsx index 69e04f4af1..d675fb38ed 100644 --- a/src/screens/Settings/AppearanceSettings.tsx +++ b/src/screens/Settings/AppearanceSettings.tsx @@ -205,7 +205,7 @@ export function AppearanceToggleButtonGroup({ }) { const t = useTheme() return ( - + diff --git a/src/screens/Settings/components/DeactivateAccountDialog.tsx b/src/screens/Settings/components/DeactivateAccountDialog.tsx index 6958b7a478..2be42d13e6 100644 --- a/src/screens/Settings/components/DeactivateAccountDialog.tsx +++ b/src/screens/Settings/components/DeactivateAccountDialog.tsx @@ -102,7 +102,7 @@ function DeactivateAccountDialogInner({ diff --git a/src/screens/Signup/StepInfo/index.tsx b/src/screens/Signup/StepInfo/index.tsx index 2d4b07318d..e0a7912fd7 100644 --- a/src/screens/Signup/StepInfo/index.tsx +++ b/src/screens/Signup/StepInfo/index.tsx @@ -3,10 +3,8 @@ import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import * as EmailValidator from 'email-validator' -import type tldts from 'tldts' import {logEvent} from '#/lib/statsig/statsig' -import {isEmailMaybeInvalid} from '#/lib/strings/email' import {logger} from '#/logger' import {ScreenTransition} from '#/screens/Login/ScreenTransition' import {is13, is18, useSignupContext} from '#/screens/Signup/state' @@ -48,41 +46,13 @@ export function StepInfo({ const inviteCodeValueRef = useRef(state.inviteCode) const emailValueRef = useRef(state.email) - const prevEmailValueRef = useRef(state.email) const passwordValueRef = useRef(state.password) - const [hasWarnedEmail, setHasWarnedEmail] = React.useState(false) - - const tldtsRef = React.useRef() - React.useEffect(() => { - // @ts-expect-error - valid path - import('tldts/dist/index.cjs.min.js').then(tldts => { - tldtsRef.current = tldts - }) - }, []) - - const onNextPress = () => { + const onNextPress = React.useCallback(async () => { const inviteCode = inviteCodeValueRef.current const email = emailValueRef.current - const emailChanged = prevEmailValueRef.current !== email const password = passwordValueRef.current - if (emailChanged && tldtsRef.current) { - if (isEmailMaybeInvalid(email, tldtsRef.current)) { - prevEmailValueRef.current = email - setHasWarnedEmail(true) - return dispatch({ - type: 'setError', - value: _( - msg`It looks like you may have entered your email address incorrectly. Are you sure it's right?`, - ), - }) - } - } else if (hasWarnedEmail) { - setHasWarnedEmail(false) - } - prevEmailValueRef.current = email - if (!is13(state.dateOfBirth)) { return } @@ -119,7 +89,13 @@ export function StepInfo({ logEvent('signup:nextPressed', { activeStep: state.activeStep, }) - } + }, [ + _, + dispatch, + state.activeStep, + state.dateOfBirth, + state.serviceDescription?.inviteCodeRequired, + ]) return ( @@ -172,9 +148,6 @@ export function StepInfo({ testID="emailInput" onChangeText={value => { emailValueRef.current = value.trim() - if (hasWarnedEmail) { - setHasWarnedEmail(false) - } }} label={_(msg`Enter your email address`)} defaultValue={state.email} @@ -235,7 +208,6 @@ export function StepInfo({ onBackPress={onPressBack} onNextPress={onNextPress} onRetryPress={refetchServer} - overrideNextText={hasWarnedEmail ? _(msg`It's correct`) : undefined} /> ) diff --git a/src/screens/Signup/index.tsx b/src/screens/Signup/index.tsx index 3209800328..0e1a2e61fa 100644 --- a/src/screens/Signup/index.tsx +++ b/src/screens/Signup/index.tsx @@ -8,8 +8,8 @@ import {useLingui} from '@lingui/react' import {useAnalytics} from '#/lib/analytics/analytics' import {FEEDBACK_FORM_URL} from '#/lib/constants' import {useServiceQuery} from '#/state/queries/service' -import {useStarterPackQuery} from '#/state/queries/starter-packs' -import {useActiveStarterPack} from '#/state/shell/starter-pack' +import {useStarterPackQuery} from 'state/queries/starter-packs' +import {useActiveStarterPack} from 'state/shell/starter-pack' import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout' import { initialState, @@ -132,7 +132,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) { !gtMobile && {paddingBottom: 100}, ]}> - + Step {state.activeStep + 1} of{' '} {state.serviceDescription && diff --git a/src/screens/StarterPack/StarterPackLandingScreen.tsx b/src/screens/StarterPack/StarterPackLandingScreen.tsx index 68ff3aa7bc..5f1d5e0628 100644 --- a/src/screens/StarterPack/StarterPackLandingScreen.tsx +++ b/src/screens/StarterPack/StarterPackLandingScreen.tsx @@ -11,22 +11,22 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {isAndroidWeb} from '#/lib/browser' import {JOINED_THIS_WEEK} from '#/lib/constants' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {logEvent} from '#/lib/statsig/statsig' -import {createStarterPackGooglePlayUri} from '#/lib/strings/starter-pack' -import {isWeb} from '#/platform/detection' -import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {useStarterPackQuery} from '#/state/queries/starter-packs' +import {isAndroidWeb} from 'lib/browser' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {logEvent} from 'lib/statsig/statsig' +import {createStarterPackGooglePlayUri} from 'lib/strings/starter-pack' +import {isWeb} from 'platform/detection' +import {useModerationOpts} from 'state/preferences/moderation-opts' +import {useStarterPackQuery} from 'state/queries/starter-packs' import { useActiveStarterPack, useSetActiveStarterPack, -} from '#/state/shell/starter-pack' -import {LoggedOutScreenState} from '#/view/com/auth/LoggedOut' +} from 'state/shell/starter-pack' import {formatCount} from '#/view/com/util/numeric/format' -import {CenteredView} from '#/view/com/util/Views' -import {Logo} from '#/view/icons/Logo' +import {LoggedOutScreenState} from 'view/com/auth/LoggedOut' +import {CenteredView} from 'view/com/util/Views' +import {Logo} from 'view/icons/Logo' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonText} from '#/components/Button' import {useDialogControl} from '#/components/Dialog' @@ -188,7 +188,12 @@ function LandingScreenLoaded({ {record.name} + style={[ + a.text_center, + a.font_semibold, + a.text_md, + {color: 'white'}, + ]}> Starter pack by {`@${creator.handle}`} @@ -214,7 +219,11 @@ function LandingScreenLoaded({ color={t.atoms.text_contrast_medium.color} /> {formatCount(i18n, JOINED_THIS_WEEK)} joined this week @@ -299,7 +308,7 @@ function LandingScreenLoaded({ label={_(msg`Signup without a starter pack`)} variant="solid" color="secondary" - size="large" + size="medium" style={[a.py_lg]} onPress={onJoinWithoutPress}> diff --git a/src/screens/StarterPack/StarterPackScreen.tsx b/src/screens/StarterPack/StarterPackScreen.tsx index e3d32a1dd5..5b267ff272 100644 --- a/src/screens/StarterPack/StarterPackScreen.tsx +++ b/src/screens/StarterPack/StarterPackScreen.tsx @@ -15,35 +15,35 @@ import {useNavigation} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' import {useQueryClient} from '@tanstack/react-query' -import {batchedUpdates} from '#/lib/batchedUpdates' -import {HITSLOP_20} from '#/lib/constants' -import {isBlockedOrBlocking, isMuted} from '#/lib/moderation/blocked-and-muted' -import {makeProfileLink, makeStarterPackLink} from '#/lib/routes/links' -import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types' -import {logEvent} from '#/lib/statsig/statsig' import {cleanError} from '#/lib/strings/errors' -import {getStarterPackOgCard} from '#/lib/strings/starter-pack' import {logger} from '#/logger' -import {isWeb} from '#/platform/detection' -import {updateProfileShadow} from '#/state/cache/profile-shadow' -import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {getAllListMembers} from '#/state/queries/list-members' -import {useResolvedStarterPackShortLink} from '#/state/queries/resolve-short-link' -import {useResolveDidQuery} from '#/state/queries/resolve-uri' -import {useShortenLink} from '#/state/queries/shorten-link' import {useDeleteStarterPackMutation} from '#/state/queries/starter-packs' -import {useStarterPackQuery} from '#/state/queries/starter-packs' -import {useAgent, useSession} from '#/state/session' -import {useLoggedOutViewControls} from '#/state/shell/logged-out' import { ProgressGuideAction, useProgressGuideControls, } from '#/state/shell/progress-guide' -import {useSetActiveStarterPack} from '#/state/shell/starter-pack' -import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader' -import {ProfileSubpageHeader} from '#/view/com/profile/ProfileSubpageHeader' +import {batchedUpdates} from 'lib/batchedUpdates' +import {HITSLOP_20} from 'lib/constants' +import {isBlockedOrBlocking, isMuted} from 'lib/moderation/blocked-and-muted' +import {makeProfileLink, makeStarterPackLink} from 'lib/routes/links' +import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types' +import {logEvent} from 'lib/statsig/statsig' +import {getStarterPackOgCard} from 'lib/strings/starter-pack' +import {isWeb} from 'platform/detection' +import {updateProfileShadow} from 'state/cache/profile-shadow' +import {useModerationOpts} from 'state/preferences/moderation-opts' +import {getAllListMembers} from 'state/queries/list-members' +import {useResolvedStarterPackShortLink} from 'state/queries/resolve-short-link' +import {useResolveDidQuery} from 'state/queries/resolve-uri' +import {useShortenLink} from 'state/queries/shorten-link' +import {useStarterPackQuery} from 'state/queries/starter-packs' +import {useAgent, useSession} from 'state/session' +import {useLoggedOutViewControls} from 'state/shell/logged-out' +import {useSetActiveStarterPack} from 'state/shell/starter-pack' import * as Toast from '#/view/com/util/Toast' -import {CenteredView} from '#/view/com/util/Views' +import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' +import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader' +import {CenteredView} from 'view/com/util/Views' import {bulkWriteFollows} from '#/screens/Onboarding/util' import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' @@ -449,7 +449,7 @@ function Header({ }} variant="solid" color="primary" - size="large"> + size="medium"> Join Bluesky @@ -645,7 +645,7 @@ function OverflowMenu({ - - - ) -} - -const getInitialCrop = ( - source: ImageSource, - manips: ImageTransformation | undefined, -): PercentCrop | undefined => { - const initialArea = manips?.crop - - if (initialArea) { - return { - unit: '%', - x: (initialArea.originX / source.width) * 100, - y: (initialArea.originY / source.height) * 100, - width: (initialArea.width / source.width) * 100, - height: (initialArea.height / source.height) * 100, - } - } -} diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx index 369f08d745..7ff1b7b9ab 100644 --- a/src/view/com/composer/photos/Gallery.tsx +++ b/src/view/com/composer/photos/Gallery.tsx @@ -1,38 +1,29 @@ -import React from 'react' -import { - ImageStyle, - Keyboard, - LayoutChangeEvent, - StyleSheet, - TouchableOpacity, - View, - ViewStyle, -} from 'react-native' +import React, {useState} from 'react' +import {ImageStyle, Keyboard, LayoutChangeEvent} from 'react-native' +import {StyleSheet, TouchableOpacity, View} from 'react-native' import {Image} from 'expo-image' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {observer} from 'mobx-react-lite' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {Dimensions} from '#/lib/media/types' -import {colors, s} from '#/lib/styles' -import {isNative} from '#/platform/detection' -import {ComposerImage, cropImage} from '#/state/gallery' -import {Text} from '#/view/com/util/text/Text' +import {useModalControls} from '#/state/modals' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {Dimensions} from 'lib/media/types' +import {colors, s} from 'lib/styles' +import {isNative} from 'platform/detection' +import {GalleryModel} from 'state/models/media/gallery' +import {Text} from 'view/com/util/text/Text' import {useTheme} from '#/alf' -import * as Dialog from '#/components/Dialog' -import {EditImageDialog} from './EditImageDialog' -import {ImageAltTextDialog} from './ImageAltTextDialog' const IMAGE_GAP = 8 interface GalleryProps { - images: ComposerImage[] - onChange: (next: ComposerImage[]) => void + gallery: GalleryModel } -export let Gallery = (props: GalleryProps): React.ReactNode => { - const [containerInfo, setContainerInfo] = React.useState() +export const Gallery = (props: GalleryProps) => { + const [containerInfo, setContainerInfo] = useState() const onLayout = (evt: LayoutChangeEvent) => { const {width, height} = evt.nativeEvent.layout @@ -50,200 +41,177 @@ export let Gallery = (props: GalleryProps): React.ReactNode => { ) } -Gallery = React.memo(Gallery) interface GalleryInnerProps extends GalleryProps { containerInfo: Dimensions } -const GalleryInner = ({images, containerInfo, onChange}: GalleryInnerProps) => { +const GalleryInner = observer(function GalleryImpl({ + gallery, + containerInfo, +}: GalleryInnerProps) { + const {_} = useLingui() const {isMobile} = useWebMediaQueries() + const {openModal} = useModalControls() + const t = useTheme() - const {altTextControlStyle, imageControlsStyle, imageStyle} = - React.useMemo(() => { - const side = - images.length === 1 - ? 250 - : (containerInfo.width - IMAGE_GAP * (images.length - 1)) / - images.length + let side: number - const isOverflow = isMobile && images.length > 2 + if (gallery.size === 1) { + side = 250 + } else { + side = (containerInfo.width - IMAGE_GAP * (gallery.size - 1)) / gallery.size + } - return { - altTextControlStyle: isOverflow - ? {left: 4, bottom: 4} - : !isMobile && images.length < 3 - ? {left: 8, top: 8} - : {left: 4, top: 4}, - imageControlsStyle: { - display: 'flex' as const, - flexDirection: 'row' as const, - position: 'absolute' as const, - ...(isOverflow - ? {top: 4, right: 4, gap: 4} - : !isMobile && images.length < 3 - ? {top: 8, right: 8, gap: 8} - : {top: 4, right: 4, gap: 4}), - zIndex: 1, - }, - imageStyle: { - height: side, - width: side, - }, + const imageStyle = { + height: side, + width: side, + } + + const isOverflow = isMobile && gallery.size > 2 + + const altTextControlStyle = isOverflow + ? { + left: 4, + bottom: 4, + } + : !isMobile && gallery.size < 3 + ? { + left: 8, + top: 8, + } + : { + left: 4, + top: 4, } - }, [images.length, containerInfo, isMobile]) - return images.length !== 0 ? ( + const imageControlsStyle = { + display: 'flex' as const, + flexDirection: 'row' as const, + position: 'absolute' as const, + ...(isOverflow + ? { + top: 4, + right: 4, + gap: 4, + } + : !isMobile && gallery.size < 3 + ? { + top: 8, + right: 8, + gap: 8, + } + : { + top: 4, + right: 4, + gap: 4, + }), + zIndex: 1, + } + + return !gallery.isEmpty ? ( <> - {images.map((image, index) => { - return ( - { - onChange( - images.map(i => (i.source === image.source ? next : i)), - ) + {gallery.images.map(image => ( + + { + Keyboard.dismiss() + openModal({ + name: 'alt-text-image', + image, + }) }} - onRemove={() => { - const next = images.slice() - next.splice(index, 1) - - onChange(next) + style={[styles.altTextControl, altTextControlStyle]}> + {image.altText.length > 0 ? ( + + ) : ( + + )} + + ALT + + + + { + if (isNative) { + gallery.crop(image) + } else { + openModal({ + name: 'edit-image', + image, + gallery, + }) + } + }} + style={styles.imageControl}> + + + gallery.remove(image)} + style={styles.imageControl}> + + + + { + Keyboard.dismiss() + openModal({ + name: 'alt-text-image', + image, + }) }} + style={styles.altTextHiddenRegion} /> - ) - })} + + + + ))} ) : null -} - -type GalleryItemProps = { - image: ComposerImage - altTextControlStyle?: ViewStyle - imageControlsStyle?: ViewStyle - imageStyle?: ViewStyle - onChange: (next: ComposerImage) => void - onRemove: () => void -} - -const GalleryItem = ({ - image, - altTextControlStyle, - imageControlsStyle, - imageStyle, - onChange, - onRemove, -}: GalleryItemProps): React.ReactNode => { - const {_} = useLingui() - const t = useTheme() - - const altTextControl = Dialog.useDialogControl() - const editControl = Dialog.useDialogControl() - - const onImageEdit = () => { - if (isNative) { - cropImage(image).then(next => { - onChange(next) - }) - } else { - editControl.open() - } - } - - const onAltTextEdit = () => { - Keyboard.dismiss() - altTextControl.open() - } - - return ( - - - {image.alt.length !== 0 ? ( - - ) : ( - - )} - - ALT - - - - - - - - - - - - - - - - - - - ) -} +}) export function AltTextReminder() { const t = useTheme() @@ -295,7 +263,7 @@ const styles = StyleSheet.create({ altTextControlLabel: { color: 'white', fontSize: 12, - fontWeight: '600', + fontWeight: 'bold', letterSpacing: 1, }, altTextHiddenRegion: { diff --git a/src/view/com/composer/photos/ImageAltTextDialog.tsx b/src/view/com/composer/photos/ImageAltTextDialog.tsx deleted file mode 100644 index 123e1066a5..0000000000 --- a/src/view/com/composer/photos/ImageAltTextDialog.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import React from 'react' -import {ImageStyle, useWindowDimensions, View} from 'react-native' -import {Image} from 'expo-image' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {MAX_ALT_TEXT} from '#/lib/constants' -import {isWeb} from '#/platform/detection' -import {ComposerImage} from '#/state/gallery' -import {atoms as a, useTheme} from '#/alf' -import {Button, ButtonText} from '#/components/Button' -import * as Dialog from '#/components/Dialog' -import * as TextField from '#/components/forms/TextField' -import {Text} from '#/components/Typography' - -type Props = { - control: Dialog.DialogOuterProps['control'] - image: ComposerImage - onChange: (next: ComposerImage) => void -} - -export const ImageAltTextDialog = (props: Props): React.ReactNode => { - return ( - - - - - - ) -} - -const ImageAltTextInner = ({ - control, - image, - onChange, -}: Props): React.ReactNode => { - const {_} = useLingui() - const t = useTheme() - - const windim = useWindowDimensions() - - const [altText, setAltText] = React.useState(image.alt) - - const onPressSubmit = React.useCallback(() => { - control.close() - onChange({...image, alt: altText.trim()}) - }, [control, image, altText, onChange]) - - const imageStyle = React.useMemo(() => { - const maxWidth = isWeb ? 450 : windim.width - const source = image.transformed ?? image.source - - if (source.height > source.width) { - return { - resizeMode: 'contain', - width: '100%', - aspectRatio: 1, - borderRadius: 8, - } - } - return { - width: '100%', - height: (maxWidth / source.width) * source.height, - borderRadius: 8, - } - }, [image, windim]) - - return ( - - - - - - Add alt text - - - - - - - - - - - Descriptive alt text - - - setAltText(text)} - value={altText} - multiline - numberOfLines={3} - autoFocus - /> - - - - - - ) -} diff --git a/src/view/com/composer/photos/OpenCameraBtn.tsx b/src/view/com/composer/photos/OpenCameraBtn.tsx index 2183ca7902..f1f984103e 100644 --- a/src/view/com/composer/photos/OpenCameraBtn.tsx +++ b/src/view/com/composer/photos/OpenCameraBtn.tsx @@ -9,17 +9,17 @@ import {useCameraPermission} from '#/lib/hooks/usePermissions' import {openCamera} from '#/lib/media/picker' import {logger} from '#/logger' import {isMobileWeb, isNative} from '#/platform/detection' -import {ComposerImage, createComposerImage} from '#/state/gallery' +import {GalleryModel} from '#/state/models/media/gallery' import {atoms as a, useTheme} from '#/alf' import {Button} from '#/components/Button' import {Camera_Stroke2_Corner0_Rounded as Camera} from '#/components/icons/Camera' type Props = { + gallery: GalleryModel disabled?: boolean - onAdd: (next: ComposerImage[]) => void } -export function OpenCameraBtn({disabled, onAdd}: Props) { +export function OpenCameraBtn({gallery, disabled}: Props) { const {track} = useAnalytics() const {_} = useLingui() const {requestCameraAccessIfNeeded} = useCameraPermission() @@ -48,16 +48,13 @@ export function OpenCameraBtn({disabled, onAdd}: Props) { if (mediaPermissionRes) { await MediaLibrary.createAssetAsync(img.path) } - - const res = await createComposerImage(img) - - onAdd([res]) + gallery.add(img) } catch (err: any) { // ignore logger.warn('Error using camera', {error: err}) } }, [ - onAdd, + gallery, track, requestCameraAccessIfNeeded, mediaPermissionRes, diff --git a/src/view/com/composer/photos/SelectPhotoBtn.tsx b/src/view/com/composer/photos/SelectPhotoBtn.tsx index 95d2df022c..747653fc8d 100644 --- a/src/view/com/composer/photos/SelectPhotoBtn.tsx +++ b/src/view/com/composer/photos/SelectPhotoBtn.tsx @@ -5,20 +5,18 @@ import {useLingui} from '@lingui/react' import {useAnalytics} from '#/lib/analytics/analytics' import {usePhotoLibraryPermission} from '#/lib/hooks/usePermissions' -import {openPicker} from '#/lib/media/picker' import {isNative} from '#/platform/detection' -import {ComposerImage, createComposerImage} from '#/state/gallery' +import {GalleryModel} from '#/state/models/media/gallery' import {atoms as a, useTheme} from '#/alf' import {Button} from '#/components/Button' import {Image_Stroke2_Corner0_Rounded as Image} from '#/components/icons/Image' type Props = { - size: number + gallery: GalleryModel disabled?: boolean - onAdd: (next: ComposerImage[]) => void } -export function SelectPhotoBtn({size, disabled, onAdd}: Props) { +export function SelectPhotoBtn({gallery, disabled}: Props) { const {track} = useAnalytics() const {_} = useLingui() const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() @@ -31,17 +29,8 @@ export function SelectPhotoBtn({size, disabled, onAdd}: Props) { return } - const images = await openPicker({ - selectionLimit: 4 - size, - allowsMultipleSelection: true, - }) - - const results = await Promise.all( - images.map(img => createComposerImage(img)), - ) - - onAdd(results) - }, [track, requestPhotoAccessIfNeeded, size, onAdd]) + gallery.pick() + }, [track, requestPhotoAccessIfNeeded, gallery]) return ( ) } diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx index 2d1427347d..5bd7e0d179 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx +++ b/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx @@ -358,8 +358,9 @@ export function Controls({ style={[ a.flex_1, a.px_xs, - a.pb_sm, - a.gap_sm, + a.pt_2xs, + a.pb_md, + a.gap_md, a.flex_row, a.align_center, ]}> @@ -372,11 +373,7 @@ export function Controls({ onPress={onPressPlayPause} /> - + {formatTime(currentTime)} / {formatTime(duration)} {hasSubtitleTrack && ( diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index d4982b0e27..b4a6cf8251 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -20,10 +20,10 @@ import { ModerationDecision, } from '@atproto/api' -import {usePalette} from '#/lib/hooks/usePalette' import {ImagesLightbox, useLightboxControls} from '#/state/lightbox' import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {FeedSourceCard} from '#/view/com/feeds/FeedSourceCard' +import {usePalette} from 'lib/hooks/usePalette' +import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' import {atoms as a, useTheme} from '#/alf' import * as ListCard from '#/components/ListCard' import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard' @@ -138,7 +138,7 @@ export function PostEmbeds({ const image = images[0] return ( - + - + ) @@ -247,6 +247,9 @@ function MaybeListCard({view}: {view: AppBskyGraphDefs.ListView}) { } const styles = StyleSheet.create({ + container: { + marginTop: 8, + }, altContainer: { backgroundColor: 'rgba(0, 0, 0, 0.75)', borderRadius: 6, @@ -259,7 +262,7 @@ const styles = StyleSheet.create({ alt: { color: 'white', fontSize: 7, - fontWeight: '600', + fontWeight: 'bold', }, customFeedOuter: { borderWidth: StyleSheet.hairlineWidth, diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx index 3d885480cc..52a45b0e2e 100644 --- a/src/view/com/util/text/Text.tsx +++ b/src/view/com/util/text/Text.tsx @@ -2,40 +2,27 @@ import React from 'react' import {StyleSheet, Text as RNText, TextProps} from 'react-native' import {UITextView} from 'react-native-uitextview' -import {lh, s} from '#/lib/styles' -import {TypographyVariant, useTheme} from '#/lib/ThemeContext' -import {logger} from '#/logger' -import {isIOS} from '#/platform/detection' +import {lh, s} from 'lib/styles' +import {TypographyVariant, useTheme} from 'lib/ThemeContext' +import {isIOS, isWeb} from 'platform/detection' import {applyFonts, useAlf} from '#/alf' -import { - childHasEmoji, - childIsString, - renderChildrenWithEmoji, - StringChild, -} from '#/components/Typography' -import {IS_DEV} from '#/env' -export type CustomTextProps = Omit & { +export type CustomTextProps = TextProps & { type?: TypographyVariant lineHeight?: number title?: string dataSet?: Record selectable?: boolean -} & ( - | { - emoji: true - children: StringChild - } - | { - emoji?: false - children: TextProps['children'] - } - ) +} + +const fontFamilyStyle = { + fontFamily: + '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif', +} export function Text({ type = 'md', children, - emoji, lineHeight, style, title, @@ -48,18 +35,6 @@ export function Text({ const lineHeightStyle = lineHeight ? lh(theme, type, lineHeight) : undefined const {fonts} = useAlf() - if (IS_DEV) { - if (!emoji && childHasEmoji(children)) { - logger.warn( - `Text: emoji detected but emoji not enabled: "${children}"\n\nPlease add '`, - ) - } - - if (emoji && !childIsString(children)) { - logger.error('Text: when , children can only be strings.') - } - } - if (selectable && isIOS) { const flattened = StyleSheet.flatten([ s.black, @@ -83,7 +58,7 @@ export function Text({ selectable={selectable} uiTextView {...props}> - {isIOS && emoji ? renderChildrenWithEmoji(children) : children} + {children} ) } @@ -91,6 +66,7 @@ export function Text({ const flattened = StyleSheet.flatten([ s.black, typography, + isWeb && fontFamilyStyle, lineHeightStyle, style, ]) @@ -111,7 +87,7 @@ export function Text({ dataSet={Object.assign({tooltip: title}, dataSet || {})} selectable={selectable} {...props}> - {isIOS && emoji ? renderChildrenWithEmoji(children) : children} + {children} ) } diff --git a/src/view/com/util/text/ThemedText.tsx b/src/view/com/util/text/ThemedText.tsx new file mode 100644 index 0000000000..2844d273c2 --- /dev/null +++ b/src/view/com/util/text/ThemedText.tsx @@ -0,0 +1,80 @@ +import React from 'react' +import {CustomTextProps, Text} from './Text' +import {usePalette} from 'lib/hooks/usePalette' +import {addStyle} from 'lib/styles' + +export type ThemedTextProps = CustomTextProps & { + fg?: 'default' | 'light' | 'error' | 'inverted' | 'inverted-light' + bg?: 'default' | 'light' | 'error' | 'inverted' | 'inverted-light' + border?: 'default' | 'dark' | 'error' | 'inverted' | 'inverted-dark' + lineHeight?: number +} + +export function ThemedText({ + fg, + bg, + border, + style, + children, + ...props +}: React.PropsWithChildren) { + const pal = usePalette('default') + const palInverted = usePalette('inverted') + const palError = usePalette('error') + switch (fg) { + case 'default': + style = addStyle(style, pal.text) + break + case 'light': + style = addStyle(style, pal.textLight) + break + case 'error': + style = addStyle(style, {color: palError.colors.background}) + break + case 'inverted': + style = addStyle(style, palInverted.text) + break + case 'inverted-light': + style = addStyle(style, palInverted.textLight) + break + } + switch (bg) { + case 'default': + style = addStyle(style, pal.view) + break + case 'light': + style = addStyle(style, pal.viewLight) + break + case 'error': + style = addStyle(style, palError.view) + break + case 'inverted': + style = addStyle(style, palInverted.view) + break + case 'inverted-light': + style = addStyle(style, palInverted.viewLight) + break + } + switch (border) { + case 'default': + style = addStyle(style, pal.border) + break + case 'dark': + style = addStyle(style, pal.borderDark) + break + case 'error': + style = addStyle(style, palError.border) + break + case 'inverted': + style = addStyle(style, palInverted.border) + break + case 'inverted-dark': + style = addStyle(style, palInverted.borderDark) + break + } + return ( + + {children} + + ) +} diff --git a/src/view/screens/AccessibilitySettings.tsx b/src/view/screens/AccessibilitySettings.tsx index 158dc8b8da..2992e5c7e9 100644 --- a/src/view/screens/AccessibilitySettings.tsx +++ b/src/view/screens/AccessibilitySettings.tsx @@ -69,7 +69,7 @@ export function AccessibilitySettingsScreen({}: Props) { }, ]}> - + Accessibility Settings diff --git a/src/view/screens/LanguageSettings.tsx b/src/view/screens/LanguageSettings.tsx index bd69d7a550..0f27db5229 100644 --- a/src/view/screens/LanguageSettings.tsx +++ b/src/view/screens/LanguageSettings.tsx @@ -9,19 +9,19 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useFocusEffect} from '@react-navigation/native' -import {APP_LANGUAGES, LANGUAGES} from '#/lib/../locale/languages' -import {useAnalytics} from '#/lib/analytics/analytics' -import {usePalette} from '#/lib/hooks/usePalette' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' -import {s} from '#/lib/styles' import {sanitizeAppLanguageSetting} from '#/locale/helpers' import {useModalControls} from '#/state/modals' import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences' import {useSetMinimalShellMode} from '#/state/shell' -import {Button} from '#/view/com/util/forms/Button' -import {ViewHeader} from '#/view/com/util/ViewHeader' -import {CenteredView} from '#/view/com/util/Views' +import {APP_LANGUAGES, LANGUAGES} from 'lib/../locale/languages' +import {useAnalytics} from 'lib/analytics/analytics' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {s} from 'lib/styles' +import {Button} from 'view/com/util/forms/Button' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {CenteredView} from 'view/com/util/Views' import {Text} from '../com/util/text/Text' type Props = NativeStackScreenProps @@ -118,7 +118,7 @@ export function LanguageSettingsScreen(_props: Props) { color: pal.text.color, fontSize: 14, letterSpacing: 0.5, - fontWeight: '600', + fontWeight: '500', paddingHorizontal: 14, paddingVertical: 8, borderRadius: 24, @@ -128,7 +128,7 @@ export function LanguageSettingsScreen(_props: Props) { color: pal.text.color, fontSize: 14, letterSpacing: 0.5, - fontWeight: '600', + fontWeight: '500', paddingHorizontal: 14, paddingVertical: 8, borderRadius: 24, @@ -147,7 +147,7 @@ export function LanguageSettingsScreen(_props: Props) { fontSize: 14, fontFamily: 'inherit', letterSpacing: 0.5, - fontWeight: '600', + fontWeight: '500', paddingHorizontal: 14, paddingVertical: 8, borderRadius: 24, @@ -211,7 +211,7 @@ export function LanguageSettingsScreen(_props: Props) { color: pal.text.color, fontSize: 14, letterSpacing: 0.5, - fontWeight: '600', + fontWeight: '500', paddingHorizontal: 14, paddingVertical: 8, borderRadius: 24, @@ -221,7 +221,7 @@ export function LanguageSettingsScreen(_props: Props) { color: pal.text.color, fontSize: 14, letterSpacing: 0.5, - fontWeight: '600', + fontWeight: '500', paddingHorizontal: 14, paddingVertical: 8, borderRadius: 24, @@ -239,7 +239,7 @@ export function LanguageSettingsScreen(_props: Props) { fontSize: 14, fontFamily: 'inherit', letterSpacing: 0.5, - fontWeight: '600', + fontWeight: '500', paddingHorizontal: 14, paddingVertical: 8, borderRadius: 24, diff --git a/src/view/screens/Lists.tsx b/src/view/screens/Lists.tsx index d6a86e5143..9daeaba187 100644 --- a/src/view/screens/Lists.tsx +++ b/src/view/screens/Lists.tsx @@ -5,17 +5,17 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Trans} from '@lingui/macro' import {useFocusEffect, useNavigation} from '@react-navigation/native' -import {usePalette} from '#/lib/hooks/usePalette' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' -import {NavigationProp} from '#/lib/routes/types' -import {s} from '#/lib/styles' import {useModalControls} from '#/state/modals' import {useSetMinimalShellMode} from '#/state/shell' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {NavigationProp} from 'lib/routes/types' +import {s} from 'lib/styles' import {MyLists} from '#/view/com/lists/MyLists' -import {Button} from '#/view/com/util/forms/Button' -import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader' -import {Text} from '#/view/com/util/text/Text' +import {Button} from 'view/com/util/forms/Button' +import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader' +import {Text} from 'view/com/util/text/Text' type Props = NativeStackScreenProps export function ListsScreen({}: Props) { @@ -61,7 +61,7 @@ export function ListsScreen({}: Props) { }, ]}> - + User Lists diff --git a/src/view/screens/ModerationModlists.tsx b/src/view/screens/ModerationModlists.tsx index 39ba540b49..b7d993acc7 100644 --- a/src/view/screens/ModerationModlists.tsx +++ b/src/view/screens/ModerationModlists.tsx @@ -1,21 +1,20 @@ import React from 'react' import {View} from 'react-native' -import {AtUri} from '@atproto/api' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {Trans} from '@lingui/macro' import {useFocusEffect, useNavigation} from '@react-navigation/native' - -import {usePalette} from '#/lib/hooks/usePalette' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' -import {NavigationProp} from '#/lib/routes/types' -import {s} from '#/lib/styles' -import {useModalControls} from '#/state/modals' -import {useSetMinimalShellMode} from '#/state/shell' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {AtUri} from '@atproto/api' +import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {MyLists} from '#/view/com/lists/MyLists' -import {Button} from '#/view/com/util/forms/Button' -import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader' -import {Text} from '#/view/com/util/text/Text' +import {Text} from 'view/com/util/text/Text' +import {Button} from 'view/com/util/forms/Button' +import {NavigationProp} from 'lib/routes/types' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader' +import {s} from 'lib/styles' +import {useSetMinimalShellMode} from '#/state/shell' +import {useModalControls} from '#/state/modals' +import {Trans} from '@lingui/macro' type Props = NativeStackScreenProps export function ModerationModlistsScreen({}: Props) { @@ -55,7 +54,7 @@ export function ModerationModlistsScreen({}: Props) { !isMobile && [pal.border, {borderLeftWidth: 1, borderRightWidth: 1}] }> - + Moderation Lists diff --git a/src/view/screens/PreferencesExternalEmbeds.tsx b/src/view/screens/PreferencesExternalEmbeds.tsx index 8b3550d6b3..ade7a53d90 100644 --- a/src/view/screens/PreferencesExternalEmbeds.tsx +++ b/src/view/screens/PreferencesExternalEmbeds.tsx @@ -3,21 +3,21 @@ import {StyleSheet, View} from 'react-native' import {Trans} from '@lingui/macro' import {useFocusEffect} from '@react-navigation/native' -import {useAnalytics} from '#/lib/analytics/analytics' -import {usePalette} from '#/lib/hooks/usePalette' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' import { EmbedPlayerSource, externalEmbedLabels, } from '#/lib/strings/embed-player' -import {s} from '#/lib/styles' +import {useSetMinimalShellMode} from '#/state/shell' +import {useAnalytics} from 'lib/analytics/analytics' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {s} from 'lib/styles' import { useExternalEmbedsPrefs, useSetExternalEmbedPref, -} from '#/state/preferences' -import {useSetMinimalShellMode} from '#/state/shell' -import {ToggleButton} from '#/view/com/util/forms/ToggleButton' +} from 'state/preferences' +import {ToggleButton} from 'view/com/util/forms/ToggleButton' import {atoms as a} from '#/alf' import {SimpleViewHeader} from '../com/util/SimpleViewHeader' import {Text} from '../com/util/text/Text' @@ -50,7 +50,7 @@ export function PreferencesExternalEmbeds({}: Props) { showBackButton={isTabletOrMobile} style={[pal.border, a.border_b]}> - + External Media Preferences diff --git a/src/view/screens/PreferencesFollowingFeed.tsx b/src/view/screens/PreferencesFollowingFeed.tsx index 085250e3bd..8aa4221e6c 100644 --- a/src/view/screens/PreferencesFollowingFeed.tsx +++ b/src/view/screens/PreferencesFollowingFeed.tsx @@ -44,7 +44,7 @@ export function PreferencesFollowingFeed({}: Props) { showBackButton={isTabletOrMobile} style={[pal.border, a.border_b]}> - + Following Feed Preferences diff --git a/src/view/screens/PreferencesThreads.tsx b/src/view/screens/PreferencesThreads.tsx index 7a5a88869d..4a311f91ce 100644 --- a/src/view/screens/PreferencesThreads.tsx +++ b/src/view/screens/PreferencesThreads.tsx @@ -47,7 +47,7 @@ export function PreferencesThreads({}: Props) { showBackButton={isTabletOrMobile} style={[pal.border, a.border_b]}> - + Thread Preferences diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 810bbff889..5ef6459810 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -16,18 +16,9 @@ import { useQueryClient, } from '@tanstack/react-query' -import {useAnalytics} from '#/lib/analytics/analytics' -import {useSetTitle} from '#/lib/hooks/useSetTitle' -import {ComposeIcon2} from '#/lib/icons' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' -import {combinedDisplayName} from '#/lib/strings/display-names' import {cleanError} from '#/lib/strings/errors' -import {isInvalidHandle} from '#/lib/strings/handles' -import {colors, s} from '#/lib/styles' import {useProfileShadow} from '#/state/cache/profile-shadow' -import {listenSoftReset} from '#/state/events' import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs' import {useLabelerInfoQuery} from '#/state/queries/labeler' import {resetProfilePostsQueries} from '#/state/queries/post-feed' import {useProfileQuery} from '#/state/queries/profile' @@ -35,21 +26,29 @@ import {useResolveDidQuery} from '#/state/queries/resolve-uri' import {useAgent, useSession} from '#/state/session' import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' import {useComposerControls} from '#/state/shell/composer' -import {ProfileFeedgens} from '#/view/com/feeds/ProfileFeedgens' -import {ProfileLists} from '#/view/com/lists/ProfileLists' -import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader' -import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' -import {FAB} from '#/view/com/util/fab/FAB' -import {ListRef} from '#/view/com/util/List' -import {CenteredView} from '#/view/com/util/Views' +import {useAnalytics} from 'lib/analytics/analytics' +import {useSetTitle} from 'lib/hooks/useSetTitle' +import {ComposeIcon2} from 'lib/icons' +import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {combinedDisplayName} from 'lib/strings/display-names' +import {isInvalidHandle} from 'lib/strings/handles' +import {colors, s} from 'lib/styles' +import {listenSoftReset} from 'state/events' +import {useActorStarterPacksQuery} from 'state/queries/actor-starter-packs' +import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header' import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed' import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels' -import {web} from '#/alf' import {ScreenHider} from '#/components/moderation/ScreenHider' import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks' import {navigate} from '#/Navigation' import {ExpoScrollForwarderView} from '../../../modules/expo-scroll-forwarder' +import {ProfileFeedgens} from '../com/feeds/ProfileFeedgens' +import {ProfileLists} from '../com/lists/ProfileLists' +import {ErrorScreen} from '../com/util/error/ErrorScreen' +import {FAB} from '../com/util/fab/FAB' +import {ListRef} from '../com/util/List' +import {CenteredView} from '../com/util/Views' interface SectionRef { scrollToTop: () => void @@ -108,7 +107,7 @@ export function ProfileScreen({route}: Props) { // Most pushes will happen here, since we will have only placeholder data if (isLoadingDid || isLoadingProfile || starterPacksQuery.isLoading) { return ( - + ) diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index 07d762c0fe..30d16506e0 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -24,18 +24,11 @@ import {useFocusEffect, useNavigation} from '@react-navigation/native' import {useAnalytics} from '#/lib/analytics/analytics' import {createHitslop} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants' -import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {usePalette} from '#/lib/hooks/usePalette' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {MagnifyingGlassIcon} from '#/lib/icons' import {makeProfileLink} from '#/lib/routes/links' import {NavigationProp} from '#/lib/routes/types' -import { - NativeStackScreenProps, - SearchTabNavigatorParams, -} from '#/lib/routes/types' import {augmentSearchQuery} from '#/lib/strings/helpers' -import {useTheme} from '#/lib/ThemeContext' import {logger} from '#/logger' import {isNative, isWeb} from '#/platform/detection' import {listenSoftReset} from '#/state/events' @@ -47,6 +40,13 @@ import {useSearchPostsQuery} from '#/state/queries/search-posts' import {useSession} from '#/state/session' import {useSetDrawerOpen} from '#/state/shell' import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' +import {useNonReactiveCallback} from 'lib/hooks/useNonReactiveCallback' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import { + NativeStackScreenProps, + SearchTabNavigatorParams, +} from 'lib/routes/types' +import {useTheme} from 'lib/ThemeContext' import {Pager} from '#/view/com/pager/Pager' import {TabBar} from '#/view/com/pager/TabBar' import {Post} from '#/view/com/post/Post' @@ -414,7 +414,7 @@ let SearchScreenInner = ({query}: {query?: string}): React.ReactNode => { display: 'flex', paddingVertical: 12, paddingHorizontal: 18, - fontWeight: '600', + fontWeight: 'bold', borderBottomWidth: 1, }, ]}> @@ -959,7 +959,6 @@ function SearchHistory({ accessibilityIgnoresInvertColors /> {profile.displayName || profile.handle} @@ -1135,7 +1134,7 @@ const styles = StyleSheet.create({ borderRadius: 8, }, searchHistoryTitle: { - fontWeight: '600', + fontWeight: 'bold', paddingVertical: 12, paddingHorizontal: 10, }, diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx index 737ca2d28a..fe449fcdbc 100644 --- a/src/view/screens/Settings/index.tsx +++ b/src/view/screens/Settings/index.tsx @@ -18,18 +18,6 @@ import {useLingui} from '@lingui/react' import {useFocusEffect, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' -import {useAnalytics} from '#/lib/analytics/analytics' -import {appVersion, BUNDLE_DATE, bundleInfo} from '#/lib/app-info' -import {STATUS_PAGE_URL} from '#/lib/constants' -import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' -import {useCustomPalette} from '#/lib/hooks/useCustomPalette' -import {usePalette} from '#/lib/hooks/usePalette' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {HandIcon, HashtagIcon} from '#/lib/icons' -import {makeProfileLink} from '#/lib/routes/links' -import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' -import {NavigationProp} from '#/lib/routes/types' -import {colors, s} from '#/lib/styles' import {isNative} from '#/platform/detection' import {useModalControls} from '#/state/modals' import {clearStorage} from '#/state/persisted' @@ -45,14 +33,26 @@ import {SessionAccount, useSession, useSessionApi} from '#/state/session' import {useOnboardingDispatch, useSetMinimalShellMode} from '#/state/shell' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useCloseAllActiveElements} from '#/state/util' -import {AccountDropdownBtn} from '#/view/com/util/AccountDropdownBtn' -import {ToggleButton} from '#/view/com/util/forms/ToggleButton' -import {Link, TextLink} from '#/view/com/util/Link' -import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader' -import {Text} from '#/view/com/util/text/Text' -import * as Toast from '#/view/com/util/Toast' -import {UserAvatar} from '#/view/com/util/UserAvatar' -import {ScrollView} from '#/view/com/util/Views' +import {useAnalytics} from 'lib/analytics/analytics' +import {appVersion, BUNDLE_DATE, bundleInfo} from 'lib/app-info' +import {STATUS_PAGE_URL} from 'lib/constants' +import {useAccountSwitcher} from 'lib/hooks/useAccountSwitcher' +import {useCustomPalette} from 'lib/hooks/useCustomPalette' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {HandIcon, HashtagIcon} from 'lib/icons' +import {makeProfileLink} from 'lib/routes/links' +import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {NavigationProp} from 'lib/routes/types' +import {colors, s} from 'lib/styles' +import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn' +import {ToggleButton} from 'view/com/util/forms/ToggleButton' +import {Link, TextLink} from 'view/com/util/Link' +import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader' +import {Text} from 'view/com/util/text/Text' +import * as Toast from 'view/com/util/Toast' +import {UserAvatar} from 'view/com/util/UserAvatar' +import {ScrollView} from 'view/com/util/Views' import {DeactivateAccountDialog} from '#/screens/Settings/components/DeactivateAccountDialog' import {atoms as a, useTheme} from '#/alf' import {useDialogControl} from '#/components/Dialog' @@ -298,7 +298,7 @@ export function SettingsScreen({}: Props) { !isMobile && {borderLeftWidth: 1, borderRightWidth: 1}, ]}> - + Settings diff --git a/src/view/screens/Storybook/Buttons.tsx b/src/view/screens/Storybook/Buttons.tsx index 66040c2e3d..2935103dfb 100644 --- a/src/view/screens/Storybook/Buttons.tsx +++ b/src/view/screens/Storybook/Buttons.tsx @@ -9,6 +9,7 @@ import { ButtonText, ButtonVariant, } from '#/components/Button' +import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/Arrow' import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron' import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' import {H1} from '#/components/Typography' @@ -69,115 +70,81 @@ export function Buttons() { ), )} + {/* + + {['gradient_sunset', 'gradient_nordic', 'gradient_bonfire'].map( + name => ( + + + + + ), + )} + + */} - - - - - + + - - - - - - - - - - - - - - - - - - - - - + - - - - + @@ -92,17 +91,16 @@ function StorybookInner() { - + -