(feat): version bump needed for new ttp version #4

Merged
alois merged 1 commit from dev into main 2026-05-21 22:31:56 +03:00
14 changed files with 203 additions and 44 deletions
Showing only changes of commit ba59a49f98 - Show all commits

(feat): improved conversation adding flow
All checks were successful
/ build-web (push) Successful in 1m8s
/ build-desktop (push) Successful in 11m11s
/ build-mobile (push) Successful in 15m40s
/ release (push) Successful in 21s

(feat): visual tweaks for user modal
(feat): now using full width for profile settings page on mobile
(fix): profile not saving with empty avatar
(chore): bump version due to prod release not getting created
(chore): update licenses
Alois 2026-05-21 12:15:37 +02:00

View file

@ -31,12 +31,12 @@ export function Basic({
<Tooltip>
<TooltipTrigger
render={
<div className="absolute -bottom-0.75 -right-0.75 z-10 flex h-4 w-4 items-center justify-center rounded-full bg-card">
<div className="absolute -bottom-0.5 -right-0.5 z-10 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-card">
<div
style={{
backgroundColor: getStatusColor(user.online_status),
}}
className="h-2.5 w-2.5 rounded-full"
className="h-2.25 w-2.25 rounded-full"
/>
</div>
}
@ -52,7 +52,7 @@ export function Basic({
<div className="flex flex-col gap-1 w-full items-start justify-center text-[15px]">
<p>{user.display}</p>
</div>
<div className="pr-2">{extra}</div>
<div className="pr-1">{extra}</div>
</CardHeader>
</Card>
);

View file

@ -23,7 +23,6 @@ export default function Navbar({ forMobile }: { forMobile: boolean }) {
const currentCalls = calls.filter((call) =>
call.call_members.some((member) => member === id),
);
//const currentCalls = ["leck", "schleck", "und", "eck"];
const [selectOpen, setSelectOpen] = useState(false);

View file

@ -220,7 +220,7 @@ export default function Sidebar() {
<button
type="button"
aria-label="Open profile menu"
className="inline-flex h-8 w-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground"
className="cursor-pointer inline-flex h-8 w-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground"
>
<Ellipsis />
</button>

View file

@ -76,6 +76,16 @@ export default function List() {
</div>
);
})}
<p
className="text-center text-sm px-2 text-muted-foreground transition-all duration-200 ease-in-out"
hidden={contacts.length !== 0 || category !== "conversations"}
style={{
opacity:
contacts.length === 0 && category === "conversations" ? 1 : 0,
}}
>
Get started by adding a conversation
</p>
</div>
</div>
</div>

View file

@ -11,12 +11,12 @@ import {
useIsMobile,
} from "@tensamin/ui";
import z from "zod";
import { toast } from "@tensamin/shared/log";
import { useTTP } from "@tensamin/ttp";
import { useState } from "react";
import { Loader2 } from "lucide-react";
import { isTauri } from "@tauri-apps/api/core";
import { useStorage } from "@tensamin/storage/context";
import { useSession } from "@tensamin/storage/session";
// The page
export default function Page() {
@ -43,11 +43,16 @@ export default function Page() {
// Add Conversation Button Component
function AddConversationButton() {
const { send } = useTTP();
const { contacts, insertContact } = useSession();
const [loading, setLoading] = useState(false);
const [open, setOpen] = useState(false);
const [error, setError] = useState<string | null>(null);
function submit(username: string | null) {
async function submit(username: string | null) {
if (loading) return;
setError(null);
// username check
const schema = z
.string()
.min(1, "Username is too short")
@ -56,26 +61,50 @@ function AddConversationButton() {
const result = schema.safeParse(username?.toLowerCase().trim());
if (!result.success) {
toast("error", result.error.issues[0].message);
setError(result.error.issues[0].message);
return;
}
// user existence check
const user = await send("get_user_data", {
username: result.data,
})
.then((data) => {
if (data.data.user_id === 0) {
throw new Error();
}
return data;
})
.catch(() => {
setError("User not found");
return;
});
if (!user) return;
// alrady added check
if (contacts.some((contact) => contact.user_id === user.data.user_id)) {
setError("Conversation already exists");
return;
}
// add the conv
const timeout = setTimeout(() => setLoading(true), 500);
send("add_conversation", {
chat_partner_name: result.data,
})
.then(() => {
toast("success", "Conversation added");
insertContact(user.data.user_id);
setOpen(false);
})
.catch((error) => {
if (String(error).includes("error_not_found")) {
toast("error", "User not found");
setError("User not found");
return;
}
toast("error", "Failed to add conversation, check console for details");
console.error("Failed to add conversation", error);
setError(String(error));
})
.finally(() => {
clearTimeout(timeout);
@ -84,7 +113,15 @@ function AddConversationButton() {
}
return (
<Dialog>
<Dialog
open={open}
onOpenChange={(value) => {
if (value) {
setError(null);
}
setOpen(value);
}}
>
<DialogTrigger render={<Button>Add Conversation</Button>} />
<DialogContent>
<DialogHeader>
@ -110,7 +147,10 @@ function AddConversationButton() {
name="username"
placeholder="Enter username..."
/>
<div className="flex w-full justify-end gap-1">
<div className="flex w-full justify-end items-center gap-1">
{error && (
<p className="text-sm text-destructive w-full">{error}</p>
)}
<DialogClose render={<Button variant="outline">Cancel</Button>} />
<Button disabled={loading} type="submit">
{loading && <Loader2 className="animate-spin" />} Continue

View file

@ -4,7 +4,9 @@ import {
AvatarFallback,
AvatarImage,
Button,
cn,
Input,
useIsMobile,
} from "@tensamin/ui";
import { useUser, type User } from "@tensamin/user/context";
import { useEffect, useRef, useState } from "react";
@ -83,6 +85,8 @@ export default function Page() {
}
};
const isMobile = useIsMobile();
return currentUser ? (
<>
<input
@ -93,7 +97,7 @@ export default function Page() {
}
type="file"
/>
<div className="flex flex-col gap-5 w-80">
<div className={cn("flex flex-col gap-5", isMobile ? "w-full" : "w-80")}>
<div className="flex items-center gap-3">
<Avatar className="size-14">
<AvatarImage src={effectiveAvatar} />
@ -121,7 +125,7 @@ export default function Page() {
</Button>
</div>
<p className="text-sm text-muted-foreground">
GIFs are supported in decentralised mode or with Tensamin Premium
GIFs are supported in decentralised mode or with Tensamin Premium.
<br />
Maximum file size is 16mb.
</p>
@ -162,13 +166,16 @@ export default function Page() {
/>
<Button
onClick={async () => {
const { avatar, ...draftUsersWithoutAvatar } = draftUser;
const payload = {
...draftUser,
avatar:
typeof draftUser.avatar === "string" &&
draftUser.avatar.startsWith("data:")
? (draftUser.avatar.split(",", 2)[1] ?? "")
: draftUser.avatar,
...draftUsersWithoutAvatar,
...(typeof avatar === "string"
? {
avatar: avatar.startsWith("data:")
? (avatar.split(",", 2)[1] ?? "")
: avatar,
}
: {}),
};
const validation = ttp.change_user_data.request.safeParse(payload);

View file

@ -163,18 +163,18 @@ Generated from bun.lock and installed packages in workspace node_modules folders
- Folder: `licenses/@tauri-apps_plugin-opener@2.5.4`
- Source package dir: `apps/tauri/node_modules/@tauri-apps/plugin-opener`
## @tensamin/ttp-core@0.0.15
## @tensamin/ttp-core@0.0.19
- License: UNKNOWN
- Included files: LICENSE
- Folder: `licenses/@tensamin_ttp-core@0.0.15`
- Folder: `licenses/@tensamin_ttp-core@0.0.19`
- Source package dir: `packages/ttp/node_modules/@tensamin/ttp-core`
## @tensamin/ui@0.0.30
## @tensamin/ui@0.0.34
- License: UNKNOWN
- Included files: none found
- Folder: `licenses/@tensamin_ui@0.0.30`
- Folder: `licenses/@tensamin_ui@0.0.34`
- Source package dir: `apps/tauri/node_modules/@tensamin/ui`
## @types/node@25.6.0
@ -449,6 +449,16 @@ Generated from bun.lock and installed packages in workspace node_modules folders
- Folder: `licenses/tailwindcss@4.2.4`
- Source package dir: `apps/web/node_modules/tailwindcss`
## tauri-plugin-app-events-api@0.2.0
- License: MIT
- Homepage: https://github.com/wtto00/tauri-plugin-app-events#readme
- Repository: git+https://github.com/wtto00/tauri-plugin-app-events.git
- Description: A plugin for tauri@v2 to listen some events on iOS and Android.
- Included files: LICENSE
- Folder: `licenses/tauri-plugin-app-events-api@0.2.0`
- Source package dir: `apps/web/node_modules/tauri-plugin-app-events-api`
## tw-animate-css@1.4.0
- License: MIT

View file

@ -3,7 +3,7 @@
"specVersion": "1.5",
"version": 1,
"metadata": {
"timestamp": "2026-05-04T13:05:17.472Z",
"timestamp": "2026-05-21T10:15:15.541Z",
"tools": [
{
"vendor": "OpenAI",
@ -580,15 +580,15 @@
},
{
"type": "library",
"bomRef": "pkg:npm/%40tensamin/ttp-core@0.0.15",
"bomRef": "pkg:npm/%40tensamin/ttp-core@0.0.19",
"name": "@tensamin/ttp-core",
"version": "0.0.15",
"purl": "pkg:npm/%40tensamin/ttp-core@0.0.15",
"version": "0.0.19",
"purl": "pkg:npm/%40tensamin/ttp-core@0.0.19",
"externalReferences": [],
"properties": [
{
"name": "local:licenseFolder",
"value": "licenses/@tensamin_ttp-core@0.0.15"
"value": "licenses/@tensamin_ttp-core@0.0.19"
},
{
"name": "local:sourcePackageDir",
@ -598,15 +598,15 @@
},
{
"type": "library",
"bomRef": "pkg:npm/%40tensamin/ui@0.0.30",
"bomRef": "pkg:npm/%40tensamin/ui@0.0.34",
"name": "@tensamin/ui",
"version": "0.0.30",
"purl": "pkg:npm/%40tensamin/ui@0.0.30",
"version": "0.0.34",
"purl": "pkg:npm/%40tensamin/ui@0.0.34",
"externalReferences": [],
"properties": [
{
"name": "local:licenseFolder",
"value": "licenses/@tensamin_ui@0.0.30"
"value": "licenses/@tensamin_ui@0.0.34"
},
{
"name": "local:sourcePackageDir",
@ -1562,6 +1562,41 @@
}
]
},
{
"type": "library",
"bomRef": "pkg:npm/tauri-plugin-app-events-api@0.2.0",
"name": "tauri-plugin-app-events-api",
"version": "0.2.0",
"purl": "pkg:npm/tauri-plugin-app-events-api@0.2.0",
"description": "A plugin for tauri@v2 to listen some events on iOS and Android.",
"licenses": [
{
"license": {
"id": "MIT"
}
}
],
"externalReferences": [
{
"type": "website",
"url": "https://github.com/wtto00/tauri-plugin-app-events#readme"
},
{
"type": "vcs",
"url": "git+https://github.com/wtto00/tauri-plugin-app-events.git"
}
],
"properties": [
{
"name": "local:licenseFolder",
"value": "licenses/tauri-plugin-app-events-api@0.2.0"
},
{
"name": "local:sourcePackageDir",
"value": "apps/web/node_modules/tauri-plugin-app-events-api"
}
]
},
{
"type": "library",
"bomRef": "pkg:npm/tw-animate-css@1.4.0",

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 简静凡
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,6 +1,6 @@
{
"generatedAt": "2026-05-04T13:05:17.471Z",
"packageCount": 53,
"generatedAt": "2026-05-21T10:15:15.540Z",
"packageCount": 54,
"packages": [
{
"name": "@codemirror/commands",
@ -227,7 +227,7 @@
},
{
"name": "@tensamin/ttp-core",
"version": "0.0.15",
"version": "0.0.19",
"license": "UNKNOWN",
"homepage": null,
"repository": null,
@ -235,18 +235,18 @@
"files": [
"LICENSE"
],
"licenseFolder": "licenses/@tensamin_ttp-core@0.0.15",
"licenseFolder": "licenses/@tensamin_ttp-core@0.0.19",
"sourcePackageDir": "packages/ttp/node_modules/@tensamin/ttp-core"
},
{
"name": "@tensamin/ui",
"version": "0.0.30",
"version": "0.0.34",
"license": "UNKNOWN",
"homepage": null,
"repository": null,
"description": null,
"files": [],
"licenseFolder": "licenses/@tensamin_ui@0.0.30",
"licenseFolder": "licenses/@tensamin_ui@0.0.34",
"sourcePackageDir": "apps/tauri/node_modules/@tensamin/ui"
},
{
@ -614,6 +614,19 @@
"licenseFolder": "licenses/tailwindcss@4.2.4",
"sourcePackageDir": "apps/web/node_modules/tailwindcss"
},
{
"name": "tauri-plugin-app-events-api",
"version": "0.2.0",
"license": "MIT",
"homepage": "https://github.com/wtto00/tauri-plugin-app-events#readme",
"repository": "git+https://github.com/wtto00/tauri-plugin-app-events.git",
"description": "A plugin for tauri@v2 to listen some events on iOS and Android.",
"files": [
"LICENSE"
],
"licenseFolder": "licenses/tauri-plugin-app-events-api@0.2.0",
"sourcePackageDir": "apps/web/node_modules/tauri-plugin-app-events-api"
},
{
"name": "tw-animate-css",
"version": "1.4.0",

View file

@ -1,6 +1,6 @@
{
"name": "tensamin",
"version": "0.0.1",
"version": "0.0.2",
"private": true,
"workspaces": [
"packages/*",

View file

@ -115,7 +115,8 @@ export const ttp = {
},
get_user_data: {
request: z.object({
user_id: z.number(),
user_id: z.number().optional(),
username: z.string().optional(),
}),
response: user,
},

View file

@ -14,6 +14,7 @@ interface SessionContextType {
communities: Communities;
calls: Calls;
moveUserIdToTop: (userId: number) => void;
insertContact: (userId: number) => void;
}
const SessionContext = createContext<SessionContextType | undefined>(undefined);
@ -69,9 +70,31 @@ export default function SessionProvider({ children }: { children: ReactNode }) {
});
};
const insertContact = (userId: number) => {
setContacts((prevContacts) => {
if (prevContacts.some((contact) => contact.user_id === userId)) {
return prevContacts;
}
const newUser = {
user_id: userId,
last_message_at: new Date().getTime(),
messages: [],
} satisfies Contacts[0];
return [newUser, ...prevContacts];
});
};
return (
<SessionContext.Provider
value={{ contacts, communities, calls: freshCalls, moveUserIdToTop }}
value={{
contacts,
communities,
calls: freshCalls,
moveUserIdToTop,
insertContact,
}}
>
{children}
</SessionContext.Provider>