fix #35: pass correct value to TootActionGroup
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				/ depoly (push) Successful in 1m19s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	/ depoly (push) Successful in 1m19s
				
			* add createDefaultTootEnv
This commit is contained in:
		
							parent
							
								
									6dd6065711
								
							
						
					
					
						commit
						5742932c86
					
				
					 7 changed files with 179 additions and 235 deletions
				
			
		|  | @ -26,8 +26,8 @@ export type Account = AccountKey & { | |||
|   inf?: mastodon.v1.AccountCredentials; | ||||
| }; | ||||
| 
 | ||||
| export function isAccount(object: AccountKey) { | ||||
|   return !!(object as Record<string, unknown>)["tokenType"]; | ||||
| export function isAccount(object: RemoteServer) { | ||||
|   return isAccountKey(object) && !!(object as Record<string, unknown>)["tokenType"]; | ||||
| } | ||||
| 
 | ||||
| export const $accounts = persistentAtom<Account[]>("accounts", [], { | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ import { | |||
|   untrack, | ||||
|   useContext, | ||||
| } from "solid-js"; | ||||
| import { Account } from "../accounts/stores"; | ||||
| import { Account, type RemoteServer } from "../accounts/stores"; | ||||
| import { createRestAPIClient, mastodon } from "masto"; | ||||
| import { useLocation } from "@solidjs/router"; | ||||
| import { useNavigator } from "~platform/StackedRouter"; | ||||
|  | @ -131,8 +131,8 @@ export function useSessionForAcctStr(acct: Accessor<string>) { | |||
|     return ( | ||||
|       authedSession ?? { | ||||
|         client: createUnauthorizedClient(inputSite), | ||||
|         account: { site: inputSite }, // TODO: we need some security checks here?
 | ||||
|       } | ||||
|         account: { site: inputSite } as RemoteServer, // TODO: we need some security checks here?
 | ||||
|       } as const | ||||
|     ); | ||||
|   }); | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import { | ||||
|   catchError, | ||||
|   createRenderEffect, | ||||
|   createResource, | ||||
|   createSignal, | ||||
|   createUniqueId, | ||||
|  | @ -14,7 +13,6 @@ import { | |||
| } from "solid-js"; | ||||
| import Scaffold from "~material/Scaffold"; | ||||
| import { | ||||
|   AppBar, | ||||
|   Avatar, | ||||
|   Button, | ||||
|   Checkbox, | ||||
|  | @ -26,7 +24,6 @@ import { | |||
|   ListItemSecondaryAction, | ||||
|   ListItemText, | ||||
|   MenuItem, | ||||
|   Toolbar, | ||||
| } from "@suid/material"; | ||||
| import { | ||||
|   Close, | ||||
|  | @ -63,6 +60,7 @@ import { | |||
|   default as ItemSelectionProvider, | ||||
| } from "../timelines/toots/ItemSelectionProvider"; | ||||
| import AppTopBar from "~material/AppTopBar"; | ||||
| import type { Account } from "../accounts/stores"; | ||||
| 
 | ||||
| const Profile: Component = () => { | ||||
|   const { pop } = useNavigator(); | ||||
|  | @ -117,7 +115,7 @@ const Profile: Component = () => { | |||
|   }; | ||||
| 
 | ||||
|   const isCurrentSessionProfile = () => { | ||||
|     return session().account?.inf?.url === profile()?.url; | ||||
|     return (session().account as Account).inf?.url === profile()?.url; | ||||
|   }; | ||||
| 
 | ||||
|   const [recentTootFilter, setRecentTootFilter] = createSignal({ | ||||
|  | @ -172,8 +170,8 @@ const Profile: Component = () => { | |||
| 
 | ||||
|   const sessionDisplayName = createMemo(() => | ||||
|     resolveCustomEmoji( | ||||
|       session().account?.inf?.displayName || "", | ||||
|       session().account?.inf?.emojis ?? [], | ||||
|       (session().account as Account).inf?.displayName || "", | ||||
|       (session().account as Account).inf?.emojis ?? [], | ||||
|     ), | ||||
|   ); | ||||
| 
 | ||||
|  | @ -244,7 +242,7 @@ const Profile: Component = () => { | |||
|           <Show when={session().account}> | ||||
|             <MenuItem> | ||||
|               <ListItemAvatar> | ||||
|                 <Avatar src={session().account?.inf?.avatar} /> | ||||
|                 <Avatar src={(session().account as Account).inf?.avatar} /> | ||||
|               </ListItemAvatar> | ||||
|               <ListItemText secondary={"Default account"}> | ||||
|                 <span innerHTML={sessionDisplayName()}></span> | ||||
|  | @ -367,7 +365,7 @@ const Profile: Component = () => { | |||
|             aria-label={`${relationship()?.following ? "Unfollow" : "Follow"} on your home timeline`} | ||||
|           > | ||||
|             <ListItemAvatar> | ||||
|               <Avatar src={session().account?.inf?.avatar}></Avatar> | ||||
|               <Avatar src={(session().account as Account).inf?.avatar}></Avatar> | ||||
|             </ListItemAvatar> | ||||
|             <ListItemText | ||||
|               secondary={ | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ import TootPoll from "./toots/TootPoll"; | |||
| import TootActionGroup from "./toots/TootActionGroup.js"; | ||||
| import TootAuthorGroup from "./toots/TootAuthorGroup.js"; | ||||
| import "./RegularToot.css"; | ||||
| import { vibrate } from "~platform/hardware.js"; | ||||
| 
 | ||||
| export type TootEnv = { | ||||
|   boost: (value: mastodon.v1.Status) => void; | ||||
|  | @ -52,6 +53,107 @@ export function useTootEnv() { | |||
|   return env; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Create default toot env. | ||||
|  * | ||||
|  * This function does not provides the "reply" action. | ||||
|  */ | ||||
| export function createDefaultTootEnv( | ||||
|   client: () => mastodon.rest.Client | undefined, | ||||
|   setToot: (id: string, status: mastodon.v1.Status) => void, | ||||
| ): TootEnv { | ||||
|   return { | ||||
|     async bookmark(status: mastodon.v1.Status) { | ||||
|       const c = client(); | ||||
|       if (!c) return; | ||||
| 
 | ||||
|       const result = await (status.bookmarked | ||||
|         ? c.v1.statuses.$select(status.id).unbookmark() | ||||
|         : c.v1.statuses.$select(status.id).bookmark()); | ||||
| 
 | ||||
|       setToot(result.id, result); | ||||
|     }, | ||||
| 
 | ||||
|     async boost(status: mastodon.v1.Status) { | ||||
|       const c = client(); | ||||
|       if (!c) return; | ||||
| 
 | ||||
|       vibrate(50); | ||||
|       const rootStatus = status.reblog ? status.reblog : status; | ||||
|       const reblogged = rootStatus.reblogged; | ||||
|       if (status.reblog) { | ||||
|         setToot(status.id, { | ||||
|           ...status, | ||||
|           reblog: { ...status.reblog!, reblogged: !reblogged }, | ||||
|           reblogged: !reblogged, | ||||
|         }); | ||||
|       } else { | ||||
|         setToot(status.id, { | ||||
|           ...status, | ||||
|           reblogged: !reblogged, | ||||
|         }); | ||||
|       } | ||||
|       // modified the original
 | ||||
| 
 | ||||
|       const result = reblogged | ||||
|         ? await c.v1.statuses.$select(status.id).unreblog() | ||||
|         : (await c.v1.statuses.$select(status.id).reblog()); | ||||
| 
 | ||||
|       if (status.reblog && !reblogged) { | ||||
|         // When calling /reblog, the result is the boost object (the actor
 | ||||
|         // is the calling account); for /unreblog, the result is the original
 | ||||
|         // toot. So we only do this trick only on the reblogging.
 | ||||
|         setToot(status.id, { | ||||
|           ...status, | ||||
|           reblog: result.reblog, | ||||
|         }); | ||||
|       } else { | ||||
|         setToot(status.id, result); | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|     async favourite(status: mastodon.v1.Status) { | ||||
|       const c = client(); | ||||
|       if (!c) return; | ||||
| 
 | ||||
|       const ovalue = status.favourited; | ||||
|       setToot(status.id, { ...status, favourited: !ovalue }); | ||||
| 
 | ||||
|       const result = ovalue | ||||
|         ? await c.v1.statuses.$select(status.id).unfavourite() | ||||
|         : await c.v1.statuses.$select(status.id).favourite(); | ||||
|       setToot(status.id, result); | ||||
|     }, | ||||
| 
 | ||||
|     async vote(status: mastodon.v1.Status, votes: readonly number[]) { | ||||
|       const c = client(); | ||||
|       if (!c) return; | ||||
| 
 | ||||
|       const toot = status.reblog ?? status; | ||||
|       if (!toot.poll) return; | ||||
| 
 | ||||
|       const npoll = await c.v1.polls.$select(toot.poll.id).votes.create({ | ||||
|         choices: votes, | ||||
|       }); | ||||
| 
 | ||||
|       if (status.reblog) { | ||||
|         setToot(status.id, { | ||||
|           ...status, | ||||
|           reblog: { | ||||
|             ...status.reblog, | ||||
|             poll: npoll, | ||||
|           }, | ||||
|         }); | ||||
|       } else { | ||||
|         setToot(status.id, { | ||||
|           ...status, | ||||
|           poll: npoll, | ||||
|         }); | ||||
|       } | ||||
|     }, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| type RegularTootProps = { | ||||
|   status: mastodon.v1.Status; | ||||
|   actionable?: boolean; | ||||
|  | @ -203,7 +305,7 @@ const RegularToot: Component<RegularTootProps> = (oprops) => { | |||
|             class={cardStyle.cardNoPad} | ||||
|             style={{ "margin-top": "8px" }} | ||||
|           /> | ||||
|           <TootActionGroup value={toot()} class={cardStyle.cardGutSkip} /> | ||||
|           <TootActionGroup value={status()} class={cardStyle.cardGutSkip} /> | ||||
|         </Show> | ||||
|       </article> | ||||
|     </> | ||||
|  |  | |||
|  | @ -1,23 +1,17 @@ | |||
| import { useParams } from "@solidjs/router"; | ||||
| import { | ||||
|   catchError, | ||||
|   createResource, | ||||
|   Show, | ||||
|   type Component, | ||||
| } from "solid-js"; | ||||
| import { catchError, createResource, Show, type Component } from "solid-js"; | ||||
| import Scaffold from "~material/Scaffold"; | ||||
| import { CircularProgress } from "@suid/material"; | ||||
| import { Title } from "~material/typography"; | ||||
| import { useSessionForAcctStr } from "../masto/clients"; | ||||
| import { resolveCustomEmoji } from "../masto/toot"; | ||||
| import RegularToot, { | ||||
|   createDefaultTootEnv, | ||||
|   findElementActionable, | ||||
|   TootEnvProvider, | ||||
| } from "./RegularToot"; | ||||
| import type { mastodon } from "masto"; | ||||
| import cards from "~material/cards.module.css"; | ||||
| import { css } from "solid-styled"; | ||||
| import { vibrate } from "~platform/hardware"; | ||||
| import { createTimeSource, TimeSourceProvider } from "~platform/timesrc"; | ||||
| import TootComposer from "./TootComposer"; | ||||
| import { useDocumentTitle } from "../utils"; | ||||
|  | @ -53,7 +47,7 @@ const TootBottomSheet: Component = (props) => { | |||
| 
 | ||||
|   const [tootContextErrorUncaught, { refetch: refetchContext }] = | ||||
|     createResource( | ||||
|       () => [session().client, params.id] as const, | ||||
|       () => [session().client, toot()?.reblog?.id ?? params.id] as const, | ||||
|       async ([client, id]) => { | ||||
|         return await client.v1.statuses.$select(id).context.fetch(); | ||||
|       }, | ||||
|  | @ -89,49 +83,10 @@ const TootBottomSheet: Component = (props) => { | |||
|     return s.account ? s : undefined; | ||||
|   }; | ||||
| 
 | ||||
|   const onBookmark = async () => { | ||||
|     const status = remoteToot()!; | ||||
|     const client = actSession()!.client; | ||||
|     setRemoteToot( | ||||
|       Object.assign({}, status, { | ||||
|         bookmarked: !status.bookmarked, | ||||
|       }), | ||||
|   const mainTootEnv = createDefaultTootEnv( | ||||
|     () => actSession()?.client, | ||||
|     (_, status) => setRemoteToot(status), | ||||
|   ); | ||||
|     const result = await (status.bookmarked | ||||
|       ? client.v1.statuses.$select(status.id).unbookmark() | ||||
|       : client.v1.statuses.$select(status.id).bookmark()); | ||||
|     setRemoteToot(result); | ||||
|   }; | ||||
| 
 | ||||
|   const onBoost = async () => { | ||||
|     const status = remoteToot()!; | ||||
|     const client = actSession()!.client; | ||||
|     vibrate(50); | ||||
|     setRemoteToot( | ||||
|       Object.assign({}, status, { | ||||
|         reblogged: !status.reblogged, | ||||
|       }), | ||||
|     ); | ||||
|     const result = await (status.reblogged | ||||
|       ? client.v1.statuses.$select(status.id).unreblog() | ||||
|       : client.v1.statuses.$select(status.id).reblog()); | ||||
|     vibrate([20, 30]); | ||||
|     setRemoteToot(result.reblog!); | ||||
|   }; | ||||
| 
 | ||||
|   const onFav = async () => { | ||||
|     const status = remoteToot()!; | ||||
|     const client = actSession()!.client; | ||||
|     setRemoteToot( | ||||
|       Object.assign({}, status, { | ||||
|         favourited: !status.favourited, | ||||
|       }), | ||||
|     ); | ||||
|     const result = await (status.favourited | ||||
|       ? client.v1.statuses.$select(status.id).favourite() | ||||
|       : client.v1.statuses.$select(status.id).unfavourite()); | ||||
|     setRemoteToot(result); | ||||
|   }; | ||||
| 
 | ||||
|   const defaultMentions = () => { | ||||
|     const tootAcct = remoteToot()?.reblog?.account ?? remoteToot()?.account; | ||||
|  | @ -145,33 +100,6 @@ const TootBottomSheet: Component = (props) => { | |||
|     return Array.from(new Set(values).keys()); | ||||
|   }; | ||||
| 
 | ||||
|   const vote = async (status: mastodon.v1.Status, votes: readonly number[]) => { | ||||
|     const client = session()?.client; | ||||
|     if (!client) return; | ||||
| 
 | ||||
|     const toot = status.reblog ?? status; | ||||
|     if (!toot.poll) return; | ||||
| 
 | ||||
|     const npoll = await client.v1.polls.$select(toot.poll.id).votes.create({ | ||||
|       choices: votes, | ||||
|     }); | ||||
| 
 | ||||
|     if (status.reblog) { | ||||
|       setRemoteToot({ | ||||
|         ...status, | ||||
|         reblog: { | ||||
|           ...status.reblog, | ||||
|           poll: npoll, | ||||
|         }, | ||||
|       }); | ||||
|     } else { | ||||
|       setRemoteToot({ | ||||
|         ...status, | ||||
|         poll: npoll, | ||||
|       }); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const handleMainTootClick = ( | ||||
|     event: MouseEvent & { currentTarget: HTMLElement }, | ||||
|   ) => { | ||||
|  | @ -237,6 +165,7 @@ const TootBottomSheet: Component = (props) => { | |||
|     > | ||||
|       <div class="Scrollable"> | ||||
|         <TimeSourceProvider value={time}> | ||||
|           <ItemSelectionProvider value={selectionState}> | ||||
|             <TootList | ||||
|               threads={ancestors.list} | ||||
|               onUnknownThread={ancestors.getPath} | ||||
|  | @ -245,14 +174,7 @@ const TootBottomSheet: Component = (props) => { | |||
| 
 | ||||
|             <article> | ||||
|               <Show when={toot()}> | ||||
|               <TootEnvProvider | ||||
|                 value={{ | ||||
|                   bookmark: onBookmark, | ||||
|                   boost: onBoost, | ||||
|                   favourite: onFav, | ||||
|                   vote, | ||||
|                 }} | ||||
|               > | ||||
|                 <TootEnvProvider value={mainTootEnv}> | ||||
|                   <RegularToot | ||||
|                     id={`toot-${toot()!.id}`} | ||||
|                     class={cards.card} | ||||
|  | @ -288,7 +210,6 @@ const TootBottomSheet: Component = (props) => { | |||
|               </div> | ||||
|             </Show> | ||||
| 
 | ||||
|           <ItemSelectionProvider value={selectionState}> | ||||
|             <TootList | ||||
|               threads={descendants.list} | ||||
|               onUnknownThread={descendants.getPath} | ||||
|  |  | |||
|  | @ -1,19 +1,16 @@ | |||
| import { | ||||
|   Component, | ||||
|   createSignal, | ||||
|   ErrorBoundary, | ||||
|   type Ref, | ||||
|   createSelector, | ||||
|   Index, | ||||
|   createMemo, | ||||
|   For, | ||||
|   createUniqueId, | ||||
| } from "solid-js"; | ||||
| import { type mastodon } from "masto"; | ||||
| import { vibrate } from "~platform/hardware"; | ||||
| import { useDefaultSession } from "../masto/clients"; | ||||
| import { setCache as setTootBottomSheetCache } from "./TootBottomSheet"; | ||||
| import RegularToot, { | ||||
|   createDefaultTootEnv, | ||||
|   findElementActionable, | ||||
|   findRootToot, | ||||
|   TootEnvProvider, | ||||
|  | @ -62,62 +59,12 @@ const TootList: Component<{ | |||
|   const [isExpanded, setExpanded] = useItemSelection(); | ||||
|   const { push } = useNavigator(); | ||||
| 
 | ||||
|   const onBookmark = async (status: mastodon.v1.Status) => { | ||||
|     const client = session()?.client; | ||||
|     if (!client) return; | ||||
|   const tootEnv = createDefaultTootEnv( | ||||
|     () => session()?.client, | ||||
|     (...args) => props.onChangeToot(...args), | ||||
|   ); | ||||
| 
 | ||||
|     const result = await (status.bookmarked | ||||
|       ? client.v1.statuses.$select(status.id).unbookmark() | ||||
|       : client.v1.statuses.$select(status.id).bookmark()); | ||||
|     props.onChangeToot(result.id, result); | ||||
|   }; | ||||
| 
 | ||||
|   const toggleBoost = async (status: mastodon.v1.Status) => { | ||||
|     const client = session()?.client; | ||||
|     if (!client) return; | ||||
| 
 | ||||
|     vibrate(50); | ||||
|     const rootStatus = status.reblog ? status.reblog : status; | ||||
|     const reblogged = rootStatus.reblogged; | ||||
|     if (status.reblog) { | ||||
|       props.onChangeToot(status.id, { | ||||
|         ...status, | ||||
|         reblog: { ...status.reblog, reblogged: !reblogged }, | ||||
|       }); | ||||
|     } else { | ||||
|       props.onChangeToot(status.id, { | ||||
|         ...status, | ||||
|         reblogged: !reblogged, | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     const result = reblogged | ||||
|       ? await client.v1.statuses.$select(status.id).unreblog() | ||||
|       : (await client.v1.statuses.$select(status.id).reblog()).reblog!; | ||||
| 
 | ||||
|     if (status.reblog) { | ||||
|       props.onChangeToot(status.id, { | ||||
|         ...status, | ||||
|         reblog: result, | ||||
|       }); | ||||
|     } else { | ||||
|       props.onChangeToot(status.id, result); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const toggleFavourite = async (status: mastodon.v1.Status) => { | ||||
|     const client = session()?.client; | ||||
|     if (!client) return; | ||||
|     const ovalue = status.favourited; | ||||
|     props.onChangeToot(status.id, { ...status, favourited: !ovalue }); | ||||
| 
 | ||||
|     const result = ovalue | ||||
|       ? await client.v1.statuses.$select(status.id).unfavourite() | ||||
|       : await client.v1.statuses.$select(status.id).favourite(); | ||||
|     props.onChangeToot(status.id, result); | ||||
|   }; | ||||
| 
 | ||||
|   const openFullScreenToot = ( | ||||
|   const openFullScreenToot = async ( | ||||
|     toot: mastodon.v1.Status, | ||||
|     srcElement: HTMLElement, | ||||
|     reply?: boolean, | ||||
|  | @ -235,33 +182,6 @@ const TootList: Component<{ | |||
|     openFullScreenToot(status, element, true); | ||||
|   }; | ||||
| 
 | ||||
|   const vote = async (status: mastodon.v1.Status, votes: readonly number[]) => { | ||||
|     const client = session()?.client; | ||||
|     if (!client) return; | ||||
| 
 | ||||
|     const toot = status.reblog ?? status; | ||||
|     if (!toot.poll) return; | ||||
| 
 | ||||
|     const npoll = await client.v1.polls.$select(toot.poll.id).votes.create({ | ||||
|       choices: votes, | ||||
|     }); | ||||
| 
 | ||||
|     if (status.reblog) { | ||||
|       props.onChangeToot(status.id, { | ||||
|         ...status, | ||||
|         reblog: { | ||||
|           ...status.reblog, | ||||
|           poll: npoll, | ||||
|         }, | ||||
|       }); | ||||
|     } else { | ||||
|       props.onChangeToot(status.id, { | ||||
|         ...status, | ||||
|         poll: npoll, | ||||
|       }); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   return ( | ||||
|     <ErrorBoundary | ||||
|       fallback={(err, reset) => { | ||||
|  | @ -271,11 +191,8 @@ const TootList: Component<{ | |||
|     > | ||||
|       <TootEnvProvider | ||||
|         value={{ | ||||
|           boost: toggleBoost, | ||||
|           bookmark: onBookmark, | ||||
|           favourite: toggleFavourite, | ||||
|           ...tootEnv, | ||||
|           reply: reply, | ||||
|           vote: vote, | ||||
|         }} | ||||
|       > | ||||
|         <div ref={props.ref} id={props.id} class="toot-list"> | ||||
|  |  | |||
|  | @ -24,13 +24,19 @@ function isolatedCallback(e: MouseEvent) { | |||
|   e.stopPropagation(); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * The actions of the toot card. | ||||
|  * | ||||
|  * The `value` must be the original toot (contains `reblog` if | ||||
|  * it's a boost), since the value will be passed to the callbacks. | ||||
|  */ | ||||
| function TootActionGroup<T extends mastodon.v1.Status>(props: { | ||||
|   value: T; | ||||
|   class?: string; | ||||
| }) { | ||||
|   const { reply, boost, favourite, bookmark } = useTootEnv(); | ||||
|   let actGrpElement: HTMLDivElement; | ||||
|   const toot = () => props.value; | ||||
|   const toot = () => props.value.reblog ?? props.value; | ||||
|   return ( | ||||
|     <div | ||||
|       ref={actGrpElement!} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue