mirror of
https://github.com/cheeaun/phanpy.git
synced 2024-11-22 17:25:40 +03:00
Add account info into Account statuses page
This commit is contained in:
parent
b4f8f92431
commit
6fd9c106c6
8 changed files with 401 additions and 194 deletions
|
@ -16,7 +16,7 @@ import {
|
|||
} from 'react-router-dom';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import Account from './components/account';
|
||||
import AccountSheet from './components/account-sheet';
|
||||
import Compose from './components/compose';
|
||||
import Drafts from './components/drafts';
|
||||
import Loader from './components/loader';
|
||||
|
@ -409,7 +409,7 @@ function App() {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<Account
|
||||
<AccountSheet
|
||||
account={snapStates.showAccount?.account || snapStates.showAccount}
|
||||
instance={snapStates.showAccount?.instance}
|
||||
onClose={() => {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import './account-block.css';
|
||||
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import emojifyText from '../utils/emojify-text';
|
||||
import niceDateTime from '../utils/nice-date-time';
|
||||
import states from '../utils/states';
|
||||
|
@ -12,6 +14,7 @@ function AccountBlock({
|
|||
avatarSize = 'xl',
|
||||
instance,
|
||||
external,
|
||||
internal,
|
||||
onClick,
|
||||
showActivity = false,
|
||||
}) {
|
||||
|
@ -22,13 +25,16 @@ function AccountBlock({
|
|||
<span>
|
||||
<b>████████</b>
|
||||
<br />
|
||||
@██████
|
||||
<span class="account-block-acct">@██████</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
id,
|
||||
acct,
|
||||
avatar,
|
||||
avatarStatic,
|
||||
|
@ -40,6 +46,7 @@ function AccountBlock({
|
|||
lastStatusAt,
|
||||
} = account;
|
||||
const displayNameWithEmoji = emojifyText(displayName, emojis);
|
||||
const [_, acct1, acct2] = acct.match(/([^@]+)(@.+)/i) || [, acct];
|
||||
|
||||
return (
|
||||
<a
|
||||
|
@ -51,10 +58,14 @@ function AccountBlock({
|
|||
if (external) return;
|
||||
e.preventDefault();
|
||||
if (onClick) return onClick(e);
|
||||
states.showAccount = {
|
||||
account,
|
||||
instance,
|
||||
};
|
||||
if (internal) {
|
||||
navigate(`/${instance}/a/${id}`);
|
||||
} else {
|
||||
states.showAccount = {
|
||||
account,
|
||||
instance,
|
||||
};
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Avatar url={avatar} size={avatarSize} />
|
||||
|
@ -68,7 +79,12 @@ function AccountBlock({
|
|||
) : (
|
||||
<b>{username}</b>
|
||||
)}
|
||||
<br />@{acct}
|
||||
<br />
|
||||
<span class="account-block-acct">
|
||||
@{acct1}
|
||||
<wbr />
|
||||
{acct2}
|
||||
</span>
|
||||
{showActivity && (
|
||||
<>
|
||||
<br />
|
||||
|
|
225
src/components/account-info.css
Normal file
225
src/components/account-info.css
Normal file
|
@ -0,0 +1,225 @@
|
|||
.account-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.account-container.skeleton {
|
||||
color: var(--outline-color);
|
||||
}
|
||||
|
||||
.account-container .header-banner {
|
||||
/* pointer-events: none; */
|
||||
aspect-ratio: 6 / 1;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
/* mask fade out bottom of banner */
|
||||
mask-image: linear-gradient(
|
||||
to bottom,
|
||||
hsl(0, 0%, 0%) 0%,
|
||||
hsla(0, 0%, 0%, 0.987) 14%,
|
||||
hsla(0, 0%, 0%, 0.951) 26.2%,
|
||||
hsla(0, 0%, 0%, 0.896) 36.8%,
|
||||
hsla(0, 0%, 0%, 0.825) 45.9%,
|
||||
hsla(0, 0%, 0%, 0.741) 53.7%,
|
||||
hsla(0, 0%, 0%, 0.648) 60.4%,
|
||||
hsla(0, 0%, 0%, 0.55) 66.2%,
|
||||
hsla(0, 0%, 0%, 0.45) 71.2%,
|
||||
hsla(0, 0%, 0%, 0.352) 75.6%,
|
||||
hsla(0, 0%, 0%, 0.259) 79.6%,
|
||||
hsla(0, 0%, 0%, 0.175) 83.4%,
|
||||
hsla(0, 0%, 0%, 0.104) 87.2%,
|
||||
hsla(0, 0%, 0%, 0.049) 91.1%,
|
||||
hsla(0, 0%, 0%, 0.013) 95.3%,
|
||||
hsla(0, 0%, 0%, 0) 100%
|
||||
);
|
||||
margin-bottom: -44px;
|
||||
}
|
||||
.account-container .header-banner:hover {
|
||||
animation: position-object 5s ease-in-out 1s 5;
|
||||
}
|
||||
|
||||
@media (min-height: 480px) {
|
||||
.account-container .header-banner {
|
||||
aspect-ratio: 3 / 1;
|
||||
}
|
||||
}
|
||||
|
||||
.account-container header {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
text-shadow: -8px 0 12px -6px var(--bg-color), 8px 0 12px -6px var(--bg-color),
|
||||
-8px 0 24px var(--header-color-3, --bg-color),
|
||||
8px 0 24px var(--header-color-4, --bg-color);
|
||||
}
|
||||
.account-container header .avatar {
|
||||
box-shadow: -8px 0 24px var(--header-color-3, --bg-color),
|
||||
8px 0 24px var(--header-color-4, --bg-color);
|
||||
}
|
||||
|
||||
.account-container .note {
|
||||
font-size: 95%;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.account-container .note:not(:has(p)):not(:empty) {
|
||||
/* Some notes don't have <p> tags, so we need to add some padding */
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
.account-container .stats {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
gap: 16px;
|
||||
opacity: 0.75;
|
||||
font-size: 90%;
|
||||
background-color: var(--bg-faded-color);
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.account-container .stats > * {
|
||||
text-align: center;
|
||||
}
|
||||
.account-container .stats a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.account-container .actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
min-height: 2.5em;
|
||||
}
|
||||
.account-container .actions button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.account-container .profile-metadata {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
.account-container .profile-field {
|
||||
min-width: 0;
|
||||
flex-grow: 1;
|
||||
font-size: 90%;
|
||||
background-color: var(--bg-faded-color);
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
filter: saturate(0.75);
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.account-container :is(.note, .profile-field) .invisible {
|
||||
display: none;
|
||||
}
|
||||
.account-container :is(.note, .profile-field) .ellipsis::after {
|
||||
content: '…';
|
||||
}
|
||||
|
||||
.account-container .profile-field b {
|
||||
font-size: 90%;
|
||||
color: var(--text-insignificant-color);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.account-container .profile-field b .icon {
|
||||
color: var(--green-color);
|
||||
}
|
||||
.account-container .profile-field p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.account-container .common-followers {
|
||||
border-top: 1px solid var(--outline-color);
|
||||
border-bottom: 1px solid var(--outline-color);
|
||||
padding: 8px 0;
|
||||
font-size: 90%;
|
||||
line-height: 1.5;
|
||||
color: var(--text-insignificant-color);
|
||||
}
|
||||
|
||||
.timeline-start .account-container {
|
||||
border-bottom: 1px solid var(--outline-color);
|
||||
}
|
||||
.timeline-start .account-container :is(header, main) {
|
||||
padding: 16px 16px 4px;
|
||||
}
|
||||
.timeline-start .account-container .account-block .account-block-acct {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.timeline-start .account-container .actions {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
0% {
|
||||
left: -100%;
|
||||
}
|
||||
100% {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
.timeline-start .account-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.timeline-start .account-container:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: linear-gradient(
|
||||
100deg,
|
||||
rgba(255, 255, 255, 0) 30%,
|
||||
rgba(255, 255, 255, 0.25),
|
||||
rgba(255, 255, 255, 0) 70%
|
||||
);
|
||||
top: 0;
|
||||
left: -100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
.timeline-start .account-container:hover:before {
|
||||
animation: shine 1s ease-in-out 1s;
|
||||
}
|
||||
|
||||
@media (min-width: 40em) {
|
||||
.timeline-start .account-container {
|
||||
--item-radius: 16px;
|
||||
border: 1px solid var(--divider-color);
|
||||
margin: 16px 0;
|
||||
background-color: var(--bg-color);
|
||||
border-radius: var(--item-radius);
|
||||
overflow: hidden;
|
||||
/* box-shadow: 0px 1px var(--bg-blur-color), 0 0 64px var(--bg-color); */
|
||||
--shadow-offset: 16px;
|
||||
--shadow-blur: 32px;
|
||||
--shadow-spread: calc(var(--shadow-blur) * -0.75);
|
||||
box-shadow: calc(var(--shadow-offset) * -1) var(--shadow-offset)
|
||||
var(--shadow-blur) var(--shadow-spread)
|
||||
var(--header-color-1, var(--drop-shadow-color)),
|
||||
var(--shadow-offset) var(--shadow-offset) var(--shadow-blur)
|
||||
var(--shadow-spread) var(--header-color-2, var(--drop-shadow-color));
|
||||
}
|
||||
.timeline-start .account-container .header-banner {
|
||||
margin-bottom: -77px;
|
||||
}
|
||||
.timeline-start .account-container header .account-block {
|
||||
font-size: 175%;
|
||||
margin-bottom: -8px;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -1px;
|
||||
mix-blend-mode: multiply;
|
||||
gap: 12px;
|
||||
}
|
||||
.timeline-start .account-container header .account-block .avatar {
|
||||
width: 112px !important;
|
||||
height: 112px !important;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import './account.css';
|
||||
import './account-info.css';
|
||||
|
||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
import { api } from '../utils/api';
|
||||
import emojifyText from '../utils/emojify-text';
|
||||
|
@ -17,49 +16,32 @@ import Avatar from './avatar';
|
|||
import Icon from './icon';
|
||||
import Link from './link';
|
||||
|
||||
function Account({ account, instance: propInstance, onClose }) {
|
||||
const { masto, instance, authenticated } = api({ instance: propInstance });
|
||||
function AccountInfo({
|
||||
account,
|
||||
fetchAccount = () => {},
|
||||
standalone,
|
||||
instance,
|
||||
authenticated,
|
||||
}) {
|
||||
const [uiState, setUIState] = useState('default');
|
||||
const isString = typeof account === 'string';
|
||||
const [info, setInfo] = useState(isString ? null : account);
|
||||
|
||||
useEffect(() => {
|
||||
if (isString) {
|
||||
setUIState('loading');
|
||||
(async () => {
|
||||
try {
|
||||
const info = await masto.v1.accounts.lookup({
|
||||
acct: account,
|
||||
skip_webfinger: false,
|
||||
});
|
||||
setInfo(info);
|
||||
setUIState('default');
|
||||
} catch (e) {
|
||||
try {
|
||||
const result = await masto.v2.search({
|
||||
q: account,
|
||||
type: 'accounts',
|
||||
limit: 1,
|
||||
resolve: authenticated,
|
||||
});
|
||||
if (result.accounts.length) {
|
||||
setInfo(result.accounts[0]);
|
||||
setUIState('default');
|
||||
return;
|
||||
}
|
||||
setInfo(null);
|
||||
setUIState('error');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setInfo(null);
|
||||
setUIState('error');
|
||||
}
|
||||
}
|
||||
})();
|
||||
} else {
|
||||
setInfo(account);
|
||||
}
|
||||
}, [account]);
|
||||
if (!isString) return;
|
||||
setUIState('loading');
|
||||
(async () => {
|
||||
try {
|
||||
const info = await fetchAccount();
|
||||
setInfo(info);
|
||||
setUIState('default');
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setInfo(null);
|
||||
setUIState('error');
|
||||
}
|
||||
})();
|
||||
}, [isString, fetchAccount]);
|
||||
|
||||
const {
|
||||
acct,
|
||||
|
@ -84,13 +66,17 @@ function Account({ account, instance: propInstance, onClose }) {
|
|||
username,
|
||||
} = info || {};
|
||||
|
||||
const escRef = useHotkeys('esc', onClose, [onClose]);
|
||||
const [headerCornerColors, setHeaderCornerColors] = useState([]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={escRef}
|
||||
id="account-container"
|
||||
class={`sheet ${uiState === 'loading' ? 'skeleton' : ''}`}
|
||||
class={`account-container ${uiState === 'loading' ? 'skeleton' : ''}`}
|
||||
style={{
|
||||
'--header-color-1': headerCornerColors[0],
|
||||
'--header-color-2': headerCornerColors[1],
|
||||
'--header-color-3': headerCornerColors[2],
|
||||
'--header-color-4': headerCornerColors[3],
|
||||
}}
|
||||
>
|
||||
{uiState === 'error' && (
|
||||
<div class="ui-state">
|
||||
|
@ -128,7 +114,47 @@ function Account({ account, instance: propInstance, onClose }) {
|
|||
alt=""
|
||||
class="header-banner"
|
||||
onError={(e) => {
|
||||
e.target.src = headerStatic;
|
||||
if (e.target.crossOrigin) {
|
||||
if (e.target.src !== headerStatic) {
|
||||
e.target.src = headerStatic;
|
||||
} else {
|
||||
e.target.removeAttribute('crossorigin');
|
||||
e.target.src = header;
|
||||
}
|
||||
} else if (e.target.src !== headerStatic) {
|
||||
e.target.src = headerStatic;
|
||||
} else {
|
||||
e.target.remove();
|
||||
}
|
||||
}}
|
||||
crossOrigin="anonymous"
|
||||
onLoad={(e) => {
|
||||
try {
|
||||
// Get color from four corners of image
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
canvas.width = e.target.width;
|
||||
canvas.height = e.target.height;
|
||||
ctx.drawImage(e.target, 0, 0);
|
||||
const colors = [
|
||||
ctx.getImageData(0, 0, 1, 1).data,
|
||||
ctx.getImageData(e.target.width - 1, 0, 1, 1).data,
|
||||
ctx.getImageData(0, e.target.height - 1, 1, 1).data,
|
||||
ctx.getImageData(
|
||||
e.target.width - 1,
|
||||
e.target.height - 1,
|
||||
1,
|
||||
1,
|
||||
).data,
|
||||
];
|
||||
const rgbColors = colors.map((color) => {
|
||||
return `rgb(${color[0]}, ${color[1]}, ${color[2]}, 0.3)`;
|
||||
});
|
||||
setHeaderCornerColors(rgbColors);
|
||||
console.log({ colors, rgbColors });
|
||||
} catch (e) {
|
||||
// Silently fail
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
@ -137,7 +163,8 @@ function Account({ account, instance: propInstance, onClose }) {
|
|||
account={info}
|
||||
instance={instance}
|
||||
avatarSize="xxxl"
|
||||
external
|
||||
external={standalone}
|
||||
internal={!standalone}
|
||||
/>
|
||||
</header>
|
||||
<main tabIndex="-1">
|
||||
|
@ -429,4 +456,4 @@ function RelatedActions({ info, instance, authenticated }) {
|
|||
);
|
||||
}
|
||||
|
||||
export default Account;
|
||||
export default AccountInfo;
|
56
src/components/account-sheet.jsx
Normal file
56
src/components/account-sheet.jsx
Normal file
|
@ -0,0 +1,56 @@
|
|||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
import { api } from '../utils/api';
|
||||
|
||||
import AccountInfo from './account-info';
|
||||
|
||||
function AccountSheet({ account, instance: propInstance, onClose }) {
|
||||
const { masto, instance, authenticated } = api({ instance: propInstance });
|
||||
const isString = typeof account === 'string';
|
||||
|
||||
const escRef = useHotkeys('esc', onClose, [onClose]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={escRef}
|
||||
class="sheet"
|
||||
onClick={(e) => {
|
||||
const accountBlock = e.target.closest('.account-block');
|
||||
if (accountBlock) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AccountInfo
|
||||
instance={instance}
|
||||
authenticated={authenticated}
|
||||
account={account}
|
||||
fetchAccount={async () => {
|
||||
if (isString) {
|
||||
try {
|
||||
const info = await masto.v1.accounts.lookup({
|
||||
acct: account,
|
||||
skip_webfinger: false,
|
||||
});
|
||||
return info;
|
||||
} catch (e) {
|
||||
const result = await masto.v2.search({
|
||||
q: account,
|
||||
type: 'accounts',
|
||||
limit: 1,
|
||||
resolve: authenticated,
|
||||
});
|
||||
if (result.accounts.length) {
|
||||
return result.accounts[0];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return account;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AccountSheet;
|
|
@ -1,134 +0,0 @@
|
|||
#account-container.skeleton {
|
||||
color: var(--outline-color);
|
||||
}
|
||||
|
||||
#account-container .header-banner {
|
||||
/* pointer-events: none; */
|
||||
aspect-ratio: 6 / 1;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
/* mask fade out bottom of banner */
|
||||
mask-image: linear-gradient(
|
||||
to bottom,
|
||||
hsl(0, 0%, 0%) 0%,
|
||||
hsla(0, 0%, 0%, 0.987) 14%,
|
||||
hsla(0, 0%, 0%, 0.951) 26.2%,
|
||||
hsla(0, 0%, 0%, 0.896) 36.8%,
|
||||
hsla(0, 0%, 0%, 0.825) 45.9%,
|
||||
hsla(0, 0%, 0%, 0.741) 53.7%,
|
||||
hsla(0, 0%, 0%, 0.648) 60.4%,
|
||||
hsla(0, 0%, 0%, 0.55) 66.2%,
|
||||
hsla(0, 0%, 0%, 0.45) 71.2%,
|
||||
hsla(0, 0%, 0%, 0.352) 75.6%,
|
||||
hsla(0, 0%, 0%, 0.259) 79.6%,
|
||||
hsla(0, 0%, 0%, 0.175) 83.4%,
|
||||
hsla(0, 0%, 0%, 0.104) 87.2%,
|
||||
hsla(0, 0%, 0%, 0.049) 91.1%,
|
||||
hsla(0, 0%, 0%, 0.013) 95.3%,
|
||||
hsla(0, 0%, 0%, 0) 100%
|
||||
);
|
||||
margin-bottom: -44px;
|
||||
}
|
||||
#account-container .header-banner:hover {
|
||||
animation: position-object 5s ease-in-out 1s 5;
|
||||
}
|
||||
|
||||
@media (min-height: 480px) {
|
||||
#account-container .header-banner {
|
||||
aspect-ratio: 3 / 1;
|
||||
}
|
||||
}
|
||||
|
||||
#account-container header {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
text-shadow: 0 0 24px var(--bg-color);
|
||||
}
|
||||
#account-container header .avatar {
|
||||
box-shadow: 0 0 24px var(--bg-color);
|
||||
}
|
||||
|
||||
#account-container .note {
|
||||
font-size: 95%;
|
||||
line-height: 1.4;
|
||||
}
|
||||
#account-container .note:not(:has(p)):not(:empty) {
|
||||
/* Some notes don't have <p> tags, so we need to add some padding */
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
#account-container .stats {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
gap: 16px;
|
||||
opacity: 0.75;
|
||||
font-size: 90%;
|
||||
background-color: var(--bg-faded-color);
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
#account-container .stats > * {
|
||||
text-align: center;
|
||||
}
|
||||
#account-container .stats a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#account-container .actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
min-height: 2.5em;
|
||||
}
|
||||
#account-container .actions button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
#account-container .profile-metadata {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
#account-container .profile-field {
|
||||
min-width: 0;
|
||||
flex-grow: 1;
|
||||
font-size: 90%;
|
||||
background-color: var(--bg-faded-color);
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
filter: saturate(0.75);
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
#account-container :is(.note, .profile-field) .invisible {
|
||||
display: none;
|
||||
}
|
||||
#account-container :is(.note, .profile-field) .ellipsis::after {
|
||||
content: '…';
|
||||
}
|
||||
|
||||
#account-container .profile-field b {
|
||||
font-size: 90%;
|
||||
color: var(--text-insignificant-color);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
#account-container .profile-field b .icon {
|
||||
color: var(--green-color);
|
||||
}
|
||||
#account-container .profile-field p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#account-container .common-followers {
|
||||
border-top: 1px solid var(--outline-color);
|
||||
border-bottom: 1px solid var(--outline-color);
|
||||
padding: 8px 0;
|
||||
font-size: 90%;
|
||||
line-height: 1.5;
|
||||
color: var(--text-insignificant-color);
|
||||
}
|
|
@ -27,6 +27,7 @@ function Timeline({
|
|||
checkForUpdatesInterval = 60_000, // 1 minute
|
||||
headerStart,
|
||||
headerEnd,
|
||||
timelineStart,
|
||||
}) {
|
||||
const [items, setItems] = useState([]);
|
||||
const [uiState, setUIState] = useState('default');
|
||||
|
@ -292,6 +293,7 @@ function Timeline({
|
|||
</button>
|
||||
)}
|
||||
</header>
|
||||
{!!timelineStart && <div class="timeline-start">{timelineStart}</div>}
|
||||
{!!items.length ? (
|
||||
<>
|
||||
<ul class="timeline">
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import AccountInfo from '../components/account-info';
|
||||
import Timeline from '../components/timeline';
|
||||
import { api } from '../utils/api';
|
||||
import emojifyText from '../utils/emojify-text';
|
||||
|
@ -13,7 +14,7 @@ const LIMIT = 20;
|
|||
function AccountStatuses() {
|
||||
const snapStates = useSnapshot(states);
|
||||
const { id, ...params } = useParams();
|
||||
const { masto, instance } = api({ instance: params.instance });
|
||||
const { masto, instance, authenticated } = api({ instance: params.instance });
|
||||
const accountStatusesIterator = useRef();
|
||||
async function fetchAccountStatuses(firstLoad) {
|
||||
const results = [];
|
||||
|
@ -27,7 +28,7 @@ function AccountStatuses() {
|
|||
pinnedStatuses.forEach((status) => {
|
||||
status._pinned = true;
|
||||
});
|
||||
if (pinnedStatuses.length > 1) {
|
||||
if (pinnedStatuses.length >= 3) {
|
||||
const pinnedStatusesIds = pinnedStatuses.map((status) => status.id);
|
||||
results.push({
|
||||
id: pinnedStatusesIds,
|
||||
|
@ -54,7 +55,7 @@ function AccountStatuses() {
|
|||
};
|
||||
}
|
||||
|
||||
const [account, setAccount] = useState({});
|
||||
const [account, setAccount] = useState();
|
||||
useTitle(
|
||||
`${account?.acct ? '@' + account.acct : 'Posts'}`,
|
||||
'/:instance?/a/:id',
|
||||
|
@ -71,7 +72,20 @@ function AccountStatuses() {
|
|||
})();
|
||||
}, [id]);
|
||||
|
||||
const { displayName, acct, emojis } = account;
|
||||
const { displayName, acct, emojis } = account || {};
|
||||
|
||||
const TimelineStart = useMemo(
|
||||
() => (
|
||||
<AccountInfo
|
||||
instance={instance}
|
||||
account={id}
|
||||
fetchAccount={() => masto.v1.accounts.fetch(id)}
|
||||
authenticated={authenticated}
|
||||
standalone
|
||||
/>
|
||||
),
|
||||
[id, instance, authenticated],
|
||||
);
|
||||
|
||||
return (
|
||||
<Timeline
|
||||
|
@ -103,6 +117,7 @@ function AccountStatuses() {
|
|||
errorText="Unable to load statuses"
|
||||
fetchItems={fetchAccountStatuses}
|
||||
boostsCarousel={snapStates.settings.boostsCarousel}
|
||||
timelineStart={TimelineStart}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue