merge next into next-reply
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+3
-2
@@ -57,6 +57,7 @@ const fbMessaging = firebase.messaging();
|
||||
// This method shows the push notifications while the window is in background.
|
||||
fbMessaging.onBackgroundMessage((payload) => {
|
||||
if (payload.data.silent == 'true') {
|
||||
// TODO: if this is an 'msg', fetch the new message in the background.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -68,8 +69,8 @@ fbMessaging.onBackgroundMessage((payload) => {
|
||||
const pushType = payload.data.what || 'msg';
|
||||
const title = payload.data.title || self.i18nMessage(pushType == 'msg' ? 'new_message' : 'new_chat');
|
||||
const options = {
|
||||
body: payload.data.content || '',
|
||||
icon: '/img/logo96.png',
|
||||
body: payload.data.content || '', // TODO: content for 'sub' should be topic's or user's title.
|
||||
icon: '/img/logo96.png', // TODO: use topic's or user's avatar (would have to fetch for 'sub', read from db for 'msg').
|
||||
badge: '/img/badge96.png',
|
||||
tag: payload.data.topic || undefined,
|
||||
data: {
|
||||
|
||||
@@ -39,7 +39,7 @@ export function bytesToHumanSize(bytes) {
|
||||
|
||||
// Get 32 bit integer hash value for a string. Ideally it should produce the same value
|
||||
// as Java's String#hash().
|
||||
function stringToColorHash(value) {
|
||||
export function stringToColorHash(value) {
|
||||
let hash = 0;
|
||||
value = '' + value;
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
|
||||
+2
-2
@@ -2,11 +2,11 @@
|
||||
|
||||
import Tinode from 'tinode-sdk';
|
||||
|
||||
import { stringHash } from './strformat.js';
|
||||
import { stringToColorHash } from './strformat.js';
|
||||
|
||||
// User avatar letter color id.
|
||||
export function letterTileColor(userId) {
|
||||
return Math.abs(stringHash(userId)) % 16;
|
||||
return Math.abs(stringToColorHash(userId)) % 16;
|
||||
}
|
||||
|
||||
// Make shortcut icon appear with a green dot + show unread count in title.
|
||||
|
||||
+86
-88
@@ -91,9 +91,11 @@ class MessagesView extends React.Component {
|
||||
this.state = MessagesView.getDerivedStateFromProps(props, {});
|
||||
|
||||
this.leave = this.leave.bind(this);
|
||||
this.sendMessage = this.sendMessage.bind(this);
|
||||
this.sendImageAttachment = this.sendImageAttachment.bind(this);
|
||||
this.sendFileAttachment = this.sendFileAttachment.bind(this);
|
||||
this.sendKeyPress = this.sendKeyPress.bind(this);
|
||||
this.subscribe = this.subscribe.bind(this);
|
||||
this.handleScrollReference = this.handleScrollReference.bind(this);
|
||||
this.handleScrollEvent = this.handleScrollEvent.bind(this);
|
||||
this.handleDescChange = this.handleDescChange.bind(this);
|
||||
@@ -114,8 +116,6 @@ class MessagesView extends React.Component {
|
||||
this.postReadNotification = this.postReadNotification.bind(this);
|
||||
this.clearNotificationQueue = this.clearNotificationQueue.bind(this);
|
||||
|
||||
this.doSendMessage = this.doSendMessage.bind(this);
|
||||
this.handleSendMessage = this.handleSendMessage.bind(this);
|
||||
this.handlePickReply = this.handlePickReply.bind(this);
|
||||
this.handleCancelReply = this.handleCancelReply.bind(this);
|
||||
this.handleQuoteClick = this.handleQuoteClick.bind(this);
|
||||
@@ -145,7 +145,7 @@ class MessagesView extends React.Component {
|
||||
// or vertical shrinking.
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.messagesScroller) {
|
||||
if (prevState.topic != this.state.topic || prevState.latestMsgSeq != this.state.latestMsgSeq) {
|
||||
if (prevState.topic != this.state.topic || prevState.messageCount != this.state.messageCount) {
|
||||
// New message
|
||||
this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;
|
||||
} else if (prevProps.viewportHeight > this.props.viewportHeight) {
|
||||
@@ -178,49 +178,8 @@ class MessagesView extends React.Component {
|
||||
this.postReadNotification(0);
|
||||
}
|
||||
|
||||
if (topic && !topic.isSubscribed() && this.props.ready &&
|
||||
((this.state.topic != prevState.topic) || !prevProps.ready)) {
|
||||
// Is this a new topic?
|
||||
const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic);
|
||||
|
||||
// Don't request the tags. They are useless unless the user
|
||||
// is the owner and is editing the topic.
|
||||
let getQuery = topic.startMetaQuery().withLaterDesc().withLaterSub();
|
||||
if (this.state.isReader || newTopic) {
|
||||
// Reading is either permitted or we don't know because it's a new topic. Ask for messages.
|
||||
getQuery = getQuery.withLaterData(MESSAGES_PAGE);
|
||||
if (this.state.isReader) {
|
||||
getQuery = getQuery.withLaterDel();
|
||||
}
|
||||
// And show "loading" spinner.
|
||||
this.setState({ fetchingMessages: true });
|
||||
}
|
||||
const setQuery = newTopic ? this.props.newTopicParams : undefined;
|
||||
topic.subscribe(getQuery.build(), setQuery)
|
||||
.then((ctrl) => {
|
||||
if (ctrl.code == 303) {
|
||||
// Redirect to another topic requested.
|
||||
HashNavigation.navigateTo(HashNavigation.setUrlTopic('', ctrl.params.topic));
|
||||
return;
|
||||
}
|
||||
if (this.state.topic != ctrl.topic) {
|
||||
this.setState({topic: ctrl.topic});
|
||||
}
|
||||
this.props.onNewTopicCreated(this.props.topic, ctrl.topic);
|
||||
// If there are unsent messages, try sending them now.
|
||||
topic.queuedMessages((pub) => {
|
||||
if (!pub._sending && topic.isSubscribed()) {
|
||||
topic.publishMessage(pub);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Failed subscription to", this.state.topic);
|
||||
this.props.onError(err.message, 'err');
|
||||
const blankState = MessagesView.getDerivedStateFromProps({}, {});
|
||||
blankState.title = this.props.intl.formatMessage(messages.not_found);
|
||||
this.setState(blankState);
|
||||
});
|
||||
if ((this.state.topic != prevState.topic) || !prevProps.ready) {
|
||||
this.subscribe(topic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +188,7 @@ class MessagesView extends React.Component {
|
||||
if (!nextProps.topic) {
|
||||
// Default state: no topic.
|
||||
nextState = {
|
||||
latestMsgSeq: -1,
|
||||
messageCount: 0,
|
||||
latestClearId: -1,
|
||||
onlineSubs: [],
|
||||
topic: null,
|
||||
@@ -300,14 +259,14 @@ class MessagesView extends React.Component {
|
||||
});
|
||||
}
|
||||
Object.assign(nextState, {
|
||||
latestMsgSeq: topic.maxMsgSeq(),
|
||||
messageCount: topic.messageCount(),
|
||||
latestClearId: topic.maxClearId(),
|
||||
channel: topic.isChannelType()
|
||||
});
|
||||
} else {
|
||||
// Invalid topic.
|
||||
Object.assign(nextState, {
|
||||
latestMsgSeq: -1,
|
||||
messageCount: 0,
|
||||
latestClearId: -1,
|
||||
onlineSubs: [],
|
||||
title: '',
|
||||
@@ -358,6 +317,55 @@ class MessagesView extends React.Component {
|
||||
return nextState;
|
||||
}
|
||||
|
||||
subscribe(topic) {
|
||||
if (!topic || topic.isSubscribed() || !this.props.ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Is this a new topic?
|
||||
const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic);
|
||||
|
||||
// Don't request the tags. They are useless unless the user
|
||||
// is the owner and is editing the topic.
|
||||
let getQuery = topic.startMetaQuery().withLaterDesc().withLaterSub();
|
||||
if (this.state.isReader || newTopic) {
|
||||
// Reading is either permitted or we don't know because it's a new topic. Ask for messages.
|
||||
getQuery = getQuery.withLaterData(MESSAGES_PAGE);
|
||||
if (this.state.isReader) {
|
||||
getQuery = getQuery.withLaterDel();
|
||||
}
|
||||
// And show "loading" spinner.
|
||||
this.setState({ fetchingMessages: true });
|
||||
}
|
||||
|
||||
const setQuery = newTopic ? this.props.newTopicParams : undefined;
|
||||
topic.subscribe(getQuery.build(), setQuery)
|
||||
.then((ctrl) => {
|
||||
if (ctrl.code == 303) {
|
||||
// Redirect to another topic requested.
|
||||
HashNavigation.navigateTo(HashNavigation.setUrlTopic('', ctrl.params.topic));
|
||||
return;
|
||||
}
|
||||
if (this.state.topic != ctrl.topic) {
|
||||
this.setState({topic: ctrl.topic});
|
||||
}
|
||||
this.props.onNewTopicCreated(this.props.topic, ctrl.topic);
|
||||
// If there are unsent messages, try sending them now.
|
||||
topic.queuedMessages((pub) => {
|
||||
if (!pub._sending && topic.isSubscribed()) {
|
||||
this.sendMessage(pub);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Failed subscription to", this.state.topic);
|
||||
this.props.onError(err.message, 'err');
|
||||
const blankState = MessagesView.getDerivedStateFromProps({}, {});
|
||||
blankState.title = this.props.intl.formatMessage(messages.not_found);
|
||||
this.setState(blankState);
|
||||
});
|
||||
}
|
||||
|
||||
leave(oldTopicName) {
|
||||
if (!oldTopicName || !this.props.tinode.isTopicCached(oldTopicName)) {
|
||||
return;
|
||||
@@ -391,24 +399,20 @@ class MessagesView extends React.Component {
|
||||
// Get older messages
|
||||
handleScrollEvent(event) {
|
||||
this.setState({scrollPosition: event.target.scrollHeight - event.target.scrollTop});
|
||||
if (this.state.fetchingMessages) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.target.scrollTop <= 0) {
|
||||
this.setState((prevState, props) => {
|
||||
const newState = {};
|
||||
if (!prevState.fetchingMessages) {
|
||||
const topic = this.props.tinode.getTopic(this.state.topic);
|
||||
if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {
|
||||
newState.fetchingMessages = true;
|
||||
this.setState({fetchingMessages: true}, () => {
|
||||
topic.getMessagesPage(MESSAGES_PAGE)
|
||||
.then(() => this.setState({fetchingMessages: false}))
|
||||
.catch((err) => {
|
||||
this.setState({fetchingMessages: false});
|
||||
this.props.onError(err.message, 'err');
|
||||
.catch((err) => this.props.onError(err.message, 'err'))
|
||||
.finally(() => this.setState({fetchingMessages: false}));
|
||||
});
|
||||
}
|
||||
}
|
||||
return newState;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleDescChange(desc) {
|
||||
@@ -530,10 +534,11 @@ class MessagesView extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({latestMsgSeq: topic.maxMsgSeq()});
|
||||
clearTimeout(this.keyPressTimer)
|
||||
this.setState({messageCount: topic.messageCount(), typingIndicator: false});
|
||||
|
||||
// If the message is added to the end of the message list,
|
||||
// scroll to the bottom.
|
||||
// Scroll to the bottom if the message is added to the end of the message list.
|
||||
// TODO: This should be replaced by showing a "scroll to bottom" button.
|
||||
if (topic.isNewMessage(msg.seq)) {
|
||||
this.setState({scrollPosition: 0});
|
||||
}
|
||||
@@ -560,9 +565,8 @@ class MessagesView extends React.Component {
|
||||
switch (info.what) {
|
||||
case 'kp': {
|
||||
clearTimeout(this.keyPressTimer);
|
||||
var instance = this;
|
||||
this.keyPressTimer = setTimeout(function() {
|
||||
instance.setState({typingIndicator: false});
|
||||
this.keyPressTimer = setTimeout(() => {
|
||||
this.setState({typingIndicator: false});
|
||||
}, KEYPRESS_DELAY + 1000);
|
||||
if (!this.state.typingIndicator) {
|
||||
this.setState({typingIndicator: true});
|
||||
@@ -592,7 +596,7 @@ class MessagesView extends React.Component {
|
||||
|
||||
handleFormResponse(action, text, data) {
|
||||
if (action == 'pub') {
|
||||
this.props.sendMessage(Drafty.attachJSON(Drafty.parse(text), data));
|
||||
this.sendMessage(Drafty.attachJSON(Drafty.parse(text), data));
|
||||
} else if (action == 'url') {
|
||||
const url = new URL(data.ref);
|
||||
const params = url.searchParams;
|
||||
@@ -653,15 +657,19 @@ class MessagesView extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
doSendMessage(msg, uploadCompletionPromise, uploader) {
|
||||
let head = null;
|
||||
// sendMessage sends the message with an optional subscription to topic first.
|
||||
sendMessage(msg, uploadCompletionPromise, uploader) {
|
||||
let head;
|
||||
if (this.state.reply && this.state.reply.content) {
|
||||
head = {replyToSeq: this.state.reply.seq};
|
||||
let quote = this.state.reply.content;
|
||||
msg = Drafty.attachQuote(msg, quote);
|
||||
// Turn it into Drafty so we can make a quoted Drafty object later.
|
||||
if (typeof msg == 'string') {
|
||||
msg = Drafty.parse(msg);
|
||||
}
|
||||
msg = Drafty.attachQuote(msg, this.state.reply.content);
|
||||
this.setState({reply: null});
|
||||
}
|
||||
this.props.sendMessage(msg, uploadCompletionPromise, uploader, head);
|
||||
this.setState({reply: null});
|
||||
}
|
||||
|
||||
// Send attachment as Drafty message:
|
||||
@@ -683,13 +691,12 @@ class MessagesView extends React.Component {
|
||||
urlPromise: uploadCompletionPromise
|
||||
});
|
||||
// Pass data and the uploader to the TinodeWeb.
|
||||
this.doSendMessage(msg, uploadCompletionPromise, uploader);
|
||||
this.sendMessage(msg, uploadCompletionPromise, uploader);
|
||||
} else {
|
||||
// Small enough to send inband.
|
||||
fileToBase64(file,
|
||||
(mime, bits, fname) => {
|
||||
const msg = Drafty.attachFile(null, {mime: mime, data: bits, filename: fname});
|
||||
this.doSendMessage(msg, undefined, undefined);
|
||||
this.sendMessage(Drafty.attachFile(null, {mime: mime, data: bits, filename: fname}));
|
||||
},
|
||||
this.props.onError
|
||||
);
|
||||
@@ -753,7 +760,7 @@ class MessagesView extends React.Component {
|
||||
msg = Drafty.append(msg, Drafty.parse(caption));
|
||||
}
|
||||
// Pass data and the uploader to the TinodeWeb.
|
||||
this.doSendMessage(msg, uploadCompletionPromise, uploader);
|
||||
this.sendMessage(msg, uploadCompletionPromise, uploader);
|
||||
}
|
||||
)},
|
||||
// Failure
|
||||
@@ -778,7 +785,7 @@ class MessagesView extends React.Component {
|
||||
msg = Drafty.appendLineBreak(msg);
|
||||
msg = Drafty.append(msg, Drafty.parse(caption));
|
||||
}
|
||||
this.doSendMessage(msg, undefined, undefined);
|
||||
this.sendMessage(msg);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -920,14 +927,6 @@ class MessagesView extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleSendMessage(msg) {
|
||||
if (this.state.reply && this.state.reply.content && typeof msg == 'string') {
|
||||
// Turn it into Drafty so we can make a quoted Drafty object later.
|
||||
msg = Drafty.parse(msg);
|
||||
}
|
||||
this.doSendMessage(msg, undefined, undefined);
|
||||
}
|
||||
|
||||
handleCancelReply() {
|
||||
this.setState({reply: null})
|
||||
}
|
||||
@@ -1159,16 +1158,15 @@ class MessagesView extends React.Component {
|
||||
<Invitation onAction={this.handleNewChatAcceptance} />
|
||||
:
|
||||
<SendMessage
|
||||
tinode={this.props.tinode}
|
||||
disabled={!this.state.isWriter}
|
||||
onKeyPress={this.sendKeyPress}
|
||||
onSendMessage={this.sendMessage}
|
||||
onAttachFile={this.handleAttachFile}
|
||||
onAttachImage={this.handleAttachImage}
|
||||
onError={this.props.onError}
|
||||
|
||||
tinode={this.props.tinode}
|
||||
replyTo={this.state.reply}
|
||||
onQuoteClick={this.handleQuoteClick}
|
||||
onSendMessage={this.handleSendMessage}
|
||||
onFormatQuote={draftyFormatter}
|
||||
onCancelReply={this.handleCancelReply} />}
|
||||
</>
|
||||
|
||||
@@ -56,16 +56,17 @@ class NewTopicView extends React.Component {
|
||||
this.setState({searchQuery: Tinode.isNullValue(query) ? null : query});
|
||||
}
|
||||
|
||||
handleContactSelected(sel) {
|
||||
if (this.state.tabSelected === 'p2p') {
|
||||
handleContactSelected(uid) {
|
||||
if (this.state.tabSelected == 'p2p') {
|
||||
HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));
|
||||
this.props.onCreateTopic(sel);
|
||||
this.props.onCreateTopic(uid);
|
||||
}
|
||||
}
|
||||
|
||||
handleNewGroupSubmit(name, description, dataUrl, priv, tags, isChannel) {
|
||||
HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));
|
||||
this.props.onCreateTopic(undefined, theCard(name, dataUrl, null, description), priv, tags, isChannel);
|
||||
this.props.onCreateTopic(undefined,
|
||||
{public: theCard(name, dataUrl, null, description), private: priv, tags: tags}, isChannel);
|
||||
}
|
||||
|
||||
handleGroupByID(topicName) {
|
||||
@@ -112,6 +113,7 @@ class NewTopicView extends React.Component {
|
||||
type="p2p"
|
||||
onSearchContacts={this.handleSearchContacts} />
|
||||
<ContactList
|
||||
tinode={this.props.tinode}
|
||||
contacts={this.props.searchResults}
|
||||
myUserId={this.props.myUserId}
|
||||
emptyListMessage={no_contacts_placeholder}
|
||||
|
||||
@@ -917,10 +917,20 @@ class TinodeWeb extends React.Component {
|
||||
}
|
||||
|
||||
if (!topic.isSubscribed()) {
|
||||
// Topic is not subscribed yet. Subscribe.
|
||||
if (!promise) {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
promise = promise.then(() => { return topic.subscribe(); });
|
||||
promise = promise
|
||||
.then(() => topic.subscribe())
|
||||
.then(() => {
|
||||
// If there are unsent messages, try sending them now.
|
||||
topic.queuedMessages((pub) => {
|
||||
if (!pub._sending && topic.isSubscribed()) {
|
||||
this.sendMessage(pub);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (promise) {
|
||||
@@ -1184,7 +1194,7 @@ class TinodeWeb extends React.Component {
|
||||
}
|
||||
|
||||
// Request to start a topic, new or selected from search results, or "by ID".
|
||||
handleStartTopicRequest(topicName, pub, priv, tags, isChannel) {
|
||||
handleStartTopicRequest(topicName, newTopicParams, isChannel) {
|
||||
// Check if topic is indeed new. If not, launch it.
|
||||
if (topicName && this.tinode.isTopicCached(topicName)) {
|
||||
this.handleTopicSelected(topicName);
|
||||
@@ -1199,14 +1209,14 @@ class TinodeWeb extends React.Component {
|
||||
params.desc = {defacs: {auth: DEFAULT_P2P_ACCESS_MODE}};
|
||||
} else {
|
||||
topicName = topicName || this.tinode.newGroupTopicName(isChannel);
|
||||
params.desc = {public: pub, private: {comment: priv}};
|
||||
params.tags = tags;
|
||||
params.desc = {public: newTopicParams.public, private: {comment: newTopicParams.private}};
|
||||
params.tags = newTopicParams.tags;
|
||||
}
|
||||
params._topicName = topicName;
|
||||
this.setState({newTopicParams: params}, () => {this.handleTopicSelected(topicName)});
|
||||
}
|
||||
|
||||
// New topic was creted, here is the new topic name.
|
||||
// New topic was created, here is the new topic name.
|
||||
handleNewTopicCreated(oldName, newName) {
|
||||
if (this.state.topicSelected == oldName && oldName != newName) {
|
||||
// If the current URl contains the old topic name, replace it with new.
|
||||
|
||||
@@ -354,7 +354,7 @@ class ContextMenu extends React.Component {
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
console.log("Invalid menu item ID", e.currentTarget.dataset.id);
|
||||
console.error("Invalid menu item ID", e.currentTarget.dataset.id);
|
||||
} else {
|
||||
this.props.onAction(
|
||||
item.id,
|
||||
@@ -425,12 +425,12 @@ class ContextMenu extends React.Component {
|
||||
|
||||
replyToMessage(params, errorHandler) {
|
||||
console.log("Reply to message", params);
|
||||
params.pickReply({ seq: params.seq, content: params.content })
|
||||
params.pickReply({ seq: params.seq, content: params.content });
|
||||
}
|
||||
|
||||
render() {
|
||||
const menu = [];
|
||||
let count = 0;
|
||||
let menu = [];
|
||||
this.props.items.map((item) => {
|
||||
if (typeof item == 'string') {
|
||||
item = this.MenuItems[item];
|
||||
|
||||
@@ -35,7 +35,7 @@ export default class InPlaceEdit extends React.Component {
|
||||
}
|
||||
|
||||
handeTextChange(e) {
|
||||
this.setState({value: e.target.value});
|
||||
this.setState({value: e.target.value || ''});
|
||||
}
|
||||
|
||||
handleKeyDown(e) {
|
||||
@@ -59,13 +59,13 @@ export default class InPlaceEdit extends React.Component {
|
||||
}
|
||||
|
||||
handleEditingFinished(event) {
|
||||
if (this.props.required && !event.target.checkValidity()) {
|
||||
const value = this.state.value.trim();
|
||||
if (this.props.required && (!event.target.checkValidity() || !value)) {
|
||||
// Empty input
|
||||
this.setState({value: this.props.value, active: false});
|
||||
return;
|
||||
}
|
||||
this.setState({active: false});
|
||||
const value = this.state.value.trim();
|
||||
if ((value || this.props.value) && (value !== this.props.value)) {
|
||||
this.props.onFinished(value);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export default class NewTopicGroup extends React.PureComponent {
|
||||
}
|
||||
|
||||
handleFieldEdit(name, e) {
|
||||
this.setState({[name]: e.target.value});
|
||||
this.setState({[name]: e.target.value || ''});
|
||||
}
|
||||
|
||||
handleImageChanged(img) {
|
||||
@@ -82,7 +82,7 @@ export default class NewTopicGroup extends React.PureComponent {
|
||||
<FormattedMessage id="topic_name_editing_placeholder" defaultMessage="Freeform name of the group"
|
||||
description="Prompt for entering topic name">{
|
||||
(placeholder) => <input type="text" id="new-topic-fn" placeholder={placeholder}
|
||||
ref={this.fullName} value={this.state.fullName} onChange={this.handleFieldEdit.bind('fullName')}
|
||||
ref={this.fullName} value={this.state.fullName} onChange={this.handleFieldEdit.bind(this, 'fullName')}
|
||||
autoFocus required tabIndex={0} />
|
||||
}</FormattedMessage>
|
||||
</div>
|
||||
@@ -94,7 +94,7 @@ export default class NewTopicGroup extends React.PureComponent {
|
||||
<FormattedMessage id="private_editing_placeholder" defaultMessage="Visible to you only"
|
||||
description="Placeholder for editing 'private'">{
|
||||
(placeholder) => <input type="text" id="new-topic-priv" placeholder={placeholder}
|
||||
value={this.state.private} onChange={this.handleFieldEdit.bind('private')} tabIndex={1} />
|
||||
value={this.state.private} onChange={this.handleFieldEdit.bind(this, 'private')} tabIndex={1} />
|
||||
}</FormattedMessage>
|
||||
</div>
|
||||
<div className="group">
|
||||
@@ -105,7 +105,7 @@ export default class NewTopicGroup extends React.PureComponent {
|
||||
<FormattedMessage id="description_editing_placeholder" defaultMessage="Description (optional)"
|
||||
description="Placeholder for editing topic description">{
|
||||
(placeholder) => <input type="text" id="new-topic-desc" placeholder={placeholder}
|
||||
value={this.state.description} onChange={this.handleFieldEdit.bind('description')} tabIndex={2} />
|
||||
value={this.state.description} onChange={this.handleFieldEdit.bind(this, 'description')} tabIndex={2} />
|
||||
}</FormattedMessage>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -212,12 +212,15 @@ export default class TopicDescEdit extends React.Component {
|
||||
description="Label for editing topic name" />
|
||||
</label></div>
|
||||
<div>
|
||||
<InPlaceEdit
|
||||
placeholder={this.state.groupTopic ? "Group name" : <i>Unknown</i>}
|
||||
<FormattedMessage id="topic_name_editing_placeholder" defaultMessage="Freeform name of the group"
|
||||
description="Prompt for entering topic name">{
|
||||
(group_name_placeholder) => <InPlaceEdit
|
||||
placeholder={group_name_placeholder}
|
||||
readOnly={!editable}
|
||||
value={this.state.fullName}
|
||||
required={true}
|
||||
onFinished={this.handleFullNameUpdate} />
|
||||
}</FormattedMessage>
|
||||
</div>
|
||||
</div>
|
||||
<div className="group">
|
||||
@@ -228,8 +231,7 @@ export default class TopicDescEdit extends React.Component {
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<FormattedMessage id="private_editing_placeholder"
|
||||
defaultMessage="Visible to you only"
|
||||
<FormattedMessage id="private_editing_placeholder" defaultMessage="Visible to you only"
|
||||
description="Placeholder for editing 'private'">{
|
||||
(private_placeholder) => <InPlaceEdit
|
||||
placeholder={private_placeholder}
|
||||
|
||||
+311
-338
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+494
-102
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user