Compare commits

..

No commits in common. "4734a67f5a02d0d6896f7d1653189335632a1ade" and "b25c2d5f4562aadea9da6066125040f3ea1d65fb" have entirely different histories.

4 changed files with 14 additions and 73 deletions

View file

@ -2,8 +2,6 @@
Tutu is a comfortable experience for tooting. Designed to work on any device - desktop, phone and tablet. Tutu is a comfortable experience for tooting. Designed to work on any device - desktop, phone and tablet.
[Launch Tutu](https://tutu.lightstands.xyz)
## Compatibility ## Compatibility
The code is built against those targets and Tutu must run on those platforms: The code is built against those targets and Tutu must run on those platforms:
@ -14,12 +12,6 @@ The code is built against those targets and Tutu must run on those platforms:
Tutu trys to push the Web technology to its limit. Some features might not be available on the platform does not meet the requirement. Tutu trys to push the Web technology to its limit. Some features might not be available on the platform does not meet the requirement.
## The "Next" Branch
The "next" branch of the app is built on every commit pushed into "master". You can tatse latest change but risks your data.
[Launch Tutu (Next)](https://next.tututheapp.pages.dev)
## Build & Depoly ## Build & Depoly
Tutu uses [bun](https://bun.sh) as the package manager. Run Tutu uses [bun](https://bun.sh) as the package manager. Run

View file

@ -49,8 +49,7 @@ export type Session = {
client: mastodon.rest.Client; client: mastodon.rest.Client;
}; };
const Context = const Context = /* @__PURE__ */ createContext<Accessor<readonly Readonly<Session>[]>>();
/* @__PURE__ */ createContext<Accessor<readonly Readonly<Session>[]>>();
export const Provider = Context.Provider; export const Provider = Context.Provider;
@ -78,9 +77,7 @@ function useSessionsRaw() {
return store; return store;
} }
const DefaultSessionContext = /* @__PURE__ */ createContext<Accessor<number>>( const DefaultSessionContext = /* @__PURE__ */ createContext<Accessor<number>>(() => 0)
() => 0,
);
export const DefaultSessionProvider = DefaultSessionContext.Provider; export const DefaultSessionProvider = DefaultSessionContext.Provider;
@ -90,14 +87,14 @@ export const DefaultSessionProvider = DefaultSessionContext.Provider;
* This function may return `undefined`, but it will try to redirect the user to the sign in. * This function may return `undefined`, but it will try to redirect the user to the sign in.
*/ */
export function useDefaultSession() { export function useDefaultSession() {
const sessions = useSessions(); const sessions = useSessions()
const sessionIndex = useContext(DefaultSessionContext); const sessionIndex = useContext(DefaultSessionContext)
return () => { return () => {
if (sessions().length > 0) { if (sessions().length > 0) {
return sessions()[sessionIndex()]; return sessions()[sessionIndex()]
} }
}; }
} }
/** /**
@ -117,7 +114,7 @@ export function useDefaultSession() {
* unauthorised client for the site. This client may not available for some operations. * unauthorised client for the site. This client may not available for some operations.
*/ */
export function useSessionForAcctStr(acct: Accessor<string>) { export function useSessionForAcctStr(acct: Accessor<string>) {
const allSessions = useSessions(); const allSessions = useSessions()
return createMemo(() => { return createMemo(() => {
const [inputUsername, inputSite] = acct().split("@", 2); const [inputUsername, inputSite] = acct().split("@", 2);
@ -135,6 +132,4 @@ export function useSessionForAcctStr(acct: Accessor<string>) {
}); });
} }
export function makeAcctText(session: Session) {
return `${session.account.inf?.username}@${session.account.site}`;
}

View file

@ -116,19 +116,10 @@ const Profile: Component = () => {
} }
.acct-grp { .acct-grp {
display: flex; display: grid;
flex-flow: row wrap; grid-template-columns: auto 1fr auto;
gap: 16px; gap: 16px;
align-items: center; align-items: center;
& > :nth-child(2) {
flex-grow: 1;
}
& > :last-child {
flex-grow: 1;
text-align: right;
}
} }
.name-grp { .name-grp {
@ -137,13 +128,11 @@ const Profile: Component = () => {
} }
table.acct-fields { table.acct-fields {
word-break: break-all;
& td > :global(a) { & td > :global(a) {
display: inline-flex; display: inline-flex;
min-height: 44px;
align-items: center; align-items: center;
color: inherit; color: inherit;
min-height: 44px;
} }
& :global(a > .invisible) { & :global(a > .invisible) {
@ -157,9 +146,6 @@ const Profile: Component = () => {
.page-title { .page-title {
flex-grow: 1; flex-grow: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
`; `;

View file

@ -39,8 +39,6 @@ import { FastAverageColor } from "fast-average-color";
import Color from "colorjs.io"; import Color from "colorjs.io";
import { useDateFnLocale } from "../platform/i18n"; import { useDateFnLocale } from "../platform/i18n";
import { canShare, share } from "../platform/share"; import { canShare, share } from "../platform/share";
import { makeAcctText, useDefaultSession } from "../masto/clients";
import { useNavigate } from "@solidjs/router";
type TootContentViewProps = { type TootContentViewProps = {
source?: string; source?: string;
@ -211,16 +209,12 @@ function TootActionGroup<T extends mastodon.v1.Status>(
); );
} }
function TootAuthorGroup(props: { function TootAuthorGroup(props: { status: mastodon.v1.Status; now: Date }) {
status: mastodon.v1.Status;
now: Date;
onClick?: JSX.EventHandlerUnion<HTMLDivElement, MouseEvent>;
}) {
const toot = () => props.status; const toot = () => props.status;
const dateFnLocale = useDateFnLocale(); const dateFnLocale = useDateFnLocale();
return ( return (
<div class={tootStyle.tootAuthorGrp} onClick={props.onClick}> <div class={tootStyle.tootAuthorGrp}>
<Img src={toot().account.avatar} class={tootStyle.tootAvatar} /> <Img src={toot().account.avatar} class={tootStyle.tootAvatar} />
<div class={tootStyle.tootAuthorNameGrp}> <div class={tootStyle.tootAuthorNameGrp}>
<Body2 <Body2
@ -315,13 +309,6 @@ export function TootPreviewCard(props: {
); );
} }
/**
* Component for a toot.
*
* If the session involved is not the first session, you must wrap
* this component under a `<DefaultSessionProvier />` with correct
* session.
*/
const RegularToot: Component<TootCardProps> = (props) => { const RegularToot: Component<TootCardProps> = (props) => {
let rootRef: HTMLElement; let rootRef: HTMLElement;
const [managed, managedActionGroup, rest] = splitProps( const [managed, managedActionGroup, rest] = splitProps(
@ -332,25 +319,6 @@ const RegularToot: Component<TootCardProps> = (props) => {
const now = useTimeSource(); const now = useTimeSource();
const status = () => managed.status; const status = () => managed.status;
const toot = () => status().reblog ?? status(); const toot = () => status().reblog ?? status();
const session = useDefaultSession();
const navigate = useNavigate();
const openProfile = (event: MouseEvent) => {
if (!managed.evaluated) return;
event.stopPropagation();
const s = session();
if (!s) {
console.warn("No session is provided");
return;
}
const acct = makeAcctText(s);
navigate(
`/${encodeURIComponent(acct)}/profile/${managed.status.account.id}`,
);
};
css` css`
.reply-sep { .reply-sep {
@ -428,7 +396,7 @@ const RegularToot: Component<TootCardProps> = (props) => {
</span> </span>
</div> </div>
</Show> </Show>
<TootAuthorGroup status={toot()} now={now()} onClick={openProfile} /> <TootAuthorGroup status={toot()} now={now()} />
<TootContentView <TootContentView
source={toot().content} source={toot().content}
emojis={toot().emojis} emojis={toot().emojis}