From 7a7104dfa5f7aec0a23c6c6d57d569138f2c665d Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 17 Feb 2020 11:02:35 +1100 Subject: [PATCH] Roation fix of groupsettings chevron --- js/modules/signal.js | 2 + js/views/app_view.js | 11 +- js/views/session_conversation_view.js | 28 ++ .../session/SessionChannelSettings.tsx | 302 ++++++++++++++++++ ts/components/session/SessionConversation.tsx | 21 ++ 5 files changed, 356 insertions(+), 8 deletions(-) create mode 100644 js/views/session_conversation_view.js create mode 100644 ts/components/session/SessionChannelSettings.tsx create mode 100644 ts/components/session/SessionConversation.tsx diff --git a/js/modules/signal.js b/js/modules/signal.js index f5ddc198b..3b0cb5932 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -18,6 +18,7 @@ const LinkPreviews = require('./link_previews'); const AttachmentDownloads = require('./attachment_downloads'); // Components +const { SessionConversation } = require('../../ts/components/session/SessionConversation'); const { ConversationLoadingScreen, } = require('../../ts/components/ConversationLoadingScreen'); @@ -274,6 +275,7 @@ exports.setup = (options = {}) => { }); const Components = { + SessionConversation, ConversationLoadingScreen, AttachmentList, CaptionEditor, diff --git a/js/views/app_view.js b/js/views/app_view.js index 1940fa7ff..ee5f45126 100644 --- a/js/views/app_view.js +++ b/js/views/app_view.js @@ -228,15 +228,10 @@ const dialog = new Whisper.CreateGroupDialogView(); this.el.append(dialog.el); }, - showUpdateGroupNameDialog(groupConvo) { - const dialog = new Whisper.UpdateGroupNameDialogView(groupConvo); - this.el.append(dialog.el); - }, - showUpdateGroupMembersDialog(groupConvo) { - const dialog = new Whisper.UpdateGroupMembersDialogView(groupConvo); - this.el.append(dialog.el); + showUpdateGroupDialog(groupConvo) { + const dialog = new Whisper.UpdateGroupDialogView(groupConvo); + this.el.prepend(dialog.el); }, - showSessionRestoreConfirmation(options) { const dialog = new Whisper.ConfirmSessionResetView(options); this.el.append(dialog.el); diff --git a/js/views/session_conversation_view.js b/js/views/session_conversation_view.js new file mode 100644 index 000000000..be2813344 --- /dev/null +++ b/js/views/session_conversation_view.js @@ -0,0 +1,28 @@ +/* global Whisper $ */ + +// eslint-disable-next-line func-names +(function() { + 'use strict'; + + window.Whisper = window.Whisper || {}; + + Whisper.SessionConversationView = Whisper.View.extend({ + initialize(options) { + this.props = { + el: $('body'), + ...options, + }; + }, + + render() { + this.conversationView = new Whisper.SessionConversationView({ + className: 'session-conversation-wrapper', + Component: window.Signal.Components.SessionConversation, + props: this.props, + }); + + this.$el.prepend(this.conversationView.el); + }, + }); + })(); + \ No newline at end of file diff --git a/ts/components/session/SessionChannelSettings.tsx b/ts/components/session/SessionChannelSettings.tsx new file mode 100644 index 000000000..7b01ce114 --- /dev/null +++ b/ts/components/session/SessionChannelSettings.tsx @@ -0,0 +1,302 @@ +import React from 'react'; +import { SessionIconButton, SessionIconSize, SessionIconType } from './icon'; +import { Avatar } from '../Avatar'; +import { + SessionButton, + SessionButtonColor, + SessionButtonType, +} from './SessionButton'; +import { SessionDropdown } from './SessionDropdown'; +import { MediaGallery } from '../conversation/media-gallery/MediaGallery'; +import _ from 'lodash'; +import { TimerOption } from '../conversation/ConversationHeader'; + +interface Props { + id: string; + name: string; + memberCount: number; + description: string; + avatarPath: string; + timerOptions: Array; + isPublic: boolean; + + onGoBack: () => void; + onInviteFriends: () => void; + onLeaveGroup: () => void; + onShowLightBox: (options: any) => void; + onSetDisappearingMessages: (seconds: number) => void; +} + +export class SessionChannelSettings extends React.Component { + public constructor(props: Props) { + super(props); + + this.state = { + documents: Array(), + media: Array(), + onItemClick: undefined, + }; + } + + public componentWillMount() { + this.getMediaGalleryProps() + .then(({ documents, media, onItemClick }) => { + this.setState({ + documents, + media, + onItemClick, + }); + }) + .ignore(); + } + + public componentDidUpdate() { + const mediaScanInterval = 1000; + + setTimeout(() => { + this.getMediaGalleryProps() + .then(({ documents, media, onItemClick }) => { + this.setState({ + documents, + media, + onItemClick, + }); + }) + .ignore(); + }, mediaScanInterval); + } + + public async getMediaGalleryProps() { + // We fetch more documents than media as they don’t require to be loaded + // into memory right away. Revisit this once we have infinite scrolling: + const DEFAULT_MEDIA_FETCH_COUNT = 50; + const DEFAULT_DOCUMENTS_FETCH_COUNT = 150; + const conversationId = this.props.id; + const rawMedia = await window.Signal.Data.getMessagesWithVisualMediaAttachments( + conversationId, + { + limit: DEFAULT_MEDIA_FETCH_COUNT, + MessageCollection: window.Whisper.MessageCollection, + } + ); + const rawDocuments = await window.Signal.Data.getMessagesWithFileAttachments( + conversationId, + { + limit: DEFAULT_DOCUMENTS_FETCH_COUNT, + MessageCollection: window.Whisper.MessageCollection, + } + ); + + // First we upgrade these messages to ensure that they have thumbnails + const max = rawMedia.length; + for (let i = 0; i < max; i += 1) { + const message = rawMedia[i]; + const { schemaVersion } = message; + + if (schemaVersion < message.VERSION_NEEDED_FOR_DISPLAY) { + // Yep, we really do want to wait for each of these + // eslint-disable-next-line no-await-in-loop + rawMedia[i] = await window.Signal.Migrations.upgradeMessageSchema( + message + ); + // eslint-disable-next-line no-await-in-loop + await window.Signal.Data.saveMessage(rawMedia[i], { + Message: window.Whisper.Message, + }); + } + } + + // tslint:disable-next-line: underscore-consistent-invocation + const media = _.flatten( + rawMedia.map((message: { attachments: any }) => { + const { attachments } = message; + + return (attachments || []) + .filter( + (attachment: { thumbnail: any; pending: any; error: any }) => + attachment.thumbnail && !attachment.pending && !attachment.error + ) + .map( + ( + attachment: { path?: any; contentType?: any; thumbnail?: any }, + index: any + ) => { + const { thumbnail } = attachment; + + return { + objectURL: window.Signal.Migrations.getAbsoluteAttachmentPath( + attachment.path + ), + thumbnailObjectUrl: thumbnail + ? window.Signal.Migrations.getAbsoluteAttachmentPath( + thumbnail.path + ) + : null, + contentType: attachment.contentType, + index, + attachment, + message, + }; + } + ); + }) + ); + + // Unlike visual media, only one non-image attachment is supported + const documents = rawDocuments.map( + (message: { attachments: Array }) => { + const attachments = message.attachments || []; + const attachment = attachments[0]; + + return { + contentType: attachment.contentType, + index: 0, + attachment, + message, + }; + } + ); + + const saveAttachment = async ({ attachment, message }: any = {}) => { + const timestamp = message.received_at; + window.Signal.Types.Attachment.save({ + attachment, + document, + getAbsolutePath: window.Signal.Migrations.getAbsoluteAttachmentPath, + timestamp, + }); + }; + + const onItemClick = async ({ message, attachment, type }: any) => { + switch (type) { + case 'documents': { + saveAttachment({ message, attachment }).ignore(); + break; + } + + case 'media': { + const lightBoxOptions = { + media, + attachment, + message, + }; + this.onShowLightBox(lightBoxOptions); + break; + } + + default: + throw new TypeError(`Unknown attachment type: '${type}'`); + } + }; + + return { + media, + documents, + onItemClick, + }; + } + + public onShowLightBox(options: any) { + this.props.onShowLightBox(options); + } + + public render() { + const { + memberCount, + name, + timerOptions, + onLeaveGroup, + isPublic, + } = this.props; + const { documents, media, onItemClick } = this.state; + const showMemberCount = !!(memberCount && memberCount > 0); + const hasDisappearingMessages = !isPublic; + const leaveGroupString = isPublic + ? window.i18n('leaveOpenGroup') + : window.i18n('leaveClosedGroup'); + + const disappearingMessagesOptions = timerOptions.map(option => { + return { + content: option.name, + onClick: () => { + this.props.onSetDisappearingMessages(option.value); + }, + }; + }); + + return ( +
+ {this.renderHeader()} +

{name}

+ {showMemberCount && ( + <> +
+
+ {window.i18n('members', memberCount)} +
+
+ + )} + + +
+ {window.i18n('notifications')} +
+ + {hasDisappearingMessages && ( + + )} + + + +
+ ); + } + + private renderHeader() { + const { id, onGoBack, onInviteFriends, avatarPath } = this.props; + const shouldShowInviteFriends = !this.props.isPublic; + + return ( +
+ + + +
+ {shouldShowInviteFriends && ( + + )} +
+
+ ); + } +} diff --git a/ts/components/session/SessionConversation.tsx b/ts/components/session/SessionConversation.tsx new file mode 100644 index 000000000..7366b0c71 --- /dev/null +++ b/ts/components/session/SessionConversation.tsx @@ -0,0 +1,21 @@ +import React from 'react'; + +interface Props{}; + +interface State{}; + + +export class SessionConversation extends React.Component { + public constructor(props: Props) { + super(props); + this.state = {}; + } + + render() { + return ( +
+ THIS IS AN INBOX VIEW +
+ ) + } +} \ No newline at end of file