Compare commits
10 commits
7fa08d4b6d
...
e8a4bf58b1
Author | SHA1 | Date | |
---|---|---|---|
Natsu Kagami | e8a4bf58b1 | ||
Natsu Kagami | f50445e508 | ||
Natsu Kagami | 94141db483 | ||
Natsu Kagami | 1e1bab23e9 | ||
Natsu Kagami | e91c124116 | ||
Natsu Kagami | 2d81a7a590 | ||
Natsu Kagami | 991dd0d93d | ||
Natsu Kagami | 0caed40558 | ||
Natsu Kagami | adfa10b696 | ||
Natsu Kagami | 069dc4fde3 |
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -27,3 +27,7 @@ dist-ssr
|
||||||
.env.dev
|
.env.dev
|
||||||
phanpy-dist.zip
|
phanpy-dist.zip
|
||||||
phanpy-dist.tar.gz
|
phanpy-dist.tar.gz
|
||||||
|
|
||||||
|
# Nix
|
||||||
|
.direnv
|
||||||
|
result
|
||||||
|
|
|
@ -43,7 +43,7 @@ Everything is designed and engineered following my taste and vision. This is a p
|
||||||
|
|
||||||
- **Status actions (reply, boost, favourite, bookmark, etc) are hidden by default**.<br>They only appear in individual status page. This is to reduce clutter and distraction. It may result in lower engagement, but we're not chasing numbers here.
|
- **Status actions (reply, boost, favourite, bookmark, etc) are hidden by default**.<br>They only appear in individual status page. This is to reduce clutter and distraction. It may result in lower engagement, but we're not chasing numbers here.
|
||||||
- **Boost is represented with the rocket icon**.<br>The green double arrow icon (retweet for Twitter) doesn't look right for the term "boost". Green rocket looks weird, so I use purple.
|
- **Boost is represented with the rocket icon**.<br>The green double arrow icon (retweet for Twitter) doesn't look right for the term "boost". Green rocket looks weird, so I use purple.
|
||||||
- **Short usernames (`@username`) are displayed in timelines, instead of the full account username (`@username@instance`)**.<br>Despite the [guideline](https://docs.joinmastodon.org/api/guidelines/#username) mentioned that "Decentralization must be transparent to the user", I don't think we should shove it to the face every single time. There are also some [screen-reader-related accessibility concerns](https://twitter.com/lifeofablindgrl/status/1595864647554502656) with the full username, though this web app is unfortunately not accessible yet.
|
- ~~**Short usernames (`@username`) are displayed in timelines, instead of the full account username (`@username@instance`)**.<br>Despite the [guideline](https://docs.joinmastodon.org/api/guidelines/#username) mentioned that "Decentralization must be transparent to the user", I don't think we should shove it to the face every single time. There are also some [screen-reader-related accessibility concerns](https://twitter.com/lifeofablindgrl/status/1595864647554502656) with the full username, though this web app is unfortunately not accessible yet.~~ DTTHDon fork displays the full username by default.
|
||||||
- **No autoplay for video/GIF/whatever in timeline**.<br>The timeline is already a huge mess with lots of people, brands, news and media trying to grab your attention. Let's not make it worse. (Current exception now would be animated emojis.)
|
- **No autoplay for video/GIF/whatever in timeline**.<br>The timeline is already a huge mess with lots of people, brands, news and media trying to grab your attention. Let's not make it worse. (Current exception now would be animated emojis.)
|
||||||
- **Hash-based URLs**.<br>This web app is not meant to be a full-fledged replacement to Mastodon's existing front-end. There's no SEO, database, serverless or any long-running servers. I could be wrong one day.
|
- **Hash-based URLs**.<br>This web app is not meant to be a full-fledged replacement to Mastodon's existing front-end. There's no SEO, database, serverless or any long-running servers. I could be wrong one day.
|
||||||
|
|
||||||
|
|
61
flake.lock
Normal file
61
flake.lock
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1694529238,
|
||||||
|
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1711703276,
|
||||||
|
"narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=",
|
||||||
|
"owner": "nixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "d8fe5e6c92d0d190646fb9f1056741a229980089",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
42
flake.nix
Normal file
42
flake.nix
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
inputs.nixpkgs.url = github:nixOS/nixpkgs/nixos-unstable;
|
||||||
|
inputs.flake-utils.url = github:numtide/flake-utils;
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
lib = pkgs.lib;
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
packages.default = pkgs.buildNpmPackage {
|
||||||
|
pname = "dtth-phanpy";
|
||||||
|
version = "0.1.0";
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [ git ];
|
||||||
|
ESBUILD_BINARY_PATH = lib.getExe pkgs.esbuild;
|
||||||
|
|
||||||
|
src = lib.cleanSource ./.;
|
||||||
|
|
||||||
|
npmFlags = [ "--legacy-peer-deps" ];
|
||||||
|
npmDepsHash = "sha256-9tcZ3jQg+mHeI2K/dNFs0C1k7CfHwxbx+/+I8pdO/wQ=";
|
||||||
|
# npmDepsHash = lib.fakeHash;
|
||||||
|
|
||||||
|
# DTTH-specific env variables
|
||||||
|
PHANPY_CLIENT_NAME = "DTTH Phanpy";
|
||||||
|
PHANPY_CLIENT_ID = "ch.dtth.phanpy";
|
||||||
|
PHANPY_WEBSITE = "https://social.dtth.ch";
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
mkdir -p $out/lib
|
||||||
|
cp -r dist $out/lib/phanpy
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
inputsFrom = [ packages.default ];
|
||||||
|
buildInputs = with pkgs; [ nodejs ];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
|
@ -2780,8 +2780,8 @@ ul.link-list li a .icon {
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
} */
|
} */
|
||||||
li.timeline-item-carousel {
|
li.timeline-item-carousel {
|
||||||
width: 95vw;
|
/* width: 95vw;
|
||||||
max-width: calc(320px * 3.3);
|
max-width: calc(320px * 3.3); */
|
||||||
transform: translateX(calc(-50% + var(--main-width) / 2));
|
transform: translateX(calc(-50% + var(--main-width) / 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,4 +108,5 @@ export const ICONS = {
|
||||||
settings: () => import('@iconify-icons/mingcute/settings-6-line'),
|
settings: () => import('@iconify-icons/mingcute/settings-6-line'),
|
||||||
'heart-break': () => import('@iconify-icons/mingcute/heart-crack-line'),
|
'heart-break': () => import('@iconify-icons/mingcute/heart-crack-line'),
|
||||||
'user-x': () => import('@iconify-icons/mingcute/user-x-line'),
|
'user-x': () => import('@iconify-icons/mingcute/user-x-line'),
|
||||||
|
'user-setting': () => import('@iconify-icons/mingcute/user-setting-line'),
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,6 +26,9 @@ a.name-text.short:is(:hover, :focus) i {
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
.name-text i.instance {
|
||||||
|
opacity: 0.35;
|
||||||
|
}
|
||||||
|
|
||||||
.name-text .avatar {
|
.name-text .avatar {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
|
@ -76,12 +76,13 @@ function NameText({
|
||||||
<b>
|
<b>
|
||||||
<EmojiText text={displayName} emojis={emojis} />
|
<EmojiText text={displayName} emojis={emojis} />
|
||||||
</b>
|
</b>
|
||||||
{!showAcct && username && (
|
{!showAcct && username ? (
|
||||||
<>
|
<>
|
||||||
{' '}
|
{' '}
|
||||||
<i>@{username}</i>
|
<i>@{username}</i>
|
||||||
</>
|
</>
|
||||||
)}
|
) : ' '}
|
||||||
|
<i class="instance">{acct2}</i>
|
||||||
</>
|
</>
|
||||||
) : short ? (
|
) : short ? (
|
||||||
<i>{username}</i>
|
<i>{username}</i>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import './nav-menu.css';
|
import './nav-menu.css';
|
||||||
|
|
||||||
import { ControlledMenu, MenuDivider, MenuItem } from '@szhsin/react-menu';
|
import { ControlledMenu, FocusableItem, MenuDivider, MenuItem } from '@szhsin/react-menu';
|
||||||
import { memo } from 'preact/compat';
|
import { memo } from 'preact/compat';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
||||||
import { useLongPress } from 'use-long-press';
|
import { useLongPress } from 'use-long-press';
|
||||||
|
@ -18,6 +18,7 @@ import Avatar from './avatar';
|
||||||
import Icon from './icon';
|
import Icon from './icon';
|
||||||
import MenuLink from './menu-link';
|
import MenuLink from './menu-link';
|
||||||
import SubMenu2 from './submenu2';
|
import SubMenu2 from './submenu2';
|
||||||
|
import { accountsIsDtth, gtsDtthSettings } from '../utils/dtth';
|
||||||
|
|
||||||
function NavMenu(props) {
|
function NavMenu(props) {
|
||||||
const snapStates = useSnapshot(states);
|
const snapStates = useSnapshot(states);
|
||||||
|
@ -209,6 +210,10 @@ function NavMenu(props) {
|
||||||
<Icon icon="user" size="l" /> <span>Profile</span>
|
<Icon icon="user" size="l" /> <span>Profile</span>
|
||||||
</MenuLink>
|
</MenuLink>
|
||||||
)}
|
)}
|
||||||
|
{currentAccount && accountsIsDtth(currentAccount) &&
|
||||||
|
<FocusableItem title="Takes you to DTTHDon settings">
|
||||||
|
<a href={gtsDtthSettings} target='_blank'><Icon icon="user-setting" size="l" /> <span>User Settings…</span></a>
|
||||||
|
</FocusableItem>}
|
||||||
{lists?.length > 0 ? (
|
{lists?.length > 0 ? (
|
||||||
<SubMenu2
|
<SubMenu2
|
||||||
menuClassName="nav-submenu"
|
menuClassName="nav-submenu"
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
--sai-left: env(safe-area-inset-left);
|
--sai-left: env(safe-area-inset-left);
|
||||||
|
|
||||||
--text-size: 16px;
|
--text-size: 16px;
|
||||||
--main-width: 40em;
|
--main-width: max(60dvw, 40em);
|
||||||
text-size-adjust: none;
|
text-size-adjust: none;
|
||||||
--hairline-width: 1px;
|
--hairline-width: 1px;
|
||||||
--monospace-font: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono',
|
--monospace-font: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono',
|
||||||
|
|
|
@ -11,6 +11,7 @@ import instancesListURL from '../data/instances.json?url';
|
||||||
import { getAuthorizationURL, registerApplication } from '../utils/auth';
|
import { getAuthorizationURL, registerApplication } from '../utils/auth';
|
||||||
import store from '../utils/store';
|
import store from '../utils/store';
|
||||||
import useTitle from '../utils/useTitle';
|
import useTitle from '../utils/useTitle';
|
||||||
|
import { gtsDtth } from '../utils/dtth';
|
||||||
|
|
||||||
const { PHANPY_DEFAULT_INSTANCE: DEFAULT_INSTANCE } = import.meta.env;
|
const { PHANPY_DEFAULT_INSTANCE: DEFAULT_INSTANCE } = import.meta.env;
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ function Login() {
|
||||||
const instance = searchParams.get('instance');
|
const instance = searchParams.get('instance');
|
||||||
const submit = searchParams.get('submit');
|
const submit = searchParams.get('submit');
|
||||||
const [instanceText, setInstanceText] = useState(
|
const [instanceText, setInstanceText] = useState(
|
||||||
instance || cachedInstanceURL?.toLowerCase() || '',
|
instance || cachedInstanceURL?.toLowerCase() || gtsDtth,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [instancesList, setInstancesList] = useState([]);
|
const [instancesList, setInstancesList] = useState([]);
|
||||||
|
|
|
@ -603,14 +603,18 @@ function Settings({ onClose }) {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@phanpy
|
@phanpy
|
||||||
|
</a> (
|
||||||
|
<a href="https://git.dtth.ch/nki/phanpy" target="_blank">
|
||||||
|
DTTH Fork
|
||||||
</a>
|
</a>
|
||||||
|
)
|
||||||
<br />
|
<br />
|
||||||
<a
|
<a
|
||||||
href="https://github.com/cheeaun/phanpy"
|
href="https://github.com/cheeaun/phanpy"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
Built
|
Original
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
by{' '}
|
by{' '}
|
||||||
<a
|
<a
|
||||||
|
@ -665,10 +669,10 @@ function Settings({ onClose }) {
|
||||||
type="text"
|
type="text"
|
||||||
class="version-string"
|
class="version-string"
|
||||||
readOnly
|
readOnly
|
||||||
size="18" // Manually calculated here
|
size={10 /* build time */ + (1+8) /* commit hash */ + '-dtth'.length}
|
||||||
value={`${__BUILD_TIME__.slice(0, 10).replace(/-/g, '.')}${
|
value={`${__BUILD_TIME__.slice(0, 10).replace(/-/g, '.')}${
|
||||||
__COMMIT_HASH__ ? `.${__COMMIT_HASH__}` : ''
|
__COMMIT_HASH__ ? `.${__COMMIT_HASH__.slice(0, 8)}` : ''
|
||||||
}`}
|
}-dtth`}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.target.select();
|
e.target.select();
|
||||||
// Copy to clipboard
|
// Copy to clipboard
|
||||||
|
@ -685,7 +689,7 @@ function Settings({ onClose }) {
|
||||||
<span class="ib insignificant">
|
<span class="ib insignificant">
|
||||||
(
|
(
|
||||||
<a
|
<a
|
||||||
href={`https://github.com/cheeaun/phanpy/commit/${__COMMIT_HASH__}`}
|
href={`https://git.dtth.ch/nki/phanpy/commit/${__COMMIT_HASH__}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
|
|
|
@ -104,6 +104,7 @@ function Welcome() {
|
||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
|
<p class="desc">A minimalistic opinionated DTTHDon web client.</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="why-container">
|
<div id="why-container">
|
||||||
<div class="sections">
|
<div class="sections">
|
||||||
|
@ -162,6 +163,38 @@ function Welcome() {
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<p>
|
||||||
|
<a href="https://git.dtth.ch/nki/phanpy" target="_blank">
|
||||||
|
DTTHDon Fork
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://github.com/cheeaun/phanpy" target="_blank">
|
||||||
|
Original built
|
||||||
|
</a>{' '}
|
||||||
|
by{' '}
|
||||||
|
<a
|
||||||
|
href="https://mastodon.social/@cheeaun"
|
||||||
|
target="_blank"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
states.showAccount = 'cheeaun@mastodon.social';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
@cheeaun
|
||||||
|
</a>
|
||||||
|
.{' '}
|
||||||
|
<a
|
||||||
|
href="https://github.com/cheeaun/phanpy/blob/main/PRIVACY.MD"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Privacy Policy
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
13
src/utils/dtth.js
Normal file
13
src/utils/dtth.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export function accountsIsDtth(account) {
|
||||||
|
return (
|
||||||
|
account.info &&
|
||||||
|
typeof account.info.url === 'string' &&
|
||||||
|
account.info.url.startsWith(gtsDtth)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** URL to DTTHDon */
|
||||||
|
export const gtsDtth = 'https://gts.dtth.ch';
|
||||||
|
|
||||||
|
/** URL to DTTHDon settings */
|
||||||
|
export const gtsDtthSettings = 'https://gts.dtth.ch/settings';
|
|
@ -24,8 +24,12 @@ try {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If error, means git is not installed or not a git repo (could be downloaded instead of git cloned)
|
// If error, means git is not installed or not a git repo (could be downloaded instead of git cloned)
|
||||||
// Fallback to random hash which should be different on every build run 🤞
|
// Fallback to random hash which should be different on every build run 🤞
|
||||||
|
if (process.env.PHANPY_COMMIT_HASH) {
|
||||||
|
commitHash = process.env.PHANPY_COMMIT_HASH;
|
||||||
|
} else {
|
||||||
commitHash = uid();
|
commitHash = uid();
|
||||||
fakeCommitHash = true;
|
fakeCommitHash = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rollbarCode = fs.readFileSync(
|
const rollbarCode = fs.readFileSync(
|
||||||
|
|
Loading…
Reference in a new issue