-
+
{"__context" in canvas && (
diff --git a/packages/social-media-app/frontend/src/canvas/toolbar/DebugGeneratePostButton.tsx b/packages/social-media-app/frontend/src/canvas/toolbar/DebugGeneratePostButton.tsx
index 5fe1bbd6..b89d741e 100644
--- a/packages/social-media-app/frontend/src/canvas/toolbar/DebugGeneratePostButton.tsx
+++ b/packages/social-media-app/frontend/src/canvas/toolbar/DebugGeneratePostButton.tsx
@@ -12,6 +12,7 @@ import {
StaticImage,
StaticMarkdownText,
} from "@dao-xyz/social";
+import { Ed25519Keypair } from "@peerbit/crypto";
const generateATextInMarkdown = (length: number = 100) => {
let text = "";
@@ -114,14 +115,20 @@ export const DebugGeneratePostButton = () => {
["image", "image"],
"text",
];
- for (const type of postsToCreate) {
+
+ for (const [px, type] of postsToCreate.entries()) {
+ let publicKeyTuse =
+ px % 2 === 0
+ ? peer.identity.publicKey
+ : (await Ed25519Keypair.create()).publicKey;
+
const typeArray = Array.isArray(type) ? type : [type];
// create a post (canvas) that references its parent
const canvas = new Canvas({
parent: new CanvasAddressReference({
canvas: path[path.length - 1],
}),
- publicKey: peer.identity.publicKey,
+ publicKey: publicKeyTuse,
});
// open it (so we can insert elements)
diff --git a/packages/social-media-app/frontend/src/content/Frame.tsx b/packages/social-media-app/frontend/src/content/Frame.tsx
index 78723be9..d21f6e4c 100644
--- a/packages/social-media-app/frontend/src/content/Frame.tsx
+++ b/packages/social-media-app/frontend/src/content/Frame.tsx
@@ -6,6 +6,29 @@ import { useApps } from "./useApps";
import { CuratedWebApp } from "@giga-app/app-service";
import { HostProvider as GigaHost } from "@giga-app/sdk";
+/**
+ * Frame component for displaying different types of content with controls.
+ *
+ * @param props - Component props
+ * @param props.pending - Whether the frame is in a pending state
+ * @param props.element - The element to display
+ * @param props.active - Whether the frame is in active state
+ * @param props.setActive - Function to set the active state
+ * @param props.editMode - Whether the content is in edit mode
+ * @param props.showCanvasControls - Whether to show canvas controls
+ * @param props.thumbnail - Whether to display as a thumbnail
+ * @param props.replace - Function to replace content with a new URL
+ * @param props.onLoad - Callback when the content loads
+ * @param props.onContentChange - Callback when the content changes
+ * @param props.key - React key for the component
+ * @param props.delete - Function to delete the frame
+ * @param props.fit - How this frame should fit in its container
+ * @param props.previewLines - Number of lines (text) to show in preview mode
+ * @param props.noPadding - Whether to remove padding from contained apps
+ * @param props.onClick - Callback when the frame is clicked
+ *
+ * @returns Frame component with appropriate content and controls
+ */
export const Frame = (properties: {
pending: boolean;
element: Element;
diff --git a/packages/social-media-app/frontend/src/content/native/Markdown.tsx b/packages/social-media-app/frontend/src/content/native/Markdown.tsx
index bd4d7f73..d9cee8d5 100644
--- a/packages/social-media-app/frontend/src/content/native/Markdown.tsx
+++ b/packages/social-media-app/frontend/src/content/native/Markdown.tsx
@@ -19,6 +19,20 @@ const textHasOneOrMoreLines = (text: string) => {
return text.split("\n").length > 1;
};
+/**
+ * Component for rendering markdown content with various display options.
+ *
+ * @param props - Component props
+ * @param props.content - The markdown content to display
+ * @param props.onResize - Callback when the content resizes, provides dimensions {width, height}
+ * @param props.editable - Whether the content can be edited by the user (default: false)
+ * @param props.onChange - Callback when content is changed during editing
+ * @param props.thumbnail - Whether the content is displayed as a thumbnail
+ * @param props.previewLines - Number of lines to show in preview mode, content will be truncated
+ * @param props.noPadding - Whether to remove padding from the container
+ *
+ * @returns Rendered markdown content
+ */
export const MarkdownContent = ({
content,
onResize,
@@ -43,7 +57,7 @@ export const MarkdownContent = ({
useEffect(() => {
if (!containerRef.current) return;
const observer = new ResizeObserver((entries) => {
- for (let entry of entries) {
+ for (const entry of entries) {
const { width, height } = entry.contentRect;
const newDims = { width, height };
if (
diff --git a/packages/social-media-app/frontend/src/content/native/NativeContent.tsx b/packages/social-media-app/frontend/src/content/native/NativeContent.tsx
index 1641ca89..af33e381 100644
--- a/packages/social-media-app/frontend/src/content/native/NativeContent.tsx
+++ b/packages/social-media-app/frontend/src/content/native/NativeContent.tsx
@@ -7,6 +7,9 @@ import { MarkdownContent } from "./Markdown";
import { ImageContent } from "./image/Image";
import { ChangeCallback } from "./types";
+/**
+ * Props for the EditableStaticContent component.
+ */
export type EditableStaticContentProps = {
staticContent: StaticContent["content"];
onResize: (dims: { width: number; height: number }) => void;
@@ -19,6 +22,21 @@ export type EditableStaticContentProps = {
inFullscreen?: boolean;
};
+/**
+ * Component for rendering different types of static content with editing capabilities.
+ *
+ * @param props - Component props
+ * @param props.staticContent - The static content to display
+ * @param props.onResize - Callback when the content resizes, provides dimensions {width, height}
+ * @param props.editable - Whether the content can be edited by the user (default: false)
+ * @param props.onChange - Callback when content is changed during editing
+ * @param props.thumbnail - Whether the content is displayed as a thumbnail
+ * @param props.fit - How images should fit in their container (cover or contain)
+ * @param props.previewLines - Number of lines to show in preview mode, content will be truncated
+ * @param props.noPadding - Whether to remove padding from the container
+ *
+ * @returns Rendered content based on type
+ */
export const EditableStaticContent = ({
staticContent,
onResize,
diff --git a/packages/social-media-app/frontend/src/context/CanvasPath.tsx b/packages/social-media-app/frontend/src/context/CanvasPath.tsx
index 7740d3e4..90489783 100644
--- a/packages/social-media-app/frontend/src/context/CanvasPath.tsx
+++ b/packages/social-media-app/frontend/src/context/CanvasPath.tsx
@@ -1,13 +1,11 @@
import { useEffect, useState, useRef } from "react";
import { useLocation, useNavigate } from "react-router-dom";
-import TagInput from "./TagInput"; // import the controlled TagInput above
import { useCanvases } from "../canvas/useCanvas";
import { getCanvasPath } from "../routes";
-import { IoIosArrowBack } from "react-icons/io";
-import { Canvas } from "../canvas/Canvas";
import { Canvas as CanvasDB } from "@dao-xyz/social";
import { CanvasWrapper } from "../canvas/CanvasWrapper";
import { CanvasPreview } from "../canvas/Preview";
+import { tw } from "../utils/tailwind";
export const CanvasPath = ({
isBreadcrumbExpanded,
@@ -79,26 +77,27 @@ export const CanvasPath = ({
setIsBreadcrumbExpanded((breadcrumb) => !breadcrumb)
}
>
-
- {path.slice(0).map((x, ix) => {
- return (
-
+ {path.slice(0).map((x, ix) => (
+ <>
+
- {ix > 1 && (
-
- /
-
+ /
+
+
- {renderBreadcrumb(x, ix)}
-
-
- );
- })}
+ >
+ {renderBreadcrumb(x, ix)}
+
+ >
+ ))}
{
);
return (
-
+
{path.slice(1).map((p, i) => (
))}
diff --git a/packages/social-media-app/frontend/src/identity/useIdentities.tsx b/packages/social-media-app/frontend/src/identity/useIdentities.tsx
index 70066ce2..68721fbb 100644
--- a/packages/social-media-app/frontend/src/identity/useIdentities.tsx
+++ b/packages/social-media-app/frontend/src/identity/useIdentities.tsx
@@ -1,8 +1,7 @@
import { useLocal, usePeer, useProgram } from "@peerbit/react";
import React, { useContext } from "react";
-import { Connection, Identities, Profiles } from "@dao-xyz/social";
+import { Connection, Identities } from "@dao-xyz/social";
import { And, BoolQuery, ByteMatchQuery, Or } from "@peerbit/indexer-interface";
-import { CiCircleRemove } from "react-icons/ci";
interface IIdentitiesContext {
identities?: Identities;
diff --git a/packages/social-media-app/frontend/src/utils/tailwind.ts b/packages/social-media-app/frontend/src/utils/tailwind.ts
new file mode 100644
index 00000000..a48c76bd
--- /dev/null
+++ b/packages/social-media-app/frontend/src/utils/tailwind.ts
@@ -0,0 +1,27 @@
+export function TW(...strings: (string | undefined)[]): TWClass {
+ return new TWClass(strings);
+}
+
+export function tw(...strings: (string | undefined)[]): string {
+ return TW(...strings).toString();
+}
+
+class TWClass {
+ private classes: string[];
+
+ constructor(classes: (string | undefined)[]) {
+ this.classes = classes.filter(
+ (cls): cls is string => cls !== undefined
+ );
+ }
+
+ toString(): string {
+ return this.classes.join(" ");
+ }
+}
+
+// Make TypeScript happy with the dual function/constructor pattern
+export interface TW {
+ (...strings: (string | undefined)[]): TWClass;
+ new (...strings: (string | undefined)[]): TWClass;
+}
diff --git a/packages/social-media-app/frontend/src/view/View.tsx b/packages/social-media-app/frontend/src/view/View.tsx
new file mode 100644
index 00000000..b5838625
--- /dev/null
+++ b/packages/social-media-app/frontend/src/view/View.tsx
@@ -0,0 +1,40 @@
+import React, { createContext, useState, useContext } from "react";
+
+// Define the view type
+export type ViewType = "chat" | "thread";
+
+// Create the context
+const ViewContext = createContext<
+ | {
+ view: ViewType;
+ setView: React.Dispatch
>;
+ }
+ | undefined
+>(undefined);
+
+// Custom hook to use the view context
+export const useView = () => {
+ const context = useContext(ViewContext);
+ if (!context) {
+ throw new Error("useView must be used within a ViewProvider");
+ }
+ return context;
+};
+
+// Provider component
+export const ViewProvider = ({
+ children,
+ initialView = "chat" as ViewType,
+}) => {
+ const [view, setView] = useState(initialView);
+
+ // Value object to be provided to consumers
+ const value = {
+ view,
+ setView,
+ };
+
+ return (
+ {children}
+ );
+};
diff --git a/yarn.lock b/yarn.lock
index 2819abf8..691b55a6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9594,7 +9594,7 @@ mdast-util-find-and-replace@^3.0.0:
unist-util-is "^6.0.0"
unist-util-visit-parents "^6.0.0"
-mdast-util-from-markdown@^2.0.0:
+mdast-util-from-markdown@^2.0.0, mdast-util-from-markdown@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a"
integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==
@@ -12860,7 +12860,16 @@ string-natural-compare@^3.0.1:
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
-"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0":
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -12968,7 +12977,14 @@ stringify-entities@^4.0.0:
character-entities-html4 "^2.0.0"
character-entities-legacy "^3.0.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -13950,7 +13966,7 @@ workerpool@^6.5.1:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544"
integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -13968,6 +13984,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"