Adding Quip Comments
It's easy to support cross-platform Quip commenting in your app. To enable comments for a Record, you'll need to override two quip.apps.Record
methods:
quip.apps.Record.prototype.supportsComments()
should returntrue
quip.apps.Record.prototype.getDom()
should return the DOM node in your app that corresponds to the record.- When the comments bubble for the record is opened, the platform will call this method to know which DOM node to highlight.
Then, add the CommentsTrigger
React component and pass in the relevant record. This component renders a standard Quip comment bubble that automatically updates with the current number of comments.
Each Record can have its own Quip comment thread.
If you want to customize the appearance of the trigger, you can make your own custom trigger with these Record methods:
quip.apps.Record.prototype.getCommentCount()
: Returns the number of comments on the Record.quip.apps.Record.prototype.showComments()
: Displays the Quip comments bubble.quip.apps.Record.prototype.listenToComments(listener)
: Provide a listener method in order to keep your app up-to-date if the number of comments changes.quip.apps.Record.prototype.unlistenToComments(listener)
: Unsubscribe your listener when you don't need it anymore.
A full example of an app with comment records:
- Typescript
- Javascript
import React, { Component } from "react";
import quip from "quip-apps-api";
class RootRecord extends quip.apps.RootRecord {
static getProperties() {
return {
commentableRecord: CommentableRecord,
};
}
static getDefaultProperties(): {[property: string]: any} {
return {
commentableRecord: {},
};
}
commentableRecord(): CommentableRecord {
return this.get("commentableRecord") as CommentableRecord;
}
}
quip.apps.registerClass(RootRecord, "root-record");
class CommentableRecord extends quip.apps.Record {
static getProperties = () => ({})
node: Element | null = null;
getDom() {
if (!this.node) {
throw new Error("Attempt to call getDom before node was set");
}
return this.node;
}
setDom(node: Element | null) {
this.node = node;
}
supportsComments() {
return true;
}
}
quip.apps.registerClass(CommentableRecord, "commentable-record");
interface MainProps {
rootRecord: RootRecord;
}
class Main extends Component<MainProps> {
render() {
const {rootRecord} = this.props;
const commentableRecord = rootRecord.commentableRecord()
return (
<div ref={(c) => commentableRecord.setDom(c)}>
Comment
<quip.apps.ui.CommentsTrigger
record={commentableRecord}
showEmpty={true}
/>
</div>
);
}
}
quip.apps.initialize({
initializationCallback: (
rootNode: Element,
params: {
isCreation: boolean;
creationUrl?: string;
}
) => {
const rootRecord = quip.apps.getRootRecord() as RootEntity;
ReactDOM.render(
<Main rootRecord={rootRecord}/>,
rootNode);
},
});
import React, { Component } from "react";
import PropTypes from "prop-types";
import quip from "quip-apps-api";
class RootRecord extends quip.apps.RootRecord {
static getProperties() {
return {
commentableRecord: CommentableRecord,
};
}
static getDefaultProperties() {
return {
commentableRecord: {},
};
}
}
quip.apps.registerClass(RootRecord, "root-record");
class CommentableRecord extends quip.apps.Record {
static getProperties = () => ({})
getDom() {
if (!this.node) {
throw new Error("Attempt to call getDom before node was set");
}
return this.node;
}
setDom(node) {
this.node = node;
}
supportsComments() {
return true;
}
}
quip.apps.registerClass(CommentableRecord, "commentable-record");
class Main extends Component {
render() {
const {rootRecord} = this.props;
const commentableRecord = rootRecord.get("commentableRecord")
return (
<div ref={(c) => commentableRecord.setDom(c)}>
Comment
<quip.apps.ui.CommentsTrigger
record={commentableRecord}
showEmpty={true}
/>
</div>
);
}
}
Main.propTypes = {
rootRecord: PropTypes.instanceOf(RootRecord)
}
quip.apps.initialize({
initializationCallback: (rootNode, params) => {
const rootRecord = quip.apps.getRootRecord() as RootEntity;
ReactDOM.render(
<Main rootRecord={rootRecord}/>,
rootNode);
},
});