+{% endif -%}
{%- endblock %}
diff --git a/code-signing/certificate.pem b/code-signing/certificate.pem
new file mode 100644
index 0000000000..bfc5cdbde8
--- /dev/null
+++ b/code-signing/certificate.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbmgAwIBAgIJcMN2yt5KNDqTMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV
+BAMTB0JsdWVza3kwHhcNMjQwMzE0MDA1OTU4WhcNMzQwMzE0MDA1OTU4WjASMRAw
+DgYDVQQDEwdCbHVlc2t5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+izSAWEc3wRoa3eTBEh/kE9pH0d6jhEGw9GrYfei60MHT1pSq2cTdyUM1yUZchAeW
+gFFtqFxX0pfIZQyMlIZbjkaOxOqzWhB0aCsxngnhbSahFwRxkVwTAuonhqIpaLBL
+hrCCCQ2IfZUpy8QeasqlTlmvmijuCC34fXxJlxNcj8SqzIZi+civ7U5PMPfIMMnD
+tCDIBy1vxMk57m25X2ikcWUFW64qNVLkFAL36xEnmFTL4Ivqpz23gUcUIe1zbesY
+jAgDtlwnAE7mU3oagCUDcSuOveT4POhT35Xp3Y/07I68kmXtrPxwd5k0L0zbisEm
+poKZ87E2X29BitihicMpBwIDAQABoyowKDAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0l
+AQH/BAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAED1gdMF0yr8Gy87
+RgyaeVpPySwSsO0selmXXrcmOWgiPA05lubyhFEa4P5kdzBEByG2MT+pJkjGYpvK
+XRnqXM5VvdS2RhYYFH0cFOIUqBKwCnzViCMuGQeoGUx4oPcKFS0PQ1WjW2d4pS75
+51GBfB6LOepsCHUG0A9XEk7EAyUWc4M2ITCJsTtJh8CVn2pTks2q14ETDs86YQv4
+peDaJv8nhIe8oQkeGn2o/P/ctkwJg/uBydQUsWgjjGTQZTilVjGTW1mwDr9FucAE
+d5gKIk4rtR/3Zd/NDdqp8PrkoWeVM7Hwr789/mpUOeqa/j7YNkDYQh7x+M/odd1D
+KY0bQEQ=
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/docs/build.md b/docs/build.md
index 01befccd41..d1f9f93b5a 100644
--- a/docs/build.md
+++ b/docs/build.md
@@ -2,24 +2,49 @@
## App Build
-- Setup your environment [using the react native instructions](https://reactnative.dev/docs/environment-setup).
+- Set up your environment [using the react native instructions](https://reactnative.dev/docs/environment-setup).
+- If you're running macOS, make sure you are running the correct versions of Ruby and Cocoapods:
+ - Check if you've installed Cocoapods through `homebrew`. If you have, remove it:
+ - `brew info cocoapods`
+ - If output says `Installed`:
+ - `brew remove cocoapods`
+ - If you have not installed `rbenv`:
+ - `brew install rbenv`
+ - `rbenv install 2.7.6`
+ - `rbenv global 2.7.6`
+ - Add `eval "$(rbenv init - zsh)"` to your `~/.zshrc`
+ - From inside the project directory:
+ - `bundler install`
- Setup your environment [for e2e testing using detox](https://wix.github.io/Detox/docs/introduction/getting-started):
- - yarn global add detox-cli
- - brew tap wix/brew
- - brew install applesimutils
+ - `yarn global add detox-cli`
+ - `brew tap wix/brew`
+ - `brew install applesimutils`
- After initial setup:
- Copy `google-services.json.example` to `google-services.json` or provide your own `google-services.json`. (A real firebase project is NOT required)
- `npx expo prebuild` -> you will also need to run this anytime `app.json` or native `package.json` deps change
+ - `yarn intl:build` -> you will also need to run this anytime `./src/locale/{locale}/messages.po` change
- Start the dev servers
- `git clone git@github.com:bluesky-social/atproto.git`
- `cd atproto`
+ - `brew install pnpm`
+ - `brew install jq`
- `pnpm i`
+ - `pnpm build`
+ - Start the docker daemon (on MacOS this entails starting the Docker Desktop app)
+ - Launch a Postgres database on port 5432
- `cd packages/dev-env && pnpm start`
- Run the dev app
- iOS: `yarn ios`
+ - Xcode must be installed for this to run.
+ - A simulator must be preconfigured in Xcode settings.
+ - if no iOS versions are available, install the iOS runtime at `Xcode > Settings > Platforms`.
+ - In addition, ensure Xcode Command Line Tools are installed using `xcode-select --install`.
+ - Pods must be installed:
+ - From the project directory root: `cd ios && pod install`.
+ - Expo will require you to configure Xcode Signing. Follow the linked instructions. Error messages in Xcode related to the signing process can be safely ignored when installing on the iOS Simulator; Expo merely requires the profile to exist in order to install the app on the Simulator.
- Android: `yarn android`
- Web: `yarn web`
-- If you are cloning or forking this repo as an open source developer, please check the tips below as well
+- If you are cloning or forking this repo as an open-source developer, please check the tips below as well
- Run e2e tests
- Start in various console tabs:
- `yarn e2e:mock-server`
@@ -27,14 +52,14 @@
- Run once: `yarn e2e:build`
- Each test run: `yarn e2e:run`
- Tips
- - Make sure to copy the `.env.example` to `.env` and fill in any necessary tokens. (The Sentry token is NOT required; see instructions below if you want to enable Sentry.)
- - To run on device, add `--device` to the command (e.g. `yarn android --device`). To build in production mode (slower build, faster app), also add `--variant release`.
- - If you want to use Expo EAS on your own builds without ejecting from Expo, make sure to change the `owner` as well as `extra.eas.projectId` properties. If you do not have an Expo account, you may remove these properties.
+ - Copy the `.env.example` to `.env` and fill in any necessary tokens. (The Sentry token is NOT required; see instructions below if you want to enable Sentry.)
+ - To run on the device, add `--device` to the command (e.g. `yarn android --device`). To build in production mode (slower build, faster app), also add `--variant release`.
+ - If you want to use Expo EAS on your own builds without ejecting from Expo, make sure to change the `owner` and `extra.eas.projectId` properties. If you do not have an Expo account, you may remove these properties.
- `npx react-native info` Checks what has been installed.
- - If the android simulator frequently hangs or is very sluggish, [bump its memory limit](https://stackoverflow.com/a/40068396)
- - The android simulator won't be able to access localhost services unless you run `adb reverse tcp:{PORT} tcp:{PORT}`
+ - If the Android simulator frequently hangs or is very sluggish, [bump its memory limit](https://stackoverflow.com/a/40068396)
+ - The Android simulator won't be able to access localhost services unless you run `adb reverse tcp:{PORT} tcp:{PORT}`
- For instance, the locally-hosted dev-wallet will need `adb reverse tcp:3001 tcp:3001`
- - For some reason, the typescript compiler chokes on platform-specific files (e.g. `foo.native.ts`) but only when compiling for Web thus far. Therefore we always have one version of the file which doesn't use a platform specifier, and that should be the Web version. ([More info](https://stackoverflow.com/questions/44001050/platform-specific-import-component-in-react-native-with-typescript).)
+ - For some reason, the typescript compiler chokes on platform-specific files (e.g. `foo.native.ts`) but only when compiling for Web thus far. Therefore we always have one version of the file that doesn't use a platform specifier, and that should be the Web version. ([More info](https://stackoverflow.com/questions/44001050/platform-specific-import-component-in-react-native-with-typescript).)
### Adding Sentry
@@ -80,24 +105,24 @@ To open the [Developer Menu](https://docs.expo.dev/debugging/tools/#developer-me
- Android Device: Shake the device vertically, or if your device is connected via USB, run adb shell input keyevent 82 in your terminal
- Android Emulator: Either press Cmd ⌘ + m or Ctrl + m or run adb shell input keyevent 82 in your terminal
- iOS Device: Shake the device, or touch 3 fingers to the screen
-- iOS Simulator: Press Ctrl + Cmd ⌘ + z on a Mac in the emulator to simulate the shake gesture, or press Cmd ⌘ + d
+- iOS Simulator: Press Ctrl + Cmd ⌘ + z on a Mac in the emulator to simulate the shake gesture or press Cmd ⌘ + d
### Running E2E Tests
-- Make sure you've setup your environment following above
+- Make sure you've set your environment following the above
- Make sure Metro and the dev server are running
- Run `yarn e2e`
- Find the artifacts in the `artifact` folder
### Polyfills
-`./platform/polyfills.*.ts` adds polyfills to the environment. Currently this includes:
+`./platform/polyfills.*.ts` adds polyfills to the environment. Currently, this includes:
- TextEncoder / TextDecoder
### Sentry sourcemaps
-Sourcemaps should automatically be updated when a signed build is created using `eas build` and published using `eas submit` due to the postPublish hook setup in `app.json`. However, if an update is created and published OTA using `eas update`, we need to the take the following steps to upload sourcemaps to Sentry:
+Sourcemaps should automatically be updated when a signed build is created using `eas build` and published using `eas submit` due to the postPublish hook setup in `app.json`. However, if an update is created and published OTA using `eas update`, we need to take the following steps to upload sourcemaps to Sentry:
- Run eas update. This will generate a dist folder in your project root, which contains your JavaScript bundles and source maps. This command will also output the 'Android update ID' and 'iOS update ID' that we'll need in the next step.
- Copy or rename the bundle names in the `dist/bundles` folder to match `index.android.bundle` (Android) or `main.jsbundle` (iOS).
@@ -121,6 +146,6 @@ dist/bundles/main.jsbundle dist/bundles/ios-.map`
### OTA updates
-To create OTA updates, run `eas update` along with the `--branch` flag to indicate which branch you want to push the update to, and the `--message` flag to indicate a message for yourself and your team that shows up on https://expo.dev. ALl the channels (which make up the options for the `--branch` flag) are given in `eas.json`. [See more here](https://docs.expo.dev/eas-update/getting-started/)
+To create OTA updates, run `eas update` along with the `--branch` flag to indicate which branch you want to push the update to, and the `--message` flag to indicate a message for yourself and your team that shows up on https://expo.dev. All the channels (which make up the options for the `--branch` flag) are given in `eas.json`. [See more here](https://docs.expo.dev/eas-update/getting-started/)
-The clients which can receive an OTA update is governed by the `runtimeVersion` property in `app.json`. Right now, it is set so that only apps with the same `appVersion` (same as `version` property in `app.json`) can receive the update and install it. However, we can manually set `"runtimeVersion": "1.34.0"` or anything along those lines as well. This is useful if very little native code changes from update-to-update. If we are manually setting `runtimeVersion`, we should increment the version each time native code is changed. [See more here](https://docs.expo.dev/eas-update/runtime-versions/)
+The clients which can receive an OTA update are governed by the `runtimeVersion` property in `app.json`. Right now, it is set so that only apps with the same `appVersion` (same as `version` property in `app.json`) can receive the update and install it. However, we can manually set `"runtimeVersion": "1.34.0"` or anything along those lines as well. This is useful if very little native code changes from update to update. If we are manually setting `runtimeVersion`, we should increment the version each time the native code is changed. [See more here](https://docs.expo.dev/eas-update/runtime-versions/)
diff --git a/docs/localization.md b/docs/localization.md
new file mode 100644
index 0000000000..0de2447952
--- /dev/null
+++ b/docs/localization.md
@@ -0,0 +1,119 @@
+# Internationalization
+
+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 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
+import { Text } from "react-native";
+
+Hello World
+```
+
+```jsx
+// After
+import { Text } from "react-native";
+import { Trans } from "@lingui/macro";
+
+Hello World
+```
+
+The `` macro will extract the string and add it to the catalog. It is not really a component, but a macro. Further reading [here](https://lingui.dev/ref/macro.html)
+
+However sometimes you will run into this case:
+```jsx
+// Before
+import { Text } from "react-native";
+
+const text = "Hello World";
+{text}
+```
+In this case, you can use the `useLingui()` hook:
+```jsx
+import { msg } from "@lingui/macro";
+import { useLingui } from "@lingui/react";
+
+const { _ } = useLingui();
+return {text}
+```
+
+If you want to do this outside of a React component, you can use the `t` macro instead (note: this won't react to changes if the locale is switched dynamically within the app):
+```jsx
+import { t } from "@lingui/macro";
+
+const text = t`Hello World`;
+```
+
+We can then run `yarn intl:extract` to update the catalog in `src/locale/locales/{locale}/messages.po`. This will add the new string to the catalog.
+We can then run `yarn intl:compile` to update the translation files in `src/locale/locales/{locale}/messages.js`. This will add the new string to the translation files.
+The configuration for translations is defined in `lingui.config.js`
+
+So the workflow is as follows:
+1. Wrap messages in Trans macro
+2. Run `yarn intl:extract` command to generate message catalogs
+3. Translate message catalogs (send them to translators usually)
+4. Run `yarn intl:compile` to create runtime catalogs
+5. Load runtime catalog
+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
+import { msg } from "@lingui/macro";
+import { i18n } from "@lingui/core";
+
+const welcomeMessage = msg`Welcome!`;
+
+// ❌ Bad! This code won't work
+export function Welcome() {
+ const buggyWelcome = useMemo(() => {
+ return i18n._(welcomeMessage);
+ }, []);
+
+ return
{buggyWelcome}
;
+}
+
+// ❌ Bad! This code won't work either because the reference to i18n does not change
+export function Welcome() {
+ const { i18n } = useLingui();
+
+ const buggyWelcome = useMemo(() => {
+ return i18n._(welcomeMessage);
+ }, [i18n]);
+
+ return
{buggyWelcome}
;
+}
+
+// ✅ Good! `useMemo` has i18n context in the dependency
+export function Welcome() {
+ const linguiCtx = useLingui();
+
+ const welcome = useMemo(() => {
+ return linguiCtx.i18n._(welcomeMessage);
+ }, [linguiCtx]);
+
+ return
{welcome}
;
+}
+
+// 🤩 Better! `useMemo` consumes the `_` function from the Lingui context
+export function Welcome() {
+ const { _ } = useLingui();
+
+ const welcome = useMemo(() => {
+ return _(welcomeMessage);
+ }, [_]);
+
+ return