Merge patchset from akko branch

This commit is contained in:
Laura Hausmann
2023-10-12 20:13:42 +02:00
parent d235afc303
commit 960614ec6f
22 changed files with 350 additions and 69 deletions

101
public/auth.js Normal file
View File

@@ -0,0 +1,101 @@
document.addEventListener("DOMContentLoaded", async function() {
await ready();
});
async function ready() {
const domain = localStorage.getItem('domain');
let accessToken = localStorage.getItem(`access_token`);
if (domain) document.getElementById('instance').value = domain;
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
if (domain && code && !accessToken) await getToken(code, domain).then(res => accessToken = res);
if (accessToken) {
window.location.href = '/prepare.html';
}
}
async function auth() {
setMessage('Please wait');
const instance = document.getElementById('instance').value;
const domain = instance.match(/(?:https?:\/\/)?(.*)/)[1];
if (!domain) {
setMessage('Invalid instance', false);
return;
}
localStorage.setItem('domain', domain);
// We need to run this every time in cases like Iceshrimp, where the client id/secret aren't reusable (yet) because they contain use-once session information
await registerApp(domain);
authorize(domain);
}
async function registerApp(domain) {
setMessage('Registering app');
const appsUrl = `https://${domain}/api/v1/apps`;
const formData = new FormData();
formData.append('client_name', 'Masto-FE standalone');
formData.append('redirect_uris', document.location.origin + document.location.pathname);
formData.append('scopes', 'read write follow push');
// eslint-disable-next-line promise/catch-or-return
await fetch(appsUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams(formData),
})
.then(async res => {
const app = await res.json();
localStorage.setItem(`client_id`, app.client_id);
localStorage.setItem(`client_secret`, app.client_secret);
});
}
function authorize(domain) {
setMessage('Authorizing');
const clientId = localStorage.getItem(`client_id`);
document.location.href = `https://${domain}/oauth/authorize?response_type=code&client_id=${clientId}&redirect_uri=${document.location.origin + document.location.pathname}&scope=read+write+follow+push`;
}
async function getToken(code, domain) {
setMessage('Getting token');
const tokenUrl = `https://${domain}/oauth/token`;
const clientId = localStorage.getItem(`client_id`);
const clientSecret = localStorage.getItem(`client_secret`);
const formData = new FormData();
formData.append('grant_type', 'authorization_code');
formData.append('code', code);
formData.append('client_id', clientId);
formData.append('client_secret', clientSecret);
formData.append('scope', 'read write follow push');
formData.append('redirect_uri', document.location.origin + document.location.pathname);
// eslint-disable-next-line promise/catch-or-return
return fetch(tokenUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams(formData),
})
.then(async res => {
const app = await res.json();
if (app.access_token) localStorage.setItem(`access_token`, app.access_token);
return app.access_token;
});
}
function setMessage(message, disabled = true) {
document.getElementById('message').textContent = message;
document.getElementById('btn').disabled = disabled;
}

11
public/images/mascot.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 29 KiB

33
public/index.html Normal file
View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta content='width=device-width, initial-scale=1' name='viewport'>
<title>Masto-FE standalone</title>
<link rel="manifest" type="applicaton/manifest+json" href="/manifest.json" />
<meta name="theme-color" content="#282c37" />
<script crossorigin='anonymous' src="/packs/js/locales.js"></script>
<script crossorigin='anonymous' src="/packs/js/locales/glitch/en.js"></script>
<link rel='preload' as='script' href='/packs/js/flavours/glitch/async/getting_started.js'>
<link rel='preload' as='script' href='/packs/js/flavours/glitch/async/compose.js'>
<link rel='preload' as='script' href='/packs/js/flavours/glitch/async/home_timeline.js'>
<link rel='preload' as='script' href='/packs/js/flavours/glitch/async/notifications.js'>
<script id='initial-state' type='application/json'>{}</script>
<script src="/verify-state.js"></script>
<script src="/packs/js/core/common.js"></script>
<link rel="stylesheet" media="all" href="/packs/css/core/common.css" />
<script src="/packs/js/flavours/glitch/common.js"></script>
<link rel="stylesheet" media="all" href="/packs/css/flavours/glitch/common.css" />
<script src="/packs/js/flavours/glitch/home.js"></script>
</head>
<body class='app-body no-reduce-motion system-font'>
<div class='app-holder' data-props='{&quot;locale&quot;:&quot;en&quot;}' id='mastodon'>
</div>
</body>
</html>

13
public/login.html Normal file
View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login | Masto-FE standalone</title>
<script src="/auth.js"></script>
</head>
<body>
<input type="text" id="instance" placeholder="yourinstance.tld">
<button onclick="auth()" id="btn">Log in</button>
<span id="message"></span>
</body>
</html>

14
public/logout.html Normal file
View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Logout | Masto-FE standalone</title>
<script>
localStorage.clear();
window.location.href = "/login.html";
</script>
</head>
<body>
Clearing local storage and redirecting back to <a href="/login.html">login</a>...
</body>
</html>

12
public/manifest.json Normal file
View File

@@ -0,0 +1,12 @@
{
"background_color": "#191b22",
"categories": ["social"],
"description": "Masto-FE standalone",
"display": "standalone",
"name": "Masto-FE standalone",
"serviceworker": {
"src": "/sw.js"
},
"start_url": "/getting-started",
"theme_color": "#282c37"
}

11
public/prepare.html Normal file
View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login | Masto-FE standalone</title>
<script src="/verify-state.js"></script>
</head>
<body>
<p>Preparing state object...</p>
</body>
</html>

103
public/verify-state.js Normal file
View File

@@ -0,0 +1,103 @@
loadState().then(_ => null);
async function loadState() {
const domain = localStorage.getItem('domain');
const access_token = localStorage.getItem('access_token');
const storedState = localStorage.getItem('initial_state');
if (!domain || !access_token) {
window.location.href = '/login.html';
return;
}
if (storedState && window.location.pathname !== '/prepare.html') {
document.getElementById('initial-state').textContent = storedState;
}
const apiUrl = `https://${domain}/api`;
const instance = await fetch(`${apiUrl}/v1/instance`).then(async p => await p.json());
const options = {headers: {Authorization: `Bearer ${access_token}`}};
const credentials = await fetch(`${apiUrl}/v1/accounts/verify_credentials`, options).then(async p => await p.json());
const state = {
"accounts": {
"plc":{
"accepts_direct_messages_from":"everybody",
"acct": credentials.acct,
"avatar": credentials.avatar,
"avatar_static": credentials.avatar_static,
"bot": credentials.bot,
"created_at": credentials.created_at,
"display_name": credentials.display_name,
"emojis":[],
"fields":[],
"follow_requests_count":0,
"followers_count": credentials.followers_count,
"following_count": credentials.following_count,
"fqn":`${credentials.acct}@${domain}`,
"header": credentials.header,
"header_static": credentials.header_static,
"id": credentials.id,
"last_status_at": credentials.created_at,
"locked": credentials.locked,
"note":"",
"source": credentials.source,
"statuses_count": credentials.statuses_count,
"url": credentials.url,
"username": credentials.acct
}
},
"char_limit": instance.configuration.statuses.max_characters,
"compose": {
"allow_content_types": [
"text/x.misskeymarkdown"
],
"default_privacy": credentials.source.privacy,
"default_sensitive": credentials.source.sensitive,
"me": credentials.id
},
"media_attachments": {
"accept_content_types": instance.configuration.media_attachments.supported_mime_types
},
"meta": {
"access_token": access_token,
"admin": "0",
"advanced_layout": true,
"auto_play_gif": false,
"boost_modal": false,
"compact_reaction": false,
"delete_modal": true,
"display_sensitive_media": false,
"domain": domain,
"enable_reaction": true,
"locale": "en",
"mascot": "/images/mascot.svg",
"max_toot_chars": instance.configuration.statuses.max_characters,
"me": credentials.id,
"reduce_motion": false,
"show_quote_button": true,
"base_url": `https://${domain}`,
"streaming_api_base_url": `wss://${domain}`,
"title": `${instance.title}`,
"unfollow_modal": true,
"source_url": 'https://iceshrimp.dev/iceshrimp/masto-fe-standalone',
"version": instance.version
},
"poll_limits": {
"max_expiration": instance.configuration.polls.max_expiration,
"max_option_chars": instance.configuration.polls.max_characters_per_option,
"max_options": instance.configuration.polls.max_options,
"min_expiration": instance.configuration.polls.min_expiration
},
"push_subscription": null,
"rights": {
"admin": false,
"delete_others_notice": false
},
"settings": {}
};
const json = JSON.stringify(state);
if (window.location.pathname !== '/prepare.html') document.getElementById('initial-state').textContent = json;
localStorage.setItem("initial_state", json);
if (window.location.pathname === '/prepare.html') window.location.href = '/';
}