export_template.html (13830B) - raw


      1 <style>
      2     :root {
      3         --Snap-sigIconPrimary: #dedede;
      4         --Snap-sigIconSecondary: #999;
      5         --Snap-sigIconTertiary: #616161;
      6         --Snap-sigIconNegative: #f23c57;
      7         --Snap-sigTextPrimary: #dedede;
      8         --Snap-sigTextPrimaryInverse: #000;
      9         --Snap-sigTextSecondary: #999;
     10         --Snap-sigTextTertiary: #616161;
     11         --Snap-sigTextPlayer: #fff;
     12         --Snap-sigTextNegative: #f23c57;
     13         --Snap-sigColorBackgroundBorder: rgba(255, 255, 255, 0.1);
     14         --Snap-sigBackgroundPrimary: #121212;
     15         --Snap-sigBackgroundPrimaryInverse: #fff;
     16         --Snap-sigBackgroundSecondary: #1e1e1e;
     17         --Snap-sigBackgroundSecondaryHover: #2b2b2b;
     18         --Snap-sigBackgroundFeedHover: rgba(255, 255, 255, 0.1);
     19         --Snap-sigBackgroundMessageHover: #292929;
     20         --Snap-sigBackgroundMessageSaved: #333232;
     21         --Snap-sigBackgroundMessageSavedHover: #3a3a3a;
     22         --Snap-sigMediaControlContainerBackground: rgba(255, 255, 255, 0.1);
     23         --Snap-sigStartupFooterBackground: rgba(0, 0, 0, 0.05);
     24         --Snap-sigButtonPrimary: #0fadff;
     25         --Snap-sigButtonPrimaryHover: #42bfff;
     26         --Snap-sigButtonSecondary: #2b2b2b;
     27         --Snap-sigButtonSecondaryHover: #424242;
     28         --Snap-sigButtonSecondaryActive: #5c5c5c;
     29         --Snap-sigButtonTertiary: #4e565f;
     30         --Snap-sigButtonQuaternary: #fff;
     31         --Snap-sigButtonInactive: #1e1e1e;
     32         --Snap-sigButtonNegative: #e1143d;
     33         --Snap-sigButtonOnPrimary: #fff;
     34         --Snap-sigButtonOnSecondary: #dedede;
     35         --Snap-sigButtonOnTertiary: #fff;
     36         --Snap-sigButtonOnQuaternary: #1e1e1e;
     37         --Snap-sigButtonOnInactive: rgba(255, 255, 255, 0.3);
     38         --Snap-sigButtonOnNegative: #fff;
     39         --Snap-sigMain: #121212;
     40         --Snap-sigSubscreen: #121212;
     41         --Snap-sigOverlay: rgba(0, 0, 0, 0.4);
     42         --Snap-sigOverlayHover: rgba(0, 0, 0, 0.35);
     43         --Snap-sigSurface: #1e1e1e;
     44         --Snap-sigSurfaceRGB: 30, 30, 30;
     45         --Snap-sigSurfaceDown: #212121;
     46         --Snap-sigAboveSurface: #292929;
     47         --Snap-sigObject: rgba(255, 255, 255, 0.1);
     48         --Snap-sigObjectDown: rgba(255, 255, 255, 0.19);
     49         --Snap-sigConversationBoxBackground: rgba(255, 255, 255, 0.25);
     50         --Snap-sigDivider: rgba(255, 255, 255, 0.1);
     51         --Snap-sigDividerLight: rgba(255, 255, 255, 0.2);
     52         --Snap-sigPlaceholder: #1e1e1e;
     53         --Snap-sigDisabled: rgba(255, 255, 255, 0.1);
     54         --Snap-sigCallTileHighlight: rgba(255, 255, 255, 0.8);
     55         --Snap-sigChat: #0fadff;
     56         --Snap-sigSnapWithoutSound: #f23c57;
     57         --Snap-sigSnapWithSound: #a05dcd;
     58         --Snap-sigChatSurfaceCalling: #39ca8e;
     59         --Snap-sigChatSurfaceCallingDisabled: #105e3d;
     60         --Snap-sigChatPending: #767676;
     61         --Snap-sigChatPendingHover: #8f8f8f;
     62         --Snap-sigChatIcon: #0fadff;
     63         --Snap-sigChatIconCaret: #f8616d;
     64         --Snap-sigChatShadowOne: 0 0 17px rgba(33, 33, 33, 0.07), 0 0 22px rgba(0, 0, 0, 0.06), 0 0 8px rgba(84, 84, 84, 0.1);
     65         --Snap-selectedMiddleColorGradient: rgba(4, 4, 4, 0.1);
     66         --Snap-selectedRightColorGradient: rgba(4, 4, 4, 0);
     67         --Border: 1px solid var(--Snap-sigColorBackgroundBorder);
     68     }
     69 
     70     body {
     71         font-family: 'Avenir Next', sans-serif;
     72         color: var(--Snap-sigTextPrimary);
     73         background-color: var(--Snap-sigBackgroundPrimary);
     74         margin: 0;
     75         padding: 0;
     76         display: flex;
     77         flex-direction: column;
     78         align-items: center;
     79         justify-content: flex-start;
     80     }
     81 
     82     header {
     83         width: 100%;
     84         padding: 10px 0px;
     85         display: flex;
     86         flex-direction: column;
     87         align-items: center;
     88         justify-content: flex-start;
     89     }
     90 
     91     header .title {
     92         background-color: var(--Snap-sigButtonSecondary);
     93         height: 40px;
     94         font-weight: 600;
     95         padding-inline-end: 16px;
     96         border-radius: 999px;
     97         line-height: 40px;
     98         padding: 0 20px;
     99     }
    100 
    101     main {
    102         background-color: var(--Snap-sigBackgroundSecondary);
    103         border: var(--Border);
    104         border-radius: 12px;
    105         width: calc(100% - 30px);
    106         display: flex;
    107         flex-direction: column;
    108     }
    109 
    110     main>.message {
    111         display: flex;
    112         flex-direction: column;
    113         align-items: stretch;
    114         justify-content: flex-start;
    115         flex-wrap: nowrap;
    116         margin: 5px 15px;
    117     }
    118 
    119     main>.message .header {
    120         width: 100%;
    121         display: flex;
    122         vertical-align: top;
    123         align-self: flex-start;
    124         flex-direction: row;
    125         flex-wrap: nowrap;
    126         justify-content: space-between;
    127         align-items: center;
    128     }
    129 
    130     main>.message:nth-child(2n) .username {
    131         color: #dcedc1;
    132     }
    133 
    134     main>.message:nth-child(2n + 1) .username {
    135         color: #ffd3b6;
    136     }
    137 
    138     main>.message .username {
    139         font-weight: bold;
    140     }
    141 
    142     main>.message .time {
    143         color: var(--Snap-sigTextSecondary);
    144         font-size: 12px;
    145         font-weight: 600;
    146     }
    147 
    148     main>.message:nth-child(2n) .content {
    149         border-color: #dcedc1;
    150     }
    151 
    152     main>.message:nth-child(2n + 1) .content {
    153         border-color: #ffd3b6;
    154     }
    155 
    156     main>.message .content {
    157         background-color: var(--Snap-sigBackgroundMessageSaved);
    158         border-left: 3px solid;
    159         border-radius: 3px;
    160         margin-top: 4px;
    161         padding-left: 4px;
    162         padding: 3px 0 3px 6px;
    163     }
    164 
    165     main>.message .content div:has(.chat_media:not(audio):not(.overlay_media)) {
    166         display: inline-block;
    167         resize: horizontal;
    168         overflow: hidden;
    169         line-height: 0;
    170         height: auto;
    171         width: 300px;
    172     }
    173 
    174     main>.message .chat_media:not(audio):not(.overlay_media) {
    175         width: 100%;
    176         height: auto;
    177     }
    178 
    179     @-moz-document url-prefix() {
    180         main>.message .content div {
    181             display: inline-block;
    182             resize: horizontal;
    183             overflow: hidden;
    184             line-height: 0;
    185             height: auto;
    186             width: 300px;
    187         }
    188 
    189         main>.message .chat_media:not(.overlay_media) {
    190             width: 100%;
    191             height: auto;
    192         }
    193     }
    194 
    195 
    196     main>.message .overlay_media {
    197         width: inherit;
    198         height: inherit;
    199         position: absolute;
    200         pointer-events: none;
    201     }
    202 
    203     main>.message .red_snap_svg {
    204         color: var(--Snap-sigSnapWithoutSound);
    205     }
    206 </style>
    207 <body>
    208 <header>
    209     <div class="title"></div>
    210     <div>
    211         <label>
    212             <input type="checkbox" class="sort_by_date" onchange="makeMain();"> Sort by date
    213         </label>
    214     </div>
    215 </header>
    216 
    217 <main></main>
    218 
    219 <div style="display: none;">
    220     <svg class="red_snap_svg" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
    221         <rect x="4" y="5" width="10.5" height="10.5" rx="1.808" stroke="currentColor" stroke-width="1.5"></rect>
    222     </svg>
    223 </div>
    224 
    225 <script>
    226     function base64decode(data) {
    227         return new Uint8Array(atob(data).split('').map(c => c.charCodeAt(0)))
    228     }
    229 
    230     const conversationData = JSON.parse(new TextDecoder().decode(new Uint8Array(inflate(base64decode(document.querySelector(".exported_content").innerHTML)))))
    231     const participants = Object.values(conversationData.participants)
    232 
    233     function makeHeader() {
    234         const conversationTitle = conversationData.conversationName != null ? conversationData.conversationName : "DM with " + Object.values(participants).map(user => user.username).join(", ")
    235         document.querySelector("header > .title").textContent = conversationTitle
    236         document.title = conversationTitle
    237     }
    238 
    239     function decodeMedia(element) {
    240         try {
    241             const decodedData = new Uint8Array(
    242                 inflate(
    243                     base64decode(
    244                         element.innerHTML.substring(5, element.innerHTML.length - 4)
    245                     )
    246                 )
    247             )
    248             return URL.createObjectURL(new Blob([decodedData]))
    249         } catch (e) {
    250             return null
    251         }
    252     }
    253 
    254     function makeMain() {
    255         document.querySelector('main').innerHTML = ""
    256         const messageTemplate = document.querySelector("#message_template")
    257         let messageList = Object.values(conversationData.messages)
    258 
    259         if (document.querySelector(".sort_by_date").checked) {
    260             messageList = messageList.reverse()
    261         }
    262 
    263         messageList.forEach(message => {
    264             const messageObject = document.createElement("div")
    265             messageObject.classList.add("message")
    266 
    267             messageObject.appendChild(((headerElement) => {
    268                 headerElement.classList.add("header")
    269 
    270                 headerElement.appendChild(((elem) => {
    271                     elem.classList.add("username")
    272                     const participant = participants[message.senderId]
    273                     elem.innerHTML = (participant == null) ? "Unknown user" : participant.username
    274                     return elem
    275                 })(document.createElement("div")))
    276 
    277 
    278                 headerElement.appendChild(((elem) => {
    279                     elem.classList.add("time")
    280                     elem.innerHTML = new Date(message.createdTimestamp).toUTCString()
    281                     return elem
    282                 })(document.createElement("div")))
    283 
    284                 return headerElement
    285             })(document.createElement("div")))
    286 
    287             messageObject.appendChild(((messageContainer) => {
    288                 messageContainer.classList.add("content")
    289 
    290                 const observers = []
    291 
    292                 function loadContent() {
    293                     if (!message.serializedContent) {
    294                         let messageData = ""
    295                         switch (message.type) {
    296                             case "SNAP":
    297                                 messageContainer.appendChild(document.querySelector('.red_snap_svg').cloneNode(true))
    298                                 messageData += "Snap"
    299                                 break
    300                             default:
    301                                 messageData += message.type
    302                         }
    303                         messageContainer.innerHTML = messageData
    304                         messageContainer.onclick = () => {
    305                             messageContainer.prepend(document.createElement("br"))
    306                             observers.forEach(f => f())
    307                         }
    308                     } else {
    309                         messageContainer.innerHTML = message.serializedContent
    310                     }
    311                 }
    312 
    313                 loadContent()
    314 
    315                 if (message.attachments && message.attachments.length > 0) {
    316                     message.attachments.reverse().forEach((attachment, index) => {
    317                         const mediaKey = attachment.key.replace(/(=)/g, "")
    318 
    319                         observers.push(() => {
    320                             messageContainer.onclick = () => {}
    321                             const originalMedia = document.querySelector('.media-ORIGINAL_' + mediaKey)
    322                             if (!originalMedia) {
    323                                 return
    324                             }
    325 
    326                             const originalMediaUrl = decodeMedia(originalMedia)
    327 
    328                             const mediaContainer = document.createElement("div")
    329                             messageContainer.prepend(mediaContainer)
    330 
    331                             const imageTag = document.createElement("img")
    332                             imageTag.src = originalMediaUrl
    333                             imageTag.classList.add("chat_media")
    334                             mediaContainer.appendChild(imageTag)
    335 
    336                             imageTag.onerror = () => {
    337                                 mediaContainer.removeChild(imageTag)
    338                                 const mediaTag = document.createElement(message.type === "NOTE" ? "audio" : "video")
    339                                 mediaTag.classList.add("chat_media")
    340                                 mediaTag.src = originalMediaUrl
    341                                 mediaTag.preload = "metadata"
    342                                 mediaTag.controls = true
    343                                 mediaContainer.appendChild(mediaTag)
    344                             }
    345 
    346                             const overlay = document.querySelector('.media-OVERLAY_' + mediaKey)
    347                             if (!overlay) {
    348                                 return
    349                             }
    350 
    351                             const overlayImage = document.createElement("img")
    352                             overlayImage.src = decodeMedia(overlay)
    353                             overlayImage.classList.add("chat_media")
    354                             overlayImage.classList.add("overlay_media")
    355                             mediaContainer.appendChild(overlayImage)
    356                         })
    357                     })
    358 
    359                     let fetched = false
    360 
    361                     new IntersectionObserver(entries => {
    362                         if (!fetched && entries[0].isIntersecting === true) {
    363                             fetched = true
    364                             loadContent()
    365                             messageContainer.prepend(document.createElement("br"))
    366                             observers.forEach(c => {
    367                                 try {
    368                                     c()
    369                                 } catch (e) {
    370                                     console.log(e)
    371                                 }
    372                             })
    373                         }
    374                     }).observe(messageContainer)
    375                 }
    376 
    377                 return messageContainer
    378             })(document.createElement("div")))
    379 
    380             document.querySelector('main').appendChild(messageObject)
    381         })
    382     }
    383 
    384     makeHeader()
    385     makeMain()
    386 </script>
    387 </body>