Skip to content

Commit

Permalink
Merge branch 'bluesky-social:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
jaoler authored Feb 29, 2024
2 parents 1f95628 + a35976c commit 963a44a
Show file tree
Hide file tree
Showing 131 changed files with 7,094 additions and 1,712 deletions.
10 changes: 7 additions & 3 deletions .github/workflows/build-submit-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ jobs:
uses: actions/checkout@v4

- name: 🔧 Setup Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18.x
node-version-file: .nvmrc
cache: yarn

- name: 🔨 Setup EAS
Expand All @@ -49,10 +49,14 @@ jobs:
- name: ⚙️ Install dependencies
run: yarn install

- name: 🔤 Compile translations
run: yarn intl:build

- name: ✏️ Write environment variables
run: |
export json='${{ secrets.GOOGLE_SERVICES_TOKEN }}'
echo "${{ secrets.ENV_TOKEN }}" > .env
echo "${{ secrets.GOOGLE_SERVICES_TOKEN }}" > google-services.json
echo "$json" > google-services.json
- name: 🏗️ EAS Build
run: yarn use-build-number eas build -p android --profile production --local --output build.aab --non-interactive
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/build-submit-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ jobs:
uses: actions/checkout@v4

- name: 🔧 Setup Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18.x
node-version-file: .nvmrc
cache: yarn

- name: 🔨 Setup EAS
Expand Down Expand Up @@ -60,6 +60,9 @@ jobs:
# change unless the yarn version changes as well.
key: ${{ runner.os }}-pods-${{ hashFiles('yarn.lock') }}

- name: 🔤 Compile translations
run: yarn intl:build

- name: ✏️ Write environment variables
run: |
echo "${{ secrets.ENV_TOKEN }}" > .env
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ jobs:
name: Run tests
runs-on: ubuntu-latest
steps:
- name: Install node 18
uses: actions/setup-node@v4
with:
node-version: 18
- name: Check out Git repository
uses: actions/checkout@v3
- name: Install node
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
- name: Yarn install
uses: Wandalen/wretry.action@master
with:
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
18
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ COPY . .
RUN mkdir --parents $NVM_DIR && \
wget \
--output-document=/tmp/nvm-install.sh \
https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh && \
https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh && \
bash /tmp/nvm-install.sh

RUN \. "$NVM_DIR/nvm.sh" && \
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ If you discover any security issues, please send an email to [email protected].

Bluesky is an open social network built on the AT Protocol, a flexible technology that will never lock developers out of the ecosystems that they help build. With atproto, third-party can be as seamless as first-party through custom feeds, federated services, clients, and more.

If you're a developer interested in building on atproto, we'd love to email you a Bluesky invite code. Simply share your GitHub (or similar) profile with us via [this form](https://forms.gle/BF21oxVNZiDjDhXF9).

## License (MIT)

See [./LICENSE](./LICENSE) for the full license.
Expand Down
25 changes: 25 additions & 0 deletions app.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ module.exports = function (config) {
...SPLASH_CONFIG,
dark: DARK_SPLASH_CONFIG,
},
entitlements: {
'com.apple.security.application-groups': 'group.app.bsky',
},
},
androidStatusBar: {
barStyle: 'dark-content',
Expand All @@ -89,6 +92,10 @@ module.exports = function (config) {
scheme: 'https',
host: 'bsky.app',
},
{
scheme: 'http',
host: 'localhost:19006',
},
],
category: ['BROWSABLE', 'DEFAULT'],
},
Expand Down Expand Up @@ -137,9 +144,27 @@ module.exports = function (config) {
},
],
'./plugins/withAndroidManifestPlugin.js',
'./plugins/shareExtension/withShareExtensions.js',
].filter(Boolean),
extra: {
eas: {
build: {
experimental: {
ios: {
appExtensions: [
{
targetName: 'Share-with-Bluesky',
bundleIdentifier: 'xyz.blueskyweb.app.Share-with-Bluesky',
entitlements: {
'com.apple.security.application-groups': [
'group.app.bsky',
],
},
},
],
},
},
},
projectId: '55bd077a-d905-4184-9c7f-94789ba0f302',
},
},
Expand Down
1 change: 1 addition & 0 deletions assets/icons/checkThick_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/clipboard_stroke2_corner2_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/magnifyingGlass2_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/mute_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/pageText_stroke2_corner0_rounded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions bskyweb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ To build the SPA bundle (`bundle.web.js`), first get a JavaScript development
environment set up. Either follow the top-level README, or something quick
like:

# install nodejs 18 (specifically)
nvm install 18
nvm use 18
# install nodejs
nvm install
nvm use
npm install --global yarn

# setup tools and deps (in top level of this repo)
Expand Down
1 change: 1 addition & 0 deletions bskyweb/cmd/bskyweb/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ func serve(cctx *cli.Context) error {
e.GET("/support/tos", server.WebGeneric)
e.GET("/support/community-guidelines", server.WebGeneric)
e.GET("/support/copyright", server.WebGeneric)
e.GET("/intent/compose", server.WebGeneric)

// profile endpoints; only first populates info
e.GET("/profile/:handleOrDID", server.WebProfile)
Expand Down
5 changes: 5 additions & 0 deletions bskyweb/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@
[data-tooltip]:hover::before {
display:block;
}

/* NativeDropdown component */
.nativeDropdown-item:focus {
outline: none;
}
</style>
{% include "scripts.html" %}
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
Expand Down
41 changes: 41 additions & 0 deletions modules/Share-with-Bluesky/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>10</integer>
</dict>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
<key>MainAppScheme</key>
<string>bluesky</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleDisplayName</key>
<string>Extension</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
</dict>
</plist>
10 changes: 10 additions & 0 deletions modules/Share-with-Bluesky/Share-with-Bluesky.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.app.bsky</string>
</array>
</dict>
</plist>
153 changes: 153 additions & 0 deletions modules/Share-with-Bluesky/ShareViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import UIKit

class ShareViewController: UIViewController {
// This allows other forks to use this extension while also changing their
// scheme.
let appScheme = Bundle.main.object(forInfoDictionaryKey: "MainAppScheme") as? String ?? "bluesky"

//
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

guard let extensionItem = extensionContext?.inputItems.first as? NSExtensionItem,
let attachments = extensionItem.attachments,
let firstAttachment = extensionItem.attachments?.first
else {
self.completeRequest()
return
}

Task {
if firstAttachment.hasItemConformingToTypeIdentifier("public.text") {
await self.handleText(item: firstAttachment)
} else if firstAttachment.hasItemConformingToTypeIdentifier("public.url") {
await self.handleUrl(item: firstAttachment)
} else if firstAttachment.hasItemConformingToTypeIdentifier("public.image") {
await self.handleImages(items: attachments)
} else {
self.completeRequest()
}
}
}

private func handleText(item: NSItemProvider) async -> Void {
do {
if let data = try await item.loadItem(forTypeIdentifier: "public.text") as? String {
if let encoded = data.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)")
{
_ = self.openURL(url)
}
}
self.completeRequest()
} catch {
self.completeRequest()
}
}

private func handleUrl(item: NSItemProvider) async -> Void {
do {
if let data = try await item.loadItem(forTypeIdentifier: "public.url") as? URL {
if let encoded = data.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)")
{
_ = self.openURL(url)
}
}
self.completeRequest()
} catch {
self.completeRequest()
}
}

private func handleImages(items: [NSItemProvider]) async -> Void {
let firstFourItems: [NSItemProvider]
if items.count < 4 {
firstFourItems = items
} else {
firstFourItems = Array(items[0...3])
}

var valid = true
var imageUris = ""

for (index, item) in firstFourItems.enumerated() {
var imageUriInfo: String? = nil

do {
if let dataUri = try await item.loadItem(forTypeIdentifier: "public.image") as? URL {
// We need to duplicate this image, since we don't have access to the outgoing temp directory
// We also will get the image dimensions here, sinze RN makes it difficult to get those dimensions for local files
let data = try Data(contentsOf: dataUri)
let image = UIImage(data: data)
imageUriInfo = self.saveImageWithInfo(image)
} else if let image = try await item.loadItem(forTypeIdentifier: "public.image") as? UIImage {
imageUriInfo = self.saveImageWithInfo(image)
}
} catch {
valid = false
}

if let imageUriInfo = imageUriInfo {
imageUris.append(imageUriInfo)
if index < items.count - 1 {
imageUris.append(",")
}
} else {
valid = false
}
}

if valid,
let encoded = imageUris.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
let url = URL(string: "\(self.appScheme)://intent/compose?imageUris=\(encoded)")
{
_ = self.openURL(url)
}

self.completeRequest()
}

private func saveImageWithInfo(_ image: UIImage?) -> String? {
guard let image = image else {
return nil
}

do {
// Saving this file to the bundle group's directory lets us access it from
// inside of the app. Otherwise, we wouldn't have access even though the
// extension does.
if let dir = FileManager()
.containerURL(
forSecurityApplicationGroupIdentifier: "group.app.bsky")
{
let filePath = "\(dir.absoluteString)\(ProcessInfo.processInfo.globallyUniqueString).jpeg"

if let newUri = URL(string: filePath),
let jpegData = image.jpegData(compressionQuality: 1)
{
try jpegData.write(to: newUri)
return "\(newUri.absoluteString)|\(image.size.width)|\(image.size.height)"
}
}
return nil
} catch {
return nil
}
}

private func completeRequest() -> Void {
self.extensionContext?.completeRequest(returningItems: nil)
}

@objc func openURL(_ url: URL) -> Bool {
var responder: UIResponder? = self
while responder != nil {
if let application = responder as? UIApplication {
return application.perform(#selector(openURL(_:)), with: url) != nil
}
responder = responder?.next
}
return false
}
}
8 changes: 8 additions & 0 deletions modules/expo-receive-android-intents/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Expo Receive Android Intents

This module handles incoming intents on Android. Handled intents are `text/plain` and `image/*` (single or multiple).
The module handles saving images to the app's filesystem for access within the app, limiting the selection of images
to a max of four, and handling intent types. No JS code is required for this module, and it is no-op on non-android
platforms.

No installation is required. Gradle will automatically add this module on build.
Loading

0 comments on commit 963a44a

Please sign in to comment.