diff --git a/.env.example b/.env.example index a4d21ac332..e18cda4e55 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,32 @@ -# Copy this to `.env` and `.env.test` files +# The env the app is running in e.g. development, testflight, production +EXPO_PUBLIC_ENV=development -BITDRIFT_API_KEY= -SENTRY_AUTH_TOKEN= -EXPO_PUBLIC_LOG_LEVEL=debug -EXPO_PUBLIC_LOG_DEBUG= +# This is the semver release version of the app, pulled from package.json +EXPO_PUBLIC_RELEASE_VERSION= + +# This is the commit hash that the current bundle was made from. EXPO_PUBLIC_BUNDLE_IDENTIFIER= + +# Should be formatted YYMMDDHH so that it increases for each build. EXPO_PUBLIC_BUNDLE_DATE=0 + +# The log level for the app's logger transports +EXPO_PUBLIC_LOG_LEVEL=debug + +# Enable debug logs for specific logger instances +EXPO_PUBLIC_LOG_DEBUG=session + +# Chat service DID +EXPO_PUBLIC_CHAT_PROXY_DID= + +# +# +# Bluesky specific values +# +# + +# Sentry DSN for telemetry +EXPO_PUBLIC_SENTRY_DSN= + +# Bitdrift API key. If undefined, Bitdrift will be disabled. +EXPO_PUBLIC_BITDRIFT_API_KEY= diff --git a/.github/workflows/build-and-push-bskyweb-aws.yaml b/.github/workflows/build-and-push-bskyweb-aws.yaml index 399e8669f0..6d573b0f71 100644 --- a/.github/workflows/build-and-push-bskyweb-aws.yaml +++ b/.github/workflows/build-and-push-bskyweb-aws.yaml @@ -43,12 +43,11 @@ jobs: tags: | type=sha,enable=true,priority=100,prefix=,suffix=,format=long - - name: Set outputs - id: vars + - name: Env + id: env run: | - echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - echo "SENTRY_DIST=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - echo "SENTRY_RELEASE=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - name: Build and push Docker image id: build-and-push @@ -62,8 +61,8 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max build-args: | - EXPO_PUBLIC_BUNDLE_IDENTIFIER=${{ steps.vars.outputs.sha_short }} - SENTRY_DIST=${{ steps.vars.outputs.SENTRY_DIST }} - SENTRY_RELEASE=${{ steps.vars.outputs.SENTRY_RELEASE }} + EXPO_PUBLIC_ENV=production + EXPO_PUBLIC_RELEASE_VERSION=${{ steps.env.outputs.EXPO_PUBLIC_RELEASE_VERSION }} + EXPO_PUBLIC_BUNDLE_IDENTIFIER=${{ steps.env.outputs.EXPO_PUBLIC_BUNDLE_IDENTIFIER }} + EXPO_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }} SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_DSN=${{ secrets.SENTRY_DSN }} diff --git a/.github/workflows/build-and-push-embedr-aws.yaml b/.github/workflows/build-and-push-embedr-aws.yaml index 23d1538fe5..a6b2d9c3e0 100644 --- a/.github/workflows/build-and-push-embedr-aws.yaml +++ b/.github/workflows/build-and-push-embedr-aws.yaml @@ -3,6 +3,7 @@ on: push: branches: - main + - echoprom_fix env: REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }} diff --git a/.github/workflows/build-submit-android.yml b/.github/workflows/build-submit-android.yml index e862a701fd..f1e75f87e5 100644 --- a/.github/workflows/build-submit-android.yml +++ b/.github/workflows/build-submit-android.yml @@ -62,23 +62,30 @@ jobs: - name: Check for i18n compilation errors run: if grep -q "invalid syntax" "i18n.log"; then echo "\n\nFound compilation errors!\n\n" && exit 1; else echo "\n\nNo compilation errors!\n\n"; fi - - name: ✏️ Write environment variables + # EXPO_PUBLIC_ENV is handled in eas.json + - name: Env + id: env run: | export json='${{ secrets.GOOGLE_SERVICES_TOKEN }}' echo "${{ secrets.ENV_TOKEN }}" > .env - echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse --short HEAD)" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> .env + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env - echo "BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env + echo "EXPO_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env + echo "EXPO_PUBLIC_BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env echo "$json" > google-services.json - - name: Setup Sentry vars for build-time injection - id: sentry - run: | - echo "SENTRY_DIST=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - echo "SENTRY_RELEASE=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT - - name: 🏗️ EAS Build - run: SENTRY_DIST=${{ steps.sentry.outputs.SENTRY_DIST }} SENTRY_RELEASE=${{ steps.sentry.outputs.SENTRY_RELEASE }} SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_DSN=${{ secrets.SENTRY_DSN }} yarn use-build-number-with-bump eas build -p android --profile ${{ inputs.profile || 'testflight-android' }} --local --output build.aab --non-interactive + run: > + SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_RELEASE=${{ steps.env.outputs.EXPO_PUBLIC_RELEASE_VERSION }} + SENTRY_DIST=${{ steps.env.outputs.EXPO_PUBLIC_BUNDLE_IDENTIFIER }} + yarn use-build-number-with-bump + eas build -p android + --profile ${{ inputs.profile || 'testflight-android' }} + --local --output build.aab --non-interactive - name: ✍️ Rename Testflight bundle if: ${{ inputs.profile != 'production' }} @@ -140,7 +147,14 @@ jobs: - name: 🏗️ Build Production APK if: ${{ inputs.profile == 'production' }} - run: SENTRY_DIST=${{ steps.sentry.outputs.SENTRY_DIST }} SENTRY_RELEASE=${{ steps.sentry.outputs.SENTRY_RELEASE }} SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_DSN=${{ secrets.SENTRY_DSN }} yarn use-build-number-with-bump eas build -p android --profile production-apk --local --output build.apk --non-interactive + run: > + SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_RELEASE=${{ steps.env.outputs.EXPO_PUBLIC_RELEASE_VERSION }} + SENTRY_DIST=${{ steps.env.outputs.EXPO_PUBLIC_BUNDLE_IDENTIFIER }} + yarn use-build-number-with-bump + eas build -p android + --profile production-apk + --local --output build.apk --non-interactive - name: 🚀 Upload Production APK Artifact id: upload-artifact-production-apk diff --git a/.github/workflows/build-submit-ios.yml b/.github/workflows/build-submit-ios.yml index a197695db7..f3759d0dd4 100644 --- a/.github/workflows/build-submit-ios.yml +++ b/.github/workflows/build-submit-ios.yml @@ -75,22 +75,29 @@ jobs: - name: Check for i18n compilation errors run: if grep -q "invalid syntax" "i18n.log"; then echo "\n\nFound compilation errors!\n\n" && exit 1; else echo "\n\nNo compilation errors!\n\n"; fi + # EXPO_PUBLIC_ENV is handled in eas.json - name: ✏️ Write environment variables + id: env run: | echo "${{ secrets.ENV_TOKEN }}" > .env - echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse --short HEAD)" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> .env + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env - echo "BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env + echo "EXPO_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env + echo "EXPO_PUBLIC_BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env echo "${{ secrets.GOOGLE_SERVICES_TOKEN }}" > google-services.json - - name: Setup Sentry vars for build-time injection - id: sentry - run: | - echo "SENTRY_DIST=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - echo "SENTRY_RELEASE=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT - - name: 🏗️ EAS Build - run: SENTRY_DIST=${{ steps.sentry.outputs.SENTRY_DIST }} SENTRY_RELEASE=${{ steps.sentry.outputs.SENTRY_RELEASE }} SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_DSN=${{ secrets.SENTRY_DSN }} yarn use-build-number-with-bump eas build -p ios --profile ${{ inputs.profile || 'testflight' }} --local --output build.ipa --non-interactive + run: > + SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_RELEASE=${{ steps.env.outputs.EXPO_PUBLIC_RELEASE_VERSION }} + SENTRY_DIST=${{ steps.env.outputs.EXPO_PUBLIC_BUNDLE_IDENTIFIER }} + yarn use-build-number-with-bump + eas build -p ios + --profile ${{ inputs.profile || 'testflight' }} + --local --output build.ipa --non-interactive - name: 🚀 Deploy run: eas submit -p ios --non-interactive --path build.ipa diff --git a/.github/workflows/bundle-deploy-eas-update.yml b/.github/workflows/bundle-deploy-eas-update.yml index 475bc087f1..1bd0b71c47 100644 --- a/.github/workflows/bundle-deploy-eas-update.yml +++ b/.github/workflows/bundle-deploy-eas-update.yml @@ -101,25 +101,30 @@ jobs: if: ${{ !steps.fingerprint.outputs.includes-changes }} uses: dcarbone/install-jq-action@v2 - - name: ✏️ Write environment variables + # eas.json not used here, set EXPO_PUBLIC_ENV + - name: Env + id: env if: ${{ !steps.fingerprint.outputs.includes-changes }} run: | export json='${{ secrets.GOOGLE_SERVICES_TOKEN }}' echo "${{ secrets.ENV_TOKEN }}" > .env - echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse --short HEAD)" >> .env + echo "EXPO_PUBLIC_ENV=${{ inputs.channel || 'testflight' }}" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> .env + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env - echo "BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env + echo "EXPO_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env + echo "EXPO_PUBLIC_BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env echo "$json" > google-services.json - - name: Setup Sentry vars for build-time injection - id: sentry - run: | - echo "SENTRY_DIST=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - echo "SENTRY_RELEASE=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT - - name: 🏗️ Create Bundle if: ${{ !steps.fingerprint.outputs.includes-changes }} - run: SENTRY_DIST=${{ steps.sentry.outputs.SENTRY_DIST }} SENTRY_RELEASE=${{ steps.sentry.outputs.SENTRY_RELEASE }} SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_DSN=${{ secrets.SENTRY_DSN }} EXPO_PUBLIC_ENV="${{ inputs.channel || 'testflight' }}" yarn export + run: > + SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_RELEASE=${{ steps.env.outputs.EXPO_PUBLIC_RELEASE_VERSION }} + SENTRY_DIST=${{ steps.env.outputs.EXPO_PUBLIC_BUNDLE_IDENTIFIER }} + yarn export - name: 📦 Package Bundle and 🚀 Deploy if: ${{ !steps.fingerprint.outputs.includes-changes }} @@ -205,16 +210,29 @@ jobs: - name: 🔤 Compile translations run: yarn intl:build - - name: ✏️ Write environment variables + # EXPO_PUBLIC_ENV is handled in eas.json + - name: Env + id: env run: | echo "${{ secrets.ENV_TOKEN }}" > .env - echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse --short HEAD)" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> .env + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env - echo "BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env + echo "EXPO_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env + echo "EXPO_PUBLIC_BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env echo "${{ secrets.GOOGLE_SERVICES_TOKEN }}" > google-services.json - name: 🏗️ EAS Build - run: yarn use-build-number-with-bump eas build -p ios --profile testflight --local --output build.ipa --non-interactive + run: > + SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_RELEASE=${{ steps.env.outputs.EXPO_PUBLIC_RELEASE_VERSION }} + SENTRY_DIST=${{ steps.env.outputs.EXPO_PUBLIC_BUNDLE_IDENTIFIER }} + yarn use-build-number-with-bump + eas build -p ios + --profile testflight + --local --output build.ipa --non-interactive - name: 🚀 Deploy run: eas submit -p ios --non-interactive --path build.ipa @@ -282,17 +300,30 @@ jobs: - name: 🔤 Compile translations run: yarn intl:build - - name: ✏️ Write environment variables + # EXPO_PUBLIC_ENV is handled in eas.json + - name: Env + id: env run: | export json='${{ secrets.GOOGLE_SERVICES_TOKEN }}' echo "${{ secrets.ENV_TOKEN }}" > .env - echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse --short HEAD)" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> .env + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env - echo "BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env + echo "EXPO_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env + echo "EXPO_PUBLIC_BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env echo "$json" > google-services.json - name: 🏗️ EAS Build - run: yarn use-build-number-with-bump eas build -p android --profile testflight-android --local --output build.apk --non-interactive + run: > + SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_RELEASE=${{ steps.env.outputs.EXPO_PUBLIC_RELEASE_VERSION }} + SENTRY_DIST=${{ steps.env.outputs.EXPO_PUBLIC_BUNDLE_IDENTIFIER }} + yarn use-build-number-with-bump + eas build -p android + --profile testflight-android + --local --output build.apk --non-interactive - name: ⏰ Get a timestamp id: timestamp diff --git a/.github/workflows/pull-request-comment.yml b/.github/workflows/pull-request-comment.yml index e40eff6c7d..d043010027 100644 --- a/.github/workflows/pull-request-comment.yml +++ b/.github/workflows/pull-request-comment.yml @@ -152,23 +152,27 @@ jobs: - name: 🪛 Setup jq uses: dcarbone/install-jq-action@v2 - - name: ✏️ Write environment variables + - name: Env + id: env run: | export json='${{ secrets.GOOGLE_SERVICES_TOKEN }}' echo "${{ secrets.ENV_TOKEN }}" > .env - echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse --short HEAD)" >> .env + echo "EXPO_PUBLIC_ENV=testflight" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> .env + echo "EXPO_PUBLIC_RELEASE_VERSION=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> .env + echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env - echo "BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env + echo "EXPO_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env + echo "EXPO_PUBLIC_BITDRIFT_API_KEY=${{ secrets.BITDRIFT_API_KEY }}" >> .env echo "$json" > google-services.json - - name: Setup Sentry vars for build-time injection - id: sentry - run: | - echo "SENTRY_DIST=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - echo "SENTRY_RELEASE=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT - - name: 🏗️ Create Bundle - run: SENTRY_DIST=${{ steps.sentry.outputs.SENTRY_DIST }} SENTRY_RELEASE=${{ steps.sentry.outputs.SENTRY_RELEASE }} SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_DSN=${{ secrets.SENTRY_DSN }} EXPO_PUBLIC_ENV="testflight" yarn export + run: > + SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_RELEASE=${{ steps.env.outputs.EXPO_PUBLIC_RELEASE_VERSION }} + SENTRY_DIST=${{ steps.env.outputs.EXPO_PUBLIC_BUNDLE_IDENTIFIER }} + yarn export - name: 📦 Package Bundle and 🚀 Deploy run: yarn use-build-number bash scripts/bundleUpdate.sh diff --git a/Dockerfile b/Dockerfile index 50dc28b4b3..93b17a86b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.23-bullseye AS build-env +FROM golang:1.24.5-bullseye AS build-env WORKDIR /usr/src/social-app @@ -19,28 +19,28 @@ ENV GOARCH="amd64" ENV CGO_ENABLED=1 ENV GOEXPERIMENT="loopvar" +# The latest git hash of the preview branch on render.com +# https://render.com/docs/docker-secrets#environment-variables-in-docker-builds +ARG RENDER_GIT_COMMIT + # # Expo # +ARG EXPO_PUBLIC_ENV +ENV EXPO_PUBLIC_ENV=${EXPO_PUBLIC_ENV:-development} +ARG EXPO_PUBLIC_RELEASE_VERSION +ENV EXPO_PUBLIC_RELEASE_VERSION=$EXPO_PUBLIC_RELEASE_VERSION ARG EXPO_PUBLIC_BUNDLE_IDENTIFIER -ENV EXPO_PUBLIC_BUNDLE_IDENTIFIER=${EXPO_PUBLIC_BUNDLE_IDENTIFIER:-dev} - -# The latest git hash of the preview branch on render.com -ARG RENDER_GIT_COMMIT +# If not set by GitHub workflows, we're probably in Render +ENV EXPO_PUBLIC_BUNDLE_IDENTIFIER=${EXPO_PUBLIC_BUNDLE_IDENTIFIER:-$RENDER_GIT_COMMIT} # # Sentry # ARG SENTRY_AUTH_TOKEN ENV SENTRY_AUTH_TOKEN=${SENTRY_AUTH_TOKEN:-unknown} -# Will fall back to package.json#version, but this is handled elsewhere -ARG SENTRY_RELEASE -ENV SENTRY_RELEASE=$SENTRY_RELEASE -ARG SENTRY_DIST -# Default to RENDER_GIT_COMMIT if not set by GitHub workflows -ENV SENTRY_DIST=${SENTRY_DIST:-$RENDER_GIT_COMMIT} -ARG SENTRY_DSN -ENV SENTRY_DSN=$SENTRY_DSN +ARG EXPO_PUBLIC_SENTRY_DSN +ENV EXPO_PUBLIC_SENTRY_DSN=$EXPO_PUBLIC_SENTRY_DSN # # Copy everything into the container @@ -60,13 +60,16 @@ RUN \. "$NVM_DIR/nvm.sh" && \ nvm install $NODE_VERSION && \ nvm use $NODE_VERSION && \ echo "Using bundle identifier: $EXPO_PUBLIC_BUNDLE_IDENTIFIER" && \ + echo "EXPO_PUBLIC_ENV=$EXPO_PUBLIC_ENV" >> .env && \ + echo "EXPO_PUBLIC_RELEASE_VERSION=$EXPO_PUBLIC_RELEASE_VERSION" >> .env && \ echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$EXPO_PUBLIC_BUNDLE_IDENTIFIER" >> .env && \ echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env && \ + echo "EXPO_PUBLIC_SENTRY_DSN=$EXPO_PUBLIC_SENTRY_DSN" >> .env && \ npm install --global yarn && \ yarn && \ yarn intl:build 2>&1 | tee i18n.log && \ if grep -q "invalid syntax" "i18n.log"; then echo "\n\nFound compilation errors!\n\n" && exit 1; else echo "\n\nNo compile errors!\n\n"; fi && \ - EXPO_PUBLIC_BUNDLE_IDENTIFIER=$EXPO_PUBLIC_BUNDLE_IDENTIFIER EXPO_PUBLIC_BUNDLE_DATE=$() SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN SENTRY_RELEASE=$SENTRY_RELEASE SENTRY_DIST=$SENTRY_DIST SENTRY_DSN=$SENTRY_DSN yarn build-web + SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN SENTRY_RELEASE=$EXPO_PUBLIC_RELEASE_VERSION SENTRY_DIST=$EXPO_PUBLIC_BUNDLE_IDENTIFIER yarn build-web # DEBUG RUN find ./bskyweb/static && find ./web-build/static diff --git a/Dockerfile.embedr b/Dockerfile.embedr index 46719dc438..7fa8b9ae53 100644 --- a/Dockerfile.embedr +++ b/Dockerfile.embedr @@ -1,4 +1,4 @@ -FROM golang:1.23-bullseye AS build-env +FROM golang:1.24.5-bullseye AS build-env WORKDIR /usr/src/social-app diff --git a/README.md b/README.md index f86d52b530..eed6ac4fec 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Get the app itself: ## Development Resources -This is a [React Native](https://reactnative.dev/) application, written in the TypeScript programming language. It builds on the `atproto` TypeScript packages (like [`@atproto/api`](https://www.npmjs.com/package/@atproto/api)), code for which is also open source, but in [a different git repository](https://github.com/bluesky-social/atproto). +This is a [React Native](https://reactnative.dev/) application, written in the TypeScript programming language. It builds on the `atproto` TypeScript packages (like [`@atproto/api`](https://www.npmjs.com/package/@atproto/api)), which are also open source, but in [a different git repository](https://github.com/bluesky-social/atproto). There is a small amount of Go language source code (in `./bskyweb/`), for a web service that returns the React Native Web application. @@ -19,7 +19,7 @@ The [Build Instructions](./docs/build.md) are a good place to get started with t The Authenticated Transfer Protocol ("AT Protocol" or "atproto") is a decentralized social media protocol. You don't *need* to understand AT Protocol to work with this application, but it can help. Learn more at: - [Overview and Guides](https://atproto.com/guides/overview) -- [Github Discussions](https://github.com/bluesky-social/atproto/discussions) 👈 Great place to ask questions +- [GitHub Discussions](https://github.com/bluesky-social/atproto/discussions) 👈 Great place to ask questions - [Protocol Specifications](https://atproto.com/specs/atp) - [Blogpost on self-authenticating data structures](https://bsky.social/about/blog/3-6-2022-a-self-authenticating-social-protocol) @@ -27,6 +27,7 @@ The Bluesky Social application encompasses a set of schemas and APIs built in th ## Contributions +> [!NOTE] > While we do accept contributions, we prioritize high quality issues and pull requests. Adhering to the below guidelines will ensure a more timely review. **Rules:** @@ -59,7 +60,7 @@ Please be sure to: ## Security disclosures -If you discover any security issues, please send an email to security@bsky.app. The email is automatically CCed to the entire team and we'll respond promptly. +If you discover any security issues, please send an email to security@bsky.app. The email is automatically CC'd to the entire team and we'll respond promptly. ## Are you a developer interested in building on atproto? diff --git a/bskyweb/cmd/embedr/server.go b/bskyweb/cmd/embedr/server.go index d8df4276f2..62427e70e7 100644 --- a/bskyweb/cmd/embedr/server.go +++ b/bskyweb/cmd/embedr/server.go @@ -154,7 +154,13 @@ func serve(cctx *cli.Context) error { RedirectCode: http.StatusFound, })) - e.Use(echoprometheus.NewMiddleware("")) + echoprom := echoprometheus.NewMiddlewareWithConfig( + echoprometheus.MiddlewareConfig{ + DoNotUseRequestPathFor404: true, + }, + ) + + e.Use(echoprom) // // configure routes diff --git a/bskyweb/go.mod b/bskyweb/go.mod index f4689af5ae..407ba7d55d 100644 --- a/bskyweb/go.mod +++ b/bskyweb/go.mod @@ -1,8 +1,6 @@ module github.com/bluesky-social/social-app/bskyweb -go 1.24 - -toolchain go1.24.5 +go 1.24.5 require ( github.com/bluesky-social/indigo v0.0.0-20250729223159-573ae927246a @@ -10,7 +8,9 @@ require ( github.com/ipfs/go-log v1.0.5 github.com/joho/godotenv v1.5.1 github.com/klauspost/compress v1.18.0 - github.com/labstack/echo/v4 v4.13.3 + github.com/labstack/echo-contrib v0.17.4 + github.com/labstack/echo/v4 v4.13.4 + github.com/prometheus/client_golang v1.22.0 github.com/urfave/cli/v2 v2.25.7 ) @@ -53,7 +53,6 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/labstack/echo-contrib v0.17.4 github.com/labstack/gommon v0.4.2 // indirect github.com/lestrrat-go/blackmagic v1.0.1 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect @@ -74,10 +73,9 @@ require ( github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/orandin/slog-gorm v1.3.2 // indirect github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect - github.com/prometheus/client_golang v1.22.0 github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.16.1 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.17.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect @@ -95,12 +93,12 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/time v0.11.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect + golang.org/x/time v0.12.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/protobuf v1.36.6 // indirect gorm.io/driver/postgres v1.5.7 // indirect diff --git a/bskyweb/go.sum b/bskyweb/go.sum index 144ad7cfa3..ffad3e8825 100644 --- a/bskyweb/go.sum +++ b/bskyweb/go.sum @@ -115,8 +115,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo-contrib v0.17.4 h1:g5mfsrJfJTKv+F5uNKCyrjLK7js+ZW6HTjg4FnDxxgk= github.com/labstack/echo-contrib v0.17.4/go.mod h1:9O7ZPAHUeMGTOAfg80YqQduHzt0CzLak36PZRldYrZ0= -github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= -github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= +github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA= +github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80= @@ -168,10 +168,10 @@ github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/ github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= -github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= +github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= @@ -253,8 +253,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -272,16 +272,16 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -297,8 +297,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -310,10 +310,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/docs/build.md b/docs/build.md index 7817dd0957..87b653bdb9 100644 --- a/docs/build.md +++ b/docs/build.md @@ -89,9 +89,9 @@ If you change `SENTRY_AUTH_TOKEN`, you need to do `yarn prebuild` before running ### Adding bitdrift -Adding bitdirft is NOT required. You can keep `BITDRIFT_API_KEY=` in `.env` which will avoid initializing bitdrift during startup. +Adding bitdrift is NOT required. You can keep `EXPO_PUBLIC_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. +However, if you're a part of the Bluesky team and want to enable bitdrift, fill in `EXPO_PUBLIC_BITDRIFT_API_KEY` in your `.env` to enable bitdrift. ### Adding and Updating Locales diff --git a/src/App.web.tsx b/src/App.web.tsx index 04de8529ff..1f795cb3e0 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -50,7 +50,6 @@ 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 * as Toast from '#/view/com/util/Toast' -import {ToastContainer} from '#/view/com/util/Toast.web' import {Shell} from '#/view/shell/index' import {ThemeProvider as Alf} from '#/alf' import {useColorModeTheme} from '#/alf/util/useColorModeTheme' @@ -61,6 +60,7 @@ import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialo import {Provider as PortalProvider} from '#/components/Portal' import {Provider as ActiveVideoProvider} from '#/components/Post/Embed/VideoEmbed/ActiveVideoWebContext' import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' +import {ToastContainer} from '#/components/Toast' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottomBarBorder' diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 42eb648443..57a8530b5f 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -14,31 +14,36 @@ import { View, type ViewStyle, } from 'react-native' -import {LinearGradient} from 'expo-linear-gradient' -import {atoms as a, flatten, select, tokens, useTheme} from '#/alf' +import {atoms as a, flatten, select, useTheme} from '#/alf' import {type Props as SVGIconProps} from '#/components/icons/common' import {Text} from '#/components/Typography' -export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'gradient' +/** + * The `Button` component, and some extensions of it like `Link` are intended + * to be generic and therefore apply no styles by default. These `VariantProps` + * are what control the `Button`'s presentation, and are intended only use cases where the buttons appear as, well, buttons. + * + * If `Button` or an extension of it are used for other compound components, use this property to avoid misuse of these variant props further down the line. + * + * @example + * type MyComponentProps = Omit & {...} + */ +export type UninheritableButtonProps = 'variant' | 'color' | 'size' | 'shape' + +export type ButtonVariant = 'solid' | 'outline' | 'ghost' export type ButtonColor = | 'primary' | 'secondary' | 'secondary_inverted' | 'negative' | 'negative_secondary' - | 'gradient_primary' - | 'gradient_sky' - | 'gradient_midnight' - | 'gradient_sunrise' - | 'gradient_sunset' - | 'gradient_nordic' - | 'gradient_bonfire' export type ButtonSize = 'tiny' | 'small' | 'large' export type ButtonShape = 'round' | 'square' | 'default' export type VariantProps = { /** * The style variation of the button + * @deprecated Use `color` instead. */ variant?: ButtonVariant /** @@ -131,6 +136,15 @@ export const Button = React.forwardRef( }, ref, ) => { + /** + * The `variant` prop is deprecated in favor of simply specifying `color`. + * If a `color` is set, then we want to use the existing codepaths for + * "solid" buttons. This is to maintain backwards compatibility. + */ + if (!variant && color) { + variant = 'solid' + } + const t = useTheme() const [state, setState] = React.useState({ pressed: false, @@ -203,8 +217,13 @@ export const Button = React.forwardRef( const baseStyles: ViewStyle[] = [] const hoverStyles: ViewStyle[] = [] - if (color === 'primary') { - if (variant === 'solid') { + /* + * This is the happy path for new button styles, following the + * deprecation of `variant` prop. This redundant `variant` check is here + * just to make this handling easier to understand. + */ + if (variant === 'solid') { + if (color === 'primary') { if (!disabled) { baseStyles.push({ backgroundColor: t.palette.primary_500, @@ -221,64 +240,14 @@ export const Button = React.forwardRef( }), }) } - } else if (variant === 'outline') { - baseStyles.push(a.border, t.atoms.bg, { - borderWidth: 1, - }) - - if (!disabled) { - baseStyles.push(a.border, { - borderColor: t.palette.primary_500, - }) - hoverStyles.push(a.border, { - backgroundColor: t.palette.primary_50, - }) - } else { - baseStyles.push(a.border, { - borderColor: t.palette.primary_200, - }) - } - } else if (variant === 'ghost') { - if (!disabled) { - baseStyles.push(t.atoms.bg) - hoverStyles.push({ - backgroundColor: t.palette.primary_100, - }) - } - } - } else if (color === 'secondary') { - if (variant === 'solid') { + } else if (color === 'secondary') { if (!disabled) { baseStyles.push(t.atoms.bg_contrast_25) hoverStyles.push(t.atoms.bg_contrast_50) } else { baseStyles.push(t.atoms.bg_contrast_100) } - } else if (variant === 'outline') { - baseStyles.push(a.border, t.atoms.bg, { - borderWidth: 1, - }) - - if (!disabled) { - baseStyles.push(a.border, { - borderColor: t.palette.contrast_300, - }) - hoverStyles.push(t.atoms.bg_contrast_50) - } else { - baseStyles.push(a.border, { - borderColor: t.palette.contrast_200, - }) - } - } else if (variant === 'ghost') { - if (!disabled) { - baseStyles.push(t.atoms.bg) - hoverStyles.push({ - backgroundColor: t.palette.contrast_25, - }) - } - } - } else if (color === 'secondary_inverted') { - if (variant === 'solid') { + } else if (color === 'secondary_inverted') { if (!disabled) { baseStyles.push({ backgroundColor: t.palette.contrast_900, @@ -291,31 +260,7 @@ export const Button = React.forwardRef( backgroundColor: t.palette.contrast_600, }) } - } else if (variant === 'outline') { - baseStyles.push(a.border, t.atoms.bg, { - borderWidth: 1, - }) - - if (!disabled) { - baseStyles.push(a.border, { - borderColor: t.palette.contrast_300, - }) - hoverStyles.push(t.atoms.bg_contrast_50) - } else { - baseStyles.push(a.border, { - borderColor: t.palette.contrast_200, - }) - } - } else if (variant === 'ghost') { - if (!disabled) { - baseStyles.push(t.atoms.bg) - hoverStyles.push({ - backgroundColor: t.palette.contrast_25, - }) - } - } - } else if (color === 'negative') { - if (variant === 'solid') { + } else if (color === 'negative') { if (!disabled) { baseStyles.push({ backgroundColor: t.palette.negative_500, @@ -332,33 +277,7 @@ export const Button = React.forwardRef( }), }) } - } else if (variant === 'outline') { - baseStyles.push(a.border, t.atoms.bg, { - borderWidth: 1, - }) - - if (!disabled) { - baseStyles.push(a.border, { - borderColor: t.palette.negative_500, - }) - hoverStyles.push(a.border, { - backgroundColor: t.palette.negative_50, - }) - } else { - baseStyles.push(a.border, { - borderColor: t.palette.negative_200, - }) - } - } else if (variant === 'ghost') { - if (!disabled) { - baseStyles.push(t.atoms.bg) - hoverStyles.push({ - backgroundColor: t.palette.negative_100, - }) - } - } - } else if (color === 'negative_secondary') { - if (variant === 'solid') { + } else if (color === 'negative_secondary') { if (!disabled) { baseStyles.push({ backgroundColor: select(t.name, { @@ -383,31 +302,141 @@ export const Button = React.forwardRef( }), }) } - } else if (variant === 'outline') { - baseStyles.push(a.border, t.atoms.bg, { - borderWidth: 1, - }) + } + } else { + /* + * BEGIN DEPRECATED STYLES + */ + if (color === 'primary') { + if (variant === 'outline') { + baseStyles.push(a.border, t.atoms.bg, { + borderWidth: 1, + }) - if (!disabled) { - baseStyles.push(a.border, { - borderColor: t.palette.negative_500, - }) - hoverStyles.push(a.border, { - backgroundColor: t.palette.negative_50, - }) - } else { - baseStyles.push(a.border, { - borderColor: t.palette.negative_200, - }) + if (!disabled) { + baseStyles.push(a.border, { + borderColor: t.palette.primary_500, + }) + hoverStyles.push(a.border, { + backgroundColor: t.palette.primary_50, + }) + } else { + baseStyles.push(a.border, { + borderColor: t.palette.primary_200, + }) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push(t.atoms.bg) + hoverStyles.push({ + backgroundColor: t.palette.primary_100, + }) + } } - } else if (variant === 'ghost') { - if (!disabled) { - baseStyles.push(t.atoms.bg) - hoverStyles.push({ - backgroundColor: t.palette.negative_100, + } else if (color === 'secondary') { + if (variant === 'outline') { + baseStyles.push(a.border, t.atoms.bg, { + borderWidth: 1, }) + + if (!disabled) { + baseStyles.push(a.border, { + borderColor: t.palette.contrast_300, + }) + hoverStyles.push(t.atoms.bg_contrast_50) + } else { + baseStyles.push(a.border, { + borderColor: t.palette.contrast_200, + }) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push(t.atoms.bg) + hoverStyles.push({ + backgroundColor: t.palette.contrast_25, + }) + } + } + } else if (color === 'secondary_inverted') { + if (variant === 'outline') { + baseStyles.push(a.border, t.atoms.bg, { + borderWidth: 1, + }) + + if (!disabled) { + baseStyles.push(a.border, { + borderColor: t.palette.contrast_300, + }) + hoverStyles.push(t.atoms.bg_contrast_50) + } else { + baseStyles.push(a.border, { + borderColor: t.palette.contrast_200, + }) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push(t.atoms.bg) + hoverStyles.push({ + backgroundColor: t.palette.contrast_25, + }) + } + } + } else if (color === 'negative') { + if (variant === 'outline') { + baseStyles.push(a.border, t.atoms.bg, { + borderWidth: 1, + }) + + if (!disabled) { + baseStyles.push(a.border, { + borderColor: t.palette.negative_500, + }) + hoverStyles.push(a.border, { + backgroundColor: t.palette.negative_50, + }) + } else { + baseStyles.push(a.border, { + borderColor: t.palette.negative_200, + }) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push(t.atoms.bg) + hoverStyles.push({ + backgroundColor: t.palette.negative_100, + }) + } + } + } else if (color === 'negative_secondary') { + if (variant === 'outline') { + baseStyles.push(a.border, t.atoms.bg, { + borderWidth: 1, + }) + + if (!disabled) { + baseStyles.push(a.border, { + borderColor: t.palette.negative_500, + }) + hoverStyles.push(a.border, { + backgroundColor: t.palette.negative_50, + }) + } else { + baseStyles.push(a.border, { + borderColor: t.palette.negative_200, + }) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push(t.atoms.bg) + hoverStyles.push({ + backgroundColor: t.palette.negative_100, + }) + } } } + /* + * END DEPRECATED STYLES + */ } if (shape === 'default') { @@ -471,49 +500,6 @@ export const Button = React.forwardRef( } }, [t, variant, color, size, shape, disabled]) - const gradientValues = React.useMemo(() => { - const gradient = { - primary: tokens.gradients.sky, - secondary: tokens.gradients.sky, - secondary_inverted: tokens.gradients.sky, - negative: tokens.gradients.sky, - negative_secondary: tokens.gradients.sky, - gradient_primary: tokens.gradients.primary, - gradient_sky: tokens.gradients.sky, - gradient_midnight: tokens.gradients.midnight, - gradient_sunrise: tokens.gradients.sunrise, - gradient_sunset: tokens.gradients.sunset, - gradient_nordic: tokens.gradients.nordic, - gradient_bonfire: tokens.gradients.bonfire, - }[color || 'primary'] - - if (variant === 'gradient') { - if (gradient.values.length < 2) { - throw new Error( - 'Gradient buttons must have at least two colors in the gradient', - ) - } - - return { - colors: gradient.values.map(([_, color]) => color) as [ - string, - string, - ...string[], - ], - hoverColors: gradient.values.map(_ => gradient.hover_value) as [ - string, - string, - ...string[], - ], - locations: gradient.values.map(([location, _]) => location) as [ - number, - number, - ...number[], - ], - } - } - }, [variant, color]) - const context = React.useMemo( () => ({ ...state, @@ -556,27 +542,6 @@ export const Button = React.forwardRef( onHoverOut={onHoverOut} onFocus={onFocus} onBlur={onBlur}> - {variant === 'gradient' && gradientValues && ( - - - - )} {typeof children === 'function' ? children(context) : children} @@ -592,30 +557,19 @@ export function useSharedButtonTextStyles() { return React.useMemo(() => { const baseStyles: TextStyle[] = [] - if (color === 'primary') { - if (variant === 'solid') { + /* + * This is the happy path for new button styles, following the + * deprecation of `variant` prop. This redundant `variant` check is here + * just to make this handling easier to understand. + */ + if (variant === 'solid') { + if (color === 'primary') { if (!disabled) { baseStyles.push({color: t.palette.white}) } else { baseStyles.push({color: t.palette.white, opacity: 0.5}) } - } else if (variant === 'outline') { - if (!disabled) { - baseStyles.push({ - color: t.palette.primary_600, - }) - } else { - baseStyles.push({color: t.palette.primary_600, opacity: 0.5}) - } - } else if (variant === 'ghost') { - if (!disabled) { - baseStyles.push({color: t.palette.primary_600}) - } else { - baseStyles.push({color: t.palette.primary_600, opacity: 0.5}) - } - } - } else if (color === 'secondary') { - if (variant === 'solid' || variant === 'gradient') { + } else if (color === 'secondary') { if (!disabled) { baseStyles.push({ color: t.palette.contrast_700, @@ -625,29 +579,7 @@ export function useSharedButtonTextStyles() { color: t.palette.contrast_400, }) } - } else if (variant === 'outline') { - if (!disabled) { - baseStyles.push({ - color: t.palette.contrast_600, - }) - } else { - baseStyles.push({ - color: t.palette.contrast_300, - }) - } - } else if (variant === 'ghost') { - if (!disabled) { - baseStyles.push({ - color: t.palette.contrast_600, - }) - } else { - baseStyles.push({ - color: t.palette.contrast_300, - }) - } - } - } else if (color === 'secondary_inverted') { - if (variant === 'solid' || variant === 'gradient') { + } else if (color === 'secondary_inverted') { if (!disabled) { baseStyles.push({ color: t.palette.contrast_50, @@ -657,49 +589,13 @@ export function useSharedButtonTextStyles() { color: t.palette.contrast_400, }) } - } else if (variant === 'outline') { - if (!disabled) { - baseStyles.push({ - color: t.palette.contrast_600, - }) - } else { - baseStyles.push({ - color: t.palette.contrast_300, - }) - } - } else if (variant === 'ghost') { - if (!disabled) { - baseStyles.push({ - color: t.palette.contrast_600, - }) - } else { - baseStyles.push({ - color: t.palette.contrast_300, - }) - } - } - } else if (color === 'negative') { - if (variant === 'solid' || variant === 'gradient') { + } else if (color === 'negative') { if (!disabled) { baseStyles.push({color: t.palette.white}) } else { baseStyles.push({color: t.palette.white, opacity: 0.5}) } - } else if (variant === 'outline') { - if (!disabled) { - baseStyles.push({color: t.palette.negative_400}) - } else { - baseStyles.push({color: t.palette.negative_400, opacity: 0.5}) - } - } else if (variant === 'ghost') { - if (!disabled) { - baseStyles.push({color: t.palette.negative_400}) - } else { - baseStyles.push({color: t.palette.negative_400, opacity: 0.5}) - } - } - } else if (color === 'negative_secondary') { - if (variant === 'solid' || variant === 'gradient') { + } else if (color === 'negative_secondary') { if (!disabled) { baseStyles.push({ color: select(t.name, { @@ -718,25 +614,103 @@ export function useSharedButtonTextStyles() { opacity: 0.5, }) } - } else if (variant === 'outline') { - if (!disabled) { - baseStyles.push({color: t.palette.negative_400}) - } else { - baseStyles.push({color: t.palette.negative_400, opacity: 0.5}) - } - } else if (variant === 'ghost') { - if (!disabled) { - baseStyles.push({color: t.palette.negative_400}) - } else { - baseStyles.push({color: t.palette.negative_400, opacity: 0.5}) - } } } else { - if (!disabled) { - baseStyles.push({color: t.palette.white}) - } else { - baseStyles.push({color: t.palette.white, opacity: 0.5}) + /* + * BEGIN DEPRECATED STYLES + */ + if (color === 'primary') { + if (variant === 'outline') { + if (!disabled) { + baseStyles.push({ + color: t.palette.primary_600, + }) + } else { + baseStyles.push({color: t.palette.primary_600, opacity: 0.5}) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push({color: t.palette.primary_600}) + } else { + baseStyles.push({color: t.palette.primary_600, opacity: 0.5}) + } + } + } else if (color === 'secondary') { + if (variant === 'outline') { + if (!disabled) { + baseStyles.push({ + color: t.palette.contrast_600, + }) + } else { + baseStyles.push({ + color: t.palette.contrast_300, + }) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push({ + color: t.palette.contrast_600, + }) + } else { + baseStyles.push({ + color: t.palette.contrast_300, + }) + } + } + } else if (color === 'secondary_inverted') { + if (variant === 'outline') { + if (!disabled) { + baseStyles.push({ + color: t.palette.contrast_600, + }) + } else { + baseStyles.push({ + color: t.palette.contrast_300, + }) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push({ + color: t.palette.contrast_600, + }) + } else { + baseStyles.push({ + color: t.palette.contrast_300, + }) + } + } + } else if (color === 'negative') { + if (variant === 'outline') { + if (!disabled) { + baseStyles.push({color: t.palette.negative_400}) + } else { + baseStyles.push({color: t.palette.negative_400, opacity: 0.5}) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push({color: t.palette.negative_400}) + } else { + baseStyles.push({color: t.palette.negative_400, opacity: 0.5}) + } + } + } else if (color === 'negative_secondary') { + if (variant === 'outline') { + if (!disabled) { + baseStyles.push({color: t.palette.negative_400}) + } else { + baseStyles.push({color: t.palette.negative_400, opacity: 0.5}) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push({color: t.palette.negative_400}) + } else { + baseStyles.push({color: t.palette.negative_400, opacity: 0.5}) + } + } } + /* + * END DEPRECATED STYLES + */ } if (size === 'large') { diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx index 2a3a00ba7d..18da12b22c 100644 --- a/src/components/FeedInterstitials.tsx +++ b/src/components/FeedInterstitials.tsx @@ -378,8 +378,8 @@ export function ProfileGrid({ style={[a.border_t, t.atoms.border_contrast_low, t.atoms.bg_contrast_25]}> {gtMobile ? ( - + {content} @@ -411,9 +411,8 @@ export function ProfileGrid({ horizontal showsHorizontalScrollIndicator={false} snapToInterval={MOBILE_CARD_WIDTH + a.gap_md.gap} - decelerationRate="fast" - style={[a.overflow_visible]}> - + decelerationRate="fast"> + {content} @@ -434,6 +433,7 @@ function SeeMoreSuggestedProfilesCard() { return ( - - - ))} + + ))} - - - - {['gradient_sky', 'gradient_midnight', 'gradient_sunrise'].map( - name => ( - - - - - ), - )} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/view/screens/Storybook/Forms.tsx b/src/view/screens/Storybook/Forms.tsx index ad130b376d..45a1d9aa00 100644 --- a/src/view/screens/Storybook/Forms.tsx +++ b/src/view/screens/Storybook/Forms.tsx @@ -1,5 +1,5 @@ import React from 'react' -import {TextInput, View} from 'react-native' +import {type TextInput, View} from 'react-native' import {atoms as a} from '#/alf' import {Button, ButtonText} from '#/components/Button' @@ -216,8 +216,8 @@ export function Forms() { ) diff --git a/src/view/screens/Storybook/index.tsx b/src/view/screens/Storybook/index.tsx index afcc1c4e73..1151d5a3ce 100644 --- a/src/view/screens/Storybook/index.tsx +++ b/src/view/screens/Storybook/index.tsx @@ -52,7 +52,6 @@ function StorybookInner() { <>