[still broken]* A bit progress on:

- transforming the export format to json
- async
- cleanup cod
(*broken state, just a commit to save progress)
This commit is contained in:
Simon Laux
2020-06-11 21:21:32 +02:00
parent da9f45d9ff
commit 54637004cd
2 changed files with 211 additions and 1007 deletions

View File

@@ -1,788 +0,0 @@
/* TODO inlcude the referenced svgs as base64 data uris */
.header {
background-color: #415e6b;
color: #fff;
position: absolute;
height: 52px;
width: 100%;
z-index: 5;
display:flex;
}
.header .avatar {
height: 36px;
width: 36px;
border-radius: 100%;
user-select: none;
margin: 8px;
}
.header .avatar.text-avatar {
background-color: #505050;
color: white;
font-size: 26px;
line-height: 36px;
text-align: center;
}
.header .name {
height: 52px;
line-height: 52px;
margin-left: 3px;
font-size: 20px;
}
.message.outgoing .author-avatar, .message.outgoing .author {
display: none!important;
}
:root {--colorPrimary: #42A5F5;--colorDanger: #f96856;--colorNone: #a0a0a0;--ovalButtonBg: #415e6b;--ovalButtonBgHover: rgb(120, 156, 173);--ovalButtonText: #fff;--ovalButtonTextHover: rgb(0, 0, 0);--navBarBackground: #415e6b;--navBarText: #fff;--navBarSearchPlaceholder: rgb(186, 186, 186);--navBarGroupSubtitle: rgb(186, 186, 186);--chatViewBg: #e6dcd4;--chatViewBgImgPath: url(../images/background_light.svg);--composerBg: #fff;--composerText: #010101;--composerPlaceholderText: rgba(1, 1, 1, 0.5);--composerBtnColor: rgba(1, 1, 1, 0.9);--composerSendButton: #415e6b;--emojiSelectorSelectionColor: #2090ea;--chatListItemSelectedBg: #4c6e7d;--chatListItemSelectedBgHover: #5E889B;--chatListItemSelectedText: #fff;--chatListItemBgHover: rgb(228, 228, 228);--chatListBorderColor: #b9b9b9;--chatListBorder: 1px solid undefined;--messageText: #010101;--messageTextLink: #010101;--setupMessageText: #ed824e;--infoMessageBubbleBg: #0000008c;--infoMessageBubbleText: white;--messageIncommingBg: #fff;--messageIncommingDate: #010101;--messageOutgoingBg: #efffde;--messageOutgoingStatusColor: #4caf50;--messageButtons: #8b8e91;--messageButtonsHover: #070c14;--messageStatusIcon: #4caf50;--messageStatusIconSending: #62656a;--messagePadlockOutgoing: #4caf50;--messagePadlockIncomming: #a4a6a9;--messageMetadataDate: #62656a;--messageMetadataIncomming: rgba(#ffffff, 0.7);--messageAuthor: #ffffff;--messageAttachmentIconExtentionColor: #070c14;--messageAttachmentIconBg: transparent;--messageAttachmentFileInfo: #010101;--loginInputFocusColor: #42A5F5;--loginButtonText: #42A5F5;--deltaChatPrimaryFg: #010101;--deltaChatPrimaryFgLight: #62656a;--contextMenuBg: #fff;--contextMenuBorder: rgb(221, 221, 221);--contextMenuText: #62656a;--contextMenuSelected: #f5f5f5;--contextMenuSelectedBg: #a4a6a9;--bp3DialogHeaderBg: #fff;--bp3DialogHeaderIcon: #666666;--bp3DialogBgSecondary: #ececec;--bp3DialogBgPrimary: #fff;--bp3Heading: #010101;--bp3ButtonText: #010101;--bp3ButtonBg: #fff;--bp3ButtonGradientTop: rgba(255,255,255,0.8);--bp3ButtonGradientBottom: rgba(255,255,255,0);--bp3ButtonHoverBg: #ebf1f5;--bp3InputText: #010101;--bp3InputBg: #fff;--bp3InputPlaceholder: lightgray;--bp3MenuText: #010101;--bp3MenuBg: #fff;--bp3Switch: #7a8084;--bp3SwitchShadow: unset;--bp3SwitchChecked: #acd4e8;--bp3SwitchShadowChecked: unset;--bp3SwitchKnob: #f5f5f5;--bp3SwitchKnobShadow: 0px 2px 0 0px #d2cfcfad;--bp3SwitchKnobChecked: #42A5F5;--bp3SwitchKnobShadowChecked: 0px 1px 0 0px #c9d4d2d1;--bp3SpinnerTrack: #acd4e8;--bp3SpinnerHead: #42a5f5;--bp3SelectorTop: rgba(255, 255, 255, 0.8);--bp3SelectorBottom: rgba(255, 255, 255, 0.0);--outlineProperties: 1px solid transparent;--outlineColor: b9b9b9;--emojiMartText: #010101;--emojiMartSearchBorder: lightgrey;--emojiMartBg: #fff;--emojiMartOutsideRadius: 5px;--emojiMartCategoryIcons: rgb(99, 99, 99);--emojiMartInputBg: #f5f5f5;--emojiMartInputText: #010101;--emojiMartInputPlaceholder: rgb(74, 74, 74);--emojiMartSelect: rgb(198, 198, 198);--galleryBg: #fff;--avatarLabelColor: #ffffff;--brokenMediaText: #070c14;--brokenMediaBg: #ffffff;--unreadCountBg: #2090ea;--unreadCountLabel: #ffffff;--contactListItemBg: #62656a;--contactListInitalColor: #62656a;--contactEmailColor: #62656a;--errorColor: #f44336;--globalLinkColor: #2090ea;--globalBackground: #fff;--globalText: #010101;--mapOverlayBg: #fff;--videoPlayBtnIcon: #2090ea;--videoPlayBtnBg: #ffffff;--scrollbarThumb: #666666;--scrollbarThumbHover: #606060;}
* {
box-sizing: border-box;
}
html {
height: 100%;
--messageIncommingBg: rgb(232, 232, 232);
}
body {
position: relative;
height: 100%;
width: 100%;
margin: 0;
font-family: Roboto, "Apple Color Emoji", NotoEmoji, "Helvetica Neue", Arial,
Helvetica, NotoMono, sans-serif !important;
font-size: 14px;
color: black;
background-color: white;
}
ul {
list-style: none;
padding-left: 0;
}
input:focus {
outline: 0 !important;
}
button:focus {
outline: none;
}
button:focus {
outline: none;
}
::-webkit-scrollbar {
width: 6px;
height: 0;
}
::-webkit-scrollbar-track {
background: white;
}
::-webkit-scrollbar-thumb {
background: var(--scrollbarThumb);
}
::-webkit-scrollbar-thumb:hover {
background: var(--scrollbarThumbHover);
}
::-webkit-scrollbar-corner {
background: transparent;
}
span.module-contact-name {
font-weight: 200;
font-size: medium;
}
.module-contact-name__profile-name {
font-style: italic;
}
.AvatarBubble {
position: relative;
z-index: 2;
object-fit: cover;
height: 48px;
width: 48px;
margin-top: 8px;
margin-bottom: 8px;
border-radius: 100%;
background-color: #505050;
color: var(--avatarLabelColor);
font-size: 26px;
line-height: 48px;
text-align: center;
user-select: none;
}
.AvatarBubble.large {
height: 64px;
width: 64px;
line-height: 64px;
font-size: 39px;
}
.AvatarBubble--NoSearchResults {
transform: rotate(45deg);
line-height: 46px;
letter-spacing: 1px;
}
.AvatarBubble--NoSearchResults::after {
content: ":-(";
}
.AvatarImage {
position: relative;
z-index: 2;
object-fit: cover;
height: 48px;
width: 48px;
margin-top: 8px;
margin-bottom: 8px;
border-radius: 100%;
user-select: none;
}
.AvatarImage.large {
height: 64px;
width: 64px;
}
.attachment-overlay .attachment-view {
height: 100%;
padding: 0;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: #313131;
}
.attachment-overlay .attachment-view img,
.attachment-overlay .attachment-view video {
width: 100vw;
max-height: 100vh;
object-fit: contain;
}
.attachment-overlay .attachment-view video {
width: 95vw;
}
.attachment-overlay .render-media-wrapper {
width: 100vw;
height: 100vh;
}
.attachment-overlay .btn-wrapper {
float: right;
position: absolute;
z-index: 10;
cursor: pointer;
}
.attachment-overlay .download-btn {
height: 36px;
width: 36px;
display: inline-block;
-webkit-mask: url("../images/download.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: var(--messageButtons);
}
.attachment-overlay .download-btn:hover {
background-color: var(--messageButtons);
}
.message-attachment-media {
text-align: center;
position: relative;
cursor: pointer;
margin-left: -12px;
margin-right: -12px;
margin-top: -10px;
margin-bottom: -10px;
border-radius: 16px;
overflow: hidden;
background-color: var(--messageAttachmentIconBg);
}
.message-attachment-media > .attachment-content {
object-fit: scale-down;
object-position: center;
min-height: 150px;
max-height: 300px;
max-width: 40vw;
margin-bottom: -4px;
cursor: pointer;
}
.message-attachment-media > .video-play-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 48px;
height: 48px;
background-color: var(--videoPlayBtnBg);
border-radius: 24px;
}
.message-attachment-media > .video-play-btn > .video-play-btn-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 36px;
width: 36px;
-webkit-mask: url("../images/play.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: var(--videoPlayBtnIcon);
}
.message-attachment-media.content-below {
margin-bottom: 7px;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}
.message-attachment-media.content-above {
margin-top: 4px;
border-top-left-radius: 0px;
border-top-right-radius: 0px;
}
.message-attachment-broken-media {
font-size: 11px;
line-height: 16px;
letter-spacing: 0.3px;
padding: 10px;
text-align: center;
text-transform: uppercase;
color: var(--brokenMediaBg);
}
.message-attachment-broken-media.incoming {
color: var(--brokenMediaText);
}
.message-attachment-audio {
margin-top: 2px;
display: block;
margin-right: 30px;
}
.message-attachment-audio.content-below {
margin-bottom: 5px;
}
.message-attachment-audio.content-above {
margin-top: 6px;
}
.message-attachment-generic {
display: flex;
flex-direction: row;
}
.message-attachment-generic.content-below {
padding-bottom: 6px;
}
.message-attachment-generic.content-above {
padding-top: 4px;
}
.message-attachment-generic > .file-icon {
background: url("../images/file-gradient.svg") no-repeat center;
height: 44px;
width: 56px;
margin-left: -13px;
margin-right: -14px;
margin-bottom: -4px;
display: flex;
flex-direction: row;
align-items: center;
}
.message-attachment-generic > .file-icon > .file-extension {
font-size: 10px;
line-height: 13px;
letter-spacing: 0.1px;
text-transform: uppercase;
text-align: center;
width: 25px;
margin-left: auto;
margin-right: auto;
overflow-x: hidden;
white-space: nowrap;
text-overflow: clip;
color: var(--messageAttachmentIconExtentionColor);
font-family: monospace;
}
.message-attachment-generic > .text-part {
flex-grow: 1;
margin-left: 8px;
max-width: calc(100% - 37px);
}
.message-attachment-generic > .text-part > .name {
color: var(--messageAttachmentFileInfo);
font-size: 14px;
line-height: 18px;
font-weight: 300;
margin-top: 2px;
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.message-attachment-generic > .text-part > .size {
color: var(--messageAttachmentFileInfo);
font-size: 11px;
line-height: 16px;
letter-spacing: 0.3px;
margin-top: 3px;
}
.module-message-detail {
margin-top: -20px;
}
.module-message-detail .bp3-callout {
max-height: 50vh;
overflow: auto;
}
.module-message-detail p {
white-space: pre-line;
user-select: text;
}
.module-message-detail__message-container {
padding-top: 20px;
padding-bottom: 20px;
}
.module-message-detail__message-container:after {
content: ".";
visibility: hidden;
display: block;
height: 0;
clear: both;
}
.module-message-detail__label {
font-weight: 300;
padding-right: 5px;
}
.module-message-detail__unix-timestamp {
color: #eeefef;
}
.module-message-detail__delete-button-container {
text-align: center;
margin-top: 10px;
}
.module-message-detail__delete-button {
background: none;
color: inherit;
border: none;
padding: 0;
font: inherit;
cursor: pointer;
outline: inherit;
background-color: #f44336;
color: #fff;
box-shadow: 0 0 10px -3px rgba(97, 97, 97, 0.7);
border-radius: 5px;
border: solid 1px #a4a6a9;
cursor: pointer;
margin: 1em auto;
padding: 1em;
}
.module-message-detail .message-content * {
background-color: lightgrey;
width: 100%;
resize: none;
padding: 1rem;
}
.message-list-and-composer {
width: 70%;
float: right;
display: grid;
grid-template-columns: auto;
height: calc(100vh - 50px);
margin-top: 50px;
background-image: var(--chatViewBgImgPath);
background-size: cover;
background-color: var(--chatViewBg);
}
.message-list-and-composer__message-list #message-list {
background: #dbdbdb;
position: absolute;
bottom: 0;
overflow: scroll;
max-height: 100%;
width: 100%;
padding: 0 0.5em;
top: 52px;
}
.message-list-and-composer__message-list
#message-list::-webkit-scrollbar-track {
background: transparent;
}
.message-list-and-composer__message-list ul {
list-style: none;
min-width: 200px;
}
.message-list-and-composer__message-list ul li {
margin-bottom: 10px;
min-width: 200px;
}
.message-list-and-composer__message-list ul li::after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.message-list-and-composer__message-list ul li .info-message {
max-width: 550px;
font-size: 1rem;
padding: 2rem;
font-style: normal;
white-space: pre-wrap;
text-align: left;
}
.message {
position: relative;
display: inline-flex;
flex-direction: row;
align-items: stretch;
}
.message:hover .message-buttons {
opacity: 1;
}
.message > .author-avatar {
align-self: flex-end;
bottom: 0px;
position: static;
margin-right: 8px;
user-select: none;
}
.message > .author-avatar img {
height: 36px;
width: 36px;
border-radius: 18px;
object-fit: cover;
}
.message > .author-avatar.default {
text-align: center;
}
.message > .author-avatar.default > .label {
user-select: none;
color: var(--avatarLabelColor);
top: -121px;
left: -10px;
border-radius: 50%;
width: 36px;
height: 36px;
font-size: 25px;
line-height: 36px;
}
.message .message-buttons {
position: absolute;
top: 5px;
right: -4px;
display: inline-flex;
flex-direction: row;
align-items: center;
opacity: 0;
z-index: 10;
user-select: text;
}
.message .message-buttons .msg-button {
height: 24px;
width: 24px;
display: inline-block;
cursor: pointer;
}
.message .message-buttons .msg-button:hover {
background-color: var(--messageButtons);
}
.message .message-buttons .msg-button.download {
-webkit-mask: url("../images/download.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: var(--messageButtons);
}
.message .message-buttons .msg-button.reply {
display: none;
-webkit-mask: url("../images/reply.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: var(--messageButtons);
user-select: none;
}
.message .message-buttons .msg-button.menu {
-webkit-mask: url("../images/ellipsis.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: var(--messageButtons);
transform: rotate(90deg);
-webkit-mask-position-y: 4px;
user-select: none;
}
.message .msg-container {
position: relative;
display: inline-block;
border-radius: 16px;
padding-right: 12px;
padding-left: 12px;
padding-top: 10px;
padding-bottom: 10px;
}
.message .msg-container > .author {
display: inline-block;
max-width: 40vw;
font-size: 13px;
font-weight: 300;
line-height: 18px;
height: 18px;
overflow-x: hidden;
overflow-y: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.message .msg-container .msg-body.msg-body--clickable {
cursor: pointer;
}
.message .msg-container .msg-body > .text {
color: var(--messageText);
font-size: 14px;
line-height: 18px;
text-align: start;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
white-space: pre-wrap;
margin-right: 10px;
}
.message .msg-container .msg-body > .text a {
text-decoration: underline;
color: var(--messageTextLink);
}
.message .msg-container .msg-body > .text .double-line-break {
height: 28px;
}
.message .msg-container .msg-body > .text .line-break {
height: 14px;
}
.message .msg-container .msg-body > .text .line-break:last-child {
height: 0px;
}
.message .metadata {
margin-top: 10px;
margin-bottom: -7px;
float: right;
}
.message .module-message__img-attachment {
object-fit: cover;
width: auto;
max-width: 100%;
min-height: unset;
}
.message.incoming {
margin-left: 0;
margin-right: 32px;
}
.message.incoming .metadata:not(.with-image-no-caption) > .padlock-icon {
-webkit-mask: url("../images/padlock.svg") no-repeat center;
-webkit-mask-size: 125%;
background-color: var(--messagePadlockIncomming);
}
.message.incoming .metadata:not(.with-image-no-caption) > .location-icon {
-webkit-mask: url("../images/map-marker.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: var(--messagePadlockIncomming);
}
.message.incoming .metadata:not(.with-image-no-caption) > .date {
color: var(--messageMetadataIncomming);
}
.message.incoming .msg-container {
background-color: var(--messageIncommingBg);
}
.message.incoming .msg-container,
.message.incoming .msg-container .message-attachment-media {
border-bottom-left-radius: 1px;
}
.message.outgoing {
float: right;
margin-right: 0;
margin-left: 32px;
}
.message.outgoing .metadata > .date {
color: var(--messageOutgoingStatusColor);
}
.message.outgoing .metadata > .padlock-icon {
-webkit-mask: url("../images/padlock.svg") no-repeat center;
-webkit-mask-size: 125%;
background-color: var(--messagePadlockOutgoing);
}
.message.outgoing .metadata > .location-icon {
-webkit-mask: url("../images/map-marker.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: var(--messagePadlockOutgoing);
}
.message.outgoing .metadata > .status-icon.read,
.message.outgoing .metadata > .status-icon.delivered {
background-color: var(--messageOutgoingStatusColor);
}
.message.outgoing .msg-container {
background-color: var(--messageOutgoingBg);
}
.message.outgoing .msg-container,
.message.outgoing .msg-container .message-attachment-media {
border-bottom-right-radius: 1px;
}
.message.type-sticker .msg-container {
background-color: transparent !important;
}
.message.type-sticker .message-attachment-media {
background-color: transparent;
}
.message.type-sticker .message-attachment-media > .attachment-content {
margin-bottom: 20px;
}
.message.type-sticker .metadata {
float: right;
padding: 4px 10px 1px 10px;
margin-bottom: -7px;
background-color: #01010159;
border-radius: 4px;
color: black;
font-weight: bold;
}
.message.type-sticker .metadata > .date {
font-size: 11px;
color: white;
}
.message.type-sticker .metadata > .padlock-icon {
-webkit-mask: url("../images/padlock.svg") no-repeat center;
-webkit-mask-size: 125%;
background-color: #fff;
}
.message.type-sticker .metadata > .location-icon {
-webkit-mask: url("../images/map-marker.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: #fff;
}
.message.type-sticker .status-icon.read,
.message.type-sticker .status-icon.delivered {
background-color: white;
}
.message.type-sticker:hover .msg-button.menu {
background-color: white;
}
.message.type-sticker:hover .react-contextmenu-wrapper {
background-color: #2525258f;
border-radius: 4px;
}
.message.error.incoming .text {
font-style: italic;
}
.message.forwarded .forwarded-indicator {
font-weight: bold;
font-size: 0.9em;
margin-bottom: 3px;
opacity: 0.86;
}
.message.forwarded .message-attachment-media {
border-top-left-radius: 0;
border-top-right-radius: 0;
margin-top: 0;
}
.setupMessage .message .text {
color: var(--setupMessageText);
}
.hide-on-small {
display: initial;
}
@media (max-width: 800px) {
.hide-on-small {
display: none;
}
}
@media (min-width: 800px) and (max-width: 925px) {
.message {
max-width: 374px;
}
.message.incoming {
margin-right: auto;
}
.message.outgoing {
margin-left: auto;
}
}
@media (min-width: 926px) {
.message {
max-width: 66%;
}
.message.incoming {
margin-right: auto;
}
.message.outgoing {
margin-left: auto;
}
}
.metadata {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 3px;
margin-bottom: -3px;
}
.metadata.with-image-no-caption {
position: absolute;
right: 5px;
bottom: 5px;
float: right;
padding: 4px 10px 1px 10px;
margin: 0;
background-color: #0000008f;
border-radius: 4px;
font-weight: bold;
}
.metadata.with-image-no-caption > .date {
color: white;
}
.metadata.with-image-no-caption > .padlock-icon {
-webkit-mask: url("../images/padlock.svg") no-repeat center;
-webkit-mask-size: 125%;
background-color: #fff;
}
.metadata.with-image-no-caption .status-icon.sending {
background-color: white;
}
.metadata > .status-icon {
margin-bottom: 2px;
}
.metadata > .username {
margin-right: 10px;
}
.metadata > .date {
font-size: 11.5px;
line-height: 16px;
letter-spacing: 0.3px;
color: var(--messageMetadataDate);
text-transform: uppercase;
}
.metadata > .spacer {
flex-grow: 1;
}
.metadata > .padlock-icon,
.metadata > .location-icon {
width: 12px;
height: 12px;
display: inline-block;
margin-right: 2px;
margin-bottom: 3px;
}
.metadata > .location-icon {
margin-bottom: 0;
}
@keyframes __status-icon--spinning {
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
.status-icon {
width: 18px;
height: 12px;
display: inline-block;
margin-left: 2px;
}
.status-icon.sending {
-webkit-mask: url("../images/sending.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: var(--messageStatusIconSending);
animation: __status-icon--spinning 4s linear infinite;
width: 12px;
margin-left: 8px;
}
.status-icon.delivered {
-webkit-mask: url("../images/sent.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: var(--messageStatusIcon);
}
.status-icon.read {
-webkit-mask: url("../images/read.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: var(--messageStatusIcon);
}
.status-icon.error {
-webkit-mask: url("../images/error.svg") no-repeat center;
-webkit-mask-size: 100%;
background-color: var(--errorColor);
width: 12px;
margin-left: 8px;
}

View File

@@ -11,19 +11,15 @@ use std::io::prelude::*;
use std::path::Path; use std::path::Path;
use zip::write::FileOptions; use zip::write::FileOptions;
use serde::Serialize;
#[derive(Debug)] #[derive(Debug)]
pub struct ExportChatResult { pub struct ExportChatResult {
html: String, chat_json: String,
// locations_geo_json: String,
referenced_blobs: Vec<String>, referenced_blobs: Vec<String>,
} }
struct ContactInfo {
name: String,
initial: String,
color: String,
profile_img: Option<String>,
}
pub fn pack_exported_chat( pub fn pack_exported_chat(
context: &Context, context: &Context,
artifact: ExportChatResult, artifact: ExportChatResult,
@@ -34,11 +30,8 @@ pub fn pack_exported_chat(
let mut zip = zip::ZipWriter::new(file); let mut zip = zip::ZipWriter::new(file);
zip.start_file("index.html", Default::default())?; zip.start_file("index.json", Default::default())?;
zip.write_all(artifact.html.as_bytes())?; zip.write_all(artifact.chat_json.as_bytes())?;
zip.start_file("styles.css", Default::default())?;
zip.write_all(include_bytes!("../assets/exported-chat.css"))?;
zip.add_directory("blobs/", Default::default())?; zip.add_directory("blobs/", Default::default())?;
@@ -60,14 +53,58 @@ pub fn pack_exported_chat(
Ok(()) Ok(())
} }
pub fn export_chat(context: &Context, chat_id: ChatId) -> ExportChatResult { #[derive(Serialize)]
struct ChatJSON {
name: String,
color: String,
profile_img: Option<String>,
contacts: HashMap<u32, ContactJSON>,
messages: Vec<MessageJSON>,
}
#[derive(Serialize)]
struct ContactJSON {
name: String,
email: String,
color: String,
profile_img: Option<String>,
}
#[derive(Serialize)]
struct FileReference {
name: String,
filesize: String, /* todo human readable file size*/
extension: String,
}
#[derive(Serialize)]
enum MessageJSON {
Message {
id: u32,
author_id: u32, // from_id
viewType: Viewtype,
timestamp_sort: i64,
timestamp_sent: i64,
timestamp_rcvd: i64,
text: Option<String>,
attachment: Option<FileReference>,
// location
}, // Info Message?
}
impl MessageJSON {
pub fn from_message(message: Message, context: &Context) -> MessageJSON {}
}
pub async fn export_chat(context: &Context, chat_id: ChatId) -> ExportChatResult {
let mut blobs = Vec::new(); let mut blobs = Vec::new();
let mut chat_author_ids = Vec::new(); let mut chat_author_ids = Vec::new();
// get all messages // get all messages
let messages: Vec<std::result::Result<Message, Error>> = let messages: Vec<std::result::Result<Message, Error>> =
get_chat_msgs(context, chat_id, 0, None) get_chat_msgs(context, chat_id, 0, None)
.await
.into_iter() .into_iter()
.map(|msg_id| Message::load_from_db(context, msg_id)) .map(async move |msg_id| Message::load_from_db(context, msg_id).await)
.collect(); .collect();
// push all referenced blobs and populate contactid list // push all referenced blobs and populate contactid list
for message in &messages { for message in &messages {
@@ -82,22 +119,22 @@ pub fn export_chat(context: &Context, chat_id: ChatId) -> ExportChatResult {
} }
// deduplicate contact list and load the contacts // deduplicate contact list and load the contacts
chat_author_ids.dedup(); chat_author_ids.dedup();
// chache information about the authors // load information about the authors
let mut chat_authors: HashMap<u32, ContactInfo> = HashMap::new(); let mut chat_authors: HashMap<u32, ContactJSON> = HashMap::new();
chat_authors.insert( chat_authors.insert(
0, 0,
ContactInfo { ContactJSON {
name: "Err: Contact not found".to_owned(), name: "Err: Contact not found".to_owned(),
initial: "#".to_owned(), email: "error@localhost".to_owned(),
profile_img: None, profile_img: None,
color: "grey".to_owned(), color: "grey".to_owned(),
}, },
); );
for author_id in chat_author_ids { for author_id in chat_author_ids {
let contact = Contact::get_by_id(context, author_id); let contact = Contact::get_by_id(context, author_id).await;
if let Ok(c) = contact { if let Ok(c) = contact {
let profile_img_path: String; let profile_img_path: String;
if let Some(path) = c.get_profile_image(context) { if let Some(path) = c.get_profile_image(context).await {
profile_img_path = path profile_img_path = path
.file_name() .file_name()
.unwrap_or_else(|| std::ffi::OsStr::new("")) .unwrap_or_else(|| std::ffi::OsStr::new(""))
@@ -111,43 +148,22 @@ pub fn export_chat(context: &Context, chat_id: ChatId) -> ExportChatResult {
} }
chat_authors.insert( chat_authors.insert(
author_id, author_id,
ContactInfo { ContactJSON {
name: c.get_display_name().to_owned(), name: c.get_display_name().to_owned(),
initial: "#".to_owned(), // TODO email: c.get_addr().to_owned(),
profile_img: match profile_img_path != "" { profile_img: match profile_img_path != "" {
true => Some(profile_img_path), true => Some(profile_img_path),
false => None, false => None,
}, },
color: "rgb(18, 126, 208)".to_owned(), // TODO color: format!("{:#}", c.get_color()), // TODO
}, },
); );
} }
} }
// run message_to_html for each message and generate the html that way // Load information about the chat
let mut html_messages: Vec<String> = Vec::new(); let chat: Chat = Chat::load_from_db(context, chat_id).await.unwrap();
for message in messages { let chat_avatar = match chat.get_profile_image(context).await {
if let Ok(msg) = message {
html_messages.push(message_to_html(&chat_authors, msg, context));
} else {
html_messages.push(format!(
r#"<li>
<div class='message error'>
<div class="msg-container">
<div class="msg-body">
<div dir="auto" class="text">{:?}</div>
</div>
</div>
</div>
</li>"#,
message.unwrap_err()
));
}
}
// todo chat image, chat name and so on..
let chat = Chat::load_from_db(context, chat_id).unwrap();
let chat_avatar = match chat.get_profile_image(context) {
Some(img) => { Some(img) => {
let path = img let path = img
.file_name() .file_name()
@@ -156,190 +172,166 @@ pub fn export_chat(context: &Context, chat_id: ChatId) -> ExportChatResult {
.unwrap() .unwrap()
.to_owned(); .to_owned();
blobs.push(path.clone()); blobs.push(path.clone());
format!("<img class=\"avatar\" src=\"blobs/{}\" />", path) Some(format!("blobs/{}", path))
} }
None => format!( None => None,
"<div class=\"avatar text-avatar\" style=\"background-color:#{:#}\">{}</div>",
chat.get_color(context),
chat.get_name().chars().next().unwrap()
),
}; };
// todo option to export locations as kml? let chat_json = ChatJSON {
name: chat.get_name(),
// todo export message infos and save them to txt files color: format!("{:#}", chat.get_color()),
// (those can be linked from the messages, they are stored in msg_info/[msg-id].txt) profile_img: chat_avatar,
contacts: chat_authors,
messages: vec![], //todo
};
blobs.dedup(); blobs.dedup();
ExportChatResult { ExportChatResult {
html: format!( chat_json: serde_json::to_string(&chat_json).unwrap(),
"<html>\
<head>\
<title>{chat_name}</title>\
<link rel=\"stylesheet\" href=\"styles.css\" type=\"text/css\">\
</head>\
<body>\
<div class=\"header\">\
{chat_avatar}\
<div class=\"name\">{chat_name}</div>\
</div>\
<div class=\"message-list-and-composer__message-list\">\
<div id=\"message-list\">\
<ul>{messages}</ul>\
</div>\
</div>\
</body>\
</html>",
chat_name = chat.get_name(),
chat_avatar = chat_avatar,
messages = html_messages.join("")
),
referenced_blobs: blobs, referenced_blobs: blobs,
} }
} }
fn message_to_html( // fn message_to_html(
author_cache: &HashMap<u32, ContactInfo>, // author_cache: &HashMap<u32, ContactInfo>,
message: Message, // message: Message,
context: &Context, // context: &Context,
) -> String { // ) -> String {
let author: &ContactInfo = { // let author: &ContactInfo = {
if let Some(c) = author_cache.get(&message.get_from_id()) { // if let Some(c) = author_cache.get(&message.get_from_id()) {
c // c
} else { // } else {
author_cache.get(&0).unwrap() // author_cache.get(&0).unwrap()
} // }
}; // };
let avatar: String = { // let avatar: String = {
if let Some(profile_img) = &author.profile_img { // if let Some(profile_img) = &author.profile_img {
format!( // format!(
"<div class=\"author-avatar\">\ // "<div class=\"author-avatar\">\
<img \ // <img \
alt=\"{author_name}\"\ // alt=\"{author_name}\"\
src=\"blobs/{author_avatar_src}\"\ // src=\"blobs/{author_avatar_src}\"\
/>\ // />\
</div>", // </div>",
author_name = author.name, // author_name = author.name,
author_avatar_src = profile_img // author_avatar_src = profile_img
) // )
} else { // } else {
format!( // format!(
"<div class=\"author-avatar default\" alt=\"{name}\">\ // "<div class=\"author-avatar default\" alt=\"{name}\">\
<div class=\"label\" style=\"background-color: {color}\">\ // <div class=\"label\" style=\"background-color: {color}\">\
{initial}\ // {initial}\
</div>\ // </div>\
</div>", // </div>",
name = author.name, // name = author.name,
initial = author.initial, // initial = author.initial,
color = author.color // color = author.color
) // )
} // }
}; // };
// save and refernce message source code somehow? // // save and refernce message source code somehow?
let has_text = message.get_text().is_some() && !message.get_text().unwrap().is_empty(); // let has_text = message.get_text().is_some() && !message.get_text().unwrap().is_empty();
let attachment = match message.get_file(context) { // let attachment = match message.get_file(context) {
None => "".to_owned(), // None => "".to_owned(),
Some(file) => { // Some(file) => {
let modifier_class = if has_text { "content-below" } else { "" }; // let modifier_class = if has_text { "content-below" } else { "" };
let filename = file // let filename = file
.file_name() // .file_name()
.unwrap_or_else(|| std::ffi::OsStr::new("")) // .unwrap_or_else(|| std::ffi::OsStr::new(""))
.to_str() // .to_str()
.unwrap() // .unwrap()
.to_owned(); // .to_owned();
match message.get_viewtype() { // match message.get_viewtype() {
Viewtype::Audio => { // Viewtype::Audio => {
format!("<audio \ // format!("<audio \
controls \ // controls \
class=\"message-attachment-audio {}\"> \ // class=\"message-attachment-audio {}\"> \
<source src=\"blobs/{}\" /> \ // <source src=\"blobs/{}\" /> \
</audio>", modifier_class ,filename) // </audio>", modifier_class ,filename)
}, // },
Viewtype::Gif | Viewtype::Image | Viewtype::Sticker => { // Viewtype::Gif | Viewtype::Image | Viewtype::Sticker => {
format!("<a \ // format!("<a \
href=\"blobs/{filename}\" \ // href=\"blobs/{filename}\" \
role=\"button\" \ // role=\"button\" \
class=\"message-attachment-media {modifier_class}\"> \ // class=\"message-attachment-media {modifier_class}\"> \
<img className='attachment-content' src=\"blobs/{filename}\" /> \ // <img className='attachment-content' src=\"blobs/{filename}\" /> \
</a>", modifier_class=modifier_class, filename=filename) // </a>", modifier_class=modifier_class, filename=filename)
}, // },
Viewtype::Video => { // Viewtype::Video => {
format!("<a \ // format!("<a \
href=\"blobs/{filename}\" \ // href=\"blobs/{filename}\" \
role=\"button\" \ // role=\"button\" \
class=\"message-attachment-media {modifier_class}\"> \ // class=\"message-attachment-media {modifier_class}\"> \
<video className='attachment-content' src=\"blobs/{filename}\" controls=\"true\" /> \ // <video className='attachment-content' src=\"blobs/{filename}\" controls=\"true\" /> \
</a>", modifier_class=modifier_class, filename=filename) // </a>", modifier_class=modifier_class, filename=filename)
}, // },
_ => { // _ => {
format!("<div class=\"message-attachment-generic {modifier_class}\">\ // format!("<div class=\"message-attachment-generic {modifier_class}\">\
<div class=\"file-icon\">\ // <div class=\"file-icon\">\
<div class=\"file-extension\">\ // <div class=\"file-extension\">\
{extension} \ // {extension} \
</div>\ // </div>\
</div>\ // </div>\
<div className=\"text-part\">\ // <div className=\"text-part\">\
<a href=\"blobs/{filename}\" className=\"name\">{filename}</a>\ // <a href=\"blobs/{filename}\" className=\"name\">{filename}</a>\
<div className=\"size\">{filesize}</div>\ // <div className=\"size\">{filesize}</div>\
</div>\ // </div>\
</div>", // </div>",
modifier_class=modifier_class, // modifier_class=modifier_class,
filename=filename, // filename=filename,
filesize=message.get_filebytes(&context) /* todo human readable file size*/, // filesize=message.get_filebytes(&context) /* todo human readable file size*/,
extension=file.extension().unwrap_or_else(|| std::ffi::OsStr::new("")).to_str().unwrap().to_owned()) // extension=file.extension().unwrap_or_else(|| std::ffi::OsStr::new("")).to_str().unwrap().to_owned())
} // }
} // }
} // }
}; // };
format!( // format!(
"<li>\ // "<li>\
<div class=\"message {direction}\">\ // <div class=\"message {direction}\">\
{avatar}\ // {avatar}\
<div class=\"msg-container\">\ // <div class=\"msg-container\">\
<span class=\"author\" style=\"color: {author_color};\">{author_name}</span>\ // <span class=\"author\" style=\"color: {author_color};\">{author_name}</span>\
<div class=\"msg-body\">\ // <div class=\"msg-body\">\
{attachment} // {attachment}
<div dir=\"auto\" class=\"text\">\ // <div dir=\"auto\" class=\"text\">\
{content}\ // {content}\
</div>\ // </div>\
<div class=\"metadata {with_image_no_caption}\">\ // <div class=\"metadata {with_image_no_caption}\">\
{encryption}\ // {encryption}\
<span class=\"date date--{direction}\" title=\"{full_time}\">{relative_time}</span>\ // <span class=\"date date--{direction}\" title=\"{full_time}\">{relative_time}</span>\
<span class=\"spacer\"></span>\ // <span class=\"spacer\"></span>\
</div>\ // </div>\
</div>\ // </div>\
</div>\ // </div>\
<div>\ // <div>\
</li>", // </li>",
direction = match message.from_id == DC_CONTACT_ID_SELF { // direction = match message.from_id == DC_CONTACT_ID_SELF {
true => "outgoing", // true => "outgoing",
false => "incoming", // false => "incoming",
}, // },
avatar = avatar, // avatar = avatar,
author_name = author.name, // author_name = author.name,
author_color = author.color, // author_color = author.color,
attachment = attachment, // attachment = attachment,
content = message.get_text().unwrap_or_else(|| "".to_owned()), // content = message.get_text().unwrap_or_else(|| "".to_owned()),
with_image_no_caption = if !has_text && message.get_viewtype() == Viewtype::Image { // with_image_no_caption = if !has_text && message.get_viewtype() == Viewtype::Image {
"with-image-no-caption" // "with-image-no-caption"
} else { // } else {
"" // ""
}, // },
encryption = match message.get_showpadlock() { // encryption = match message.get_showpadlock() {
true => r#"<div aria-label="Encryption padlock" class="padlock-icon"></div>"#, // true => r#"<div aria-label="Encryption padlock" class="padlock-icon"></div>"#,
false => "", // false => "",
}, // },
full_time = "Tue, Feb 25, 2020 3:49 PM", // message.get_timestamp() ? // todo // full_time = "Tue, Feb 25, 2020 3:49 PM", // message.get_timestamp() ? // todo
relative_time = "Tue 3:49 PM" // todo // relative_time = "Tue 3:49 PM" // todo
) // )
// todo link to raw message data // // todo link to raw message data
// todo link to message info // // todo link to message info
} // }
//TODO tests