diff --git a/bun.lock b/bun.lock
index 3050f1a..57d87bd 100644
--- a/bun.lock
+++ b/bun.lock
@@ -62,7 +62,7 @@
"dependencies": {
"@auth/core": "^0.40.0",
"@convex-dev/auth": "^0.0.87",
- "convex": "^1.25.2",
+ "convex": "^1.27.1",
"resend": "^4.7.0",
},
"devDependencies": {
@@ -1101,6 +1101,8 @@
"@mmailaender/convex-auth-svelte/path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="],
+ "@packages/convex/convex": ["convex@1.27.1", "", { "dependencies": { "esbuild": "0.25.4", "jwt-decode": "^4.0.0", "prettier": "^3.0.0" }, "peerDependencies": { "@auth0/auth0-react": "^2.0.1", "@clerk/clerk-react": "^4.12.8 || ^5.0.0", "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" }, "optionalPeers": ["@auth0/auth0-react", "@clerk/clerk-react", "react"], "bin": { "convex": "bin/main.js" } }, "sha512-kep7JFn5Bil9/OUZUsL1bgoo0G9DmEf7stkBW67+NqP2FrzBt2TX8yz4V6oKLygzepGy90Ura2FtqXawYKXYIg=="],
+
"@storybook/csf-plugin/unplugin": ["unplugin@1.16.1", "", { "dependencies": { "acorn": "^8.14.0", "webpack-virtual-modules": "^0.6.2" } }, "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.5", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.4", "tslib": "^2.4.0" }, "bundled": true }, "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q=="],
diff --git a/packages/client/src/components/channels/Channel.svelte b/packages/client/src/components/channels/Channel.svelte
index 9a13fc8..9bb37ad 100644
--- a/packages/client/src/components/channels/Channel.svelte
+++ b/packages/client/src/components/channels/Channel.svelte
@@ -30,5 +30,5 @@
{/if}
-
+
diff --git a/packages/client/src/components/chat/MessageInput.svelte b/packages/client/src/components/chat/MessageInput.svelte
index 46a0041..d3736b7 100644
--- a/packages/client/src/components/chat/MessageInput.svelte
+++ b/packages/client/src/components/chat/MessageInput.svelte
@@ -1,13 +1,14 @@
diff --git a/packages/client/src/components/chat/ReactionList.svelte b/packages/client/src/components/chat/ReactionList.svelte
index 01db719..71dd0e8 100644
--- a/packages/client/src/components/chat/ReactionList.svelte
+++ b/packages/client/src/components/chat/ReactionList.svelte
@@ -3,10 +3,11 @@
import { useQuery } from "convex-svelte";
interface Props {
+ organizationId: Id<"organizations">;
messageId: Id<"messages">;
}
- let { messageId }: Props = $props();
+ let { organizationId, messageId }: Props = $props();
const reactions = useQuery(api.messages.getReactions, () => ({ messageId }));
@@ -30,8 +31,13 @@
reactions.data ? [...new Set(reactions.data.map((r) => r.userId))] : [],
);
- const userNamesById = useQuery(api.users.getUserNames, () => ({
+ // const userNamesById = useQuery(api.users.getUserNames, () => ({
+ // userIds: allUserIdsInReactions,
+ // }));
+
+ const userNamesById = useQuery(api.users.getUserNicknames, () => ({
userIds: allUserIdsInReactions,
+ organizationId: organizationId,
}));
function toggleUserList(emoji: string) {
diff --git a/packages/client/src/components/chat/VoteMaker.svelte b/packages/client/src/components/chat/VoteMaker.svelte
new file mode 100644
index 0000000..5dceba8
--- /dev/null
+++ b/packages/client/src/components/chat/VoteMaker.svelte
@@ -0,0 +1,71 @@
+
+
+
+
+
+
一人が投票できる最大数:
+
{
+ vote.maxVotes = vote.maxVotes ?? 0;
+ }}
+ />
+
+
+ {#each vote.voteOptions as option, i}
+
+
{i}:{option}
+
+
+ {/each}
+
+
+
+
選択肢を追加:
+
+
+
diff --git a/packages/client/src/components/chat/VoteViewer.svelte b/packages/client/src/components/chat/VoteViewer.svelte
new file mode 100644
index 0000000..d5fe7b5
--- /dev/null
+++ b/packages/client/src/components/chat/VoteViewer.svelte
@@ -0,0 +1,107 @@
+
+
+
+
投票:
+
{vote.data?.title}
+
+ 一人の最大投票数:{vote.data?.maxVotes}票
+
+ {#each vote.data?.voteOptions as option, i}
+
+
+ {option}{isResultVisible
+ ? ":" + numbersOfVotersPerOption[i] + "人"
+ : ""}
+
+
+
+ {/each}
+
+
diff --git a/packages/convex/package.json b/packages/convex/package.json
index 98c7b83..6b163d0 100644
--- a/packages/convex/package.json
+++ b/packages/convex/package.json
@@ -16,7 +16,7 @@
"dependencies": {
"@auth/core": "^0.40.0",
"@convex-dev/auth": "^0.0.87",
- "convex": "^1.25.2",
+ "convex": "^1.27.1",
"resend": "^4.7.0"
}
}
diff --git a/packages/convex/src/convex/messages.ts b/packages/convex/src/convex/messages.ts
index e8311f6..ac5365a 100644
--- a/packages/convex/src/convex/messages.ts
+++ b/packages/convex/src/convex/messages.ts
@@ -27,6 +27,7 @@ export const send = mutation({
author: v.string(),
parentId: v.optional(v.id("messages")),
attachments: v.optional(v.array(v.id("files"))),
+ vote: v.optional(v.id("votes")),
},
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
@@ -54,6 +55,7 @@ export const send = mutation({
createdAt: Date.now(),
parentId: args.parentId,
attachments: args.attachments,
+ vote: args.vote,
});
},
});
diff --git a/packages/convex/src/convex/schema.ts b/packages/convex/src/convex/schema.ts
index 044203e..b0f147e 100644
--- a/packages/convex/src/convex/schema.ts
+++ b/packages/convex/src/convex/schema.ts
@@ -42,6 +42,8 @@ export default defineSchema({
parentId: v.optional(v.id("messages")),
// 添付ファイル
attachments: v.optional(v.array(v.id("files"))),
+ //投票
+ vote: v.optional(v.id("votes")),
}).index("by_channel", ["channelId"]),
reactions: defineTable({
messageId: v.id("messages"),
@@ -51,6 +53,18 @@ export default defineSchema({
})
.index("by_message", ["messageId"])
.index("by_user", ["userId"]),
+ votes: defineTable({
+ title: v.string(),
+ maxVotes: v.number(),
+ //numberOfOptions: v.number(),
+ voteOptions: v.array(v.string()),
+ voters: v.array(
+ v.object({
+ userId: v.id("users"),
+ votedOptions: v.array(v.number()),
+ }),
+ ),
+ }),
personalization: defineTable({
userId: v.id("users"),
organizationId: v.id("organizations"),
diff --git a/packages/convex/src/convex/users.ts b/packages/convex/src/convex/users.ts
index 2d795cb..757267f 100644
--- a/packages/convex/src/convex/users.ts
+++ b/packages/convex/src/convex/users.ts
@@ -31,3 +31,36 @@ export const getUserNames = query({
return userNames;
},
});
+
+export const getUserNicknames = query({
+ args: {
+ userIds: v.array(v.id("users")),
+ organizationId: v.id("organizations"),
+ },
+ handler: async (ctx, { userIds, organizationId }) => {
+ const users = await Promise.all(
+ userIds.map((userId) => ctx.db.get(userId)),
+ );
+ const personalizations = await Promise.all(
+ userIds.map((userId) =>
+ ctx.db
+ .query("personalization")
+ .filter((q) => q.eq(q.field("userId"), userId))
+ .filter((q) => q.eq(q.field("organizationId"), organizationId))
+ .unique(),
+ ),
+ );
+ const userNicknames: Record
, string> = Object.fromEntries(
+ users
+ .filter((user) => user !== null)
+ .map((user) => [
+ user._id,
+ personalizations.find((p) => p?.userId === user._id)?.nickname ??
+ user.name ??
+ "",
+ ]),
+ );
+
+ return userNicknames;
+ },
+});
diff --git a/packages/convex/src/convex/vote.ts b/packages/convex/src/convex/vote.ts
new file mode 100644
index 0000000..f337a7c
--- /dev/null
+++ b/packages/convex/src/convex/vote.ts
@@ -0,0 +1,50 @@
+import { v } from "convex/values";
+import type { Id } from "./_generated/dataModel";
+import { mutation, query } from "./_generated/server";
+
+export const addVote = mutation({
+ args: {
+ title: v.string(),
+ maxVotes: v.number(),
+ voteOptions: v.array(v.string()),
+ },
+ handler: async (ctx, args) => {
+ const id = await ctx.db.insert("votes", {
+ title: args.title,
+ maxVotes: args.maxVotes,
+ voteOptions: args.voteOptions,
+ voters: [],
+ });
+ return id;
+ },
+});
+
+export const getVote = query({
+ args: {
+ id: v.id("votes"),
+ },
+ handler: async (ctx, args) => {
+ return await ctx.db.get(args.id);
+ },
+});
+
+export const vote = mutation({
+ args: {
+ voteId: v.id("votes"),
+ userId: v.id("users"),
+ votedOptions: v.array(v.number()),
+ },
+ handler: async (ctx, args) => {
+ const vote = await ctx.db.get(args.voteId);
+ const tempVoters = vote?.voters.filter(
+ (v: { userId: Id<"users">; votedOptions: Array }) =>
+ v.userId !== args.userId,
+ );
+ await ctx.db.patch(args.voteId, {
+ voters: [
+ ...tempVoters,
+ { userId: args.userId, votedOptions: args.votedOptions },
+ ],
+ });
+ },
+});