useTimeline: use store to provide partial editing
This commit is contained in:
		
							parent
							
								
									9486ac34ea
								
							
						
					
					
						commit
						fc8d489977
					
				
					 3 changed files with 65 additions and 30 deletions
				
			
		|  | @ -1,5 +1,6 @@ | |||
| import { type mastodon } from "masto"; | ||||
| import { Accessor, createResource, createSignal } from "solid-js"; | ||||
| import { Accessor, createEffect, createResource, createSignal } from "solid-js"; | ||||
| import { createStore } from "solid-js/store"; | ||||
| 
 | ||||
| type TimelineFetchTips = { | ||||
|   direction?: "new" | "old"; | ||||
|  | @ -17,8 +18,8 @@ export function useTimeline(timeline: Accessor<Timeline>) { | |||
|   let maxId: string | undefined; | ||||
|   let otl: Timeline | undefined; | ||||
|   const idSet = new Set<string>(); | ||||
|   return createResource< | ||||
|     mastodon.v1.Status[], | ||||
|   const [snapshot, { refetch }] = createResource< | ||||
|     { records: mastodon.v1.Status[]; direction: "old" | "new" }, | ||||
|     [Timeline], | ||||
|     TimelineFetchTips | undefined | ||||
|   >( | ||||
|  | @ -28,7 +29,6 @@ export function useTimeline(timeline: Accessor<Timeline>) { | |||
|         minId = undefined; | ||||
|         maxId = undefined; | ||||
|         idSet.clear(); | ||||
|         info.value = []; | ||||
|         otl = tl; | ||||
|       } | ||||
|       const direction = | ||||
|  | @ -44,7 +44,6 @@ export function useTimeline(timeline: Accessor<Timeline>) { | |||
|               minId: maxId, | ||||
|             }, | ||||
|       ); | ||||
|       const old = info.value || []; | ||||
|       const diff = pager.filter((x) => !idSet.has(x.id)); | ||||
|       for (const v of diff.map((x) => x.id)) { | ||||
|         idSet.add(v); | ||||
|  | @ -54,20 +53,39 @@ export function useTimeline(timeline: Accessor<Timeline>) { | |||
|         if (!maxId && pager.length > 0) { | ||||
|           maxId = pager[0].id; | ||||
|         } | ||||
|         return [...old, ...diff]; | ||||
|         return { | ||||
|           direction: "old" as const, | ||||
|           records: diff, | ||||
|         }; | ||||
|       } else { | ||||
|         maxId = pager.length > 0 ? pager[0].id : undefined; | ||||
|         if (!minId && pager.length > 0) { | ||||
|           minId = pager[pager.length - 1]?.id; | ||||
|         } | ||||
|         return [...diff, ...old]; | ||||
|         return { direction: "new" as const, records: diff }; | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       initialValue: [], | ||||
|       storage(init) { | ||||
|         return createSignal(init, { equals: false }); | ||||
|       }, | ||||
|     }, | ||||
|   ); | ||||
| 
 | ||||
|   const [store, setStore] = createStore([] as mastodon.v1.Status[]); | ||||
| 
 | ||||
|   createEffect(() => { | ||||
|     const shot = snapshot(); | ||||
|     if (!shot) return; | ||||
|     const { direction, records } = shot; | ||||
|     if (direction == "new") { | ||||
|       setStore((x) => [...records, ...x]); | ||||
|     } else if (direction == "old") { | ||||
|       setStore((x) => [...x, ...records]); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   return [ | ||||
|     store, | ||||
|     snapshot, | ||||
|     { | ||||
|       refetch, | ||||
|       mutate: setStore, | ||||
|     }, | ||||
|   ] as const; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										6
									
								
								src/platform/hardware.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/platform/hardware.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| export function vibrate(pattern: number | number[]) { | ||||
|   if (typeof navigator.vibrate !== "undefined") { | ||||
|     return navigator.vibrate(pattern); | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
|  | @ -37,21 +37,25 @@ import { makeEventListener } from "@solid-primitives/event-listener"; | |||
| import BottomSheet from "../material/BottomSheet"; | ||||
| import { $settings } from "../settings/stores"; | ||||
| import { useStore } from "@nanostores/solid"; | ||||
| import { vibrate } from "../platform/hardware"; | ||||
| 
 | ||||
| const TimelinePanel: Component<{ | ||||
|   client: mastodon.rest.Client; | ||||
|   name: "home" | "public" | "trends"; | ||||
|   prefetch?: boolean; | ||||
| }> = (props) => { | ||||
|   const [timeline, { refetch: refetchTimeline, mutate: mutateTimeline }] = | ||||
|     useTimeline(() => | ||||
|   const [ | ||||
|     timeline, | ||||
|     snapshot, | ||||
|     { refetch: refetchTimeline, mutate: mutateTimeline }, | ||||
|   ] = useTimeline(() => | ||||
|     props.name !== "trends" | ||||
|       ? props.client.v1.timelines[props.name] | ||||
|       : props.client.v1.trends.statuses, | ||||
|   ); | ||||
| 
 | ||||
|   const tlEndObserver = new IntersectionObserver(() => { | ||||
|     if (untrack(() => props.prefetch) && !timeline.loading) | ||||
|     if (untrack(() => props.prefetch) && !snapshot.loading) | ||||
|       refetchTimeline({ direction: "old" }); | ||||
|   }); | ||||
| 
 | ||||
|  | @ -76,12 +80,19 @@ const TimelinePanel: Component<{ | |||
|     client: mastodon.rest.Client, | ||||
|     status: mastodon.v1.Status, | ||||
|   ) => { | ||||
|     const reblogged = false; | ||||
|     mutateTimeline((o) => { | ||||
|       Object.assign(o[index].reblog ?? o[index], { | ||||
|     const reblogged = status.reblog | ||||
|       ? status.reblog.reblogged | ||||
|       : status.reblogged; | ||||
|     vibrate(50); | ||||
|     mutateTimeline(index, (x) => { | ||||
|       if (x.reblog) { | ||||
|         x.reblog = { ...x.reblog, reblogged: !reblogged }; | ||||
|         return Object.assign({}, x); | ||||
|       } else { | ||||
|         return Object.assign({}, x, { | ||||
|           reblogged: !reblogged, | ||||
|         }); | ||||
|       return o; | ||||
|       } | ||||
|     }); | ||||
|     const result = reblogged | ||||
|       ? await client.v1.statuses.$select(status.id).unreblog() | ||||
|  | @ -98,7 +109,7 @@ const TimelinePanel: Component<{ | |||
|   return ( | ||||
|     <> | ||||
|       <div> | ||||
|         <For each={timeline()}> | ||||
|         <For each={timeline}> | ||||
|           {(item, index) => { | ||||
|             return ( | ||||
|               <TootThread | ||||
|  | @ -113,12 +124,12 @@ const TimelinePanel: Component<{ | |||
|       </div> | ||||
| 
 | ||||
|       <div ref={(e) => tlEndObserver.observe(e)}></div> | ||||
|       <Show when={timeline.loading}> | ||||
|       <Show when={snapshot.loading}> | ||||
|         <div class="loading-line" style={{ width: "100%" }}> | ||||
|           <LinearProgress /> | ||||
|         </div> | ||||
|       </Show> | ||||
|       <Show when={timeline.error}> | ||||
|       <Show when={snapshot.error}> | ||||
|         <div | ||||
|           style={{ | ||||
|             display: "flex", | ||||
|  | @ -132,7 +143,7 @@ const TimelinePanel: Component<{ | |||
|           </Button> | ||||
|         </div> | ||||
|       </Show> | ||||
|       <Show when={!props.prefetch && !timeline.loading}> | ||||
|       <Show when={!props.prefetch && !snapshot.loading}> | ||||
|         <div | ||||
|           style={{ | ||||
|             display: "flex", | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue