Compare commits
1 Commits
0564f93ad8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
7c225b22fc
|
30
.github/workflows/cf-pages.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
name: Deploy to Cloudflare Pages
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'html/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
deployments: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Deploy to Cloudflare Pages
|
||||||
|
uses: cloudflare/pages-action@v1
|
||||||
|
with:
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
|
||||||
|
directory: html
|
||||||
|
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
wranglerVersion: '3'
|
||||||
3
.gitignore
vendored
@@ -2,3 +2,6 @@ repos
|
|||||||
.claude
|
.claude
|
||||||
deploy.yml
|
deploy.yml
|
||||||
claude.md
|
claude.md
|
||||||
|
embedded.mobileprovision
|
||||||
|
.env
|
||||||
|
html.zip
|
||||||
|
|||||||
21
README.md
@@ -65,3 +65,24 @@ $ curl -sL "syu.is/xrpc/com.atproto.repo.listRecords?repo=${handle}&collection=a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## build
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# build
|
||||||
|
./install.zsh
|
||||||
|
|
||||||
|
# build social-app
|
||||||
|
./install.zsh pull;./install.zsh patch;./install.zsh build social-app;./install.zsh push social-app
|
||||||
|
---
|
||||||
|
# server
|
||||||
|
./install.zsh
|
||||||
|
---
|
||||||
|
# social-app ios
|
||||||
|
# https://appstoreconnect.apple.com/
|
||||||
|
# https://developer.apple.com/account/resources/profiles/list
|
||||||
|
./install.zsh pull;./ios/setup.zsh
|
||||||
|
./ios/build.zsh
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
database:
|
database:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
#command: ["/bigsky", "--crawl-insecure-ws"]
|
||||||
|
|
||||||
social-app:
|
social-app:
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ FEEDGEN_LISTENHOST=0.0.0.0
|
|||||||
FEEDGEN_SQLITE_LOCATION=/data/db.sqlite
|
FEEDGEN_SQLITE_LOCATION=/data/db.sqlite
|
||||||
FEEDGEN_HOSTNAME=feed.syu.is
|
FEEDGEN_HOSTNAME=feed.syu.is
|
||||||
FEEDGEN_PUBLISHER_DID=did:plc:6qyecktefllvenje24fcxnie
|
FEEDGEN_PUBLISHER_DID=did:plc:6qyecktefllvenje24fcxnie
|
||||||
FEEDGEN_SUBSCRIPTION_ENDPOINT=ws://bgs:2470
|
|
||||||
FEEDGEN_SERVICE_DID=did:web:feed.syu.is
|
FEEDGEN_SERVICE_DID=did:web:feed.syu.is
|
||||||
|
FEEDGEN_JETSTREAM_URL=ws://jetstream:6008/subscribe
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
JETSTREAM_WS_URL=wss://bgs.${host}/xrpc/com.atproto.sync.subscribeRepos
|
JETSTREAM_WS_URL=ws://bgs.${host}/xrpc/com.atproto.sync.subscribeRepos
|
||||||
JETSTREAM_DATA_DIR=/data
|
JETSTREAM_DATA_DIR=/data
|
||||||
JETSTREAM_LISTEN_ADDR=:6008
|
JETSTREAM_LISTEN_ADDR=:6008
|
||||||
JETSTREAM_METRICS_LISTEN_ADDR=:6009
|
JETSTREAM_METRICS_LISTEN_ADDR=:6009
|
||||||
|
|||||||
135
html/about/support/app.html
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ja">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||||
|
<title>App Info - Aiat</title>
|
||||||
|
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #1a1a1a;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body { background: #000; color: #e0e0e0; }
|
||||||
|
a { color: #6bb3ff; }
|
||||||
|
h1, h2, h3 { color: #fff; }
|
||||||
|
.section { background: #1a1a1a; }
|
||||||
|
.info-item { background: #2a2a2a; }
|
||||||
|
}
|
||||||
|
.header { margin-bottom: 32px; }
|
||||||
|
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; color: #0066cc; text-decoration: none; }
|
||||||
|
.back-link:hover { text-decoration: underline; }
|
||||||
|
.app-header { text-align: center; margin-bottom: 32px; }
|
||||||
|
.app-icon { width: 80px; height: 80px; border-radius: 18px; margin-bottom: 12px; }
|
||||||
|
.app-name { font-size: 24px; font-weight: bold; margin-bottom: 4px; }
|
||||||
|
.app-version { font-size: 14px; color: #666; }
|
||||||
|
.section { background: #f5f5f5; border-radius: 16px; padding: 20px; margin-bottom: 16px; }
|
||||||
|
.section-title { font-size: 13px; font-weight: 600; color: #999; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }
|
||||||
|
.description { font-size: 15px; line-height: 22px; }
|
||||||
|
.info-grid { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||||
|
.info-item { flex: 1; min-width: 45%; text-align: center; background: #e8e8e8; border-radius: 12px; padding: 12px; }
|
||||||
|
.info-label { font-size: 11px; color: #999; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
|
||||||
|
.info-value { font-size: 16px; font-weight: 600; }
|
||||||
|
.developer-name { font-size: 18px; font-weight: 600; margin-bottom: 12px; }
|
||||||
|
.link-row { display: flex; align-items: center; padding: 12px 0; border-top: 1px solid rgba(0,0,0,0.1); }
|
||||||
|
.link-icon { font-size: 14px; font-weight: 600; color: #666; width: 70px; }
|
||||||
|
.link-value { flex: 1; font-size: 14px; color: #0084ff; text-decoration: none; }
|
||||||
|
.link-value:hover { text-decoration: underline; }
|
||||||
|
.link-arrow { font-size: 16px; color: #ccc; }
|
||||||
|
.bitcoin-row { display: flex; align-items: center; background: rgba(247, 147, 26, 0.08); border-radius: 12px; padding: 14px; gap: 10px; }
|
||||||
|
.bitcoin-label { font-size: 18px; font-weight: 600; color: #f7931a; }
|
||||||
|
.bitcoin-address { flex: 1; font-size: 11px; font-family: monospace; color: #666; word-break: break-all; }
|
||||||
|
.copy-btn { font-size: 12px; color: #999; cursor: pointer; min-width: 50px; text-align: right; }
|
||||||
|
.copy-btn:hover { color: #0084ff; }
|
||||||
|
.footer { text-align: center; margin-top: 32px; padding-top: 20px; border-top: 1px solid #ddd; }
|
||||||
|
.copyright { font-size: 12px; color: #999; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<a href="/" class="back-link">← Back to syu.is</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="app-header">
|
||||||
|
<img src="/static/app.png" alt="Aiat" class="app-icon">
|
||||||
|
<div class="app-name">Aiat</div>
|
||||||
|
<div class="app-version">v1.111.0</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<p class="description">Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">App Information</div>
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">Version</div>
|
||||||
|
<div class="info-value">1.111.0</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">Category</div>
|
||||||
|
<div class="info-value">Social</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">Supported OS</div>
|
||||||
|
<div class="info-value">iOS 26.0+</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">Price</div>
|
||||||
|
<div class="info-value">Free</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">Developer</div>
|
||||||
|
<div class="developer-name">syui</div>
|
||||||
|
<div class="link-row">
|
||||||
|
<span class="link-icon">Git</span>
|
||||||
|
<a href="https://git.syui.ai/syui" class="link-value" target="_blank">git.syui.ai/syui</a>
|
||||||
|
<span class="link-arrow">→</span>
|
||||||
|
</div>
|
||||||
|
<div class="link-row">
|
||||||
|
<span class="link-icon">ATProto</span>
|
||||||
|
<a href="https://syu.is/syui" class="link-value" target="_blank">syu.is/syui</a>
|
||||||
|
<span class="link-arrow">→</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">Bitcoin</div>
|
||||||
|
<div class="bitcoin-row">
|
||||||
|
<span class="bitcoin-label">₿</span>
|
||||||
|
<span class="bitcoin-address" id="btc-address">3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr</span>
|
||||||
|
<span class="copy-btn" onclick="copyBTC()">copy</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p class="copyright">© syui</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function copyBTC() {
|
||||||
|
const addr = document.getElementById('btc-address').textContent;
|
||||||
|
navigator.clipboard.writeText(addr).then(() => {
|
||||||
|
const btn = document.querySelector('.copy-btn');
|
||||||
|
btn.textContent = 'copied!';
|
||||||
|
btn.style.color = '#4CAF50';
|
||||||
|
setTimeout(() => {
|
||||||
|
btn.textContent = 'copy';
|
||||||
|
btn.style.color = '';
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
100
html/about/support/help.html
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ja">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||||
|
<title>Help - syu.is</title>
|
||||||
|
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #1a1a1a;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body { background: #000; color: #e0e0e0; }
|
||||||
|
a { color: #6bb3ff; }
|
||||||
|
h1, h2, h3 { color: #fff; }
|
||||||
|
}
|
||||||
|
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
|
||||||
|
h2 { font-size: 20px; margin: 24px 0 12px; }
|
||||||
|
h3 { font-size: 16px; margin: 16px 0 8px; }
|
||||||
|
p { margin-bottom: 16px; }
|
||||||
|
ul { margin: 0 0 16px 24px; }
|
||||||
|
li { margin-bottom: 8px; }
|
||||||
|
a { color: #0066cc; text-decoration: none; }
|
||||||
|
a:hover { text-decoration: underline; }
|
||||||
|
.header { margin-bottom: 32px; }
|
||||||
|
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
|
||||||
|
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
|
||||||
|
.faq-item { margin-bottom: 24px; }
|
||||||
|
.contact-box { background: #f5f5f5; padding: 20px; border-radius: 8px; margin: 20px 0; }
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.contact-box { background: #1a1a1a; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<a href="/" class="back-link">← Back to syu.is</a>
|
||||||
|
<h1>Help Center</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>About syu.is</h2>
|
||||||
|
<p>syu.is is a social networking service built on the AT Protocol (Authenticated Transfer Protocol). It allows users to share content, connect with others, and participate in a decentralized social network.</p>
|
||||||
|
|
||||||
|
<h2>Frequently Asked Questions</h2>
|
||||||
|
|
||||||
|
<div class="faq-item">
|
||||||
|
<h3>What is the AT Protocol?</h3>
|
||||||
|
<p>The AT Protocol is a decentralized social networking protocol that allows users to own their data and identity. It enables federation between different services while maintaining user control.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="faq-item">
|
||||||
|
<h3>How do I create an account?</h3>
|
||||||
|
<p>You can create an account by downloading the app or visiting the website. You'll need to provide an email address and choose a username.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="faq-item">
|
||||||
|
<h3>How do I reset my password?</h3>
|
||||||
|
<p>You can reset your password through the login screen by selecting "Forgot Password" and following the instructions sent to your email.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="faq-item">
|
||||||
|
<h3>How do I delete my account?</h3>
|
||||||
|
<p>You can delete your account through Settings > Account. Please note that account deletion is permanent and cannot be undone.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="faq-item">
|
||||||
|
<h3>How do I report abuse or inappropriate content?</h3>
|
||||||
|
<p>You can report content by using the report function available on each post. Our moderation team will review reports and take appropriate action.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Contact</h2>
|
||||||
|
<div class="contact-box">
|
||||||
|
<p>For additional support or questions:</p>
|
||||||
|
<ul>
|
||||||
|
<li>GitHub: <a href="https://github.com/syui" target="_blank">github.com/syui</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Related Links</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/about/support/tos">Terms of Service</a></li>
|
||||||
|
<li><a href="/about/support/privacy-policy">Privacy Policy</a></li>
|
||||||
|
<li><a href="/about/support/license">License</a></li>
|
||||||
|
<li><a href="/about/support/app">App Info</a></li>
|
||||||
|
<li><a href="https://atproto.com" target="_blank">AT Protocol Documentation</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p>Last updated: 2025</p>
|
||||||
|
<p>© syui</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
66
html/about/support/license.html
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ja">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||||
|
<title>License - syu.is</title>
|
||||||
|
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #1a1a1a;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body { background: #000; color: #e0e0e0; }
|
||||||
|
a { color: #6bb3ff; }
|
||||||
|
h1, h2, h3 { color: #fff; }
|
||||||
|
}
|
||||||
|
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
|
||||||
|
h2 { font-size: 20px; margin: 24px 0 12px; }
|
||||||
|
p { margin-bottom: 16px; }
|
||||||
|
ul { margin: 0 0 16px 24px; }
|
||||||
|
li { margin-bottom: 8px; }
|
||||||
|
a { color: #0066cc; text-decoration: none; }
|
||||||
|
a:hover { text-decoration: underline; }
|
||||||
|
.header { margin-bottom: 32px; }
|
||||||
|
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
|
||||||
|
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
|
||||||
|
pre { background: #f5f5f5; padding: 16px; border-radius: 8px; overflow-x: auto; font-size: 13px; }
|
||||||
|
@media (prefers-color-scheme: dark) { pre { background: #1a1a1a; } }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<a href="/" class="back-link">← Back to syu.is</a>
|
||||||
|
<h1>License</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Aiat (iOS/Android App)</h2>
|
||||||
|
<p>This application is based on the Bluesky Social App, which is open source software.</p>
|
||||||
|
|
||||||
|
<h2>Open Source Licenses</h2>
|
||||||
|
<p>This app uses the following open source software:</p>
|
||||||
|
|
||||||
|
<h3>Bluesky Social App</h3>
|
||||||
|
<p>Licensed under the MIT License</p>
|
||||||
|
<p><a href="https://github.com/bluesky-social/social-app" target="_blank">https://github.com/bluesky-social/social-app</a></p>
|
||||||
|
|
||||||
|
<h3>AT Protocol</h3>
|
||||||
|
<p>Licensed under the MIT License / Apache 2.0</p>
|
||||||
|
<p><a href="https://github.com/bluesky-social/atproto" target="_blank">https://github.com/bluesky-social/atproto</a></p>
|
||||||
|
|
||||||
|
<h2>Third Party Libraries</h2>
|
||||||
|
<p>This application includes various third-party libraries, each with their own licenses. For a complete list, please see the application's source code repository.</p>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p>Last updated: 2025</p>
|
||||||
|
<p>© syui</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
92
html/about/support/privacy.html
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ja">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||||
|
<title>Privacy Policy - syu.is</title>
|
||||||
|
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #1a1a1a;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body { background: #000; color: #e0e0e0; }
|
||||||
|
a { color: #6bb3ff; }
|
||||||
|
h1, h2, h3 { color: #fff; }
|
||||||
|
}
|
||||||
|
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
|
||||||
|
h2 { font-size: 20px; margin: 24px 0 12px; }
|
||||||
|
p { margin-bottom: 16px; }
|
||||||
|
ul { margin: 0 0 16px 24px; }
|
||||||
|
li { margin-bottom: 8px; }
|
||||||
|
a { color: #0066cc; text-decoration: none; }
|
||||||
|
a:hover { text-decoration: underline; }
|
||||||
|
.header { margin-bottom: 32px; }
|
||||||
|
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
|
||||||
|
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<a href="/" class="back-link">← Back to syu.is</a>
|
||||||
|
<h1>Privacy Policy</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>1. Introduction</h2>
|
||||||
|
<p>This Privacy Policy explains how syu.is collects, uses, and protects your personal information when you use our service.</p>
|
||||||
|
|
||||||
|
<h2>2. Information We Collect</h2>
|
||||||
|
<p>We collect the following types of information:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Account Information:</strong> Email address, username, and profile information you provide</li>
|
||||||
|
<li><strong>Content:</strong> Posts, messages, and other content you create on the platform</li>
|
||||||
|
<li><strong>Usage Data:</strong> Information about how you interact with our service</li>
|
||||||
|
<li><strong>Device Information:</strong> Browser type, operating system, and device identifiers</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>3. How We Use Your Information</h2>
|
||||||
|
<p>We use your information to:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Provide and maintain our service</li>
|
||||||
|
<li>Improve and personalize your experience</li>
|
||||||
|
<li>Communicate with you about the service</li>
|
||||||
|
<li>Ensure security and prevent abuse</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>4. Data Sharing</h2>
|
||||||
|
<p>As part of the AT Protocol federation, your public content may be shared with other servers in the network. We do not sell your personal information to third parties.</p>
|
||||||
|
|
||||||
|
<h2>5. Data Security</h2>
|
||||||
|
<p>We implement appropriate security measures to protect your personal information. However, no method of transmission over the Internet is 100% secure.</p>
|
||||||
|
|
||||||
|
<h2>6. Your Rights</h2>
|
||||||
|
<p>You have the right to:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Access your personal data</li>
|
||||||
|
<li>Request correction of your data</li>
|
||||||
|
<li>Request deletion of your account</li>
|
||||||
|
<li>Export your data</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>7. Cookies</h2>
|
||||||
|
<p>We use cookies and similar technologies to maintain your session and improve your experience.</p>
|
||||||
|
|
||||||
|
<h2>8. Changes to This Policy</h2>
|
||||||
|
<p>We may update this Privacy Policy from time to time. We will notify you of any significant changes.</p>
|
||||||
|
|
||||||
|
<h2>9. Contact</h2>
|
||||||
|
<p>For privacy-related questions, please visit our <a href="/about/support/help">Help page</a>.</p>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p>Last updated: 2025</p>
|
||||||
|
<p>© syui</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
84
html/about/support/tos.html
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ja">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||||
|
<title>Terms of Service - syu.is</title>
|
||||||
|
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #1a1a1a;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body { background: #000; color: #e0e0e0; }
|
||||||
|
a { color: #6bb3ff; }
|
||||||
|
h1, h2, h3 { color: #fff; }
|
||||||
|
}
|
||||||
|
h1 { font-size: 28px; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 1px solid #ddd; }
|
||||||
|
h2 { font-size: 20px; margin: 24px 0 12px; }
|
||||||
|
p { margin-bottom: 16px; }
|
||||||
|
ul { margin: 0 0 16px 24px; }
|
||||||
|
li { margin-bottom: 8px; }
|
||||||
|
a { color: #0066cc; text-decoration: none; }
|
||||||
|
a:hover { text-decoration: underline; }
|
||||||
|
.header { margin-bottom: 32px; }
|
||||||
|
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; }
|
||||||
|
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<a href="/" class="back-link">← Back to syu.is</a>
|
||||||
|
<h1>Terms of Service</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>1. Introduction</h2>
|
||||||
|
<p>Welcome to syu.is. By using our service, you agree to these terms. Please read them carefully.</p>
|
||||||
|
|
||||||
|
<h2>2. Service Description</h2>
|
||||||
|
<p>syu.is is a social networking service built on the AT Protocol. We provide a platform for users to share content and connect with others.</p>
|
||||||
|
|
||||||
|
<h2>3. User Responsibilities</h2>
|
||||||
|
<p>As a user of syu.is, you agree to:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Provide accurate information when creating an account</li>
|
||||||
|
<li>Keep your account credentials secure</li>
|
||||||
|
<li>Not use the service for illegal activities</li>
|
||||||
|
<li>Respect other users and their content</li>
|
||||||
|
<li>Comply with applicable laws and regulations</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>4. Content Guidelines</h2>
|
||||||
|
<p>Users are responsible for the content they post. Prohibited content includes:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Illegal content</li>
|
||||||
|
<li>Harassment or abuse</li>
|
||||||
|
<li>Spam or misleading information</li>
|
||||||
|
<li>Content that violates others' rights</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>5. Privacy</h2>
|
||||||
|
<p>Your privacy is important to us. Please review our <a href="/about/support/privacy-policy">Privacy Policy</a> to understand how we handle your data.</p>
|
||||||
|
|
||||||
|
<h2>6. Disclaimer</h2>
|
||||||
|
<p>The service is provided "as is" without warranties of any kind. We are not liable for any damages arising from your use of the service.</p>
|
||||||
|
|
||||||
|
<h2>7. Changes to Terms</h2>
|
||||||
|
<p>We may update these terms from time to time. Continued use of the service after changes constitutes acceptance of the new terms.</p>
|
||||||
|
|
||||||
|
<h2>8. Contact</h2>
|
||||||
|
<p>For questions about these terms, please visit our <a href="/about/support/help">Help page</a>.</p>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p>Last updated: 2025</p>
|
||||||
|
<p>© syui</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
135
html/index.html
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ja">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||||
|
<title>App Info - Aiat</title>
|
||||||
|
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #1a1a1a;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body { background: #000; color: #e0e0e0; }
|
||||||
|
a { color: #6bb3ff; }
|
||||||
|
h1, h2, h3 { color: #fff; }
|
||||||
|
.section { background: #1a1a1a; }
|
||||||
|
.info-item { background: #2a2a2a; }
|
||||||
|
}
|
||||||
|
.header { margin-bottom: 32px; }
|
||||||
|
.back-link { display: inline-block; margin-bottom: 16px; font-size: 14px; color: #0066cc; text-decoration: none; }
|
||||||
|
.back-link:hover { text-decoration: underline; }
|
||||||
|
.app-header { text-align: center; margin-bottom: 32px; }
|
||||||
|
.app-icon { width: 80px; height: 80px; border-radius: 18px; margin-bottom: 12px; }
|
||||||
|
.app-name { font-size: 24px; font-weight: bold; margin-bottom: 4px; }
|
||||||
|
.app-version { font-size: 14px; color: #666; }
|
||||||
|
.section { background: #f5f5f5; border-radius: 16px; padding: 20px; margin-bottom: 16px; }
|
||||||
|
.section-title { font-size: 13px; font-weight: 600; color: #999; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 12px; }
|
||||||
|
.description { font-size: 15px; line-height: 22px; }
|
||||||
|
.info-grid { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||||
|
.info-item { flex: 1; min-width: 45%; text-align: center; background: #e8e8e8; border-radius: 12px; padding: 12px; }
|
||||||
|
.info-label { font-size: 11px; color: #999; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
|
||||||
|
.info-value { font-size: 16px; font-weight: 600; }
|
||||||
|
.developer-name { font-size: 18px; font-weight: 600; margin-bottom: 12px; }
|
||||||
|
.link-row { display: flex; align-items: center; padding: 12px 0; border-top: 1px solid rgba(0,0,0,0.1); }
|
||||||
|
.link-icon { font-size: 14px; font-weight: 600; color: #666; width: 70px; }
|
||||||
|
.link-value { flex: 1; font-size: 14px; color: #0084ff; text-decoration: none; }
|
||||||
|
.link-value:hover { text-decoration: underline; }
|
||||||
|
.link-arrow { font-size: 16px; color: #ccc; }
|
||||||
|
.bitcoin-row { display: flex; align-items: center; background: rgba(247, 147, 26, 0.08); border-radius: 12px; padding: 14px; gap: 10px; }
|
||||||
|
.bitcoin-label { font-size: 18px; font-weight: 600; color: #f7931a; }
|
||||||
|
.bitcoin-address { flex: 1; font-size: 11px; font-family: monospace; color: #666; word-break: break-all; }
|
||||||
|
.copy-btn { font-size: 12px; color: #999; cursor: pointer; min-width: 50px; text-align: right; }
|
||||||
|
.copy-btn:hover { color: #0084ff; }
|
||||||
|
.footer { text-align: center; margin-top: 32px; padding-top: 20px; border-top: 1px solid #ddd; }
|
||||||
|
.copyright { font-size: 12px; color: #999; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<a href="/" class="back-link">← Back to syu.is</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="app-header">
|
||||||
|
<img src="/static/app.png" alt="Aiat" class="app-icon">
|
||||||
|
<div class="app-name">Aiat</div>
|
||||||
|
<div class="app-version">v1.111.0</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<p class="description">Aiat is a social networking application based on AT Protocol. Connect with your community on syu.is.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">App Information</div>
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">Version</div>
|
||||||
|
<div class="info-value">1.111.2</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">Category</div>
|
||||||
|
<div class="info-value">Social</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">Supported OS</div>
|
||||||
|
<div class="info-value">iOS 26.0+</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">Price</div>
|
||||||
|
<div class="info-value">Free</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">Developer</div>
|
||||||
|
<div class="developer-name">syui</div>
|
||||||
|
<div class="link-row">
|
||||||
|
<span class="link-icon">Git</span>
|
||||||
|
<a href="https://git.syui.ai/syui" class="link-value" target="_blank">git.syui.ai/syui</a>
|
||||||
|
<span class="link-arrow">→</span>
|
||||||
|
</div>
|
||||||
|
<div class="link-row">
|
||||||
|
<span class="link-icon">ATProto</span>
|
||||||
|
<a href="https://syu.is/syui" class="link-value" target="_blank">syu.is/syui</a>
|
||||||
|
<span class="link-arrow">→</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">Bitcoin</div>
|
||||||
|
<div class="bitcoin-row">
|
||||||
|
<span class="bitcoin-label">₿</span>
|
||||||
|
<span class="bitcoin-address" id="btc-address">3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr</span>
|
||||||
|
<span class="copy-btn" onclick="copyBTC()">copy</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p class="copyright">© syui</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function copyBTC() {
|
||||||
|
const addr = document.getElementById('btc-address').textContent;
|
||||||
|
navigator.clipboard.writeText(addr).then(() => {
|
||||||
|
const btn = document.querySelector('.copy-btn');
|
||||||
|
btn.textContent = 'copied!';
|
||||||
|
btn.style.color = '#4CAF50';
|
||||||
|
setTimeout(() => {
|
||||||
|
btn.textContent = 'copy';
|
||||||
|
btn.style.color = '';
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
BIN
html/static/favicon.png
Normal file
|
After Width: | Height: | Size: 1005 B |
105
install.zsh
@@ -52,6 +52,12 @@ function at-repos-env() {
|
|||||||
name=${host%%.*}
|
name=${host%%.*}
|
||||||
domain=${host##*.}
|
domain=${host##*.}
|
||||||
dport=5000
|
dport=5000
|
||||||
|
|
||||||
|
typeset -A PINNED_COMMITS
|
||||||
|
PINNED_COMMITS=(
|
||||||
|
[indigo]="d49b454196351c988ceb5ce1f5e21b689487b5ab"
|
||||||
|
[atproto]="104e6ed37b0589cc000109dc76316be35b2257e1"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Arrays for patch management
|
# Arrays for patch management
|
||||||
@@ -100,6 +106,10 @@ function at-repos-pull() {
|
|||||||
echo $repo
|
echo $repo
|
||||||
if [ -d $d/repos/${repo##*/} ];then
|
if [ -d $d/repos/${repo##*/} ];then
|
||||||
cd $d/repos/${repo##*/}
|
cd $d/repos/${repo##*/}
|
||||||
|
# Clean up before pull: reset changes, remove .orig files and untracked patch-created files
|
||||||
|
git checkout -- .
|
||||||
|
find . -name "*.orig" -type f -delete 2>/dev/null
|
||||||
|
git clean -fd 2>/dev/null
|
||||||
git stash -u
|
git stash -u
|
||||||
if ! git pull;then
|
if ! git pull;then
|
||||||
rm -rf $d/repos/${repo##*/}
|
rm -rf $d/repos/${repo##*/}
|
||||||
@@ -116,29 +126,24 @@ function at-repos-pull() {
|
|||||||
cd $d
|
cd $d
|
||||||
}
|
}
|
||||||
|
|
||||||
function at-repos-social-app-avatar-write() {
|
function at-repos-checkout-pinned() {
|
||||||
did_admin=did:plc:6qyecktefllvenje24fcxnie
|
echo "🔒 Checking out pinned commits..."
|
||||||
dt=$d/repos/social-app/src
|
cd $d/repos
|
||||||
cd $dt
|
for repo_name pinned_commit in ${(kv)PINNED_COMMITS}; do
|
||||||
grep -R syu.is .|cut -d : -f 1|sort -u|xargs sediment "s/syu.is/${host}/g"
|
if [ -n "$pinned_commit" ] && [ -d "$d/repos/$repo_name" ]; then
|
||||||
grep -R web.syu.is .|cut -d : -f 1|sort -u|xargs sediment "s/web.syu.is/web.${host}/g"
|
echo " 📌 $repo_name -> $pinned_commit"
|
||||||
f=$dt/lib/constants.ts
|
cd $d/repos/$repo_name
|
||||||
sediment "s#export const BSKY_SERVICE = 'https://bsky.social'#export const BSKY_SERVICE = 'https://${host}'#g" $f
|
git fetch origin
|
||||||
sediment "s#export const BSKY_SERVICE_DID = 'did:web:bsky.social'#export const BSKY_SERVICE_DID = 'did:web:${host}'#g" $f
|
git checkout $pinned_commit
|
||||||
sediment "s#export const PUBLIC_BSKY_SERVICE = 'https://public.api.bsky.app'#export const PUBLIC_BSKY_SERVICE = 'https://bsky.${host}'#g" $f
|
cd $d/repos
|
||||||
sediment "s#export const PUBLIC_APPVIEW = 'https://api.bsky.app'#export const PUBLIC_APPVIEW = 'https://bsky.${host}'#g" $f
|
fi
|
||||||
sediment "s#export const PUBLIC_APPVIEW_DID = 'did:web:api.bsky.app'#export const PUBLIC_APPVIEW_DID = 'did:web:bsky.${host}'#g" $f
|
done
|
||||||
|
cd $d
|
||||||
f=$dt/view/com/util/UserAvatar.tsx
|
|
||||||
curl -sL https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/src/view/com/util/UserAvatar.tsx -o $f
|
|
||||||
sediment "s#/img/avatar/plain/#https://cdn.web.syu.is/img/avatar/plain/#g" $f
|
|
||||||
sediment "s#/img/avatar_thumbnail/plain/#https://bsky.${host}/img/avatar/plain/#g" $f
|
|
||||||
sediment "s#source={{uri: avatar}}#source={{ uri: hackModifyThumbnailPath(avatar, 1 > 0), }}#g" $f
|
|
||||||
curl -sL https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/src/lib/strings/url-helpers.ts -o $dt/lib/strings/url-helpers.ts
|
|
||||||
sediment "s#https://go.web.syu.is/redirect?u=\${encodeURIComponent(url)}#\${url}#g" $dt/lib/strings/url-helpers.ts
|
|
||||||
grep -R $did_admin .|cut -d : -f 1|sort -u|xargs sediment "s/${did_admin}/${did}/g"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function at-repos-social-app-ios-patch() {
|
||||||
|
$d/ios/setup.zsh
|
||||||
|
}
|
||||||
|
|
||||||
# Common patch function with status detection
|
# Common patch function with status detection
|
||||||
function apply-patch() {
|
function apply-patch() {
|
||||||
@@ -333,6 +338,9 @@ function at-repos-build-docker-atproto() {
|
|||||||
for ((i=1; i<=${#services}; i++)); do
|
for ((i=1; i<=${#services}; i++)); do
|
||||||
service=${services[$i]}
|
service=${services[$i]}
|
||||||
docker compose build --no-cache $service
|
docker compose build --no-cache $service
|
||||||
|
if [ "$service" = "ozone" ]; then
|
||||||
|
docker compose build --no-cache ${service}-web
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
docker compose build --no-cache $1
|
docker compose build --no-cache $1
|
||||||
@@ -354,12 +362,11 @@ function at-repos-push-reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function at-repos-push-docker() {
|
function at-repos-push-docker() {
|
||||||
if [ -z "$1" ];then
|
if [ -z "$1" ] || [ "$1" = "push" ]; then
|
||||||
for ((i=1; i<=${#services}; i++)); do
|
for service in "${services[@]}"; do
|
||||||
service=${services[$i]}
|
|
||||||
docker tag at-${service}:latest localhost:${dport}/${service}:latest
|
docker tag at-${service}:latest localhost:${dport}/${service}:latest
|
||||||
docker push localhost:${dport}/${service}:latest
|
docker push localhost:${dport}/${service}:latest
|
||||||
if [ "$service" == "ozone" ];then
|
if [ "$service" = "ozone" ]; then
|
||||||
docker tag at-${service}-web:latest localhost:${dport}/${service}-web:latest
|
docker tag at-${service}-web:latest localhost:${dport}/${service}-web:latest
|
||||||
docker push localhost:${dport}/${service}-web:latest
|
docker push localhost:${dport}/${service}-web:latest
|
||||||
fi
|
fi
|
||||||
@@ -404,28 +411,55 @@ function at-repos-reset-bgs-db() {
|
|||||||
echo "⚙️ Updating Slurp Config..."
|
echo "⚙️ Updating Slurp Config..."
|
||||||
docker exec -i $dp psql -U postgres -d bgs -c "UPDATE slurp_configs SET new_subs_disabled = false, new_pds_per_day_limit = 1000 WHERE id = 1;"
|
docker exec -i $dp psql -U postgres -d bgs -c "UPDATE slurp_configs SET new_subs_disabled = false, new_pds_per_day_limit = 1000 WHERE id = 1;"
|
||||||
|
|
||||||
echo "🔗 Registering Trusted Domain & Resetting Repos..."
|
# host=pds:3000
|
||||||
|
echo "🔗 Registering Trusted Domain..."
|
||||||
# Retry loop for addTrustedDomain as BGS might still be warming up
|
# Retry loop for addTrustedDomain as BGS might still be warming up
|
||||||
for i in {1..5}; do
|
for i in {1..5}; do
|
||||||
if curl -f -X POST "https://bgs.${host}/admin/pds/addTrustedDomain?domain=${host}" -H "Authorization: Bearer ${BGS_ADMIN_KEY}"; then
|
if curl -f -X POST "https://bgs.${host}/admin/pds/addTrustedDomain?domain=${host}" -H "Authorization: Bearer ${BGS_ADMIN_KEY}"; then
|
||||||
|
echo ""
|
||||||
echo "✅ Trusted domain registered"
|
echo "✅ Trusted domain registered"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
echo "Bot failed to contact BGS (attempt $i/5)... waiting 5s"
|
echo "Failed to contact BGS (attempt $i/5)... waiting 5s"
|
||||||
sleep 5
|
sleep 5
|
||||||
done
|
done
|
||||||
|
|
||||||
|
echo "🔗 Requesting PDS Crawl..."
|
||||||
|
# Request BGS to crawl the PDS - this registers the PDS and starts subscription
|
||||||
|
for i in {1..5}; do
|
||||||
|
result=$(curl -s -X POST "https://bgs.${host}/admin/pds/requestCrawl" \
|
||||||
|
-H "Authorization: Bearer ${BGS_ADMIN_KEY}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"hostname\":\"{$host}\"}" \
|
||||||
|
-w "%{http_code}" -o /dev/null)
|
||||||
|
if [ "$result" = "200" ]; then
|
||||||
|
echo "✅ PDS crawl requested successfully"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "Failed to request crawl (attempt $i/5, status: $result)... waiting 5s"
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "⏳ Waiting 5s for BGS to connect to PDS..."
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
echo "🔄 Triggering repo sync for existing users..."
|
||||||
for ((i=1; i<=${#handles}; i++)); do
|
for ((i=1; i<=${#handles}; i++)); do
|
||||||
handle=${handles[$i]}
|
handle=${handles[$i]}
|
||||||
did=`curl -sL "https://${host}/xrpc/com.atproto.repo.describeRepo?repo=${handle}" |jq -r .did`
|
did=$(curl -sL "https://${host}/xrpc/com.atproto.repo.describeRepo?repo=${handle}" | jq -r .did)
|
||||||
if [ ! -z "$did" ] && [ "$did" != "null" ]; then
|
if [ -n "$did" ] && [ "$did" != "null" ]; then
|
||||||
echo "Resetting repo: $handle ($did)"
|
echo " Syncing repo: $handle ($did)"
|
||||||
curl -X POST "https://bgs.${host}/admin/repo/reset?did=${did}" \
|
# Use takedown=false to trigger a resync without actually taking down
|
||||||
-H "Authorization: Bearer ${BGS_ADMIN_KEY}"
|
curl -s -X POST "https://bgs.${host}/admin/repo/takedown?did=${did}&takedown=false" \
|
||||||
|
-H "Authorization: Bearer ${BGS_ADMIN_KEY}" || true
|
||||||
else
|
else
|
||||||
echo "Skipping reset for $handle (DID not found)"
|
echo " Skipping $handle (DID not found)"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ BGS reset complete!"
|
||||||
|
echo " PDS should now be subscribed and syncing repos."
|
||||||
}
|
}
|
||||||
|
|
||||||
function at-repos-feed-generator-start-push() {
|
function at-repos-feed-generator-start-push() {
|
||||||
@@ -496,7 +530,7 @@ case "$1" in
|
|||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
patch)
|
patch)
|
||||||
at-repos-social-app-avatar-write
|
at-repos-social-app-ios-patch
|
||||||
at-repos-patch-apply-all
|
at-repos-patch-apply-all
|
||||||
at-repos-ozone-patch
|
at-repos-ozone-patch
|
||||||
show-failed-patches
|
show-failed-patches
|
||||||
@@ -536,7 +570,8 @@ case "`cat /etc/hostname`" in
|
|||||||
*)
|
*)
|
||||||
at-repos-clone
|
at-repos-clone
|
||||||
at-repos-pull
|
at-repos-pull
|
||||||
at-repos-social-app-avatar-write
|
at-repos-checkout-pinned
|
||||||
|
at-repos-social-app-ios-patch
|
||||||
at-repos-patch-apply-all
|
at-repos-patch-apply-all
|
||||||
at-repos-ozone-patch
|
at-repos-ozone-patch
|
||||||
show-failed-patches
|
show-failed-patches
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ APP_NAME="Aiat"
|
|||||||
REPO_DIR="../repos/social-app"
|
REPO_DIR="../repos/social-app"
|
||||||
APP_SLUG="aiat"
|
APP_SLUG="aiat"
|
||||||
APP_SCHEME="syui"
|
APP_SCHEME="syui"
|
||||||
BUNDLE_ID="ai.syui.at"
|
|
||||||
APP_GROUP="group.ai.syui.at"
|
APP_GROUP="group.ai.syui.at"
|
||||||
|
APP_MAIL=user@example.com
|
||||||
|
APP_KEYCHAIN=@keychain:KEYCHAIN_NAME
|
||||||
|
BUNDLE_ID="ai.syui.at"
|
||||||
SERVICE_URL="https://syu.is"
|
SERVICE_URL="https://syu.is"
|
||||||
HELP_URL="https://syu.is/about/support/help"
|
HELP_URL="https://syu.is/about/support/help"
|
||||||
PRIVACY_URL="https://syu.is/about/support/privacy-policy"
|
PRIVACY_URL="https://syu.is/about/support/privacy-policy"
|
||||||
@@ -11,3 +13,5 @@ TERMS_URL="https://syu.is/about/support/tos"
|
|||||||
REPO_DIR="../repos/social-app"
|
REPO_DIR="../repos/social-app"
|
||||||
CONFIG_FILE="$REPO_DIR/app.config.js"
|
CONFIG_FILE="$REPO_DIR/app.config.js"
|
||||||
CONSTANTS_FILE="$REPO_DIR/src/lib/constants.ts"
|
CONSTANTS_FILE="$REPO_DIR/src/lib/constants.ts"
|
||||||
|
IOS_CERTIFICATE_NAME="Apple Distribution: $TEAM($TEAM_ID)"
|
||||||
|
PDS_HOST=syu.is
|
||||||
@@ -5,68 +5,5 @@ https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/LICE
|
|||||||
|
|
||||||
2. "Bluesky"という名称を使用しないこと。アイコンの変更。リンクの変更
|
2. "Bluesky"という名称を使用しないこと。アイコンの変更。リンクの変更
|
||||||
|
|
||||||
3. selfhostでも動くこと。本来のsocial-appは動きませんので、これは不便なのでiosアプリに出品することにしました。なお、これはすでにpatchで実現しています。
|
3. selfhostでも動くこと。これはすでにpatchで実現しています。
|
||||||
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ ./install.zsh pull
|
|
||||||
$ ./install.zsh patch
|
|
||||||
$ ./ios/setup.zsh
|
|
||||||
$ ./ios/preview.zsh
|
|
||||||
```
|
|
||||||
|
|
||||||
## 実装済み
|
|
||||||
|
|
||||||
1. 最初の画面で、webではちゃんと私のサイトのロゴが表示されていますが、ios モバイル版では、未だにBluesky (icon)です。アカウント作成、サインイン、が表示されています。
|
|
||||||
|
|
||||||
2. 上のメニューバーにもBlueskyのロゴが表示されています。
|
|
||||||
|
|
||||||
3. サインイン後のホスティングプロバイダーで中身はsyu.isですが、表示は"Bluesky Social"になっています。これをsyu.isに変更してください。ios/webでコードは異なります。
|
|
||||||
|
|
||||||
4. チャット機能
|
|
||||||
チャット機能は今回無効化するので、下メニューバーやプロフィール、設定画面に表示しないでください。
|
|
||||||
|
|
||||||
5. 設定ボタン(左カラム)を押すと、フィードバック、ヘルプが表示されますが、非表示にしてください。
|
|
||||||
|
|
||||||
6. 設定ボタン(左カラム)を押すと、フィード、リスト、保存済みの項目がありますが、これを削除してください。
|
|
||||||
|
|
||||||
7. 設定ボタン(左カラム)を押すと、下に利用規約、プライバシーポリシーが表示されますが、リンクがbsky.socialです。
|
|
||||||
- /about/support/privacy-policy
|
|
||||||
- /about/support/tos
|
|
||||||
このページを独自に作って表示してください。
|
|
||||||
|
|
||||||
8. LOG 09:52:20 (logger) Poll latest failed {
|
|
||||||
"feed": "following",
|
|
||||||
"message": "Error: Could not find repo: did:plc:z72i7hdynmk6r22z27h6tvur"
|
|
||||||
}
|
|
||||||
|
|
||||||
9. LOG 10:24:03 (metric) router:navigate
|
|
||||||
LOG 10:24:04 (dms-agent) init failed {
|
|
||||||
"safeMessage": "could not resolve iss did"
|
|
||||||
}
|
|
||||||
|
|
||||||
9. 設定ボタン(左カラム)の一番下、利用規約やプライバシーポリシーが表示されいてるライセンスという項目を追加。ページを追加して、ライセンスの表示。
|
|
||||||
https://github.com/bluesky-social/social-app
|
|
||||||
https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/LICENSE
|
|
||||||
|
|
||||||
10. アカウント作成時(create account)のページに"Having trouble?"で`Contact support`のリンクがありますが、これを削除してください。
|
|
||||||
|
|
||||||
11. スタートページ、つまり、`Create account`, `Sign in`があるページの一番下にライセンスページへのリンクを追加してください。また、footerに`© syui`を表示してください。このページのタイトル下にある文字`What's up?`の項目は削除。
|
|
||||||
|
|
||||||
12. スタートページのラインセンスリンクが機能しない。おそらくページ変遷に問題があるため。また、ライセンスページは上下が隠れて見えてしまうため、大きく上下に空間を開けること。
|
|
||||||
|
|
||||||
13. 利用規約、プライバシーポリシーのページの言語が日本語で書かれています。ラインセンスと同様に、英語を基本とし、日本語訳をその下に表示してください。
|
|
||||||
|
|
||||||
14. Settings/ 項目の非表示を追加。
|
|
||||||
- Helpの非表示
|
|
||||||
- Aboutのリンクを変更
|
|
||||||
|
|
||||||
## 壊れた実装
|
|
||||||
|
|
||||||
1. ログイン後のメイン画面、"Following"の項目(フィード)に表示されるものをシンプルにします。表示するのはFollowingのみで、以下のものを削除してください。
|
|
||||||
- おすすめの削除
|
|
||||||
- Discoverの削除
|
|
||||||
- アカウントを探すの削除
|
|
||||||
|
|
||||||
2. 年齢保証、年齢確認ページがでてくるのを削除。誕生日を入力する処理を削除。アプリ配布国は限定します。
|
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
BIN
ios/assets/app-icons/ios_icon_legacy_light.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
ios/assets/favicons/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
ios/assets/favicons/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 574 B |
BIN
ios/assets/favicons/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1005 B |
BIN
ios/assets/favicons/favicon.png
Normal file
|
After Width: | Height: | Size: 1005 B |
BIN
ios/assets/logo.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
202
ios/build.zsh
Executable file
@@ -0,0 +1,202 @@
|
|||||||
|
#!/bin/zsh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR=${0:a:h}
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
source .env
|
||||||
|
|
||||||
|
function sediment() {
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
sed -i '' "$@"
|
||||||
|
else
|
||||||
|
sed -i "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 絶対パスに変換
|
||||||
|
REPO_DIR="$SCRIPT_DIR/../repos/social-app"
|
||||||
|
APP_NAME="Aiat"
|
||||||
|
WORKSPACE="$REPO_DIR/ios/${APP_NAME}.xcworkspace"
|
||||||
|
SCHEME="$APP_NAME"
|
||||||
|
BUILD_DIR="$REPO_DIR/build"
|
||||||
|
MOBILEPROVISION="$REPO_DIR/embedded.mobileprovision"
|
||||||
|
ASSETS_DIR="$SCRIPT_DIR/assets"
|
||||||
|
|
||||||
|
echo "Running iOS preview workflow..."
|
||||||
|
cd "$REPO_DIR"
|
||||||
|
|
||||||
|
# 0. Environment Setup (Fix Node Version)
|
||||||
|
export NVM_DIR="$HOME/.nvm"
|
||||||
|
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
||||||
|
echo "Checking Node version..."
|
||||||
|
if command -v nvm >/dev/null; then
|
||||||
|
nvm use 22 || nvm use 20 || echo "Warning: Could not switch to Node 22/20. Current: $(node -v)"
|
||||||
|
else
|
||||||
|
echo "nvm not found, using system node: $(node -v)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. Install dependencies
|
||||||
|
echo "1. Installing dependencies (yarn)..."
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
# 1.5. Copy assets
|
||||||
|
echo "1.5. Copying assets..."
|
||||||
|
if [ -d "$ASSETS_DIR" ]; then
|
||||||
|
cp -rf "$ASSETS_DIR/"* "$REPO_DIR/assets/"
|
||||||
|
echo "✅ Copied all assets (including logo.png, logo-1024.png)"
|
||||||
|
else
|
||||||
|
echo "⚠️ Warning: $ASSETS_DIR not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
function cleanup_build {
|
||||||
|
# 1.8. Update package.json version (prevent App Store version conflict)
|
||||||
|
echo "1.8. Updating package.json version..."
|
||||||
|
if [ -n "$APP_VERSION" ]; then
|
||||||
|
# Use node to update version in package.json (already in REPO_DIR)
|
||||||
|
node -e "
|
||||||
|
const fs = require('fs');
|
||||||
|
const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
||||||
|
pkg.version = '$APP_VERSION';
|
||||||
|
fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');
|
||||||
|
"
|
||||||
|
echo " ✅ Set version to $APP_VERSION"
|
||||||
|
else
|
||||||
|
echo " ⚠️ APP_VERSION not set in .env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1.9. Update buildNumber (CFBundleVersion) with current timestamp
|
||||||
|
echo "1.9. Updating buildNumber..."
|
||||||
|
local build_number=$(date +%y%m%d%H%M%S)
|
||||||
|
sediment "s/buildNumber: '[0-9]*'/buildNumber: '${build_number}'/" "./app.config.js"
|
||||||
|
echo " ✅ Set buildNumber to $build_number"
|
||||||
|
|
||||||
|
# 2. Prebuild (Generate ios directory)
|
||||||
|
echo "2. Running Expo Prebuild..."
|
||||||
|
# Clean old ios folder to remove old entitlements/AppClip targets
|
||||||
|
rm -rf ios
|
||||||
|
npx expo prebuild --platform ios --clean
|
||||||
|
|
||||||
|
# 3. CocoaPods
|
||||||
|
echo "3. Installing CocoaPods..."
|
||||||
|
# Ensure PATH includes Homebrew ruby gems if needed
|
||||||
|
export PATH="/opt/homebrew/lib/ruby/gems/3.4.0/bin:$PATH"
|
||||||
|
cd ios
|
||||||
|
pod install
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# 4. Signing (Automated)
|
||||||
|
echo "4. Configuring Xcode Signing..."
|
||||||
|
XCODE_PROJ="ios/${APP_NAME}.xcodeproj"
|
||||||
|
if [ ! -d "$XCODE_PROJ" ]; then
|
||||||
|
XCODE_PROJ=$(find ios -name "*.xcodeproj" | head -n 1)
|
||||||
|
fi
|
||||||
|
PBXPROJ="$XCODE_PROJ/project.pbxproj"
|
||||||
|
|
||||||
|
# Set DEVELOPMENT_TEAM in pbxproj
|
||||||
|
if [ -n "$DEVELOPMENT_TEAM" ]; then
|
||||||
|
echo " Setting DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM"
|
||||||
|
sediment "s/PRODUCT_BUNDLE_IDENTIFIER = /DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM; PRODUCT_BUNDLE_IDENTIFIER = /g" "$PBXPROJ"
|
||||||
|
sediment "s/DEVELOPMENT_TEAM = \"\";/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/g" "$PBXPROJ"
|
||||||
|
sediment "s/DEVELOPMENT_TEAM = ;/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/g" "$PBXPROJ"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create/Update entitlements file with App Group
|
||||||
|
ENTITLEMENTS_FILE="ios/${APP_NAME}/${APP_NAME}.entitlements"
|
||||||
|
if [ -n "$APP_GROUP" ]; then
|
||||||
|
echo " Setting APP_GROUP=$APP_GROUP"
|
||||||
|
cat > "$ENTITLEMENTS_FILE" << EOF
|
||||||
|
<?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>aps-environment</key>
|
||||||
|
<string>production</string>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>${APP_GROUP}</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
if ! grep -q "CODE_SIGN_ENTITLEMENTS" "$PBXPROJ"; then
|
||||||
|
sediment "s/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM; CODE_SIGN_ENTITLEMENTS = ${APP_NAME}\\/${APP_NAME}.entitlements;/g" "$PBXPROJ"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Signing configured automatically"
|
||||||
|
|
||||||
|
# (Old manual step - commented out)
|
||||||
|
# open "$XCODE_PROJ"
|
||||||
|
# read
|
||||||
|
}
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
i)
|
||||||
|
cleanup_build
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "Building $APP_NAME for App Store upload..."
|
||||||
|
|
||||||
|
# ビルドディレクトリ作成
|
||||||
|
mkdir -p "$BUILD_DIR"
|
||||||
|
|
||||||
|
# アーカイブ(詳細ログ出力)
|
||||||
|
xcodebuild -workspace "$WORKSPACE" \
|
||||||
|
-scheme "$SCHEME" \
|
||||||
|
-configuration Release \
|
||||||
|
-archivePath "$BUILD_DIR/${APP_NAME}.xcarchive" \
|
||||||
|
-allowProvisioningUpdates \
|
||||||
|
DEVELOPMENT_TEAM="$DEVELOPMENT_TEAM" \
|
||||||
|
archive 2>&1 | tee "$BUILD_DIR/build.log"
|
||||||
|
|
||||||
|
# アーカイブ成功確認
|
||||||
|
if [ ! -d "$BUILD_DIR/${APP_NAME}.xcarchive" ]; then
|
||||||
|
echo "Error: Archive failed. Check $BUILD_DIR/build.log for details"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
|
||||||
|
|
||||||
|
# IPA作成
|
||||||
|
rm -rf Payload ${APP_NAME}.ipa
|
||||||
|
mkdir -p Payload
|
||||||
|
cp -R ${APP_NAME}.xcarchive/Products/Applications/${APP_NAME}.app Payload/
|
||||||
|
|
||||||
|
# store.mobileprovisionの存在確認とコピー
|
||||||
|
# https://developer.apple.com/account/resources/profiles/list
|
||||||
|
if [ ! -f "$MOBILEPROVISION" ]; then
|
||||||
|
echo "Error: store.mobileprovision not found at $MOBILEPROVISION"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp "$MOBILEPROVISION" Payload/${APP_NAME}.app/embedded.mobileprovision
|
||||||
|
|
||||||
|
# entitlements抽出
|
||||||
|
security cms -D -i Payload/${APP_NAME}.app/embedded.mobileprovision > /tmp/profile.plist
|
||||||
|
/usr/libexec/PlistBuddy -x -c "Print :Entitlements" /tmp/profile.plist > /tmp/entitlements.plist
|
||||||
|
|
||||||
|
# 署名
|
||||||
|
CERT="$IOS_CERTIFICATE_NAME"
|
||||||
|
|
||||||
|
# Frameworksディレクトリが存在する場合のみ署名
|
||||||
|
if [ -d "Payload/${APP_NAME}.app/Frameworks" ]; then
|
||||||
|
for framework in Payload/${APP_NAME}.app/Frameworks/*.framework; do
|
||||||
|
if [ -e "$framework" ]; then
|
||||||
|
echo "Signing $framework"
|
||||||
|
codesign -f -s "$CERT" "$framework"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# アプリ本体に署名
|
||||||
|
codesign -f -s "$CERT" --entitlements /tmp/entitlements.plist Payload/${APP_NAME}.app
|
||||||
|
|
||||||
|
# IPA作成
|
||||||
|
zip -r ${APP_NAME}.ipa Payload
|
||||||
|
|
||||||
|
# アップロード
|
||||||
|
xcrun altool --upload-app -f ${APP_NAME}.ipa -t ios -u "${APP_MAIL}" -p "${APP_KEYCHAIN}"
|
||||||
|
|
||||||
|
echo "Upload complete: ${APP_NAME}.ipa"
|
||||||
@@ -1,56 +1,55 @@
|
|||||||
diff --git a/Dockerfile b/Dockerfile
|
|
||||||
index 371e8402c..2e139503e 100644
|
|
||||||
--- a/Dockerfile
|
|
||||||
+++ b/Dockerfile
|
|
||||||
@@ -66,7 +66,8 @@ RUN \. "$NVM_DIR/nvm.sh" && \
|
|
||||||
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 config set registry https://registry.npmjs.org/ && \
|
|
||||||
+ yarn install --frozen-lockfile --network-timeout 100000 && \
|
|
||||||
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 && \
|
|
||||||
SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN SENTRY_RELEASE=$EXPO_PUBLIC_RELEASE_VERSION SENTRY_DIST=$EXPO_PUBLIC_BUNDLE_IDENTIFIER yarn build-web
|
|
||||||
diff --git a/app.config.js b/app.config.js
|
diff --git a/app.config.js b/app.config.js
|
||||||
index 246d8abd3..a6582864b 100644
|
index 246d8abd3..ed8f7b2b2 100644
|
||||||
--- a/app.config.js
|
--- a/app.config.js
|
||||||
+++ b/app.config.js
|
+++ b/app.config.js
|
||||||
@@ -33,8 +33,8 @@ module.exports = function (_config) {
|
@@ -18,10 +18,7 @@ module.exports = function (_config) {
|
||||||
|
const IS_DEV = !IS_TESTFLIGHT || !IS_PRODUCTION
|
||||||
|
|
||||||
|
const ASSOCIATED_DOMAINS = [
|
||||||
|
- 'applinks:bsky.app',
|
||||||
|
- 'applinks:staging.bsky.app',
|
||||||
|
- 'appclips:bsky.app',
|
||||||
|
- 'appclips:go.bsky.app', // Allows App Clip to work when scanning QR codes
|
||||||
|
+ 'applinks:syu.is',
|
||||||
|
// When testing local services, enter an ngrok (et al) domain here. It must use a standard HTTP/HTTPS port.
|
||||||
|
...(IS_DEV || IS_TESTFLIGHT ? [] : []),
|
||||||
|
]
|
||||||
|
@@ -33,27 +30,25 @@ module.exports = function (_config) {
|
||||||
return {
|
return {
|
||||||
expo: {
|
expo: {
|
||||||
version: VERSION,
|
version: VERSION,
|
||||||
- name: 'Bluesky',
|
- name: 'Bluesky',
|
||||||
- slug: 'bluesky',
|
- slug: 'bluesky',
|
||||||
|
- scheme: 'bluesky',
|
||||||
+ name: 'Aiat',
|
+ name: 'Aiat',
|
||||||
+ slug: 'aiat',
|
+ slug: 'aiat',
|
||||||
scheme: 'bluesky',
|
+ scheme: 'syui',
|
||||||
owner: 'blueskysocial',
|
owner: 'blueskysocial',
|
||||||
runtimeVersion: {
|
runtimeVersion: {
|
||||||
@@ -45,15 +45,20 @@ module.exports = function (_config) {
|
policy: 'appVersion',
|
||||||
|
},
|
||||||
|
- icon: './assets/app-icons/ios_icon_default_next.png',
|
||||||
|
+ icon: './assets/logo.png',
|
||||||
|
userInterfaceStyle: 'automatic',
|
||||||
primaryColor: '#1083fe',
|
primaryColor: '#1083fe',
|
||||||
newArchEnabled: false,
|
newArchEnabled: false,
|
||||||
ios: {
|
ios: {
|
||||||
+ infoPlist: {
|
|
||||||
+ NSAppTransportSecurity: {
|
|
||||||
+ NSAllowsArbitraryLoads: true,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
supportsTablet: false,
|
supportsTablet: false,
|
||||||
- bundleIdentifier: 'xyz.blueskyweb.app',
|
- bundleIdentifier: 'xyz.blueskyweb.app',
|
||||||
+ bundleIdentifier: 'ai.syui.at',
|
+ bundleIdentifier: 'ai.syui.at',
|
||||||
|
+ buildNumber: '__BUILD_NUMBER__',
|
||||||
config: {
|
config: {
|
||||||
usesNonExemptEncryption: false,
|
usesNonExemptEncryption: false,
|
||||||
},
|
},
|
||||||
icon:
|
- icon:
|
||||||
PLATFORM === 'web' // web build doesn't like .icon files
|
- PLATFORM === 'web' // web build doesn't like .icon files
|
||||||
? './assets/app-icons/ios_icon_default_next.png'
|
- ? './assets/app-icons/ios_icon_default_next.png'
|
||||||
- : './assets/app-icons/ios_icon_default.icon',
|
- : './assets/app-icons/ios_icon_default.icon',
|
||||||
+ : './assets/app-icons/ios_icon_default_next.png',
|
+ icon: './assets/logo.png',
|
||||||
infoPlist: {
|
infoPlist: {
|
||||||
UIBackgroundModes: ['remote-notification'],
|
UIBackgroundModes: ['remote-notification'],
|
||||||
NSCameraUsageDescription:
|
NSCameraUsageDescription:
|
||||||
@@ -113,7 +118,7 @@ module.exports = function (_config) {
|
@@ -113,7 +107,7 @@ module.exports = function (_config) {
|
||||||
entitlements: {
|
entitlements: {
|
||||||
'com.apple.developer.kernel.increased-memory-limit': true,
|
'com.apple.developer.kernel.increased-memory-limit': true,
|
||||||
'com.apple.developer.kernel.extended-virtual-addressing': true,
|
'com.apple.developer.kernel.extended-virtual-addressing': true,
|
||||||
@@ -59,7 +58,15 @@ index 246d8abd3..a6582864b 100644
|
|||||||
},
|
},
|
||||||
privacyManifests: {
|
privacyManifests: {
|
||||||
NSPrivacyCollectedDataTypes: [
|
NSPrivacyCollectedDataTypes: [
|
||||||
@@ -182,7 +187,7 @@ module.exports = function (_config) {
|
@@ -175,14 +169,14 @@ module.exports = function (_config) {
|
||||||
|
barStyle: 'light-content',
|
||||||
|
},
|
||||||
|
android: {
|
||||||
|
- icon: './assets/app-icons/android_icon_default_next.png',
|
||||||
|
+ icon: './assets/logo.png',
|
||||||
|
adaptiveIcon: {
|
||||||
|
foregroundImage: './assets/icon-android-foreground.png',
|
||||||
|
monochromeImage: './assets/icon-android-monochrome.png',
|
||||||
backgroundColor: '#006AFF',
|
backgroundColor: '#006AFF',
|
||||||
},
|
},
|
||||||
googleServicesFile: './google-services.json',
|
googleServicesFile: './google-services.json',
|
||||||
@@ -68,28 +75,38 @@ index 246d8abd3..a6582864b 100644
|
|||||||
intentFilters: [
|
intentFilters: [
|
||||||
{
|
{
|
||||||
action: 'VIEW',
|
action: 'VIEW',
|
||||||
@@ -220,7 +225,7 @@ module.exports = function (_config) {
|
@@ -190,7 +184,7 @@ module.exports = function (_config) {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
scheme: 'https',
|
||||||
|
- host: 'bsky.app',
|
||||||
|
+ host: 'syu.is',
|
||||||
|
},
|
||||||
|
IS_DEV && {
|
||||||
|
scheme: 'http',
|
||||||
|
@@ -213,9 +207,9 @@ module.exports = function (_config) {
|
||||||
|
: undefined,
|
||||||
|
codeSigningMetadata: UPDATES_ENABLED
|
||||||
|
? {
|
||||||
|
- keyid: 'main',
|
||||||
|
- alg: 'rsa-v1_5-sha256',
|
||||||
|
- }
|
||||||
|
+ keyid: 'main',
|
||||||
|
+ alg: 'rsa-v1_5-sha256',
|
||||||
|
+ }
|
||||||
|
: undefined,
|
||||||
checkAutomatically: 'NEVER',
|
checkAutomatically: 'NEVER',
|
||||||
},
|
},
|
||||||
plugins: [
|
@@ -225,7 +219,7 @@ module.exports = function (_config) {
|
||||||
- 'expo-video',
|
|
||||||
+ 'expo-video', './plugins/withCodeSignEntitlements.js',
|
|
||||||
'expo-localization',
|
|
||||||
'expo-web-browser',
|
'expo-web-browser',
|
||||||
[
|
[
|
||||||
@@ -239,6 +244,11 @@ module.exports = function (_config) {
|
'react-native-edge-to-edge',
|
||||||
'expo-build-properties',
|
- {android: {enforceNavigationBarContrast: false}},
|
||||||
{
|
+ { android: { enforceNavigationBarContrast: false } },
|
||||||
ios: {
|
],
|
||||||
+ infoPlist: {
|
USE_SENTRY && [
|
||||||
+ NSAppTransportSecurity: {
|
'@sentry/react-native/expo',
|
||||||
+ NSAllowsArbitraryLoads: true,
|
@@ -264,7 +258,6 @@ module.exports = function (_config) {
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
deploymentTarget: '15.1',
|
|
||||||
buildReactNativeFromSource: true,
|
|
||||||
},
|
|
||||||
@@ -264,7 +274,6 @@ module.exports = function (_config) {
|
|
||||||
networkInstrumentation: true,
|
networkInstrumentation: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -97,54 +114,53 @@ index 246d8abd3..a6582864b 100644
|
|||||||
'./plugins/withGradleJVMHeapSizeIncrease.js',
|
'./plugins/withGradleJVMHeapSizeIncrease.js',
|
||||||
'./plugins/withAndroidManifestLargeHeapPlugin.js',
|
'./plugins/withAndroidManifestLargeHeapPlugin.js',
|
||||||
'./plugins/withAndroidManifestFCMIconPlugin.js',
|
'./plugins/withAndroidManifestFCMIconPlugin.js',
|
||||||
@@ -296,6 +305,11 @@ module.exports = function (_config) {
|
@@ -272,8 +265,6 @@ module.exports = function (_config) {
|
||||||
'expo-splash-screen',
|
'./plugins/withAndroidStylesAccentColorPlugin.js',
|
||||||
|
'./plugins/withAndroidDayNightThemePlugin.js',
|
||||||
|
'./plugins/withAndroidNoJitpackPlugin.js',
|
||||||
|
- './plugins/shareExtension/withShareExtensions.js',
|
||||||
|
- './plugins/notificationsExtension/withNotificationsExtension.js',
|
||||||
|
[
|
||||||
|
'expo-font',
|
||||||
{
|
{
|
||||||
ios: {
|
@@ -386,7 +377,7 @@ module.exports = function (_config) {
|
||||||
+ infoPlist: {
|
},
|
||||||
+ NSAppTransportSecurity: {
|
},
|
||||||
+ NSAllowsArbitraryLoads: true,
|
],
|
||||||
+ },
|
- ['expo-screen-orientation', {initialOrientation: 'PORTRAIT_UP'}],
|
||||||
+ },
|
+ ['expo-screen-orientation', { initialOrientation: 'PORTRAIT_UP' }],
|
||||||
enableFullScreenImage_legacy: true,
|
['expo-location'],
|
||||||
backgroundColor: '#ffffff',
|
].filter(Boolean),
|
||||||
image: './assets/splash.png',
|
extra: {
|
||||||
@@ -394,29 +408,30 @@ module.exports = function (_config) {
|
@@ -394,30 +385,7 @@ module.exports = function (_config) {
|
||||||
build: {
|
build: {
|
||||||
experimental: {
|
experimental: {
|
||||||
ios: {
|
ios: {
|
||||||
+ infoPlist: {
|
- appExtensions: [
|
||||||
+ NSAppTransportSecurity: {
|
- {
|
||||||
+ NSAllowsArbitraryLoads: true,
|
- targetName: 'Share-with-Bluesky',
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
appExtensions: [
|
|
||||||
{
|
|
||||||
targetName: 'Share-with-Bluesky',
|
|
||||||
- bundleIdentifier: 'xyz.blueskyweb.app.Share-with-Bluesky',
|
- bundleIdentifier: 'xyz.blueskyweb.app.Share-with-Bluesky',
|
||||||
+ bundleIdentifier: 'ai.syui.at.Share-with-Bluesky',
|
- entitlements: {
|
||||||
entitlements: {
|
- 'com.apple.security.application-groups': [
|
||||||
'com.apple.security.application-groups': [
|
|
||||||
- 'group.app.bsky',
|
- 'group.app.bsky',
|
||||||
+ 'group.ai.syui.at',
|
- ],
|
||||||
],
|
- },
|
||||||
},
|
- },
|
||||||
},
|
- {
|
||||||
{
|
- targetName: 'BlueskyNSE',
|
||||||
targetName: 'BlueskyNSE',
|
|
||||||
- bundleIdentifier: 'xyz.blueskyweb.app.BlueskyNSE',
|
- bundleIdentifier: 'xyz.blueskyweb.app.BlueskyNSE',
|
||||||
+ bundleIdentifier: 'ai.syui.at.BlueskyNSE',
|
- entitlements: {
|
||||||
entitlements: {
|
- 'com.apple.security.application-groups': [
|
||||||
'com.apple.security.application-groups': [
|
|
||||||
- 'group.app.bsky',
|
- 'group.app.bsky',
|
||||||
+ 'group.ai.syui.at',
|
- ],
|
||||||
],
|
- },
|
||||||
},
|
- },
|
||||||
},
|
|
||||||
- {
|
- {
|
||||||
- targetName: 'BlueskyClip',
|
- targetName: 'BlueskyClip',
|
||||||
- bundleIdentifier: 'xyz.blueskyweb.app.AppClip',
|
- bundleIdentifier: 'xyz.blueskyweb.app.AppClip',
|
||||||
- },
|
- },
|
||||||
],
|
- ],
|
||||||
|
+ appExtensions: [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
|
diff --git a/src/lib/api/feed/home.ts b/src/lib/api/feed/home.ts
|
||||||
|
index 7a0d72d91..93554dc3e 100644
|
||||||
|
--- a/src/lib/api/feed/home.ts
|
||||||
|
+++ b/src/lib/api/feed/home.ts
|
||||||
|
@@ -45,7 +45,7 @@ export class HomeFeedAPI implements FeedAPI {
|
||||||
|
this.following = new FollowingFeedAPI({agent})
|
||||||
|
this.discover = new CustomFeedAPI({
|
||||||
|
agent,
|
||||||
|
- feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')},
|
||||||
|
+ feedParams: {feed: PROD_DEFAULT_FEED('app')},
|
||||||
|
})
|
||||||
|
this.userInterests = userInterests
|
||||||
|
}
|
||||||
|
@@ -54,7 +54,7 @@ export class HomeFeedAPI implements FeedAPI {
|
||||||
|
this.following = new FollowingFeedAPI({agent: this.agent})
|
||||||
|
this.discover = new CustomFeedAPI({
|
||||||
|
agent: this.agent,
|
||||||
|
- feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')},
|
||||||
|
+ feedParams: {feed: PROD_DEFAULT_FEED('app')},
|
||||||
|
userInterests: this.userInterests,
|
||||||
|
})
|
||||||
|
this.usingDiscover = false
|
||||||
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
|
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
|
||||||
index 231447b4f..33c51cc0a 100644
|
index 231447b4f..a44b3da05 100644
|
||||||
--- a/src/lib/constants.ts
|
--- a/src/lib/constants.ts
|
||||||
+++ b/src/lib/constants.ts
|
+++ b/src/lib/constants.ts
|
||||||
@@ -7,12 +7,12 @@ import {BLUESKY_PROXY_DID, CHAT_PROXY_DID} from '#/env'
|
@@ -7,12 +7,12 @@ import {BLUESKY_PROXY_DID, CHAT_PROXY_DID} from '#/env'
|
||||||
@@ -16,11 +38,54 @@ index 231447b4f..33c51cc0a 100644
|
|||||||
-const HELP_DESK_LANG = 'en-us'
|
-const HELP_DESK_LANG = 'en-us'
|
||||||
-export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG}`
|
-export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG}`
|
||||||
+const HELP_DESK_LANG = 'ja-jp'
|
+const HELP_DESK_LANG = 'ja-jp'
|
||||||
+export const HELP_DESK_URL = 'https://syu.is/help'
|
+export const HELP_DESK_URL = 'https://syu.is/about/support/help'
|
||||||
export const EMBED_SERVICE = 'https://embed.bsky.app'
|
export const EMBED_SERVICE = 'https://embed.bsky.app'
|
||||||
export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js`
|
export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js`
|
||||||
export const BSKY_DOWNLOAD_URL = 'https://bsky.app/download'
|
export const BSKY_DOWNLOAD_URL = 'https://bsky.app/download'
|
||||||
@@ -209,8 +209,8 @@ export const urls = {
|
@@ -79,19 +79,17 @@ export function IS_PROD_SERVICE(url?: string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PROD_DEFAULT_FEED = (rkey: string) =>
|
||||||
|
- `at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/${rkey}`
|
||||||
|
+ `at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/${rkey}`
|
||||||
|
|
||||||
|
export const STAGING_DEFAULT_FEED = (rkey: string) =>
|
||||||
|
`at://did:plc:yofh3kx63drvfljkibw5zuxo/app.bsky.feed.generator/${rkey}`
|
||||||
|
|
||||||
|
export const PROD_FEEDS = [
|
||||||
|
- `feedgen|${PROD_DEFAULT_FEED('whats-hot')}`,
|
||||||
|
- `feedgen|${PROD_DEFAULT_FEED('thevids')}`,
|
||||||
|
+ `feedgen|${PROD_DEFAULT_FEED('app')}`,
|
||||||
|
]
|
||||||
|
|
||||||
|
export const STAGING_FEEDS = [
|
||||||
|
- `feedgen|${STAGING_DEFAULT_FEED('whats-hot')}`,
|
||||||
|
- `feedgen|${STAGING_DEFAULT_FEED('thevids')}`,
|
||||||
|
+ `feedgen|${STAGING_DEFAULT_FEED('app')}`,
|
||||||
|
]
|
||||||
|
|
||||||
|
export const POST_IMG_MAX = {
|
||||||
|
@@ -129,7 +127,7 @@ export const LANG_DROPDOWN_HITSLOP = {top: 10, bottom: 10, left: 4, right: 4}
|
||||||
|
export const BACK_HITSLOP = HITSLOP_30
|
||||||
|
export const MAX_POST_LINES = 25
|
||||||
|
|
||||||
|
-export const BSKY_APP_ACCOUNT_DID = 'did:plc:z72i7hdynmk6r22z27h6tvur'
|
||||||
|
+export const BSKY_APP_ACCOUNT_DID = 'did:plc:6qyecktefllvenje24fcxnie'
|
||||||
|
|
||||||
|
export const BSKY_FEED_OWNER_DIDS = [
|
||||||
|
BSKY_APP_ACCOUNT_DID,
|
||||||
|
@@ -138,9 +136,9 @@ export const BSKY_FEED_OWNER_DIDS = [
|
||||||
|
]
|
||||||
|
|
||||||
|
export const DISCOVER_FEED_URI =
|
||||||
|
- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot'
|
||||||
|
+ 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app'
|
||||||
|
export const VIDEO_FEED_URI =
|
||||||
|
'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/thevids'
|
||||||
|
export const STAGING_VIDEO_FEED_URI =
|
||||||
|
'at://did:plc:yofh3kx63drvfljkibw5zuxo/app.bsky.feed.generator/thevids'
|
||||||
|
export const VIDEO_FEED_URIS = [VIDEO_FEED_URI, STAGING_VIDEO_FEED_URI]
|
||||||
|
@@ -209,8 +207,8 @@ export const urls = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +96,7 @@ index 231447b4f..33c51cc0a 100644
|
|||||||
export const PUBLIC_STAGING_APPVIEW_DID = 'did:web:api.staging.bsky.dev'
|
export const PUBLIC_STAGING_APPVIEW_DID = 'did:web:api.staging.bsky.dev'
|
||||||
|
|
||||||
export const DEV_ENV_APPVIEW = `http://localhost:2584` // always the same
|
export const DEV_ENV_APPVIEW = `http://localhost:2584` // always the same
|
||||||
@@ -236,8 +236,8 @@ export const BLUESKY_MOD_SERVICE_HEADERS = {
|
@@ -236,8 +234,8 @@ export const BLUESKY_MOD_SERVICE_HEADERS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const webLinks = {
|
export const webLinks = {
|
||||||
@@ -42,30 +107,21 @@ index 231447b4f..33c51cc0a 100644
|
|||||||
community: `https://bsky.social/about/support/community-guidelines`,
|
community: `https://bsky.social/about/support/community-guidelines`,
|
||||||
communityDeprecated: `https://bsky.social/about/support/community-guidelines-deprecated`,
|
communityDeprecated: `https://bsky.social/about/support/community-guidelines-deprecated`,
|
||||||
}
|
}
|
||||||
diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx
|
diff --git a/src/lib/demo.ts b/src/lib/demo.ts
|
||||||
index 860e841eb..a595b0868 100644
|
index 5ead62c9d..7c80dfe15 100644
|
||||||
--- a/src/lib/statsig/statsig.tsx
|
--- a/src/lib/demo.ts
|
||||||
+++ b/src/lib/statsig/statsig.tsx
|
+++ b/src/lib/demo.ts
|
||||||
@@ -265,6 +265,7 @@ export async function tryFetchGates(
|
@@ -1,7 +1,7 @@
|
||||||
}
|
import {type AppBskyFeedGetFeed} from '@atproto/api'
|
||||||
|
import {subDays, subMinutes} from 'date-fns'
|
||||||
|
|
||||||
export function initialize() {
|
-const DID = `did:plc:z72i7hdynmk6r22z27h6tvur`
|
||||||
+ if (!SDK_KEY) return Promise.resolve()
|
+const DID = `did:plc:6qyecktefllvenje24fcxnie`
|
||||||
return Statsig.initialize(SDK_KEY, null, createStatsigOptions([]))
|
const NOW = new Date()
|
||||||
}
|
const POST_1_DATE = subMinutes(NOW, 2).toISOString()
|
||||||
|
const POST_2_DATE = subMinutes(NOW, 4).toISOString()
|
||||||
@@ -309,6 +310,9 @@ export function Provider({children}: {children: React.ReactNode}) {
|
|
||||||
return () => clearInterval(id)
|
|
||||||
}, [handleIntervalTick])
|
|
||||||
|
|
||||||
+ if (!SDK_KEY) {
|
|
||||||
+ return <GateCache.Provider value={gateCache}>{children}</GateCache.Provider>
|
|
||||||
+ }
|
|
||||||
return (
|
|
||||||
<GateCache.Provider value={gateCache}>
|
|
||||||
<StatsigProvider
|
|
||||||
diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts
|
diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts
|
||||||
index 6088e2806..7c903763d 100644
|
index 6088e2806..0f6787a4d 100644
|
||||||
--- a/src/lib/strings/url-helpers.ts
|
--- a/src/lib/strings/url-helpers.ts
|
||||||
+++ b/src/lib/strings/url-helpers.ts
|
+++ b/src/lib/strings/url-helpers.ts
|
||||||
@@ -53,7 +53,7 @@ export function toNiceDomain(url: string): string {
|
@@ -53,7 +53,7 @@ export function toNiceDomain(url: string): string {
|
||||||
@@ -77,3 +133,85 @@ index 6088e2806..7c903763d 100644
|
|||||||
}
|
}
|
||||||
return urlp.host ? urlp.host : url
|
return urlp.host ? urlp.host : url
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@@ -338,7 +338,7 @@ export function createProxiedUrl(url: string): string {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
- return `https://go.bsky.app/redirect?u=${encodeURIComponent(url)}`
|
||||||
|
+ return url
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isShortLink(url: string): boolean {
|
||||||
|
diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts
|
||||||
|
index de1e92533..3d1566800 100644
|
||||||
|
--- a/src/state/queries/feed.ts
|
||||||
|
+++ b/src/state/queries/feed.ts
|
||||||
|
@@ -201,14 +201,6 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) {
|
||||||
|
// for the ones we know need it
|
||||||
|
// -prf
|
||||||
|
export const KNOWN_AUTHED_ONLY_FEEDS = [
|
||||||
|
- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/with-friends', // popular with friends, by bsky.app
|
||||||
|
- 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/mutuals', // mutuals, by skyfeed
|
||||||
|
- 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/only-posts', // only posts, by skyfeed
|
||||||
|
- 'at://did:plc:wzsilnxf24ehtmmc3gssy5bu/app.bsky.feed.generator/mentions', // mentions, by flicknow
|
||||||
|
- 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/bangers', // my bangers, by jaz
|
||||||
|
- 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/mutuals', // mutuals, by bluesky
|
||||||
|
- 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/my-followers', // followers, by jaz
|
||||||
|
- 'at://did:plc:vpkhqolt662uhesyj6nxm7ys/app.bsky.feed.generator/followpics', // the gram, by why
|
||||||
|
]
|
||||||
|
|
||||||
|
type GetPopularFeedsOptions = {limit?: number; enabled?: boolean}
|
||||||
|
diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts
|
||||||
|
index 0cf6ab546..399e592bc 100644
|
||||||
|
--- a/src/state/queries/preferences/index.ts
|
||||||
|
+++ b/src/state/queries/preferences/index.ts
|
||||||
|
@@ -270,7 +270,7 @@ export function useReplaceForYouWithDiscoverFeedMutation() {
|
||||||
|
await agent.addSavedFeeds([
|
||||||
|
{
|
||||||
|
type: 'feed',
|
||||||
|
- value: PROD_DEFAULT_FEED('whats-hot'),
|
||||||
|
+ value: PROD_DEFAULT_FEED('app'),
|
||||||
|
pinned: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
diff --git a/src/view/com/posts/FeedShutdownMsg.tsx b/src/view/com/posts/FeedShutdownMsg.tsx
|
||||||
|
index 620382175..928480da2 100644
|
||||||
|
--- a/src/view/com/posts/FeedShutdownMsg.tsx
|
||||||
|
+++ b/src/view/com/posts/FeedShutdownMsg.tsx
|
||||||
|
@@ -32,7 +32,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
|
||||||
|
f => f.value === feedUri && f.pinned,
|
||||||
|
)
|
||||||
|
const discoverFeedConfig = preferences?.savedFeeds?.find(
|
||||||
|
- f => f.value === PROD_DEFAULT_FEED('whats-hot'),
|
||||||
|
+ f => f.value === PROD_DEFAULT_FEED('app'),
|
||||||
|
)
|
||||||
|
const hasFeedPinned = Boolean(feedConfig)
|
||||||
|
const hasDiscoverPinned = Boolean(discoverFeedConfig?.pinned)
|
||||||
|
@@ -44,7 +44,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
|
||||||
|
Toast.show(_(msg`Removed from your feeds`))
|
||||||
|
}
|
||||||
|
if (hasDiscoverPinned) {
|
||||||
|
- setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`)
|
||||||
|
+ setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('app')}`)
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
Toast.show(
|
||||||
|
@@ -63,7 +63,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
|
||||||
|
forYouFeedConfig: feedConfig,
|
||||||
|
discoverFeedConfig,
|
||||||
|
})
|
||||||
|
- setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`)
|
||||||
|
+ setSelectedFeed(`feedgen|${PROD_DEFAULT_FEED('app')}`)
|
||||||
|
Toast.show(_(msg`The feed has been replaced with Discover.`))
|
||||||
|
} catch (err: any) {
|
||||||
|
Toast.show(
|
||||||
|
@@ -100,7 +100,7 @@ export function FeedShutdownMsg({feedUri}: {feedUri: string}) {
|
||||||
|
This feed is no longer online. We are showing{' '}
|
||||||
|
<InlineLinkText
|
||||||
|
label={_(msg`The Discover feed`)}
|
||||||
|
- to="/profile/bsky.app/feed/whats-hot"
|
||||||
|
+ to="/profile/did:plc:6qyecktefllvenje24fcxnie/feed/app"
|
||||||
|
style={[a.text_md]}>
|
||||||
|
Discover
|
||||||
|
</InlineLinkText>{' '}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,51 @@
|
|||||||
|
diff --git a/src/Splash.tsx b/src/Splash.tsx
|
||||||
|
index 47e70b375..616f351ed 100644
|
||||||
|
--- a/src/Splash.tsx
|
||||||
|
+++ b/src/Splash.tsx
|
||||||
|
@@ -15,8 +15,8 @@ import Animated, {
|
||||||
|
withTiming,
|
||||||
|
} from 'react-native-reanimated'
|
||||||
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
|
-import Svg, {Path, type SvgProps} from 'react-native-svg'
|
||||||
|
import {Image} from 'expo-image'
|
||||||
|
+import {type SvgProps} from 'react-native-svg'
|
||||||
|
import * as SplashScreen from 'expo-splash-screen'
|
||||||
|
|
||||||
|
import {Logotype} from '#/view/icons/Logotype'
|
||||||
|
@@ -29,21 +29,18 @@ const darkSplashImageUri = RNImage.resolveAssetSource(
|
||||||
|
darkSplashImagePointer,
|
||||||
|
).uri
|
||||||
|
|
||||||
|
-export const Logo = React.forwardRef(function LogoImpl(props: SvgProps, ref) {
|
||||||
|
- const width = 1000
|
||||||
|
- const height = width * (67 / 64)
|
||||||
|
+export const Logo = React.forwardRef(function LogoImpl(props: SvgProps & {fill?: string}, ref) {
|
||||||
|
+ const size = 1000
|
||||||
|
+ // @ts-ignore
|
||||||
|
return (
|
||||||
|
- <Svg
|
||||||
|
- fill="none"
|
||||||
|
- // @ts-ignore it's fiiiiine
|
||||||
|
+ <Image
|
||||||
|
+ // @ts-ignore
|
||||||
|
ref={ref}
|
||||||
|
- viewBox="0 0 64 66"
|
||||||
|
- style={[{width, height}, props.style]}>
|
||||||
|
- <Path
|
||||||
|
- fill={props.fill || '#fff'}
|
||||||
|
- d="M13.873 3.77C21.21 9.243 29.103 20.342 32 26.3v15.732c0-.335-.13.043-.41.858-1.512 4.414-7.418 21.642-20.923 7.87-7.111-7.252-3.819-14.503 9.125-16.692-7.405 1.252-15.73-.817-18.014-8.93C1.12 22.804 0 8.431 0 6.488 0-3.237 8.579-.18 13.873 3.77ZM50.127 3.77C42.79 9.243 34.897 20.342 32 26.3v15.732c0-.335.13.043.41.858 1.512 4.414 7.418 21.642 20.923 7.87 7.111-7.252 3.819-14.503-9.125-16.692 7.405 1.252 15.73-.817 18.014-8.93C62.88 22.804 64 8.431 64 6.488 64-3.237 55.422-.18 50.127 3.77Z"
|
||||||
|
- />
|
||||||
|
- </Svg>
|
||||||
|
+ source={require('../assets/logo.png')}
|
||||||
|
+ style={[{width: size, height: size}, props.style]}
|
||||||
|
+ contentFit="contain"
|
||||||
|
+ accessibilityLabel="Logo"
|
||||||
|
+ />
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx
|
diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx
|
||||||
index 8a9e51a33..cb0eb7b71 100644
|
index 8a9e51a33..65d643b89 100644
|
||||||
--- a/src/view/com/util/UserAvatar.tsx
|
--- a/src/view/com/util/UserAvatar.tsx
|
||||||
+++ b/src/view/com/util/UserAvatar.tsx
|
+++ b/src/view/com/util/UserAvatar.tsx
|
||||||
@@ -444,7 +444,7 @@ let EditableUserAvatar = ({
|
@@ -444,7 +444,7 @@ let EditableUserAvatar = ({
|
||||||
@@ -11,17 +57,20 @@ index 8a9e51a33..cb0eb7b71 100644
|
|||||||
accessibilityRole="image"
|
accessibilityRole="image"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@@ -619,7 +619,7 @@ export {PreviewableUserAvatar}
|
@@ -618,9 +618,8 @@ export {PreviewableUserAvatar}
|
||||||
|
// manually string-replace to use the smaller ones
|
||||||
// -prf
|
// -prf
|
||||||
function hackModifyThumbnailPath(uri: string, isEnabled: boolean): string {
|
function hackModifyThumbnailPath(uri: string, isEnabled: boolean): string {
|
||||||
return isEnabled
|
- return isEnabled
|
||||||
- ? uri.replace('/img/avatar/plain/', '/img/avatar_thumbnail/plain/')
|
- ? uri.replace('/img/avatar/plain/', '/img/avatar_thumbnail/plain/')
|
||||||
+ ? uri.replace('https://cdn.web.syu.is/img/avatar/plain/', 'https://bsky.syu.is/img/avatar/plain/')
|
- : uri
|
||||||
: uri
|
+ // syu.is: avatars are served directly from bsky.syu.is, no CDN transformation needed
|
||||||
|
+ return uri
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
diff --git a/src/view/icons/Logo.tsx b/src/view/icons/Logo.tsx
|
diff --git a/src/view/icons/Logo.tsx b/src/view/icons/Logo.tsx
|
||||||
index d7208df13..b711f71c7 100644
|
index d7208df13..2763800ac 100644
|
||||||
--- a/src/view/icons/Logo.tsx
|
--- a/src/view/icons/Logo.tsx
|
||||||
+++ b/src/view/icons/Logo.tsx
|
+++ b/src/view/icons/Logo.tsx
|
||||||
@@ -1,75 +1,17 @@
|
@@ -1,75 +1,17 @@
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ index 1ed913bb2..c80340edb 100644
|
|||||||
Support: '/support',
|
Support: '/support',
|
||||||
- PrivacyPolicy: '/support/privacy',
|
- PrivacyPolicy: '/support/privacy',
|
||||||
- TermsOfService: '/support/tos',
|
- TermsOfService: '/support/tos',
|
||||||
+ PrivacyPolicy: 'https://syu.is/privacy',
|
+ PrivacyPolicy: 'https://syu.is/about/support/privacy-policy',
|
||||||
+ TermsOfService: 'https://syu.is/terms',
|
+ TermsOfService: 'https://syu.is/about/support/tos',
|
||||||
CommunityGuidelines: '/support/community-guidelines',
|
CommunityGuidelines: '/support/community-guidelines',
|
||||||
CopyrightPolicy: '/support/copyright',
|
CopyrightPolicy: '/support/copyright',
|
||||||
// hashtags
|
// hashtags
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
|
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
|
||||||
index 6b8257b91..35202224b 100644
|
index 6b8257b91..48ba7909e 100644
|
||||||
--- a/src/screens/Settings/AboutSettings.tsx
|
--- a/src/screens/Settings/AboutSettings.tsx
|
||||||
+++ b/src/screens/Settings/AboutSettings.tsx
|
+++ b/src/screens/Settings/AboutSettings.tsx
|
||||||
@@ -80,7 +80,7 @@ export function AboutSettingsScreen({}: Props) {
|
@@ -80,7 +80,7 @@ export function AboutSettingsScreen({}: Props) {
|
||||||
@@ -21,26 +21,25 @@ index 6b8257b91..35202224b 100644
|
|||||||
<SettingsList.ItemIcon icon={NewspaperIcon} />
|
<SettingsList.ItemIcon icon={NewspaperIcon} />
|
||||||
<SettingsList.ItemText>
|
<SettingsList.ItemText>
|
||||||
diff --git a/src/screens/Takendown.tsx b/src/screens/Takendown.tsx
|
diff --git a/src/screens/Takendown.tsx b/src/screens/Takendown.tsx
|
||||||
index dd319a4c6..0e80f956a 100644
|
index 77f219e55..53f5e0cc0 100644
|
||||||
--- a/src/screens/Takendown.tsx
|
--- a/src/screens/Takendown.tsx
|
||||||
+++ b/src/screens/Takendown.tsx
|
+++ b/src/screens/Takendown.tsx
|
||||||
@@ -223,11 +223,11 @@ export function Takendown() {
|
@@ -217,10 +217,10 @@ export function Takendown() {
|
||||||
<Trans>
|
<Trans>
|
||||||
Your account was found to be in violation of the{' '}
|
Your account was found to be in violation of the{' '}
|
||||||
<InlineLinkText
|
<SimpleInlineLinkText
|
||||||
- label={_(msg`Bluesky Social Terms of Service`)}
|
- label={_(msg`Bluesky Social Terms of Service`)}
|
||||||
- to="https://bsky.social/about/support/tos"
|
- to="https://bsky.social/about/support/tos"
|
||||||
+ label={_(msg`syu.is Terms of Service`)}
|
+ label={_(msg`syu.is Terms of Service`)}
|
||||||
+ to="https://syu.is/about/support/tos"
|
+ to="https://syu.is/about/support/tos"
|
||||||
style={[a.text_md, a.leading_normal]}
|
style={[a.text_md, a.leading_normal]}>
|
||||||
overridePresentation>
|
|
||||||
- Bluesky Social Terms of Service
|
- Bluesky Social Terms of Service
|
||||||
+ syu.is Terms of Service
|
+ syu.is Terms of Service
|
||||||
</InlineLinkText>
|
</SimpleInlineLinkText>
|
||||||
. You have been sent an email outlining the specific violation
|
. You have been sent an email outlining the specific violation
|
||||||
and suspension period, if applicable. You can appeal this
|
and suspension period, if applicable. You can appeal this
|
||||||
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
|
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
|
||||||
index e058e2883..0f583c915 100644
|
index e058e2883..8daf41089 100644
|
||||||
--- a/src/view/screens/Home.tsx
|
--- a/src/view/screens/Home.tsx
|
||||||
+++ b/src/view/screens/Home.tsx
|
+++ b/src/view/screens/Home.tsx
|
||||||
@@ -1,23 +1,16 @@
|
@@ -1,23 +1,16 @@
|
||||||
@@ -82,17 +81,42 @@ index e058e2883..0f583c915 100644
|
|||||||
import {CustomFeedEmptyState} from '#/view/com/posts/CustomFeedEmptyState'
|
import {CustomFeedEmptyState} from '#/view/com/posts/CustomFeedEmptyState'
|
||||||
import {FollowingEmptyState} from '#/view/com/posts/FollowingEmptyState'
|
import {FollowingEmptyState} from '#/view/com/posts/FollowingEmptyState'
|
||||||
import {FollowingEndOfFeed} from '#/view/com/posts/FollowingEndOfFeed'
|
import {FollowingEndOfFeed} from '#/view/com/posts/FollowingEndOfFeed'
|
||||||
@@ -39,97 +28,60 @@ import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
|
@@ -39,97 +28,90 @@ import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
|
||||||
import * as Layout from '#/components/Layout'
|
import * as Layout from '#/components/Layout'
|
||||||
import {useDemoMode} from '#/storage/hooks/demo-mode'
|
import {useDemoMode} from '#/storage/hooks/demo-mode'
|
||||||
|
|
||||||
+const DEFAULT_PINNED_FEEDS = [{
|
+const SYU_IS_FEED_URI = 'at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/app'
|
||||||
|
+
|
||||||
|
+const DEFAULT_PINNED_FEEDS: any[] = [{
|
||||||
+ feedDescriptor: 'following',
|
+ feedDescriptor: 'following',
|
||||||
+ displayName: 'Following',
|
+ displayName: 'Following',
|
||||||
+ id: 'following',
|
+ id: 'following',
|
||||||
|
+ uri: 'following',
|
||||||
+ type: 'feed',
|
+ type: 'feed',
|
||||||
+ savedFeed: undefined,
|
+ savedFeed: undefined,
|
||||||
+ pinned: true,
|
+ pinned: true,
|
||||||
|
+ route: { href: '/', name: 'Home', params: {} },
|
||||||
|
+ cid: '',
|
||||||
|
+ avatar: '',
|
||||||
|
+ creatorDid: '',
|
||||||
|
+ creatorHandle: '',
|
||||||
|
+}, {
|
||||||
|
+ feedDescriptor: `feedgen|${SYU_IS_FEED_URI}`,
|
||||||
|
+ displayName: 'Feeds',
|
||||||
|
+ id: SYU_IS_FEED_URI,
|
||||||
|
+ uri: SYU_IS_FEED_URI,
|
||||||
|
+ type: 'feed',
|
||||||
|
+ savedFeed: {
|
||||||
|
+ type: 'feed',
|
||||||
|
+ value: SYU_IS_FEED_URI,
|
||||||
|
+ pinned: true,
|
||||||
|
+ },
|
||||||
|
+ pinned: true,
|
||||||
|
+ route: { href: '/', name: 'Home', params: {} },
|
||||||
|
+ cid: '',
|
||||||
|
+ avatar: '',
|
||||||
|
+ creatorDid: '',
|
||||||
|
+ creatorHandle: '',
|
||||||
+}]
|
+}]
|
||||||
+
|
+
|
||||||
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home' | 'Start'>
|
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home' | 'Start'>
|
||||||
@@ -105,7 +129,12 @@ index e058e2883..0f583c915 100644
|
|||||||
+ const {data: pinnedFeedInfos} = usePinnedFeedsInfos()
|
+ const {data: pinnedFeedInfos} = usePinnedFeedsInfos()
|
||||||
+
|
+
|
||||||
+ const safePreferences = preferences || { feedViewPrefs: { lab_mergeFeedEnabled: false }, savedFeeds: [] } as any
|
+ const safePreferences = preferences || { feedViewPrefs: { lab_mergeFeedEnabled: false }, savedFeeds: [] } as any
|
||||||
+ const safePinnedFeedInfos = pinnedFeedInfos || DEFAULT_PINNED_FEEDS
|
+ // Use user's pinned feeds when logged in and available, otherwise use defaults
|
||||||
|
+ const safePinnedFeedInfos = !currentAccount
|
||||||
|
+ ? DEFAULT_PINNED_FEEDS.filter(f => f.feedDescriptor !== 'following')
|
||||||
|
+ : (pinnedFeedInfos && pinnedFeedInfos.length > 0)
|
||||||
|
+ ? pinnedFeedInfos
|
||||||
|
+ : DEFAULT_PINNED_FEEDS
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isWeb && !currentAccount) {
|
if (isWeb && !currentAccount) {
|
||||||
@@ -189,8 +218,7 @@ index e058e2883..0f583c915 100644
|
|||||||
- const maybeSelectedFeed: FeedDescriptor | undefined = allFeeds[selectedIndex]
|
- const maybeSelectedFeed: FeedDescriptor | undefined = allFeeds[selectedIndex]
|
||||||
+ const maybeSelectedFeed = allFeeds[selectedIndex]
|
+ const maybeSelectedFeed = allFeeds[selectedIndex]
|
||||||
const requestNotificationsPermission = useRequestNotificationsPermission()
|
const requestNotificationsPermission = useRequestNotificationsPermission()
|
||||||
-
|
|
||||||
+
|
|
||||||
useSetTitle(pinnedFeedInfos[selectedIndex]?.displayName)
|
useSetTitle(pinnedFeedInfos[selectedIndex]?.displayName)
|
||||||
useOTAUpdates()
|
useOTAUpdates()
|
||||||
-
|
-
|
||||||
@@ -208,7 +236,7 @@ index e058e2883..0f583c915 100644
|
|||||||
if (selectedIndex !== lastPagerReportedIndexRef.current) {
|
if (selectedIndex !== lastPagerReportedIndexRef.current) {
|
||||||
lastPagerReportedIndexRef.current = selectedIndex
|
lastPagerReportedIndexRef.current = selectedIndex
|
||||||
pagerRef.current?.setPage(selectedIndex)
|
pagerRef.current?.setPage(selectedIndex)
|
||||||
@@ -138,205 +90,43 @@ function HomeScreenReady({
|
@@ -138,205 +120,43 @@ function HomeScreenReady({
|
||||||
|
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const setMinimalShellMode = useSetMinimalShellMode()
|
const setMinimalShellMode = useSetMinimalShellMode()
|
||||||
@@ -217,7 +245,8 @@ index e058e2883..0f583c915 100644
|
|||||||
- setMinimalShellMode(false)
|
- setMinimalShellMode(false)
|
||||||
- }, [setMinimalShellMode]),
|
- }, [setMinimalShellMode]),
|
||||||
- )
|
- )
|
||||||
-
|
+ useFocusEffect(React.useCallback(() => { setMinimalShellMode(false) }, [setMinimalShellMode]))
|
||||||
|
|
||||||
- useFocusEffect(
|
- useFocusEffect(
|
||||||
- useNonReactiveCallback(() => {
|
- useNonReactiveCallback(() => {
|
||||||
- if (maybeSelectedFeed) {
|
- if (maybeSelectedFeed) {
|
||||||
@@ -235,7 +264,13 @@ index e058e2883..0f583c915 100644
|
|||||||
- (index: number) => {
|
- (index: number) => {
|
||||||
- setMinimalShellMode(false)
|
- setMinimalShellMode(false)
|
||||||
- const maybeFeed = allFeeds[index]
|
- const maybeFeed = allFeeds[index]
|
||||||
-
|
+ const onPageSelected = React.useCallback((index) => {
|
||||||
|
+ setMinimalShellMode(false)
|
||||||
|
+ const maybeFeed = allFeeds[index]
|
||||||
|
+ lastPagerReportedIndexRef.current = index
|
||||||
|
+ setSelectedFeed(maybeFeed)
|
||||||
|
+ }, [setSelectedFeed, setMinimalShellMode, allFeeds])
|
||||||
|
|
||||||
- // Mutate the ref before setting state to avoid the imperative syncing effect
|
- // Mutate the ref before setting state to avoid the imperative syncing effect
|
||||||
- // above from starting a loop on Android when swiping back and forth.
|
- // above from starting a loop on Android when swiping back and forth.
|
||||||
- lastPagerReportedIndexRef.current = index
|
- lastPagerReportedIndexRef.current = index
|
||||||
@@ -265,15 +300,6 @@ index e058e2883..0f583c915 100644
|
|||||||
- },
|
- },
|
||||||
- [setMinimalShellMode],
|
- [setMinimalShellMode],
|
||||||
- )
|
- )
|
||||||
+ useFocusEffect(React.useCallback(() => { setMinimalShellMode(false) }, [setMinimalShellMode]))
|
|
||||||
+
|
|
||||||
+ const onPageSelected = React.useCallback((index) => {
|
|
||||||
+ setMinimalShellMode(false)
|
|
||||||
+ const maybeFeed = allFeeds[index]
|
|
||||||
+ lastPagerReportedIndexRef.current = index
|
|
||||||
+ setSelectedFeed(maybeFeed)
|
|
||||||
+ }, [setSelectedFeed, setMinimalShellMode, allFeeds])
|
|
||||||
+
|
|
||||||
+ const onPressSelected = React.useCallback(() => { emitSoftReset() }, [])
|
+ const onPressSelected = React.useCallback(() => { emitSoftReset() }, [])
|
||||||
+ const onPageScrollStateChanged = React.useCallback((state) => {
|
+ const onPageScrollStateChanged = React.useCallback((state) => {
|
||||||
+ 'worklet'
|
+ 'worklet'
|
||||||
@@ -281,7 +307,10 @@ index e058e2883..0f583c915 100644
|
|||||||
+ }, [setMinimalShellMode])
|
+ }, [setMinimalShellMode])
|
||||||
|
|
||||||
const [demoMode] = useDemoMode()
|
const [demoMode] = useDemoMode()
|
||||||
-
|
+ const renderTabBar = React.useCallback((props) => {
|
||||||
|
+ return <HomeHeader key="FEEDS_TAB_BAR" {...props} testID="homeScreenFeedTabs" onPressSelected={onPressSelected} feeds={pinnedFeedInfos} />
|
||||||
|
+ }, [onPressSelected, pinnedFeedInfos])
|
||||||
|
|
||||||
- const renderTabBar = React.useCallback(
|
- const renderTabBar = React.useCallback(
|
||||||
- (props: RenderTabBarFnProps) => {
|
- (props: RenderTabBarFnProps) => {
|
||||||
- if (demoMode) {
|
- if (demoMode) {
|
||||||
@@ -312,11 +341,16 @@ index e058e2883..0f583c915 100644
|
|||||||
- const renderFollowingEmptyState = React.useCallback(() => {
|
- const renderFollowingEmptyState = React.useCallback(() => {
|
||||||
- return <FollowingEmptyState />
|
- return <FollowingEmptyState />
|
||||||
- }, [])
|
- }, [])
|
||||||
-
|
+ const renderFollowingEmptyState = React.useCallback(() => <FollowingEmptyState />, [])
|
||||||
|
+ const renderCustomFeedEmptyState = React.useCallback(() => <CustomFeedEmptyState />, [])
|
||||||
|
|
||||||
- const renderCustomFeedEmptyState = React.useCallback(() => {
|
- const renderCustomFeedEmptyState = React.useCallback(() => {
|
||||||
- return <CustomFeedEmptyState />
|
- return <CustomFeedEmptyState />
|
||||||
- }, [])
|
- }, [])
|
||||||
-
|
+ const homeFeedParams = React.useMemo(() => ({
|
||||||
|
+ mergeFeedEnabled: false, mergeFeedSources: []
|
||||||
|
+ }), [preferences])
|
||||||
|
|
||||||
- const homeFeedParams = React.useMemo<FeedParams>(() => {
|
- const homeFeedParams = React.useMemo<FeedParams>(() => {
|
||||||
- return {
|
- return {
|
||||||
- mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
|
- mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled),
|
||||||
@@ -368,17 +402,6 @@ index e058e2883..0f583c915 100644
|
|||||||
- renderTabBar={renderTabBar}>
|
- renderTabBar={renderTabBar}>
|
||||||
- {pinnedFeedInfos.length ? (
|
- {pinnedFeedInfos.length ? (
|
||||||
- pinnedFeedInfos.map((feedInfo, index) => {
|
- pinnedFeedInfos.map((feedInfo, index) => {
|
||||||
+ const renderTabBar = React.useCallback((props) => {
|
|
||||||
+ return <HomeHeader key="FEEDS_TAB_BAR" {...props} testID="homeScreenFeedTabs" onPressSelected={onPressSelected} feeds={pinnedFeedInfos} />
|
|
||||||
+ }, [onPressSelected, pinnedFeedInfos])
|
|
||||||
+
|
|
||||||
+ const renderFollowingEmptyState = React.useCallback(() => <FollowingEmptyState />, [])
|
|
||||||
+ const renderCustomFeedEmptyState = React.useCallback(() => <CustomFeedEmptyState />, [])
|
|
||||||
+
|
|
||||||
+ const homeFeedParams = React.useMemo(() => ({
|
|
||||||
+ mergeFeedEnabled: false, mergeFeedSources: []
|
|
||||||
+ }), [preferences])
|
|
||||||
+
|
|
||||||
+ return (
|
+ return (
|
||||||
+ <Pager ref={pagerRef} testID="homeScreen" initialPage={selectedIndex} onPageSelected={onPageSelected} onPageScrollStateChanged={onPageScrollStateChanged} renderTabBar={renderTabBar}>
|
+ <Pager ref={pagerRef} testID="homeScreen" initialPage={selectedIndex} onPageSelected={onPageSelected} onPageScrollStateChanged={onPageScrollStateChanged} renderTabBar={renderTabBar}>
|
||||||
+ {pinnedFeedInfos.map((feedInfo, index) => {
|
+ {pinnedFeedInfos.map((feedInfo, index) => {
|
||||||
@@ -447,7 +470,7 @@ index e058e2883..0f583c915 100644
|
|||||||
-})
|
-})
|
||||||
+const styles = StyleSheet.create({ loading: { height: '100%', alignContent: 'center', justifyContent: 'center', paddingBottom: 100 } })
|
+const styles = StyleSheet.create({ loading: { height: '100%', alignContent: 'center', justifyContent: 'center', paddingBottom: 100 } })
|
||||||
diff --git a/src/view/screens/PrivacyPolicy.tsx b/src/view/screens/PrivacyPolicy.tsx
|
diff --git a/src/view/screens/PrivacyPolicy.tsx b/src/view/screens/PrivacyPolicy.tsx
|
||||||
index a89eaadc4..228af4966 100644
|
index a89eaadc4..1da393f03 100644
|
||||||
--- a/src/view/screens/PrivacyPolicy.tsx
|
--- a/src/view/screens/PrivacyPolicy.tsx
|
||||||
+++ b/src/view/screens/PrivacyPolicy.tsx
|
+++ b/src/view/screens/PrivacyPolicy.tsx
|
||||||
@@ -1,52 +1,13 @@
|
@@ -1,52 +1,13 @@
|
||||||
@@ -504,12 +527,12 @@ index a89eaadc4..228af4966 100644
|
|||||||
- </View>
|
- </View>
|
||||||
- <View style={s.footerSpacer} />
|
- <View style={s.footerSpacer} />
|
||||||
- </ScrollView>
|
- </ScrollView>
|
||||||
+ <WebView source={{ uri: 'https://syu.is/privacy' }} style={{ flex: 1 }} />
|
+ <WebView source={{ uri: 'https://syu.is/about/support/privacy-policy' }} style={{ flex: 1 }} />
|
||||||
</Layout.Screen>
|
</Layout.Screen>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx
|
diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx
|
||||||
index d843c713c..c0b34c886 100644
|
index d843c713c..b81767bd5 100644
|
||||||
--- a/src/view/screens/TermsOfService.tsx
|
--- a/src/view/screens/TermsOfService.tsx
|
||||||
+++ b/src/view/screens/TermsOfService.tsx
|
+++ b/src/view/screens/TermsOfService.tsx
|
||||||
@@ -1,50 +1,13 @@
|
@@ -1,50 +1,13 @@
|
||||||
@@ -564,7 +587,7 @@ index d843c713c..c0b34c886 100644
|
|||||||
- </View>
|
- </View>
|
||||||
- <View style={s.footerSpacer} />
|
- <View style={s.footerSpacer} />
|
||||||
- </ScrollView>
|
- </ScrollView>
|
||||||
+ <WebView source={{ uri: 'https://syu.is/terms' }} style={{ flex: 1 }} />
|
+ <WebView source={{ uri: 'https://syu.is/about/support/tos' }} style={{ flex: 1 }} />
|
||||||
</Layout.Screen>
|
</Layout.Screen>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,311 +1,8 @@
|
|||||||
diff --git a/src/components/dialogs/BirthDateSettings.tsx b/src/components/dialogs/BirthDateSettings.tsx
|
|
||||||
index 9915d0a2d..c200a7c67 100644
|
|
||||||
--- a/src/components/dialogs/BirthDateSettings.tsx
|
|
||||||
+++ b/src/components/dialogs/BirthDateSettings.tsx
|
|
||||||
@@ -163,7 +163,7 @@ function BirthdayInner({
|
|
||||||
<Trans>
|
|
||||||
You must be at least 13 years old to use Bluesky. Read our{' '}
|
|
||||||
<SimpleInlineLinkText
|
|
||||||
- to="https://bsky.social/about/support/tos"
|
|
||||||
+ to="https://syu.is/about/support/tos"
|
|
||||||
label={_(msg`Terms of Service`)}>
|
|
||||||
Terms of Service
|
|
||||||
</SimpleInlineLinkText>{' '}
|
|
||||||
diff --git a/src/components/dialogs/ServerInput.tsx b/src/components/dialogs/ServerInput.tsx
|
|
||||||
index d7c02bb9f..fda1dfe4a 100644
|
|
||||||
--- a/src/components/dialogs/ServerInput.tsx
|
|
||||||
+++ b/src/components/dialogs/ServerInput.tsx
|
|
||||||
@@ -165,7 +165,7 @@ function DialogInner({
|
|
||||||
<Trans>
|
|
||||||
Bluesky is an open network where you can choose your own
|
|
||||||
provider. If you're new here, we recommend sticking with the
|
|
||||||
- default Bluesky Social option.
|
|
||||||
+ default syu.is option.
|
|
||||||
</Trans>
|
|
||||||
</Admonition>
|
|
||||||
</View>
|
|
||||||
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
|
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
|
||||||
index ed2a6cfb7..2f387b4a8 100644
|
index f76147ccf..36b4d7de1 100644
|
||||||
--- a/src/view/shell/Drawer.tsx
|
--- a/src/view/shell/Drawer.tsx
|
||||||
+++ b/src/view/shell/Drawer.tsx
|
+++ b/src/view/shell/Drawer.tsx
|
||||||
@@ -1,60 +1,50 @@
|
@@ -292,17 +292,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
||||||
-import React, {type ComponentProps, type JSX} from 'react'
|
|
||||||
-import {Linking, ScrollView, TouchableOpacity, View} from 'react-native'
|
|
||||||
-import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
|
||||||
-import {msg, Plural, plural, Trans} from '@lingui/macro'
|
|
||||||
-import {useLingui} from '@lingui/react'
|
|
||||||
-import {StackActions, useNavigation} from '@react-navigation/native'
|
|
||||||
-
|
|
||||||
-import {useActorStatus} from '#/lib/actor-status'
|
|
||||||
-import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
|
|
||||||
-import {type PressableScale} from '#/lib/custom-animations/PressableScale'
|
|
||||||
-import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
|
|
||||||
-import {getTabState, TabState} from '#/lib/routes/helpers'
|
|
||||||
-import {type NavigationProp} from '#/lib/routes/types'
|
|
||||||
-import {sanitizeHandle} from '#/lib/strings/handles'
|
|
||||||
-import {colors} from '#/lib/styles'
|
|
||||||
-import {isWeb} from '#/platform/detection'
|
|
||||||
-import {emitSoftReset} from '#/state/events'
|
|
||||||
-import {useKawaiiMode} from '#/state/preferences/kawaii'
|
|
||||||
-import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
|
||||||
-import {useProfileQuery} from '#/state/queries/profile'
|
|
||||||
-import {type SessionAccount, useSession} from '#/state/session'
|
|
||||||
-import {useSetDrawerOpen} from '#/state/shell'
|
|
||||||
-import {formatCount} from '#/view/com/util/numeric/format'
|
|
||||||
-import {UserAvatar} from '#/view/com/util/UserAvatar'
|
|
||||||
-import {NavSignupCard} from '#/view/shell/NavSignupCard'
|
|
||||||
-import {atoms as a, tokens, useTheme, web} from '#/alf'
|
|
||||||
-import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
|
||||||
-import {Divider} from '#/components/Divider'
|
|
||||||
+import React, { type ComponentProps, type JSX } from 'react'
|
|
||||||
+import { Linking, ScrollView, TouchableOpacity, View } from 'react-native'
|
|
||||||
+import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
||||||
+import { msg, Plural, plural, Trans } from '@lingui/macro'
|
|
||||||
+import { useLingui } from '@lingui/react'
|
|
||||||
+import { StackActions, useNavigation } from '@react-navigation/native'
|
|
||||||
+
|
|
||||||
+import { useActorStatus } from '#/lib/actor-status'
|
|
||||||
+import { FEEDBACK_FORM_URL, HELP_DESK_URL } from '#/lib/constants'
|
|
||||||
+import { type PressableScale } from '#/lib/custom-animations/PressableScale'
|
|
||||||
+import { useNavigationTabState } from '#/lib/hooks/useNavigationTabState'
|
|
||||||
+import { getTabState, TabState } from '#/lib/routes/helpers'
|
|
||||||
+import { type NavigationProp } from '#/lib/routes/types'
|
|
||||||
+import { sanitizeHandle } from '#/lib/strings/handles'
|
|
||||||
+import { colors } from '#/lib/styles'
|
|
||||||
+import { isWeb } from '#/platform/detection'
|
|
||||||
+import { emitSoftReset } from '#/state/events'
|
|
||||||
+import { useKawaiiMode } from '#/state/preferences/kawaii'
|
|
||||||
+import { useUnreadNotifications } from '#/state/queries/notifications/unread'
|
|
||||||
+import { useProfileQuery } from '#/state/queries/profile'
|
|
||||||
+import { type SessionAccount, useSession } from '#/state/session'
|
|
||||||
+import { useSetDrawerOpen } from '#/state/shell'
|
|
||||||
+import { formatCount } from '#/view/com/util/numeric/format'
|
|
||||||
+import { UserAvatar } from '#/view/com/util/UserAvatar'
|
|
||||||
+import { NavSignupCard } from '#/view/shell/NavSignupCard'
|
|
||||||
+import { atoms as a, tokens, useTheme, web } from '#/alf'
|
|
||||||
+import { Button } from '#/components/Button'
|
|
||||||
+import { Divider } from '#/components/Divider'
|
|
||||||
import {
|
|
||||||
Bell_Filled_Corner0_Rounded as BellFilled,
|
|
||||||
Bell_Stroke2_Corner0_Rounded as Bell,
|
|
||||||
} from '#/components/icons/Bell'
|
|
||||||
-import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark'
|
|
||||||
-import {BulletList_Stroke2_Corner0_Rounded as List} from '#/components/icons/BulletList'
|
|
||||||
-import {
|
|
||||||
- Hashtag_Filled_Corner0_Rounded as HashtagFilled,
|
|
||||||
- Hashtag_Stroke2_Corner0_Rounded as Hashtag,
|
|
||||||
-} from '#/components/icons/Hashtag'
|
|
||||||
import {
|
|
||||||
HomeOpen_Filled_Corner0_Rounded as HomeFilled,
|
|
||||||
HomeOpen_Stoke2_Corner0_Rounded as Home,
|
|
||||||
} from '#/components/icons/HomeOpen'
|
|
||||||
-import {MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled} from '#/components/icons/MagnifyingGlass'
|
|
||||||
-import {MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass2'
|
|
||||||
-import {
|
|
||||||
- Message_Stroke2_Corner0_Rounded as Message,
|
|
||||||
- Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
|
|
||||||
-} from '#/components/icons/Message'
|
|
||||||
-import {SettingsGear2_Stroke2_Corner0_Rounded as Settings} from '#/components/icons/SettingsGear2'
|
|
||||||
+import { MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled } from '#/components/icons/MagnifyingGlass'
|
|
||||||
+import { MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass } from '#/components/icons/MagnifyingGlass2'
|
|
||||||
+import { SettingsGear2_Stroke2_Corner0_Rounded as Settings } from '#/components/icons/SettingsGear2'
|
|
||||||
import {
|
|
||||||
UserCircle_Filled_Corner0_Rounded as UserCircleFilled,
|
|
||||||
UserCircle_Stroke2_Corner0_Rounded as UserCircle,
|
|
||||||
} from '#/components/icons/UserCircle'
|
|
||||||
-import {InlineLinkText} from '#/components/Link'
|
|
||||||
-import {Text} from '#/components/Typography'
|
|
||||||
-import {useSimpleVerificationState} from '#/components/verification'
|
|
||||||
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
|
|
||||||
+import { InlineLinkText } from '#/components/Link'
|
|
||||||
+import { Text } from '#/components/Typography'
|
|
||||||
+import { useSimpleVerificationState } from '#/components/verification'
|
|
||||||
+import { VerificationCheck } from '#/components/verification/VerificationCheck'
|
|
||||||
|
|
||||||
const iconWidth = 26
|
|
||||||
|
|
||||||
@@ -65,11 +55,11 @@ let DrawerProfileCard = ({
|
|
||||||
account: SessionAccount
|
|
||||||
onPressProfile: () => void
|
|
||||||
}): React.ReactNode => {
|
|
||||||
- const {_, i18n} = useLingui()
|
|
||||||
+ const { _, i18n } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
- const {data: profile} = useProfileQuery({did: account.did})
|
|
||||||
- const verification = useSimpleVerificationState({profile})
|
|
||||||
- const {isActive: live} = useActorStatus(profile)
|
|
||||||
+ const { data: profile } = useProfileQuery({ did: account.did })
|
|
||||||
+ const verification = useSimpleVerificationState({ profile })
|
|
||||||
+ const { isActive: live } = useActorStatus(profile)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TouchableOpacity
|
|
||||||
@@ -81,7 +71,6 @@ let DrawerProfileCard = ({
|
|
||||||
<UserAvatar
|
|
||||||
size={52}
|
|
||||||
avatar={profile?.avatar}
|
|
||||||
- // See https://github.com/bluesky-social/social-app/pull/1801:
|
|
||||||
usePlainRNImage={true}
|
|
||||||
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
|
||||||
live={live}
|
|
||||||
@@ -140,9 +129,9 @@ let DrawerProfileCard = ({
|
|
||||||
)
|
|
||||||
}
|
|
||||||
DrawerProfileCard = React.memo(DrawerProfileCard)
|
|
||||||
-export {DrawerProfileCard}
|
|
||||||
+export { DrawerProfileCard }
|
|
||||||
|
|
||||||
-let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
+let DrawerContent = ({ }: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
const t = useTheme()
|
|
||||||
const insets = useSafeAreaInsets()
|
|
||||||
const setDrawerOpen = useSetDrawerOpen()
|
|
||||||
@@ -150,27 +139,20 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
const {
|
|
||||||
isAtHome,
|
|
||||||
isAtSearch,
|
|
||||||
- isAtFeeds,
|
|
||||||
- isAtBookmarks,
|
|
||||||
isAtNotifications,
|
|
||||||
isAtMyProfile,
|
|
||||||
- isAtMessages,
|
|
||||||
} = useNavigationTabState()
|
|
||||||
- const {hasSession, currentAccount} = useSession()
|
|
||||||
-
|
|
||||||
- // events
|
|
||||||
- // =
|
|
||||||
+ const { hasSession, currentAccount } = useSession()
|
|
||||||
|
|
||||||
const onPressTab = React.useCallback(
|
|
||||||
(tab: 'Home' | 'Search' | 'Messages' | 'Notifications' | 'MyProfile') => {
|
|
||||||
const state = navigation.getState()
|
|
||||||
setDrawerOpen(false)
|
|
||||||
if (isWeb) {
|
|
||||||
- // hack because we have flat navigator for web and MyProfile does not exist on the web navigator -ansh
|
|
||||||
if (tab === 'MyProfile') {
|
|
||||||
- navigation.navigate('Profile', {name: currentAccount!.handle})
|
|
||||||
+ navigation.navigate('Profile', { name: currentAccount!.handle })
|
|
||||||
} else {
|
|
||||||
- // @ts-expect-error struggles with string unions, apparently
|
|
||||||
+ // @ts-expect-error struggles with string unions
|
|
||||||
navigation.navigate(tab)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
@@ -178,21 +160,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
if (tabState === TabState.InsideAtRoot) {
|
|
||||||
emitSoftReset()
|
|
||||||
} else if (tabState === TabState.Inside) {
|
|
||||||
- // find the correct navigator in which to pop-to-top
|
|
||||||
- const target = state.routes.find(route => route.name === `${tab}Tab`)
|
|
||||||
- ?.state?.key
|
|
||||||
+ const target = state.routes.find(route => route.name === `${tab}Tab`)?.state?.key
|
|
||||||
if (target) {
|
|
||||||
- // if we found it, trigger pop-to-top
|
|
||||||
- navigation.dispatch({
|
|
||||||
- ...StackActions.popToTop(),
|
|
||||||
- target,
|
|
||||||
- })
|
|
||||||
+ navigation.dispatch({ ...StackActions.popToTop(), target })
|
|
||||||
} else {
|
|
||||||
- // fallback: reset navigation
|
|
||||||
- navigation.reset({
|
|
||||||
- index: 0,
|
|
||||||
- routes: [{name: `${tab}Tab`}],
|
|
||||||
- })
|
|
||||||
+ navigation.reset({ index: 0, routes: [{ name: `${tab}Tab` }] })
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
navigation.navigate(`${tab}Tab`)
|
|
||||||
@@ -203,76 +175,21 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
)
|
|
||||||
|
|
||||||
const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab])
|
|
||||||
-
|
|
||||||
- const onPressSearch = React.useCallback(
|
|
||||||
- () => onPressTab('Search'),
|
|
||||||
- [onPressTab],
|
|
||||||
- )
|
|
||||||
-
|
|
||||||
- const onPressMessages = React.useCallback(
|
|
||||||
- () => onPressTab('Messages'),
|
|
||||||
- [onPressTab],
|
|
||||||
- )
|
|
||||||
-
|
|
||||||
- const onPressNotifications = React.useCallback(
|
|
||||||
- () => onPressTab('Notifications'),
|
|
||||||
- [onPressTab],
|
|
||||||
- )
|
|
||||||
-
|
|
||||||
- const onPressProfile = React.useCallback(() => {
|
|
||||||
- onPressTab('MyProfile')
|
|
||||||
- }, [onPressTab])
|
|
||||||
-
|
|
||||||
- const onPressMyFeeds = React.useCallback(() => {
|
|
||||||
- navigation.navigate('Feeds')
|
|
||||||
- setDrawerOpen(false)
|
|
||||||
- }, [navigation, setDrawerOpen])
|
|
||||||
-
|
|
||||||
- const onPressLists = React.useCallback(() => {
|
|
||||||
- navigation.navigate('Lists')
|
|
||||||
- setDrawerOpen(false)
|
|
||||||
- }, [navigation, setDrawerOpen])
|
|
||||||
-
|
|
||||||
- const onPressBookmarks = React.useCallback(() => {
|
|
||||||
- navigation.navigate('Bookmarks')
|
|
||||||
- setDrawerOpen(false)
|
|
||||||
- }, [navigation, setDrawerOpen])
|
|
||||||
-
|
|
||||||
+ const onPressSearch = React.useCallback(() => onPressTab('Search'), [onPressTab])
|
|
||||||
+ const onPressNotifications = React.useCallback(() => onPressTab('Notifications'), [onPressTab])
|
|
||||||
+ const onPressProfile = React.useCallback(() => { onPressTab('MyProfile') }, [onPressTab])
|
|
||||||
const onPressSettings = React.useCallback(() => {
|
|
||||||
navigation.navigate('Settings')
|
|
||||||
setDrawerOpen(false)
|
|
||||||
}, [navigation, setDrawerOpen])
|
|
||||||
|
|
||||||
- const onPressFeedback = React.useCallback(() => {
|
|
||||||
- Linking.openURL(
|
|
||||||
- FEEDBACK_FORM_URL({
|
|
||||||
- email: currentAccount?.email,
|
|
||||||
- handle: currentAccount?.handle,
|
|
||||||
- }),
|
|
||||||
- )
|
|
||||||
- }, [currentAccount])
|
|
||||||
-
|
|
||||||
- const onPressHelp = React.useCallback(() => {
|
|
||||||
- Linking.openURL(HELP_DESK_URL)
|
|
||||||
- }, [])
|
|
||||||
-
|
|
||||||
- // rendering
|
|
||||||
- // =
|
|
||||||
-
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
testID="drawer"
|
|
||||||
style={[a.flex_1, a.border_r, t.atoms.bg, t.atoms.border_contrast_low]}>
|
|
||||||
<ScrollView
|
|
||||||
style={[a.flex_1]}
|
|
||||||
- contentContainerStyle={[
|
|
||||||
- {
|
|
||||||
- paddingTop: Math.max(
|
|
||||||
- insets.top + a.pt_xl.paddingTop,
|
|
||||||
- a.pt_xl.paddingTop,
|
|
||||||
- ),
|
|
||||||
- },
|
|
||||||
- ]}>
|
|
||||||
+ contentContainerStyle={[{ paddingTop: Math.max(insets.top + a.pt_xl.paddingTop, a.pt_xl.paddingTop) }]}>
|
|
||||||
<View style={[a.px_xl]}>
|
|
||||||
{hasSession && currentAccount ? (
|
|
||||||
<DrawerProfileCard
|
|
||||||
@@ -284,7 +201,6 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
<NavSignupCard />
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
-
|
|
||||||
<Divider style={[a.mt_xl, a.mb_sm]} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
@@ -292,17 +208,10 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
<>
|
<>
|
||||||
<SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} />
|
<SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} />
|
||||||
<HomeMenuItem isActive={isAtHome} onPress={onPressHome} />
|
<HomeMenuItem isActive={isAtHome} onPress={onPressHome} />
|
||||||
@@ -314,7 +11,7 @@ index ed2a6cfb7..2f387b4a8 100644
|
|||||||
isActive={isAtNotifications}
|
isActive={isAtNotifications}
|
||||||
onPress={onPressNotifications}
|
onPress={onPressNotifications}
|
||||||
/>
|
/>
|
||||||
- <FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
|
<FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
|
||||||
- <ListsMenuItem onPress={onPressLists} />
|
- <ListsMenuItem onPress={onPressLists} />
|
||||||
- <BookmarksMenuItem
|
- <BookmarksMenuItem
|
||||||
- isActive={isAtBookmarks}
|
- isActive={isAtBookmarks}
|
||||||
@@ -323,53 +20,10 @@ index ed2a6cfb7..2f387b4a8 100644
|
|||||||
<ProfileMenuItem
|
<ProfileMenuItem
|
||||||
isActive={isAtMyProfile}
|
isActive={isAtMyProfile}
|
||||||
onPress={onPressProfile}
|
onPress={onPressProfile}
|
||||||
@@ -312,7 +221,6 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
@@ -357,17 +351,7 @@ let DrawerFooter = ({
|
||||||
) : (
|
),
|
||||||
<>
|
},
|
||||||
<HomeMenuItem isActive={isAtHome} onPress={onPressHome} />
|
]}>
|
||||||
- <FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
|
|
||||||
<SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
@@ -322,69 +230,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
<ExtraLinks />
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
-
|
|
||||||
- <DrawerFooter
|
|
||||||
- onPressFeedback={onPressFeedback}
|
|
||||||
- onPressHelp={onPressHelp}
|
|
||||||
- />
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
DrawerContent = React.memo(DrawerContent)
|
|
||||||
-export {DrawerContent}
|
|
||||||
-
|
|
||||||
-let DrawerFooter = ({
|
|
||||||
- onPressFeedback,
|
|
||||||
- onPressHelp,
|
|
||||||
-}: {
|
|
||||||
- onPressFeedback: () => void
|
|
||||||
- onPressHelp: () => void
|
|
||||||
-}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
- const insets = useSafeAreaInsets()
|
|
||||||
- return (
|
|
||||||
- <View
|
|
||||||
- style={[
|
|
||||||
- a.flex_row,
|
|
||||||
- a.gap_sm,
|
|
||||||
- a.flex_wrap,
|
|
||||||
- a.pl_xl,
|
|
||||||
- a.pt_md,
|
|
||||||
- {
|
|
||||||
- paddingBottom: Math.max(
|
|
||||||
- insets.bottom + tokens.space.xs,
|
|
||||||
- tokens.space.xl,
|
|
||||||
- ),
|
|
||||||
- },
|
|
||||||
- ]}>
|
|
||||||
- <Button
|
- <Button
|
||||||
- label={_(msg`Send feedback`)}
|
- label={_(msg`Send feedback`)}
|
||||||
- size="small"
|
- size="small"
|
||||||
@@ -381,333 +35,22 @@ index ed2a6cfb7..2f387b4a8 100644
|
|||||||
- <Trans>Feedback</Trans>
|
- <Trans>Feedback</Trans>
|
||||||
- </ButtonText>
|
- </ButtonText>
|
||||||
- </Button>
|
- </Button>
|
||||||
- <Button
|
+{/* Feedback button removed for syu.is */}
|
||||||
- label={_(msg`Get help`)}
|
|
||||||
- size="small"
|
|
||||||
- variant="outline"
|
|
||||||
- color="secondary"
|
|
||||||
- onPress={onPressHelp}
|
|
||||||
- style={{
|
|
||||||
- backgroundColor: 'transparent',
|
|
||||||
- }}>
|
|
||||||
- <ButtonText>
|
|
||||||
- <Trans>Help</Trans>
|
|
||||||
- </ButtonText>
|
|
||||||
- </Button>
|
|
||||||
- </View>
|
|
||||||
- )
|
|
||||||
-}
|
|
||||||
-DrawerFooter = React.memo(DrawerFooter)
|
|
||||||
+export { DrawerContent }
|
|
||||||
|
|
||||||
interface MenuItemProps extends ComponentProps<typeof PressableScale> {
|
|
||||||
icon: JSX.Element
|
|
||||||
@@ -400,7 +250,7 @@ let SearchMenuItem = ({
|
|
||||||
isActive: boolean
|
|
||||||
onPress: () => void
|
|
||||||
}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
@@ -426,7 +276,7 @@ let HomeMenuItem = ({
|
|
||||||
isActive: boolean
|
|
||||||
onPress: () => void
|
|
||||||
}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
@@ -445,32 +295,6 @@ let HomeMenuItem = ({
|
|
||||||
}
|
|
||||||
HomeMenuItem = React.memo(HomeMenuItem)
|
|
||||||
|
|
||||||
-let ChatMenuItem = ({
|
|
||||||
- isActive,
|
|
||||||
- onPress,
|
|
||||||
-}: {
|
|
||||||
- isActive: boolean
|
|
||||||
- onPress: () => void
|
|
||||||
-}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
- const t = useTheme()
|
|
||||||
- return (
|
|
||||||
- <MenuItem
|
|
||||||
- icon={
|
|
||||||
- isActive ? (
|
|
||||||
- <MessageFilled style={[t.atoms.text]} width={iconWidth} />
|
|
||||||
- ) : (
|
|
||||||
- <Message style={[t.atoms.text]} width={iconWidth} />
|
|
||||||
- )
|
|
||||||
- }
|
|
||||||
- label={_(msg`Chat`)}
|
|
||||||
- bold={isActive}
|
|
||||||
- onPress={onPress}
|
|
||||||
- />
|
|
||||||
- )
|
|
||||||
-}
|
|
||||||
-ChatMenuItem = React.memo(ChatMenuItem)
|
|
||||||
-
|
|
||||||
let NotificationsMenuItem = ({
|
|
||||||
isActive,
|
|
||||||
onPress,
|
|
||||||
@@ -478,7 +302,7 @@ let NotificationsMenuItem = ({
|
|
||||||
isActive: boolean
|
|
||||||
onPress: () => void
|
|
||||||
}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
const numUnreadNotifications = useUnreadNotifications()
|
|
||||||
return (
|
|
||||||
@@ -495,11 +319,11 @@ let NotificationsMenuItem = ({
|
|
||||||
numUnreadNotifications === ''
|
|
||||||
? ''
|
|
||||||
: _(
|
|
||||||
- msg`${plural(numUnreadNotifications ?? 0, {
|
|
||||||
- one: '# unread item',
|
|
||||||
- other: '# unread items',
|
|
||||||
- })}` || '',
|
|
||||||
- )
|
|
||||||
+ msg`${plural(numUnreadNotifications ?? 0, {
|
|
||||||
+ one: '# unread item',
|
|
||||||
+ other: '# unread items',
|
|
||||||
+ })}` || '',
|
|
||||||
+ )
|
|
||||||
}
|
|
||||||
count={numUnreadNotifications}
|
|
||||||
bold={isActive}
|
|
||||||
@@ -509,72 +333,6 @@ let NotificationsMenuItem = ({
|
|
||||||
}
|
|
||||||
NotificationsMenuItem = React.memo(NotificationsMenuItem)
|
|
||||||
|
|
||||||
-let FeedsMenuItem = ({
|
|
||||||
- isActive,
|
|
||||||
- onPress,
|
|
||||||
-}: {
|
|
||||||
- isActive: boolean
|
|
||||||
- onPress: () => void
|
|
||||||
-}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
- const t = useTheme()
|
|
||||||
- return (
|
|
||||||
- <MenuItem
|
|
||||||
- icon={
|
|
||||||
- isActive ? (
|
|
||||||
- <HashtagFilled width={iconWidth} style={[t.atoms.text]} />
|
|
||||||
- ) : (
|
|
||||||
- <Hashtag width={iconWidth} style={[t.atoms.text]} />
|
|
||||||
- )
|
|
||||||
- }
|
|
||||||
- label={_(msg`Feeds`)}
|
|
||||||
- bold={isActive}
|
|
||||||
- onPress={onPress}
|
|
||||||
- />
|
|
||||||
- )
|
|
||||||
-}
|
|
||||||
-FeedsMenuItem = React.memo(FeedsMenuItem)
|
|
||||||
-
|
|
||||||
-let ListsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
- const t = useTheme()
|
|
||||||
-
|
|
||||||
- return (
|
|
||||||
- <MenuItem
|
|
||||||
- icon={<List style={[t.atoms.text]} width={iconWidth} />}
|
|
||||||
- label={_(msg`Lists`)}
|
|
||||||
- onPress={onPress}
|
|
||||||
- />
|
|
||||||
- )
|
|
||||||
-}
|
|
||||||
-ListsMenuItem = React.memo(ListsMenuItem)
|
|
||||||
-
|
|
||||||
-let BookmarksMenuItem = ({
|
|
||||||
- isActive,
|
|
||||||
- onPress,
|
|
||||||
-}: {
|
|
||||||
- isActive: boolean
|
|
||||||
- onPress: () => void
|
|
||||||
-}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
- const t = useTheme()
|
|
||||||
-
|
|
||||||
- return (
|
|
||||||
- <MenuItem
|
|
||||||
- icon={
|
|
||||||
- isActive ? (
|
|
||||||
- <BookmarkFilled style={[t.atoms.text]} width={iconWidth} />
|
|
||||||
- ) : (
|
|
||||||
- <Bookmark style={[t.atoms.text]} width={iconWidth} />
|
|
||||||
- )
|
|
||||||
- }
|
|
||||||
- label={_(msg({message: 'Saved', context: 'link to bookmarks screen'}))}
|
|
||||||
- onPress={onPress}
|
|
||||||
- />
|
|
||||||
- )
|
|
||||||
-}
|
|
||||||
-BookmarksMenuItem = React.memo(BookmarksMenuItem)
|
|
||||||
-
|
|
||||||
let ProfileMenuItem = ({
|
|
||||||
isActive,
|
|
||||||
onPress,
|
|
||||||
@@ -582,7 +340,7 @@ let ProfileMenuItem = ({
|
|
||||||
isActive: boolean
|
|
||||||
onPress: () => void
|
|
||||||
}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
@@ -600,8 +358,8 @@ let ProfileMenuItem = ({
|
|
||||||
}
|
|
||||||
ProfileMenuItem = React.memo(ProfileMenuItem)
|
|
||||||
|
|
||||||
-let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+let SettingsMenuItem = ({ onPress }: { onPress: () => void }): React.ReactNode => {
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
@@ -613,7 +371,7 @@ let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
|
|
||||||
}
|
|
||||||
SettingsMenuItem = React.memo(SettingsMenuItem)
|
|
||||||
|
|
||||||
-function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
|
|
||||||
+function MenuItem({ icon, label, count, bold, onPress }: MenuItemProps) {
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<Button
|
<Button
|
||||||
@@ -621,7 +379,7 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
|
label={_(msg`Get help`)}
|
||||||
onPress={onPress}
|
size="small"
|
||||||
accessibilityRole="tab"
|
@@ -695,12 +679,12 @@ function ExtraLinks() {
|
||||||
label={label}>
|
|
||||||
- {({hovered, pressed}) => (
|
|
||||||
+ {({ hovered, pressed }) => (
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
a.flex_1,
|
|
||||||
@@ -640,7 +398,7 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
|
|
||||||
a.absolute,
|
|
||||||
a.inset_0,
|
|
||||||
a.align_end,
|
|
||||||
- {top: -4, right: a.gap_sm.gap * -1},
|
|
||||||
+ { top: -4, right: a.gap_sm.gap * -1 },
|
|
||||||
]}>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
@@ -686,37 +444,23 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
|
|
||||||
}
|
|
||||||
|
|
||||||
function ExtraLinks() {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
const kawaii = useKawaiiMode()
|
|
||||||
+ const navigation = useNavigation<NavigationProp>()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={[a.flex_col, a.gap_md, a.flex_wrap]}>
|
|
||||||
- <InlineLinkText
|
|
||||||
- style={[a.text_md]}
|
|
||||||
- label={_(msg`Terms of Service`)}
|
|
||||||
- to="https://bsky.social/about/support/tos">
|
|
||||||
- <Trans>Terms of Service</Trans>
|
|
||||||
- </InlineLinkText>
|
|
||||||
- <InlineLinkText
|
|
||||||
- style={[a.text_md]}
|
|
||||||
- to="https://bsky.social/about/support/privacy-policy"
|
|
||||||
- label={_(msg`Privacy Policy`)}>
|
|
||||||
- <Trans>Privacy Policy</Trans>
|
|
||||||
- </InlineLinkText>
|
|
||||||
- {kawaii && (
|
|
||||||
- <Text style={t.atoms.text_contrast_medium}>
|
|
||||||
- <Trans>
|
|
||||||
- Logo by{' '}
|
|
||||||
- <InlineLinkText
|
|
||||||
- style={[a.text_md]}
|
|
||||||
- to="/profile/sawaratsuki.bsky.social"
|
|
||||||
- label="@sawaratsuki.bsky.social">
|
|
||||||
- @sawaratsuki.bsky.social
|
|
||||||
- </InlineLinkText>
|
|
||||||
- </Trans>
|
|
||||||
+ <TouchableOpacity onPress={() => navigation.navigate('TermsOfService')}>
|
|
||||||
+ <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
|
||||||
+ <Trans>Terms of Service</Trans>
|
|
||||||
</Text>
|
|
||||||
- )}
|
|
||||||
+ </TouchableOpacity>
|
|
||||||
+ <TouchableOpacity onPress={() => navigation.navigate('PrivacyPolicy')}>
|
|
||||||
+ <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
|
||||||
+ <Trans>Privacy Policy</Trans>
|
|
||||||
+ </Text>
|
|
||||||
+ </TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx
|
|
||||||
index 779ebda68..bfd9b70fa 100644
|
|
||||||
--- a/src/view/shell/bottom-bar/BottomBar.tsx
|
|
||||||
+++ b/src/view/shell/bottom-bar/BottomBar.tsx
|
|
||||||
@@ -198,38 +198,6 @@ export function BottomBar({navigation}: BottomTabBarProps) {
|
|
||||||
accessibilityLabel={_(msg`Search`)}
|
|
||||||
accessibilityHint=""
|
|
||||||
/>
|
|
||||||
- <Btn
|
|
||||||
- testID="bottomBarMessagesBtn"
|
|
||||||
- icon={
|
|
||||||
- isAtMessages ? (
|
|
||||||
- <MessageFilled
|
|
||||||
- width={iconWidth - 1}
|
|
||||||
- style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
|
|
||||||
- />
|
|
||||||
- ) : (
|
|
||||||
- <Message
|
|
||||||
- width={iconWidth - 1}
|
|
||||||
- style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
|
|
||||||
- />
|
|
||||||
- )
|
|
||||||
- }
|
|
||||||
- onPress={onPressMessages}
|
|
||||||
- notificationCount={numUnreadMessages.numUnread}
|
|
||||||
- hasNew={numUnreadMessages.hasNew}
|
|
||||||
- accessible={true}
|
|
||||||
- accessibilityRole="tab"
|
|
||||||
- accessibilityLabel={_(msg`Chat`)}
|
|
||||||
- accessibilityHint={
|
|
||||||
- numUnreadMessages.count > 0
|
|
||||||
- ? _(
|
|
||||||
- msg`${plural(numUnreadMessages.numUnread ?? 0, {
|
|
||||||
- one: '# unread item',
|
|
||||||
- other: '# unread items',
|
|
||||||
- })}` || '',
|
|
||||||
- )
|
|
||||||
- : ''
|
|
||||||
- }
|
|
||||||
- />
|
|
||||||
<Btn
|
|
||||||
testID="bottomBarNotificationsBtn"
|
|
||||||
icon={
|
|
||||||
diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx
|
|
||||||
index 1d097fc9a..e11a3a202 100644
|
|
||||||
--- a/src/view/shell/desktop/RightNav.tsx
|
|
||||||
+++ b/src/view/shell/desktop/RightNav.tsx
|
|
||||||
@@ -109,13 +109,13 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<InlineLinkText
|
<InlineLinkText
|
||||||
|
style={[a.text_md]}
|
||||||
|
label={_(msg`Terms of Service`)}
|
||||||
|
- to="https://bsky.social/about/support/tos">
|
||||||
|
+ to="https://syu.is/about/support/tos">
|
||||||
|
<Trans>Terms of Service</Trans>
|
||||||
|
</InlineLinkText>
|
||||||
|
<InlineLinkText
|
||||||
|
style={[a.text_md]}
|
||||||
- to="https://bsky.social/about/support/privacy-policy"
|
- to="https://bsky.social/about/support/privacy-policy"
|
||||||
+ to="https://syu.is/about/support/privacy-policy"
|
+ to="https://syu.is/about/support/privacy-policy"
|
||||||
label={_(msg`Privacy`)}>
|
label={_(msg`Privacy Policy`)}>
|
||||||
{_(msg`Privacy`)}
|
<Trans>Privacy Policy</Trans>
|
||||||
</InlineLinkText>
|
|
||||||
{' • '}
|
|
||||||
<InlineLinkText
|
|
||||||
- to="https://bsky.social/about/support/tos"
|
|
||||||
+ to="https://syu.is/about/support/tos"
|
|
||||||
label={_(msg`Terms`)}>
|
|
||||||
{_(msg`Terms`)}
|
|
||||||
</InlineLinkText>
|
</InlineLinkText>
|
||||||
|
|||||||
@@ -11,112 +11,6 @@ index 6a00cfd23..f91decc08 100644
|
|||||||
|
|
||||||
return withPlugins(config, [
|
return withPlugins(config, [
|
||||||
// IOS
|
// IOS
|
||||||
diff --git a/src/ageAssurance/util.ts b/src/ageAssurance/util.ts
|
|
||||||
index 104328330..c992a21de 100644
|
|
||||||
--- a/src/ageAssurance/util.ts
|
|
||||||
+++ b/src/ageAssurance/util.ts
|
|
||||||
@@ -2,87 +2,32 @@ import {useMemo} from 'react'
|
|
||||||
import {
|
|
||||||
ageAssuranceRuleIDs as ids,
|
|
||||||
type AppBskyAgeassuranceDefs,
|
|
||||||
- getAgeAssuranceRegionConfig,
|
|
||||||
} from '@atproto/api'
|
|
||||||
-
|
|
||||||
-import {getAge} from '#/lib/strings/time'
|
|
||||||
-import {useAgeAssuranceDataContext} from '#/ageAssurance/data'
|
|
||||||
import {AgeAssuranceAccess} from '#/ageAssurance/types'
|
|
||||||
import {type Geolocation, useGeolocation} from '#/geolocation'
|
|
||||||
+import {useAgeAssuranceDataContext} from '#/ageAssurance/data'
|
|
||||||
|
|
||||||
-const DEFAULT_MIN_AGE = 13
|
|
||||||
-
|
|
||||||
-/**
|
|
||||||
- * Get age assurance region config based on geolocation, with fallback to
|
|
||||||
- * app defaults if no region config is found.
|
|
||||||
- *
|
|
||||||
- * See {@link getAgeAssuranceRegionConfig} for the generic option, which can
|
|
||||||
- * return undefined if the geolocation does not match any AA region.
|
|
||||||
- */
|
|
||||||
export function getAgeAssuranceRegionConfigWithFallback(
|
|
||||||
config: AppBskyAgeassuranceDefs.Config,
|
|
||||||
geolocation: Geolocation,
|
|
||||||
): AppBskyAgeassuranceDefs.ConfigRegion {
|
|
||||||
- const region = getAgeAssuranceRegionConfig(config, {
|
|
||||||
- countryCode: geolocation.countryCode ?? '',
|
|
||||||
- regionCode: geolocation.regionCode,
|
|
||||||
- })
|
|
||||||
-
|
|
||||||
- return (
|
|
||||||
- region || {
|
|
||||||
- countryCode: '*',
|
|
||||||
- regionCode: undefined,
|
|
||||||
- rules: [
|
|
||||||
- {
|
|
||||||
- $type: ids.IfDeclaredOverAge,
|
|
||||||
- age: DEFAULT_MIN_AGE,
|
|
||||||
- access: AgeAssuranceAccess.Full,
|
|
||||||
- },
|
|
||||||
- {
|
|
||||||
- $type: ids.Default,
|
|
||||||
- access: AgeAssuranceAccess.None,
|
|
||||||
- },
|
|
||||||
- ],
|
|
||||||
- }
|
|
||||||
- )
|
|
||||||
+ return {
|
|
||||||
+ countryCode: '*',
|
|
||||||
+ regionCode: undefined,
|
|
||||||
+ rules: [{ $type: ids.Default, access: AgeAssuranceAccess.Full }],
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
-/**
|
|
||||||
- * Hook to get the age assurance region config based on current geolocation.
|
|
||||||
- * Does not fall-back to our app defaults. If no config is found, returns
|
|
||||||
- * undefined, which indicates no regional age assurance rules apply.
|
|
||||||
- */
|
|
||||||
export function useAgeAssuranceRegionConfig() {
|
|
||||||
const geolocation = useGeolocation()
|
|
||||||
const {config} = useAgeAssuranceDataContext()
|
|
||||||
- return useMemo(() => {
|
|
||||||
- if (!config) return
|
|
||||||
- // use generic helper, we want to potentially return undefined
|
|
||||||
- return getAgeAssuranceRegionConfig(config, {
|
|
||||||
- countryCode: geolocation.countryCode ?? '',
|
|
||||||
- regionCode: geolocation.regionCode,
|
|
||||||
- })
|
|
||||||
- }, [config, geolocation])
|
|
||||||
+ return useMemo(() => ({
|
|
||||||
+ countryCode: '*',
|
|
||||||
+ regionCode: undefined,
|
|
||||||
+ rules: [{ $type: ids.Default, access: AgeAssuranceAccess.Full }],
|
|
||||||
+ }), [config, geolocation])
|
|
||||||
}
|
|
||||||
|
|
||||||
-/**
|
|
||||||
- * Some users may have erroneously set their birth date to the current date
|
|
||||||
- * if one wasn't set on their account. We previously didn't do validation on
|
|
||||||
- * the bday dialog, and it defaulted to the current date. This bug _has_ been
|
|
||||||
- * seen in production, so we need to check for it where possible.
|
|
||||||
- */
|
|
||||||
-export function isLegacyBirthdateBug(birthDate: string) {
|
|
||||||
- return ['2025', '2024', '2023'].includes((birthDate || '').slice(0, 4))
|
|
||||||
-}
|
|
||||||
-
|
|
||||||
-/**
|
|
||||||
- * Returns whether the user is under the minimum age required to use the app.
|
|
||||||
- * This applies to all regions.
|
|
||||||
- */
|
|
||||||
-export function isUserUnderMinimumAge(birthDate: string) {
|
|
||||||
- return getAge(new Date(birthDate)) < DEFAULT_MIN_AGE
|
|
||||||
-}
|
|
||||||
-
|
|
||||||
-export function isUserUnderAdultAge(birthDate: string) {
|
|
||||||
- return getAge(new Date(birthDate)) < 18
|
|
||||||
-}
|
|
||||||
+export function isLegacyBirthdateBug(birthDate: string) { return false }
|
|
||||||
+export function isUserUnderMinimumAge(birthDate: string) { return false }
|
|
||||||
+export function isUserUnderAdultAge(birthDate: string) { return false }
|
|
||||||
diff --git a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx b/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
|
diff --git a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx b/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
|
||||||
index 8365057e8..59c8506a2 100644
|
index 8365057e8..59c8506a2 100644
|
||||||
--- a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
|
--- a/src/components/PolicyUpdateOverlay/updates/202508/index.tsx
|
||||||
|
|||||||
@@ -1,318 +0,0 @@
|
|||||||
diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx
|
|
||||||
index 4f25468c9..95b183dcc 100644
|
|
||||||
--- a/src/view/com/posts/PostFeed.tsx
|
|
||||||
+++ b/src/view/com/posts/PostFeed.tsx
|
|
||||||
@@ -298,8 +298,13 @@ let PostFeed = ({
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
+ const errorMsg = String(e)
|
|
||||||
+ // Skip errors for missing repos (deleted accounts)
|
|
||||||
+ if (errorMsg.includes('Could not find repo')) {
|
|
||||||
+ return
|
|
||||||
+ }
|
|
||||||
if (!isNetworkError(e)) {
|
|
||||||
- logger.error('Poll latest failed', {feed, message: String(e)})
|
|
||||||
+ logger.error('Poll latest failed', {feed, message: errorMsg})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
diff --git a/src/view/screens/PrivacyPolicy.tsx b/src/view/screens/PrivacyPolicy.tsx
|
|
||||||
index a89eaadc4..c3a8b6114 100644
|
|
||||||
--- a/src/view/screens/PrivacyPolicy.tsx
|
|
||||||
+++ b/src/view/screens/PrivacyPolicy.tsx
|
|
||||||
@@ -1,51 +1,95 @@
|
|
||||||
import React from 'react'
|
|
||||||
-import {View} from 'react-native'
|
|
||||||
-import {msg, Trans} from '@lingui/macro'
|
|
||||||
-import {useLingui} from '@lingui/react'
|
|
||||||
-import {useFocusEffect} from '@react-navigation/native'
|
|
||||||
-
|
|
||||||
-import {usePalette} from '#/lib/hooks/usePalette'
|
|
||||||
-import {
|
|
||||||
- type CommonNavigatorParams,
|
|
||||||
- type NativeStackScreenProps,
|
|
||||||
-} from '#/lib/routes/types'
|
|
||||||
-import {s} from '#/lib/styles'
|
|
||||||
-import {useSetMinimalShellMode} from '#/state/shell'
|
|
||||||
-import {TextLink} from '#/view/com/util/Link'
|
|
||||||
-import {Text} from '#/view/com/util/text/Text'
|
|
||||||
-import {ScrollView} from '#/view/com/util/Views'
|
|
||||||
+import { ScrollView } from 'react-native'
|
|
||||||
import * as Layout from '#/components/Layout'
|
|
||||||
-import {ViewHeader} from '../com/util/ViewHeader'
|
|
||||||
-
|
|
||||||
-type Props = NativeStackScreenProps<CommonNavigatorParams, 'PrivacyPolicy'>
|
|
||||||
-export const PrivacyPolicyScreen = (_props: Props) => {
|
|
||||||
- const pal = usePalette('default')
|
|
||||||
- const {_} = useLingui()
|
|
||||||
- const setMinimalShellMode = useSetMinimalShellMode()
|
|
||||||
-
|
|
||||||
- useFocusEffect(
|
|
||||||
- React.useCallback(() => {
|
|
||||||
- setMinimalShellMode(false)
|
|
||||||
- }, [setMinimalShellMode]),
|
|
||||||
- )
|
|
||||||
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
|
||||||
+import {atoms as a, useTheme} from '#/alf'
|
|
||||||
+import {Text} from '#/components/Typography'
|
|
||||||
+
|
|
||||||
+export function PrivacyPolicyScreen() {
|
|
||||||
+ useSetTitle('Privacy Policy')
|
|
||||||
+ const t = useTheme()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout.Screen>
|
|
||||||
- <ViewHeader title={_(msg`Privacy Policy`)} />
|
|
||||||
- <ScrollView style={[s.hContentRegion, pal.view]}>
|
|
||||||
- <View style={[s.p20]}>
|
|
||||||
- <Text style={pal.text}>
|
|
||||||
- <Trans>
|
|
||||||
- The Privacy Policy has been moved to{' '}
|
|
||||||
- <TextLink
|
|
||||||
- style={pal.link}
|
|
||||||
- href="https://bsky.social/about/support/privacy-policy"
|
|
||||||
- text="bsky.social/about/support/privacy-policy"
|
|
||||||
- />
|
|
||||||
- </Trans>
|
|
||||||
- </Text>
|
|
||||||
- </View>
|
|
||||||
- <View style={s.footerSpacer} />
|
|
||||||
+ <ScrollView
|
|
||||||
+ style={[a.flex_1, {backgroundColor: t.palette.white}]}
|
|
||||||
+ contentContainerStyle={[a.p_lg, a.pt_5xl, a.pb_5xl]}>
|
|
||||||
+ <Text style={[a.text_2xl, a.font_bold, a.mb_lg]}>Privacy Policy</Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ This application (hereinafter referred to as "the App") respects user privacy and is committed to protecting personal information.
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>1. Information We Collect</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ The App is a decentralized social network using the AT Protocol.
|
|
||||||
+ Information posted by users is stored on the selected server (PDS).
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>2. Use of Information</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ The collected information is used for the following purposes:
|
|
||||||
+ {'\n'}- Providing and operating the service
|
|
||||||
+ {'\n'}- User support
|
|
||||||
+ {'\n'}- Service improvement
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>3. Third-Party Disclosure</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ The App will not provide users' personal information to third parties without user consent, except as required by law.
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>4. Security</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ The App takes appropriate security measures to protect personal information.
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>5. Contact</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ For questions regarding this Privacy Policy, please contact us through the support link in the settings screen.
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_5xl, a.mb_md]}>日本語訳(参考)</Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_xl, a.font_bold, a.mb_lg]}>プライバシーポリシー</Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ 本アプリケーション(以下「本アプリ」)は、ユーザーのプライバシーを尊重し、個人情報の保護に努めます。
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>1. 収集する情報</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ 本アプリは、ATプロトコルを使用した分散型ソーシャルネットワークです。
|
|
||||||
+ ユーザーが投稿した情報は、選択したサーバー(PDS)に保存されます。
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>2. 情報の利用目的</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ 収集した情報は、以下の目的で利用されます:
|
|
||||||
+ {'\n'}- サービスの提供・運営
|
|
||||||
+ {'\n'}- ユーザーサポート
|
|
||||||
+ {'\n'}- サービスの改善
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>3. 情報の第三者提供</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ 本アプリは、ユーザーの個人情報を、法令に基づく場合を除き、
|
|
||||||
+ ユーザーの同意なく第三者に提供することはありません。
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>4. セキュリティ</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ 本アプリは、個人情報の保護のため、適切な安全対策を講じます。
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>5. お問い合わせ</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ プライバシーポリシーに関するご質問は、設定画面のサポートリンクからお問い合わせください。
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
|
|
||||||
+ Last Updated: December 7, 2025
|
|
||||||
+ </Text>
|
|
||||||
</ScrollView>
|
|
||||||
</Layout.Screen>
|
|
||||||
)
|
|
||||||
diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx
|
|
||||||
index d843c713c..3d2323f73 100644
|
|
||||||
--- a/src/view/screens/TermsOfService.tsx
|
|
||||||
+++ b/src/view/screens/TermsOfService.tsx
|
|
||||||
@@ -1,49 +1,110 @@
|
|
||||||
import React from 'react'
|
|
||||||
-import {View} from 'react-native'
|
|
||||||
-import {msg, Trans} from '@lingui/macro'
|
|
||||||
-import {useLingui} from '@lingui/react'
|
|
||||||
-import {useFocusEffect} from '@react-navigation/native'
|
|
||||||
-
|
|
||||||
-import {usePalette} from '#/lib/hooks/usePalette'
|
|
||||||
-import {
|
|
||||||
- type CommonNavigatorParams,
|
|
||||||
- type NativeStackScreenProps,
|
|
||||||
-} from '#/lib/routes/types'
|
|
||||||
-import {s} from '#/lib/styles'
|
|
||||||
-import {useSetMinimalShellMode} from '#/state/shell'
|
|
||||||
-import {TextLink} from '#/view/com/util/Link'
|
|
||||||
-import {Text} from '#/view/com/util/text/Text'
|
|
||||||
-import {ScrollView} from '#/view/com/util/Views'
|
|
||||||
+import { ScrollView } from 'react-native'
|
|
||||||
import * as Layout from '#/components/Layout'
|
|
||||||
-import {ViewHeader} from '../com/util/ViewHeader'
|
|
||||||
-
|
|
||||||
-type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'>
|
|
||||||
-export const TermsOfServiceScreen = (_props: Props) => {
|
|
||||||
- const pal = usePalette('default')
|
|
||||||
- const setMinimalShellMode = useSetMinimalShellMode()
|
|
||||||
- const {_} = useLingui()
|
|
||||||
-
|
|
||||||
- useFocusEffect(
|
|
||||||
- React.useCallback(() => {
|
|
||||||
- setMinimalShellMode(false)
|
|
||||||
- }, [setMinimalShellMode]),
|
|
||||||
- )
|
|
||||||
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
|
||||||
+import {atoms as a, useTheme} from '#/alf'
|
|
||||||
+import {Text} from '#/components/Typography'
|
|
||||||
+
|
|
||||||
+export function TermsOfServiceScreen() {
|
|
||||||
+ useSetTitle('Terms of Service')
|
|
||||||
+ const t = useTheme()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout.Screen>
|
|
||||||
- <ViewHeader title={_(msg`Terms of Service`)} />
|
|
||||||
- <ScrollView style={[s.hContentRegion, pal.view]}>
|
|
||||||
- <View style={[s.p20]}>
|
|
||||||
- <Text style={pal.text}>
|
|
||||||
- <Trans>The Terms of Service have been moved to</Trans>{' '}
|
|
||||||
- <TextLink
|
|
||||||
- style={pal.link}
|
|
||||||
- href="https://bsky.social/about/support/tos"
|
|
||||||
- text="bsky.social/about/support/tos"
|
|
||||||
- />
|
|
||||||
- </Text>
|
|
||||||
- </View>
|
|
||||||
- <View style={s.footerSpacer} />
|
|
||||||
+ <ScrollView
|
|
||||||
+ style={[a.flex_1, {backgroundColor: t.palette.white}]}
|
|
||||||
+ contentContainerStyle={[a.p_lg, a.pt_5xl, a.pb_5xl]}>
|
|
||||||
+ <Text style={[a.text_2xl, a.font_bold, a.mb_lg]}>Terms of Service</Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ These Terms of Service (hereinafter referred to as "these Terms") set forth the conditions for using this application (hereinafter referred to as "the App").
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 1 (Application)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ These Terms shall apply to all relationships between users and the provider of the App regarding the use of the App.
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 2 (User Registration)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ Use of the App requires an AT Protocol-compatible account.
|
|
||||||
+ Registration information must be kept accurate and up-to-date.
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 3 (Prohibited Acts)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ Users shall not engage in the following acts when using the App:
|
|
||||||
+ {'\n'}- Acts that violate laws or public order and morals
|
|
||||||
+ {'\n'}- Acts related to criminal activity
|
|
||||||
+ {'\n'}- Acts that infringe upon the rights of other users or third parties
|
|
||||||
+ {'\n'}- Acts that interfere with the operation of the App
|
|
||||||
+ {'\n'}- Unauthorized access or attempts thereof
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 4 (Disclaimer)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ The App is a decentralized service using the AT Protocol.
|
|
||||||
+ The provider makes no warranties, express or implied, regarding the App.
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 5 (Changes to Terms)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ The provider may change these Terms without obtaining user consent when deemed necessary.
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>Article 6 (Contact)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ For questions regarding these Terms, please contact us through the support link in the settings screen.
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_5xl, a.mb_md]}>日本語訳(参考)</Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_xl, a.font_bold, a.mb_lg]}>利用規約</Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ 本利用規約(以下「本規約」)は、本アプリケーション(以下「本アプリ」)の利用条件を定めるものです。
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第1条(適用)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ 本規約は、ユーザーと本アプリの提供者との間の本アプリの利用に関わる一切の関係に適用されます。
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第2条(利用登録)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ 本アプリの利用にあたっては、ATプロトコル対応のアカウントが必要です。
|
|
||||||
+ 登録情報は正確かつ最新の状態に保つ必要があります。
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第3条(禁止事項)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ ユーザーは、本アプリの利用にあたり、以下の行為をしてはなりません:
|
|
||||||
+ {'\n'}- 法令または公序良俗に違反する行為
|
|
||||||
+ {'\n'}- 犯罪行為に関連する行為
|
|
||||||
+ {'\n'}- 他のユーザーまたは第三者の権利を侵害する行為
|
|
||||||
+ {'\n'}- 本アプリの運営を妨害する行為
|
|
||||||
+ {'\n'}- 不正アクセスまたはこれを試みる行為
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第4条(免責事項)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ 本アプリは、ATプロトコルを使用した分散型サービスです。
|
|
||||||
+ 提供者は、本アプリに関して、明示的・黙示的を問わず、いかなる保証も行いません。
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第5条(規約の変更)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ 提供者は、必要と判断した場合、ユーザーの承諾を得ることなく本規約を変更できるものとします。
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_lg, a.font_bold, a.mt_lg, a.mb_md]}>第6条(お問い合わせ)</Text>
|
|
||||||
+ <Text style={[a.mb_md]}>
|
|
||||||
+ 本規約に関するご質問は、設定画面のサポートリンクからお問い合わせください。
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
+ <Text style={[a.text_sm, a.mt_xl, {color: t.palette.contrast_500}]}>
|
|
||||||
+ Last Updated: December 7, 2025
|
|
||||||
+ </Text>
|
|
||||||
</ScrollView>
|
|
||||||
</Layout.Screen>
|
|
||||||
)
|
|
||||||
@@ -34,648 +34,141 @@ index c315a8341..9b2f50a83 100644
|
|||||||
CommunityGuidelines: undefined
|
CommunityGuidelines: undefined
|
||||||
CopyrightPolicy: undefined
|
CopyrightPolicy: undefined
|
||||||
LanguageSettings: undefined
|
LanguageSettings: undefined
|
||||||
diff --git a/src/routes.ts b/src/routes.ts
|
diff --git a/src/view/screens/License.tsx b/src/view/screens/License.tsx
|
||||||
index 1ed913bb2..aa6fccc4e 100644
|
new file mode 100644
|
||||||
--- a/src/routes.ts
|
index 000000000..87f52a972
|
||||||
+++ b/src/routes.ts
|
--- /dev/null
|
||||||
@@ -71,8 +71,9 @@ export const router = new Router<AllNavigatableRoutes>({
|
+++ b/src/view/screens/License.tsx
|
||||||
MiscellaneousNotificationSettings: '/settings/notifications/miscellaneous',
|
@@ -0,0 +1,132 @@
|
||||||
// support
|
+import React from 'react'
|
||||||
Support: '/support',
|
+import { ScrollView, Text as RNText, StyleSheet } from 'react-native'
|
||||||
- PrivacyPolicy: '/support/privacy',
|
+import * as Layout from '#/components/Layout'
|
||||||
- TermsOfService: '/support/tos',
|
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
||||||
+ PrivacyPolicy: 'https://syu.is/privacy',
|
+import {atoms as a, useTheme} from '#/alf'
|
||||||
+ TermsOfService: 'https://syu.is/terms',
|
|
||||||
+ License: '/support/license',
|
|
||||||
CommunityGuidelines: '/support/community-guidelines',
|
|
||||||
CopyrightPolicy: '/support/copyright',
|
|
||||||
// hashtags
|
|
||||||
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
|
|
||||||
index ed2a6cfb7..982c40b55 100644
|
|
||||||
--- a/src/view/shell/Drawer.tsx
|
|
||||||
+++ b/src/view/shell/Drawer.tsx
|
|
||||||
@@ -1,60 +1,50 @@
|
|
||||||
-import React, {type ComponentProps, type JSX} from 'react'
|
|
||||||
-import {Linking, ScrollView, TouchableOpacity, View} from 'react-native'
|
|
||||||
-import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
|
||||||
-import {msg, Plural, plural, Trans} from '@lingui/macro'
|
|
||||||
-import {useLingui} from '@lingui/react'
|
|
||||||
-import {StackActions, useNavigation} from '@react-navigation/native'
|
|
||||||
-
|
|
||||||
-import {useActorStatus} from '#/lib/actor-status'
|
|
||||||
-import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
|
|
||||||
-import {type PressableScale} from '#/lib/custom-animations/PressableScale'
|
|
||||||
-import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState'
|
|
||||||
-import {getTabState, TabState} from '#/lib/routes/helpers'
|
|
||||||
-import {type NavigationProp} from '#/lib/routes/types'
|
|
||||||
-import {sanitizeHandle} from '#/lib/strings/handles'
|
|
||||||
-import {colors} from '#/lib/styles'
|
|
||||||
-import {isWeb} from '#/platform/detection'
|
|
||||||
-import {emitSoftReset} from '#/state/events'
|
|
||||||
-import {useKawaiiMode} from '#/state/preferences/kawaii'
|
|
||||||
-import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
|
||||||
-import {useProfileQuery} from '#/state/queries/profile'
|
|
||||||
-import {type SessionAccount, useSession} from '#/state/session'
|
|
||||||
-import {useSetDrawerOpen} from '#/state/shell'
|
|
||||||
-import {formatCount} from '#/view/com/util/numeric/format'
|
|
||||||
-import {UserAvatar} from '#/view/com/util/UserAvatar'
|
|
||||||
-import {NavSignupCard} from '#/view/shell/NavSignupCard'
|
|
||||||
-import {atoms as a, tokens, useTheme, web} from '#/alf'
|
|
||||||
-import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
|
||||||
-import {Divider} from '#/components/Divider'
|
|
||||||
+import React, { type ComponentProps, type JSX } from 'react'
|
|
||||||
+import { Linking, ScrollView, TouchableOpacity, View } from 'react-native'
|
|
||||||
+import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
||||||
+import { msg, Plural, plural, Trans } from '@lingui/macro'
|
|
||||||
+import { useLingui } from '@lingui/react'
|
|
||||||
+import { StackActions, useNavigation } from '@react-navigation/native'
|
|
||||||
+
|
+
|
||||||
+import { useActorStatus } from '#/lib/actor-status'
|
+export function LicenseScreen() {
|
||||||
+import { FEEDBACK_FORM_URL, HELP_DESK_URL } from '#/lib/constants'
|
+ useSetTitle('License')
|
||||||
+import { type PressableScale } from '#/lib/custom-animations/PressableScale'
|
+ const t = useTheme()
|
||||||
+import { useNavigationTabState } from '#/lib/hooks/useNavigationTabState'
|
+
|
||||||
+import { getTabState, TabState } from '#/lib/routes/helpers'
|
+ return (
|
||||||
+import { type NavigationProp } from '#/lib/routes/types'
|
+ <Layout.Screen>
|
||||||
+import { sanitizeHandle } from '#/lib/strings/handles'
|
+ <Layout.Header.Outer>
|
||||||
+import { colors } from '#/lib/styles'
|
+ <Layout.Header.BackButton />
|
||||||
+import { isWeb } from '#/platform/detection'
|
+ <Layout.Header.Content>
|
||||||
+import { emitSoftReset } from '#/state/events'
|
+ <Layout.Header.TitleText>License</Layout.Header.TitleText>
|
||||||
+import { useKawaiiMode } from '#/state/preferences/kawaii'
|
+ </Layout.Header.Content>
|
||||||
+import { useUnreadNotifications } from '#/state/queries/notifications/unread'
|
+ <Layout.Header.Slot />
|
||||||
+import { useProfileQuery } from '#/state/queries/profile'
|
+ </Layout.Header.Outer>
|
||||||
+import { type SessionAccount, useSession } from '#/state/session'
|
+ <Layout.Content>
|
||||||
+import { useSetDrawerOpen } from '#/state/shell'
|
+ <ScrollView
|
||||||
+import { formatCount } from '#/view/com/util/numeric/format'
|
+ style={[a.flex_1]}
|
||||||
+import { UserAvatar } from '#/view/com/util/UserAvatar'
|
+ contentContainerStyle={[a.p_lg, a.pt_xl, a.pb_5xl]}>
|
||||||
+import { NavSignupCard } from '#/view/shell/NavSignupCard'
|
+ <RNText style={styles.text}>
|
||||||
+import { atoms as a, tokens, useTheme, web } from '#/alf'
|
+ This application is based on Bluesky Social App.
|
||||||
+import { Button } from '#/components/Button'
|
+ </RNText>
|
||||||
+import { Divider } from '#/components/Divider'
|
+
|
||||||
import {
|
+ <RNText style={styles.link}>
|
||||||
Bell_Filled_Corner0_Rounded as BellFilled,
|
+ https://github.com/bluesky-social/social-app
|
||||||
Bell_Stroke2_Corner0_Rounded as Bell,
|
+ </RNText>
|
||||||
} from '#/components/icons/Bell'
|
+
|
||||||
-import {Bookmark, BookmarkFilled} from '#/components/icons/Bookmark'
|
+ <RNText style={styles.sectionTitle}>MIT License</RNText>
|
||||||
-import {BulletList_Stroke2_Corner0_Rounded as List} from '#/components/icons/BulletList'
|
+
|
||||||
-import {
|
+ <RNText style={styles.mono}>
|
||||||
- Hashtag_Filled_Corner0_Rounded as HashtagFilled,
|
+ Copyright (c) 2022-2025 Bluesky PBC
|
||||||
- Hashtag_Stroke2_Corner0_Rounded as Hashtag,
|
+ </RNText>
|
||||||
-} from '#/components/icons/Hashtag'
|
+
|
||||||
import {
|
+ <RNText style={styles.text}>
|
||||||
HomeOpen_Filled_Corner0_Rounded as HomeFilled,
|
+ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
HomeOpen_Stoke2_Corner0_Rounded as Home,
|
+ of this software and associated documentation files (the "Software"), to deal
|
||||||
} from '#/components/icons/HomeOpen'
|
+ in the Software without restriction, including without limitation the rights
|
||||||
-import {MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled} from '#/components/icons/MagnifyingGlass'
|
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
-import {MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass2'
|
+ copies of the Software, and to permit persons to whom the Software is
|
||||||
-import {
|
+ furnished to do so, subject to the following conditions:
|
||||||
- Message_Stroke2_Corner0_Rounded as Message,
|
+ </RNText>
|
||||||
- Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
|
+
|
||||||
-} from '#/components/icons/Message'
|
+ <RNText style={styles.text}>
|
||||||
-import {SettingsGear2_Stroke2_Corner0_Rounded as Settings} from '#/components/icons/SettingsGear2'
|
+ The above copyright notice and this permission notice shall be included in all
|
||||||
+import { MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled } from '#/components/icons/MagnifyingGlass'
|
+ copies or substantial portions of the Software.
|
||||||
+import { MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass } from '#/components/icons/MagnifyingGlass2'
|
+ </RNText>
|
||||||
+import { SettingsGear2_Stroke2_Corner0_Rounded as Settings } from '#/components/icons/SettingsGear2'
|
+
|
||||||
import {
|
+ <RNText style={styles.text}>
|
||||||
UserCircle_Filled_Corner0_Rounded as UserCircleFilled,
|
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
UserCircle_Stroke2_Corner0_Rounded as UserCircle,
|
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
} from '#/components/icons/UserCircle'
|
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
-import {InlineLinkText} from '#/components/Link'
|
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
-import {Text} from '#/components/Typography'
|
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
-import {useSimpleVerificationState} from '#/components/verification'
|
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
|
+ SOFTWARE.
|
||||||
+import { InlineLinkText } from '#/components/Link'
|
+ </RNText>
|
||||||
+import { Text } from '#/components/Typography'
|
+
|
||||||
+import { useSimpleVerificationState } from '#/components/verification'
|
+ <RNText style={styles.sectionTitle2}>日本語訳(参考)</RNText>
|
||||||
+import { VerificationCheck } from '#/components/verification/VerificationCheck'
|
+
|
||||||
|
+ <RNText style={styles.text}>
|
||||||
const iconWidth = 26
|
+ 本ソフトウェアおよび関連文書ファイル(以下「ソフトウェア」)のコピーを取得する
|
||||||
|
+ すべての人に対し、ソフトウェアを無制限に扱うことを無償で許可します。これには、
|
||||||
@@ -65,11 +55,11 @@ let DrawerProfileCard = ({
|
+ ソフトウェアのコピーを使用、複製、変更、結合、公開、配布、サブライセンス、
|
||||||
account: SessionAccount
|
+ および/または販売する権利、ならびにソフトウェアを提供する相手にそうした行為を
|
||||||
onPressProfile: () => void
|
+ 許可する権利が含まれますが、これらに限定されません。
|
||||||
}): React.ReactNode => {
|
+ </RNText>
|
||||||
- const {_, i18n} = useLingui()
|
+
|
||||||
+ const { _, i18n } = useLingui()
|
+ <RNText style={styles.text}>
|
||||||
const t = useTheme()
|
+ 上記の著作権表示および本許諾表示を、ソフトウェアのすべてのコピーまたは
|
||||||
- const {data: profile} = useProfileQuery({did: account.did})
|
+ 重要な部分に記載するものとします。
|
||||||
- const verification = useSimpleVerificationState({profile})
|
+ </RNText>
|
||||||
- const {isActive: live} = useActorStatus(profile)
|
+
|
||||||
+ const { data: profile } = useProfileQuery({ did: account.did })
|
+ <RNText style={styles.text}>
|
||||||
+ const verification = useSimpleVerificationState({ profile })
|
+ ソフトウェアは「現状のまま」で提供され、明示黙示を問わず、商品性、特定目的への
|
||||||
+ const { isActive: live } = useActorStatus(profile)
|
+ 適合性、および権利非侵害についての保証を含む、いかなる種類の保証もなされません。
|
||||||
|
+ いかなる場合においても、作者または著作権者は、契約行為、不法行為、またはそれ以外で
|
||||||
return (
|
+ あろうと、ソフトウェアに起因または関連し、あるいはソフトウェアの使用または
|
||||||
<TouchableOpacity
|
+ その他の扱いによって生じる一切の請求、損害、その他の義務について責任を負わないものとします。
|
||||||
@@ -81,7 +71,6 @@ let DrawerProfileCard = ({
|
+ </RNText>
|
||||||
<UserAvatar
|
+
|
||||||
size={52}
|
+ <RNText style={styles.footer}>
|
||||||
avatar={profile?.avatar}
|
+ Original License: https://github.com/bluesky-social/social-app/blob/main/LICENSE
|
||||||
- // See https://github.com/bluesky-social/social-app/pull/1801:
|
+ </RNText>
|
||||||
usePlainRNImage={true}
|
+ </ScrollView>
|
||||||
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
+ </Layout.Content>
|
||||||
live={live}
|
+ </Layout.Screen>
|
||||||
@@ -140,9 +129,9 @@ let DrawerProfileCard = ({
|
|
||||||
)
|
|
||||||
}
|
|
||||||
DrawerProfileCard = React.memo(DrawerProfileCard)
|
|
||||||
-export {DrawerProfileCard}
|
|
||||||
+export { DrawerProfileCard }
|
|
||||||
|
|
||||||
-let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
+let DrawerContent = ({ }: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
const t = useTheme()
|
|
||||||
const insets = useSafeAreaInsets()
|
|
||||||
const setDrawerOpen = useSetDrawerOpen()
|
|
||||||
@@ -150,27 +139,20 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
const {
|
|
||||||
isAtHome,
|
|
||||||
isAtSearch,
|
|
||||||
- isAtFeeds,
|
|
||||||
- isAtBookmarks,
|
|
||||||
isAtNotifications,
|
|
||||||
isAtMyProfile,
|
|
||||||
- isAtMessages,
|
|
||||||
} = useNavigationTabState()
|
|
||||||
- const {hasSession, currentAccount} = useSession()
|
|
||||||
-
|
|
||||||
- // events
|
|
||||||
- // =
|
|
||||||
+ const { hasSession, currentAccount } = useSession()
|
|
||||||
|
|
||||||
const onPressTab = React.useCallback(
|
|
||||||
(tab: 'Home' | 'Search' | 'Messages' | 'Notifications' | 'MyProfile') => {
|
|
||||||
const state = navigation.getState()
|
|
||||||
setDrawerOpen(false)
|
|
||||||
if (isWeb) {
|
|
||||||
- // hack because we have flat navigator for web and MyProfile does not exist on the web navigator -ansh
|
|
||||||
if (tab === 'MyProfile') {
|
|
||||||
- navigation.navigate('Profile', {name: currentAccount!.handle})
|
|
||||||
+ navigation.navigate('Profile', { name: currentAccount!.handle })
|
|
||||||
} else {
|
|
||||||
- // @ts-expect-error struggles with string unions, apparently
|
|
||||||
+ // @ts-expect-error struggles with string unions
|
|
||||||
navigation.navigate(tab)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
@@ -178,21 +160,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
if (tabState === TabState.InsideAtRoot) {
|
|
||||||
emitSoftReset()
|
|
||||||
} else if (tabState === TabState.Inside) {
|
|
||||||
- // find the correct navigator in which to pop-to-top
|
|
||||||
- const target = state.routes.find(route => route.name === `${tab}Tab`)
|
|
||||||
- ?.state?.key
|
|
||||||
+ const target = state.routes.find(route => route.name === `${tab}Tab`)?.state?.key
|
|
||||||
if (target) {
|
|
||||||
- // if we found it, trigger pop-to-top
|
|
||||||
- navigation.dispatch({
|
|
||||||
- ...StackActions.popToTop(),
|
|
||||||
- target,
|
|
||||||
- })
|
|
||||||
+ navigation.dispatch({ ...StackActions.popToTop(), target })
|
|
||||||
} else {
|
|
||||||
- // fallback: reset navigation
|
|
||||||
- navigation.reset({
|
|
||||||
- index: 0,
|
|
||||||
- routes: [{name: `${tab}Tab`}],
|
|
||||||
- })
|
|
||||||
+ navigation.reset({ index: 0, routes: [{ name: `${tab}Tab` }] })
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
navigation.navigate(`${tab}Tab`)
|
|
||||||
@@ -203,76 +175,21 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
)
|
|
||||||
|
|
||||||
const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab])
|
|
||||||
-
|
|
||||||
- const onPressSearch = React.useCallback(
|
|
||||||
- () => onPressTab('Search'),
|
|
||||||
- [onPressTab],
|
|
||||||
- )
|
|
||||||
-
|
|
||||||
- const onPressMessages = React.useCallback(
|
|
||||||
- () => onPressTab('Messages'),
|
|
||||||
- [onPressTab],
|
|
||||||
- )
|
|
||||||
-
|
|
||||||
- const onPressNotifications = React.useCallback(
|
|
||||||
- () => onPressTab('Notifications'),
|
|
||||||
- [onPressTab],
|
|
||||||
- )
|
|
||||||
-
|
|
||||||
- const onPressProfile = React.useCallback(() => {
|
|
||||||
- onPressTab('MyProfile')
|
|
||||||
- }, [onPressTab])
|
|
||||||
-
|
|
||||||
- const onPressMyFeeds = React.useCallback(() => {
|
|
||||||
- navigation.navigate('Feeds')
|
|
||||||
- setDrawerOpen(false)
|
|
||||||
- }, [navigation, setDrawerOpen])
|
|
||||||
-
|
|
||||||
- const onPressLists = React.useCallback(() => {
|
|
||||||
- navigation.navigate('Lists')
|
|
||||||
- setDrawerOpen(false)
|
|
||||||
- }, [navigation, setDrawerOpen])
|
|
||||||
-
|
|
||||||
- const onPressBookmarks = React.useCallback(() => {
|
|
||||||
- navigation.navigate('Bookmarks')
|
|
||||||
- setDrawerOpen(false)
|
|
||||||
- }, [navigation, setDrawerOpen])
|
|
||||||
-
|
|
||||||
+ const onPressSearch = React.useCallback(() => onPressTab('Search'), [onPressTab])
|
|
||||||
+ const onPressNotifications = React.useCallback(() => onPressTab('Notifications'), [onPressTab])
|
|
||||||
+ const onPressProfile = React.useCallback(() => { onPressTab('MyProfile') }, [onPressTab])
|
|
||||||
const onPressSettings = React.useCallback(() => {
|
|
||||||
navigation.navigate('Settings')
|
|
||||||
setDrawerOpen(false)
|
|
||||||
}, [navigation, setDrawerOpen])
|
|
||||||
|
|
||||||
- const onPressFeedback = React.useCallback(() => {
|
|
||||||
- Linking.openURL(
|
|
||||||
- FEEDBACK_FORM_URL({
|
|
||||||
- email: currentAccount?.email,
|
|
||||||
- handle: currentAccount?.handle,
|
|
||||||
- }),
|
|
||||||
- )
|
|
||||||
- }, [currentAccount])
|
|
||||||
-
|
|
||||||
- const onPressHelp = React.useCallback(() => {
|
|
||||||
- Linking.openURL(HELP_DESK_URL)
|
|
||||||
- }, [])
|
|
||||||
-
|
|
||||||
- // rendering
|
|
||||||
- // =
|
|
||||||
-
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
testID="drawer"
|
|
||||||
style={[a.flex_1, a.border_r, t.atoms.bg, t.atoms.border_contrast_low]}>
|
|
||||||
<ScrollView
|
|
||||||
style={[a.flex_1]}
|
|
||||||
- contentContainerStyle={[
|
|
||||||
- {
|
|
||||||
- paddingTop: Math.max(
|
|
||||||
- insets.top + a.pt_xl.paddingTop,
|
|
||||||
- a.pt_xl.paddingTop,
|
|
||||||
- ),
|
|
||||||
- },
|
|
||||||
- ]}>
|
|
||||||
+ contentContainerStyle={[{ paddingTop: Math.max(insets.top + a.pt_xl.paddingTop, a.pt_xl.paddingTop) }]}>
|
|
||||||
<View style={[a.px_xl]}>
|
|
||||||
{hasSession && currentAccount ? (
|
|
||||||
<DrawerProfileCard
|
|
||||||
@@ -284,7 +201,6 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
<NavSignupCard />
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
-
|
|
||||||
<Divider style={[a.mt_xl, a.mb_sm]} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
@@ -292,17 +208,10 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
<>
|
|
||||||
<SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} />
|
|
||||||
<HomeMenuItem isActive={isAtHome} onPress={onPressHome} />
|
|
||||||
- <ChatMenuItem isActive={isAtMessages} onPress={onPressMessages} />
|
|
||||||
<NotificationsMenuItem
|
|
||||||
isActive={isAtNotifications}
|
|
||||||
onPress={onPressNotifications}
|
|
||||||
/>
|
|
||||||
- <FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
|
|
||||||
- <ListsMenuItem onPress={onPressLists} />
|
|
||||||
- <BookmarksMenuItem
|
|
||||||
- isActive={isAtBookmarks}
|
|
||||||
- onPress={onPressBookmarks}
|
|
||||||
- />
|
|
||||||
<ProfileMenuItem
|
|
||||||
isActive={isAtMyProfile}
|
|
||||||
onPress={onPressProfile}
|
|
||||||
@@ -312,7 +221,6 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<HomeMenuItem isActive={isAtHome} onPress={onPressHome} />
|
|
||||||
- <FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
|
|
||||||
<SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
@@ -322,69 +230,11 @@ let DrawerContent = ({}: React.PropsWithoutRef<{}>): React.ReactNode => {
|
|
||||||
<ExtraLinks />
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
-
|
|
||||||
- <DrawerFooter
|
|
||||||
- onPressFeedback={onPressFeedback}
|
|
||||||
- onPressHelp={onPressHelp}
|
|
||||||
- />
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
DrawerContent = React.memo(DrawerContent)
|
|
||||||
-export {DrawerContent}
|
|
||||||
-
|
|
||||||
-let DrawerFooter = ({
|
|
||||||
- onPressFeedback,
|
|
||||||
- onPressHelp,
|
|
||||||
-}: {
|
|
||||||
- onPressFeedback: () => void
|
|
||||||
- onPressHelp: () => void
|
|
||||||
-}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
- const insets = useSafeAreaInsets()
|
|
||||||
- return (
|
|
||||||
- <View
|
|
||||||
- style={[
|
|
||||||
- a.flex_row,
|
|
||||||
- a.gap_sm,
|
|
||||||
- a.flex_wrap,
|
|
||||||
- a.pl_xl,
|
|
||||||
- a.pt_md,
|
|
||||||
- {
|
|
||||||
- paddingBottom: Math.max(
|
|
||||||
- insets.bottom + tokens.space.xs,
|
|
||||||
- tokens.space.xl,
|
|
||||||
- ),
|
|
||||||
- },
|
|
||||||
- ]}>
|
|
||||||
- <Button
|
|
||||||
- label={_(msg`Send feedback`)}
|
|
||||||
- size="small"
|
|
||||||
- variant="solid"
|
|
||||||
- color="secondary"
|
|
||||||
- onPress={onPressFeedback}>
|
|
||||||
- <ButtonIcon icon={Message} position="left" />
|
|
||||||
- <ButtonText>
|
|
||||||
- <Trans>Feedback</Trans>
|
|
||||||
- </ButtonText>
|
|
||||||
- </Button>
|
|
||||||
- <Button
|
|
||||||
- label={_(msg`Get help`)}
|
|
||||||
- size="small"
|
|
||||||
- variant="outline"
|
|
||||||
- color="secondary"
|
|
||||||
- onPress={onPressHelp}
|
|
||||||
- style={{
|
|
||||||
- backgroundColor: 'transparent',
|
|
||||||
- }}>
|
|
||||||
- <ButtonText>
|
|
||||||
- <Trans>Help</Trans>
|
|
||||||
- </ButtonText>
|
|
||||||
- </Button>
|
|
||||||
- </View>
|
|
||||||
- )
|
|
||||||
-}
|
|
||||||
-DrawerFooter = React.memo(DrawerFooter)
|
|
||||||
+export { DrawerContent }
|
|
||||||
|
|
||||||
interface MenuItemProps extends ComponentProps<typeof PressableScale> {
|
|
||||||
icon: JSX.Element
|
|
||||||
@@ -400,7 +250,7 @@ let SearchMenuItem = ({
|
|
||||||
isActive: boolean
|
|
||||||
onPress: () => void
|
|
||||||
}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
@@ -426,7 +276,7 @@ let HomeMenuItem = ({
|
|
||||||
isActive: boolean
|
|
||||||
onPress: () => void
|
|
||||||
}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
@@ -445,32 +295,6 @@ let HomeMenuItem = ({
|
|
||||||
}
|
|
||||||
HomeMenuItem = React.memo(HomeMenuItem)
|
|
||||||
|
|
||||||
-let ChatMenuItem = ({
|
|
||||||
- isActive,
|
|
||||||
- onPress,
|
|
||||||
-}: {
|
|
||||||
- isActive: boolean
|
|
||||||
- onPress: () => void
|
|
||||||
-}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
- const t = useTheme()
|
|
||||||
- return (
|
|
||||||
- <MenuItem
|
|
||||||
- icon={
|
|
||||||
- isActive ? (
|
|
||||||
- <MessageFilled style={[t.atoms.text]} width={iconWidth} />
|
|
||||||
- ) : (
|
|
||||||
- <Message style={[t.atoms.text]} width={iconWidth} />
|
|
||||||
- )
|
|
||||||
- }
|
|
||||||
- label={_(msg`Chat`)}
|
|
||||||
- bold={isActive}
|
|
||||||
- onPress={onPress}
|
|
||||||
- />
|
|
||||||
- )
|
|
||||||
-}
|
|
||||||
-ChatMenuItem = React.memo(ChatMenuItem)
|
|
||||||
-
|
|
||||||
let NotificationsMenuItem = ({
|
|
||||||
isActive,
|
|
||||||
onPress,
|
|
||||||
@@ -478,7 +302,7 @@ let NotificationsMenuItem = ({
|
|
||||||
isActive: boolean
|
|
||||||
onPress: () => void
|
|
||||||
}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
const numUnreadNotifications = useUnreadNotifications()
|
|
||||||
return (
|
|
||||||
@@ -495,11 +319,11 @@ let NotificationsMenuItem = ({
|
|
||||||
numUnreadNotifications === ''
|
|
||||||
? ''
|
|
||||||
: _(
|
|
||||||
- msg`${plural(numUnreadNotifications ?? 0, {
|
|
||||||
- one: '# unread item',
|
|
||||||
- other: '# unread items',
|
|
||||||
- })}` || '',
|
|
||||||
- )
|
|
||||||
+ msg`${plural(numUnreadNotifications ?? 0, {
|
|
||||||
+ one: '# unread item',
|
|
||||||
+ other: '# unread items',
|
|
||||||
+ })}` || '',
|
|
||||||
+ )
|
+ )
|
||||||
}
|
+}
|
||||||
count={numUnreadNotifications}
|
+
|
||||||
bold={isActive}
|
+const styles = StyleSheet.create({
|
||||||
@@ -509,72 +333,6 @@ let NotificationsMenuItem = ({
|
+ title: {
|
||||||
}
|
+ fontSize: 24,
|
||||||
NotificationsMenuItem = React.memo(NotificationsMenuItem)
|
+ fontWeight: 'bold',
|
||||||
|
+ marginBottom: 16,
|
||||||
-let FeedsMenuItem = ({
|
+ },
|
||||||
- isActive,
|
+ text: {
|
||||||
- onPress,
|
+ fontSize: 14,
|
||||||
-}: {
|
+ marginBottom: 12,
|
||||||
- isActive: boolean
|
+ lineHeight: 20,
|
||||||
- onPress: () => void
|
+ },
|
||||||
-}): React.ReactNode => {
|
+ link: {
|
||||||
- const {_} = useLingui()
|
+ fontSize: 14,
|
||||||
- const t = useTheme()
|
+ marginBottom: 12,
|
||||||
- return (
|
+ color: '#0066cc',
|
||||||
- <MenuItem
|
+ },
|
||||||
- icon={
|
+ sectionTitle: {
|
||||||
- isActive ? (
|
+ fontSize: 18,
|
||||||
- <HashtagFilled width={iconWidth} style={[t.atoms.text]} />
|
+ fontWeight: 'bold',
|
||||||
- ) : (
|
+ marginTop: 16,
|
||||||
- <Hashtag width={iconWidth} style={[t.atoms.text]} />
|
+ marginBottom: 12,
|
||||||
- )
|
+ },
|
||||||
- }
|
+ sectionTitle2: {
|
||||||
- label={_(msg`Feeds`)}
|
+ fontSize: 18,
|
||||||
- bold={isActive}
|
+ fontWeight: 'bold',
|
||||||
- onPress={onPress}
|
+ marginTop: 24,
|
||||||
- />
|
+ marginBottom: 12,
|
||||||
- )
|
+ },
|
||||||
-}
|
+ mono: {
|
||||||
-FeedsMenuItem = React.memo(FeedsMenuItem)
|
+ fontSize: 14,
|
||||||
-
|
+ marginBottom: 12,
|
||||||
-let ListsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
|
+ fontFamily: 'monospace',
|
||||||
- const {_} = useLingui()
|
+ },
|
||||||
- const t = useTheme()
|
+ footer: {
|
||||||
-
|
+ fontSize: 12,
|
||||||
- return (
|
+ marginTop: 24,
|
||||||
- <MenuItem
|
+ color: '#666666',
|
||||||
- icon={<List style={[t.atoms.text]} width={iconWidth} />}
|
+ },
|
||||||
- label={_(msg`Lists`)}
|
+})
|
||||||
- onPress={onPress}
|
|
||||||
- />
|
|
||||||
- )
|
|
||||||
-}
|
|
||||||
-ListsMenuItem = React.memo(ListsMenuItem)
|
|
||||||
-
|
|
||||||
-let BookmarksMenuItem = ({
|
|
||||||
- isActive,
|
|
||||||
- onPress,
|
|
||||||
-}: {
|
|
||||||
- isActive: boolean
|
|
||||||
- onPress: () => void
|
|
||||||
-}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
- const t = useTheme()
|
|
||||||
-
|
|
||||||
- return (
|
|
||||||
- <MenuItem
|
|
||||||
- icon={
|
|
||||||
- isActive ? (
|
|
||||||
- <BookmarkFilled style={[t.atoms.text]} width={iconWidth} />
|
|
||||||
- ) : (
|
|
||||||
- <Bookmark style={[t.atoms.text]} width={iconWidth} />
|
|
||||||
- )
|
|
||||||
- }
|
|
||||||
- label={_(msg({message: 'Saved', context: 'link to bookmarks screen'}))}
|
|
||||||
- onPress={onPress}
|
|
||||||
- />
|
|
||||||
- )
|
|
||||||
-}
|
|
||||||
-BookmarksMenuItem = React.memo(BookmarksMenuItem)
|
|
||||||
-
|
|
||||||
let ProfileMenuItem = ({
|
|
||||||
isActive,
|
|
||||||
onPress,
|
|
||||||
@@ -582,7 +340,7 @@ let ProfileMenuItem = ({
|
|
||||||
isActive: boolean
|
|
||||||
onPress: () => void
|
|
||||||
}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
@@ -600,8 +358,8 @@ let ProfileMenuItem = ({
|
|
||||||
}
|
|
||||||
ProfileMenuItem = React.memo(ProfileMenuItem)
|
|
||||||
|
|
||||||
-let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+let SettingsMenuItem = ({ onPress }: { onPress: () => void }): React.ReactNode => {
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
@@ -613,7 +371,7 @@ let SettingsMenuItem = ({onPress}: {onPress: () => void}): React.ReactNode => {
|
|
||||||
}
|
|
||||||
SettingsMenuItem = React.memo(SettingsMenuItem)
|
|
||||||
|
|
||||||
-function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
|
|
||||||
+function MenuItem({ icon, label, count, bold, onPress }: MenuItemProps) {
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
@@ -621,7 +379,7 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
|
|
||||||
onPress={onPress}
|
|
||||||
accessibilityRole="tab"
|
|
||||||
label={label}>
|
|
||||||
- {({hovered, pressed}) => (
|
|
||||||
+ {({ hovered, pressed }) => (
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
a.flex_1,
|
|
||||||
@@ -640,7 +398,7 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
|
|
||||||
a.absolute,
|
|
||||||
a.inset_0,
|
|
||||||
a.align_end,
|
|
||||||
- {top: -4, right: a.gap_sm.gap * -1},
|
|
||||||
+ { top: -4, right: a.gap_sm.gap * -1 },
|
|
||||||
]}>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
@@ -686,37 +444,28 @@ function MenuItem({icon, label, count, bold, onPress}: MenuItemProps) {
|
|
||||||
}
|
|
||||||
|
|
||||||
function ExtraLinks() {
|
|
||||||
- const {_} = useLingui()
|
|
||||||
+ const { _ } = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
const kawaii = useKawaiiMode()
|
|
||||||
+ const navigation = useNavigation<NavigationProp>()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={[a.flex_col, a.gap_md, a.flex_wrap]}>
|
|
||||||
- <InlineLinkText
|
|
||||||
- style={[a.text_md]}
|
|
||||||
- label={_(msg`Terms of Service`)}
|
|
||||||
- to="https://bsky.social/about/support/tos">
|
|
||||||
- <Trans>Terms of Service</Trans>
|
|
||||||
- </InlineLinkText>
|
|
||||||
- <InlineLinkText
|
|
||||||
- style={[a.text_md]}
|
|
||||||
- to="https://bsky.social/about/support/privacy-policy"
|
|
||||||
- label={_(msg`Privacy Policy`)}>
|
|
||||||
- <Trans>Privacy Policy</Trans>
|
|
||||||
- </InlineLinkText>
|
|
||||||
- {kawaii && (
|
|
||||||
- <Text style={t.atoms.text_contrast_medium}>
|
|
||||||
- <Trans>
|
|
||||||
- Logo by{' '}
|
|
||||||
- <InlineLinkText
|
|
||||||
- style={[a.text_md]}
|
|
||||||
- to="/profile/sawaratsuki.bsky.social"
|
|
||||||
- label="@sawaratsuki.bsky.social">
|
|
||||||
- @sawaratsuki.bsky.social
|
|
||||||
- </InlineLinkText>
|
|
||||||
- </Trans>
|
|
||||||
+ <TouchableOpacity onPress={() => navigation.navigate('TermsOfService')}>
|
|
||||||
+ <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
|
||||||
+ <Trans>Terms of Service</Trans>
|
|
||||||
</Text>
|
|
||||||
- )}
|
|
||||||
+ </TouchableOpacity>
|
|
||||||
+ <TouchableOpacity onPress={() => navigation.navigate('PrivacyPolicy')}>
|
|
||||||
+ <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
|
||||||
+ <Trans>Privacy Policy</Trans>
|
|
||||||
+ </Text>
|
|
||||||
+ </TouchableOpacity>
|
|
||||||
+ <TouchableOpacity onPress={() => navigation.navigate('License')}>
|
|
||||||
+ <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
|
||||||
+ <Trans>License</Trans>
|
|
||||||
+ </Text>
|
|
||||||
+ </TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,24 @@
|
|||||||
|
diff --git a/src/routes.ts b/src/routes.ts
|
||||||
|
--- a/src/routes.ts
|
||||||
|
+++ b/src/routes.ts
|
||||||
|
@@ -74,6 +74,7 @@ export const router = new Router<AllNavigatableRoutes>({
|
||||||
|
PrivacyPolicy: 'https://syu.is/about/support/privacy-policy',
|
||||||
|
TermsOfService: 'https://syu.is/about/support/tos',
|
||||||
|
CommunityGuidelines: '/support/community-guidelines',
|
||||||
|
+ License: 'https://syu.is/about/support/license',
|
||||||
|
CopyrightPolicy: '/support/copyright',
|
||||||
|
// hashtags
|
||||||
|
Hashtag: '/hashtag/:tag',
|
||||||
diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx
|
diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx
|
||||||
index 3442d1bdf..2b059af52 100644
|
|
||||||
--- a/src/view/com/auth/SplashScreen.tsx
|
--- a/src/view/com/auth/SplashScreen.tsx
|
||||||
+++ b/src/view/com/auth/SplashScreen.tsx
|
+++ b/src/view/com/auth/SplashScreen.tsx
|
||||||
@@ -40,16 +40,6 @@ export const SplashScreen = ({
|
@@ -1,4 +1,5 @@
|
||||||
|
import {View} from 'react-native'
|
||||||
|
+import {Pressable, Linking} from 'react-native'
|
||||||
|
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
|
||||||
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
|
import {msg, Trans} from '@lingui/macro'
|
||||||
|
@@ -40,16 +41,6 @@ export const SplashScreen = ({
|
||||||
<View style={[a.pb_sm, a.pt_5xl]}>
|
<View style={[a.pb_sm, a.pt_5xl]}>
|
||||||
<Logotype width={161} fill={t.atoms.text.color} />
|
<Logotype width={161} fill={t.atoms.text.color} />
|
||||||
</View>
|
</View>
|
||||||
@@ -19,52 +35,35 @@ index 3442d1bdf..2b059af52 100644
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
@@ -94,7 +84,7 @@ export const SplashScreen = ({
|
@@ -102,6 +93,21 @@ export const SplashScreen = ({
|
||||||
style={[
|
|
||||||
a.px_lg,
|
|
||||||
a.pt_md,
|
|
||||||
- a.pb_2xl,
|
|
||||||
+ a.pb_md,
|
|
||||||
a.justify_center,
|
|
||||||
a.align_center,
|
|
||||||
]}>
|
|
||||||
@@ -102,6 +92,17 @@ export const SplashScreen = ({
|
|
||||||
<AppLanguageDropdown />
|
<AppLanguageDropdown />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
+ <View
|
+ <View style={[a.pb_sm, a.justify_center, a.align_center]}>
|
||||||
|
+ <Pressable onPress={() => Linking.openURL('https://syu.is/about/support/license')}>
|
||||||
|
+ <Text
|
||||||
+ style={[
|
+ style={[
|
||||||
+ a.px_lg,
|
+ a.text_xs,
|
||||||
+ a.pb_xl,
|
+ t.atoms.text_contrast_low,
|
||||||
+ a.justify_center,
|
+ {textDecorationLine: 'underline'},
|
||||||
+ a.align_center,
|
|
||||||
+ ]}>
|
+ ]}>
|
||||||
+ <Text style={[a.text_xs, t.atoms.text_contrast_low]}>
|
+ License
|
||||||
+ © syui
|
|
||||||
+ </Text>
|
+ </Text>
|
||||||
|
+ </Pressable>
|
||||||
|
+ </View>
|
||||||
|
+ <View style={[a.pb_xl, a.justify_center, a.align_center]}>
|
||||||
|
+ <Text style={[a.text_xs, t.atoms.text_contrast_low]}>© syui</Text>
|
||||||
+ </View>
|
+ </View>
|
||||||
<View style={{height: insets.bottom}} />
|
<View style={{height: insets.bottom}} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx
|
diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx
|
||||||
index 22dd23d7f..08cd8aa33 100644
|
|
||||||
--- a/src/view/com/auth/SplashScreen.web.tsx
|
--- a/src/view/com/auth/SplashScreen.web.tsx
|
||||||
+++ b/src/view/com/auth/SplashScreen.web.tsx
|
+++ b/src/view/com/auth/SplashScreen.web.tsx
|
||||||
@@ -3,7 +3,9 @@ import {Pressable, View} from 'react-native'
|
@@ -94,14 +94,6 @@ export const SplashScreen = ({
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
|
||||||
import {msg, Trans} from '@lingui/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
+import {useNavigation} from '@react-navigation/native'
|
|
||||||
|
|
||||||
+import {NavigationProp} from '#/lib/routes/types'
|
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
|
||||||
import {useKawaiiMode} from '#/state/preferences/kawaii'
|
|
||||||
import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
|
|
||||||
@@ -93,15 +95,6 @@ export const SplashScreen = ({
|
|
||||||
<Logotype width={161} fill={t.atoms.text.color} />
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
-
|
|
||||||
- <Text
|
- <Text
|
||||||
- style={[
|
- style={[
|
||||||
- a.text_md,
|
- a.text_md,
|
||||||
@@ -76,42 +75,3 @@ index 22dd23d7f..08cd8aa33 100644
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
@@ -150,7 +143,6 @@ export const SplashScreen = ({
|
|
||||||
|
|
||||||
function Footer() {
|
|
||||||
const t = useTheme()
|
|
||||||
- const {_} = useLingui()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
@@ -168,26 +160,12 @@ function Footer() {
|
|
||||||
a.flex_1,
|
|
||||||
t.atoms.border_contrast_medium,
|
|
||||||
]}>
|
|
||||||
- <InlineLinkText
|
|
||||||
- label={_(msg`Learn more about Bluesky`)}
|
|
||||||
- to="https://bsky.social">
|
|
||||||
- <Trans>Business</Trans>
|
|
||||||
- </InlineLinkText>
|
|
||||||
- <InlineLinkText
|
|
||||||
- label={_(msg`Read the Bluesky blog`)}
|
|
||||||
- to="https://bsky.social/about/blog">
|
|
||||||
- <Trans>Blog</Trans>
|
|
||||||
- </InlineLinkText>
|
|
||||||
- <InlineLinkText
|
|
||||||
- label={_(msg`See jobs at Bluesky`)}
|
|
||||||
- to="https://bsky.social/about/join">
|
|
||||||
- <Trans comment="Link to a page with job openings at Bluesky">
|
|
||||||
- Jobs
|
|
||||||
- </Trans>
|
|
||||||
- </InlineLinkText>
|
|
||||||
-
|
|
||||||
<View style={a.flex_1} />
|
|
||||||
|
|
||||||
+ <Text style={[a.text_xs, t.atoms.text_contrast_low]}>
|
|
||||||
+ © syui
|
|
||||||
+ </Text>
|
|
||||||
+
|
|
||||||
<AppLanguageDropdown />
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
|
|
||||||
index 6b8257b91..bed851753 100644
|
|
||||||
--- a/src/screens/Settings/AboutSettings.tsx
|
|
||||||
+++ b/src/screens/Settings/AboutSettings.tsx
|
|
||||||
@@ -80,7 +80,7 @@ export function AboutSettingsScreen({}: Props) {
|
|
||||||
<Layout.Content>
|
|
||||||
<SettingsList.Container>
|
|
||||||
<SettingsList.LinkItem
|
|
||||||
- to="https://bsky.social/about/support/tos"
|
|
||||||
+ to="/about/support/tos"
|
|
||||||
label={_(msg`Terms of Service`)}>
|
|
||||||
<SettingsList.ItemIcon icon={NewspaperIcon} />
|
|
||||||
<SettingsList.ItemText>
|
|
||||||
@@ -88,7 +88,7 @@ export function AboutSettingsScreen({}: Props) {
|
|
||||||
</SettingsList.ItemText>
|
|
||||||
</SettingsList.LinkItem>
|
|
||||||
<SettingsList.LinkItem
|
|
||||||
- to="https://bsky.social/about/support/privacy-policy"
|
|
||||||
+ to="/about/support/privacy-policy"
|
|
||||||
label={_(msg`Privacy Policy`)}>
|
|
||||||
<SettingsList.ItemIcon icon={NewspaperIcon} />
|
|
||||||
<SettingsList.ItemText>
|
|
||||||
@@ -96,7 +96,7 @@ export function AboutSettingsScreen({}: Props) {
|
|
||||||
</SettingsList.ItemText>
|
|
||||||
</SettingsList.LinkItem>
|
|
||||||
<SettingsList.LinkItem
|
|
||||||
- to={STATUS_PAGE_URL}
|
|
||||||
+ to="https://syu.is/"
|
|
||||||
label={_(msg`Status Page`)}>
|
|
||||||
<SettingsList.ItemIcon icon={GlobeIcon} />
|
|
||||||
<SettingsList.ItemText>
|
|
||||||
@@ -1,8 +1,35 @@
|
|||||||
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
|
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
|
||||||
index 1703036b5..42baa462c 100644
|
index 6b0e184c0..42b609c9e 100644
|
||||||
--- a/src/screens/Settings/Settings.tsx
|
--- a/src/screens/Settings/Settings.tsx
|
||||||
+++ b/src/screens/Settings/Settings.tsx
|
+++ b/src/screens/Settings/Settings.tsx
|
||||||
@@ -231,16 +231,6 @@ export function SettingsScreen({}: Props) {
|
@@ -203,24 +203,8 @@ export function SettingsScreen({}: Props) {
|
||||||
|
<Trans>Notifications</Trans>
|
||||||
|
</SettingsList.ItemText>
|
||||||
|
</SettingsList.LinkItem>
|
||||||
|
- <SettingsList.LinkItem
|
||||||
|
- to="/settings/content-and-media"
|
||||||
|
- label={_(msg`Content and media`)}>
|
||||||
|
- <SettingsList.ItemIcon icon={WindowIcon} />
|
||||||
|
- <SettingsList.ItemText>
|
||||||
|
- <Trans>Content and media</Trans>
|
||||||
|
- </SettingsList.ItemText>
|
||||||
|
- </SettingsList.LinkItem>
|
||||||
|
- {isNative && findContactsEnabled && (
|
||||||
|
- <SettingsList.LinkItem
|
||||||
|
- to="/settings/find-contacts"
|
||||||
|
- label={_(msg`Find friends from contacts`)}>
|
||||||
|
- <SettingsList.ItemIcon icon={ContactsIcon} />
|
||||||
|
- <SettingsList.ItemText>
|
||||||
|
- <Trans>Find friends from contacts</Trans>
|
||||||
|
- </SettingsList.ItemText>
|
||||||
|
- </SettingsList.LinkItem>
|
||||||
|
- )}
|
||||||
|
+{/* Content and media removed for syu.is */}
|
||||||
|
+{/* Find friends from contacts removed for syu.is */}
|
||||||
|
<SettingsList.LinkItem
|
||||||
|
to="/settings/appearance"
|
||||||
|
label={_(msg`Appearance`)}>
|
||||||
|
@@ -245,16 +229,6 @@ export function SettingsScreen({}: Props) {
|
||||||
<Trans>Languages</Trans>
|
<Trans>Languages</Trans>
|
||||||
</SettingsList.ItemText>
|
</SettingsList.ItemText>
|
||||||
</SettingsList.LinkItem>
|
</SettingsList.LinkItem>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
diff --git a/plugins/withCodeSignEntitlements.js b/plugins/withCodeSignEntitlements.js
|
diff --git a/plugins/withCodeSignEntitlements.js b/plugins/withCodeSignEntitlements.js
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 000000000..e69de29bb
|
index 000000000..b03b6bd68
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/plugins/withCodeSignEntitlements.js
|
+++ b/plugins/withCodeSignEntitlements.js
|
||||||
@@ -0,0 +1,22 @@
|
@@ -0,0 +1,25 @@
|
||||||
+/* eslint-disable @typescript-eslint/no-var-requires */
|
+/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
+const { withXcodeProject } = require('@expo/config-plugins')
|
+const { withXcodeProject } = require('@expo/config-plugins')
|
||||||
+
|
+
|
||||||
@@ -16,6 +16,7 @@ index 000000000..e69de29bb
|
|||||||
+ const configuration = configurations[key]
|
+ const configuration = configurations[key]
|
||||||
+ if (
|
+ if (
|
||||||
+ configuration.buildSettings &&
|
+ configuration.buildSettings &&
|
||||||
|
+ configuration.comment &&
|
||||||
+ !configuration.comment.includes('TEST')
|
+ !configuration.comment.includes('TEST')
|
||||||
+ ) {
|
+ ) {
|
||||||
+ configuration.buildSettings.CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION =
|
+ configuration.buildSettings.CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION =
|
||||||
|
|||||||
30
ios/patching/020-social-app-ios-bypass-age-assurance.patch
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
diff --git a/src/ageAssurance/index.tsx b/src/ageAssurance/index.tsx
|
||||||
|
index 9a0a9c9d5..5a6563e52 100644
|
||||||
|
--- a/src/ageAssurance/index.tsx
|
||||||
|
+++ b/src/ageAssurance/index.tsx
|
||||||
|
@@ -88,19 +88,16 @@ function InnerProvider({children}: {children: React.ReactNode}) {
|
||||||
|
return (
|
||||||
|
<AgeAssuranceStateContext.Provider
|
||||||
|
value={useMemo(() => {
|
||||||
|
- const chatDisabled = state.access !== AgeAssuranceAccess.Full
|
||||||
|
- const isUnderage = data?.birthdate
|
||||||
|
- ? isUserUnderAdultAge(data.birthdate)
|
||||||
|
- : true
|
||||||
|
- const adultContentDisabled =
|
||||||
|
- state.access !== AgeAssuranceAccess.Full || isUnderage
|
||||||
|
return {
|
||||||
|
Access: AgeAssuranceAccess,
|
||||||
|
Status: AgeAssuranceStatus,
|
||||||
|
- state,
|
||||||
|
+ state: {
|
||||||
|
+ ...state,
|
||||||
|
+ access: AgeAssuranceAccess.Full,
|
||||||
|
+ },
|
||||||
|
flags: {
|
||||||
|
- adultContentDisabled,
|
||||||
|
- chatDisabled,
|
||||||
|
+ adultContentDisabled: false,
|
||||||
|
+ chatDisabled: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, [state, data])}>
|
||||||
247
ios/patching/021-social-app-ios-clean-feed.patch
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
diff --git a/src/view/com/posts/DiscoverFallbackHeader.tsx b/src/view/com/posts/DiscoverFallbackHeader.tsx
|
||||||
|
index e35a33aaf..a36f84ae0 100644
|
||||||
|
--- a/src/view/com/posts/DiscoverFallbackHeader.tsx
|
||||||
|
+++ b/src/view/com/posts/DiscoverFallbackHeader.tsx
|
||||||
|
@@ -7,37 +7,5 @@ import {TextLink} from '../util/Link'
|
||||||
|
import {Text} from '../util/text/Text'
|
||||||
|
|
||||||
|
export function DiscoverFallbackHeader() {
|
||||||
|
- const pal = usePalette('default')
|
||||||
|
- return (
|
||||||
|
- <View
|
||||||
|
- style={[
|
||||||
|
- {
|
||||||
|
- flexDirection: 'row',
|
||||||
|
- alignItems: 'center',
|
||||||
|
- paddingVertical: 12,
|
||||||
|
- paddingHorizontal: 12,
|
||||||
|
- borderTopWidth: 1,
|
||||||
|
- },
|
||||||
|
- pal.border,
|
||||||
|
- pal.viewLight,
|
||||||
|
- ]}>
|
||||||
|
- <View style={{width: 68, paddingLeft: 12}}>
|
||||||
|
- <InfoCircleIcon size={36} style={pal.textLight} strokeWidth={1.5} />
|
||||||
|
- </View>
|
||||||
|
- <View style={{flex: 1}}>
|
||||||
|
- <Text type="md" style={pal.text}>
|
||||||
|
- <Trans>
|
||||||
|
- We ran out of posts from your follows. Here's the latest from{' '}
|
||||||
|
- <TextLink
|
||||||
|
- type="md-medium"
|
||||||
|
- href="/profile/bsky.app/feed/whats-hot"
|
||||||
|
- text="Discover"
|
||||||
|
- style={pal.link}
|
||||||
|
- />
|
||||||
|
- .
|
||||||
|
- </Trans>
|
||||||
|
- </Text>
|
||||||
|
- </View>
|
||||||
|
- </View>
|
||||||
|
- )
|
||||||
|
+ return null
|
||||||
|
}
|
||||||
|
diff --git a/src/view/com/posts/FollowingEmptyState.tsx b/src/view/com/posts/FollowingEmptyState.tsx
|
||||||
|
index 352cc1dc0..f477521af 100644
|
||||||
|
--- a/src/view/com/posts/FollowingEmptyState.tsx
|
||||||
|
+++ b/src/view/com/posts/FollowingEmptyState.tsx
|
||||||
|
@@ -1,37 +1,14 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {StyleSheet, View} from 'react-native'
|
||||||
|
-import {
|
||||||
|
- FontAwesomeIcon,
|
||||||
|
- type FontAwesomeIconStyle,
|
||||||
|
-} from '@fortawesome/react-native-fontawesome'
|
||||||
|
import {Trans} from '@lingui/macro'
|
||||||
|
-import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
|
import {MagnifyingGlassIcon} from '#/lib/icons'
|
||||||
|
-import {type NavigationProp} from '#/lib/routes/types'
|
||||||
|
import {s} from '#/lib/styles'
|
||||||
|
-import {isWeb} from '#/platform/detection'
|
||||||
|
-import {Button} from '../util/forms/Button'
|
||||||
|
import {Text} from '../util/text/Text'
|
||||||
|
|
||||||
|
export function FollowingEmptyState() {
|
||||||
|
const pal = usePalette('default')
|
||||||
|
- const palInverted = usePalette('inverted')
|
||||||
|
- const navigation = useNavigation<NavigationProp>()
|
||||||
|
-
|
||||||
|
- const onPressFindAccounts = React.useCallback(() => {
|
||||||
|
- if (isWeb) {
|
||||||
|
- navigation.navigate('Search', {})
|
||||||
|
- } else {
|
||||||
|
- navigation.navigate('SearchTab')
|
||||||
|
- navigation.popToTop()
|
||||||
|
- }
|
||||||
|
- }, [navigation])
|
||||||
|
-
|
||||||
|
- const onPressDiscoverFeeds = React.useCallback(() => {
|
||||||
|
- navigation.navigate('Feeds')
|
||||||
|
- }, [navigation])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
@@ -45,36 +22,6 @@ export function FollowingEmptyState() {
|
||||||
|
happening.
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
- <Button
|
||||||
|
- type="inverted"
|
||||||
|
- style={styles.emptyBtn}
|
||||||
|
- onPress={onPressFindAccounts}>
|
||||||
|
- <Text type="lg-medium" style={palInverted.text}>
|
||||||
|
- <Trans>Find accounts to follow</Trans>
|
||||||
|
- </Text>
|
||||||
|
- <FontAwesomeIcon
|
||||||
|
- icon="angle-right"
|
||||||
|
- style={palInverted.text as FontAwesomeIconStyle}
|
||||||
|
- size={14}
|
||||||
|
- />
|
||||||
|
- </Button>
|
||||||
|
-
|
||||||
|
- <Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}>
|
||||||
|
- <Trans>You can also discover new Custom Feeds to follow.</Trans>
|
||||||
|
- </Text>
|
||||||
|
- <Button
|
||||||
|
- type="inverted"
|
||||||
|
- style={[styles.emptyBtn, s.mt10]}
|
||||||
|
- onPress={onPressDiscoverFeeds}>
|
||||||
|
- <Text type="lg-medium" style={palInverted.text}>
|
||||||
|
- <Trans>Discover new custom feeds</Trans>
|
||||||
|
- </Text>
|
||||||
|
- <FontAwesomeIcon
|
||||||
|
- icon="angle-right"
|
||||||
|
- style={palInverted.text as FontAwesomeIconStyle}
|
||||||
|
- size={14}
|
||||||
|
- />
|
||||||
|
- </Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
@@ -98,13 +45,4 @@ const styles = StyleSheet.create({
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
},
|
||||||
|
- emptyBtn: {
|
||||||
|
- marginVertical: 20,
|
||||||
|
- flexDirection: 'row',
|
||||||
|
- alignItems: 'center',
|
||||||
|
- justifyContent: 'space-between',
|
||||||
|
- paddingVertical: 18,
|
||||||
|
- paddingHorizontal: 24,
|
||||||
|
- borderRadius: 30,
|
||||||
|
- },
|
||||||
|
})
|
||||||
|
diff --git a/src/view/com/posts/FollowingEndOfFeed.tsx b/src/view/com/posts/FollowingEndOfFeed.tsx
|
||||||
|
index e3c84d782..efb55d406 100644
|
||||||
|
--- a/src/view/com/posts/FollowingEndOfFeed.tsx
|
||||||
|
+++ b/src/view/com/posts/FollowingEndOfFeed.tsx
|
||||||
|
@@ -1,36 +1,13 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {Dimensions, StyleSheet, View} from 'react-native'
|
||||||
|
-import {
|
||||||
|
- FontAwesomeIcon,
|
||||||
|
- type FontAwesomeIconStyle,
|
||||||
|
-} from '@fortawesome/react-native-fontawesome'
|
||||||
|
import {Trans} from '@lingui/macro'
|
||||||
|
-import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
|
-import {type NavigationProp} from '#/lib/routes/types'
|
||||||
|
import {s} from '#/lib/styles'
|
||||||
|
-import {isWeb} from '#/platform/detection'
|
||||||
|
-import {Button} from '../util/forms/Button'
|
||||||
|
import {Text} from '../util/text/Text'
|
||||||
|
|
||||||
|
export function FollowingEndOfFeed() {
|
||||||
|
const pal = usePalette('default')
|
||||||
|
- const palInverted = usePalette('inverted')
|
||||||
|
- const navigation = useNavigation<NavigationProp>()
|
||||||
|
-
|
||||||
|
- const onPressFindAccounts = React.useCallback(() => {
|
||||||
|
- if (isWeb) {
|
||||||
|
- navigation.navigate('Search', {})
|
||||||
|
- } else {
|
||||||
|
- navigation.navigate('SearchTab')
|
||||||
|
- navigation.popToTop()
|
||||||
|
- }
|
||||||
|
- }, [navigation])
|
||||||
|
-
|
||||||
|
- const onPressDiscoverFeeds = React.useCallback(() => {
|
||||||
|
- navigation.navigate('Feeds')
|
||||||
|
- }, [navigation])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
@@ -41,41 +18,8 @@ export function FollowingEndOfFeed() {
|
||||||
|
]}>
|
||||||
|
<View style={styles.inner}>
|
||||||
|
<Text type="xl-medium" style={[s.textCenter, pal.text]}>
|
||||||
|
- <Trans>
|
||||||
|
- You've reached the end of your feed! Find some more accounts to
|
||||||
|
- follow.
|
||||||
|
- </Trans>
|
||||||
|
- </Text>
|
||||||
|
- <Button
|
||||||
|
- type="inverted"
|
||||||
|
- style={styles.emptyBtn}
|
||||||
|
- onPress={onPressFindAccounts}>
|
||||||
|
- <Text type="lg-medium" style={palInverted.text}>
|
||||||
|
- <Trans>Find accounts to follow</Trans>
|
||||||
|
- </Text>
|
||||||
|
- <FontAwesomeIcon
|
||||||
|
- icon="angle-right"
|
||||||
|
- style={palInverted.text as FontAwesomeIconStyle}
|
||||||
|
- size={14}
|
||||||
|
- />
|
||||||
|
- </Button>
|
||||||
|
-
|
||||||
|
- <Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}>
|
||||||
|
- <Trans>You can also discover new Custom Feeds to follow.</Trans>
|
||||||
|
+ <Trans>You've reached the end of your feed!</Trans>
|
||||||
|
</Text>
|
||||||
|
- <Button
|
||||||
|
- type="inverted"
|
||||||
|
- style={[styles.emptyBtn, s.mt10]}
|
||||||
|
- onPress={onPressDiscoverFeeds}>
|
||||||
|
- <Text type="lg-medium" style={palInverted.text}>
|
||||||
|
- <Trans>Discover new custom feeds</Trans>
|
||||||
|
- </Text>
|
||||||
|
- <FontAwesomeIcon
|
||||||
|
- icon="angle-right"
|
||||||
|
- style={palInverted.text as FontAwesomeIconStyle}
|
||||||
|
- size={14}
|
||||||
|
- />
|
||||||
|
- </Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
@@ -93,13 +37,4 @@ const styles = StyleSheet.create({
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 460,
|
||||||
|
},
|
||||||
|
- emptyBtn: {
|
||||||
|
- marginVertical: 20,
|
||||||
|
- flexDirection: 'row',
|
||||||
|
- alignItems: 'center',
|
||||||
|
- justifyContent: 'space-between',
|
||||||
|
- paddingVertical: 18,
|
||||||
|
- paddingHorizontal: 24,
|
||||||
|
- borderRadius: 30,
|
||||||
|
- },
|
||||||
|
})
|
||||||
|
diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx
|
||||||
|
index 4f25468c9..a72a10b80 100644
|
||||||
|
--- a/src/view/com/posts/PostFeed.tsx
|
||||||
|
+++ b/src/view/com/posts/PostFeed.tsx
|
||||||
|
@@ -766,7 +766,7 @@ let PostFeed = ({
|
||||||
|
} else if (row.type === 'feedShutdownMsg') {
|
||||||
|
return <FeedShutdownMsg feedUri={feedUriOrActorDid} />
|
||||||
|
} else if (row.type === 'interstitialFollows') {
|
||||||
|
- return <SuggestedFollows feed={feed} />
|
||||||
|
+ return null
|
||||||
|
} else if (row.type === 'interstitialProgressGuide') {
|
||||||
|
return <ProgressGuide />
|
||||||
|
} else if (row.type === 'ageAssuranceBanner') {
|
||||||
51
ios/patching/022-social-app-ios-bskyweb-support-pages.patch
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go
|
||||||
|
index 790f211ee..ec05a8bcd 100644
|
||||||
|
--- a/bskyweb/cmd/bskyweb/server.go
|
||||||
|
+++ b/bskyweb/cmd/bskyweb/server.go
|
||||||
|
@@ -317,6 +317,12 @@ 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)
|
||||||
|
+ // about/support pages (syu.is specific)
|
||||||
|
+ e.GET("/about/support/tos", server.WebAboutTOS)
|
||||||
|
+ e.GET("/about/support/privacy-policy", server.WebAboutPrivacy)
|
||||||
|
+ e.GET("/about/support/help", server.WebAboutHelp)
|
||||||
|
+ e.GET("/about/support/license", server.WebAboutLicense)
|
||||||
|
+ e.GET("/about/support/app", server.WebAboutApp)
|
||||||
|
e.GET("/intent/compose", server.WebGeneric)
|
||||||
|
e.GET("/intent/verify-email", server.WebGeneric)
|
||||||
|
e.GET("/intent/age-assurance", server.WebGeneric)
|
||||||
|
@@ -825,3 +831,33 @@ func (srv *Server) serveSitemapRequest(c echo.Context, url, sitemapType string)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+// Handler for About TOS page (syu.is specific)
|
||||||
|
+func (srv *Server) WebAboutTOS(c echo.Context) error {
|
||||||
|
+ data := srv.NewTemplateContext()
|
||||||
|
+ return c.Render(http.StatusOK, "about-tos.html", data)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Handler for About Privacy Policy page (syu.is specific)
|
||||||
|
+func (srv *Server) WebAboutPrivacy(c echo.Context) error {
|
||||||
|
+ data := srv.NewTemplateContext()
|
||||||
|
+ return c.Render(http.StatusOK, "about-privacy.html", data)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Handler for About Help page (syu.is specific)
|
||||||
|
+func (srv *Server) WebAboutHelp(c echo.Context) error {
|
||||||
|
+ data := srv.NewTemplateContext()
|
||||||
|
+ return c.Render(http.StatusOK, "about-help.html", data)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Handler for About License page (syu.is specific)
|
||||||
|
+func (srv *Server) WebAboutLicense(c echo.Context) error {
|
||||||
|
+ data := srv.NewTemplateContext()
|
||||||
|
+ return c.Render(http.StatusOK, "about-license.html", data)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Handler for About App page (syu.is specific)
|
||||||
|
+func (srv *Server) WebAboutApp(c echo.Context) error {
|
||||||
|
+ data := srv.NewTemplateContext()
|
||||||
|
+ return c.Render(http.StatusOK, "about-app.html", data)
|
||||||
|
+}
|
||||||
70
ios/patching/023-social-app-ios-disable-dm.patch
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
diff --git a/src/state/messages/events/index.tsx b/src/state/messages/events/index.tsx
|
||||||
|
index 2ff0784ae..dc314ecc5 100644
|
||||||
|
--- a/src/state/messages/events/index.tsx
|
||||||
|
+++ b/src/state/messages/events/index.tsx
|
||||||
|
@@ -10,13 +10,7 @@ const MessagesEventBusContext = React.createContext<MessagesEventBus | null>(
|
||||||
|
MessagesEventBusContext.displayName = 'MessagesEventBusContext'
|
||||||
|
|
||||||
|
export function useMessagesEventBus() {
|
||||||
|
- const ctx = React.useContext(MessagesEventBusContext)
|
||||||
|
- if (!ctx) {
|
||||||
|
- throw new Error(
|
||||||
|
- 'useMessagesEventBus must be used within a MessagesEventBusProvider',
|
||||||
|
- )
|
||||||
|
- }
|
||||||
|
- return ctx
|
||||||
|
+ return React.useContext(MessagesEventBusContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MessagesEventBusProvider({
|
||||||
|
@@ -24,18 +18,11 @@ export function MessagesEventBusProvider({
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
- const {currentAccount} = useSession()
|
||||||
|
-
|
||||||
|
- if (!currentAccount) {
|
||||||
|
- return (
|
||||||
|
- <MessagesEventBusContext.Provider value={null}>
|
||||||
|
- {children}
|
||||||
|
- </MessagesEventBusContext.Provider>
|
||||||
|
- )
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
+ // DM functionality is disabled for syu.is
|
||||||
|
return (
|
||||||
|
- <MessagesEventBusProviderInner>{children}</MessagesEventBusProviderInner>
|
||||||
|
+ <MessagesEventBusContext.Provider value={null}>
|
||||||
|
+ {children}
|
||||||
|
+ </MessagesEventBusContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/src/state/queries/messages/list-conversations.tsx b/src/state/queries/messages/list-conversations.tsx
|
||||||
|
index c5457d1cb..5bc37bdce 100644
|
||||||
|
--- a/src/state/queries/messages/list-conversations.tsx
|
||||||
|
+++ b/src/state/queries/messages/list-conversations.tsx
|
||||||
|
@@ -74,17 +74,12 @@ export function useListConvos() {
|
||||||
|
|
||||||
|
const empty = {accepted: [], request: []}
|
||||||
|
export function ListConvosProvider({children}: {children: React.ReactNode}) {
|
||||||
|
- const {hasSession} = useSession()
|
||||||
|
-
|
||||||
|
- if (!hasSession) {
|
||||||
|
- return (
|
||||||
|
- <ListConvosContext.Provider value={empty}>
|
||||||
|
- {children}
|
||||||
|
- </ListConvosContext.Provider>
|
||||||
|
- )
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return <ListConvosProviderInner>{children}</ListConvosProviderInner>
|
||||||
|
+ // DM functionality is disabled for syu.is - always return empty
|
||||||
|
+ return (
|
||||||
|
+ <ListConvosContext.Provider value={empty}>
|
||||||
|
+ {children}
|
||||||
|
+ </ListConvosContext.Provider>
|
||||||
|
+ )
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ListConvosProviderInner({
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
diff --git a/src/env/common.ts b/src/env/common.ts
|
||||||
|
--- a/src/env/common.ts
|
||||||
|
+++ b/src/env/common.ts
|
||||||
|
@@ -107,9 +107,8 @@ export const GCP_PROJECT_ID: number =
|
||||||
|
/**
|
||||||
|
* URLs for the app config web worker. Can be a
|
||||||
|
* locally running server, see `env.example` for more.
|
||||||
|
+ * Disabled for self-hosted environment to avoid CORS errors
|
||||||
|
*/
|
||||||
|
export const BAPP_CONFIG_DEV_URL = process.env.BAPP_CONFIG_DEV_URL
|
||||||
|
export const BAPP_CONFIG_PROD_URL = `https://ip.bsky.app`
|
||||||
|
-export const BAPP_CONFIG_URL = IS_DEV
|
||||||
|
- ? (BAPP_CONFIG_DEV_URL ?? BAPP_CONFIG_PROD_URL)
|
||||||
|
- : BAPP_CONFIG_PROD_URL
|
||||||
|
+export const BAPP_CONFIG_URL = null
|
||||||
91
ios/patching/025-social-app-ios-bskyweb-title.patch
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html
|
||||||
|
--- a/bskyweb/templates/base.html
|
||||||
|
+++ b/bskyweb/templates/base.html
|
||||||
|
@@ -7,9 +7,9 @@
|
||||||
|
<!--
|
||||||
|
Preconnect to essential domains
|
||||||
|
-->
|
||||||
|
- <link rel="preconnect" href="https://bsky.social">
|
||||||
|
- <link rel="preconnect" href="https://go.bsky.app">
|
||||||
|
- <title>{%- block head_title -%}Bluesky{%- endblock -%}</title>
|
||||||
|
+ <link rel="preconnect" href="https://syu.is">
|
||||||
|
+ <link rel="preconnect" href="https://bsky.syu.is">
|
||||||
|
+ <title>{%- block head_title -%}syu.is{%- endblock -%}</title>
|
||||||
|
|
||||||
|
<!-- Hello Humans! API docs at https://atproto.com -->
|
||||||
|
|
||||||
|
@@ -121,7 +121,7 @@
|
||||||
|
<noscript>
|
||||||
|
<h1 lang="en">JavaScript Required</h1>
|
||||||
|
<p lang="en">This is a heavily interactive web application, and JavaScript is required. Simple HTML interfaces are possible, but that is not what this is.
|
||||||
|
- <p lang="en">Learn more about Bluesky at <a href="https://bsky.social">bsky.social</a> and <a href="https://atproto.com">atproto.com</a>.
|
||||||
|
+ <p lang="en">Learn more at <a href="https://syu.is">syu.is</a> and <a href="https://atproto.com">atproto.com</a>.
|
||||||
|
{% block noscript_extra %}{% endblock %}
|
||||||
|
</noscript>
|
||||||
|
{% endblock -%}
|
||||||
|
diff --git a/bskyweb/templates/home.html b/bskyweb/templates/home.html
|
||||||
|
--- a/bskyweb/templates/home.html
|
||||||
|
+++ b/bskyweb/templates/home.html
|
||||||
|
@@ -1,17 +1,17 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
-{% block head_title %}Bluesky{% endblock %}
|
||||||
|
+{% block head_title %}syu.is{% endblock %}
|
||||||
|
|
||||||
|
{% block html_head_extra -%}
|
||||||
|
- <meta property="og:title" content="Bluesky" />
|
||||||
|
- <meta name="twitter:title" content="Bluesky" />
|
||||||
|
+ <meta property="og:title" content="syu.is" />
|
||||||
|
+ <meta name="twitter:title" content="syu.is" />
|
||||||
|
|
||||||
|
<meta name="description" content="Social media as it should be. Find your community among millions of users, unleash your creativity, and have some fun again." />
|
||||||
|
<meta name="og:description" content="Social media as it should be. Find your community among millions of users, unleash your creativity, and have some fun again." />
|
||||||
|
<meta name="twitter:description" content="Social media as it should be. Find your community among millions of users, unleash your creativity, and have some fun again." />
|
||||||
|
|
||||||
|
- <meta property="og:url" content="https://bsky.app" />
|
||||||
|
- <meta name="twitter:url" content="https://bsky.app" />
|
||||||
|
- <link rel="canonical" href="https://bsky.app" />
|
||||||
|
+ <meta property="og:url" content="https://syu.is" />
|
||||||
|
+ <meta name="twitter:url" content="https://syu.is" />
|
||||||
|
+ <link rel="canonical" href="https://syu.is" />
|
||||||
|
|
||||||
|
<meta property="og:image" content="https://bsky.app/static/social-card-default-gradient.png" />
|
||||||
|
diff --git a/bskyweb/templates/error.html b/bskyweb/templates/error.html
|
||||||
|
--- a/bskyweb/templates/error.html
|
||||||
|
+++ b/bskyweb/templates/error.html
|
||||||
|
@@ -1,6 +1,6 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
-{% block head_title %}Error {{ statusCode }} - Bluesky{% endblock %}
|
||||||
|
+{% block head_title %}Error {{ statusCode }} - syu.is{% endblock %}
|
||||||
|
|
||||||
|
{% block noscript_extra %}
|
||||||
|
{%- if statusCode == 404 %}
|
||||||
|
diff --git a/bskyweb/templates/starterpack.html b/bskyweb/templates/starterpack.html
|
||||||
|
--- a/bskyweb/templates/starterpack.html
|
||||||
|
+++ b/bskyweb/templates/starterpack.html
|
||||||
|
@@ -17,8 +17,8 @@
|
||||||
|
<meta property="og:title" content="{{ title }}" />
|
||||||
|
<meta name="twitter:title" content="{{ title }}" />
|
||||||
|
{%- else -%}
|
||||||
|
- <meta property="og:title" content="Bluesky" />
|
||||||
|
- <meta name="twitter:title" content="Bluesky" />
|
||||||
|
+ <meta property="og:title" content="syu.is" />
|
||||||
|
+ <meta name="twitter:title" content="syu.is" />
|
||||||
|
{% endif -%}
|
||||||
|
<meta name="description" content="Join the conversation" />
|
||||||
|
<meta name="og:description" content="Join the conversation" />
|
||||||
|
diff --git a/web/index.html b/web/index.html
|
||||||
|
--- a/web/index.html
|
||||||
|
+++ b/web/index.html
|
||||||
|
@@ -14,8 +14,8 @@
|
||||||
|
<!--
|
||||||
|
Preconnect to essential domains
|
||||||
|
-->
|
||||||
|
- <link rel="preconnect" href="https://bsky.social">
|
||||||
|
- <link rel="preconnect" href="https://go.bsky.app">
|
||||||
|
+ <link rel="preconnect" href="https://syu.is">
|
||||||
|
+ <link rel="preconnect" href="https://bsky.syu.is">
|
||||||
|
<title>%WEB_TITLE%</title>
|
||||||
|
|
||||||
|
<link rel="preload" as="font" type="font/woff2" href="/static/media/InterVariable.c504db5c06caaf7cdfba.woff2" crossorigin>
|
||||||
101
ios/patching/027-social-app-ios-remove-birthdate.patch
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
diff --git a/src/screens/Signup/StepInfo/index.tsx b/src/screens/Signup/StepInfo/index.tsx
|
||||||
|
--- a/src/screens/Signup/StepInfo/index.tsx
|
||||||
|
+++ b/src/screens/Signup/StepInfo/index.tsx
|
||||||
|
@@ -7,11 +7,9 @@
|
||||||
|
|
||||||
|
import {isEmailMaybeInvalid} from '#/lib/strings/email'
|
||||||
|
import {logger} from '#/logger'
|
||||||
|
-import {is13, is18, useSignupContext} from '#/screens/Signup/state'
|
||||||
|
+import {useSignupContext} from '#/screens/Signup/state'
|
||||||
|
import {Policies} from '#/screens/Signup/StepInfo/Policies'
|
||||||
|
import {atoms as a, native} from '#/alf'
|
||||||
|
-import * as DateField from '#/components/forms/DateField'
|
||||||
|
-import {type DateFieldRef} from '#/components/forms/DateField/types'
|
||||||
|
import {FormError} from '#/components/forms/FormError'
|
||||||
|
import {HostingProvider} from '#/components/forms/HostingProvider'
|
||||||
|
import * as TextField from '#/components/forms/TextField'
|
||||||
|
@@ -22,16 +20,6 @@
|
||||||
|
import {usePreemptivelyCompleteActivePolicyUpdate} from '#/components/PolicyUpdateOverlay/usePreemptivelyCompleteActivePolicyUpdate'
|
||||||
|
import {BackNextButtons} from '../BackNextButtons'
|
||||||
|
|
||||||
|
-function sanitizeDate(date: Date): Date {
|
||||||
|
- if (!date || date.toString() === 'Invalid Date') {
|
||||||
|
- logger.error(`Create account: handled invalid date for birthDate`, {
|
||||||
|
- hasDate: !!date,
|
||||||
|
- })
|
||||||
|
- return new Date()
|
||||||
|
- }
|
||||||
|
- return date
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
export function StepInfo({
|
||||||
|
onPressBack,
|
||||||
|
isServerError,
|
||||||
|
@@ -55,7 +43,6 @@
|
||||||
|
|
||||||
|
const emailInputRef = useRef<TextInput>(null)
|
||||||
|
const passwordInputRef = useRef<TextInput>(null)
|
||||||
|
- const birthdateInputRef = useRef<DateFieldRef>(null)
|
||||||
|
|
||||||
|
const [hasWarnedEmail, setHasWarnedEmail] = React.useState<boolean>(false)
|
||||||
|
|
||||||
|
@@ -76,10 +63,6 @@
|
||||||
|
const emailChanged = prevEmailValueRef.current !== email
|
||||||
|
const password = passwordValueRef.current
|
||||||
|
|
||||||
|
- if (!is13(state.dateOfBirth)) {
|
||||||
|
- return
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
if (state.serviceDescription?.inviteCodeRequired && !inviteCode) {
|
||||||
|
return dispatch({
|
||||||
|
type: 'setError',
|
||||||
|
@@ -246,44 +229,21 @@
|
||||||
|
secureTextEntry
|
||||||
|
autoComplete="new-password"
|
||||||
|
autoCapitalize="none"
|
||||||
|
- returnKeyType="next"
|
||||||
|
- submitBehavior={native('blurAndSubmit')}
|
||||||
|
- onSubmitEditing={native(() =>
|
||||||
|
- birthdateInputRef.current?.focus(),
|
||||||
|
- )}
|
||||||
|
+ returnKeyType="done"
|
||||||
|
passwordRules="minlength: 8;"
|
||||||
|
/>
|
||||||
|
</TextField.Root>
|
||||||
|
</View>
|
||||||
|
- <View>
|
||||||
|
- <DateField.LabelText>
|
||||||
|
- <Trans>Your birth date</Trans>
|
||||||
|
- </DateField.LabelText>
|
||||||
|
- <DateField.DateField
|
||||||
|
- testID="date"
|
||||||
|
- inputRef={birthdateInputRef}
|
||||||
|
- value={state.dateOfBirth}
|
||||||
|
- onChangeDate={date => {
|
||||||
|
- dispatch({
|
||||||
|
- type: 'setDateOfBirth',
|
||||||
|
- value: sanitizeDate(new Date(date)),
|
||||||
|
- })
|
||||||
|
- }}
|
||||||
|
- label={_(msg`Date of birth`)}
|
||||||
|
- accessibilityHint={_(msg`Select your date of birth`)}
|
||||||
|
- maximumDate={new Date()}
|
||||||
|
- />
|
||||||
|
- </View>
|
||||||
|
<Policies
|
||||||
|
serviceDescription={state.serviceDescription}
|
||||||
|
- needsGuardian={!is18(state.dateOfBirth)}
|
||||||
|
- under13={!is13(state.dateOfBirth)}
|
||||||
|
+ needsGuardian={false}
|
||||||
|
+ under13={false}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : undefined}
|
||||||
|
</View>
|
||||||
|
<BackNextButtons
|
||||||
|
- hideNext={!is13(state.dateOfBirth)}
|
||||||
|
+ hideNext={false}
|
||||||
|
showRetry={isServerError}
|
||||||
|
isLoading={state.isLoading}
|
||||||
|
onBackPress={onPressBack}
|
||||||
16
ios/patching/028-social-app-ios-remove-discover-feeds.patch
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
diff --git a/src/screens/Search/Explore.tsx b/src/screens/Search/Explore.tsx
|
||||||
|
--- a/src/screens/Search/Explore.tsx
|
||||||
|
+++ b/src/screens/Search/Explore.tsx
|
||||||
|
@@ -687,12 +687,7 @@ export function Explore({
|
||||||
|
|
||||||
|
if (useFullExperience) {
|
||||||
|
i.push(trendingTopicsModule)
|
||||||
|
- i.push(...suggestedFeedsModule)
|
||||||
|
- i.push(...suggestedFollowsModule)
|
||||||
|
- i.push(...suggestedStarterPacksModule)
|
||||||
|
i.push(...feedPreviewsModule)
|
||||||
|
- } else {
|
||||||
|
- i.push(...suggestedFollowsModule)
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
84
ios/patching/029-social-app-ios-remove-feeds-discover.patch
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx
|
||||||
|
--- a/src/view/screens/Feeds.tsx
|
||||||
|
+++ b/src/view/screens/Feeds.tsx
|
||||||
|
@@ -288,80 +288,7 @@ export function FeedsScreen(_props: Props) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (!hasSession || (hasSession && canShowDiscoverSection)) {
|
||||||
|
- slices.push({
|
||||||
|
- key: 'popularFeedsHeader',
|
||||||
|
- type: 'popularFeedsHeader',
|
||||||
|
- })
|
||||||
|
|
||||||
|
- if (popularFeedsError || searchError) {
|
||||||
|
- slices.push({
|
||||||
|
- key: 'popularFeedsError',
|
||||||
|
- type: 'error',
|
||||||
|
- error: cleanError(
|
||||||
|
- popularFeedsError?.toString() ?? searchError?.toString() ?? '',
|
||||||
|
- ),
|
||||||
|
- })
|
||||||
|
- } else {
|
||||||
|
- if (isUserSearching) {
|
||||||
|
- if (isSearchPending || !searchResults) {
|
||||||
|
- slices.push({
|
||||||
|
- key: 'popularFeedsLoading',
|
||||||
|
- type: 'popularFeedsLoading',
|
||||||
|
- })
|
||||||
|
- } else {
|
||||||
|
- if (!searchResults || searchResults?.length === 0) {
|
||||||
|
- slices.push({
|
||||||
|
- key: 'popularFeedsNoResults',
|
||||||
|
- type: 'popularFeedsNoResults',
|
||||||
|
- })
|
||||||
|
- } else {
|
||||||
|
- slices = slices.concat(
|
||||||
|
- searchResults.map(feed => ({
|
||||||
|
- key: `popularFeed:${feed.uri}`,
|
||||||
|
- type: 'popularFeed',
|
||||||
|
- feedUri: feed.uri,
|
||||||
|
- feed,
|
||||||
|
- })),
|
||||||
|
- )
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- } else {
|
||||||
|
- if (isPopularFeedsFetching && !popularFeeds?.pages) {
|
||||||
|
- slices.push({
|
||||||
|
- key: 'popularFeedsLoading',
|
||||||
|
- type: 'popularFeedsLoading',
|
||||||
|
- })
|
||||||
|
- } else {
|
||||||
|
- if (!popularFeeds?.pages) {
|
||||||
|
- slices.push({
|
||||||
|
- key: 'popularFeedsNoResults',
|
||||||
|
- type: 'popularFeedsNoResults',
|
||||||
|
- })
|
||||||
|
- } else {
|
||||||
|
- for (const page of popularFeeds.pages || []) {
|
||||||
|
- slices = slices.concat(
|
||||||
|
- page.feeds.map(feed => ({
|
||||||
|
- key: `popularFeed:${feed.uri}`,
|
||||||
|
- type: 'popularFeed',
|
||||||
|
- feedUri: feed.uri,
|
||||||
|
- feed,
|
||||||
|
- })),
|
||||||
|
- )
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (isPopularFeedsFetchingNextPage) {
|
||||||
|
- slices.push({
|
||||||
|
- key: 'popularFeedsLoadingMore',
|
||||||
|
- type: 'popularFeedsLoadingMore',
|
||||||
|
- })
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
return slices
|
||||||
|
}, [
|
||||||
|
hasSession,
|
||||||
361
ios/patching/030-social-app-ios-appinfo.patch
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
|
||||||
|
--- a/src/Navigation.tsx
|
||||||
|
+++ b/src/Navigation.tsx
|
||||||
|
@@ -63,6 +63,7 @@ import {NotificationsScreen} from '#/view/screens/Notifications'
|
||||||
|
import {PostThreadScreen} from '#/view/screens/PostThread'
|
||||||
|
import {PrivacyPolicyScreen} from '#/view/screens/PrivacyPolicy'
|
||||||
|
import {LicenseScreen} from '#/view/screens/License'
|
||||||
|
+import {AppInfoScreen} from '#/view/screens/AppInfo'
|
||||||
|
import {ProfileScreen} from '#/view/screens/Profile'
|
||||||
|
import {ProfileFeedLikedByScreen} from '#/view/screens/ProfileFeedLikedBy'
|
||||||
|
import {Storybook} from '#/view/screens/Storybook'
|
||||||
|
@@ -341,6 +342,11 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) {
|
||||||
|
getComponent={() => LicenseScreen}
|
||||||
|
options={{title: title(msg`License`)}}
|
||||||
|
/>
|
||||||
|
+ <Stack.Screen
|
||||||
|
+ name="AppInfo"
|
||||||
|
+ getComponent={() => AppInfoScreen}
|
||||||
|
+ options={{title: title(msg`App Info`)}}
|
||||||
|
+ />
|
||||||
|
<Stack.Screen
|
||||||
|
name="CommunityGuidelines"
|
||||||
|
getComponent={() => CommunityGuidelinesScreen}
|
||||||
|
diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts
|
||||||
|
--- a/src/lib/routes/types.ts
|
||||||
|
+++ b/src/lib/routes/types.ts
|
||||||
|
@@ -40,6 +40,7 @@ export type CommonNavigatorParams = {
|
||||||
|
PrivacyPolicy: undefined
|
||||||
|
TermsOfService: undefined
|
||||||
|
License: undefined
|
||||||
|
+ AppInfo: undefined
|
||||||
|
CommunityGuidelines: undefined
|
||||||
|
CopyrightPolicy: undefined
|
||||||
|
LanguageSettings: undefined
|
||||||
|
diff --git a/src/routes.ts b/src/routes.ts
|
||||||
|
--- a/src/routes.ts
|
||||||
|
+++ b/src/routes.ts
|
||||||
|
@@ -75,6 +75,7 @@ export const router = new Router<AllNavigatableRoutes>({
|
||||||
|
TermsOfService: 'https://syu.is/about/support/tos',
|
||||||
|
CommunityGuidelines: '/support/community-guidelines',
|
||||||
|
License: 'https://syu.is/about/support/license',
|
||||||
|
+ AppInfo: 'https://syu.is/about/support/app',
|
||||||
|
CopyrightPolicy: '/support/copyright',
|
||||||
|
// hashtags
|
||||||
|
Hashtag: '/hashtag/:tag',
|
||||||
|
diff --git a/src/view/screens/AppInfo.tsx b/src/view/screens/AppInfo.tsx
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000..000000001
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/view/screens/AppInfo.tsx
|
||||||
|
@@ -0,0 +1,310 @@
|
||||||
|
+import React, {useState} from 'react'
|
||||||
|
+import {
|
||||||
|
+ View,
|
||||||
|
+ ScrollView,
|
||||||
|
+ StyleSheet,
|
||||||
|
+ Pressable,
|
||||||
|
+ Image,
|
||||||
|
+} from 'react-native'
|
||||||
|
+import * as Clipboard from 'expo-clipboard'
|
||||||
|
+import * as WebBrowser from 'expo-web-browser'
|
||||||
|
+import {Trans} from '@lingui/macro'
|
||||||
|
+
|
||||||
|
+import * as Layout from '#/components/Layout'
|
||||||
|
+import {Text} from '#/components/Typography'
|
||||||
|
+import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
||||||
|
+import {atoms as a, useTheme} from '#/alf'
|
||||||
|
+
|
||||||
|
+const APP_VERSION = '1.111.0'
|
||||||
|
+const APP_NAME = 'Aiat'
|
||||||
|
+const BITCOIN_ADDRESS = '3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr'
|
||||||
|
+
|
||||||
|
+export function AppInfoScreen() {
|
||||||
|
+ useSetTitle('App Info')
|
||||||
|
+ const t = useTheme()
|
||||||
|
+ const [copied, setCopied] = useState(false)
|
||||||
|
+
|
||||||
|
+ const copyToClipboard = async (text: string) => {
|
||||||
|
+ try {
|
||||||
|
+ await Clipboard.setStringAsync(text)
|
||||||
|
+ setCopied(true)
|
||||||
|
+ setTimeout(() => setCopied(false), 2000)
|
||||||
|
+ } catch (e) {
|
||||||
|
+ console.log('Clipboard not available')
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ const openUrl = (url: string) => {
|
||||||
|
+ WebBrowser.openBrowserAsync(url)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return (
|
||||||
|
+ <Layout.Screen>
|
||||||
|
+ <Layout.Header.Outer>
|
||||||
|
+ <Layout.Header.BackButton />
|
||||||
|
+ <Layout.Header.Content>
|
||||||
|
+ <Layout.Header.TitleText>
|
||||||
|
+ <Trans>App Info</Trans>
|
||||||
|
+ </Layout.Header.TitleText>
|
||||||
|
+ </Layout.Header.Content>
|
||||||
|
+ <Layout.Header.Slot />
|
||||||
|
+ </Layout.Header.Outer>
|
||||||
|
+ <Layout.Content>
|
||||||
|
+ <ScrollView
|
||||||
|
+ style={[a.flex_1]}
|
||||||
|
+ contentContainerStyle={[a.p_lg, a.pt_xl, a.pb_5xl]}>
|
||||||
|
+ {/* App Header */}
|
||||||
|
+ <View style={styles.appHeader}>
|
||||||
|
+ <View style={[styles.appIconContainer, t.atoms.bg_contrast_25]}>
|
||||||
|
+ <Image
|
||||||
|
+ source={require('../../../assets/logo.png')}
|
||||||
|
+ style={styles.appIcon}
|
||||||
|
+ resizeMode="cover"
|
||||||
|
+ />
|
||||||
|
+ </View>
|
||||||
|
+ <Text style={[styles.appName, t.atoms.text]}>
|
||||||
|
+ {APP_NAME}
|
||||||
|
+ </Text>
|
||||||
|
+ <Text style={[styles.appVersion, t.atoms.text_contrast_medium]}>
|
||||||
|
+ v{APP_VERSION}
|
||||||
|
+ </Text>
|
||||||
|
+ </View>
|
||||||
|
+
|
||||||
|
+ {/* Description Section */}
|
||||||
|
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||||
|
+ <Text style={[styles.description, t.atoms.text]}>
|
||||||
|
+ {APP_NAME} is a social networking application based on AT Protocol.
|
||||||
|
+ Connect with your community on syu.is.
|
||||||
|
+ </Text>
|
||||||
|
+ </View>
|
||||||
|
+
|
||||||
|
+ {/* App Information Section */}
|
||||||
|
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||||
|
+ <Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||||
|
+ App Information
|
||||||
|
+ </Text>
|
||||||
|
+ <View style={styles.infoGrid}>
|
||||||
|
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||||
|
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||||
|
+ Version
|
||||||
|
+ </Text>
|
||||||
|
+ <Text style={[styles.infoValue, t.atoms.text]}>
|
||||||
|
+ {APP_VERSION}
|
||||||
|
+ </Text>
|
||||||
|
+ </View>
|
||||||
|
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||||
|
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||||
|
+ Category
|
||||||
|
+ </Text>
|
||||||
|
+ <Text style={[styles.infoValue, t.atoms.text]}>
|
||||||
|
+ Social
|
||||||
|
+ </Text>
|
||||||
|
+ </View>
|
||||||
|
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||||
|
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||||
|
+ Supported OS
|
||||||
|
+ </Text>
|
||||||
|
+ <Text style={[styles.infoValue, t.atoms.text]}>
|
||||||
|
+ iOS 26.0+
|
||||||
|
+ </Text>
|
||||||
|
+ </View>
|
||||||
|
+ <View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||||
|
+ <Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||||
|
+ Price
|
||||||
|
+ </Text>
|
||||||
|
+ <Text style={[styles.infoValue, t.atoms.text]}>
|
||||||
|
+ Free
|
||||||
|
+ </Text>
|
||||||
|
+ </View>
|
||||||
|
+ </View>
|
||||||
|
+ </View>
|
||||||
|
+
|
||||||
|
+ {/* Developer Section */}
|
||||||
|
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||||
|
+ <Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||||
|
+ Developer
|
||||||
|
+ </Text>
|
||||||
|
+ <View style={styles.developerCard}>
|
||||||
|
+ <Text style={[styles.developerName, t.atoms.text]}>syui</Text>
|
||||||
|
+ </View>
|
||||||
|
+
|
||||||
|
+ <Pressable
|
||||||
|
+ onPress={() => openUrl('https://github.com/syui')}
|
||||||
|
+ style={[styles.linkRow, t.atoms.border_contrast_low]}>
|
||||||
|
+ <Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
|
||||||
|
+ GitHub
|
||||||
|
+ </Text>
|
||||||
|
+ <Text style={[styles.linkValue, {color: '#0084ff'}]}>
|
||||||
|
+ github.com/syui
|
||||||
|
+ </Text>
|
||||||
|
+ <Text style={[styles.linkArrow, t.atoms.text_contrast_low]}>→</Text>
|
||||||
|
+ </Pressable>
|
||||||
|
+
|
||||||
|
+ <Pressable
|
||||||
|
+ onPress={() => openUrl('https://syu.is/syui')}
|
||||||
|
+ style={[styles.linkRow, t.atoms.border_contrast_low]}>
|
||||||
|
+ <Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
|
||||||
|
+ ATProto
|
||||||
|
+ </Text>
|
||||||
|
+ <Text style={[styles.linkValue, {color: '#0084ff'}]}>
|
||||||
|
+ syu.is/syui
|
||||||
|
+ </Text>
|
||||||
|
+ <Text style={[styles.linkArrow, t.atoms.text_contrast_low]}>→</Text>
|
||||||
|
+ </Pressable>
|
||||||
|
+ </View>
|
||||||
|
+
|
||||||
|
+ {/* Bitcoin Section */}
|
||||||
|
+ <View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||||
|
+ <Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||||
|
+ Bitcoin
|
||||||
|
+ </Text>
|
||||||
|
+ <Pressable
|
||||||
|
+ onPress={() => copyToClipboard(BITCOIN_ADDRESS)}
|
||||||
|
+ style={styles.bitcoinRow}>
|
||||||
|
+ <Text style={styles.bitcoinLabel}>₿</Text>
|
||||||
|
+ <Text style={[styles.bitcoinAddress, t.atoms.text_contrast_medium]}>
|
||||||
|
+ {BITCOIN_ADDRESS}
|
||||||
|
+ </Text>
|
||||||
|
+ <Text style={[styles.copyHint, copied && styles.copiedHint]}>
|
||||||
|
+ {copied ? 'copied!' : 'copy'}
|
||||||
|
+ </Text>
|
||||||
|
+ </Pressable>
|
||||||
|
+ </View>
|
||||||
|
+
|
||||||
|
+ {/* Copyright */}
|
||||||
|
+ <View style={styles.copyright}>
|
||||||
|
+ <Text style={[styles.copyrightText, t.atoms.text_contrast_low]}>
|
||||||
|
+ © syui
|
||||||
|
+ </Text>
|
||||||
|
+ </View>
|
||||||
|
+ </ScrollView>
|
||||||
|
+ </Layout.Content>
|
||||||
|
+ </Layout.Screen>
|
||||||
|
+ )
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+const styles = StyleSheet.create({
|
||||||
|
+ appHeader: {
|
||||||
|
+ alignItems: 'center',
|
||||||
|
+ marginBottom: 24,
|
||||||
|
+ },
|
||||||
|
+ appIconContainer: {
|
||||||
|
+ width: 80,
|
||||||
|
+ height: 80,
|
||||||
|
+ borderRadius: 18,
|
||||||
|
+ overflow: 'hidden',
|
||||||
|
+ marginBottom: 12,
|
||||||
|
+ },
|
||||||
|
+ appIcon: {
|
||||||
|
+ width: '100%',
|
||||||
|
+ height: '100%',
|
||||||
|
+ },
|
||||||
|
+ appName: {
|
||||||
|
+ fontSize: 24,
|
||||||
|
+ fontWeight: 'bold',
|
||||||
|
+ marginBottom: 4,
|
||||||
|
+ },
|
||||||
|
+ appVersion: {
|
||||||
|
+ fontSize: 14,
|
||||||
|
+ },
|
||||||
|
+ section: {
|
||||||
|
+ marginBottom: 16,
|
||||||
|
+ borderRadius: 16,
|
||||||
|
+ padding: 16,
|
||||||
|
+ },
|
||||||
|
+ sectionTitle: {
|
||||||
|
+ fontSize: 13,
|
||||||
|
+ fontWeight: '600',
|
||||||
|
+ textTransform: 'uppercase',
|
||||||
|
+ letterSpacing: 0.5,
|
||||||
|
+ marginBottom: 12,
|
||||||
|
+ },
|
||||||
|
+ description: {
|
||||||
|
+ fontSize: 15,
|
||||||
|
+ lineHeight: 22,
|
||||||
|
+ },
|
||||||
|
+ infoGrid: {
|
||||||
|
+ flexDirection: 'row',
|
||||||
|
+ flexWrap: 'wrap',
|
||||||
|
+ gap: 8,
|
||||||
|
+ },
|
||||||
|
+ infoItem: {
|
||||||
|
+ flex: 1,
|
||||||
|
+ minWidth: '45%',
|
||||||
|
+ alignItems: 'center',
|
||||||
|
+ borderRadius: 12,
|
||||||
|
+ padding: 12,
|
||||||
|
+ },
|
||||||
|
+ infoLabel: {
|
||||||
|
+ fontSize: 11,
|
||||||
|
+ textTransform: 'uppercase',
|
||||||
|
+ letterSpacing: 0.5,
|
||||||
|
+ marginBottom: 4,
|
||||||
|
+ },
|
||||||
|
+ infoValue: {
|
||||||
|
+ fontSize: 16,
|
||||||
|
+ fontWeight: '600',
|
||||||
|
+ textAlign: 'center',
|
||||||
|
+ },
|
||||||
|
+ developerCard: {
|
||||||
|
+ marginBottom: 12,
|
||||||
|
+ },
|
||||||
|
+ developerName: {
|
||||||
|
+ fontSize: 18,
|
||||||
|
+ fontWeight: '600',
|
||||||
|
+ },
|
||||||
|
+ linkRow: {
|
||||||
|
+ flexDirection: 'row',
|
||||||
|
+ alignItems: 'center',
|
||||||
|
+ paddingVertical: 12,
|
||||||
|
+ borderTopWidth: 1,
|
||||||
|
+ },
|
||||||
|
+ linkIcon: {
|
||||||
|
+ fontSize: 14,
|
||||||
|
+ fontWeight: '600',
|
||||||
|
+ width: 70,
|
||||||
|
+ },
|
||||||
|
+ linkValue: {
|
||||||
|
+ flex: 1,
|
||||||
|
+ fontSize: 14,
|
||||||
|
+ },
|
||||||
|
+ linkArrow: {
|
||||||
|
+ fontSize: 16,
|
||||||
|
+ },
|
||||||
|
+ bitcoinRow: {
|
||||||
|
+ flexDirection: 'row',
|
||||||
|
+ alignItems: 'center',
|
||||||
|
+ backgroundColor: 'rgba(247, 147, 26, 0.08)',
|
||||||
|
+ borderRadius: 12,
|
||||||
|
+ padding: 14,
|
||||||
|
+ gap: 10,
|
||||||
|
+ },
|
||||||
|
+ bitcoinLabel: {
|
||||||
|
+ fontSize: 18,
|
||||||
|
+ fontWeight: '600',
|
||||||
|
+ color: '#f7931a',
|
||||||
|
+ },
|
||||||
|
+ bitcoinAddress: {
|
||||||
|
+ flex: 1,
|
||||||
|
+ fontSize: 11,
|
||||||
|
+ fontFamily: 'monospace',
|
||||||
|
+ },
|
||||||
|
+ copyHint: {
|
||||||
|
+ fontSize: 12,
|
||||||
|
+ color: '#999999',
|
||||||
|
+ minWidth: 50,
|
||||||
|
+ textAlign: 'right',
|
||||||
|
+ },
|
||||||
|
+ copiedHint: {
|
||||||
|
+ color: '#4CAF50',
|
||||||
|
+ fontWeight: '600',
|
||||||
|
+ },
|
||||||
|
+ copyright: {
|
||||||
|
+ alignItems: 'center',
|
||||||
|
+ marginTop: 20,
|
||||||
|
+ marginBottom: 20,
|
||||||
|
+ },
|
||||||
|
+ copyrightText: {
|
||||||
|
+ fontSize: 12,
|
||||||
|
+ },
|
||||||
|
+})
|
||||||
71
ios/patching/032-social-app-ios-feed-loggedout.patch
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
diff --git a/src/lib/api/feed/custom.ts b/src/lib/api/feed/custom.ts
|
||||||
|
index 18bb8c8f0..bab286d7a 100644
|
||||||
|
--- a/src/lib/api/feed/custom.ts
|
||||||
|
+++ b/src/lib/api/feed/custom.ts
|
||||||
|
@@ -5,6 +5,7 @@ import {
|
||||||
|
jsonStringToLex,
|
||||||
|
} from '@atproto/api'
|
||||||
|
|
||||||
|
+import {PUBLIC_APPVIEW} from '#/lib/constants'
|
||||||
|
import {
|
||||||
|
getAppLanguageAsContentLanguage,
|
||||||
|
getContentLanguages,
|
||||||
|
@@ -12,6 +13,17 @@ import {
|
||||||
|
import {type FeedAPI, type FeedAPIResponse} from './types'
|
||||||
|
import {createBskyTopicsHeader, isBlueskyOwnedFeed} from './utils'
|
||||||
|
|
||||||
|
+// Check if the feed is hosted on syu.is network
|
||||||
|
+function isSyuIsFeed(feedUri: string): boolean {
|
||||||
|
+ return feedUri.includes('did:plc:6qyecktefllvenje24fcxnie') || feedUri.includes('syu.is')
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Check if the agent is connected to syu.is
|
||||||
|
+function isAgentOnSyuIs(agent: BskyAgent): boolean {
|
||||||
|
+ const serviceUrl = agent.service?.toString() || ''
|
||||||
|
+ return serviceUrl.includes('syu.is')
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
export class CustomFeedAPI implements FeedAPI {
|
||||||
|
agent: BskyAgent
|
||||||
|
params: GetCustomFeed.QueryParams
|
||||||
|
@@ -54,8 +66,12 @@ export class CustomFeedAPI implements FeedAPI {
|
||||||
|
const agent = this.agent
|
||||||
|
const isBlueskyOwned = isBlueskyOwnedFeed(this.params.feed)
|
||||||
|
|
||||||
|
- const res = agent.did
|
||||||
|
- ? await this.agent.app.bsky.feed.getFeed(
|
||||||
|
+ // For syu.is feeds accessed from non-syu.is accounts, use PUBLIC_APPVIEW
|
||||||
|
+ const needsPublicAppView = isSyuIsFeed(this.params.feed) && !isAgentOnSyuIs(agent)
|
||||||
|
+
|
||||||
|
+ const res = !agent.did || needsPublicAppView
|
||||||
|
+ ? await loggedOutFetch({...this.params, cursor, limit})
|
||||||
|
+ : await this.agent.app.bsky.feed.getFeed(
|
||||||
|
{
|
||||||
|
...this.params,
|
||||||
|
cursor,
|
||||||
|
@@ -70,7 +86,6 @@ export class CustomFeedAPI implements FeedAPI {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
- : await loggedOutFetch({...this.params, cursor, limit})
|
||||||
|
if (res.success) {
|
||||||
|
// NOTE
|
||||||
|
// some custom feeds fail to enforce the pagination limit
|
||||||
|
@@ -120,7 +135,7 @@ async function loggedOutFetch({
|
||||||
|
|
||||||
|
// manually construct fetch call so we can add the `lang` cache-busting param
|
||||||
|
let res = await fetch(
|
||||||
|
- `https://api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=${feed}${
|
||||||
|
+ `${PUBLIC_APPVIEW}/xrpc/app.bsky.feed.getFeed?feed=${encodeURIComponent(feed)}${
|
||||||
|
cursor ? `&cursor=${cursor}` : ''
|
||||||
|
}&limit=${limit}&lang=${contentLangs}`,
|
||||||
|
{
|
||||||
|
@@ -140,7 +155,7 @@ async function loggedOutFetch({
|
||||||
|
|
||||||
|
// no data, try again with language headers removed
|
||||||
|
res = await fetch(
|
||||||
|
- `https://api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=${feed}${
|
||||||
|
+ `${PUBLIC_APPVIEW}/xrpc/app.bsky.feed.getFeed?feed=${encodeURIComponent(feed)}${
|
||||||
|
cursor ? `&cursor=${cursor}` : ''
|
||||||
|
}&limit=${limit}`,
|
||||||
|
{method: 'GET', headers: {'Accept-Language': '', ...labelersHeader}},
|
||||||
23
ios/patching/033-social-app-ios-hide-profile-tabs.patch
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
|
||||||
|
index 123456789..987654321 100644
|
||||||
|
--- a/src/view/screens/Profile.tsx
|
||||||
|
+++ b/src/view/screens/Profile.tsx
|
||||||
|
@@ -218,13 +218,13 @@ function ProfileScreenLoaded({
|
||||||
|
const showPostsTab = true
|
||||||
|
const showRepliesTab = hasSession
|
||||||
|
const showMediaTab = !hasLabeler
|
||||||
|
- const showVideosTab = !hasLabeler
|
||||||
|
+ const showVideosTab = false
|
||||||
|
const showLikesTab = isMe
|
||||||
|
const feedGenCount = profile.associated?.feedgens || 0
|
||||||
|
- const showFeedsTab = isMe || feedGenCount > 0
|
||||||
|
+ const showFeedsTab = feedGenCount > 0
|
||||||
|
const starterPackCount = profile.associated?.starterPacks || 0
|
||||||
|
- const showStarterPacksTab = isMe || starterPackCount > 0
|
||||||
|
+ const showStarterPacksTab = false
|
||||||
|
// subtract starterpack count from list count, since starterpacks are a type of list
|
||||||
|
const listCount = (profile.associated?.lists || 0) - starterPackCount
|
||||||
|
- const showListsTab = hasSession && (isMe || listCount > 0)
|
||||||
|
+ const showListsTab = false
|
||||||
|
|
||||||
|
const sectionTitles = [
|
||||||
28
ios/patching/036-social-app-ios-homeheader-loggedout.patch
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
diff --git a/src/view/com/home/HomeHeader.tsx b/src/view/com/home/HomeHeader.tsx
|
||||||
|
--- a/src/view/com/home/HomeHeader.tsx
|
||||||
|
+++ b/src/view/com/home/HomeHeader.tsx
|
||||||
|
@@ -3,7 +3,6 @@ import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
|
import {type NavigationProp} from '#/lib/routes/types'
|
||||||
|
import {type FeedSourceInfo} from '#/state/queries/feed'
|
||||||
|
-import {useSession} from '#/state/session'
|
||||||
|
import {type RenderTabBarFnProps} from '#/view/com/pager/Pager'
|
||||||
|
import {TabBar} from '../pager/TabBar'
|
||||||
|
import {HomeHeaderLayout} from './HomeHeaderLayout'
|
||||||
|
@@ -16,17 +15,15 @@ export function HomeHeader(
|
||||||
|
) {
|
||||||
|
const {feeds, onSelect: onSelectProp} = props
|
||||||
|
- const {hasSession} = useSession()
|
||||||
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
|
||||||
|
const hasPinnedCustom = React.useMemo<boolean>(() => {
|
||||||
|
- if (!hasSession) return false
|
||||||
|
return feeds.some(tab => {
|
||||||
|
const isFollowing = tab.uri === 'following'
|
||||||
|
return !isFollowing
|
||||||
|
})
|
||||||
|
- }, [feeds, hasSession])
|
||||||
|
+ }, [feeds])
|
||||||
|
|
||||||
|
const items = React.useMemo(() => {
|
||||||
|
const pinnedNames = feeds.map(f => f.displayName)
|
||||||
13
ios/patching/037-social-app-ios-disable-contacts-nux.patch
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/src/components/dialogs/nuxs/index.tsx b/src/components/dialogs/nuxs/index.tsx
|
||||||
|
index 63e11a7f4..70fa993cf 100644
|
||||||
|
--- a/src/components/dialogs/nuxs/index.tsx
|
||||||
|
+++ b/src/components/dialogs/nuxs/index.tsx
|
||||||
|
@@ -46,7 +46,7 @@ const queuedNuxs: {
|
||||||
|
enabled: ({currentProfile}) => {
|
||||||
|
return (
|
||||||
|
isNative &&
|
||||||
|
- isExistingUserAsOf('2025-12-16T00:00:00.000Z', currentProfile.createdAt)
|
||||||
|
+ isExistingUserAsOf('2099-12-16T00:00:00.000Z', currentProfile.createdAt)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
310
ios/patching/AppInfo.tsx
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
import React, {useState} from 'react'
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
ScrollView,
|
||||||
|
StyleSheet,
|
||||||
|
Pressable,
|
||||||
|
Image,
|
||||||
|
} from 'react-native'
|
||||||
|
import * as Clipboard from 'expo-clipboard'
|
||||||
|
import * as WebBrowser from 'expo-web-browser'
|
||||||
|
import {Trans} from '@lingui/macro'
|
||||||
|
|
||||||
|
import * as Layout from '#/components/Layout'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
import {useSetTitle} from '#/lib/hooks/useSetTitle'
|
||||||
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
|
|
||||||
|
const APP_VERSION = '1.111.0'
|
||||||
|
const APP_NAME = 'Aiat'
|
||||||
|
const BITCOIN_ADDRESS = '3BqHXxraZyBapyNpJmniJDh9zqzuB8aoRr'
|
||||||
|
|
||||||
|
export function AppInfoScreen() {
|
||||||
|
useSetTitle('App Info')
|
||||||
|
const t = useTheme()
|
||||||
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
|
const copyToClipboard = async (text: string) => {
|
||||||
|
try {
|
||||||
|
await Clipboard.setStringAsync(text)
|
||||||
|
setCopied(true)
|
||||||
|
setTimeout(() => setCopied(false), 2000)
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Clipboard not available')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openUrl = (url: string) => {
|
||||||
|
WebBrowser.openBrowserAsync(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.Screen>
|
||||||
|
<Layout.Header.Outer>
|
||||||
|
<Layout.Header.BackButton />
|
||||||
|
<Layout.Header.Content>
|
||||||
|
<Layout.Header.TitleText>
|
||||||
|
<Trans>App Info</Trans>
|
||||||
|
</Layout.Header.TitleText>
|
||||||
|
</Layout.Header.Content>
|
||||||
|
<Layout.Header.Slot />
|
||||||
|
</Layout.Header.Outer>
|
||||||
|
<Layout.Content>
|
||||||
|
<ScrollView
|
||||||
|
style={[a.flex_1]}
|
||||||
|
contentContainerStyle={[a.p_lg, a.pt_xl, a.pb_5xl]}>
|
||||||
|
{/* App Header */}
|
||||||
|
<View style={styles.appHeader}>
|
||||||
|
<View style={[styles.appIconContainer, t.atoms.bg_contrast_25]}>
|
||||||
|
<Image
|
||||||
|
source={require('../../../assets/logo.png')}
|
||||||
|
style={styles.appIcon}
|
||||||
|
resizeMode="cover"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Text style={[styles.appName, t.atoms.text]}>
|
||||||
|
{APP_NAME}
|
||||||
|
</Text>
|
||||||
|
<Text style={[styles.appVersion, t.atoms.text_contrast_medium]}>
|
||||||
|
v{APP_VERSION}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Description Section */}
|
||||||
|
<View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||||
|
<Text style={[styles.description, t.atoms.text]}>
|
||||||
|
{APP_NAME} is a social networking application based on AT Protocol.
|
||||||
|
Connect with your community on syu.is.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* App Information Section */}
|
||||||
|
<View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||||
|
<Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||||
|
App Information
|
||||||
|
</Text>
|
||||||
|
<View style={styles.infoGrid}>
|
||||||
|
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||||
|
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||||
|
Version
|
||||||
|
</Text>
|
||||||
|
<Text style={[styles.infoValue, t.atoms.text]}>
|
||||||
|
{APP_VERSION}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||||
|
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||||
|
Category
|
||||||
|
</Text>
|
||||||
|
<Text style={[styles.infoValue, t.atoms.text]}>
|
||||||
|
Social
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||||
|
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||||
|
Supported OS
|
||||||
|
</Text>
|
||||||
|
<Text style={[styles.infoValue, t.atoms.text]}>
|
||||||
|
iOS 26.0+
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[styles.infoItem, t.atoms.bg_contrast_50]}>
|
||||||
|
<Text style={[styles.infoLabel, t.atoms.text_contrast_medium]}>
|
||||||
|
Price
|
||||||
|
</Text>
|
||||||
|
<Text style={[styles.infoValue, t.atoms.text]}>
|
||||||
|
Free
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Developer Section */}
|
||||||
|
<View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||||
|
<Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||||
|
Developer
|
||||||
|
</Text>
|
||||||
|
<View style={styles.developerCard}>
|
||||||
|
<Text style={[styles.developerName, t.atoms.text]}>syui</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Pressable
|
||||||
|
onPress={() => openUrl('https://git.syui.ai/syui')}
|
||||||
|
style={[styles.linkRow, t.atoms.border_contrast_low]}>
|
||||||
|
<Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
|
||||||
|
Git
|
||||||
|
</Text>
|
||||||
|
<Text style={[styles.linkValue, {color: '#0084ff'}]}>
|
||||||
|
git.syui.ai/syui
|
||||||
|
</Text>
|
||||||
|
<Text style={[styles.linkArrow, t.atoms.text_contrast_low]}>→</Text>
|
||||||
|
</Pressable>
|
||||||
|
|
||||||
|
<Pressable
|
||||||
|
onPress={() => openUrl('https://syu.is/syui')}
|
||||||
|
style={[styles.linkRow, t.atoms.border_contrast_low]}>
|
||||||
|
<Text style={[styles.linkIcon, t.atoms.text_contrast_medium]}>
|
||||||
|
ATProto
|
||||||
|
</Text>
|
||||||
|
<Text style={[styles.linkValue, {color: '#0084ff'}]}>
|
||||||
|
syu.is/syui
|
||||||
|
</Text>
|
||||||
|
<Text style={[styles.linkArrow, t.atoms.text_contrast_low]}>→</Text>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Bitcoin Section */}
|
||||||
|
<View style={[styles.section, t.atoms.bg_contrast_25]}>
|
||||||
|
<Text style={[styles.sectionTitle, t.atoms.text_contrast_medium]}>
|
||||||
|
Bitcoin
|
||||||
|
</Text>
|
||||||
|
<Pressable
|
||||||
|
onPress={() => copyToClipboard(BITCOIN_ADDRESS)}
|
||||||
|
style={styles.bitcoinRow}>
|
||||||
|
<Text style={styles.bitcoinLabel}>₿</Text>
|
||||||
|
<Text style={[styles.bitcoinAddress, t.atoms.text_contrast_medium]}>
|
||||||
|
{BITCOIN_ADDRESS}
|
||||||
|
</Text>
|
||||||
|
<Text style={[styles.copyHint, copied && styles.copiedHint]}>
|
||||||
|
{copied ? 'copied!' : 'copy'}
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Copyright */}
|
||||||
|
<View style={styles.copyright}>
|
||||||
|
<Text style={[styles.copyrightText, t.atoms.text_contrast_low]}>
|
||||||
|
© syui
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</Layout.Content>
|
||||||
|
</Layout.Screen>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
appHeader: {
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
appIconContainer: {
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
borderRadius: 18,
|
||||||
|
overflow: 'hidden',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
appIcon: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
appName: {
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
appVersion: {
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
section: {
|
||||||
|
marginBottom: 16,
|
||||||
|
borderRadius: 16,
|
||||||
|
padding: 16,
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: '600',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
fontSize: 15,
|
||||||
|
lineHeight: 22,
|
||||||
|
},
|
||||||
|
infoGrid: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
infoItem: {
|
||||||
|
flex: 1,
|
||||||
|
minWidth: '45%',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 12,
|
||||||
|
},
|
||||||
|
infoLabel: {
|
||||||
|
fontSize: 11,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
infoValue: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
developerCard: {
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
developerName: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
linkRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingVertical: 12,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
},
|
||||||
|
linkIcon: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: '600',
|
||||||
|
width: 70,
|
||||||
|
},
|
||||||
|
linkValue: {
|
||||||
|
flex: 1,
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
linkArrow: {
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
bitcoinRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: 'rgba(247, 147, 26, 0.08)',
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 14,
|
||||||
|
gap: 10,
|
||||||
|
},
|
||||||
|
bitcoinLabel: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#f7931a',
|
||||||
|
},
|
||||||
|
bitcoinAddress: {
|
||||||
|
flex: 1,
|
||||||
|
fontSize: 11,
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
},
|
||||||
|
copyHint: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#999999',
|
||||||
|
minWidth: 50,
|
||||||
|
textAlign: 'right',
|
||||||
|
},
|
||||||
|
copiedHint: {
|
||||||
|
color: '#4CAF50',
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
copyright: {
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
copyrightText: {
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -4,19 +4,22 @@
|
|||||||
|
|
||||||
## パッチファイル一覧
|
## パッチファイル一覧
|
||||||
|
|
||||||
- `001-social-app-ios-config.patch` - Dockerfile, app.config.js の設定変更
|
- `001-social-app-ios-config.patch` - app.config.js の設定変更(アプリ名、Bundle ID、アイコンパス、ドメイン等)
|
||||||
- `002-social-app-ios-lib.patch` - lib/constants.ts, lib/statsig, lib/url-helpers の変更
|
- `002-social-app-ios-lib.patch` - lib/constants.ts, lib/statsig, lib/url-helpers の変更
|
||||||
- `003-social-app-ios-view.patch` - Logo, Logotype, UserAvatar の UI 変更
|
- `003-social-app-ios-view.patch` - Logo, Logotype, UserAvatar, Splash.tsx の UI 変更(Bluesky 蝶ロゴを logo.png に変更)
|
||||||
- `004-social-app-ios-core.patch` - agent.ts, App.native.tsx, routes.ts のコア変更
|
- `004-social-app-ios-core.patch` - agent.ts, App.native.tsx, routes.ts のコア変更
|
||||||
- `005-social-app-ios-screens.patch` - Settings, Home, Privacy, TOS 画面の変更
|
- `005-social-app-ios-screens.patch` - Settings, Home, Privacy, TOS 画面の変更
|
||||||
- `006-social-app-ios-shell.patch` - Drawer, BottomBar, RightNav, ServerInput などシェル変更
|
- `006-social-app-ios-shell.patch` - Drawer, BottomBar, RightNav, ServerInput などシェル変更
|
||||||
- `007-social-app-ios-misc.patch` - notifications, ageAssurance, PolicyUpdate などその他変更
|
- `007-social-app-ios-misc.patch` - notifications, ageAssurance, PolicyUpdate などその他変更
|
||||||
- `008-social-app-ios-policy-tos-error.patch` - プライバシーポリシー・利用規約の英語/日本語二言語化、DIDエラー処理
|
- `008-social-app-ios-policy-tos-error.patch` - プライバシーポリシー・利用規約をネイティブコンポーネントで表示(WebView から ScrollView + Text へ変更)
|
||||||
- `009-social-app-ios-license.patch` - ライセンスページの追加(Drawer, Navigation, routes, types)
|
- `009-social-app-ios-license.patch` - ライセンスページの追加(Drawer, Navigation, routes, types)
|
||||||
- `010-social-app-ios-remove-contact-support.patch` - アカウント作成時の「Contact support」リンクを削除
|
- `010-social-app-ios-remove-contact-support.patch` - アカウント作成時の「Contact support」リンクを削除
|
||||||
- `011-social-app-ios-splash-license-footer.patch` - スプラッシュ画面の「What's up?」削除、© syui フッター追加
|
- `011-social-app-ios-splash-license-footer.patch` - スプラッシュ画面の「What's up?」削除、© syui フッター追加
|
||||||
- `012-social-app-ios-settings-about-help.patch` - About 設定のリンク修正(syu.is に変更)
|
- `012-social-app-ios-settings-about-help.patch` - About 設定と routes.ts のリンクを内部ルートに変更(/support/tos, /support/privacy-policy, /support/license)
|
||||||
- `013-social-app-ios-settings-remove-help.patch` - Settings から Help 項目を削除
|
- `013-social-app-ios-settings-remove-help.patch` - Settings から Help 項目を削除
|
||||||
|
- `019-social-app-ios-entitlements-plugin.patch` - iOS entitlements プラグイン設定
|
||||||
|
- `020-social-app-ios-bypass-age-assurance.patch` - 年齢確認を完全に無効化(access を Full に固定、chatDisabled と adultContentDisabled を false に固定)
|
||||||
|
- `021-social-app-ios-clean-feed.patch` - Following フィードのシンプル化(DiscoverFallbackHeader の (i) アイコンと Discover リンク削除、SuggestedFollows インタースティシャル無効化、おすすめボタン削除)
|
||||||
- `License.tsx` - ライセンス表示画面(新規ファイル)
|
- `License.tsx` - ライセンス表示画面(新規ファイル)
|
||||||
|
|
||||||
## 使用方法
|
## 使用方法
|
||||||
@@ -28,6 +31,11 @@ cd /Users/syui/ai/at/ios
|
|||||||
./setup.zsh patch
|
./setup.zsh patch
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**注意**: setup.zsh が自動的に以下を実行します:
|
||||||
|
- パッチファイルの適用
|
||||||
|
- License.tsx のコピー
|
||||||
|
- Xcode AppIcon を logo.png から 1024x1024 にリサイズして配置(GraphicsMagick または sips を使用)
|
||||||
|
|
||||||
### リポジトリのリセット
|
### リポジトリのリセット
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
105
ios/preview.zsh
@@ -5,6 +5,16 @@ cd $d
|
|||||||
|
|
||||||
source $d/.env
|
source $d/.env
|
||||||
|
|
||||||
|
function sediment() {
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
sed -i '' "$@"
|
||||||
|
else
|
||||||
|
sed -i "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#xcrun simctl uninstall booted $BUNDLE_ID
|
||||||
|
|
||||||
echo "Running iOS preview workflow..."
|
echo "Running iOS preview workflow..."
|
||||||
cd "$REPO_DIR"
|
cd "$REPO_DIR"
|
||||||
|
|
||||||
@@ -22,6 +32,36 @@ fi
|
|||||||
echo "1. Installing dependencies (yarn)..."
|
echo "1. Installing dependencies (yarn)..."
|
||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
|
# 1.5. Copy assets
|
||||||
|
echo "1.5. Copying assets..."
|
||||||
|
ASSETS_DIR="${0:a:h}/assets"
|
||||||
|
if [ -d "$ASSETS_DIR" ]; then
|
||||||
|
cp -rf "$ASSETS_DIR/"* "$REPO_DIR/assets/"
|
||||||
|
echo "✅ Copied all assets (including logo.png, logo-1024.png)"
|
||||||
|
else
|
||||||
|
echo "⚠️ Warning: $ASSETS_DIR not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1.8. Update package.json version (prevent App Store version conflict)
|
||||||
|
echo "1.8. Updating package.json version..."
|
||||||
|
if [ -n "$APP_VERSION" ]; then
|
||||||
|
node -e "
|
||||||
|
const fs = require('fs');
|
||||||
|
const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
||||||
|
pkg.version = '$APP_VERSION';
|
||||||
|
fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');
|
||||||
|
"
|
||||||
|
echo " ✅ Set version to $APP_VERSION"
|
||||||
|
else
|
||||||
|
echo " ⚠️ APP_VERSION not set in .env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1.9. Update buildNumber (CFBundleVersion) with current timestamp
|
||||||
|
echo "1.9. Updating buildNumber..."
|
||||||
|
build_number=$(date +%y%m%d%H%M%S)
|
||||||
|
sediment "s/buildNumber: '[0-9]*'/buildNumber: '${build_number}'/" "./app.config.js"
|
||||||
|
echo " ✅ Set buildNumber to $build_number"
|
||||||
|
|
||||||
# 2. Prebuild (Generate ios directory)
|
# 2. Prebuild (Generate ios directory)
|
||||||
echo "2. Running Expo Prebuild..."
|
echo "2. Running Expo Prebuild..."
|
||||||
# Clean old ios folder to remove old entitlements/AppClip targets
|
# Clean old ios folder to remove old entitlements/AppClip targets
|
||||||
@@ -36,25 +76,62 @@ cd ios
|
|||||||
pod install
|
pod install
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
# 4. Signing (Manual Step)
|
# 4. Signing (Automated)
|
||||||
echo "4. Opening Xcode for Signing..."
|
echo "4. Configuring Xcode Signing..."
|
||||||
XCODE_PROJ="ios/${APP_NAME}.xcodeproj"
|
XCODE_PROJ="ios/${APP_NAME}.xcodeproj"
|
||||||
# Fallback search if variable name logic differs
|
|
||||||
if [ ! -d "$XCODE_PROJ" ]; then
|
if [ ! -d "$XCODE_PROJ" ]; then
|
||||||
XCODE_PROJ=$(find ios -name "*.xcodeproj" | head -n 1)
|
XCODE_PROJ=$(find ios -name "*.xcodeproj" | head -n 1)
|
||||||
fi
|
fi
|
||||||
|
PBXPROJ="$XCODE_PROJ/project.pbxproj"
|
||||||
|
|
||||||
open "$XCODE_PROJ"
|
# Set DEVELOPMENT_TEAM in pbxproj
|
||||||
echo "========================================================"
|
if [ -n "$DEVELOPMENT_TEAM" ]; then
|
||||||
echo " [ACTION REQUIRED] "
|
echo " Setting DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM"
|
||||||
echo " Xcode opened ($XCODE_PROJ)."
|
# Add DEVELOPMENT_TEAM to all build configurations
|
||||||
echo " 1. Go to 'Signing & Capabilities' tab."
|
sediment "s/PRODUCT_BUNDLE_IDENTIFIER = /DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM; PRODUCT_BUNDLE_IDENTIFIER = /g" "$PBXPROJ"
|
||||||
echo " 2. Select your Team."
|
# Also set where it might already exist but be empty
|
||||||
echo " 3. Verify 'App Clip' target is gone."
|
sediment "s/DEVELOPMENT_TEAM = \"\";/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/g" "$PBXPROJ"
|
||||||
echo " 4. Ensure no red errors exist."
|
sediment "s/DEVELOPMENT_TEAM = ;/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/g" "$PBXPROJ"
|
||||||
echo " Press ENTER here once you are done to continue building."
|
fi
|
||||||
echo "========================================================"
|
|
||||||
read
|
# Create/Update entitlements file with App Group
|
||||||
|
ENTITLEMENTS_FILE="ios/${APP_NAME}/${APP_NAME}.entitlements"
|
||||||
|
if [ -n "$APP_GROUP" ]; then
|
||||||
|
echo " Setting APP_GROUP=$APP_GROUP"
|
||||||
|
cat > "$ENTITLEMENTS_FILE" << EOF
|
||||||
|
<?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>aps-environment</key>
|
||||||
|
<string>development</string>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>${APP_GROUP}</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
# Add CODE_SIGN_ENTITLEMENTS to pbxproj if not present
|
||||||
|
if ! grep -q "CODE_SIGN_ENTITLEMENTS" "$PBXPROJ"; then
|
||||||
|
sediment "s/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM; CODE_SIGN_ENTITLEMENTS = ${APP_NAME}\\/${APP_NAME}.entitlements;/g" "$PBXPROJ"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Signing configured automatically"
|
||||||
|
|
||||||
|
# (Old manual step - commented out)
|
||||||
|
# open "$XCODE_PROJ"
|
||||||
|
# echo "========================================================"
|
||||||
|
# echo " [ACTION REQUIRED] "
|
||||||
|
# echo " Xcode opened ($XCODE_PROJ)."
|
||||||
|
# echo " 1. Go to 'Signing & Capabilities' tab."
|
||||||
|
# echo " 2. Select your Team."
|
||||||
|
# echo " 3. Verify 'App Clip' target is gone."
|
||||||
|
# echo " 4. Ensure no red errors exist."
|
||||||
|
# echo " Press ENTER here once you are done to continue building."
|
||||||
|
# echo "========================================================"
|
||||||
|
# read
|
||||||
|
|
||||||
# 5. Run
|
# 5. Run
|
||||||
echo "5. Building and Running..."
|
echo "5. Building and Running..."
|
||||||
|
|||||||
141
ios/setup.zsh
@@ -5,6 +5,15 @@ cd ${0:a:h}
|
|||||||
# iOS Social App Patch Setup Script
|
# iOS Social App Patch Setup Script
|
||||||
# Usage: ./ios/setup.zsh [patch|reset]
|
# Usage: ./ios/setup.zsh [patch|reset]
|
||||||
|
|
||||||
|
# Cross-platform sed (macOS vs Linux)
|
||||||
|
function sediment() {
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
sed -i '' "$@"
|
||||||
|
else
|
||||||
|
sed -i "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Arrays for patch management
|
# Arrays for patch management
|
||||||
typeset -a FAILED_PATCHES
|
typeset -a FAILED_PATCHES
|
||||||
|
|
||||||
@@ -18,12 +27,25 @@ PATCH_FILES_IOS=(
|
|||||||
"005-social-app-ios-screens.patch"
|
"005-social-app-ios-screens.patch"
|
||||||
"006-social-app-ios-shell.patch"
|
"006-social-app-ios-shell.patch"
|
||||||
"007-social-app-ios-misc.patch"
|
"007-social-app-ios-misc.patch"
|
||||||
"008-social-app-ios-policy-tos-error.patch"
|
|
||||||
"009-social-app-ios-license.patch"
|
"009-social-app-ios-license.patch"
|
||||||
"010-social-app-ios-remove-contact-support.patch"
|
"010-social-app-ios-remove-contact-support.patch"
|
||||||
"011-social-app-ios-splash-license-footer.patch"
|
"011-social-app-ios-splash-license-footer.patch"
|
||||||
"012-social-app-ios-settings-about-help.patch"
|
|
||||||
"013-social-app-ios-settings-remove-help.patch"
|
"013-social-app-ios-settings-remove-help.patch"
|
||||||
|
"019-social-app-ios-entitlements-plugin.patch"
|
||||||
|
"020-social-app-ios-bypass-age-assurance.patch"
|
||||||
|
"021-social-app-ios-clean-feed.patch"
|
||||||
|
"022-social-app-ios-bskyweb-support-pages.patch"
|
||||||
|
"023-social-app-ios-disable-dm.patch"
|
||||||
|
"024-social-app-ios-disable-external-services.patch"
|
||||||
|
"025-social-app-ios-bskyweb-title.patch"
|
||||||
|
"027-social-app-ios-remove-birthdate.patch"
|
||||||
|
"028-social-app-ios-remove-discover-feeds.patch"
|
||||||
|
"029-social-app-ios-remove-feeds-discover.patch"
|
||||||
|
"030-social-app-ios-appinfo.patch"
|
||||||
|
"032-social-app-ios-feed-loggedout.patch"
|
||||||
|
"033-social-app-ios-hide-profile-tabs.patch"
|
||||||
|
"036-social-app-ios-homeheader-loggedout.patch"
|
||||||
|
"037-social-app-ios-disable-contacts-nux.patch"
|
||||||
)
|
)
|
||||||
|
|
||||||
function ios-env() {
|
function ios-env() {
|
||||||
@@ -45,14 +67,6 @@ function apply-patch() {
|
|||||||
|
|
||||||
pushd ${target_dir} > /dev/null
|
pushd ${target_dir} > /dev/null
|
||||||
|
|
||||||
# Check if patch is already applied (reverse dry-run succeeds)
|
|
||||||
if patch -f --dry-run -p1 -R < ${patch_file} > /dev/null 2>&1; then
|
|
||||||
echo "✅ Already applied - skipping"
|
|
||||||
popd > /dev/null
|
|
||||||
echo ""
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if patch can be applied (forward dry-run succeeds)
|
# Check if patch can be applied (forward dry-run succeeds)
|
||||||
if patch --dry-run -p1 < ${patch_file} > /dev/null 2>&1; then
|
if patch --dry-run -p1 < ${patch_file} > /dev/null 2>&1; then
|
||||||
echo "🔧 Applying patch..."
|
echo "🔧 Applying patch..."
|
||||||
@@ -110,6 +124,30 @@ function patch-apply() {
|
|||||||
apply-patch "${name}" "$target_dir" "$patching_dir/${patch_file}"
|
apply-patch "${name}" "$target_dir" "$patching_dir/${patch_file}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Generate build number from timestamp (YYMMDDHHMMSS)
|
||||||
|
function ios-generate-build-number() {
|
||||||
|
local build_number=$(date +%y%m%d%H%M%S)
|
||||||
|
local config_patch="$patching_dir/001-social-app-ios-config.patch"
|
||||||
|
|
||||||
|
if [ -f "$config_patch" ]; then
|
||||||
|
# Replace placeholder with timestamp
|
||||||
|
sediment "s/__BUILD_NUMBER__/${build_number}/" "$config_patch"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "🔢 Build number: ${build_number}"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Restore placeholder after patching (for git cleanliness)
|
||||||
|
function ios-restore-build-placeholder() {
|
||||||
|
local config_patch="$patching_dir/001-social-app-ios-config.patch"
|
||||||
|
|
||||||
|
if [ -f "$config_patch" ]; then
|
||||||
|
# Restore placeholder for next build
|
||||||
|
sediment "s/buildNumber: '[0-9]*'/buildNumber: '__BUILD_NUMBER__'/" "$config_patch"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Auto-apply patches from list
|
# Auto-apply patches from list
|
||||||
function ios-patch-apply-all() {
|
function ios-patch-apply-all() {
|
||||||
for filename in "${PATCH_FILES_IOS[@]}"; do
|
for filename in "${PATCH_FILES_IOS[@]}"; do
|
||||||
@@ -123,6 +161,12 @@ function ios-copy-new-files() {
|
|||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
echo "📁 Copying new files..."
|
echo "📁 Copying new files..."
|
||||||
|
|
||||||
|
# Copy all assets from ios/assets/ to repos/social-app/assets/
|
||||||
|
if [ -d "$d/ios/assets" ]; then
|
||||||
|
cp -rf "$d/ios/assets/"* "$target_dir/assets/"
|
||||||
|
echo "✅ Copied all assets (including logo.png, app-icons)"
|
||||||
|
fi
|
||||||
|
|
||||||
# Copy License.tsx
|
# Copy License.tsx
|
||||||
if [ -f "$patching_dir/License.tsx" ]; then
|
if [ -f "$patching_dir/License.tsx" ]; then
|
||||||
mkdir -p "$target_dir/src/view/screens"
|
mkdir -p "$target_dir/src/view/screens"
|
||||||
@@ -130,6 +174,25 @@ function ios-copy-new-files() {
|
|||||||
echo "✅ Copied License.tsx"
|
echo "✅ Copied License.tsx"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Copy AppInfo.tsx
|
||||||
|
if [ -f "$patching_dir/AppInfo.tsx" ]; then
|
||||||
|
mkdir -p "$target_dir/src/view/screens"
|
||||||
|
cp "$patching_dir/AppInfo.tsx" "$target_dir/src/view/screens/AppInfo.tsx"
|
||||||
|
echo "✅ Copied AppInfo.tsx"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy pre-generated favicons for bskyweb
|
||||||
|
local favicon_src="$d/ios/assets/favicons"
|
||||||
|
local bskyweb_static="$target_dir/bskyweb/static"
|
||||||
|
if [ -d "$favicon_src" ] && [ -d "$bskyweb_static" ]; then
|
||||||
|
cp -f "$d/ios/assets/logo.png" "$bskyweb_static/app.png"
|
||||||
|
cp -f "$favicon_src/favicon.png" "$bskyweb_static/favicon.png"
|
||||||
|
cp -f "$favicon_src/favicon-16x16.png" "$bskyweb_static/favicon-16x16.png"
|
||||||
|
cp -f "$favicon_src/favicon-32x32.png" "$bskyweb_static/favicon-32x32.png"
|
||||||
|
cp -f "$favicon_src/apple-touch-icon.png" "$bskyweb_static/apple-touch-icon.png"
|
||||||
|
echo "✅ Copied favicons to bskyweb/static"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +205,53 @@ function ios-setup-clone() {
|
|||||||
echo "Repository found: $target_dir"
|
echo "Repository found: $target_dir"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Generate bskyweb templates from html/ source
|
||||||
|
# html/ is the source of truth, bskyweb templates are generated
|
||||||
|
function ios-generate-bskyweb-templates() {
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "🌐 Generating bskyweb templates from html/..."
|
||||||
|
|
||||||
|
local html_src="$d/html/about/support"
|
||||||
|
local templates="$target_dir/bskyweb/templates"
|
||||||
|
local static_src="$d/html/static"
|
||||||
|
local static_out="$target_dir/bskyweb/static"
|
||||||
|
|
||||||
|
# Check if html source exists
|
||||||
|
if [ ! -d "$html_src" ]; then
|
||||||
|
echo "⚠️ html/about/support not found, skipping template generation"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create output directory
|
||||||
|
mkdir -p "$templates"
|
||||||
|
mkdir -p "$static_out"
|
||||||
|
|
||||||
|
# Convert html/ to bskyweb templates
|
||||||
|
# Add {{ staticCDNHost }} prefix to /static/ paths
|
||||||
|
for html_file in privacy.html license.html tos.html help.html app.html; do
|
||||||
|
if [ -f "$html_src/$html_file" ]; then
|
||||||
|
local template_name="about-${html_file}"
|
||||||
|
sed 's|href="/static/|href="{{ staticCDNHost }}/static/|g; s|src="/static/|src="{{ staticCDNHost }}/static/|g' \
|
||||||
|
"$html_src/$html_file" > "$templates/$template_name"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Also generate about-app.html from index.html if exists
|
||||||
|
if [ -f "$d/html/index.html" ]; then
|
||||||
|
sed 's|href="/static/|href="{{ staticCDNHost }}/static/|g; s|src="/static/|src="{{ staticCDNHost }}/static/|g' \
|
||||||
|
"$d/html/index.html" > "$templates/about-app.html"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy static assets
|
||||||
|
if [ -d "$static_src" ]; then
|
||||||
|
cp -f "$static_src/"* "$static_out/" 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Generated bskyweb templates"
|
||||||
|
echo " - about-privacy.html, about-tos.html, etc."
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
function ios-setup-reset() {
|
function ios-setup-reset() {
|
||||||
echo "Resetting social-app repository..."
|
echo "Resetting social-app repository..."
|
||||||
cd $target_dir
|
cd $target_dir
|
||||||
@@ -156,8 +266,11 @@ ios-env
|
|||||||
case "$1" in
|
case "$1" in
|
||||||
patch)
|
patch)
|
||||||
ios-setup-clone
|
ios-setup-clone
|
||||||
|
ios-generate-build-number
|
||||||
ios-patch-apply-all
|
ios-patch-apply-all
|
||||||
|
ios-restore-build-placeholder
|
||||||
ios-copy-new-files
|
ios-copy-new-files
|
||||||
|
ios-generate-bskyweb-templates
|
||||||
show-failed-patches
|
show-failed-patches
|
||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
@@ -165,10 +278,18 @@ case "$1" in
|
|||||||
ios-setup-reset
|
ios-setup-reset
|
||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
|
html)
|
||||||
|
# Generate bskyweb templates only (requires patches to be applied first)
|
||||||
|
ios-generate-bskyweb-templates
|
||||||
|
exit
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
ios-setup-clone
|
ios-setup-clone
|
||||||
|
ios-generate-build-number
|
||||||
ios-patch-apply-all
|
ios-patch-apply-all
|
||||||
|
ios-restore-build-placeholder
|
||||||
ios-copy-new-files
|
ios-copy-new-files
|
||||||
|
ios-generate-bskyweb-templates
|
||||||
show-failed-patches
|
show-failed-patches
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -22,14 +22,31 @@ index 0000000..993c83d
|
|||||||
+
|
+
|
||||||
+EXPOSE 3000
|
+EXPOSE 3000
|
||||||
+CMD ["yarn", "start"]
|
+CMD ["yarn", "start"]
|
||||||
|
diff --git a/package.json b/package.json
|
||||||
|
index 1431a9e..6a7c33c 100644
|
||||||
|
--- a/package.json
|
||||||
|
+++ b/package.json
|
||||||
|
@@ -23,9 +23,11 @@
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"kysely": "^0.27.4",
|
||||||
|
- "multiformats": "^9.9.0"
|
||||||
|
+ "multiformats": "^9.9.0",
|
||||||
|
+ "ws": "^8.14.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
+ "@types/ws": "^8.5.10",
|
||||||
|
"@types/better-sqlite3": "^7.6.11",
|
||||||
|
"@types/express": "^4.17.17",
|
||||||
|
"@types/node": "^20.1.2",
|
||||||
diff --git a/scripts/publish.ts b/scripts/publish.ts
|
diff --git a/scripts/publish.ts b/scripts/publish.ts
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 0000000..966edcf
|
index 0000000..044f1d9
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/scripts/publish.ts
|
+++ b/scripts/publish.ts
|
||||||
@@ -0,0 +1,64 @@
|
@@ -0,0 +1,64 @@
|
||||||
+import dotenv from 'dotenv'
|
+import dotenv from 'dotenv'
|
||||||
+import { AtpAgent, BlobRef, AppBskyFeedDefs } from '@atproto/api'
|
+import { AtpAgent, BlobRef } from '@atproto/api'
|
||||||
+import fs from 'fs/promises'
|
+import fs from 'fs/promises'
|
||||||
+import { ids } from '../src/lexicon/lexicons'
|
+import { ids } from '../src/lexicon/lexicons'
|
||||||
+
|
+
|
||||||
@@ -88,7 +105,7 @@ index 0000000..966edcf
|
|||||||
+ },
|
+ },
|
||||||
+ })
|
+ })
|
||||||
+
|
+
|
||||||
+ console.log('All done 🎉')
|
+ console.log('All done')
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
+run()
|
+run()
|
||||||
@@ -152,12 +169,15 @@ index b7ee48a..102cb93 100644
|
|||||||
|
|
||||||
export default algos
|
export default algos
|
||||||
diff --git a/src/index.ts b/src/index.ts
|
diff --git a/src/index.ts b/src/index.ts
|
||||||
index c3bd006..1e7f0b5 100644
|
index 7128525..40d985c 100644
|
||||||
--- a/src/index.ts
|
--- a/src/index.ts
|
||||||
+++ b/src/index.ts
|
+++ b/src/index.ts
|
||||||
@@ -24,6 +24,8 @@ const run = async () => {
|
@@ -22,8 +22,10 @@ const run = async () => {
|
||||||
|
})
|
||||||
|
await server.start()
|
||||||
console.log(
|
console.log(
|
||||||
`🤖 running feed generator at http://${server.cfg.listenhost}:${server.cfg.port}`,
|
- `🤖 running feed generator at http://${server.cfg.listenhost}:${server.cfg.port}`,
|
||||||
|
+ `running feed generator at http://${server.cfg.listenhost}:${server.cfg.port}`,
|
||||||
)
|
)
|
||||||
+ console.log('Supported algos:', Object.keys(require('./algos').default))
|
+ console.log('Supported algos:', Object.keys(require('./algos').default))
|
||||||
+ console.log('Publisher DID:', server.cfg.publisherDid)
|
+ console.log('Publisher DID:', server.cfg.publisherDid)
|
||||||
@@ -165,7 +185,7 @@ index c3bd006..1e7f0b5 100644
|
|||||||
|
|
||||||
const maybeStr = (val?: string) => {
|
const maybeStr = (val?: string) => {
|
||||||
diff --git a/src/methods/feed-generation.ts b/src/methods/feed-generation.ts
|
diff --git a/src/methods/feed-generation.ts b/src/methods/feed-generation.ts
|
||||||
index b887413..34c5148 100644
|
index 0f4989e..17be062 100644
|
||||||
--- a/src/methods/feed-generation.ts
|
--- a/src/methods/feed-generation.ts
|
||||||
+++ b/src/methods/feed-generation.ts
|
+++ b/src/methods/feed-generation.ts
|
||||||
@@ -10,7 +10,7 @@ export default function (server: Server, ctx: AppContext) {
|
@@ -10,7 +10,7 @@ export default function (server: Server, ctx: AppContext) {
|
||||||
@@ -177,18 +197,233 @@ index b887413..34c5148 100644
|
|||||||
feedUri.collection !== 'app.bsky.feed.generator' ||
|
feedUri.collection !== 'app.bsky.feed.generator' ||
|
||||||
!algo
|
!algo
|
||||||
) {
|
) {
|
||||||
|
diff --git a/src/server.ts b/src/server.ts
|
||||||
|
index c696749..9b9c382 100644
|
||||||
|
--- a/src/server.ts
|
||||||
|
+++ b/src/server.ts
|
||||||
|
@@ -6,7 +6,7 @@ import { createServer } from './lexicon'
|
||||||
|
import feedGeneration from './methods/feed-generation'
|
||||||
|
import describeGenerator from './methods/describe-generator'
|
||||||
|
import { createDb, Database, migrateToLatest } from './db'
|
||||||
|
-import { FirehoseSubscription } from './subscription'
|
||||||
|
+import { JetstreamSubscription } from './subscription'
|
||||||
|
import { AppContext, Config } from './config'
|
||||||
|
import wellKnown from './well-known'
|
||||||
|
|
||||||
|
@@ -14,25 +14,28 @@ export class FeedGenerator {
|
||||||
|
public app: express.Application
|
||||||
|
public server?: http.Server
|
||||||
|
public db: Database
|
||||||
|
- public firehose: FirehoseSubscription
|
||||||
|
+ public jetstream: JetstreamSubscription
|
||||||
|
public cfg: Config
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
app: express.Application,
|
||||||
|
db: Database,
|
||||||
|
- firehose: FirehoseSubscription,
|
||||||
|
+ jetstream: JetstreamSubscription,
|
||||||
|
cfg: Config,
|
||||||
|
) {
|
||||||
|
this.app = app
|
||||||
|
this.db = db
|
||||||
|
- this.firehose = firehose
|
||||||
|
+ this.jetstream = jetstream
|
||||||
|
this.cfg = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(cfg: Config) {
|
||||||
|
const app = express()
|
||||||
|
const db = createDb(cfg.sqliteLocation)
|
||||||
|
- const firehose = new FirehoseSubscription(db, cfg.subscriptionEndpoint)
|
||||||
|
+
|
||||||
|
+ // Use Jetstream URL from env or default to internal jetstream service
|
||||||
|
+ const jetstreamUrl = process.env.FEEDGEN_JETSTREAM_URL || 'ws://jetstream:6008/subscribe'
|
||||||
|
+ const jetstream = new JetstreamSubscription(db, jetstreamUrl, cfg.subscriptionReconnectDelay)
|
||||||
|
|
||||||
|
const didCache = new MemoryCache()
|
||||||
|
const didResolver = new DidResolver({
|
||||||
|
@@ -58,12 +61,12 @@ export class FeedGenerator {
|
||||||
|
app.use(server.xrpc.router)
|
||||||
|
app.use(wellKnown(ctx))
|
||||||
|
|
||||||
|
- return new FeedGenerator(app, db, firehose, cfg)
|
||||||
|
+ return new FeedGenerator(app, db, jetstream, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<http.Server> {
|
||||||
|
await migrateToLatest(this.db)
|
||||||
|
- this.firehose.run(this.cfg.subscriptionReconnectDelay)
|
||||||
|
+ this.jetstream.run()
|
||||||
|
this.server = this.app.listen(this.cfg.port, this.cfg.listenhost)
|
||||||
|
await events.once(this.server, 'listening')
|
||||||
|
return this.server
|
||||||
diff --git a/src/subscription.ts b/src/subscription.ts
|
diff --git a/src/subscription.ts b/src/subscription.ts
|
||||||
index 0422a03..d591ef9 100644
|
index 0422a03..7785982 100644
|
||||||
--- a/src/subscription.ts
|
--- a/src/subscription.ts
|
||||||
+++ b/src/subscription.ts
|
+++ b/src/subscription.ts
|
||||||
@@ -19,10 +19,6 @@ export class FirehoseSubscription extends FirehoseSubscriptionBase {
|
@@ -1,49 +1,126 @@
|
||||||
|
-import {
|
||||||
|
- OutputSchema as RepoEvent,
|
||||||
|
- isCommit,
|
||||||
|
-} from './lexicon/types/com/atproto/sync/subscribeRepos'
|
||||||
|
-import { FirehoseSubscriptionBase, getOpsByType } from './util/subscription'
|
||||||
|
-
|
||||||
|
-export class FirehoseSubscription extends FirehoseSubscriptionBase {
|
||||||
|
- async handleEvent(evt: RepoEvent) {
|
||||||
|
- if (!isCommit(evt)) return
|
||||||
|
-
|
||||||
|
- const ops = await getOpsByType(evt)
|
||||||
|
-
|
||||||
|
- // This logs the text of every post off the firehose.
|
||||||
|
- // Just for fun :)
|
||||||
|
- // Delete before actually using
|
||||||
|
- for (const post of ops.posts.creates) {
|
||||||
|
- console.log(post.record.text)
|
||||||
|
+import WebSocket from 'ws'
|
||||||
|
+import { Database } from './db'
|
||||||
|
+
|
||||||
|
+// Jetstream event types
|
||||||
|
+interface JetstreamEvent {
|
||||||
|
+ did: string
|
||||||
|
+ time_us: number
|
||||||
|
+ kind: 'commit' | 'identity' | 'account'
|
||||||
|
+ commit?: {
|
||||||
|
+ rev: string
|
||||||
|
+ operation: 'create' | 'update' | 'delete'
|
||||||
|
+ collection: string
|
||||||
|
+ rkey: string
|
||||||
|
+ record?: {
|
||||||
|
+ $type: string
|
||||||
|
+ text?: string
|
||||||
|
+ createdAt?: string
|
||||||
|
+ [key: string]: unknown
|
||||||
|
}
|
||||||
|
+ cid?: string
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
|
||||||
const postsToDelete = ops.posts.deletes.map((del) => del.uri)
|
- const postsToDelete = ops.posts.deletes.map((del) => del.uri)
|
||||||
const postsToCreate = ops.posts.creates
|
- const postsToCreate = ops.posts.creates
|
||||||
- .filter((create) => {
|
- .filter((create) => {
|
||||||
- // only alf-related posts
|
- // only alf-related posts
|
||||||
- return create.record.text.toLowerCase().includes('alf')
|
- return create.record.text.toLowerCase().includes('alf')
|
||||||
- })
|
- })
|
||||||
.map((create) => {
|
- .map((create) => {
|
||||||
// map alf-related posts to a db row
|
- // map alf-related posts to a db row
|
||||||
return {
|
- return {
|
||||||
|
- uri: create.uri,
|
||||||
|
- cid: create.cid,
|
||||||
|
- indexedAt: new Date().toISOString(),
|
||||||
|
- }
|
||||||
|
- })
|
||||||
|
+export class JetstreamSubscription {
|
||||||
|
+ private ws: WebSocket | null = null
|
||||||
|
+ private cursor: number = 0
|
||||||
|
|
||||||
|
- if (postsToDelete.length > 0) {
|
||||||
|
- await this.db
|
||||||
|
- .deleteFrom('post')
|
||||||
|
- .where('uri', 'in', postsToDelete)
|
||||||
|
- .execute()
|
||||||
|
+ constructor(
|
||||||
|
+ public db: Database,
|
||||||
|
+ public jetstreamUrl: string,
|
||||||
|
+ public reconnectDelay: number = 3000
|
||||||
|
+ ) {}
|
||||||
|
+
|
||||||
|
+ async run() {
|
||||||
|
+ await this.loadCursor()
|
||||||
|
+ this.connect()
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private connect() {
|
||||||
|
+ const url = new URL(this.jetstreamUrl)
|
||||||
|
+ url.searchParams.set('wantedCollections', 'app.bsky.feed.post')
|
||||||
|
+ if (this.cursor > 0) {
|
||||||
|
+ url.searchParams.set('cursor', this.cursor.toString())
|
||||||
|
}
|
||||||
|
- if (postsToCreate.length > 0) {
|
||||||
|
+
|
||||||
|
+ console.log(`Connecting to Jetstream: ${url.toString()}`)
|
||||||
|
+ this.ws = new WebSocket(url.toString())
|
||||||
|
+
|
||||||
|
+ this.ws.on('open', () => {
|
||||||
|
+ console.log('Connected to Jetstream')
|
||||||
|
+ })
|
||||||
|
+
|
||||||
|
+ this.ws.on('message', async (data: WebSocket.Data) => {
|
||||||
|
+ try {
|
||||||
|
+ const event: JetstreamEvent = JSON.parse(data.toString())
|
||||||
|
+ await this.handleEvent(event)
|
||||||
|
+ } catch (err) {
|
||||||
|
+ console.error('Failed to handle Jetstream message:', err)
|
||||||
|
+ }
|
||||||
|
+ })
|
||||||
|
+
|
||||||
|
+ this.ws.on('error', (err) => {
|
||||||
|
+ console.error('Jetstream WebSocket error:', err)
|
||||||
|
+ })
|
||||||
|
+
|
||||||
|
+ this.ws.on('close', () => {
|
||||||
|
+ console.log('Jetstream connection closed, reconnecting...')
|
||||||
|
+ setTimeout(() => this.connect(), this.reconnectDelay)
|
||||||
|
+ })
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private async handleEvent(event: JetstreamEvent) {
|
||||||
|
+ if (event.kind !== 'commit' || !event.commit) return
|
||||||
|
+ if (event.commit.collection !== 'app.bsky.feed.post') return
|
||||||
|
+
|
||||||
|
+ const uri = `at://${event.did}/${event.commit.collection}/${event.commit.rkey}`
|
||||||
|
+
|
||||||
|
+ if (event.commit.operation === 'delete') {
|
||||||
|
+ await this.db.deleteFrom('post').where('uri', '=', uri).execute()
|
||||||
|
+ } else if (event.commit.operation === 'create' && event.commit.record) {
|
||||||
|
+ const text = event.commit.record.text || ''
|
||||||
|
+
|
||||||
|
+ // Filter: posts starting with / or @ai
|
||||||
|
+ if (!text.match(/^\/[a-z]/) && !text.match(/^@ai/)) {
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ console.log(`[post] ${event.did}: ${text.substring(0, 50)}...`)
|
||||||
|
+
|
||||||
|
await this.db
|
||||||
|
.insertInto('post')
|
||||||
|
- .values(postsToCreate)
|
||||||
|
+ .values({
|
||||||
|
+ uri: uri,
|
||||||
|
+ cid: event.commit.cid || '',
|
||||||
|
+ indexedAt: new Date().toISOString(),
|
||||||
|
+ })
|
||||||
|
.onConflict((oc) => oc.doNothing())
|
||||||
|
.execute()
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ // Update cursor periodically
|
||||||
|
+ this.cursor = event.time_us
|
||||||
|
+ if (event.time_us % 20 === 0) {
|
||||||
|
+ await this.saveCursor()
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private async loadCursor() {
|
||||||
|
+ const res = await this.db
|
||||||
|
+ .selectFrom('sub_state')
|
||||||
|
+ .selectAll()
|
||||||
|
+ .where('service', '=', 'jetstream')
|
||||||
|
+ .executeTakeFirst()
|
||||||
|
+ if (res) {
|
||||||
|
+ this.cursor = res.cursor
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private async saveCursor() {
|
||||||
|
+ await this.db
|
||||||
|
+ .insertInto('sub_state')
|
||||||
|
+ .values({ service: 'jetstream', cursor: this.cursor })
|
||||||
|
+ .onConflict((oc) => oc.column('service').doUpdateSet({ cursor: this.cursor }))
|
||||||
|
+ .execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||