Refactor Gemini model display: filter old versions, group low/high pкo, and improve naming (#114)

* Refactor Gemini model display: filter old versions, group low/high pro, and improve naming

* Fix potential negative minutes and refine Gemini 3 Pro grouping logic based on PR feedback

* The account change process has been sped up

* Address PR review: enhance error message and parameterize polling interval in process handler

* Address PR review: fix potential race condition in database backup logic

* fix: prevent renderer white screen and harden sentry gating

* feat:  localize model labels

* chore: enable hoisted node-linker

* chore: ignore .agent/.claude/.codex in prettier

---------

Co-authored-by: Draculabo <[email protected]>
This commit is contained in:
AngeloooooandDraculabo authored and GitHub committed 2026-02-19 19:37:30 +08:00
1 parent b9497648c6
commit 8ed4593f73
162 files changed
+2981 -2541

No files matched your search

@@ -32,16 +32,10 @@ function Composer({
) : isThread ? (
<AlsoSendToChannelField id={channelId} />
) : null}
{isEditing ? (
<EditActions />
) : isForwarding ? (
<ForwardActions />
) : (
<DefaultActions />
)}
{isEditing ? <EditActions /> : isForwarding ? <ForwardActions /> : <DefaultActions />}
<Footer onSubmit={onSubmit} />
</form>
)
);
}
```
@@ -61,7 +55,7 @@ function ChannelComposer() {
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
)
);
}
// Thread composer - adds "also send to channel" field
@@ -77,7 +71,7 @@ function ThreadComposer({ channelId }: { channelId: string }) {
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
)
);
}
// Edit composer - different footer actions
@@ -92,7 +86,7 @@ function EditComposer() {
<Composer.SaveEdit />
</Composer.Footer>
</Composer.Frame>
)
);
}
```
@@ -37,25 +37,21 @@ function Composer({
</Footer>
)}
</form>
)
);
}
```
**Correct (compound components with shared context):**
```tsx
const ComposerContext = createContext<ComposerContextValue | null>(null)
const ComposerContext = createContext<ComposerContextValue | null>(null);
function ComposerProvider({ children, state, actions, meta }: ProviderProps) {
return (
<ComposerContext value={{ state, actions, meta }}>
{children}
</ComposerContext>
)
return <ComposerContext value={{ state, actions, meta }}>{children}</ComposerContext>;
}
function ComposerFrame({ children }: { children: React.ReactNode }) {
return <form>{children}</form>
return <form>{children}</form>;
}
function ComposerInput() {
@@ -63,21 +59,21 @@ function ComposerInput() {
state,
actions: { update },
meta: { inputRef },
} = use(ComposerContext)
} = use(ComposerContext);
return (
<TextInput
ref={inputRef}
value={state.input}
onChangeText={(text) => update((s) => ({ ...s, input: text }))}
/>
)
);
}
function ComposerSubmit() {
const {
actions: { submit },
} = use(ComposerContext)
return <Button onPress={submit}>Send</Button>
} = use(ComposerContext);
return <Button onPress={submit}>Send</Button>;
}
// Export as compound component
@@ -91,7 +87,7 @@ const Composer = {
Attachments: ComposerAttachments,
Formatting: ComposerFormatting,
Emojis: ComposerEmojis,
}
};
```
**Usage:**
@@ -19,9 +19,9 @@ function Composer({
renderFooter,
renderActions,
}: {
renderHeader?: () => React.ReactNode
renderFooter?: () => React.ReactNode
renderActions?: () => React.ReactNode
renderHeader?: () => React.ReactNode;
renderFooter?: () => React.ReactNode;
renderActions?: () => React.ReactNode;
}) {
return (
<form>
@@ -30,7 +30,7 @@ function Composer({
{renderFooter ? renderFooter() : <DefaultFooter />}
{renderActions?.()}
</form>
)
);
}
// Usage is awkward and inflexible
@@ -45,18 +45,18 @@ return (
)}
renderActions={() => <SubmitButton />}
/>
)
);
```
**Correct (compound components with children):**
```tsx
function ComposerFrame({ children }: { children: React.ReactNode }) {
return <form>{children}</form>
return <form>{children}</form>;
}
function ComposerFooter({ children }: { children: React.ReactNode }) {
return <footer className='flex'>{children}</footer>
return <footer className="flex">{children}</footer>;
}
// Usage is flexible
@@ -70,17 +70,14 @@ return (
<SubmitButton />
</Composer.Footer>
</Composer.Frame>
)
);
```
**When render props are appropriate:**
```tsx
// Render props work well when you need to pass data back
<List
data={items}
renderItem={({ item, index }) => <Item item={item} index={index} />}
/>
<List data={items} renderItem={({ item, index }) => <Item item={item} index={index} />} />
```
Use render props when the parent needs to provide data or state to the child.
@@ -15,13 +15,7 @@ itself.
```tsx
// What does this component actually render?
<Composer
isThread
isEditing={false}
channelId='abc'
showAttachments
showFormatting={false}
/>
<Composer isThread isEditing={false} channelId="abc" showAttachments showFormatting={false} />
```
**Correct (explicit variants):**
@@ -56,7 +50,7 @@ function ThreadComposer({ channelId }: { channelId: string }) {
</Composer.Footer>
</Composer.Frame>
</ThreadProvider>
)
);
}
function EditMessageComposer({ messageId }: { messageId: string }) {
@@ -72,7 +66,7 @@ function EditMessageComposer({ messageId }: { messageId: string }) {
</Composer.Footer>
</Composer.Frame>
</EditMessageProvider>
)
);
}
function ForwardMessageComposer({ messageId }: { messageId: string }) {
@@ -87,7 +81,7 @@ function ForwardMessageComposer({ messageId }: { messageId: string }) {
</Composer.Footer>
</Composer.Frame>
</ForwardMessageProvider>
)
);
}
```
@@ -15,28 +15,28 @@ In React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `
```tsx
const ComposerInput = forwardRef<TextInput, Props>((props, ref) => {
return <TextInput ref={ref} {...props} />
})
return <TextInput ref={ref} {...props} />;
});
```
**Correct (ref as a regular prop):**
```tsx
function ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {
return <TextInput ref={ref} {...props} />
return <TextInput ref={ref} {...props} />;
}
```
**Incorrect (useContext in React 19):**
```tsx
const value = useContext(MyContext)
const value = useContext(MyContext);
```
**Correct (use instead of useContext):**
```tsx
const value = use(MyContext)
const value = use(MyContext);
```
`use()` can also be called conditionally, unlike `useContext()`.
@@ -20,8 +20,8 @@ dependency-injectable.
```tsx
function ComposerInput() {
// Tightly coupled to a specific hook
const { input, setInput } = useChannelComposerState()
return <TextInput value={input} onChangeText={setInput} />
const { input, setInput } = useChannelComposerState();
return <TextInput value={input} onChangeText={setInput} />;
}
```
@@ -30,27 +30,27 @@ function ComposerInput() {
```tsx
// Define a GENERIC interface that any provider can implement
interface ComposerState {
input: string
attachments: Attachment[]
isSubmitting: boolean
input: string;
attachments: Attachment[];
isSubmitting: boolean;
}
interface ComposerActions {
update: (updater: (state: ComposerState) => ComposerState) => void
submit: () => void
update: (updater: (state: ComposerState) => ComposerState) => void;
submit: () => void;
}
interface ComposerMeta {
inputRef: React.RefObject<TextInput>
inputRef: React.RefObject<TextInput>;
}
interface ComposerContextValue {
state: ComposerState
actions: ComposerActions
meta: ComposerMeta
state: ComposerState;
actions: ComposerActions;
meta: ComposerMeta;
}
const ComposerContext = createContext<ComposerContextValue | null>(null)
const ComposerContext = createContext<ComposerContextValue | null>(null);
```
**UI components consume the interface, not the implementation:**
@@ -61,7 +61,7 @@ function ComposerInput() {
state,
actions: { update },
meta,
} = use(ComposerContext)
} = use(ComposerContext);
// This component works with ANY provider that implements the interface
return (
@@ -70,7 +70,7 @@ function ComposerInput() {
value={state.input}
onChangeText={(text) => update((s) => ({ ...s, input: text }))}
/>
)
);
}
```
@@ -79,9 +79,9 @@ function ComposerInput() {
```tsx
// Provider A: Local state for ephemeral forms
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
const inputRef = useRef(null)
const submit = useForwardMessage()
const [state, setState] = useState(initialState);
const inputRef = useRef(null);
const submit = useForwardMessage();
return (
<ComposerContext
@@ -93,13 +93,13 @@ function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
>
{children}
</ComposerContext>
)
);
}
// Provider B: Global synced state for channels
function ChannelProvider({ channelId, children }: Props) {
const { state, update, submit } = useGlobalChannel(channelId)
const inputRef = useRef(null)
const { state, update, submit } = useGlobalChannel(channelId);
const inputRef = useRef(null);
return (
<ComposerContext
@@ -111,7 +111,7 @@ function ChannelProvider({ channelId, children }: Props) {
>
{children}
</ComposerContext>
)
);
}
```
@@ -165,21 +165,21 @@ function ForwardMessageDialog() {
</DialogActions>
</Dialog>
</ForwardMessageProvider>
)
);
}
// This button lives OUTSIDE Composer.Frame but can still submit based on its context!
function ForwardButton() {
const {
actions: { submit },
} = use(ComposerContext)
return <Button onPress={submit}>Forward</Button>
} = use(ComposerContext);
return <Button onPress={submit}>Forward</Button>;
}
// This preview lives OUTSIDE Composer.Frame but can read composer's state!
function MessagePreview() {
const { state } = use(ComposerContext)
return <Preview message={state.input} attachments={state.attachments} />
const { state } = use(ComposerContext);
return <Preview message={state.input} attachments={state.attachments} />;
}
```
@@ -16,18 +16,15 @@ useState, Zustand, or a server sync.
```tsx
function ChannelComposer({ channelId }: { channelId: string }) {
// UI component knows about global state implementation
const state = useGlobalChannelState(channelId)
const { submit, updateInput } = useChannelSync(channelId)
const state = useGlobalChannelState(channelId);
const { submit, updateInput } = useChannelSync(channelId);
return (
<Composer.Frame>
<Composer.Input
value={state.input}
onChange={(text) => sync.updateInput(text)}
/>
<Composer.Input value={state.input} onChange={(text) => sync.updateInput(text)} />
<Composer.Submit onPress={() => sync.submit()} />
</Composer.Frame>
)
);
}
```
@@ -39,21 +36,17 @@ function ChannelProvider({
channelId,
children,
}: {
channelId: string
children: React.ReactNode
channelId: string;
children: React.ReactNode;
}) {
const { state, update, submit } = useGlobalChannel(channelId)
const inputRef = useRef(null)
const { state, update, submit } = useGlobalChannel(channelId);
const inputRef = useRef(null);
return (
<Composer.Provider
state={state}
actions={{ update, submit }}
meta={{ inputRef }}
>
<Composer.Provider state={state} actions={{ update, submit }} meta={{ inputRef }}>
{children}
</Composer.Provider>
)
);
}
// UI component only knows about the context interface
@@ -66,7 +59,7 @@ function ChannelComposer() {
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
)
);
}
// Usage
@@ -75,7 +68,7 @@ function Channel({ channelId }: { channelId: string }) {
<ChannelProvider channelId={channelId}>
<ChannelComposer />
</ChannelProvider>
)
);
}
```
@@ -84,28 +77,25 @@ function Channel({ channelId }: { channelId: string }) {
```tsx
// Local state for ephemeral forms
function ForwardMessageProvider({ children }) {
const [state, setState] = useState(initialState)
const forwardMessage = useForwardMessage()
const [state, setState] = useState(initialState);
const forwardMessage = useForwardMessage();
return (
<Composer.Provider
state={state}
actions={{ update: setState, submit: forwardMessage }}
>
<Composer.Provider state={state} actions={{ update: setState, submit: forwardMessage }}>
{children}
</Composer.Provider>
)
);
}
// Global synced state for channels
function ChannelProvider({ channelId, children }) {
const { state, update, submit } = useGlobalChannel(channelId)
const { state, update, submit } = useGlobalChannel(channelId);
return (
<Composer.Provider state={state} actions={{ update, submit }}>
{children}
</Composer.Provider>
)
);
}
```
@@ -15,15 +15,15 @@ or awkward refs.
```tsx
function ForwardMessageComposer() {
const [state, setState] = useState(initialState)
const forwardMessage = useForwardMessage()
const [state, setState] = useState(initialState);
const forwardMessage = useForwardMessage();
return (
<Composer.Frame>
<Composer.Input />
<Composer.Footer />
</Composer.Frame>
)
);
}
// Problem: How does this button access composer state?
@@ -37,7 +37,7 @@ function ForwardMessageDialog() {
<ForwardButton /> {/* Needs to call submit */}
</DialogActions>
</Dialog>
)
);
}
```
@@ -45,20 +45,20 @@ function ForwardMessageDialog() {
```tsx
function ForwardMessageDialog() {
const [input, setInput] = useState('')
const [input, setInput] = useState('');
return (
<Dialog>
<ForwardMessageComposer onInputChange={setInput} />
<MessagePreview input={input} />
</Dialog>
)
);
}
function ForwardMessageComposer({ onInputChange }) {
const [state, setState] = useState(initialState)
const [state, setState] = useState(initialState);
useEffect(() => {
onInputChange(state.input) // Sync on every change 😬
}, [state.input])
onInputChange(state.input); // Sync on every change 😬
}, [state.input]);
}
```
@@ -66,13 +66,13 @@ function ForwardMessageComposer({ onInputChange }) {
```tsx
function ForwardMessageDialog() {
const stateRef = useRef(null)
const stateRef = useRef(null);
return (
<Dialog>
<ForwardMessageComposer stateRef={stateRef} />
<ForwardButton onPress={() => submit(stateRef.current)} />
</Dialog>
)
);
}
```
@@ -80,9 +80,9 @@ function ForwardMessageDialog() {
```tsx
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
const forwardMessage = useForwardMessage()
const inputRef = useRef(null)
const [state, setState] = useState(initialState);
const forwardMessage = useForwardMessage();
const inputRef = useRef(null);
return (
<Composer.Provider
@@ -92,7 +92,7 @@ function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
>
{children}
</Composer.Provider>
)
);
}
function ForwardMessageDialog() {
@@ -107,12 +107,12 @@ function ForwardMessageDialog() {
</DialogActions>
</Dialog>
</ForwardMessageProvider>
)
);
}
function ForwardButton() {
const { actions } = use(Composer.Context)
return <Button onPress={actions.submit}>Forward</Button>
const { actions } = use(Composer.Context);
return <Button onPress={actions.submit}>Forward</Button>;
}
```