Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f6ba8fc02 |
+6
-21
@@ -1,23 +1,8 @@
|
||||
# The env the app is running in e.g. development, testflight, production
|
||||
EXPO_PUBLIC_ENV=development
|
||||
# This is the commit hash that the current bundle was made from. The user can
|
||||
# see the commit hash in the app's settings along with the other version info.
|
||||
# Useful for debugging/reporting.
|
||||
EXPO_PUBLIC_BUNDLE_IDENTIFIER=
|
||||
# This will always be in the format of YYMMDD, so that it always increases for
|
||||
# each build. This should only be used for Statsig reporting and shouldn't be
|
||||
# used to identify a specific bundle.
|
||||
EXPO_PUBLIC_BUNDLE_DATE=0
|
||||
# The log level for the app's logger transports
|
||||
EXPO_PUBLIC_LOG_LEVEL=info
|
||||
# Currently unused. Enable debug for a subset of the debug logs.
|
||||
EXPO_PUBLIC_LOG_DEBUG=
|
||||
# Copy this to `.env` and `.env.test` files
|
||||
|
||||
#
|
||||
# Bluesky specific
|
||||
#
|
||||
|
||||
# Bitdrift
|
||||
BITDRIFT_API_KEY=
|
||||
# Sentry
|
||||
SENTRY_AUTH_TOKEN=
|
||||
EXPO_PUBLIC_ENV=development
|
||||
EXPO_PUBLIC_LOG_LEVEL=debug
|
||||
EXPO_PUBLIC_LOG_DEBUG=
|
||||
EXPO_PUBLIC_BUNDLE_IDENTIFIER=
|
||||
EXPO_PUBLIC_BUNDLE_DATE=0
|
||||
|
||||
@@ -93,7 +93,6 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/ban-ts-comment': 'warn',
|
||||
},
|
||||
ignorePatterns: [
|
||||
'**/__mocks__/*.ts',
|
||||
|
||||
@@ -64,7 +64,6 @@ jobs:
|
||||
echo "${{ secrets.ENV_TOKEN }}" > .env
|
||||
echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse --short HEAD)" >> .env
|
||||
echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env
|
||||
echo "BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env
|
||||
echo "$json" > google-services.json
|
||||
|
||||
- name: 🏗️ EAS Build
|
||||
|
||||
@@ -76,7 +76,6 @@ jobs:
|
||||
echo "${{ secrets.ENV_TOKEN }}" > .env
|
||||
echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse --short HEAD)" >> .env
|
||||
echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env
|
||||
echo "BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env
|
||||
echo "${{ secrets.GOOGLE_SERVICES_TOKEN }}" > google-services.json
|
||||
|
||||
- name: 🏗️ EAS Build
|
||||
|
||||
@@ -69,9 +69,6 @@ jobs:
|
||||
- name: Lint check
|
||||
run: yarn lint
|
||||
|
||||
- name: Lint lockfile
|
||||
run: yarn lockfile-lint
|
||||
|
||||
- name: Prettier check
|
||||
run: yarn prettier --check .
|
||||
|
||||
@@ -104,7 +101,6 @@ jobs:
|
||||
echo "${{ secrets.ENV_TOKEN }}" > .env
|
||||
echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse --short HEAD)" >> .env
|
||||
echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env
|
||||
echo "BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env
|
||||
echo "$json" > google-services.json
|
||||
|
||||
- name: 🏗️ Create Bundle
|
||||
@@ -200,7 +196,6 @@ jobs:
|
||||
echo "${{ secrets.ENV_TOKEN }}" > .env
|
||||
echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse --short HEAD)" >> .env
|
||||
echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env
|
||||
echo "BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env
|
||||
echo "${{ secrets.GOOGLE_SERVICES_TOKEN }}" > google-services.json
|
||||
|
||||
- name: 🏗️ EAS Build
|
||||
@@ -227,7 +222,7 @@ jobs:
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}-build-android
|
||||
cancel-in-progress: false
|
||||
needs: [bundleDeploy]
|
||||
needs: [ bundleDeploy ]
|
||||
# Gotta check if its NOT '[]' because any md5 hash in the outputs is detected as a possible secret and won't be
|
||||
# available here
|
||||
if: ${{ inputs.channel != 'production' && needs.bundleDeploy.outputs.changes-detected }}
|
||||
@@ -278,7 +273,6 @@ jobs:
|
||||
echo "${{ secrets.ENV_TOKEN }}" > .env
|
||||
echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse --short HEAD)" >> .env
|
||||
echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env
|
||||
echo "BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env
|
||||
echo "$json" > google-services.json
|
||||
|
||||
- name: 🏗️ EAS Build
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: Set up Go tooling
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.23'
|
||||
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
|
||||
- name: Check
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Set up Go tooling
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.23'
|
||||
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
|
||||
- name: Lint
|
||||
|
||||
@@ -28,8 +28,6 @@ jobs:
|
||||
attempt_delay: 2000
|
||||
- name: Lint check
|
||||
run: yarn lint
|
||||
- name: Lint lockfile
|
||||
run: yarn lockfile-lint
|
||||
- name: Prettier check
|
||||
run: yarn prettier --check .
|
||||
- name: Check & compile i18n
|
||||
@@ -56,4 +54,4 @@ jobs:
|
||||
run: yarn intl:build
|
||||
- name: Run tests
|
||||
run: |
|
||||
NODE_ENV=test yarn test --forceExit
|
||||
NODE_ENV=test EXPO_PUBLIC_ENV=test yarn test --forceExit
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
name: Nightly Update Source Languages
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
extract-languages:
|
||||
name: Nightly Update Source Languages
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
# Give the default GITHUB_TOKEN write permission to commit and push the
|
||||
# added or changed files to the repository.
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ssh-key: ${{secrets.GH_ACTION_DEPLOY_KEY}}
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Yarn install
|
||||
uses: Wandalen/wretry.action@master
|
||||
with:
|
||||
command: yarn --frozen-lockfile
|
||||
attempt_limit: 3
|
||||
attempt_delay: 2000
|
||||
- name: Extract language strings
|
||||
run: yarn intl:extract
|
||||
- name: Create commit
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: Nightly source-language update
|
||||
file_pattern: ./src/locale/locales/en/messages.po
|
||||
@@ -1,46 +0,0 @@
|
||||
name: Lockfile
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
verify-yarn-lock:
|
||||
name: No manual yarn.lock edits
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out PR HEAD
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch base branch
|
||||
run: git fetch origin ${{ github.base_ref }} --depth=1
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
|
||||
- name: Reset yarn.lock to base
|
||||
run: git show "origin/${{ github.base_ref }}:yarn.lock" > yarn.lock
|
||||
|
||||
- name: Yarn install
|
||||
uses: Wandalen/wretry.action@master
|
||||
with:
|
||||
# Fine to skip scripts since we don't run any code
|
||||
command: yarn install --ignore-scripts
|
||||
attempt_limit: 3
|
||||
attempt_delay: 2000
|
||||
|
||||
- name: Verify yarn.lock
|
||||
run: |
|
||||
git diff --quiet --exit-code || {
|
||||
echo '::error::`yarn.lock` does not match what Yarn would generate given the base `yarn.lock` and the head `package.json`.'
|
||||
echo '::error:: - If this is intentional, you can ignore this check.'
|
||||
echo '::error:: - If this is unintentional, apply the following diff:'
|
||||
git --no-pager diff
|
||||
exit 1
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
2.7.6
|
||||
3.3.6
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.23-bullseye AS build-env
|
||||
FROM golang:1.22-bullseye AS build-env
|
||||
|
||||
WORKDIR /usr/src/social-app
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.23-bullseye AS build-env
|
||||
FROM golang:1.22-bullseye AS build-env
|
||||
|
||||
WORKDIR /usr/src/social-app
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright 2023–2025 Bluesky PBC
|
||||
Copyright 2023–2024 Bluesky PBC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ build-web-embed: ## Compile web embed bundle, copy to bskyweb/embedr* directorie
|
||||
|
||||
.PHONY: test
|
||||
test: ## Run all tests
|
||||
NODE_ENV=test yarn test
|
||||
NODE_ENV=test EXPO_PUBLIC_ENV=test yarn test
|
||||
|
||||
.PHONY: lint
|
||||
lint: ## Run style checks and verify syntax
|
||||
|
||||
@@ -10,7 +10,8 @@ appId: xyz.blueskyweb.app
|
||||
id: "e2eOpenLoggedOutView"
|
||||
- tapOn:
|
||||
id: "createAccountButton"
|
||||
- tapOn: "Bluesky Social"
|
||||
- tapOn:
|
||||
id: "selectServiceButton"
|
||||
- tapOn:
|
||||
id: "customSelectBtn"
|
||||
- tapOn:
|
||||
|
||||
@@ -26,7 +26,7 @@ appId: xyz.blueskyweb.app
|
||||
- tapOn: "Save"
|
||||
- assertNotVisible:
|
||||
id: "createOrEditListModal"
|
||||
- tapOn: "People"
|
||||
- tapOn: "About"
|
||||
- assertVisible: "Good Ppl"
|
||||
- assertVisible: "They good"
|
||||
|
||||
@@ -74,6 +74,7 @@ appId: xyz.blueskyweb.app
|
||||
- tapOn: "Delete List"
|
||||
- tapOn:
|
||||
id: "confirmBtn"
|
||||
- assertVisible: "This list is empty!"
|
||||
|
||||
- tapOn:
|
||||
label: "Create a new curatelist"
|
||||
@@ -92,12 +93,14 @@ appId: xyz.blueskyweb.app
|
||||
- tapOn: "Save"
|
||||
- assertNotVisible:
|
||||
id: "createOrEditListModal"
|
||||
- tapOn: "People"
|
||||
- tapOn: "About"
|
||||
- assertVisible: "Good Ppl"
|
||||
- assertVisible: "They good"
|
||||
- tapOn: "People"
|
||||
- tapOn: "About"
|
||||
|
||||
- tapOn: "Start adding people"
|
||||
- tapOn:
|
||||
label: "Adds users on curatelists from the list"
|
||||
id: "addUserBtn"
|
||||
- assertVisible:
|
||||
id: "listAddUserModal"
|
||||
- tapOn:
|
||||
@@ -142,7 +145,7 @@ appId: xyz.blueskyweb.app
|
||||
id: "e2eGotoLists"
|
||||
- tapOn: "Good Ppl"
|
||||
|
||||
- tapOn: "People"
|
||||
- tapOn: "About"
|
||||
- assertVisible:
|
||||
label: "Removes users on curatelists from the list"
|
||||
id: "user-bob.test"
|
||||
|
||||
@@ -11,8 +11,10 @@ appId: xyz.blueskyweb.app
|
||||
|
||||
# Pin alice's feed
|
||||
- extendedWaitUntil:
|
||||
visible: "Open drawer menu"
|
||||
- tapOn: "Open drawer menu"
|
||||
visible:
|
||||
id: "viewHeaderDrawerBtn"
|
||||
- tapOn:
|
||||
id: "viewHeaderDrawerBtn"
|
||||
- tapOn:
|
||||
id: "profileCardButton"
|
||||
- tapOn:
|
||||
@@ -30,7 +32,8 @@ appId: xyz.blueskyweb.app
|
||||
text: "alice-favs"
|
||||
|
||||
# Set alice-favs first
|
||||
- tapOn: "Open drawer menu"
|
||||
- tapOn:
|
||||
id: "viewHeaderDrawerBtn"
|
||||
- tapOn:
|
||||
id: "menuItemButton-Feeds"
|
||||
- tapOn:
|
||||
@@ -41,7 +44,8 @@ appId: xyz.blueskyweb.app
|
||||
- tapOn:
|
||||
label: "Save button"
|
||||
id: "saveChangesBtn"
|
||||
- tapOn: "Go back"
|
||||
- tapOn:
|
||||
id: "viewHeaderDrawerBtn"
|
||||
- assertVisible:
|
||||
id: "homeScreenFeedTabs-selector-0"
|
||||
text: "alice-favs"
|
||||
@@ -50,7 +54,8 @@ appId: xyz.blueskyweb.app
|
||||
text: "Following"
|
||||
|
||||
# Set following first
|
||||
- tapOn: "Open drawer menu"
|
||||
- tapOn:
|
||||
id: "viewHeaderDrawerBtn"
|
||||
- tapOn:
|
||||
id: "menuItemButton-Feeds"
|
||||
- tapOn:
|
||||
@@ -61,7 +66,8 @@ appId: xyz.blueskyweb.app
|
||||
- tapOn:
|
||||
label: "Save button"
|
||||
id: "saveChangesBtn"
|
||||
- tapOn: "Go back"
|
||||
- tapOn:
|
||||
id: "viewHeaderDrawerBtn"
|
||||
- assertVisible:
|
||||
id: "homeScreenFeedTabs-selector-0"
|
||||
text: "Following"
|
||||
@@ -70,7 +76,8 @@ appId: xyz.blueskyweb.app
|
||||
text: "alice-favs"
|
||||
|
||||
# Remove following
|
||||
- tapOn: "Open drawer menu"
|
||||
- tapOn:
|
||||
id: "viewHeaderDrawerBtn"
|
||||
- tapOn:
|
||||
id: "menuItemButton-Feeds"
|
||||
- tapOn:
|
||||
@@ -81,7 +88,8 @@ appId: xyz.blueskyweb.app
|
||||
- tapOn:
|
||||
label: "Save button"
|
||||
id: "saveChangesBtn"
|
||||
- tapOn: "Go back"
|
||||
- tapOn:
|
||||
id: "viewHeaderDrawerBtn"
|
||||
- assertVisible:
|
||||
id: "homeScreenFeedTabs-selector-0"
|
||||
text: "alice-favs"
|
||||
|
||||
@@ -21,15 +21,13 @@ appId: xyz.blueskyweb.app
|
||||
label: "Feeds button disappears after pinning a feed"
|
||||
id: "bottomBarProfileBtn"
|
||||
- tapOn:
|
||||
id: "profilePager-selector-5"
|
||||
id: "profilePager-selector-4"
|
||||
- tapOn: "alice-favs"
|
||||
- tapOn: "Pin to Home"
|
||||
- tapOn:
|
||||
id: "bottomBarHomeBtn"
|
||||
- tapOn:
|
||||
id: "bottomBarHomeBtn"
|
||||
- tapOn:
|
||||
id: "bottomBarHomeBtn"
|
||||
id: "viewHeaderDrawerBtn"
|
||||
- assertNotVisible: "Feeds ✨"
|
||||
|
||||
- tapOn:
|
||||
|
||||
@@ -14,7 +14,7 @@ appId: xyz.blueskyweb.app
|
||||
id: "e2eGotoModeration"
|
||||
- tapOn:
|
||||
id: "moderationlistsBtn"
|
||||
- tapOn: "New list"
|
||||
- tapOn: "New"
|
||||
- tapOn:
|
||||
id: "editNameInput"
|
||||
- inputText: "Muted Users"
|
||||
|
||||
@@ -6,8 +6,6 @@ appId: xyz.blueskyweb.app
|
||||
- waitForAnimationToEnd
|
||||
- tapOn: "http://localhost:8081"
|
||||
- waitForAnimationToEnd
|
||||
- extendedWaitUntil:
|
||||
visible: "Continue"
|
||||
- swipe:
|
||||
from: "Bluesky"
|
||||
direction: DOWN
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import {IsValidHandle, validateServiceHandle} from '#/lib/strings/handles'
|
||||
|
||||
describe('handle validation', () => {
|
||||
const valid = [
|
||||
['ali', 'bsky.social'],
|
||||
['alice', 'bsky.social'],
|
||||
['a-lice', 'bsky.social'],
|
||||
['a-----lice', 'bsky.social'],
|
||||
['123', 'bsky.social'],
|
||||
['123456789012345678', 'bsky.social'],
|
||||
['alice', 'custom-pds.com'],
|
||||
['alice', 'my-custom-pds-with-long-name.social'],
|
||||
['123456789012345678', 'my-custom-pds-with-long-name.social'],
|
||||
]
|
||||
it.each(valid)(`should be valid: %s.%s`, (handle, service) => {
|
||||
const result = validateServiceHandle(handle, service)
|
||||
expect(result.overall).toEqual(true)
|
||||
})
|
||||
|
||||
const invalid = [
|
||||
['al', 'bsky.social', 'frontLength'],
|
||||
['-alice', 'bsky.social', 'hyphenStartOrEnd'],
|
||||
['alice-', 'bsky.social', 'hyphenStartOrEnd'],
|
||||
['%%%', 'bsky.social', 'handleChars'],
|
||||
['1234567890123456789', 'bsky.social', 'frontLength'],
|
||||
[
|
||||
'1234567890123456789',
|
||||
'my-custom-pds-with-long-name.social',
|
||||
'frontLength',
|
||||
],
|
||||
['al', 'my-custom-pds-with-long-name.social', 'frontLength'],
|
||||
['a'.repeat(300), 'toolong.com', 'totalLength'],
|
||||
] satisfies [string, string, keyof IsValidHandle][]
|
||||
it.each(invalid)(
|
||||
`should be invalid: %s.%s due to %s`,
|
||||
(handle, service, expectedError) => {
|
||||
const result = validateServiceHandle(handle, service)
|
||||
expect(result.overall).toEqual(false)
|
||||
expect(result[expectedError]).toEqual(false)
|
||||
},
|
||||
)
|
||||
})
|
||||
+5
-31
@@ -15,9 +15,9 @@ module.exports = function (config) {
|
||||
*/
|
||||
const PLATFORM = process.env.EAS_BUILD_PLATFORM
|
||||
|
||||
const IS_DEV = process.env.EXPO_PUBLIC_ENV === 'development'
|
||||
const IS_TESTFLIGHT = process.env.EXPO_PUBLIC_ENV === 'testflight'
|
||||
const IS_PRODUCTION = process.env.EXPO_PUBLIC_ENV === 'production'
|
||||
const IS_DEV = !IS_TESTFLIGHT || !IS_PRODUCTION
|
||||
|
||||
const ASSOCIATED_DOMAINS = [
|
||||
'applinks:bsky.app',
|
||||
@@ -50,6 +50,7 @@ module.exports = function (config) {
|
||||
runtimeVersion: {
|
||||
policy: 'appVersion',
|
||||
},
|
||||
orientation: 'portrait',
|
||||
icon: './assets/app-icons/ios_icon_default_light.png',
|
||||
userInterfaceStyle: 'automatic',
|
||||
primaryColor: '#1083fe',
|
||||
@@ -75,35 +76,27 @@ module.exports = function (config) {
|
||||
'an',
|
||||
'ast',
|
||||
'ca',
|
||||
'da',
|
||||
'de',
|
||||
'el',
|
||||
'es',
|
||||
'eu',
|
||||
'fi',
|
||||
'fr',
|
||||
'ga',
|
||||
'gl',
|
||||
'hi',
|
||||
'hu',
|
||||
'ia',
|
||||
'id',
|
||||
'it',
|
||||
'ja',
|
||||
'km',
|
||||
'ko',
|
||||
'ne',
|
||||
'nl',
|
||||
'pl',
|
||||
'pt-BR',
|
||||
'ro',
|
||||
'ru',
|
||||
'sv',
|
||||
'th',
|
||||
'tr',
|
||||
'uk',
|
||||
'vi',
|
||||
'yue',
|
||||
'yue-Hant',
|
||||
'zh-Hans',
|
||||
'zh-Hant',
|
||||
],
|
||||
@@ -195,7 +188,6 @@ module.exports = function (config) {
|
||||
channel: UPDATES_CHANNEL,
|
||||
},
|
||||
plugins: [
|
||||
'expo-video',
|
||||
'expo-localization',
|
||||
USE_SENTRY && [
|
||||
'@sentry/react-native/expo',
|
||||
@@ -230,18 +222,11 @@ module.exports = function (config) {
|
||||
},
|
||||
],
|
||||
'react-native-compressor',
|
||||
[
|
||||
'@bitdrift/react-native',
|
||||
{
|
||||
networkInstrumentation: true,
|
||||
},
|
||||
],
|
||||
'./plugins/starterPackAppClipExtension/withStarterPackAppClip.js',
|
||||
'./plugins/withAndroidManifestPlugin.js',
|
||||
'./plugins/withAndroidManifestFCMIconPlugin.js',
|
||||
'./plugins/withAndroidStylesAccentColorPlugin.js',
|
||||
'./plugins/withAndroidSplashScreenStatusBarTranslucentPlugin.js',
|
||||
'./plugins/withAndroidNoJitpackPlugin.js',
|
||||
'./plugins/shareExtension/withShareExtensions.js',
|
||||
'./plugins/notificationsExtension/withNotificationsExtension.js',
|
||||
'./plugins/withAppDelegateReferrer.js',
|
||||
@@ -249,8 +234,8 @@ module.exports = function (config) {
|
||||
'expo-font',
|
||||
{
|
||||
fonts: [
|
||||
'./assets/fonts/inter/InterVariable.woff2',
|
||||
'./assets/fonts/inter/InterVariable-Italic.woff2',
|
||||
'./assets/fonts/inter/InterVariable.ttf',
|
||||
'./assets/fonts/inter/InterVariable-Italic.ttf',
|
||||
// Android only
|
||||
'./assets/fonts/inter/Inter-Regular.otf',
|
||||
'./assets/fonts/inter/Inter-Italic.otf',
|
||||
@@ -355,17 +340,6 @@ module.exports = function (config) {
|
||||
},
|
||||
},
|
||||
],
|
||||
['expo-screen-orientation', {initialOrientation: 'PORTRAIT_UP'}],
|
||||
[
|
||||
'react-native-vision-camera',
|
||||
{
|
||||
enableLocation: false,
|
||||
cameraPermissionText: 'Bluesky needs access to your camera.',
|
||||
enableMicrophonePermission: true,
|
||||
microphonePermissionText:
|
||||
'Bluesky needs access to your microphone.',
|
||||
},
|
||||
],
|
||||
].filter(Boolean),
|
||||
extra: {
|
||||
eas: {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M7 2a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0V3a1 1 0 0 1 1-1Zm4 0a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V3a1 1 0 0 1 1-1Zm4 0a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V3a1 1 0 0 1 1-1ZM4 9a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2h.5a3.5 3.5 0 1 1 0 7H18v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V9Zm12 0H6v11h10V9Zm2 5h.5a1.5 1.5 0 0 0 0-3H18v3Z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 437 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M6.5 4.999Zm0 0a.1.1 0 0 1-.016.024.2.2 0 0 1-.152.043q.086.014.168.032V7.5a1 1 0 0 1-.33.742c-.543.49-.917 1.176-1.23 2.036a1 1 0 0 1-.94.657H3V14h1a1 1 0 0 1 .866.5c.436.754.879 1.195 1.637 1.636A1 1 0 0 1 7 17v2h2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2h2v-2.537a1 1 0 0 1 .332-.744A4.98 4.98 0 0 0 19 11.999 5 5 0 0 0 14 7H9.474a1 1 0 0 1-.893-.55v-.001l-.001-.002-.005-.008L9.474 6l-.9.438h.001v.002l.002.001.001.003.002.003v.003a2.6 2.6 0 0 0-.443-.538c-.319-.298-.839-.648-1.637-.814V5Zm2.082 1.452ZM9.5 4.447c.211.196.379.387.508.553H14a7 7 0 0 1 6.72 8.96 1.003 1.003 0 0 0 1.146-1.46 1 1 0 1 1 1.731-1 3 3 0 0 1-3.714 4.286A7 7 0 0 1 19 16.888V19a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2h-2a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-1.45A5.8 5.8 0 0 1 3.448 16H3a2 2 0 0 1-2-2v-3.065a2 2 0 0 1 2-2h.324c.286-.649.657-1.291 1.176-1.852V5c0-1.047.89-2.12 2.161-1.907 1.33.223 2.248.805 2.839 1.354ZM8.25 12a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5Z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" d="M7.5 2a1 1 0 0 0-1 1v3.997a6.25 6.25 0 0 1-1.83 4.42l-.377.376A1 1 0 0 0 4 12.5V15a1 1 0 0 0 1 1h6v5a1 1 0 1 0 2 0v-5h6a1 1 0 0 0 1-1v-2.5a1 1 0 0 0-.293-.707l-.376-.377a6.25 6.25 0 0 1-1.831-4.42V3.001a1 1 0 0 0-1-1h-9Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 323 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M12 4c0-1.012.894-2.187 2.237-1.846a5.002 5.002 0 0 1 3.218 7.119 4 4 0 0 1 2.345 4.976A3.501 3.501 0 0 1 18.5 21h-13a3.5 3.5 0 0 1-1.3-6.75 4 4 0 0 1 2.052-4.848A4 4 0 0 1 10 4h2.001Zm-4.187 7.009A2 2 0 0 0 6 13c0 .366.098.706.271 1H12a1 1 0 1 1 0 2H5.5a1.5 1.5 0 0 0 0 3h13a1.5 1.5 0 1 0 0-3H17a1 1 0 1 1 0-2h.729A1.96 1.96 0 0 0 18 13a2 2 0 0 0-2-2h-2a1 1 0 1 1 0-2h1.236a3.002 3.002 0 0 0-1.243-4.832A2 2 0 0 1 12 6h-2a2 2 0 0 0-1.728 3.009H9a1 1 0 0 1 0 2H7.813Z" clip-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 610 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M3 4a1 1 0 0 0-1 1v4.17a1 1 0 0 0 .667.944 2.001 2.001 0 0 1 0 3.772A1 1 0 0 0 2 14.83V19a1 1 0 0 0 1 1h18a1 1 0 0 0 1-1v-4.17a1 1 0 0 0-.667-.944 2.001 2.001 0 0 1 0-3.772A1 1 0 0 0 22 9.17V5a1 1 0 0 0-1-1H3Zm1 4.535V6h16v2.535A4 4 0 0 0 18 12c0 1.482.805 2.773 2 3.465V18H4v-2.535A4 4 0 0 0 6 12a4 4 0 0 0-2-3.465ZM15 15a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm-1-3a1 1 0 1 1 2 0 1 1 0 0 1-2 0Zm1-5a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z" clip-rule="evenodd"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" stroke="#000" stroke-linejoin="round" d="M4 5.5a.5.5 0 0 0-.5.5v2.535a.5.5 0 0 0 .25.433A3.5 3.5 0 0 1 5.5 12a3.5 3.5 0 0 1-1.75 3.032.5.5 0 0 0-.25.433V18a.5.5 0 0 0 .5.5h16a.5.5 0 0 0 .5-.5v-2.535a.5.5 0 0 0-.25-.433A3.5 3.5 0 0 1 18.5 12a3.5 3.5 0 0 1 1.75-3.032.5.5 0 0 0 .25-.433V6a.5.5 0 0 0-.5-.5H4ZM2.5 6A1.5 1.5 0 0 1 4 4.5h16A1.5 1.5 0 0 1 21.5 6v3.17a.5.5 0 0 1-.333.472 2.501 2.501 0 0 0 0 4.716.5.5 0 0 1 .333.471V18a1.5 1.5 0 0 1-1.5 1.5H4A1.5 1.5 0 0 1 2.5 18v-3.17a.5.5 0 0 1 .333-.472 2.501 2.501 0 0 0 0-4.716.5.5 0 0 1-.333-.471V6Zm12 2a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 561 B After Width: | Height: | Size: 756 B |
@@ -4,7 +4,6 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Bluesky Embed</title>
|
||||
<link rel="preconnect" href="https://public.api.bsky.app">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png">
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Bluesky Embed</title>
|
||||
<link rel="preconnect" href="https://public.api.bsky.app">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png">
|
||||
|
||||
@@ -20,7 +20,6 @@ window.addEventListener('message', event => {
|
||||
return
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const id = (event.data as {id: string}).id
|
||||
if (!id) {
|
||||
return
|
||||
@@ -34,7 +33,6 @@ window.addEventListener('message', event => {
|
||||
return
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const height = (event.data as {height: number}).height
|
||||
if (height) {
|
||||
embed.style.height = `${height}px`
|
||||
@@ -49,7 +47,7 @@ window.addEventListener('message', event => {
|
||||
* @returns
|
||||
*/
|
||||
function scan(node = document) {
|
||||
const embeds = node.querySelectorAll<HTMLIFrameElement>('[data-bluesky-uri]')
|
||||
const embeds = node.querySelectorAll('[data-bluesky-uri]')
|
||||
|
||||
for (let i = 0; i < embeds.length; i++) {
|
||||
const id = String(Math.random()).slice(2)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
export function applyTheme(theme: 'light' | 'dark') {
|
||||
document.documentElement.classList.remove('light', 'dark')
|
||||
document.documentElement.classList.add(theme)
|
||||
}
|
||||
|
||||
export function initColorMode() {
|
||||
applyTheme(
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'dark'
|
||||
: 'light',
|
||||
)
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.addEventListener('change', mql => {
|
||||
applyTheme(mql.matches ? 'dark' : 'light')
|
||||
})
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export function Container({
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className="w-full bg-white text-black hover:bg-neutral-50 dark:bg-dimmedBg dark:hover:bg-dimmedBgLighten relative transition-colors max-w-[600px] min-w-[300px] flex border dark:border-slate-600 dark:text-slate-200 rounded-xl"
|
||||
className="w-full bg-white hover:bg-neutral-50 relative transition-colors max-w-[600px] min-w-[300px] flex border rounded-xl"
|
||||
onClick={() => {
|
||||
if (ref.current && href) {
|
||||
// forwardRef requires preact/compat - let's keep it simple
|
||||
|
||||
@@ -78,9 +78,9 @@ export function Embed({
|
||||
return (
|
||||
<Link
|
||||
href={`/profile/${record.author.did}/post/${getRkey(record)}`}
|
||||
className="transition-colors hover:bg-neutral-100 dark:hover:bg-slate-700 border dark:border-slate-600 rounded-lg p-2 gap-1.5 w-full flex flex-col">
|
||||
className="transition-colors hover:bg-neutral-100 border rounded-lg p-2 gap-1.5 w-full flex flex-col">
|
||||
<div className="flex gap-1.5 items-center">
|
||||
<div className="w-4 h-4 overflow-hidden rounded-full bg-neutral-300 dark:bg-slate-700 shrink-0">
|
||||
<div className="w-4 h-4 overflow-hidden rounded-full bg-neutral-300 shrink-0">
|
||||
<img
|
||||
src={record.author.avatar}
|
||||
style={isAuthorLabeled ? {filter: 'blur(1.5px)'} : undefined}
|
||||
@@ -88,7 +88,7 @@ export function Embed({
|
||||
</div>
|
||||
<p className="line-clamp-1 text-sm">
|
||||
<span className="font-bold">{record.author.displayName}</span>
|
||||
<span className="text-textLight dark:text-textDimmed ml-1">
|
||||
<span className="text-textLight ml-1">
|
||||
@{record.author.handle}
|
||||
</span>
|
||||
</p>
|
||||
@@ -209,7 +209,7 @@ function Info({children}: {children: ComponentChildren}) {
|
||||
return (
|
||||
<div className="w-full rounded-lg border py-2 px-2.5 flex-row flex gap-2 bg-neutral-50">
|
||||
<img src={infoIcon} className="w-4 h-4 shrink-0 mt-0.5" />
|
||||
<p className="text-sm text-textLight dark:text-textDimmed">{children}</p>
|
||||
<p className="text-sm text-textLight">{children}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -308,7 +308,7 @@ function ExternalEmbed({
|
||||
return (
|
||||
<Link
|
||||
href={content.external.uri}
|
||||
className="w-full rounded-lg overflow-hidden border dark:border-slate-600 flex flex-col items-stretch"
|
||||
className="w-full rounded-lg overflow-hidden border flex flex-col items-stretch"
|
||||
disableTracking>
|
||||
{content.external.thumb && (
|
||||
<img
|
||||
@@ -317,11 +317,11 @@ function ExternalEmbed({
|
||||
/>
|
||||
)}
|
||||
<div className="py-3 px-4">
|
||||
<p className="text-sm text-textLight dark:text-textDimmed line-clamp-1">
|
||||
<p className="text-sm text-textLight line-clamp-1">
|
||||
{toNiceDomain(content.external.uri)}
|
||||
</p>
|
||||
<p className="font-semibold line-clamp-3">{content.external.title}</p>
|
||||
<p className="text-sm text-textLight dark:text-textDimmed line-clamp-2 mt-0.5">
|
||||
<p className="text-sm text-textLight line-clamp-2 mt-0.5">
|
||||
{content.external.description}
|
||||
</p>
|
||||
</div>
|
||||
@@ -345,29 +345,23 @@ function GenericWithImageEmbed({
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className="w-full rounded-lg border dark:border-slate-600 py-2 px-3 flex flex-col gap-2">
|
||||
className="w-full rounded-lg border py-2 px-3 flex flex-col gap-2">
|
||||
<div className="flex gap-2.5 items-center">
|
||||
{image ? (
|
||||
<img
|
||||
src={image}
|
||||
alt={title}
|
||||
className="w-8 h-8 rounded-md bg-neutral-300 dark:bg-slate-700 shrink-0"
|
||||
className="w-8 h-8 rounded-md bg-neutral-300 shrink-0"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-8 h-8 rounded-md bg-brand shrink-0" />
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<p className="font-bold text-sm">{title}</p>
|
||||
<p className="text-textLight dark:text-textDimmed text-sm">
|
||||
{subtitle}
|
||||
</p>
|
||||
<p className="text-textLight text-sm">{subtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
{description && (
|
||||
<p className="text-textLight dark:text-textDimmed text-sm">
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
{description && <p className="text-textLight text-sm">{description}</p>}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
@@ -412,7 +406,7 @@ function StarterPackEmbed({
|
||||
return (
|
||||
<Link
|
||||
href={starterPackHref}
|
||||
className="w-full rounded-lg overflow-hidden border dark:border-slate-600 flex flex-col items-stretch">
|
||||
className="w-full rounded-lg overflow-hidden border flex flex-col items-stretch">
|
||||
<img src={imageUri} className="aspect-[1.91/1] object-cover" />
|
||||
<div className="py-3 px-4">
|
||||
<div className="flex space-x-2 items-center">
|
||||
@@ -421,7 +415,7 @@ function StarterPackEmbed({
|
||||
<p className="font-semibold leading-[21px]">
|
||||
{content.record.name}
|
||||
</p>
|
||||
<p className="text-sm text-textLight dark:text-textDimmed line-clamp-2 leading-[18px]">
|
||||
<p className="text-sm text-textLight line-clamp-2 leading-[18px]">
|
||||
Starter pack by{' '}
|
||||
{content.creator.displayName || `@${content.creator.handle}`}
|
||||
</p>
|
||||
@@ -431,7 +425,7 @@ function StarterPackEmbed({
|
||||
<p className="text-sm mt-1">{content.record.description}</p>
|
||||
)}
|
||||
{!!content.joinedAllTimeCount && content.joinedAllTimeCount > 50 && (
|
||||
<p className="text-sm font-semibold text-textLight dark:text-textDimmed mt-1">
|
||||
<p className="text-sm font-semibold text-textLight mt-1">
|
||||
{content.joinedAllTimeCount} users have joined!
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -38,7 +38,7 @@ export function Post({thread}: Props) {
|
||||
<div className="flex-1 flex-col flex gap-2" lang={record?.langs?.[0]}>
|
||||
<div className="flex gap-2.5 items-center cursor-pointer">
|
||||
<Link href={`/profile/${post.author.did}`} className="rounded-full">
|
||||
<div className="w-10 h-10 overflow-hidden rounded-full bg-neutral-300 dark:bg-slate-700 shrink-0">
|
||||
<div className="w-10 h-10 overflow-hidden rounded-full bg-neutral-300 shrink-0">
|
||||
<img
|
||||
src={post.author.avatar}
|
||||
style={isAuthorLabeled ? {filter: 'blur(2.5px)'} : undefined}
|
||||
@@ -53,7 +53,7 @@ export function Post({thread}: Props) {
|
||||
</Link>
|
||||
<Link
|
||||
href={`/profile/${post.author.did}`}
|
||||
className="text-[15px] text-textLight dark:text-textDimmed hover:underline line-clamp-1">
|
||||
className="text-[15px] text-textLight hover:underline line-clamp-1">
|
||||
<p>@{post.author.handle}</p>
|
||||
</Link>
|
||||
</div>
|
||||
@@ -69,15 +69,15 @@ export function Post({thread}: Props) {
|
||||
<Link href={href}>
|
||||
<time
|
||||
datetime={new Date(post.indexedAt).toISOString()}
|
||||
className="text-textLight dark:text-textDimmed mt-1 text-sm hover:underline">
|
||||
className="text-textLight mt-1 text-sm hover:underline">
|
||||
{niceDate(post.indexedAt)}
|
||||
</time>
|
||||
</Link>
|
||||
<div className="border-t dark:border-slate-600 w-full pt-2.5 flex items-center gap-5 text-sm cursor-pointer">
|
||||
<div className="border-t w-full pt-2.5 flex items-center gap-5 text-sm cursor-pointer">
|
||||
{!!post.likeCount && (
|
||||
<div className="flex items-center gap-2 cursor-pointer">
|
||||
<img src={likeIcon} className="w-5 h-5" />
|
||||
<p className="font-bold text-neutral-500 dark:text-neutral-300 mb-px">
|
||||
<p className="font-bold text-neutral-500 mb-px">
|
||||
{prettyNumber(post.likeCount)}
|
||||
</p>
|
||||
</div>
|
||||
@@ -85,19 +85,17 @@ export function Post({thread}: Props) {
|
||||
{!!post.repostCount && (
|
||||
<div className="flex items-center gap-2 cursor-pointer">
|
||||
<img src={repostIcon} className="w-5 h-5" />
|
||||
<p className="font-bold text-neutral-500 dark:text-neutral-300 mb-px">
|
||||
<p className="font-bold text-neutral-500 mb-px">
|
||||
{prettyNumber(post.repostCount)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-2 cursor-pointer">
|
||||
<img src={replyIcon} className="w-5 h-5" />
|
||||
<p className="font-bold text-neutral-500 dark:text-neutral-300 mb-px">
|
||||
Reply
|
||||
</p>
|
||||
<p className="font-bold text-neutral-500 mb-px">Reply</p>
|
||||
</div>
|
||||
<div className="flex-1" />
|
||||
<p className="cursor-pointer text-brand dark:text-brandLighten font-bold hover:underline hidden min-[450px]:inline">
|
||||
<p className="cursor-pointer text-brand font-bold hover:underline hidden min-[450px]:inline">
|
||||
{post.replyCount
|
||||
? `Read ${prettyNumber(post.replyCount)} ${
|
||||
post.replyCount > 1 ? 'replies' : 'reply'
|
||||
|
||||
@@ -5,7 +5,3 @@
|
||||
.break-word {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import {useEffect, useMemo, useRef, useState} from 'preact/hooks'
|
||||
|
||||
import arrowBottom from '../../assets/arrowBottom_stroke2_corner0_rounded.svg'
|
||||
import logo from '../../assets/logo.svg'
|
||||
import {initColorMode} from '../color-mode'
|
||||
import {Container} from '../components/container'
|
||||
import {Link} from '../components/link'
|
||||
import {Post} from '../components/post'
|
||||
@@ -22,8 +21,6 @@ export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js`
|
||||
const root = document.getElementById('app')
|
||||
if (!root) throw new Error('No root element')
|
||||
|
||||
initColorMode()
|
||||
|
||||
const agent = new BskyAgent({
|
||||
service: 'https://public.api.bsky.app',
|
||||
})
|
||||
@@ -111,7 +108,7 @@ function LandingPage() {
|
||||
}, [uri])
|
||||
|
||||
return (
|
||||
<main className="w-full min-h-screen flex flex-col items-center gap-8 py-14 px-4 md:pt-32 dark:bg-dimmedBgDarken dark:text-slate-200">
|
||||
<main className="w-full min-h-screen flex flex-col items-center gap-8 py-14 px-4 md:pt-32">
|
||||
<Link
|
||||
href="https://bsky.social/about"
|
||||
className="transition-transform hover:scale-110">
|
||||
@@ -124,22 +121,20 @@ function LandingPage() {
|
||||
type="text"
|
||||
value={uri}
|
||||
onInput={e => setUri(e.currentTarget.value)}
|
||||
className="border rounded-lg py-3 w-full max-w-[600px] px-4 dark:bg-dimmedBg dark:border-slate-500"
|
||||
className="border rounded-lg py-3 w-full max-w-[600px] px-4"
|
||||
placeholder={DEFAULT_POST}
|
||||
/>
|
||||
|
||||
<img src={arrowBottom} className="w-6 dark:invert" />
|
||||
<img src={arrowBottom} className="w-6" />
|
||||
|
||||
{loading ? (
|
||||
<div className="w-full max-w-[600px]">
|
||||
<Skeleton />
|
||||
</div>
|
||||
<Skeleton />
|
||||
) : (
|
||||
<div className="w-full max-w-[600px] gap-8 flex flex-col">
|
||||
{!error && thread && uri && <Snippet thread={thread} />}
|
||||
{!error && thread && <Post thread={thread} key={thread.post.uri} />}
|
||||
{error && (
|
||||
<div className="w-full border border-red-500 bg-red-500/10 px-4 py-3 rounded-lg">
|
||||
<div className="w-full border border-red-500 bg-red-50 px-4 py-3 rounded-lg">
|
||||
<p className="text-red-500 text-center">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -154,15 +149,15 @@ function Skeleton() {
|
||||
<Container>
|
||||
<div className="flex-1 flex-col flex gap-2 pb-8">
|
||||
<div className="flex gap-2.5 items-center">
|
||||
<div className="w-10 h-10 overflow-hidden rounded-full bg-neutral-100 dark:bg-slate-700 shrink-0 animate-pulse" />
|
||||
<div className="w-10 h-10 overflow-hidden rounded-full bg-neutral-100 shrink-0 animate-pulse" />
|
||||
<div className="flex-1">
|
||||
<div className="bg-neutral-100 dark:bg-slate-700 animate-pulse w-64 h-4 rounded" />
|
||||
<div className="bg-neutral-100 dark:bg-slate-700 animate-pulse w-32 h-3 mt-1 rounded" />
|
||||
<div className="bg-neutral-100 animate-pulse w-64 h-4 rounded" />
|
||||
<div className="bg-neutral-100 animate-pulse w-32 h-3 mt-1 rounded" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full h-4 mt-2 bg-neutral-100 dark:bg-slate-700 rounded animate-pulse" />
|
||||
<div className="w-5/6 h-4 bg-neutral-100 dark:bg-slate-700 rounded animate-pulse" />
|
||||
<div className="w-3/4 h-4 bg-neutral-100 dark:bg-slate-700 rounded animate-pulse" />
|
||||
<div className="w-full h-4 mt-2 bg-neutral-100 rounded animate-pulse" />
|
||||
<div className="w-5/6 h-4 bg-neutral-100 rounded animate-pulse" />
|
||||
<div className="w-3/4 h-4 bg-neutral-100 rounded animate-pulse" />
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
@@ -225,7 +220,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) {
|
||||
ref={ref}
|
||||
type="text"
|
||||
value={snippet}
|
||||
className="border rounded-lg py-3 w-full px-4 dark:bg-dimmedBg dark:border-slate-500"
|
||||
className="border rounded-lg py-3 w-full px-4"
|
||||
readOnly
|
||||
autoFocus
|
||||
onFocus={() => {
|
||||
@@ -233,7 +228,7 @@ function Snippet({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) {
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
className="rounded-lg bg-brand text-white py-3 px-4 whitespace-nowrap min-w-28"
|
||||
className="rounded-lg bg-brand text-white color-white py-3 px-4 whitespace-nowrap min-w-28"
|
||||
onClick={() => {
|
||||
ref.current?.focus()
|
||||
ref.current?.select()
|
||||
|
||||
@@ -4,7 +4,6 @@ import {AppBskyFeedDefs, AtpAgent} from '@atproto/api'
|
||||
import {h, render} from 'preact'
|
||||
|
||||
import logo from '../../assets/logo.svg'
|
||||
import {initColorMode} from '../color-mode'
|
||||
import {Container} from '../components/container'
|
||||
import {Link} from '../components/link'
|
||||
import {Post} from '../components/post'
|
||||
@@ -22,8 +21,6 @@ if (!uri) {
|
||||
throw new Error('No uri in path')
|
||||
}
|
||||
|
||||
initColorMode()
|
||||
|
||||
agent
|
||||
.getPostThread({
|
||||
uri,
|
||||
@@ -58,13 +55,13 @@ function PwiOptOut({thread}: {thread: AppBskyFeedDefs.ThreadViewPost}) {
|
||||
<img src={logo} className="h-6" />
|
||||
</Link>
|
||||
<div className="w-full py-12 gap-4 flex flex-col items-center">
|
||||
<p className="max-w-80 text-center w-full text-textLight dark:text-textDimmed">
|
||||
<p className="max-w-80 text-center w-full text-textLight">
|
||||
The author of this post has requested their posts not be displayed on
|
||||
external sites.
|
||||
</p>
|
||||
<Link
|
||||
href={href}
|
||||
className="max-w-80 rounded-lg bg-brand text-white text-center py-1 px-4 w-full mx-auto">
|
||||
className="max-w-80 rounded-lg bg-brand text-white color-white text-center py-1 px-4 w-full mx-auto">
|
||||
View on Bluesky
|
||||
</Link>
|
||||
</div>
|
||||
@@ -80,7 +77,7 @@ function ErrorMessage() {
|
||||
className="transition-transform hover:scale-110 absolute top-4 right-4">
|
||||
<img src={logo} className="h-6" />
|
||||
</Link>
|
||||
<p className="my-16 text-center w-full text-textLight dark:text-textDimmed">
|
||||
<p className="my-16 text-center w-full text-textLight">
|
||||
Post not found, it may have been deleted.
|
||||
</p>
|
||||
</Container>
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
||||
darkMode: ['variant', [
|
||||
'&:is(.dark *):not(:is(.dark .light *))',
|
||||
]],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: 'rgb(10,122,255)',
|
||||
brandLighten: 'rgb(32,139,254)',
|
||||
textLight: 'rgb(66,87,108)',
|
||||
textDimmed: 'rgb(174,187,201)',
|
||||
dimmedBgLighten: 'rgb(30,41,54)',
|
||||
dimmedBg: 'rgb(22,30,39)',
|
||||
dimmedBgDarken: 'rgb(18,25,32)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES5",
|
||||
@@ -19,5 +20,5 @@
|
||||
"jsxFragmentFactory": "Fragment",
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"include": ["src", "snippet", "vite.config.ts"]
|
||||
"include": ["src", "vite.config.ts"]
|
||||
}
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ Then build and copy over the big 'ol `bundle.web.js` file:
|
||||
|
||||
### Golang Daemon
|
||||
|
||||
Install golang. We generally develop against the current stable release of the language, as declared in `go.mod`.
|
||||
Install golang. We are generally using v1.22+.
|
||||
|
||||
In this directory (`bskyweb/`):
|
||||
|
||||
|
||||
@@ -214,21 +214,15 @@ func serve(cctx *cli.Context) error {
|
||||
e.GET("/iframe/youtube.html", echo.WrapHandler(staticHandler))
|
||||
e.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static/", staticHandler)), func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
c.Response().Before(func() {
|
||||
if c.Response().Status >= 300 {
|
||||
return
|
||||
}
|
||||
path := c.Request().URL.Path
|
||||
maxAge := 1 * (60 * 60) // default is 1 hour
|
||||
|
||||
path := c.Request().URL.Path
|
||||
maxAge := 1 * (60 * 60) // default is 1 hour
|
||||
// all assets in /static/js, /static/css, /static/media are content-hashed and can be cached for a long time
|
||||
if strings.HasPrefix(path, "/static/js/") || strings.HasPrefix(path, "/static/css/") || strings.HasPrefix(path, "/static/media/") {
|
||||
maxAge = 365 * (60 * 60 * 24) // 1 year
|
||||
}
|
||||
|
||||
// all assets in /static/js, /static/css, /static/media are content-hashed and can be cached for a long time
|
||||
if strings.HasPrefix(path, "/static/js/") || strings.HasPrefix(path, "/static/css/") || strings.HasPrefix(path, "/static/media/") {
|
||||
maxAge = 365 * (60 * 60 * 24) // 1 year
|
||||
}
|
||||
|
||||
c.Response().Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", maxAge))
|
||||
})
|
||||
c.Response().Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", maxAge))
|
||||
return next(c)
|
||||
}
|
||||
})
|
||||
@@ -241,7 +235,6 @@ func serve(cctx *cli.Context) error {
|
||||
|
||||
// generic routes
|
||||
e.GET("/hashtag/:tag", server.WebGeneric)
|
||||
e.GET("/topic/:topic", server.WebGeneric)
|
||||
e.GET("/search", server.WebGeneric)
|
||||
e.GET("/feeds", server.WebGeneric)
|
||||
e.GET("/notifications", server.WebGeneric)
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
module github.com/bluesky-social/social-app/bskyweb
|
||||
|
||||
go 1.23
|
||||
go 1.22
|
||||
|
||||
toolchain go1.23.4
|
||||
toolchain go1.22.4
|
||||
|
||||
require (
|
||||
github.com/bluesky-social/indigo v0.0.0-20240624223826-fd66f93ce141
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -8,11 +8,13 @@
|
||||
Preconnect to essential domains
|
||||
-->
|
||||
<link rel="preconnect" href="https://bsky.social">
|
||||
<link rel="preconnect" href="https://bsky.network">
|
||||
<title>{%- block head_title -%}Bluesky{%- endblock -%}</title>
|
||||
|
||||
<!-- Hello Humans! API docs at https://atproto.com -->
|
||||
|
||||
<link rel="preload" as="font" type="font/woff2" href="{{ staticCDNHost }}/static/media/InterVariable.c504db5c06caaf7cdfba.woff2" crossorigin>
|
||||
<link rel="preload" as="font" type="font/ttf" href="{{ staticCDNHost }}/static/media/InterVariable.c9f788f6e7ebaec75d7c.ttf">
|
||||
<link rel="preload" as="font" type="font/ttf" href="{{ staticCDNHost }}/static/media/InterVariable-Italic.55d6a3f35e9b605ba6f4.ttf">
|
||||
|
||||
<style>
|
||||
/**
|
||||
@@ -24,14 +26,14 @@
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'InterVariable';
|
||||
src: url("{{ staticCDNHost }}/static/media/InterVariable.c504db5c06caaf7cdfba.woff2") format('woff2');
|
||||
src: url("{{ staticCDNHost }}/static/media/InterVariable.c9f788f6e7ebaec75d7c.ttf") format('truetype');
|
||||
font-weight: 300 1000;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'InterVariableItalic';
|
||||
src: url("{{ staticCDNHost }}/static/media/InterVariable-Italic.01dcbad1bac635f9c9cd.woff2") format('woff2');
|
||||
src: url("{{ staticCDNHost }}/static/media/InterVariable-Italic.55d6a3f35e9b605ba6f4.ttf") format('truetype');
|
||||
font-weight: 300 1000;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
{# don't include the bundle on non-404 error pages #}
|
||||
{% block head_bundle %}
|
||||
{% if statusCode == 404 %}
|
||||
{{ block.Super }}
|
||||
{{ super() }}
|
||||
{% else %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{%- block body_all %}
|
||||
{% if statusCode == 404 %}
|
||||
{{ block.Super }}
|
||||
{{ super() }}
|
||||
{% else %}
|
||||
<h1>{{ statusCode }}: Server Error</h1>
|
||||
<p>Sorry about that! Our <a href="https://bluesky.statuspage.io/">Status Page</a> might have more context.
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"project_id": "664276",
|
||||
"base_path": ".",
|
||||
"preserve_hierarchy": true,
|
||||
"files": [
|
||||
{
|
||||
"source": "/src/locale/locales/en/messages.po",
|
||||
"translation": "/src/locale/locales/%two_letters_code%/messages.po",
|
||||
"languages_mapping": {
|
||||
"two_letters_code": {
|
||||
"pt-BR": "pt-BR",
|
||||
"en-GB": "en-GB",
|
||||
"zh-CN": "zh-CN",
|
||||
"zh-TW": "zh-TW",
|
||||
"zh-HK": "zh-HK"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -87,12 +87,6 @@ However, if you're a part of the Bluesky team and want to enable Sentry, fill in
|
||||
|
||||
If you change `SENTRY_AUTH_TOKEN`, you need to do `yarn prebuild` before running `yarn ios` or `yarn android` again.
|
||||
|
||||
### Adding bitdrift
|
||||
|
||||
Adding bitdirft is NOT required. You can keep `BITDRIFT_API_KEY=` in `.env` which will avoid initializing bitdrift during startup.
|
||||
|
||||
However, if you're a part of the Bluesky team and want to enable bitdrift, fill in `BITDRIFT_API_KEY` in your `.env` to enable bitdrift.
|
||||
|
||||
### Adding and Updating Locales
|
||||
|
||||
- `yarn intl:build` -> you will also need to run this anytime `./src/locale/{locale}/messages.po` change
|
||||
|
||||
+7
-57
@@ -3,64 +3,9 @@
|
||||
We want the official Bluesky app to be supported in as many languages as possible. If you want to help us translate the app, please open a PR or issue on the [Bluesky app repo on GitHub](https://github.com/bluesky-social/social-app)
|
||||
|
||||
## Tools
|
||||
|
||||
- We use Lingui to implement translations. You can find the documentation [here](https://lingui.dev/).
|
||||
- We use Crowdin to manage translations.
|
||||
- Bluesky Crowdin: https://crowdin.com/project/bluesky-social
|
||||
- Introduction to Crowdin: https://support.crowdin.com/for-translators/
|
||||
|
||||
## Translators
|
||||
|
||||
Much of the app is translated by community contributions. (We <3 our translators!) If you want to participate in the translation of the app, read this section.
|
||||
|
||||
### Using Crowdin
|
||||
|
||||
[Crowdin](https://crowdin.com/project/bluesky-social) is our primary tool for managing translations. There are two roles:
|
||||
|
||||
- **Proof-readers**. Can create new translations and approve submitted translations.
|
||||
- **Translators**. Can create new translations.
|
||||
|
||||
All translations must be approved by proof-readers before they are accepted into the app.
|
||||
|
||||
### Using other platforms
|
||||
|
||||
You may contribute PRs separately from Crowdin, however we strongly recommend using Crowdin to avoid conflicts.
|
||||
|
||||
### Code of conduct on Crowdin
|
||||
|
||||
Please treat everyone with respect. Proof-readers are given final say on translations. Translators who frequently come into conflict with other translators, or who contribute noticably incorrect translations, will have their membership to the Crowdin project revoked.
|
||||
|
||||
### Adding a new language
|
||||
|
||||
Create a new [Crowdin discussion](https://crowdin.com/project/bluesky-social/discussions) or [GitHub issue](https://github.com/bluesky-social/social-app/issues) requesting the new language be added to the project.
|
||||
|
||||
Please only request a new language when you are certain you will be able to contribute a substantive portion of translations for the language.
|
||||
|
||||
## Maintainers
|
||||
|
||||
Install the [Crowdin CLI](https://crowdin.github.io/crowdin-cli/). You will need to [configure your API token](https://crowdin.github.io/crowdin-cli/configuration) to access the project.
|
||||
|
||||
### English source-file sync with Crowdin
|
||||
|
||||
Every night, a GitHub action will run `yarn intl:extract` to update the english `messages.po` file. This will be automatically synced with Crowdin. Crowdin should notify all subscribed users of new translations.
|
||||
|
||||
### Release process
|
||||
|
||||
1. Pull main and create a branch.
|
||||
1. Run `yarn intl:pull` to fetch all translation updates from Crowdin.
|
||||
1. Create a PR, ensure the translations all look correct, and merge.
|
||||
1. If needed:
|
||||
1. Merge all approved translation PRs (contributions from outside crowdin).
|
||||
1. Run `yarn intl:push` to sync Crowdin with the state of the repo.
|
||||
|
||||
### Testing the translations in Crowdin
|
||||
|
||||
You can run `yarn intl:pull` to pull the currently-approved translations from Crowdin.
|
||||
|
||||
## Developers
|
||||
We are using Lingui to manage translations. You can find the documentation [here](https://lingui.dev/).
|
||||
|
||||
### Adding new strings
|
||||
|
||||
When adding a new string, do it as follows:
|
||||
```jsx
|
||||
// Before
|
||||
@@ -116,7 +61,6 @@ So the workflow is as follows:
|
||||
6. Enjoy translated app!
|
||||
|
||||
### Common pitfalls
|
||||
|
||||
These pitfalls are memoization pitfalls that will cause the components to not re-render when the locale is changed -- causing stale translations to be shown.
|
||||
|
||||
```jsx
|
||||
@@ -167,3 +111,9 @@ export function Welcome() {
|
||||
return <div>{welcome}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Credits
|
||||
Please check each individual `messages.po` file for the credits of the translators. We are very grateful for their help!
|
||||
|
||||
If you would like to translate the Bluesky app into your language, please open a PR or issue on this repo.
|
||||
|
||||
@@ -5,8 +5,9 @@ import {LogBox} from 'react-native'
|
||||
import {registerRootComponent} from 'expo'
|
||||
|
||||
import App from '#/App'
|
||||
import {IS_TEST} from '#/env'
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
if (IS_TEST) {
|
||||
LogBox.ignoreAllLogs() // suppress all logs in tests
|
||||
} else {
|
||||
LogBox.ignoreLogs(['Require cycle:']) // suppress require-cycle warnings, it's fine
|
||||
|
||||
@@ -105,19 +105,3 @@ jest.mock('expo-modules-core', () => ({
|
||||
return () => null
|
||||
}),
|
||||
}))
|
||||
|
||||
jest.mock('expo-localization', () => ({
|
||||
getLocales: () => [],
|
||||
}))
|
||||
|
||||
jest.mock('statsig-react-native-expo', () => ({
|
||||
Statsig: {
|
||||
initialize() {},
|
||||
initializeCalled() {
|
||||
return false
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
jest.mock('../src/lib/bitdrift', () => ({}))
|
||||
jest.mock('../src/lib/statsig/statsig', () => ({}))
|
||||
|
||||
@@ -5,31 +5,23 @@ module.exports = {
|
||||
'an',
|
||||
'ast',
|
||||
'ca',
|
||||
'da',
|
||||
'de',
|
||||
'el',
|
||||
'en-GB',
|
||||
'es',
|
||||
'eu',
|
||||
'fi',
|
||||
'fr',
|
||||
'ga',
|
||||
'gl',
|
||||
'hi',
|
||||
'hu',
|
||||
'ia',
|
||||
'id',
|
||||
'it',
|
||||
'ja',
|
||||
'km',
|
||||
'ko',
|
||||
'ne',
|
||||
'nl',
|
||||
'pl',
|
||||
'pt-BR',
|
||||
'ro',
|
||||
'ru',
|
||||
'sv',
|
||||
'th',
|
||||
'tr',
|
||||
'uk',
|
||||
|
||||
@@ -14,8 +14,6 @@ if (process.env.BSKY_PROFILE) {
|
||||
cfg.cacheVersion += ':PROFILE'
|
||||
}
|
||||
|
||||
cfg.resolver.assetExts = [...cfg.resolver.assetExts, 'woff2']
|
||||
|
||||
cfg.resolver.resolveRequest = (context, moduleName, platform) => {
|
||||
// HACK: manually resolve a few packages that use `exports` in `package.json`.
|
||||
// A proper solution is to enable `unstable_enablePackageExports` but this needs careful testing.
|
||||
|
||||
+23
-52
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "bsky.app",
|
||||
"version": "1.98.0",
|
||||
"version": "1.96.0",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"packageManager": "yarn@1.22.19",
|
||||
"scripts": {
|
||||
"prepare": "is-ci || husky install",
|
||||
"postinstall": "patch-package && yarn intl:compile",
|
||||
@@ -43,10 +43,8 @@
|
||||
"perf:test:results": "NODE_ENV=test flashlight report .perf/results.json",
|
||||
"perf:measure": "NODE_ENV=test flashlight measure",
|
||||
"intl:build": "yarn intl:extract && yarn intl:compile",
|
||||
"intl:extract": "lingui extract --clean --locale en",
|
||||
"intl:extract": "lingui extract",
|
||||
"intl:compile": "lingui compile",
|
||||
"intl:pull": "crowdin download translations --verbose -b main",
|
||||
"intl:push": "crowdin push translations --verbose -b main",
|
||||
"nuke": "rm -rf ./node_modules && rm -rf ./ios && rm -rf ./android",
|
||||
"update-extensions": "bash scripts/updateExtensions.sh",
|
||||
"export": "npx expo export",
|
||||
@@ -56,8 +54,7 @@
|
||||
"icons:optimize": "svgo -f ./assets/icons"
|
||||
},
|
||||
"dependencies": {
|
||||
"@atproto/api": "^0.13.35",
|
||||
"@bitdrift/react-native": "^0.6.2",
|
||||
"@atproto/api": "^0.13.18",
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
@@ -65,19 +62,19 @@
|
||||
"@expo/webpack-config": "^19.0.0",
|
||||
"@floating-ui/dom": "^1.6.3",
|
||||
"@floating-ui/react-dom": "^2.0.8",
|
||||
"@formatjs/intl-locale": "^4.2.8",
|
||||
"@formatjs/intl-numberformat": "^8.15.1",
|
||||
"@formatjs/intl-pluralrules": "^5.4.1",
|
||||
"@formatjs/intl-locale": "^4.0.0",
|
||||
"@formatjs/intl-numberformat": "^8.10.3",
|
||||
"@formatjs/intl-pluralrules": "^5.2.14",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.1.1",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
||||
"@fortawesome/react-native-fontawesome": "^0.3.2",
|
||||
"@haileyok/bluesky-video": "0.2.6",
|
||||
"@haileyok/bluesky-video": "0.2.4",
|
||||
"@ipld/dag-cbor": "^9.2.0",
|
||||
"@lingui/react": "^4.14.1",
|
||||
"@mattermost/react-native-paste-input": "^0.7.1",
|
||||
"@miblanchard/react-native-slider": "^2.3.1",
|
||||
"@mozzius/expo-dynamic-app-icon": "^1.5.0",
|
||||
"@mozzius/expo-dynamic-app-icon": "^1.4.1",
|
||||
"@radix-ui/react-dismissable-layer": "^1.1.1",
|
||||
"@radix-ui/react-dropdown-menu": "2.0.1",
|
||||
"@radix-ui/react-focus-guards": "^1.1.1",
|
||||
@@ -121,15 +118,15 @@
|
||||
"emoji-mart": "^5.5.2",
|
||||
"emoji-regex": "^10.4.0",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"expo": "~52.0.19",
|
||||
"expo": "~52.0.17",
|
||||
"expo-application": "^6.0.1",
|
||||
"expo-blur": "^14.0.3",
|
||||
"expo-blur": "^14.0.1",
|
||||
"expo-build-properties": "^0.13.1",
|
||||
"expo-camera": "~16.0.7",
|
||||
"expo-clipboard": "^7.0.0",
|
||||
"expo-dev-client": "^5.0.4",
|
||||
"expo-device": "~7.0.1",
|
||||
"expo-file-system": "^18.0.6",
|
||||
"expo-file-system": "^18.0.4",
|
||||
"expo-font": "~13.0.1",
|
||||
"expo-haptics": "^14.0.0",
|
||||
"expo-image": "~2.0.3",
|
||||
@@ -140,15 +137,13 @@
|
||||
"expo-localization": "~16.0.0",
|
||||
"expo-media-library": "~17.0.3",
|
||||
"expo-navigation-bar": "~4.0.4",
|
||||
"expo-notifications": "~0.29.11",
|
||||
"expo-screen-orientation": "^8.0.2",
|
||||
"expo-notifications": "~0.29.10",
|
||||
"expo-sharing": "^13.0.0",
|
||||
"expo-splash-screen": "~0.29.18",
|
||||
"expo-splash-screen": "~0.29.16",
|
||||
"expo-status-bar": "~2.0.0",
|
||||
"expo-system-ui": "^4.0.4",
|
||||
"expo-task-manager": "~12.0.3",
|
||||
"expo-updates": "~0.26.10",
|
||||
"expo-video": "^2.0.5",
|
||||
"expo-updates": "~0.26.9",
|
||||
"expo-web-browser": "~14.0.1",
|
||||
"fast-text-encoding": "^1.0.6",
|
||||
"history": "^5.3.0",
|
||||
@@ -172,21 +167,21 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-image-crop": "^11.0.7",
|
||||
"react-keyed-flatten-children": "^3.0.0",
|
||||
"react-native": "0.76.6",
|
||||
"react-native": "0.76.3",
|
||||
"react-native-compressor": "1.10.3",
|
||||
"react-native-date-picker": "^5.0.7",
|
||||
"react-native-drawer-layout": "^4.1.1",
|
||||
"react-native-drawer-layout": "^4.0.4",
|
||||
"react-native-gesture-handler": "2.20.2",
|
||||
"react-native-get-random-values": "~1.11.0",
|
||||
"react-native-image-crop-picker": "^0.41.6",
|
||||
"react-native-ios-context-menu": "^1.15.3",
|
||||
"react-native-keyboard-controller": "^1.14.5",
|
||||
"react-native-mmkv": "^2.12.2",
|
||||
"react-native-pager-view": "6.7.0",
|
||||
"react-native-pager-view": "6.6.1",
|
||||
"react-native-picker-select": "^9.3.1",
|
||||
"react-native-progress": "bluesky-social/react-native-progress",
|
||||
"react-native-qrcode-styled": "^0.3.3",
|
||||
"react-native-reanimated": "3.17.0-nightly-20241211-17e89ca24",
|
||||
"react-native-reanimated": "^3.16.3",
|
||||
"react-native-root-siblings": "^4.1.1",
|
||||
"react-native-safe-area-context": "4.14.0",
|
||||
"react-native-screens": "~4.3.0",
|
||||
@@ -195,7 +190,6 @@
|
||||
"react-native-url-polyfill": "^1.3.0",
|
||||
"react-native-uuid": "^2.0.2",
|
||||
"react-native-view-shot": "^4.0.3",
|
||||
"react-native-vision-camera": "^4.6.3",
|
||||
"react-native-web": "~0.19.13",
|
||||
"react-native-web-webview": "^1.0.2",
|
||||
"react-native-webview": "13.12.3",
|
||||
@@ -211,12 +205,13 @@
|
||||
"zod": "^3.20.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@atproto/dev-env": "^0.3.67",
|
||||
"@atproto/dev-env": "^0.3.64",
|
||||
"@babel/core": "^7.26.0",
|
||||
"@babel/preset-env": "^7.26.0",
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@did-plc/server": "^0.0.1",
|
||||
"@expo/config-plugins": "9.0.10",
|
||||
"@expo/prebuild-config": "8.0.22",
|
||||
"@lingui/cli": "^4.14.1",
|
||||
"@lingui/macro": "^4.14.1",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
|
||||
@@ -255,7 +250,6 @@
|
||||
"jest-expo": "^52.0.2",
|
||||
"jest-junit": "^16.0.0",
|
||||
"lint-staged": "^13.2.3",
|
||||
"lockfile-lint": "^4.14.0",
|
||||
"metro-react-native-babel-preset": "^0.76.9",
|
||||
"prettier": "^2.8.3",
|
||||
"react-native-dotenv": "^3.4.11",
|
||||
@@ -272,8 +266,8 @@
|
||||
"@react-native/normalize-colors": "0.76.1",
|
||||
"@radix-ui/react-focus-scope": "1.1.0",
|
||||
"@types/react": "^18",
|
||||
"**/expo-constants": "17.0.3",
|
||||
"**/expo-device": "7.0.1",
|
||||
"**/expo-constants": "16.0.1",
|
||||
"**/expo-device": "6.0.2",
|
||||
"**/zod": "3.23.8"
|
||||
},
|
||||
"jest": {
|
||||
@@ -335,28 +329,5 @@
|
||||
"assets/icons/*.svg": [
|
||||
"svgo"
|
||||
]
|
||||
},
|
||||
"lockfile-lint": {
|
||||
"path": "yarn.lock",
|
||||
"allowedHosts": [
|
||||
"npm",
|
||||
"yarn"
|
||||
],
|
||||
"allowedSchemes": [
|
||||
"https:"
|
||||
],
|
||||
"allowedPackageNameAliases": [
|
||||
"@babel/traverse--for-generate-function-map:@babel/traverse",
|
||||
"string-width-cjs:string-width",
|
||||
"strip-ansi-cjs:strip-ansi",
|
||||
"wrap-ansi-cjs:wrap-ansi"
|
||||
],
|
||||
"allowedUrls": [
|
||||
"https://codeload.github.com/bluesky-social/react-native-bottom-sheet/tar.gz/28a87d1bb55e10fc355fa1455545a30734995908",
|
||||
"https://codeload.github.com/bluesky-social/react-native-progress/tar.gz/5a372f4f2ce5feb26f4f47b6a4d187ab9b923ab4"
|
||||
],
|
||||
"emptyHostname": false,
|
||||
"validatePackageNames": true,
|
||||
"validateIntegrity": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,157 +1,16 @@
|
||||
diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m b/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m
|
||||
index e916023..5049c33 100644
|
||||
index e916023..0564d97 100644
|
||||
--- a/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m
|
||||
+++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m
|
||||
@@ -4,6 +4,7 @@
|
||||
//
|
||||
// Created by Elias Nahum on 04-11-20.
|
||||
// Copyright © 2020 Facebook. All rights reserved.
|
||||
+// Updated to remove parent’s default text view
|
||||
//
|
||||
|
||||
#import "PasteInputView.h"
|
||||
@@ -12,49 +13,78 @@
|
||||
|
||||
@implementation PasteInputView
|
||||
{
|
||||
- PasteInputTextView *_backedTextInputView;
|
||||
+ // We'll store the custom text view in this ivar
|
||||
+ PasteInputTextView *_customBackedTextView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
+ // Must call the super’s designated initializer
|
||||
if (self = [super initWithBridge:bridge]) {
|
||||
- _backedTextInputView = [[PasteInputTextView alloc] initWithFrame:self.bounds];
|
||||
- _backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
- _backedTextInputView.textInputDelegate = self;
|
||||
+ // 1. The parent (RCTMultilineTextInputView) has already created
|
||||
+ // its own _backedTextInputView = [RCTUITextView new] in super init.
|
||||
+ // We can remove that subview:
|
||||
|
||||
- [self addSubview:_backedTextInputView];
|
||||
- }
|
||||
+ id<RCTBackedTextInputViewProtocol> parentInputView = super.backedTextInputView;
|
||||
+ if ([parentInputView isKindOfClass:[UIView class]]) {
|
||||
+ UIView *parentSubview = (UIView *)parentInputView;
|
||||
+ if (parentSubview.superview == self) {
|
||||
+ [parentSubview removeFromSuperview];
|
||||
+ }
|
||||
+ }
|
||||
|
||||
+ // 2. Now create our custom PasteInputTextView
|
||||
+ _customBackedTextView = [[PasteInputTextView alloc] initWithFrame:self.bounds];
|
||||
+ _customBackedTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
+ _customBackedTextView.textInputDelegate = self;
|
||||
+
|
||||
+ // Optional: disable inline predictions for iOS 17+
|
||||
@@ -22,6 +22,11 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
_backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_backedTextInputView.textInputDelegate = self;
|
||||
|
||||
+ // Disable inline predictions to prevent jank in the composer
|
||||
+ if (@available(iOS 17.0, *)) {
|
||||
+ _customBackedTextView.inlinePredictionType = UITextInlinePredictionTypeNo;
|
||||
+ _backedTextInputView.inlinePredictionType = UITextInlinePredictionTypeNo;
|
||||
+ }
|
||||
+
|
||||
+ // 3. Add your custom text view as the only subview
|
||||
+ [self addSubview:_customBackedTextView];
|
||||
+ }
|
||||
return self;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * Override the parent's accessor so that anywhere in RN that calls
|
||||
+ * `self.backedTextInputView` will get the custom PasteInputTextView.
|
||||
+ */
|
||||
- (id<RCTBackedTextInputViewProtocol>)backedTextInputView
|
||||
{
|
||||
- return _backedTextInputView;
|
||||
+ return _customBackedTextView;
|
||||
}
|
||||
|
||||
-- (void)setDisableCopyPaste:(BOOL)disableCopyPaste {
|
||||
- _backedTextInputView.disableCopyPaste = disableCopyPaste;
|
||||
+#pragma mark - Setters for React Props
|
||||
+
|
||||
+- (void)setDisableCopyPaste:(BOOL)disableCopyPaste
|
||||
+{
|
||||
+ _customBackedTextView.disableCopyPaste = disableCopyPaste;
|
||||
}
|
||||
|
||||
-- (void)setOnPaste:(RCTDirectEventBlock)onPaste {
|
||||
- _backedTextInputView.onPaste = onPaste;
|
||||
+- (void)setOnPaste:(RCTDirectEventBlock)onPaste
|
||||
+{
|
||||
+ _customBackedTextView.onPaste = onPaste;
|
||||
}
|
||||
|
||||
-- (void)setSmartPunctuation:(NSString *)smartPunctuation {
|
||||
- if ([smartPunctuation isEqualToString:@"enable"]) {
|
||||
- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeYes];
|
||||
- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeYes];
|
||||
- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeYes];
|
||||
- } else if ([smartPunctuation isEqualToString:@"disable"]) {
|
||||
- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeNo];
|
||||
- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeNo];
|
||||
- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeNo];
|
||||
- } else {
|
||||
- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeDefault];
|
||||
- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeDefault];
|
||||
- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeDefault];
|
||||
- }
|
||||
+- (void)setSmartPunctuation:(NSString *)smartPunctuation
|
||||
+{
|
||||
+ if ([smartPunctuation isEqualToString:@"enable"]) {
|
||||
+ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeYes];
|
||||
+ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeYes];
|
||||
+ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeYes];
|
||||
+ } else if ([smartPunctuation isEqualToString:@"disable"]) {
|
||||
+ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeNo];
|
||||
+ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeNo];
|
||||
+ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeNo];
|
||||
+ } else {
|
||||
+ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeDefault];
|
||||
+ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeDefault];
|
||||
+ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeDefault];
|
||||
+ }
|
||||
}
|
||||
|
||||
#pragma mark - UIScrollViewDelegate
|
||||
@@ -62,7 +92,6 @@
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||||
{
|
||||
RCTDirectEventBlock onScroll = self.onScroll;
|
||||
-
|
||||
if (onScroll) {
|
||||
CGPoint contentOffset = scrollView.contentOffset;
|
||||
CGSize contentSize = scrollView.contentSize;
|
||||
@@ -71,22 +100,22 @@
|
||||
|
||||
onScroll(@{
|
||||
@"contentOffset": @{
|
||||
- @"x": @(contentOffset.x),
|
||||
- @"y": @(contentOffset.y)
|
||||
+ @"x": @(contentOffset.x),
|
||||
+ @"y": @(contentOffset.y)
|
||||
},
|
||||
@"contentInset": @{
|
||||
- @"top": @(contentInset.top),
|
||||
- @"left": @(contentInset.left),
|
||||
- @"bottom": @(contentInset.bottom),
|
||||
- @"right": @(contentInset.right)
|
||||
+ @"top": @(contentInset.top),
|
||||
+ @"left": @(contentInset.left),
|
||||
+ @"bottom": @(contentInset.bottom),
|
||||
+ @"right": @(contentInset.right)
|
||||
},
|
||||
@"contentSize": @{
|
||||
- @"width": @(contentSize.width),
|
||||
- @"height": @(contentSize.height)
|
||||
+ @"width": @(contentSize.width),
|
||||
+ @"height": @(contentSize.height)
|
||||
},
|
||||
@"layoutMeasurement": @{
|
||||
- @"width": @(size.width),
|
||||
- @"height": @(size.height)
|
||||
+ @"width": @(size.width),
|
||||
+ @"height": @(size.height)
|
||||
},
|
||||
@"zoomScale": @(scrollView.zoomScale ?: 1),
|
||||
});
|
||||
[self addSubview:_backedTextInputView];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
diff --git a/node_modules/expo-image-manipulator/src/ImageManipulator.web.ts b/node_modules/expo-image-manipulator/src/ImageManipulator.web.ts
|
||||
new file mode 100644
|
||||
index 0000000..babbb3b
|
||||
--- /dev/null
|
||||
+++ b/node_modules/expo-image-manipulator/src/ImageManipulator.web.ts
|
||||
@@ -0,0 +1,60 @@
|
||||
+import { useReleasingSharedObject } from 'expo-modules-core';
|
||||
+import { SharedRef } from 'expo-modules-core/types';
|
||||
+
|
||||
+import { Action, ImageResult, SaveFormat, SaveOptions } from './ImageManipulator.types';
|
||||
+import { ImageManipulatorContext } from './ImageManipulatorContext';
|
||||
+import ExpoImageManipulator from './NativeImageManipulatorModule';
|
||||
+import { validateArguments } from './validators';
|
||||
+
|
||||
+// @needsAudit
|
||||
+/**
|
||||
+ * Manipulate the image provided via `uri`. Available modifications are rotating, flipping (mirroring),
|
||||
+ * resizing and cropping. Each invocation results in a new file. With one invocation you can provide
|
||||
+ * a set of actions to perform over the image. Overwriting the source file would not have an effect
|
||||
+ * in displaying the result as images are cached.
|
||||
+ * @param uri URI of the file to manipulate. Should be on the local file system or a base64 data URI.
|
||||
+ * @param actions An array of objects representing manipulation options. Each object should have
|
||||
+ * __only one__ of the keys that corresponds to specific transformation.
|
||||
+ * @param saveOptions A map defining how modified image should be saved.
|
||||
+ * @return Promise which fulfils with [`ImageResult`](#imageresult) object.
|
||||
+ * @deprecated It has been replaced by the new, contextual and object-oriented API.
|
||||
+ * Use [`ImageManipulator.manipulate`](#manipulateuri) or [`useImageManipulator`](#useimagemanipulatoruri) instead.
|
||||
+ */
|
||||
+export async function manipulateAsync(
|
||||
+ uri: string,
|
||||
+ actions: Action[] = [],
|
||||
+ saveOptions: SaveOptions = {}
|
||||
+): Promise<ImageResult> {
|
||||
+ validateArguments(uri, actions, saveOptions);
|
||||
+
|
||||
+ const { format = SaveFormat.JPEG, ...rest } = saveOptions;
|
||||
+ const context = ExpoImageManipulator.manipulate(uri);
|
||||
+
|
||||
+ for (const action of actions) {
|
||||
+ if ('resize' in action) {
|
||||
+ context.resize(action.resize);
|
||||
+ } else if ('rotate' in action) {
|
||||
+ context.rotate(action.rotate);
|
||||
+ } else if ('flip' in action) {
|
||||
+ context.flip(action.flip);
|
||||
+ } else if ('crop' in action) {
|
||||
+ context.crop(action.crop);
|
||||
+ } else if ('extent' in action && context.extent) {
|
||||
+ context.extent(action.extent);
|
||||
+ }
|
||||
+ }
|
||||
+ const image = await context.renderAsync(saveOptions.compress);
|
||||
+ const result = await image.saveAsync({ format, ...rest });
|
||||
+
|
||||
+ // These shared objects will not be used anymore, so free up some memory.
|
||||
+ context.release();
|
||||
+ image.release();
|
||||
+
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+export function useImageManipulator(source: string | SharedRef<'image'>): ImageManipulatorContext {
|
||||
+ return useReleasingSharedObject(() => ExpoImageManipulator.manipulate(source), [source]);
|
||||
+}
|
||||
+
|
||||
+export { ExpoImageManipulator as ImageManipulator };
|
||||
diff --git a/node_modules/expo-image-manipulator/src/ImageManipulatorContext.ts b/node_modules/expo-image-manipulator/src/ImageManipulatorContext.ts
|
||||
index 120d8d3..f8aa49c 100644
|
||||
--- a/node_modules/expo-image-manipulator/src/ImageManipulatorContext.ts
|
||||
+++ b/node_modules/expo-image-manipulator/src/ImageManipulatorContext.ts
|
||||
@@ -52,7 +52,7 @@ export declare class ImageManipulatorContext extends SharedObject {
|
||||
/**
|
||||
* Awaits for all manipulation tasks to finish and resolves with a reference to the resulted native image.
|
||||
*/
|
||||
- renderAsync(): Promise<ImageRef>;
|
||||
+ renderAsync(compress?: number): Promise<ImageRef>;
|
||||
}
|
||||
|
||||
export default ExpoImageManipulator.Context as typeof ImageManipulatorContext;
|
||||
diff --git a/node_modules/expo-image-manipulator/src/web/ImageManipulatorContext.web.ts b/node_modules/expo-image-manipulator/src/web/ImageManipulatorContext.web.ts
|
||||
index 428848c..363a57a 100644
|
||||
--- a/node_modules/expo-image-manipulator/src/web/ImageManipulatorContext.web.ts
|
||||
+++ b/node_modules/expo-image-manipulator/src/web/ImageManipulatorContext.web.ts
|
||||
@@ -41,7 +41,7 @@ export default class ImageManipulatorContext extends SharedObject {
|
||||
return this;
|
||||
}
|
||||
|
||||
- async renderAsync(): Promise<ImageManipulatorImageRef> {
|
||||
+ async renderAsync(compress?: number): Promise<ImageManipulatorImageRef> {
|
||||
const canvas = await this.currentTask;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
@@ -49,7 +49,7 @@ export default class ImageManipulatorContext extends SharedObject {
|
||||
const url = blob ? URL.createObjectURL(blob) : canvas.toDataURL();
|
||||
|
||||
resolve(new ImageManipulatorImageRef(url, canvas.width, canvas.height));
|
||||
- });
|
||||
+ }, typeof compress === 'number' ? 'image/jpeg' : undefined, compress);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
diff --git a/node_modules/expo-media-library/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt b/node_modules/expo-media-library/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt
|
||||
index b928b8f..7175cf6 100644
|
||||
--- a/node_modules/expo-media-library/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt
|
||||
+++ b/node_modules/expo-media-library/android/src/main/java/expo/modules/medialibrary/MediaLibraryModule.kt
|
||||
@@ -99,12 +99,12 @@ class MediaLibraryModule : Module() {
|
||||
}
|
||||
|
||||
AsyncFunction("createAssetAsync") { localUri: String, promise: Promise ->
|
||||
- throwUnlessPermissionsGranted {
|
||||
+ //throwUnlessPermissionsGranted {
|
||||
withModuleScope(promise) {
|
||||
CreateAsset(context, localUri, promise)
|
||||
.execute()
|
||||
}
|
||||
- }
|
||||
+ //}
|
||||
}
|
||||
|
||||
AsyncFunction("addAssetsToAlbumAsync") { assetsId: List<String>, albumId: String, copyToAlbum: Boolean, promise: Promise ->
|
||||
@@ -1,3 +0,0 @@
|
||||
## expo-modules-core Patch
|
||||
|
||||
This patch fixes an issue where bitdrift's API stream gets blocked by the Expo interceptor used to power the devtools
|
||||
@@ -1,15 +0,0 @@
|
||||
diff --git a/node_modules/expo-modules-core/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt b/node_modules/expo-modules-core/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt
|
||||
index 47c4d15..afe138d 100644
|
||||
--- a/node_modules/expo-modules-core/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt
|
||||
+++ b/node_modules/expo-modules-core/android/src/main/java/expo/modules/kotlin/devtools/ExpoNetworkInspectOkHttpInterceptors.kt
|
||||
@@ -125,6 +125,10 @@ internal fun peekResponseBody(
|
||||
}
|
||||
|
||||
internal fun shouldParseBody(response: Response): Boolean {
|
||||
+ if (response.request.url.encodedPath == "/bitdrift_public.protobuf.client.v1.ApiService/Mux") {
|
||||
+ return false
|
||||
+ }
|
||||
+
|
||||
// Check for Content-Type
|
||||
val skipContentTypes = listOf(
|
||||
"text/event-stream", // Server Sent Events
|
||||
@@ -222,7 +222,7 @@ index 40aaf9c..1c60164 100644
|
||||
{
|
||||
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
||||
diff --git a/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m b/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m
|
||||
index e9ce48c..84a6fca 100644
|
||||
index e9ce48c..ccd9ad6 100644
|
||||
--- a/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m
|
||||
+++ b/node_modules/react-native/React/Views/ScrollView/RCTScrollView.m
|
||||
@@ -159,26 +159,8 @@ - (BOOL)touchesShouldCancelInContentView:(__unused UIView *)view
|
||||
@@ -329,38 +329,3 @@ index e9ce48c..84a6fca 100644
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1055,6 +1082,22 @@ -(type)getter \
|
||||
RCT_SET_AND_PRESERVE_OFFSET(setShowsVerticalScrollIndicator, showsVerticalScrollIndicator, BOOL)
|
||||
RCT_SET_AND_PRESERVE_OFFSET(setZoomScale, zoomScale, CGFloat);
|
||||
|
||||
+- (void)setScrollIndicatorInsets:(UIEdgeInsets)value
|
||||
+{
|
||||
+ [_scrollView setScrollIndicatorInsets:value];
|
||||
+}
|
||||
+
|
||||
+- (UIEdgeInsets)scrollIndicatorInsets
|
||||
+{
|
||||
+ UIEdgeInsets verticalScrollIndicatorInsets = [_scrollView verticalScrollIndicatorInsets];
|
||||
+ UIEdgeInsets horizontalScrollIndicatorInsets = [_scrollView horizontalScrollIndicatorInsets];
|
||||
+ return UIEdgeInsetsMake(
|
||||
+ verticalScrollIndicatorInsets.top,
|
||||
+ horizontalScrollIndicatorInsets.left,
|
||||
+ verticalScrollIndicatorInsets.bottom,
|
||||
+ horizontalScrollIndicatorInsets.right);
|
||||
+}
|
||||
+
|
||||
- (void)setAutomaticallyAdjustsScrollIndicatorInsets:(BOOL)automaticallyAdjusts API_AVAILABLE(ios(13.0))
|
||||
{
|
||||
// `automaticallyAdjustsScrollIndicatorInsets` is available since iOS 13.
|
||||
diff --git a/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m b/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m
|
||||
index cd1e7eb..c1d0172 100644
|
||||
--- a/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m
|
||||
+++ b/node_modules/react-native/React/Views/ScrollView/RCTScrollViewManager.m
|
||||
@@ -83,6 +83,7 @@ - (UIView *)view
|
||||
RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval)
|
||||
RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
|
||||
+RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets)
|
||||
RCT_EXPORT_VIEW_PROPERTY(verticalScrollIndicatorInsets, UIEdgeInsets)
|
||||
RCT_EXPORT_VIEW_PROPERTY(scrollToOverflowEnabled, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int)
|
||||
@@ -1,36 +0,0 @@
|
||||
diff --git a/node_modules/react-native-gesture-handler/apple/RNGestureHandler.mm b/node_modules/react-native-gesture-handler/apple/RNGestureHandler.mm
|
||||
index 43d11b4..5bf0d32 100644
|
||||
--- a/node_modules/react-native-gesture-handler/apple/RNGestureHandler.mm
|
||||
+++ b/node_modules/react-native-gesture-handler/apple/RNGestureHandler.mm
|
||||
@@ -461,16 +461,23 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
|
||||
shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
|
||||
{
|
||||
- if ([_handlersToWaitFor count]) {
|
||||
- RNGestureHandler *handler = [RNGestureHandler findGestureHandlerByRecognizer:otherGestureRecognizer];
|
||||
- if (handler != nil) {
|
||||
- for (NSNumber *handlerTag in _handlersToWaitFor) {
|
||||
- if ([handler.tag isEqual:handlerTag]) {
|
||||
- return YES;
|
||||
- }
|
||||
- }
|
||||
+ RNGestureHandler *handler = [RNGestureHandler findGestureHandlerByRecognizer:otherGestureRecognizer];
|
||||
+ if (handler == nil) {
|
||||
+ return NO;
|
||||
+ }
|
||||
+
|
||||
+ for (NSNumber *handlerTag in _handlersToWaitFor) {
|
||||
+ if ([handler.tag isEqual:handlerTag]) {
|
||||
+ return YES;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ for (NSNumber *handlerTag in handler->_handlersThatShouldWait) {
|
||||
+ if ([_tag isEqual:handlerTag]) {
|
||||
+ return YES;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
diff --git a/node_modules/react-native-image-crop-picker/android/src/main/AndroidManifest.xml b/node_modules/react-native-image-crop-picker/android/src/main/AndroidManifest.xml
|
||||
index 391f303..8e2c3db 100644
|
||||
--- a/node_modules/react-native-image-crop-picker/android/src/main/AndroidManifest.xml
|
||||
+++ b/node_modules/react-native-image-crop-picker/android/src/main/AndroidManifest.xml
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<activity
|
||||
android:name="com.yalantis.ucrop.UCropActivity"
|
||||
- android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
+ android:theme="@style/Theme.UCropNoEdgeToEdge" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
diff --git a/node_modules/react-native-image-crop-picker/android/src/main/res/values-v35/styles.xml b/node_modules/react-native-image-crop-picker/android/src/main/res/values-v35/styles.xml
|
||||
new file mode 100644
|
||||
index 0000000..396d5a8
|
||||
--- /dev/null
|
||||
+++ b/node_modules/react-native-image-crop-picker/android/src/main/res/values-v35/styles.xml
|
||||
@@ -0,0 +1,5 @@
|
||||
+<resources>
|
||||
+ <style name="Theme.UCropNoEdgeToEdge" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
|
||||
+ </style>
|
||||
+</resources>
|
||||
diff --git a/node_modules/react-native-image-crop-picker/android/src/main/res/values/styles.xml b/node_modules/react-native-image-crop-picker/android/src/main/res/values/styles.xml
|
||||
new file mode 100644
|
||||
index 0000000..50129b6
|
||||
--- /dev/null
|
||||
+++ b/node_modules/react-native-image-crop-picker/android/src/main/res/values/styles.xml
|
||||
@@ -0,0 +1,3 @@
|
||||
+<resources>
|
||||
+ <style name="Theme.UCropNoEdgeToEdge" parent="Theme.AppCompat.Light.NoActionBar"/>
|
||||
+</resources>
|
||||
diff --git a/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m b/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m
|
||||
index 9f20973..68d4766 100644
|
||||
--- a/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m
|
||||
+++ b/node_modules/react-native-image-crop-picker/ios/src/ImageCropPicker.m
|
||||
@@ -126,7 +126,8 @@ - (void) setConfiguration:(NSDictionary *)options
|
||||
|
||||
- (UIViewController*) getRootVC {
|
||||
UIViewController *root = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
|
||||
- while (root.presentedViewController != nil) {
|
||||
+ while (root.presentedViewController != nil &&
|
||||
+ !root.presentedViewController.isBeingDismissed) {
|
||||
root = root.presentedViewController;
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
const {withProjectBuildGradle} = require('@expo/config-plugins')
|
||||
|
||||
const jitpackRepository = "maven { url 'https://www.jitpack.io' }"
|
||||
|
||||
module.exports = function withAndroidNoJitpackPlugin(config) {
|
||||
return withProjectBuildGradle(config, config => {
|
||||
if (!config.modResults.contents.includes(jitpackRepository)) {
|
||||
throw Error(
|
||||
'Expected to find the jitpack string in the config. ' +
|
||||
'You MUST verify whether it was actually removed upstream, ' +
|
||||
'or if the format has changed and this plugin no longer recognizes it.',
|
||||
)
|
||||
}
|
||||
config.modResults.contents = config.modResults.contents.replaceAll(
|
||||
jitpackRepository,
|
||||
'',
|
||||
)
|
||||
return config
|
||||
})
|
||||
}
|
||||
+58
-68
@@ -1,6 +1,5 @@
|
||||
import 'react-native-url-polyfill/auto'
|
||||
import '#/lib/sentry' // must be near top
|
||||
import '#/lib/bitdrift' // must be near top
|
||||
import '#/view/icons'
|
||||
|
||||
import React, {useEffect, useState} from 'react'
|
||||
@@ -10,7 +9,6 @@ import {
|
||||
initialWindowMetrics,
|
||||
SafeAreaProvider,
|
||||
} from 'react-native-safe-area-context'
|
||||
import * as ScreenOrientation from 'expo-screen-orientation'
|
||||
import * as SplashScreen from 'expo-splash-screen'
|
||||
import * as SystemUI from 'expo-system-ui'
|
||||
import {msg} from '@lingui/macro'
|
||||
@@ -18,12 +16,16 @@ import {useLingui} from '@lingui/react'
|
||||
|
||||
import {KeyboardControllerProvider} from '#/lib/hooks/useEnableKeyboardController'
|
||||
import {QueryProvider} from '#/lib/react-query'
|
||||
import {Provider as StatsigProvider, tryFetchGates} from '#/lib/statsig/statsig'
|
||||
import {
|
||||
initialize,
|
||||
Provider as StatsigProvider,
|
||||
tryFetchGates,
|
||||
} from '#/lib/statsig/statsig'
|
||||
import {s} from '#/lib/styles'
|
||||
import {ThemeProvider} from '#/lib/ThemeContext'
|
||||
import I18nProvider from '#/locale/i18nProvider'
|
||||
import {logger} from '#/logger'
|
||||
import {isAndroid, isIOS} from '#/platform/detection'
|
||||
import {isIOS} from '#/platform/detection'
|
||||
import {Provider as A11yProvider} from '#/state/a11y'
|
||||
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
|
||||
import {Provider as DialogStateProvider} from '#/state/dialogs'
|
||||
@@ -33,7 +35,6 @@ import {
|
||||
ensureGeolocationResolved,
|
||||
Provider as GeolocationProvider,
|
||||
} from '#/state/geolocation'
|
||||
import {Provider as HomeBadgeProvider} from '#/state/home-badge'
|
||||
import {Provider as InvitesStateProvider} from '#/state/invites'
|
||||
import {Provider as LightboxStateProvider} from '#/state/lightbox'
|
||||
import {MessagesProvider} from '#/state/messages'
|
||||
@@ -52,13 +53,11 @@ import {
|
||||
import {readLastActiveAccount} from '#/state/session/util'
|
||||
import {Provider as ShellStateProvider} from '#/state/shell'
|
||||
import {Provider as ComposerProvider} from '#/state/shell/composer'
|
||||
import {Provider as LightStatusBarProvider} from '#/state/shell/light-status-bar'
|
||||
import {Provider as LoggedOutViewProvider} from '#/state/shell/logged-out'
|
||||
import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
|
||||
import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
|
||||
import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
|
||||
import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies'
|
||||
import {Provider as TrendingConfigProvider} from '#/state/trending-config'
|
||||
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'
|
||||
@@ -69,21 +68,15 @@ import {NuxDialogs} from '#/components/dialogs/nuxs'
|
||||
import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry'
|
||||
import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialogs'
|
||||
import {Provider as PortalProvider} from '#/components/Portal'
|
||||
import * as env from '#/env'
|
||||
import {AppProfiler} from '#/AppProfiler'
|
||||
import {Splash} from '#/Splash'
|
||||
import {BottomSheetProvider} from '../modules/bottom-sheet'
|
||||
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
|
||||
|
||||
logger.info('env', env)
|
||||
|
||||
SplashScreen.preventAutoHideAsync()
|
||||
if (isIOS) {
|
||||
SystemUI.setBackgroundColorAsync('black')
|
||||
}
|
||||
if (isAndroid) {
|
||||
// iOS is handled by the config plugin -sfn
|
||||
ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP)
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin geolocation ASAP
|
||||
@@ -106,6 +99,7 @@ function InnerApp() {
|
||||
if (account) {
|
||||
await resumeSession(account)
|
||||
} else {
|
||||
await initialize()
|
||||
await tryFetchGates(undefined, 'prefer-fresh-gates')
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -146,26 +140,20 @@ function InnerApp() {
|
||||
<LoggedOutViewProvider>
|
||||
<SelectedFeedProvider>
|
||||
<HiddenRepliesProvider>
|
||||
<HomeBadgeProvider>
|
||||
<UnreadNotifsProvider>
|
||||
<BackgroundNotificationPreferencesProvider>
|
||||
<MutedThreadsProvider>
|
||||
<ProgressGuideProvider>
|
||||
<TrendingConfigProvider>
|
||||
<GestureHandlerRootView
|
||||
style={s.h100pct}>
|
||||
<IntentDialogProvider>
|
||||
<TestCtrls />
|
||||
<Shell />
|
||||
<NuxDialogs />
|
||||
</IntentDialogProvider>
|
||||
</GestureHandlerRootView>
|
||||
</TrendingConfigProvider>
|
||||
</ProgressGuideProvider>
|
||||
</MutedThreadsProvider>
|
||||
</BackgroundNotificationPreferencesProvider>
|
||||
</UnreadNotifsProvider>
|
||||
</HomeBadgeProvider>
|
||||
<UnreadNotifsProvider>
|
||||
<BackgroundNotificationPreferencesProvider>
|
||||
<MutedThreadsProvider>
|
||||
<ProgressGuideProvider>
|
||||
<GestureHandlerRootView
|
||||
style={s.h100pct}>
|
||||
<TestCtrls />
|
||||
<Shell />
|
||||
<NuxDialogs />
|
||||
</GestureHandlerRootView>
|
||||
</ProgressGuideProvider>
|
||||
</MutedThreadsProvider>
|
||||
</BackgroundNotificationPreferencesProvider>
|
||||
</UnreadNotifsProvider>
|
||||
</HiddenRepliesProvider>
|
||||
</SelectedFeedProvider>
|
||||
</LoggedOutViewProvider>
|
||||
@@ -202,40 +190,42 @@ function App() {
|
||||
* that is set up in the InnerApp component above.
|
||||
*/
|
||||
return (
|
||||
<GeolocationProvider>
|
||||
<A11yProvider>
|
||||
<KeyboardControllerProvider>
|
||||
<SessionProvider>
|
||||
<PrefsStateProvider>
|
||||
<I18nProvider>
|
||||
<ShellStateProvider>
|
||||
<InvitesStateProvider>
|
||||
<ModalStateProvider>
|
||||
<DialogStateProvider>
|
||||
<LightboxStateProvider>
|
||||
<PortalProvider>
|
||||
<BottomSheetProvider>
|
||||
<StarterPackProvider>
|
||||
<SafeAreaProvider
|
||||
initialMetrics={initialWindowMetrics}>
|
||||
<LightStatusBarProvider>
|
||||
<InnerApp />
|
||||
</LightStatusBarProvider>
|
||||
</SafeAreaProvider>
|
||||
</StarterPackProvider>
|
||||
</BottomSheetProvider>
|
||||
</PortalProvider>
|
||||
</LightboxStateProvider>
|
||||
</DialogStateProvider>
|
||||
</ModalStateProvider>
|
||||
</InvitesStateProvider>
|
||||
</ShellStateProvider>
|
||||
</I18nProvider>
|
||||
</PrefsStateProvider>
|
||||
</SessionProvider>
|
||||
</KeyboardControllerProvider>
|
||||
</A11yProvider>
|
||||
</GeolocationProvider>
|
||||
<AppProfiler>
|
||||
<GeolocationProvider>
|
||||
<A11yProvider>
|
||||
<KeyboardControllerProvider>
|
||||
<SessionProvider>
|
||||
<PrefsStateProvider>
|
||||
<I18nProvider>
|
||||
<ShellStateProvider>
|
||||
<InvitesStateProvider>
|
||||
<ModalStateProvider>
|
||||
<DialogStateProvider>
|
||||
<LightboxStateProvider>
|
||||
<PortalProvider>
|
||||
<BottomSheetProvider>
|
||||
<StarterPackProvider>
|
||||
<SafeAreaProvider
|
||||
initialMetrics={initialWindowMetrics}>
|
||||
<IntentDialogProvider>
|
||||
<InnerApp />
|
||||
</IntentDialogProvider>
|
||||
</SafeAreaProvider>
|
||||
</StarterPackProvider>
|
||||
</BottomSheetProvider>
|
||||
</PortalProvider>
|
||||
</LightboxStateProvider>
|
||||
</DialogStateProvider>
|
||||
</ModalStateProvider>
|
||||
</InvitesStateProvider>
|
||||
</ShellStateProvider>
|
||||
</I18nProvider>
|
||||
</PrefsStateProvider>
|
||||
</SessionProvider>
|
||||
</KeyboardControllerProvider>
|
||||
</A11yProvider>
|
||||
</GeolocationProvider>
|
||||
</AppProfiler>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+14
-23
@@ -22,7 +22,6 @@ import {
|
||||
ensureGeolocationResolved,
|
||||
Provider as GeolocationProvider,
|
||||
} from '#/state/geolocation'
|
||||
import {Provider as HomeBadgeProvider} from '#/state/home-badge'
|
||||
import {Provider as InvitesStateProvider} from '#/state/invites'
|
||||
import {Provider as LightboxStateProvider} from '#/state/lightbox'
|
||||
import {MessagesProvider} from '#/state/messages'
|
||||
@@ -41,13 +40,11 @@ import {
|
||||
import {readLastActiveAccount} from '#/state/session/util'
|
||||
import {Provider as ShellStateProvider} from '#/state/shell'
|
||||
import {Provider as ComposerProvider} from '#/state/shell/composer'
|
||||
import {Provider as LightStatusBarProvider} from '#/state/shell/light-status-bar'
|
||||
import {Provider as LoggedOutViewProvider} from '#/state/shell/logged-out'
|
||||
import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
|
||||
import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
|
||||
import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
|
||||
import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies'
|
||||
import {Provider as TrendingConfigProvider} from '#/state/trending-config'
|
||||
import {Provider as ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoWebContext'
|
||||
import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
@@ -122,24 +119,18 @@ function InnerApp() {
|
||||
<LoggedOutViewProvider>
|
||||
<SelectedFeedProvider>
|
||||
<HiddenRepliesProvider>
|
||||
<HomeBadgeProvider>
|
||||
<UnreadNotifsProvider>
|
||||
<BackgroundNotificationPreferencesProvider>
|
||||
<MutedThreadsProvider>
|
||||
<SafeAreaProvider>
|
||||
<ProgressGuideProvider>
|
||||
<TrendingConfigProvider>
|
||||
<IntentDialogProvider>
|
||||
<Shell />
|
||||
<NuxDialogs />
|
||||
</IntentDialogProvider>
|
||||
</TrendingConfigProvider>
|
||||
</ProgressGuideProvider>
|
||||
</SafeAreaProvider>
|
||||
</MutedThreadsProvider>
|
||||
</BackgroundNotificationPreferencesProvider>
|
||||
</UnreadNotifsProvider>
|
||||
</HomeBadgeProvider>
|
||||
<UnreadNotifsProvider>
|
||||
<BackgroundNotificationPreferencesProvider>
|
||||
<MutedThreadsProvider>
|
||||
<SafeAreaProvider>
|
||||
<ProgressGuideProvider>
|
||||
<Shell />
|
||||
<NuxDialogs />
|
||||
</ProgressGuideProvider>
|
||||
</SafeAreaProvider>
|
||||
</MutedThreadsProvider>
|
||||
</BackgroundNotificationPreferencesProvider>
|
||||
</UnreadNotifsProvider>
|
||||
</HiddenRepliesProvider>
|
||||
</SelectedFeedProvider>
|
||||
</LoggedOutViewProvider>
|
||||
@@ -189,9 +180,9 @@ function App() {
|
||||
<LightboxStateProvider>
|
||||
<PortalProvider>
|
||||
<StarterPackProvider>
|
||||
<LightStatusBarProvider>
|
||||
<IntentDialogProvider>
|
||||
<InnerApp />
|
||||
</LightStatusBarProvider>
|
||||
</IntentDialogProvider>
|
||||
</StarterPackProvider>
|
||||
</PortalProvider>
|
||||
</LightboxStateProvider>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import React, {Profiler} from 'react'
|
||||
|
||||
// Don't let it get stripped out in profiling builds (which apply production Babel preset).
|
||||
const log = (global as any)['con' + 'sole'].log
|
||||
|
||||
function onRender(id: string, phase: string, actualDuration: number) {
|
||||
if (!__DEV__) {
|
||||
// This block of code will exist in the production build of the app.
|
||||
// However, only profiling builds of React call `onRender` so it's dead code in actual production.
|
||||
const message = `<Profiler> ${id}:${phase} ${
|
||||
actualDuration > 500
|
||||
? '(╯°□°)╯ '
|
||||
: actualDuration > 100
|
||||
? '[!!] '
|
||||
: actualDuration > 16
|
||||
? '[!] '
|
||||
: ''
|
||||
}${Math.round(actualDuration)}ms`
|
||||
log(message)
|
||||
}
|
||||
}
|
||||
|
||||
export function AppProfiler({children}: {children: React.ReactNode}) {
|
||||
return (
|
||||
<Profiler id="app" onRender={onRender}>
|
||||
{children}
|
||||
</Profiler>
|
||||
)
|
||||
}
|
||||
+3
-27
@@ -55,7 +55,10 @@ import {NotificationsScreen} from '#/view/screens/Notifications'
|
||||
import {PostThreadScreen} from '#/view/screens/PostThread'
|
||||
import {PrivacyPolicyScreen} from '#/view/screens/PrivacyPolicy'
|
||||
import {ProfileScreen} from '#/view/screens/Profile'
|
||||
import {ProfileFeedScreen} from '#/view/screens/ProfileFeed'
|
||||
import {ProfileFeedLikedByScreen} from '#/view/screens/ProfileFeedLikedBy'
|
||||
import {ProfileFollowersScreen} from '#/view/screens/ProfileFollowers'
|
||||
import {ProfileFollowsScreen} from '#/view/screens/ProfileFollows'
|
||||
import {ProfileListScreen} from '#/view/screens/ProfileList'
|
||||
import {SavedFeeds} from '#/view/screens/SavedFeeds'
|
||||
import {SearchScreen} from '#/view/screens/Search'
|
||||
@@ -70,14 +73,10 @@ import {MessagesScreen} from '#/screens/Messages/ChatList'
|
||||
import {MessagesConversationScreen} from '#/screens/Messages/Conversation'
|
||||
import {MessagesSettingsScreen} from '#/screens/Messages/Settings'
|
||||
import {ModerationScreen} from '#/screens/Moderation'
|
||||
import {Screen as ModerationInteractionSettings} from '#/screens/ModerationInteractionSettings'
|
||||
import {PostLikedByScreen} from '#/screens/Post/PostLikedBy'
|
||||
import {PostQuotesScreen} from '#/screens/Post/PostQuotes'
|
||||
import {PostRepostedByScreen} from '#/screens/Post/PostRepostedBy'
|
||||
import {ProfileKnownFollowersScreen} from '#/screens/Profile/KnownFollowers'
|
||||
import {ProfileFeedScreen} from '#/screens/Profile/ProfileFeed'
|
||||
import {ProfileFollowersScreen} from '#/screens/Profile/ProfileFollowers'
|
||||
import {ProfileFollowsScreen} from '#/screens/Profile/ProfileFollows'
|
||||
import {ProfileLabelerLikedByScreen} from '#/screens/Profile/ProfileLabelerLikedBy'
|
||||
import {AppearanceSettingsScreen} from '#/screens/Settings/AppearanceSettings'
|
||||
import {AppIconSettingsScreen} from '#/screens/Settings/AppIconSettings'
|
||||
@@ -87,7 +86,6 @@ import {
|
||||
StarterPackScreenShort,
|
||||
} from '#/screens/StarterPack/StarterPackScreen'
|
||||
import {Wizard} from '#/screens/StarterPack/Wizard'
|
||||
import {VideoFeed} from '#/screens/VideoFeed'
|
||||
import {useTheme} from '#/alf'
|
||||
import {router} from '#/routes'
|
||||
import {Referrer} from '../modules/expo-bluesky-swiss-army'
|
||||
@@ -102,7 +100,6 @@ import {LanguageSettingsScreen} from './screens/Settings/LanguageSettings'
|
||||
import {PrivacyAndSecuritySettingsScreen} from './screens/Settings/PrivacyAndSecuritySettings'
|
||||
import {SettingsScreen} from './screens/Settings/Settings'
|
||||
import {ThreadPreferencesScreen} from './screens/Settings/ThreadPreferences'
|
||||
import TopicScreen from './screens/Topic'
|
||||
|
||||
const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
|
||||
|
||||
@@ -156,14 +153,6 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
|
||||
getComponent={() => ModerationBlockedAccounts}
|
||||
options={{title: title(msg`Blocked Accounts`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ModerationInteractionSettings"
|
||||
getComponent={() => ModerationInteractionSettings}
|
||||
options={{
|
||||
title: title(msg`Post Interaction Settings`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Settings"
|
||||
getComponent={() => SettingsScreen}
|
||||
@@ -387,11 +376,6 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
|
||||
getComponent={() => HashtagScreen}
|
||||
options={{title: title(msg`Hashtag`)}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Topic"
|
||||
getComponent={() => TopicScreen}
|
||||
options={{title: title(msg`Topic`)}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="MessagesConversation"
|
||||
getComponent={() => MessagesConversationScreen}
|
||||
@@ -432,14 +416,6 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
|
||||
getComponent={() => Wizard}
|
||||
options={{title: title(msg`Edit your starter pack`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="VideoFeed"
|
||||
getComponent={() => VideoFeed}
|
||||
options={{
|
||||
title: title(msg`Video Feed`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
+2
-47
@@ -68,6 +68,7 @@ export const atoms = {
|
||||
* Used for the outermost components on screens, to ensure that they can fill
|
||||
* the screen and extend beyond.
|
||||
*/
|
||||
// @ts-ignore - web only minHeight string
|
||||
util_screen_outer: [
|
||||
web({
|
||||
minHeight: '100vh',
|
||||
@@ -75,7 +76,7 @@ export const atoms = {
|
||||
native({
|
||||
height: '100%',
|
||||
}),
|
||||
] as StyleProp<ViewStyle>,
|
||||
] as ViewStyle,
|
||||
|
||||
/*
|
||||
* Theme-independent bg colors
|
||||
@@ -157,9 +158,6 @@ export const atoms = {
|
||||
flex_wrap: {
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
flex_nowrap: {
|
||||
flexWrap: 'nowrap',
|
||||
},
|
||||
flex_0: {
|
||||
flex: web('0 0 auto') || (native(0) as number),
|
||||
},
|
||||
@@ -304,18 +302,6 @@ export const atoms = {
|
||||
border_0: {
|
||||
borderWidth: 0,
|
||||
},
|
||||
border_t_0: {
|
||||
borderTopWidth: 0,
|
||||
},
|
||||
border_b_0: {
|
||||
borderBottomWidth: 0,
|
||||
},
|
||||
border_l_0: {
|
||||
borderLeftWidth: 0,
|
||||
},
|
||||
border_r_0: {
|
||||
borderRightWidth: 0,
|
||||
},
|
||||
border: {
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
},
|
||||
@@ -960,37 +946,6 @@ export const atoms = {
|
||||
transitionTimingFunction: 'cubic-bezier(0.17, 0.73, 0.14, 1)',
|
||||
transitionDuration: '100ms',
|
||||
}),
|
||||
transition_delay_50ms: web({
|
||||
transitionDelay: '50ms',
|
||||
}),
|
||||
|
||||
/*
|
||||
* Animaations
|
||||
*/
|
||||
fade_in: web({
|
||||
animation: 'fadeIn ease-out 0.15s',
|
||||
}),
|
||||
fade_out: web({
|
||||
animation: 'fadeOut ease-out 0.15s',
|
||||
}),
|
||||
zoom_in: web({
|
||||
animation: 'zoomIn ease-out 0.1s',
|
||||
}),
|
||||
zoom_out: web({
|
||||
animation: 'zoomOut ease-out 0.1s',
|
||||
}),
|
||||
slide_in_left: web({
|
||||
// exponential easing function
|
||||
animation: 'slideInLeft cubic-bezier(0.16, 1, 0.3, 1) 0.5s',
|
||||
}),
|
||||
slide_out_left: web({
|
||||
animation: 'slideOutLeft ease-in 0.15s',
|
||||
animationFillMode: 'forwards',
|
||||
}),
|
||||
// special composite animation for dialogs
|
||||
zoom_fade_in: web({
|
||||
animation: 'zoomIn ease-out 0.1s, fadeIn ease-out 0.1s',
|
||||
}),
|
||||
|
||||
/**
|
||||
* {@link Layout.SCROLLBAR_OFFSET}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import {useMemo} from 'react'
|
||||
import {useMediaQuery} from 'react-responsive'
|
||||
|
||||
export type Breakpoint = 'gtPhone' | 'gtMobile' | 'gtTablet'
|
||||
|
||||
export function useBreakpoints(): Record<Breakpoint, boolean> & {
|
||||
activeBreakpoint: Breakpoint | undefined
|
||||
} {
|
||||
const gtPhone = useMediaQuery({minWidth: 500})
|
||||
const gtMobile = useMediaQuery({minWidth: 800})
|
||||
const gtTablet = useMediaQuery({minWidth: 1300})
|
||||
return useMemo(() => {
|
||||
let active: Breakpoint | undefined
|
||||
if (gtTablet) {
|
||||
active = 'gtTablet'
|
||||
} else if (gtMobile) {
|
||||
active = 'gtMobile'
|
||||
} else if (gtPhone) {
|
||||
active = 'gtPhone'
|
||||
}
|
||||
return {
|
||||
activeBreakpoint: active,
|
||||
gtPhone,
|
||||
gtMobile,
|
||||
gtTablet,
|
||||
}
|
||||
}, [gtPhone, gtMobile, gtTablet])
|
||||
}
|
||||
+13
-2
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import {useMediaQuery} from 'react-responsive'
|
||||
|
||||
import {
|
||||
computeFontScaleMultiplier,
|
||||
@@ -13,14 +14,13 @@ import {BLUE_HUE, GREEN_HUE, RED_HUE} from '#/alf/util/colorGeneration'
|
||||
import {Device} from '#/storage'
|
||||
|
||||
export {atoms} from '#/alf/atoms'
|
||||
export * from '#/alf/breakpoints'
|
||||
export * from '#/alf/fonts'
|
||||
export * as tokens from '#/alf/tokens'
|
||||
export * from '#/alf/types'
|
||||
export * from '#/alf/util/flatten'
|
||||
export * from '#/alf/util/platform'
|
||||
export * from '#/alf/util/themeSelector'
|
||||
export * from '#/alf/util/useGutters'
|
||||
export * from '#/alf/util/useGutterStyles'
|
||||
|
||||
export type Alf = {
|
||||
themeName: ThemeName
|
||||
@@ -142,3 +142,14 @@ export function useTheme(theme?: ThemeName) {
|
||||
return theme ? alf.themes[theme] : alf.theme
|
||||
}, [theme, alf])
|
||||
}
|
||||
|
||||
export function useBreakpoints() {
|
||||
const gtPhone = useMediaQuery({minWidth: 500})
|
||||
const gtMobile = useMediaQuery({minWidth: 800})
|
||||
const gtTablet = useMediaQuery({minWidth: 1300})
|
||||
return {
|
||||
gtPhone,
|
||||
gtMobile,
|
||||
gtTablet,
|
||||
}
|
||||
}
|
||||
|
||||
+1
-5
@@ -60,7 +60,6 @@ export function createThemes({
|
||||
dim: Theme
|
||||
} {
|
||||
const color = {
|
||||
like: '#ec4899',
|
||||
trueBlack: '#000000',
|
||||
|
||||
gray_0: `hsl(${hues.primary}, 20%, ${defaultScale[14]}%)`,
|
||||
@@ -125,7 +124,6 @@ export function createThemes({
|
||||
const lightPalette = {
|
||||
white: color.gray_0,
|
||||
black: color.gray_1000,
|
||||
like: color.like,
|
||||
|
||||
contrast_25: color.gray_25,
|
||||
contrast_50: color.gray_50,
|
||||
@@ -187,7 +185,6 @@ export function createThemes({
|
||||
const darkPalette: Palette = {
|
||||
white: color.gray_25,
|
||||
black: color.trueBlack,
|
||||
like: color.like,
|
||||
|
||||
contrast_25: color.gray_975,
|
||||
contrast_50: color.gray_950,
|
||||
@@ -249,7 +246,6 @@ export function createThemes({
|
||||
const dimPalette: Palette = {
|
||||
...darkPalette,
|
||||
black: `hsl(${hues.primary}, 28%, ${dimScale[0]}%)`,
|
||||
like: color.like,
|
||||
|
||||
contrast_25: `hsl(${hues.primary}, 28%, ${dimScale[1]}%)`,
|
||||
contrast_50: `hsl(${hues.primary}, 28%, ${dimScale[2]}%)`,
|
||||
@@ -497,7 +493,7 @@ export function createThemes({
|
||||
color: dimPalette.contrast_400,
|
||||
},
|
||||
text_contrast_medium: {
|
||||
color: dimPalette.contrast_600,
|
||||
color: dimPalette.contrast_700,
|
||||
},
|
||||
text_contrast_high: {
|
||||
color: dimPalette.contrast_900,
|
||||
|
||||
@@ -12,7 +12,6 @@ export type ThemeName = 'light' | 'dim' | 'dark'
|
||||
export type Palette = {
|
||||
white: string
|
||||
black: string
|
||||
like: string
|
||||
|
||||
contrast_25: string
|
||||
contrast_50: string
|
||||
|
||||
@@ -110,7 +110,9 @@ export function renderChildrenWithEmoji(
|
||||
return child.split(EMOJI).map((stringPart, index) => [
|
||||
stringPart,
|
||||
emojis[index] ? (
|
||||
<UITextView {...props} style={[props?.style, {fontFamily: 'System'}]}>
|
||||
<UITextView
|
||||
{...props}
|
||||
style={[props?.style, {color: 'black', fontFamily: 'System'}]}>
|
||||
{emojis[index]}
|
||||
</UITextView>
|
||||
) : null,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as NavigationBar from 'expo-navigation-bar'
|
||||
import * as SystemUI from 'expo-system-ui'
|
||||
|
||||
import {isAndroid} from '#/platform/detection'
|
||||
import {Theme} from '../types'
|
||||
@@ -10,12 +9,10 @@ export function setNavigationBar(themeType: 'theme' | 'lightbox', t: Theme) {
|
||||
NavigationBar.setBackgroundColorAsync(t.atoms.bg.backgroundColor)
|
||||
NavigationBar.setBorderColorAsync(t.atoms.bg.backgroundColor)
|
||||
NavigationBar.setButtonStyleAsync(t.name !== 'light' ? 'light' : 'dark')
|
||||
SystemUI.setBackgroundColorAsync(t.atoms.bg.backgroundColor)
|
||||
} else {
|
||||
NavigationBar.setBackgroundColorAsync('black')
|
||||
NavigationBar.setBorderColorAsync('black')
|
||||
NavigationBar.setButtonStyleAsync('light')
|
||||
SystemUI.setBackgroundColorAsync('black')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {useFonts} from 'expo-font'
|
||||
*/
|
||||
export function DO_NOT_USE() {
|
||||
return useFonts({
|
||||
InterVariable: require('../../../assets/fonts/inter/InterVariable.woff2'),
|
||||
'InterVariable-Italic': require('../../../assets/fonts/inter/InterVariable-Italic.woff2'),
|
||||
InterVariable: require('../../../assets/fonts/inter/InterVariable.ttf'),
|
||||
'InterVariable-Italic': require('../../../assets/fonts/inter/InterVariable-Italic.ttf'),
|
||||
})
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user