1
0

add apple app store for ios

This commit is contained in:
2025-11-18 13:35:29 +09:00
parent 1823e5060d
commit b6f3036d5c
5 changed files with 576 additions and 38 deletions

View File

@@ -15,7 +15,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 23 node-version: 24
- run: | - run: |
npm install npm install

View File

@@ -0,0 +1,11 @@
{
"applinks": {
"apps": [],
"details": [
{
"appID": "6DQ24D2652.ai.syui.card",
"paths": ["/oauth/callback"]
}
]
}
}

267
public/app.html Normal file
View File

@@ -0,0 +1,267 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aicard - App Store</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f5f5f7;
color: #1d1d1f;
line-height: 1.8;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 40px 20px;
}
.app-header {
text-align: center;
margin-bottom: 40px;
padding-bottom: 30px;
border-bottom: 1px solid #d2d2d7;
}
.app-icon {
width: 120px;
height: 120px;
border-radius: 24px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
margin: 0 auto 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
color: #fff;
box-shadow: 0 10px 40px rgba(102, 126, 234, 0.4);
}
.app-title {
font-size: 36px;
font-weight: 700;
margin-bottom: 8px;
color: #1d1d1f;
}
.app-subtitle {
font-size: 18px;
color: #86868b;
margin-bottom: 20px;
}
.download-btn {
display: inline-block;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
padding: 14px 32px;
border-radius: 12px;
text-decoration: none;
font-weight: 600;
font-size: 16px;
transition: transform 0.2s, box-shadow 0.2s;
}
.download-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
}
.section {
background: #fff;
border-radius: 12px;
padding: 30px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.section-title {
font-size: 20px;
font-weight: 600;
margin-bottom: 16px;
color: #1d1d1f;
padding-bottom: 8px;
border-bottom: 2px solid #667eea;
display: inline-block;
}
.section p {
color: #424245;
margin-bottom: 12px;
}
.feature-list {
list-style: none;
}
.feature-list li {
padding: 12px 0;
border-bottom: 1px solid #d2d2d7;
display: flex;
align-items: center;
gap: 12px;
}
.feature-list li:last-child {
border-bottom: none;
}
.feature-icon {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
}
.feature-text h4 {
font-size: 16px;
font-weight: 600;
margin-bottom: 4px;
color: #1d1d1f;
}
.feature-text p {
font-size: 14px;
color: #424245;
margin: 0;
}
.screenshots {
display: flex;
gap: 16px;
overflow-x: auto;
padding: 10px 0;
}
.screenshot {
min-width: 200px;
height: 360px;
background: #f0f0f5;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: #86868b;
font-size: 14px;
border: 1px solid #d2d2d7;
}
.info-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
.info-item {
text-align: center;
padding: 16px;
background: #f0f0f5;
border-radius: 12px;
}
.info-item .label {
font-size: 12px;
color: #86868b;
text-transform: uppercase;
letter-spacing: 1px;
}
.info-item .value {
font-size: 18px;
font-weight: 600;
margin-top: 4px;
color: #1d1d1f;
}
footer {
text-align: center;
padding: 30px 20px;
color: #86868b;
font-size: 14px;
}
footer a {
color: #667eea;
text-decoration: none;
}
@media (max-width: 600px) {
.info-grid {
grid-template-columns: 1fr;
}
.app-title {
font-size: 28px;
}
.section {
padding: 20px;
}
}
</style>
</head>
<body>
<div class="container">
<header class="app-header">
<div class="app-icon">ai</div>
<h1 class="app-title">Aicard</h1>
<p class="app-subtitle">ai.syui.card</p>
<a href="#" class="download-btn">App Store</a>
</header>
<section class="section">
<h2 class="section-title">App</h2>
<p>
Aicard (iOS) is a card collecting game. You can save your data to your Atproto account. Saved data is linked to Airse (Windows, Mac), an open-world action game.
</p>
<p>
</p>
</section>
<section class="section">
<h2 class="section-title">Function</h2>
<ul class="feature-list">
<li>
<div class="feature-icon"></div>
<div class="feature-text">
<h4>Holographic Effect</h4>
<p>Special cards change color depending on the angle of light.</p>
</div>
</li>
<li>
<div class="feature-icon">🃏</div>
<div class="feature-text">
<h4>Card Collection</h4>
<p>The initial set includes 13 types of cards.</p>
</div>
</li>
<li>
<div class="feature-icon">🌐</div>
<div class="feature-text">
<h4>Bluesky Integration</h4>
<p>Proof of card ownership linked with a decentralized social network.</p>
</div>
</li>
</ul>
</section>
<section class="section">
<h2 class="section-title">App Information</h2>
<div class="info-grid">
<div class="info-item">
<div class="label">Version</div>
<div class="value">1.0.1</div>
</div>
<div class="info-item">
<div class="label">Category</div>
<div class="value">Entertainment</div>
</div>
<div class="info-item">
<div class="label">Supported OS</div>
<div class="value">iOS 26.0+</div>
</div>
<div class="info-item">
<div class="label">Price</div>
<div class="value">Free (Offers in-app purchases)</div>
</div>
</div>
</section>
<section class="section">
<p style="margin-top: 8px;">
<a href="/privacy.html">Privacy Policy</a>
</p>
</section>
<footer>
<p>&copy; syui</p>
<p style="margin-top: 8px;">
</p>
</footer>
</body>
</html>

View File

@@ -21,6 +21,7 @@
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
border-radius: 20px; border-radius: 20px;
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
max-width: 400px;
} }
.spinner { .spinner {
border: 4px solid rgba(255, 255, 255, 0.3); border: 4px solid rgba(255, 255, 255, 0.3);
@@ -35,6 +36,28 @@
0% { transform: rotate(0deg); } 0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); } 100% { transform: rotate(360deg); }
} }
.message {
font-size: 18px;
margin-bottom: 20px;
}
.button {
display: inline-block;
padding: 15px 30px;
font-size: 18px;
font-weight: bold;
color: white;
background: rgba(255, 255, 255, 0.2);
border: 2px solid white;
border-radius: 10px;
text-decoration: none;
transition: all 0.3s;
cursor: pointer;
margin-top: 10px;
}
.button:hover {
background: rgba(255, 255, 255, 0.3);
transform: scale(1.05);
}
.error { .error {
color: #ff6b6b; color: #ff6b6b;
background: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.9);
@@ -45,8 +68,10 @@
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<div id="content">
<div class="spinner"></div> <div class="spinner"></div>
<p id="status">認証中...</p> <p class="message">認証中...</p>
</div>
</div> </div>
<script> <script>
@@ -62,61 +87,45 @@
console.log('OAuth callback page loaded'); console.log('OAuth callback page loaded');
console.log('Params:', { code: code ? 'present' : 'missing', state: state ? 'present' : 'missing' }); console.log('Params:', { code: code ? 'present' : 'missing', state: state ? 'present' : 'missing' });
const content = document.getElementById('content');
// エラーチェック // エラーチェック
if (error) { if (error) {
document.getElementById('status').innerHTML = content.innerHTML = `<div class="error">認証エラー: ${errorDescription || error}</div>`;
`<div class="error">認証エラー: ${errorDescription || error}</div>`;
return; return;
} }
if (!code || !state) { if (!code || !state) {
document.getElementById('status').innerHTML = content.innerHTML = '<div class="error">認証パラメータが不足しています</div>';
'<div class="error">認証パラメータが不足しています</div>';
return; return;
} }
// React Nativeアプリに戻す (deep link) // カスタムスキームのDeep Linkを生成
const deepLink = `aicard://oauth/callback?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state)}`; const deepLink = `aicard://oauth/callback?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state)}`;
console.log('Deep link:', deepLink); console.log('OAuth callback received, attempting app launch...');
document.getElementById('status').innerHTML = 'アプリに戻っています...<br><small>' + deepLink + '</small>'; console.log('Universal Link should trigger automatically if configured correctly');
// Method 1: iframe approach // 即座にカスタムスキームへのリダイレクトを試行(フォールバック)
const iframe = document.createElement('iframe'); // Universal Linksが動作すればこのコードに到達する前にアプリが開く
iframe.style.display = 'none'; // 動作しない場合はカスタムスキームで開く
iframe.src = deepLink;
document.body.appendChild(iframe);
// Method 2: Direct window.location
setTimeout(() => { setTimeout(() => {
console.log('Attempting window.location redirect'); console.log('Attempting fallback custom scheme redirect');
window.location.href = deepLink; window.location.href = deepLink;
}, 500); }, 500);
// Method 3: Link click simulation // 2秒後に手動ボタンを表示念のため
setTimeout(() => { setTimeout(() => {
console.log('Attempting link click'); content.innerHTML = `
const link = document.createElement('a'); <div class="message">認証が完了しました</div>
link.href = deepLink; <p style="font-size: 14px; opacity: 0.9;">アプリが自動的に開かない場合は、<br>下のボタンをタップしてください</p>
link.click(); <a href="${deepLink}" class="button">アプリを開く</a>
}, 1000); `;
}, 2000);
// Method 4: Window.open
setTimeout(() => {
console.log('Attempting window.open');
window.open(deepLink, '_self');
}, 1500);
// フォールバック: 5秒後にメッセージ表示
setTimeout(() => {
document.getElementById('status').innerHTML =
'<div class="error">アプリが開かない場合は、下のリンクをタップしてください<br><br>' +
'<a href="' + deepLink + '" style="color: white; text-decoration: underline; font-size: 18px;">Aicardアプリを開く</a></div>';
}, 5000);
} catch (err) { } catch (err) {
console.error('Callback error:', err); console.error('Callback error:', err);
document.getElementById('status').innerHTML = document.getElementById('content').innerHTML =
`<div class="error">エラーが発生しました: ${err.message}</div>`; `<div class="error">エラーが発生しました: ${err.message}</div>`;
} }
})(); })();

251
public/privacy.html Normal file
View File

@@ -0,0 +1,251 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Privacy Policy - Aicard</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f5f5f7;
color: #1d1d1f;
line-height: 1.8;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 40px 20px;
}
header {
text-align: center;
margin-bottom: 40px;
padding-bottom: 30px;
border-bottom: 1px solid #d2d2d7;
}
h1 {
font-size: 32px;
font-weight: 700;
margin-bottom: 8px;
color: #1d1d1f;
}
.app-name {
font-size: 16px;
color: #86868b;
}
.last-updated {
font-size: 14px;
color: #86868b;
margin-top: 12px;
}
.section {
background: #fff;
border-radius: 12px;
padding: 30px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
h2 {
font-size: 20px;
font-weight: 600;
margin-bottom: 16px;
color: #1d1d1f;
padding-bottom: 8px;
border-bottom: 2px solid #667eea;
display: inline-block;
}
p {
margin-bottom: 16px;
color: #424245;
}
ul {
margin: 16px 0;
padding-left: 24px;
}
li {
margin-bottom: 8px;
color: #424245;
}
.highlight {
background: #f0f0f5;
padding: 16px 20px;
border-radius: 8px;
margin: 16px 0;
border-left: 4px solid #667eea;
}
.contact-info {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
padding: 24px;
border-radius: 12px;
margin-top: 8px;
}
.contact-info a {
color: #fff;
text-decoration: underline;
}
footer {
text-align: center;
padding: 30px 20px;
color: #86868b;
font-size: 14px;
}
footer a {
color: #667eea;
text-decoration: none;
}
@media (max-width: 600px) {
h1 {
font-size: 26px;
}
.section {
padding: 20px;
}
}
</style>
<body>
<div class="container">
<header>
<h1>Privacy Policy</h1>
<p class="app-name">Aicard</p>
<p class="last-updated">update 2025.11.23</p>
</header>
<section class="section">
<h2>Introduction</h2>
<p>
This Privacy Policy explains how Aicard (hereinafter referred to as “the App”) handles personal information.
Please read this policy carefully before using the App.
</p>
</section>
<section class="section">
<h2>Information We Collect</h2>
<p>The App may collect and use the following information:</p>
<h3 style="font-size: 16px; margin: 16px 0 8px; color: #1d1d1f;">1. Information Collected Automatically</h3>
<ul>
<li>Device information (model, OS version)</li>
<li>App usage data (launch count, viewed cards, etc.)</li>
<li>Crash logs and performance data</li>
</ul>
<h3 style="font-size: 16px; margin: 16px 0 8px; color: #1d1d1f;">2. Information Provided by Users</h3>
<ul>
<li>DID (Decentralized Identifier) when linking a Bluesky account</li>
<li>Favorite card settings</li>
</ul>
<div class="highlight">
<strong>Important:</strong> The App does not collect information that directly identifies an individual, such as name, email address, or phone number.
</div>
</section>
<section class="section">
<h2>Purpose of Use</h2>
<p>The collected information is used for the following purposes:</p>
<ul>
<li>Providing app functionality and improving services</li>
<li>Offering the card collection feature</li>
<li>Enhancing user experience</li>
<li>Diagnosing and resolving technical issues</li>
<li>Preventing fraudulent activities</li>
</ul>
</section>
<section class="section">
<h2>Information Sharing</h2>
<p>
The App does not share collected information with third parties except in the following cases:
</p>
<ul>
<li>When user consent is obtained</li>
<li>When disclosure is required by law</li>
<li>When sharing with service providers necessary for operation (under appropriate agreements)</li>
</ul>
</section>
<section class="section">
<h2>Data Storage</h2>
<p>
User data such as favorite settings is primarily stored on your device.
Data related to Bluesky integration is managed on Bluesky's servers.
</p>
<p>
Any data stored on the Apps servers is protected with appropriate security measures.
</p>
</section>
<section class="section">
<h2>External Services</h2>
<p>The App may integrate with the following external services:</p>
<ul>
<li><strong>Bluesky (AT Protocol)</strong> Card ownership verification and user authentication</li>
<li><strong>api.syui.ai</strong> Retrieving card data</li>
</ul>
<p>
Each external service has its own privacy policy.
</p>
</section>
<section class="section">
<h2>User Rights</h2>
<p>Users have the following rights:</p>
<ul>
<li>Right to access their data</li>
<li>Right to request correction or deletion</li>
<li>Right to withdraw consent to data processing</li>
</ul>
<p>
To exercise these rights, please contact us using the information below.
</p>
</section>
<section class="section">
<h2>Childrens Privacy</h2>
<p>
The App does not intentionally collect personal information from children under the age of 13.
Children under 13 should use the App only with parental consent.
</p>
</section>
<section class="section">
<h2>Changes to This Policy</h2>
<p>
This policy may be updated as necessary.
In the event of significant changes, we will notify users within the App or on the website.
By continuing to use the App after changes are made, you are deemed to have accepted the updated policy.
</p>
</section>
<section class="section">
<h2>Contact</h2>
<p>
For questions or inquiries regarding this Privacy Policy, please contact us at:
</p>
<div class="contact-info">
<p style="margin-bottom: 8px; color: #fff;">
<strong>Aicard Support</strong>
</p>
<p style="margin: 0; color: #fff;">
<a href="https://github.com/syui">syui</a>
</p>
</div>
</section>
</div>
<footer>
<p>&copy; syui</p>
<p style="margin-top: 8px;">
<a href="/app.html">Back to App Introduction</a>
</p>
</footer>
</body>
</html>