diff --git a/pkg/interface/src/logic/lib/omnibox.js b/pkg/interface/src/logic/lib/omnibox.js
index eb1d547b0..d79ae08e8 100644
--- a/pkg/interface/src/logic/lib/omnibox.js
+++ b/pkg/interface/src/logic/lib/omnibox.js
@@ -110,8 +110,12 @@ export default function index(contacts, associations, apps, currentGroup, groups
landscape.push(obj);
} else {
const app = each.metadata.module || each['app-name'];
- const group = (groups[each.group]?.hidden)
- ? '/home' : each.group;
+ let group = each.group;
+ if (groups[each.group]?.hidden && app === 'chat') {
+ group = '/messages';
+ } else if (groups[each.group]?.hidden) {
+ group = '/home';
+ }
const obj = result(
title,
`/~landscape${group}/join/${app}${each.resource}`,
diff --git a/pkg/interface/src/logic/lib/util.ts b/pkg/interface/src/logic/lib/util.ts
index 679511522..c39f9607e 100644
--- a/pkg/interface/src/logic/lib/util.ts
+++ b/pkg/interface/src/logic/lib/util.ts
@@ -392,3 +392,16 @@ export const useHovering = (): useHoveringInterface => {
};
return { hovering, bind };
};
+
+const DM_REGEX = /ship\/~([a-z]|-)*\/dm--/;
+export function getItemTitle(association: Association) {
+ if(DM_REGEX.test(association.resource)) {
+ const [,,ship,name] = association.resource.split('/');
+ if(ship.slice(1) === window.ship) {
+ return cite(`~${name.slice(4)}`);
+ }
+ return cite(ship);
+
+ }
+ return association.metadata.title || association.resource
+};
diff --git a/pkg/interface/src/logic/lib/workspace.ts b/pkg/interface/src/logic/lib/workspace.ts
index 7f63b447a..e17b81e7b 100644
--- a/pkg/interface/src/logic/lib/workspace.ts
+++ b/pkg/interface/src/logic/lib/workspace.ts
@@ -7,6 +7,8 @@ export function getTitleFromWorkspace(
switch (workspace.type) {
case "home":
return "My Channels";
+ case "messages":
+ return "Messages";
case "group":
const association = associations.groups[workspace.group];
return association?.metadata?.title || "";
diff --git a/pkg/interface/src/types/workspace.ts b/pkg/interface/src/types/workspace.ts
index 5b4e68877..69da82ed3 100644
--- a/pkg/interface/src/types/workspace.ts
+++ b/pkg/interface/src/types/workspace.ts
@@ -9,4 +9,8 @@ interface HomeWorkspace {
type: 'home'
}
-export type Workspace = HomeWorkspace | GroupWorkspace;
+interface Messages {
+ type: 'messages'
+}
+
+export type Workspace = HomeWorkspace | GroupWorkspace | Messages;
diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js
index ca6cd9da6..94fa9f949 100644
--- a/pkg/interface/src/views/components/StatusBar.js
+++ b/pkg/interface/src/views/components/StatusBar.js
@@ -27,7 +27,7 @@ const StatusBar = (props) => {
const invites = [].concat(...Object.values(props.invites).map(obj => Object.values(obj)));
const metaKey = (window.navigator.platform.includes('Mac')) ? '⌘' : 'Ctrl+';
const { toggleOmnibox, hideAvatars } =
- useLocalState(({ toggleOmnibox, hideAvatars }) =>
+ useLocalState(({ toggleOmnibox, hideAvatars }) =>
({ toggleOmnibox, hideAvatars })
);
@@ -91,6 +91,9 @@ const StatusBar = (props) => {
>
Submit an issue
+ props.history.push('/~landscape/messages')}>
+
+
`${props.baseUrl}${to}`;
return (
diff --git a/pkg/interface/src/views/landscape/components/InvitePopover.tsx b/pkg/interface/src/views/landscape/components/InvitePopover.tsx
index 2f16a42a5..bcc851b15 100644
--- a/pkg/interface/src/views/landscape/components/InvitePopover.tsx
+++ b/pkg/interface/src/views/landscape/components/InvitePopover.tsx
@@ -47,10 +47,6 @@ export function InvitePopover(props: InvitePopoverProps) {
useOutsideClick(innerRef, onOutsideClick);
const onSubmit = async ({ ships, emails }: { ships: string[] }, actions) => {
- if(props.workspace.type === 'home') {
- history.push(`/~landscape/dm/${deSig(ships[0])}`);
- return;
- }
// TODO: how to invite via email?
try {
const resource = resourceFromPath(association.group);
@@ -105,14 +101,13 @@ export function InvitePopover(props: InvitePopoverProps) {
Invite to
- {title || "DM"}
+ {title}
diff --git a/pkg/interface/src/views/landscape/components/NewChannel.tsx b/pkg/interface/src/views/landscape/components/NewChannel.tsx
index 99f2e6af3..95fb4cf15 100644
--- a/pkg/interface/src/views/landscape/components/NewChannel.tsx
+++ b/pkg/interface/src/views/landscape/components/NewChannel.tsx
@@ -3,10 +3,7 @@ import {
Box,
ManagedTextInputField as Input,
Col,
- ManagedRadioButtonField as Radio,
- Text,
- Icon,
- Row
+ Text
} from '@tlon/indigo-react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
@@ -21,8 +18,8 @@ import { useWaitForProps } from '~/logic/lib/useWaitForProps';
import { Groups } from '~/types/group-update';
import { ShipSearch, shipSearchSchemaInGroup, shipSearchSchema } from '~/views/components/ShipSearch';
import { Rolodex, Workspace } from '~/types';
-import {IconRadio} from '~/views/components/IconRadio';
-import {ChannelWriteFieldSchema, ChannelWritePerms} from './ChannelWritePerms';
+import { IconRadio } from '~/views/components/IconRadio';
+import { ChannelWriteFieldSchema, ChannelWritePerms } from './ChannelWritePerms';
type FormSchema = {
name: string;
@@ -32,7 +29,7 @@ type FormSchema = {
} & ChannelWriteFieldSchema;
const formSchema = (members?: string[]) => Yup.object({
- name: Yup.string().required('Channel must have a name'),
+ name: Yup.string(),
description: Yup.string(),
ships: Yup.array(Yup.string()),
moduleType: Yup.string().required('Must choose channel type'),
@@ -55,11 +52,16 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) {
const waiter = useWaitForProps(props, 5000);
const onSubmit = async (values: FormSchema, actions) => {
+ const name = (values.name) ? values.name : values.moduleType;
const resId: string = stringToSymbol(values.name)
- + ((workspace?.type !== 'home') ? `-${Math.floor(Math.random() * 10000)}`
+ + ((workspace?.type !== 'messages') ? `-${Math.floor(Math.random() * 10000)}`
: '');
try {
- let { name, description, moduleType, ships, writers } = values;
+ let { description, moduleType, ships, writers } = values;
+ ships = ships.filter(e => e !== "");
+ if (workspace?.type === 'messages' && ships.length === 1) {
+ return history.push(`/~landscape/dm/${deSig(ships[0])}`);
+ }
if (group) {
await api.graph.createManagedGraph(
resId,
@@ -83,7 +85,6 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) {
writers.push(us);
await api.groups.addTag(resource, tag, writers);
}
-
} else {
await api.graph.createUnmanagedGraph(
resId,
@@ -115,13 +116,13 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) {
history.push(props.baseUrl)}>
{'<- Back'}
-
- New Channel
+
+ {workspace?.type === 'messages' ? 'Direct Message' : 'New Channel'}
-
+
Channel Type
-
-
-
+
+
+
- {(workspace?.type === 'home') ? (
+ {(workspace?.type === 'home' || workspace?.type === 'messages') ? (
) : (
+ />) : (
)}
-
- Create Channel
+ Create
diff --git a/pkg/interface/src/views/landscape/components/Resource.tsx b/pkg/interface/src/views/landscape/components/Resource.tsx
index e13136060..9cafcf5e7 100644
--- a/pkg/interface/src/views/landscape/components/Resource.tsx
+++ b/pkg/interface/src/views/landscape/components/Resource.tsx
@@ -28,14 +28,14 @@ type ResourceProps = StoreState & {
} & RouteComponentProps;
export function Resource(props: ResourceProps) {
- const { association, api, notificationsGraphConfig, groups } = props;
+ const { association, api, notificationsGraphConfig, groups, contacts } = props;
const app = association.metadata.module || association["app-name"];
const rid = association.resource;
const selectedGroup = association.group;
const relativePath = (p: string) =>
`${props.baseUrl}/resource/${app}${rid}${p}`;
- const skelProps = { api, association, groups };
+ const skelProps = { api, association, groups, contacts };
let title = props.association.metadata.title;
if ('workspace' in props) {
if ('group' in props.workspace && props.workspace.group in props.associations.groups) {
@@ -65,12 +65,12 @@ export function Resource(props: ResourceProps) {
render={(routeProps) => {
return (
);
diff --git a/pkg/interface/src/views/landscape/components/ResourceSkeleton.tsx b/pkg/interface/src/views/landscape/components/ResourceSkeleton.tsx
index f4caf0bdc..63bc35f86 100644
--- a/pkg/interface/src/views/landscape/components/ResourceSkeleton.tsx
+++ b/pkg/interface/src/views/landscape/components/ResourceSkeleton.tsx
@@ -15,6 +15,8 @@ import { ChannelSettings } from "./ChannelSettings";
import { ChannelMenu } from "./ChannelMenu";
import { NotificationGraphConfig, Groups } from "~/types";
import {isWriter} from "~/logic/lib/group";
+import urbitOb from 'urbit-ob';
+import { getItemTitle } from '~/logic/lib/util';
const TruncatedBox = styled(Box)`
white-space: pre;
@@ -24,6 +26,7 @@ const TruncatedBox = styled(Box)`
type ResourceSkeletonProps = {
groups: Groups;
+ contacts: any;
association: Association;
api: GlobalApi;
baseUrl: string;
@@ -35,16 +38,30 @@ type ResourceSkeletonProps = {
export function ResourceSkeleton(props: ResourceSkeletonProps) {
const { association, api, baseUrl, children, atRoot, groups } = props;
const app = association?.metadata?.module || association["app-name"];
- const rid = association.resource;
+ const rid = association.resource;
const group = groups[association.group];
- const workspace =
- group?.hidden ? "/home" : association.group;
+ let workspace = association.group;
- const title = props.title || association?.metadata?.title;
+ if (group?.hidden && app === "chat") {
+ workspace = "/messages";
+ } else if (group?.hidden) {
+ workspace = "/home";
+ }
+
+ let title = (workspace === "/messages")
+ ? getItemTitle(association)
+ : association?.metadata?.title;
+
+ let recipient = false;
+
+ if (urbitOb.isValidPatp(title)) {
+ recipient = title;
+ title = (props.contacts?.[title]?.nickname) ? props.contacts[title].nickname : title;
+ }
const [, , ship, resource] = rid.split("/");
- const resourcePath = (p: string) => baseUrl + `/resource/${app}/ship/${ship}/${resource}` + p;
+ const resourcePath = (p: string) => baseUrl + p;
const isOwn = `~${window.ship}` === ship;
let canWrite = (app === 'publish') ? true : false;
@@ -78,7 +95,16 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
{"<- Back"}
-
+
{title}
@@ -91,12 +117,13 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
color="gray"
>
- {association?.metadata?.description}
+ {(workspace === "/messages") ? recipient : association?.metadata?.description}
diff --git a/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx b/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx
index 9dfe49a96..8998d4b92 100644
--- a/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx
+++ b/pkg/interface/src/views/landscape/components/Sidebar/Sidebar.tsx
@@ -93,6 +93,8 @@ export function Sidebar(props: SidebarProps) {
handleSubmit={setConfig}
selected={selected || ''}
workspace={workspace}
+ api={props.api}
+ history={props.history}
/>
);
diff --git a/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx b/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx
index 0e53ecc27..40cf6ff81 100644
--- a/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx
+++ b/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx
@@ -1,13 +1,14 @@
import React from "react";
import _ from 'lodash';
-import { Icon, Row, Box, Text } from "@tlon/indigo-react";
+import { Icon, Row, Box, Text, BaseImage } from "@tlon/indigo-react";
import { SidebarAppConfigs, SidebarItemStatus } from "./Sidebar";
import { HoverBoxLink } from "~/views/components/HoverBox";
import { Groups, Association } from "~/types";
-
-import { cite, getModuleIcon } from "~/logic/lib/util";
+import { Sigil } from '~/logic/lib/sigil';
+import urbitOb from 'urbit-ob';
+import { getModuleIcon, getItemTitle, uxToHex } from "~/logic/lib/util";
function SidebarItemIndicator(props: { status?: SidebarItemStatus }) {
switch (props.status) {
@@ -24,31 +25,18 @@ function SidebarItemIndicator(props: { status?: SidebarItemStatus }) {
}
}
-;
-
-const DM_REGEX = /ship\/~([a-z]|-)*\/dm--/;
-function getItemTitle(association: Association) {
- if(DM_REGEX.test(association.resource)) {
- const [,,ship,name] = association.resource.split('/');
- if(ship.slice(1) === window.ship) {
- return cite(`~${name.slice(4)}`);
- }
- return cite(ship);
-
- }
- return association.metadata.title || association.resource
-}
-
export function SidebarItem(props: {
hideUnjoined: boolean;
association: Association;
+ contacts: any;
groups: Groups;
path: string;
selected: boolean;
apps: SidebarAppConfigs;
+ workspace: Workspace;
}) {
const { association, path, selected, apps, groups } = props;
- const title = getItemTitle(association);
+ let title = getItemTitle(association);
const appName = association?.["app-name"];
const mod = association?.metadata?.module || appName;
const rid = association?.resource
@@ -58,12 +46,19 @@ export function SidebarItem(props: {
if (!app) {
return null;
}
+ const DM = (isUnmanaged && props.workspace?.type === "messages");
const itemStatus = app.getStatus(path);
const hasUnread = itemStatus === "unread" || itemStatus === "mention";
const isSynced = itemStatus !== "unsubscribed";
- const baseUrl = isUnmanaged ? `/~landscape/home` : `/~landscape${groupPath}`;
+ let baseUrl = `/~landscape${groupPath}`;
+
+ if (DM) {
+ baseUrl = '/~landscape/messages';
+ } else if (isUnmanaged) {
+ baseUrl = '/~landscape/home';
+ }
const to = isSynced
? `${baseUrl}/resource/${mod}${rid}`
@@ -75,6 +70,21 @@ export function SidebarItem(props: {
return null;
}
+ let img = null;
+
+ if (urbitOb.isValidPatp(title)) {
+ if (props.contacts?.[title] && props.contacts[title].avatar) {
+ img = ;
+ } else {
+ img =
+ }
+ if (props.contacts?.[title] && props.contacts[title].nickname) {
+ title = props.contacts[title].nickname;
+ }
+ } else {
+ img =
+ }
+
return (
-
+ {DM ? img : (
+
+ )
+ }
{
const assoc = associations[a];
- return group
- ? assoc.group === group
- : !(assoc.group in props.associations.groups);
+ if (workspace?.type === 'messages') {
+ return (!(assoc.group in props.associations.groups) && assoc.metadata.module === "chat");
+ } else {
+ return group
+ ? assoc.group === group
+ : (!(assoc.group in props.associations.groups) && assoc.metadata.module !== "chat");
+ }
})
.sort(sidebarSort(associations, props.apps)[config.sortBy]);
@@ -69,6 +75,8 @@ export function SidebarList(props: {
apps={props.apps}
hideUnjoined={config.hideUnjoined}
groups={props.groups}
+ contacts={props.contacts}
+ workspace={workspace}
/>
);
})}
diff --git a/pkg/interface/src/views/landscape/components/Sidebar/SidebarListHeader.tsx b/pkg/interface/src/views/landscape/components/Sidebar/SidebarListHeader.tsx
index 6d49d10f9..66364ac66 100644
--- a/pkg/interface/src/views/landscape/components/Sidebar/SidebarListHeader.tsx
+++ b/pkg/interface/src/views/landscape/components/Sidebar/SidebarListHeader.tsx
@@ -17,8 +17,11 @@ import { Link, useHistory } from 'react-router-dom';
import { getGroupFromWorkspace } from "~/logic/lib/workspace";
import { roleForShip } from "~/logic/lib/group";
import {Groups, Rolodex, Associations} from "~/types";
+import { NewChannel } from "~/views/landscape/components/NewChannel";
+import GlobalApi from "~/logic/api/global";
export function SidebarListHeader(props: {
+ api: GlobalApi;
initialValues: SidebarListConfig;
associations: Associations;
groups: Groups;
@@ -40,10 +43,12 @@ export function SidebarListHeader(props: {
const groupPath = getGroupFromWorkspace(props.workspace);
const role = props.groups?.[groupPath] ? roleForShip(props.groups[groupPath], window.ship) : undefined;
- const memberMetadata =
+ const memberMetadata =
groupPath ? props.associations.contacts?.[groupPath].metadata.vip === 'member-metadata' : false;
- const isAdmin = memberMetadata || (role === "admin") || (props.workspace?.type === 'home');
+ const isAdmin = memberMetadata || (role === "admin") || (props.workspace?.type === 'home') || (props.workspace?.type === "messages");
+
+ const noun = (props.workspace?.type === "messages") ? "Messages" : "Channels";
return (
- {props.initialValues.hideUnjoined ? "Joined Channels" : "All Channels"}
+ {props.initialValues.hideUnjoined ? `Joined ${noun}` : `All ${noun}`}
-
+
+
+ }
+ >
+
+
+ )
+ : (
+
+ to={!!groupPath
+ ? `/~landscape${groupPath}/new`
+ : `/~landscape/${props.workspace?.type}/new`}>
-
-
- + DM
-
-
+ )
+ }
{props.children}