aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/DocServer.ts10
-rw-r--r--src/client/documents/Documents.ts12
-rw-r--r--src/client/util/CurrentUserUtils.ts56
-rw-r--r--src/client/util/GroupManager.tsx12
-rw-r--r--src/client/util/SearchUtil.ts2
-rw-r--r--src/client/util/SettingsManager.tsx4
-rw-r--r--src/client/util/SharingManager.tsx68
-rw-r--r--src/client/views/DocComponent.tsx4
-rw-r--r--src/client/views/GlobalKeyHandler.ts3
-rw-r--r--src/client/views/MainView.tsx69
-rw-r--r--src/client/views/PropertiesView.tsx23
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx2
-rw-r--r--src/client/views/collections/CollectionMenu.tsx7
-rw-r--r--src/client/views/collections/CollectionSubView.tsx7
-rw-r--r--src/client/views/collections/CollectionView.tsx4
-rw-r--r--src/client/views/nodes/DocumentView.tsx2
-rw-r--r--src/client/views/nodes/FontIconBox.tsx2
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts8
18 files changed, 153 insertions, 142 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index d7dfa4498..9683eab45 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -9,6 +9,7 @@ import { GestureOverlay } from './views/GestureOverlay';
import MobileInkOverlay from '../mobile/MobileInkOverlay';
import { runInAction } from 'mobx';
import { ObjectField } from '../fields/ObjectField';
+import { StrCast } from '../fields/Types';
/**
* This class encapsulates the transfer and cross-client synchronization of
@@ -25,6 +26,15 @@ import { ObjectField } from '../fields/ObjectField';
*/
export namespace DocServer {
let _cache: { [id: string]: RefField | Promise<Opt<RefField>> } = {};
+
+ export function PRINT_CACHE() {
+ const strings: string[] = [];
+ Array.from(Object.keys(_cache)).forEach(key => {
+ const doc = _cache[key];
+ if (doc instanceof Doc) strings.push(StrCast(doc.author) + " " + StrCast(doc.title) + " " + StrCast(Doc.GetT(doc, "title", "string", true)));
+ });
+ strings.sort().forEach((str, i) => console.log(i.toString() + " " + str));
+ }
export let _socket: SocketIOClient.Socket;
// this client's distinct GUID created at initialization
let GUID: string;
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 47fa7067d..7ee8267f8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -574,7 +574,7 @@ export namespace Docs {
* only when creating a DockDocument from the current user's already existing
* main document.
*/
- export function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data") {
+ export function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data", protoId?: string) {
const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys, "^_");
protoProps.system = delegateProps.system;
@@ -590,7 +590,7 @@ export namespace Docs {
protoProps.isPrototype = true;
- const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey);
+ const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey, protoId);
const viewDoc = Doc.MakeDelegate(dataDoc, delegId);
// so that the list of annotations is already initialised, prevents issues in addonly.
@@ -620,8 +620,8 @@ export namespace Docs {
* @param options initial values to apply to this new delegate
* @param value the data to store in this new delegate
*/
- function MakeDataDelegate<D extends Field>(proto: Doc, options: DocumentOptions, value?: D, fieldKey: string = "data") {
- const deleg = Doc.MakeDelegate(proto);
+ function MakeDataDelegate<D extends Field>(proto: Doc, options: DocumentOptions, value?: D, fieldKey: string = "data", id: string | undefined = undefined) {
+ const deleg = Doc.MakeDelegate(proto, id);
if (value !== undefined) {
deleg[fieldKey] = value;
}
@@ -807,8 +807,8 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", dontRegisterChildViews: true, ...options, _viewType: CollectionViewType.Tree }, id);
}
- export function StackingDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Stacking }, id);
+ export function StackingDocument(documents: Array<Doc>, options: DocumentOptions, id?: string, protoId?: string) {
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Stacking }, id, undefined, protoId);
}
export function MulticolumnDocument(documents: Array<Doc>, options: DocumentOptions) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 7cc35d67a..ec550c15a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -30,6 +30,7 @@ import { Scripting } from "./Scripting";
import { SearchUtil } from "./SearchUtil";
import { SelectionManager } from "./SelectionManager";
import { UndoManager } from "./UndoManager";
+import { SharingPermissions } from "../../fields/util";
const headerViewVersion = "0.1";
@@ -404,7 +405,7 @@ export class CurrentUserUtils {
const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { title: "header", version: headerViewVersion, target: doc, _height: 70, _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, cloneFieldFilter: new List<string>(["system"]) }, "header"); // text needs to be a space to allow templateText to be created
headerTemplate[DataSym].layout =
"<div style={'height:100%'}>" +
- " <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} pointerEvents='{this._headerPointerEvents||`none`}' fontSize='{this._headerFontSize}px' height='{this._headerHeight}px' background='{this._headerColor||this.target.userColor}' />" +
+ " <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} pointerEvents='{this._headerPointerEvents||`none`}' fontSize='{this._headerFontSize}px' height='{this._headerHeight}px' background='{this._headerColor||this.target.mySharedDocs.userColor}' />" +
" <FormattedTextBox {...props} fieldKey={'text'} position='absolute' top='{(this._headerHeight)*scale}px' height='calc({100/scale}% - {this._headerHeight}px)'/>" +
"</div>";
(headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView");
@@ -513,10 +514,7 @@ export class CurrentUserUtils {
return doc.myItemCreators as Doc;
}
- static menuBtnDescriptions(doc: Doc): {
- title: string, target: Doc, icon: string, click: string, watchedDocuments?: Doc
- }[] {
- this.setupSharingSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing
+ static async menuBtnDescriptions(doc: Doc) {
return [
{ title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
{ title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
@@ -540,9 +538,10 @@ export class CurrentUserUtils {
})) as any as Doc;
}
}
- static setupMenuPanel(doc: Doc) {
+ static async setupMenuPanel(doc: Doc, sharingDocumentId: string) {
if (doc.menuStack === undefined) {
- const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments }) =>
+ await this.setupSharingSidebar(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing
+ const menuBtns = (await CurrentUserUtils.menuBtnDescriptions(doc)).map(({ title, target, icon, click, watchedDocuments }) =>
Docs.Create.FontIconDocument({
icon,
iconShape: "square",
@@ -566,7 +565,7 @@ export class CurrentUserUtils {
title: "menuItemPanel",
childDropAction: "alias",
dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
- _backgroundColor: "black",
+ _backgroundColor: "black", ignoreClick: true,
_gridGap: 0,
_yMargin: 0,
_yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, lockedPosition: true, _chromeStatus: "disabled", system: true
@@ -729,7 +728,7 @@ export class CurrentUserUtils {
if (doc.myTools === undefined) {
const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], {
- title: "My Tools", _width: 500, _yMargin: 20, lockedPosition: true, _chromeStatus: "disabled", forceActive: true, system: true, _stayInCollection: true, _hideContextMenu: true,
+ title: "My Tools", _width: 500, _yMargin: 20, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", forceActive: true, system: true, _stayInCollection: true, _hideContextMenu: true,
})) as any as Doc;
doc.myTools = toolsStack;
@@ -743,7 +742,7 @@ export class CurrentUserUtils {
doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
title: "My Dashboards", _height: 400,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
- treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
+ treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, ignoreClick: true,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
}));
const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`);
@@ -759,7 +758,7 @@ export class CurrentUserUtils {
doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
title: "My Presentations", _height: 100,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
- treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
+ treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, ignoreClick: true,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
}));
const newPresentations = ScriptField.MakeScript(`createNewPresentation()`);
@@ -777,7 +776,7 @@ export class CurrentUserUtils {
doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
title: "Recently Closed", _height: 500,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
- treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
+ treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, ignoreClick: true,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
}));
const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
@@ -792,7 +791,7 @@ export class CurrentUserUtils {
doc.myFilter = new PrefetchProxy(Docs.Create.FilterDocument({
title: "FilterDoc", _height: 500,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "none",
- treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
+ treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, ignoreClick: true,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
}));
const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
@@ -808,7 +807,7 @@ export class CurrentUserUtils {
doc.treeViewExpandedView = "fields";
doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], {
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, title: "My UserDoc",
- treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
+ treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, ignoreClick: true,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
})) as any as Doc;
}
@@ -874,9 +873,20 @@ export class CurrentUserUtils {
}
// Sharing sidebar is where shared documents are contained
- static setupSharingSidebar(doc: Doc) {
+ static async setupSharingSidebar(doc: Doc, sharingDocumentId: string) {
if (doc.mySharedDocs === undefined) {
- doc.mySharedDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, _showTitle: "title", ignoreClick: true, lockedPosition: true }));
+ let sharedDocs = await DocServer.GetRefField(sharingDocumentId + "outer");
+ if (!sharedDocs) {
+ sharedDocs = Docs.Create.StackingDocument([], {
+ title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
+ _showTitle: "title", ignoreClick: true, lockedPosition: true,
+ }, sharingDocumentId + "outer", sharingDocumentId);
+ (sharedDocs as Doc)["acl-Public"] = Doc.GetProto(sharedDocs as Doc)["acl-Public"] = SharingPermissions.Add;
+ }
+ if (sharedDocs instanceof Doc) {
+ sharedDocs.userColor = sharedDocs.userColor || "#12121233";
+ }
+ doc.mySharedDocs = new PrefetchProxy(sharedDocs);
}
}
@@ -943,11 +953,10 @@ export class CurrentUserUtils {
return doc.clickFuncs as Doc;
}
- static async updateUserDocument(doc: Doc) {
+ static async updateUserDocument(doc: Doc, sharingDocumentId: string) {
doc.system = true;
doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
doc.title = Doc.CurrentUserEmail;
- doc.userColor = doc.userColor || "#12121233";
doc._raiseWhenDragged = true;
doc.activeInkPen = doc;
doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)");
@@ -976,7 +985,7 @@ export class CurrentUserUtils {
this.setupOverlays(doc); // documents in overlay layer
this.setupDockedButtons(doc); // the bottom bar of font icons
await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels
- this.setupMenuPanel(doc);
+ await this.setupMenuPanel(doc, sharingDocumentId);
doc.globalLinkDatabase = Docs.Prototypes.MainLinkDocument();
doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument();
doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument();
@@ -1013,10 +1022,11 @@ export class CurrentUserUtils {
public static async loadUserDocument({ id, email }: { id: string, email: string }) {
this.curr_id = id;
Doc.CurrentUserEmail = email;
- await rp.get(Utils.prepend("/getUserDocumentId")).then(id => {
- if (id && id !== "guest") {
- return DocServer.GetRefField(id).then(async field =>
- Doc.SetUserDoc(await this.updateUserDocument(field instanceof Doc ? field : new Doc(id, true))));
+ await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => {
+ const { userDocumentId, sharingDocumentId } = JSON.parse(ids);
+ if (userDocumentId !== "guest") {
+ return DocServer.GetRefField(userDocumentId).then(async field =>
+ this.updateUserDocument(Doc.SetUserDoc(field instanceof Doc ? field : new Doc(userDocumentId, true)), sharingDocumentId));
} else {
throw new Error("There should be a user id! Why does Dash think there isn't one?");
}
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index cb512bca8..e81c95d83 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -64,14 +64,10 @@ export class GroupManager extends React.Component<{}> {
const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
const raw = JSON.parse(userList) as User[];
const evaluating = raw.map(async user => {
- const userDocument = await DocServer.GetRefField(user.userDocumentId);
- if (userDocument instanceof Doc) {
- const notificationDoc = await Cast(userDocument.mySharedDocs, Doc);
- runInAction(() => {
- if (notificationDoc instanceof Doc) {
- this.users.push(user.email);
- }
- });
+ const userSharingDocument = await DocServer.GetRefField(user.sharingDocumentId);
+ if (userSharingDocument instanceof Doc) {
+ const notificationDoc = await Cast(userSharingDocument.data, Doc, null);
+ runInAction(() => notificationDoc && this.users.push(user.email));
}
});
return Promise.all(evaluating).then(() => this.populating = false);
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 08ad49dcc..79759a71d 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -44,7 +44,7 @@ export namespace SearchUtil {
const header = query.match(/_[atnb]?:/) ? replacedQuery : "DEFAULT:" + replacedQuery;
replacedQuery = `{!join from=id to=proto_i}* AND ${header}`;
}
- console.log("Q: " + replacedQuery + " fq: " + options.fq);
+ //console.log("Q: " + replacedQuery + " fq: " + options.fq);
const gotten = await rp.get(rpquery, { qs: { ...options, q: replacedQuery } });
const result: IdSearchResult = gotten.startsWith("<") ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten);
if (!returnDocs) {
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index cd01fea5a..9934f26d3 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -56,7 +56,7 @@ export class SettingsManager extends React.Component<{}> {
@undoBatch changeFontFamily = action((e: React.ChangeEvent) => Doc.UserDoc().fontFamily = (e.currentTarget as any).value);
@undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value);
@undoBatch switchActiveBackgroundColor = action((color: ColorState) => Doc.UserDoc().activeCollectionBackground = String(color.hex));
- @undoBatch switchUserColor = action((color: ColorState) => Doc.UserDoc().userColor = String(color.hex));
+ @undoBatch switchUserColor = action((color: ColorState) => Doc.SharingDoc().userColor = String(color.hex));
@undoBatch
playgroundModeToggle = action(() => {
this.playgroundMode = !this.playgroundMode;
@@ -205,6 +205,6 @@ export class SettingsManager extends React.Component<{}> {
isDisplayed={this.isOpen}
interactive={true}
closeOnExternalClick={this.close}
- dialogueBoxStyle={{ width: "600px", background: Cast(Doc.UserDoc().userColor, "string", null) }} />;
+ dialogueBoxStyle={{ width: "600px", background: Cast(Doc.SharingDoc().userColor, "string", null) }} />;
}
} \ No newline at end of file
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index bcd7d4056..c67ec2db5 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -7,7 +7,7 @@ import * as RequestPromise from "request-promise";
import { AclAdmin, AclPrivate, DataSym, Doc, DocListCast, Opt, AclSym, AclAddonly, AclEdit, AclReadonly } from "../../fields/Doc";
import { List } from "../../fields/List";
import { Cast, StrCast } from "../../fields/Types";
-import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from "../../fields/util";
+import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx, normalizeEmail } from "../../fields/util";
import { Utils } from "../../Utils";
import { DocServer } from "../DocServer";
import { CollectionView } from "../views/collections/CollectionView";
@@ -25,7 +25,7 @@ import { SearchBox } from "../views/search/SearchBox";
export interface User {
email: string;
- userDocumentId: string;
+ sharingDocumentId: string;
}
/**
@@ -47,20 +47,19 @@ const groupType = "!groupType/";
const storage = "data";
/**
- * A user who also has a notificationDoc.
+ * A user who also has a sharing doc.
*/
interface ValidatedUser {
- user: User;
- notificationDoc: Doc;
- userColor: string;
+ user: User; // database minimal info to identify / communicate with a user (email, sharing doc id)
+ sharingDoc: Doc; // document to share/message another user
+ userColor: string; // stored on the sharinDoc, extracted for convenience?
}
-
@observer
export class SharingManager extends React.Component<{}> {
public static Instance: SharingManager;
@observable private isOpen = false; // whether the SharingManager modal is open or not
- @observable public users: ValidatedUser[] = []; // the list of users with notificationDocs
+ @observable public users: ValidatedUser[] = []; // the list of users with sharing docs
@observable private targetDoc: Doc | undefined; // the document being shared
@observable private targetDocView: DocumentView | undefined; // the DocumentView of the document being shared
// @observable private copied = false;
@@ -119,7 +118,7 @@ export class SharingManager extends React.Component<{}> {
}
/**
- * Populates the list of validated users (this.users) by adding registered users which have a mySharedDocs.
+ * Populates the list of validated users (this.users) by adding registered users which have a sharingDocument.
*/
populateUsers = async () => {
if (!this.populating) {
@@ -130,15 +129,9 @@ export class SharingManager extends React.Component<{}> {
const evaluating = raw.map(async user => {
const isCandidate = user.email !== Doc.CurrentUserEmail;
if (isCandidate) {
- const userDocument = await DocServer.GetRefField(user.userDocumentId);
- if (userDocument instanceof Doc) {
- const notificationDoc = await Cast(userDocument.mySharedDocs, Doc);
- const userColor = StrCast(userDocument.userColor);
- runInAction(() => {
- if (notificationDoc instanceof Doc) {
- this.users.push({ user, notificationDoc, userColor });
- }
- });
+ const userSharingDoc = await DocServer.GetRefField(user.sharingDocumentId);
+ if (userSharingDoc instanceof Doc) {
+ runInAction(() => this.users.push({ user, sharingDoc: userSharingDoc, userColor: StrCast(userSharingDoc.userColor) }));
}
}
});
@@ -154,13 +147,13 @@ export class SharingManager extends React.Component<{}> {
setInternalGroupSharing = (group: Doc | { groupName: string }, permission: string, targetDoc?: Doc) => {
const target = targetDoc || this.targetDoc!;
- const key = StrCast(group.groupName).replace(".", "_");
+ const key = normalizeEmail(StrCast(group.groupName));
const acl = `acl-${key}`;
const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
docs.forEach(doc => {
- doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`acl-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc);
+ doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc);
distributeAcls(acl, permission as SharingPermissions, doc);
if (group instanceof Doc) {
@@ -170,9 +163,9 @@ export class SharingManager extends React.Component<{}> {
// if documents have been shared, add the doc to that list if it doesn't already exist, otherwise create a new list with the doc
group.docsShared ? Doc.IndexOf(doc, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(doc) : group.docsShared = new List<Doc>([doc]);
- users.forEach(({ user, notificationDoc }) => {
- if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, doc); // add the doc to the notificationDoc if it hasn't already been added
- else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, (doc.aliasOf as Doc || doc)); // remove the doc from the list if it already exists
+ users.forEach(({ user, sharingDoc }) => {
+ if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(sharingDoc[storage])) === -1 && Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
+ else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(sharingDoc[storage])) !== -1 && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc)); // remove the doc from the list if it already exists
});
}
});
@@ -185,7 +178,7 @@ export class SharingManager extends React.Component<{}> {
*/
shareWithAddedMember = (group: Doc, emailId: string) => {
const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!;
- if (group.docsShared) DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc));
+ if (group.docsShared) DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.sharingDoc[storage])) === -1 && Doc.AddDocToList(user.sharingDoc, storage, doc));
}
/**
@@ -216,7 +209,7 @@ export class SharingManager extends React.Component<{}> {
if (group.docsShared) {
DocListCast(group.docsShared).forEach(doc => {
- Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(user.notificationDoc, storage, doc); // remove the doc only if it is in the list
+ Doc.IndexOf(doc, DocListCast(user.sharingDoc[storage])) !== -1 && Doc.RemoveDocFromList(user.sharingDoc, storage, doc); // remove the doc only if it is in the list
});
}
}
@@ -235,7 +228,7 @@ export class SharingManager extends React.Component<{}> {
const members: string[] = JSON.parse(StrCast(group.members));
const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
- users.forEach(({ notificationDoc }) => Doc.RemoveDocFromList(notificationDoc, storage, doc));
+ users.forEach(({ sharingDoc }) => Doc.RemoveDocFromList(sharingDoc, storage, doc));
});
}
}
@@ -244,19 +237,18 @@ export class SharingManager extends React.Component<{}> {
* Shares the document with a user.
*/
setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => {
- const { user, notificationDoc } = recipient;
+ const { user, sharingDoc } = recipient;
const target = targetDoc || this.targetDoc!;
- const key = user.email.replace('.', '_');
- const acl = `acl-${key}`;
+ const acl = `acl-${normalizeEmail(user.email)}`;
+ const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
-
docs.forEach(doc => {
- doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`acl-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc);
+ doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc);
distributeAcls(acl, permission as SharingPermissions, doc);
- if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, doc);
- else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, (doc.aliasOf as Doc || doc));
+ if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(sharingDoc[storage])) === -1 && Doc.AddDocToList(sharingDoc, storage, doc);
+ else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(sharingDoc[storage])) !== -1 && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc));
});
}
@@ -456,8 +448,8 @@ export class SharingManager extends React.Component<{}> {
const commonKeys = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym]?.[AclSym] && Object.keys(doc[DataSym][AclSym])));
// the list of users shared with
- const userListContents: (JSX.Element | null)[] = users.filter(({ user }) => docs.length > 1 ? commonKeys.includes(`acl-${user.email.replace('.', '_')}`) : docs[0]?.author !== user.email).map(({ user, notificationDoc, userColor }) => {
- const userKey = `acl-${user.email.replace('.', '_')}`;
+ const userListContents: (JSX.Element | null)[] = users.filter(({ user }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email).map(({ user, sharingDoc, userColor }) => {
+ const userKey = `acl-${normalizeEmail(user.email)}`;
const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey] : doc?.[DataSym]?.[AclSym]?.[userKey] === docs[0]?.[DataSym]?.[AclSym]?.[userKey]);
const permissions = uniform ? StrCast(targetDoc?.[userKey]) : "-multiple-";
@@ -472,7 +464,7 @@ export class SharingManager extends React.Component<{}> {
<select
className={"permissions-dropdown"}
value={permissions}
- onChange={e => this.setInternalSharing({ user, notificationDoc, userColor }, e.currentTarget.value)}
+ onChange={e => this.setInternalSharing({ user, sharingDoc: sharingDoc, userColor }, e.currentTarget.value)}
>
{this.sharingOptions(uniform)}
</select>
@@ -514,7 +506,7 @@ export class SharingManager extends React.Component<{}> {
<span className={"padding"}>Me</span>
<div className="edit-actions">
<div className={"permissions-dropdown"}>
- {targetDoc?.[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`]}
+ {targetDoc?.[`acl-${Doc.CurrentUserEmailNormalized}`]}
</div>
</div>
</div>
@@ -523,7 +515,7 @@ export class SharingManager extends React.Component<{}> {
// the list of groups shared with
- const groupListMap: (Doc | { groupName: string })[] = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${StrCast(groupName).replace('.', '_')}`) : true);
+ const groupListMap: (Doc | { groupName: string })[] = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(groupName))}`) : true);
groupListMap.unshift({ groupName: "Public" }, { groupName: "Override" });
const groupListContents = groupListMap.map(group => {
const groupKey = `acl-${StrCast(group.groupName)}`;
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 893b74d75..b3fbe418b 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -7,7 +7,7 @@ import { InteractionUtils } from '../util/InteractionUtils';
import { List } from '../../fields/List';
import { DateField } from '../../fields/DateField';
import { ScriptField } from '../../fields/ScriptField';
-import { GetEffectiveAcl, SharingPermissions, distributeAcls } from '../../fields/util';
+import { GetEffectiveAcl, SharingPermissions, distributeAcls, denormalizeEmail } from '../../fields/util';
/// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
@@ -166,7 +166,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
if (this.props.Document[AclSym]) {
added.forEach(d => {
for (const [key, value] of Object.entries(this.props.Document[AclSym])) {
- if (d.author === key.substring(4).replace("_", ".") && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true);
+ if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true);
//else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true);
// else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true);
}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 1b2395423..2ea1c464f 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -55,7 +55,8 @@ export class KeyManager {
});
public handle = action(async (e: KeyboardEvent) => {
- if (e.key.toLowerCase() === "shift") KeyManager.Instance.ShiftPressed = true;
+ if (e.key.toLowerCase() === "shift" && e.ctrlKey && e.altKey) KeyManager.Instance.ShiftPressed = true;
+ if (!Doc.UserDoc().noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.PRINT_CACHE();
const keyname = e.key && e.key.toLowerCase();
this.handleGreedy(keyname);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5193c3c02..8e30eba2a 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -312,39 +312,40 @@ export class MainView extends React.Component {
}
@computed get flyout() {
- return <div className={`mainView-libraryFlyout${this._flyoutWidth ? "" : "-out"}`} style={{ minWidth: this._flyoutWidth, width: this._flyoutWidth }} >
- <div className="mainView-contentArea" >
- <DocumentView
- Document={this._sidebarContent}
- DataDoc={undefined}
- LibraryPath={emptyPath}
- addDocument={undefined}
- addDocTab={this.addDocTabFunc}
- pinToPres={emptyFunction}
- rootSelected={returnTrue}
- removeDocument={returnFalse}
- onClick={undefined}
- ScreenToLocalTransform={this.mainContainerXf}
- ContentScaling={returnOne}
- PanelWidth={this.flyoutWidthFunc}
- PanelHeight={this.getContentsHeight}
- renderDepth={0}
- focus={emptyFunction}
- backgroundColor={this.defaultBackgroundColors}
- parentActive={returnTrue}
- whenActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- relative={true}
- forcedBackgroundColor={() => "lightgrey"}
- />
- </div>
- {this.docButtons}
- </div>;
+ return !this._flyoutWidth ? <div className={`mainView-libraryFlyout-out"}`} style={{ width: 0 }} /> :
+ <div className="mainView-libraryFlyout" style={{ minWidth: this._flyoutWidth, width: this._flyoutWidth }} >
+ <div className="mainView-contentArea" >
+ <DocumentView
+ Document={this._sidebarContent}
+ DataDoc={undefined}
+ LibraryPath={emptyPath}
+ addDocument={undefined}
+ addDocTab={this.addDocTabFunc}
+ pinToPres={emptyFunction}
+ rootSelected={returnTrue}
+ removeDocument={returnFalse}
+ onClick={undefined}
+ ScreenToLocalTransform={this.mainContainerXf}
+ ContentScaling={returnOne}
+ PanelWidth={this.flyoutWidthFunc}
+ PanelHeight={this.getContentsHeight}
+ renderDepth={0}
+ focus={emptyFunction}
+ backgroundColor={this.defaultBackgroundColors}
+ parentActive={returnTrue}
+ whenActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ relative={true}
+ forcedBackgroundColor={() => "lightgrey"}
+ />
+ </div>
+ {this.docButtons}
+ </div>;
}
@computed get menuPanel() {
@@ -394,6 +395,8 @@ export class MainView extends React.Component {
SearchBox.Instance._searchFullDB = "My Stuff";
SearchBox.Instance.enter(undefined);
break;
+ case "Help":
+ break;
default:
this.expandFlyout(button);
}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 5cab7726e..f3241e8d9 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,6 +1,7 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Checkbox, Tooltip } from "@material-ui/core";
+import { intersection } from "lodash";
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
import { ColorState, SketchPicker } from "react-color";
@@ -9,8 +10,8 @@ import { Id } from "../../fields/FieldSymbols";
import { InkField } from "../../fields/InkField";
import { ComputedField } from "../../fields/ScriptField";
import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { GetEffectiveAcl, SharingPermissions } from "../../fields/util";
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../Utils";
+import { GetEffectiveAcl, SharingPermissions, denormalizeEmail } from "../../fields/util";
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne } from "../../Utils";
import { DocumentType } from "../documents/DocumentTypes";
import { DocumentManager } from "../util/DocumentManager";
import { SelectionManager } from "../util/SelectionManager";
@@ -26,7 +27,6 @@ import { PresBox } from "./nodes/PresBox";
import { PropertiesButtons } from "./PropertiesButtons";
import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
import "./PropertiesView.scss";
-import { intersection } from "lodash";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -409,7 +409,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
// DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
if (commonKeys.length) {
for (const key of commonKeys) {
- const name = key.substring(4).replace("_", ".");
+ const name = denormalizeEmail(key.substring(4));
const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key] : doc?.[DataSym]?.[AclSym]?.[key] === docs[0]?.[DataSym]?.[AclSym]?.[key]);
if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public" && name !== "Override"/* && sidebarUsersDisplayed![name] !== false*/) {
tableEntries.push(this.sharingItem(name, showAdmin, uniform ? AclMap.get(this.layoutDocAcls ? target[AclSym][key] : target[DataSym][AclSym][key])! : "-multiple-"));
@@ -417,13 +417,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
}
}
- // if (Doc.UserDoc().sidebarUsersDisplayed) {
- // for (const [name, value] of Object.entries(sidebarUsersDisplayed!)) {
- // if (value === true && !this.selectedDoc![`acl-${name.substring(8).replace(".", "_")}`]) tableEntries.push(this.sharingItem(name.substring(8), effectiveAcl, SharingPermissions.None));
- // }
- // }
- // })
-
const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
// shifts the current user, owner, public to the top of the doc.
tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
@@ -910,9 +903,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
/>
<div className="propertiesView-acls-checkbox-text">Layout</div>
</div>) : (null)}
- <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
- <FontAwesomeIcon icon="redo-alt" color="white" size="1x" />
- </button>
+ <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
+ <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
+ <FontAwesomeIcon icon="redo-alt" color="white" size="1x" />
+ </button>
+ </Tooltip>
</div>
{this.sharingTable}
</div>}
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 4f1ef6e61..4e30709a6 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -166,7 +166,7 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume
render() {
const index = NumCast(this.layoutDoc._itemIndex);
- const translateX = (1 - index) / this.childLayoutPairs.length * 100;
+ const translateX = 33 * (1 - index);
return <div className="collectionCarousel3DView-outer" onClick={this.onClick} onPointerDown={this.onPointerDown} ref={this.createDashEventsTarget}>
<div className="carousel-wrapper" style={{ transform: `translateX(calc(${translateX}%` }}>
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 51d1d5559..09ff3bb0c 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -234,8 +234,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
@undoBatch
viewChanged = (e: React.ChangeEvent) => {
+ const target = this.document !== Doc.UserDoc().sidebar ? this.document : this.document.proto as Doc;
//@ts-ignore
- this.document._viewType = e.target.selectedOptions[0].value;
+ target._viewType = e.target.selectedOptions[0].value;
}
commandChanged = (e: React.ChangeEvent) => {
@@ -893,14 +894,14 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu
if (docs instanceof Doc) {
const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
- (key[0].toUpperCase() === key[0] && key.substring(0, 3) !== "acl" && key[0] !== "_"));
+ (key[0].toUpperCase() === key[0] && key[0] !== "_"));
return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
} else {
const keys = new Set<string>();
docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
- (key[0]?.toUpperCase() === key[0] && key.substring(0, 3) !== "acl" && key[0] !== "_"));
+ (key[0]?.toUpperCase() === key[0] && key[0] !== "_"));
return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
}
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 649b8c175..fa80c8062 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,6 +1,6 @@
import { action, computed, IReactionDisposer, reaction, observable, runInAction } from "mobx";
import CursorField from "../../../fields/CursorField";
-import { Doc, Opt, Field, DocListCast } from "../../../fields/Doc";
+import { Doc, Opt, Field, DocListCast, AclPrivate } from "../../../fields/Doc";
import { Id, ToString } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
import { listSpec } from "../../../fields/Schema";
@@ -101,7 +101,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
get childLayoutPairs(): { layout: Doc; data: Doc; }[] {
const { Document, DataDoc } = this.props;
- const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.annotationsKey ? DataDoc : undefined, doc)).filter(pair => pair.layout);
+ const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.annotationsKey ? DataDoc : undefined, doc)).
+ filter(pair => { // filter out any documents that have a proto that we don't have permissions to (which we determine by not having any keys
+ return pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && Object.keys(pair.layout.proto).length));
+ });
return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
}
get childDocList() {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 522f46280..2bdc8e2f3 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -10,7 +10,7 @@ import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
-import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
+import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx, normalizeEmail, denormalizeEmail } from '../../../fields/util';
import { returnFalse, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
@@ -143,7 +143,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
if (this.props.Document[AclSym]) {
added.forEach(d => {
for (const [key, value] of Object.entries(this.props.Document[AclSym])) {
- if (d.author === key.substring(4).replace("_", ".") && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true);
+ if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true);
//else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true);
//else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true);
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5f99f27b1..ddcf7f6f4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1049,7 +1049,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const titleView = (!this.ShowTitle ? (null) :
<div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
position: this.showOverlappingTitle ? "absolute" : "relative",
- background: SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.userColor || (this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.UserDoc().userColor) : "rgba(0,0,0,0.4)"),
+ background: SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.userColor || (this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)"),
pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
}}>
<EditableView ref={this._titleRef}
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 6e96513c7..156256fe5 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -105,7 +105,7 @@ export class FontIconBadge extends React.Component<FontIconBadgeProps> {
render() {
if (!(this.props.collection instanceof Doc)) return (null);
- const length = DocListCast(this.props.collection.data).length;
+ const length = DocListCast(this.props.collection.data).filter(d => Object.keys(d).length).length; // filter out any documents that we can't read
return <div className="fontIconBadge-container" style={{ width: 15, height: 15, top: 12 }} ref={this._notifsRef}>
<div className="fontIconBadge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}
onPointerDown={this.onPointerDown} >
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 921c0e128..72b91c955 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -3,7 +3,8 @@ import { NodeSelection, TextSelection } from "prosemirror-state";
import { DataSym, Doc } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
import { ComputedField } from "../../../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../../fields/Types";
+import { NumCast, StrCast } from "../../../../fields/Types";
+import { normalizeEmail } from "../../../../fields/util";
import { returnFalse, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
@@ -11,7 +12,6 @@ import { FormattedTextBox } from "./FormattedTextBox";
import { wrappingInputRule } from "./prosemirrorPatches";
import { RichTextMenu } from "./RichTextMenu";
import { schema } from "./schema_rts";
-import { List } from "../../../../fields/List";
export class RichTextRules {
public Document: Doc;
@@ -271,7 +271,7 @@ export class RichTextRules {
(state, match, start, end) => {
const fieldKey = match[1];
const rawdocid = match[3]?.substring(1);
- const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined;
+ const docid = rawdocid ? (!rawdocid.includes("@") ? normalizeEmail(Doc.CurrentUserEmail) + "@" + rawdocid : rawdocid) : undefined;
const value = match[2]?.substring(1);
if (!fieldKey) {
const linkId = Utils.GenerateGuid();
@@ -304,7 +304,7 @@ export class RichTextRules {
const fieldKey = match[1] || "";
const fieldParam = match[2]?.replace("…", "...") || "";
const rawdocid = match[3]?.substring(1);
- const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined;
+ const docid = rawdocid ? (!rawdocid.includes("@") ? normalizeEmail(Doc.CurrentUserEmail) + "@" + rawdocid : rawdocid) : undefined;
if (!fieldKey && !docid) return state.tr;
docid && DocServer.GetRefField(docid).then(docx => {
if (!(docx instanceof Doc && docx)) {