JFIFxxC      C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3RbrPK‹Z XIaachatify/composer.jsonnu[{ "name": "munafio/chatify", "description": "A package for Laravel PHP Framework to add a complete real-time chat system.", "type": "library", "keywords": [ "laravel", "messenger", "conversations", "chat", "php", "pusher", "realtime", "real-time", "chatify" ], "license": "MIT", "authors": [ { "name": "Munaf A. Mahdi", "email": "munafaqeelmahdi@gmail.com" } ], "homepage": "https://github.com/munafio/chatify", "minimum-stability": "dev", "prefer-stable": true, "require": { "pusher/pusher-php-server": "^7.0" }, "autoload": { "psr-4": { "Chatify\\": "src/" } }, "extra": { "laravel": { "providers": [ "Chatify\\ChatifyServiceProvider" ], "aliases": { "Chatify": "Chatify\\Facades\\ChatifyMessenger" } } } } PK‹Z0hchatify/UPGRADE.mdnu[# Upgrade Guide With every upgrade, make sure to re-publish Chatify's assets: ## For v1.2.3 and earlier versions ``` php artisan verndor:publish --tag=chatify-views --force php artisan verndor:publish --tag=chatify-assets --force ``` If needed, you can re-publish the other assets the same way above by just replacing the name of the asset (chatify-NAME). ## For v1.2.4+ and higher vertions To re-publish only `views` & `assets`: ``` php artisan chatify:publish ``` To re-publish all the assets (views, assets, config..): ``` php artisan chatify:publish --force ``` > This will overwrite all the assets, so all your changes will be overwritten. PK‹Z64  "chatify/src/assets/imgs/avatar.pngnu[PNG  IHDRasBITOPLTEֿվսԼӺһӹҷѸѶдϵϴβͳα:ҲnBtRNSD pHYs  ~tEXtSoftwareAdobe Fireworks CS6輲tEXtCreation Time4/28/15;%HIDATxgWJP,[9 [JK=Xdwγp25~Rmtڭm6:~:zܮ9O9϶ӻjxwJ&*עWHJ?很dx5=,ŲuF$럃xnJ?q*s8^,ʇO^W#8^ ;b5/qŤ=2y9yNC)EQPq 7(WMؙ4x'V9w}v7OZ,{7|;g%CD̴x,]ݜLC 2MQߴnTcӲB;g޼N_QkʿY77NVW) HC$ԗoיy;naѲ5?W^pt7N[moF%XIK^v?3, ~mwo[9ޮonݺnhcש7s[њt[o'cĈO@QߋD" v^c(׽1XyA"uVUn1._`K!_Z:tr4n[o!=pyp;pG*v&[Տv{Etn$ݢ~l|txɶanMB.>nLk k? L|_)xnGm;QW!e2b X@,C !e2b X@,C !e2b X t;_ËzIENDB`PK‹Z (* %chatify/src/assets/css/light.mode.cssnu[/*app scroll*/ .app-scroll::-webkit-scrollbar-thumb, .app-scroll-thin::-webkit-scrollbar-thumb { background: #d8dce0; } .app-scroll-thin::-webkit-scrollbar { background: #eee; } .app-scroll::-webkit-scrollbar:hover, .app-scroll-thin::-webkit-scrollbar:hover { background: #eee; } .messenger { background: #fff; } .messenger-search[type="text"] { background: #f7f7f7; color: #333; } .messenger-listView { background: #fff; border: 1px solid #eee; } .messenger-listView-tabs { border-bottom: 1px solid #eee; } .messenger-listView-tabs a:hover, .messenger-listView-tabs a:focus { background-color: #f7f7f7; } .messenger-favorites div.avatar { border: 2px solid #fff; } .messenger-list-item:hover { background: #f7f7f7; } .messenger-messagingView { border-top: 1px solid #eee; border-bottom: 1px solid #eee; background: #fafaff; } .m-header-messaging { background: #fff; } .messenger-infoView { background: #fff; border: 1px solid #eee; } .messenger-infoView > p { color: #000; } .divider { border-top: 1px solid #e0e0e0; } .messenger-sendCard { background: #fff; border-top: 1px solid #eee; } .attachment-preview > p { color: #333; } .m-send { color: #333; } .message-card p { background: #ffffff; color: #656b75; box-shadow: 0px 6px 11px rgba(18, 67, 105, 0.03); } .m-li-divider { border-bottom: 1px solid #ebebeb; } .m-header a, .m-header a:hover, .m-header a:focus { text-decoration: none; color: #606679; } .messenger-list-item td p { color: #606679; } .activeStatus { border: 2px solid #fff; } .messenger-list-item:hover .activeStatus { border-color: #f7f7f7; } .messenger-favorites > div p { color: #4a4a4a; } .avatar { background-color: #eee; } /* *********************************************** * Placeholder loading *********************************************** */ .loadingPlaceholder-body div, .loadingPlaceholder-header tr td div { background: #f6f7f8; background-image: -webkit-linear-gradient( left, #f6f7f8 0%, #eaecf3 20%, #f6f7f8 40%, #f6f7f8 100% ); } /* *********************************************** * App Modal *********************************************** */ .app-modal-card { background: rgba(255, 255, 255, 0.89); } .app-modal-header { color: #000; } .app-modal-body { color: #000; } /* ***************************************** * Responsive Design ***************************************** */ @media (max-width: 1060px) { .messenger-infoView { box-shadow: 0px 0px 20px rgba(18, 67, 105, 0.06); } } @media (max-width: 980px) { .messenger-listView { box-shadow: 0px 0px 20px rgba(18, 67, 105, 0.06); } } PK‹Z: $chatify/src/assets/css/dark.mode.cssnu[/*app scroll*/ .app-scroll::-webkit-scrollbar-thumb, .app-scroll-thin::-webkit-scrollbar-thumb { background: #323232; } .app-scroll-thin::-webkit-scrollbar { background: #2c2c2c; } .app-scroll::-webkit-scrollbar:hover, .app-scroll-thin::-webkit-scrollbar:hover { background: #2c2c2c; } .messenger { background: #272727; } .messenger-search[type="text"] { background: #323232; color: #fff; } .messenger-listView { background: #272727; border: 1px solid #323232; } .messenger-listView-tabs { border-bottom: 1px solid #323232; } .messenger-listView-tabs a:hover, .messenger-listView-tabs a:focus { background-color: #323232; } .messenger-favorites div.avatar { border: 2px solid #272727; } .messenger-list-item:hover { background: #323232; } .messenger-messagingView { border-top: 1px solid #323232; border-bottom: 1px solid #323232; background: #272727; } .m-header-messaging { background: #323232; } .messenger-infoView { background: #272727; border: 1px solid #323232; } .messenger-infoView > p { color: #fff; } .divider { border-top: 1px solid #2f2f2f; } .messenger-sendCard { background: #323232; border-top: 1px solid #272727; } .attachment-preview > p { color: #fff; } .m-send { color: #fff; } .message-card p { background: #323232; color: #fff; } .m-li-divider { border-bottom: 1px solid #323232; } .m-header a, .m-header a:hover, .m-header a:focus { text-decoration: none; color: #fff; } .messenger-list-item td p { color: #fff; } .activeStatus { border: 2px solid #272727; } .messenger-list-item:hover .activeStatus { border-color: #323232; } .messenger-favorites > div p { color: #ffffff; } .avatar { background-color: #323232; } /* *********************************************** * Placeholder loading *********************************************** */ .loadingPlaceholder-body div, .loadingPlaceholder-header tr td div { background: #323232; background-image: -webkit-linear-gradient( left, #323232 0%, #323232 20%, #323232 40%, #323232 100% ); } /* *********************************************** * App Modal *********************************************** */ .app-modal-card { background: rgba(50, 50, 50, 0.94); } .app-modal-header { color: #fff; } .app-modal-body { color: #fff; } .messages sub { color: #fff; } .chatify-hover-delete-btn { color: #ffffff; } PK‹ZcBB chatify/src/assets/css/style.cssnu[body { margin: 0; } /*internet connection*/ .internet-connection { display: none; background: rgba(0, 0, 0, 0.76); position: absolute; top: 43px; left: 0; right: 0; text-align: center; padding: 4px; color: #fff; } .internet-connection span { display: none; } /*green background RGBA*/ .successBG-rgba { background: rgba(54, 180, 36, 0.76) !important; } /* app scroll*/ .app-scroll::-webkit-scrollbar { width: 5px; height: 5px; border-radius: 4px; background: transparent; transition: all 0.3s ease; } .app-scroll-thin::-webkit-scrollbar { width: 1px; height: 1px; border-radius: 4px; transition: all 0.3s ease; } .app-scroll::-webkit-scrollbar-thumb, .app-scroll-thin::-webkit-scrollbar-thumb { border-radius: 4px; } .messenger { display: inline-flex; width: 100%; height: 100%; font-family: sans-serif; } .messenger-listView { position: relative; top: 0px; left: 0px; right: 0px; z-index: 1; background: transparent; width: 45%; min-width: 200px; overflow: auto; } .messenger-listView .m-header { position: absolute; top: 0; left: 0; right: 0; padding: 0px !important; } .messenger-listView .m-header > nav { margin: 10px; } .messenger-listView .m-body { margin-top: 141px; } .messenger-messagingView { position: relative; overflow: hidden; width: 100%; } .messenger-messagingView .m-body { padding-top: 15px; height: calc(100% - 90px); overflow-x: hidden; overflow-y: auto; } .m-header { padding: 10px 15px; font-weight: 600; background: transparent; } .m-header-right { display: inline-flex; float: right; } .m-header-messaging { background: #fff; box-shadow: 0px 5px 6px rgba(0, 0, 0, 0.06); } .m-header svg { margin: 0px 8px; font-size: 16px; transition: transform 0.12s; } .m-header svg:active { transform: scale(0.9); } .messenger-search[type="text"] { margin: 0px 10px; width: calc(100% - 20px); border: none; padding: 8px 10px; border-radius: 6px; outline: none; } .messenger-listView-tabs { display: inline-flex; width: 100%; margin-top: 10px; background-color: transparent; box-shadow: 0px 5px 6px rgba(0, 0, 0, 0.06); } .messenger-listView-tabs a { width: 100%; text-align: center; padding: 10px; text-decoration: none; background-color: transparent; transition: background 0.3s; } .messenger-listView-tabs a:hover, .messenger-listView-tabs a:focus { text-decoration: none; } .messenger-tab { overflow: auto; height: calc(100vh - 143px); display: none; position: relative; } .add-to-favorite { display: none; } .add-to-favorite svg { color: rgba(180, 180, 180, 0.52) !important; } .favorite-added svg { color: #ffc107 !important; } .favorite svg { color: #ffc107 !important; } .show { display: block; } .hide { display: none; } .messenger-list-item { margin: 0; width: 100%; cursor: pointer; transition: background 0.1s; } .m-list-active span, .m-list-active p { color: #fff !important; } .messenger-list-item td { padding: 10px; } .messenger-list-item tr > td:first-child { padding-right: 0; width: 55px; } .messenger-list-item td p { margin-bottom: 4px; font-size: 14px; font-weight: 600; } .messenger-list-item td p span { float: right; } .messenger-list-item td span { color: #cacaca; font-weight: 400; font-size: 13px; } .messenger-list-item td b { float: right; color: #fff; padding: 0px 4px; border-radius: 20px; font-size: 13px; width: 20px; height: auto; text-align: center; } .avatar { text-align: center; border-radius: 100%; overflow: hidden; background-image: url(""); background-repeat: no-repeat; background-size: cover; background-position: center center; } .av-l { width: 100px; height: 100px; } .av-m { width: 45px; height: 45px; } .av-s { width: 32px !important; height: 32px !important; } .messenger-favorites { padding: 10px; overflow: auto; white-space: nowrap; } .messenger-favorites > div { display: inline-block; text-align: center; transition: transform 0.3s; cursor: pointer; } .messenger-favorites > div p { font-size: 12px; margin: 8px 0px; margin-bottom: 0px; } .messenger-favorites div.avatar { border: 2px solid #fff; margin: 0px 4px; } .messenger-favorites > div:active { transform: scale(0.9); } .messenger-title { margin: 0; padding: 10px; padding-bottom: 0px; text-transform: uppercase; color: #aeaeb7; font-size: 12px; font-weight: 600; } .messenger-infoView { display: block; padding-top: 7px; overflow: auto; width: 40%; min-width: 200px; } .messenger-infoView nav a { text-decoration: none; padding: 5px 8px; font-size: 18px; } .messenger-infoView > div { margin: auto; margin-top: 8%; text-align: center; } .messenger-infoView > p { text-align: center; margin: auto; margin-top: 15px; font-size: 18px; font-weight: 600; } .messenger-infoView-btns a { display: block; text-decoration: none !important; padding: 5px 10px; margin: 0% 10%; border-radius: 3px; font-size: 14px; transition: background 0.3s; } .messenger-infoView-btns a.default:hover { background: #f0f6ff; } .messenger-infoView-btns a.danger { color: #ff5555; } .messenger-infoView-btns a.danger:hover { background: rgba(255, 85, 85, 0.11); } .shared-photo { border-radius: 3px; background: #f7f7f7; height: 120px; overflow: hidden; display: inline-block; margin: 0px 1px; width: calc(50% - 12px); background-position: center center; background-size: cover; background-repeat: no-repeat; cursor: pointer; } .shared-photo img { width: auto; height: 100%; } .messenger-infoView-shared { display: none; } .messenger-infoView-shared .messenger-title { padding-bottom: 10px; } .messenger-infoView-btns .delete-conversation { display: none; } .message-card { display: flex; flex-direction: column; margin: 2px 15px; width: calc(100% - 30px); } .message-card div { margin-top: 0px; } .message-card p { margin: 0; padding: 6px 15px; padding-bottom: 5px; max-width: 80%; width: fit-content; width: -webkit-fit-content; border-radius: 20px; word-break: break-word; } .message-card p sub { display: inline-block; font-size: 11px; } .message-card p sub:before { content: ""; background: transparent; width: 10px; height: 10px; display: inline-block; } .mc-sender { direction: rtl; } .mc-sender p { direction: ltr; color: #fff !important; } .mc-sender p sub { color: rgba(255, 255, 255, 0.67); } .mc-error p { background: rgba(255, 0, 0, 0.27) !important; color: #ff0000 !important; } .mc-error p sub { color: #ff0000 !important; } .listView-x, .show-listView { display: none; } .messenger-sendCard { display: none; position: absolute; bottom: 0; left: 0; width: 100%; } .messenger-sendCard form { width: 100%; display: inline-flex; margin: 0; } .messenger-sendCard input[type="file"] { display: none; } .messenger-sendCard button, .messenger-sendCard button:active, .messenger-sendCard button:focus { border: none; outline: none; background: none; padding: 0; margin: 0; } .messenger-sendCard label { margin: 0; } .messenger-sendCard svg { margin: 9px 10px; color: #bdcbd6; cursor: pointer; font-size: 21px; transition: transform 0.15s; } .messenger-sendCard svg:active { transform: scale(0.9); } .m-send { font-size: 14px; width: 100%; border: none; padding: 10px; outline: none; resize: none; background: transparent; font-family: sans-serif; height: 44px; max-height: 200px; } .attachment-preview { position: relative; padding: 10px; } .attachment-preview > p { margin: 0; font-weight: 600; padding: 0px; padding-top: 10px; } .attachment-preview > p > svg { font-size: 16px; margin: 0; margin-bottom: -1px; color: #737373; } .attachment-preview svg:active { transform: none; } .image-file { cursor: pointer; width: 140px; height: 70px; border-radius: 4px; background-color: #f7f7f7; background-size: cover; background-repeat: no-repeat; background-position: center center; } .attachment-preview > svg:first-child { position: absolute; background: rgba(0, 0, 0, 0.33); width: 20px; height: 20px; padding: 3px; border-radius: 100%; font-size: 16px; margin: 0; top: 10px; color: #fff; } #message-form > button { height: 40px; } .file-download { font-size: 12px; display: block; color: #fff; text-decoration: none; font-weight: 600; border: 1px solid rgba(0, 0, 0, 0.08); background: rgba(0, 0, 0, 0.03); padding: 2px 8px; margin-top: 10px; border-radius: 20px; transition: transform 0.3s, background 0.3s; } .file-download:hover, .file-download:focus { color: #fff; text-decoration: none; background: rgba(0, 0, 0, 0.08); } .file-download:active { transform: scale(0.95); } .typing-indicator { display: none; } .messages { padding: 5px 0px; } .message-hint { margin: 0; text-align: center; } .center-el { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); } .message-hint span { background: rgba(154, 161, 170, 0.13); padding: 3px 10px; border-radius: 20px; color: rgb(135, 147, 164); display: inline-block; } .upload-avatar-details { font-size: 14px; color: #949ba5; display: none; } .upload-avatar-preview { position: relative; border: 1px solid #e0e0e0; margin: 20px auto; } .upload-avatar-loading { position: absolute; top: calc(50% - 21px); margin: 0; left: calc(50% - 20px); } .divider { margin: 15px; } .update-messengerColor { margin: 1rem 0rem; } .update-messengerColor .color-btn { width: 30px; height: 30px; border-radius: 20px; display: inline-block; cursor: pointer; } .m-color-active { border: 3px solid rgba(255, 255, 255, 0.5); } .update-messengerColor .color-btn { transition: transform 0.15s, border 0.15s; } .update-messengerColor .color-btn:active { transform: scale(0.9); } .dark-mode-switch { margin: 0px 5px; cursor: pointer; } .activeStatus { width: 12px; height: 12px; background: #4caf50; border-radius: 20px; position: absolute; bottom: 12%; right: 6%; transition: border 0.1s; } /* *********************************************** * App Buttons *********************************************** */ .app-btn { cursor: pointer; border: none; padding: 3px 15px; border-radius: 20px; margin: 1px; font-size: 14px; display: inline-block; outline: none; text-decoration: none; transition: all 0.3s; color: rgb(33, 128, 243); } .app-btn:hover, .app-btn:focus { color: rgb(33, 128, 243); outline: none; text-decoration: none; } .app-btn:active { transform: scale(0.9); } .a-btn-light { background: #f1f1f1; color: #333; } .a-btn-light:hover, .a-btn-light:focus { color: #333; background: #e4e4e4; } .a-btn-primary { background: #0976d6; color: #fff; } .a-btn-primary:hover, .a-btn-primary:focus { background: #0085ef; color: #fff; } .a-btn-warning { background: #ffc107; color: #fff; } .a-btn-warning:hover, .a-btn-warning:focus { background: #ffa726; color: #fff; } .a-btn-success { background: #3fc380 !important; color: #fff; } .a-btn-success:hover, .a-btn-success:focus { background: #2ecc71 !important; color: #fff; } .a-btn-danger { background: #ea1909 !important; color: #fff; } .a-btn-danger:hover, .a-btn-danger:focus { color: #fff; background: #b70d00 !important; } .btn-disabled { opacity: 0.5; } /* *********************************************** * App Modal *********************************************** */ .app-modal { display: none; position: fixed; top: 0; bottom: 0; right: 0; left: 0; background: rgba(0, 0, 0, 0.53); z-index: 2; } .app-modal-container { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); } .app-modal-card { width: auto; max-width: 400px; margin: auto; padding: 20px 40px; border-radius: 5px; text-align: center; box-shadow: 0px 3px 15px rgba(0, 0, 0, 0.27); transform: scale(0); } .app-modal-header { font-weight: 500; } .app-modal-footer { margin-top: 10px; } .app-show-modal { transform: scale(1); animation: show_modal 0.15s; } /* modal animation */ @keyframes show_modal { from { transform: scale(0); } to { transform: scale(1); } } /* *********************************************** * Placeholder loading *********************************************** */ .loadingPlaceholder-wrapper { position: relative; } .loadingPlaceholder-body div, .loadingPlaceholder-header tr td div { background-repeat: no-repeat; background-size: 800px 104px; height: 104px; position: relative; } .loadingPlaceholder-body div { position: absolute; right: 0px; left: 0px; top: 0px; } div.loadingPlaceholder-avatar { height: 45px !important; width: 45px; margin: 10px; border-radius: 60px; } div.loadingPlaceholder-name { height: 15px !important; margin-bottom: 10px; width: 150px; border-radius: 2px; } div.loadingPlaceholder-date { height: 10px !important; width: 106px; border-radius: 2px; } /* *********************************************** * Image modal box *********************************************** */ .imageModal { display: none; position: fixed; z-index: 1; padding-top: 100px; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgb(0, 0, 0); background-color: rgba(0, 0, 0, 0.9); } .imageModal-content { margin: auto; display: block; width: 80%; max-width: 700px; } .imageModal-content { -webkit-animation-name: zoom; -webkit-animation-duration: 0.15s; animation-name: zoom; animation-duration: 0.15s; } @-webkit-keyframes zoom { from { -webkit-transform: scale(0); } to { -webkit-transform: scale(1); } } @keyframes zoom { from { transform: scale(0); } to { transform: scale(1); } } .imageModal-close { position: absolute; top: 15px; right: 35px; color: #f1f1f1; font-size: 40px; font-weight: bold; transition: 0.3s; } .imageModal-close:hover, .imageModal-close:focus { color: #bbb; text-decoration: none; cursor: pointer; } /* *********************************************** * Typing (jumping) dots animation and style *********************************************** */ .dot { width: 8px; height: 8px; background: #bcc1c6; display: inline-block; border-radius: 50%; right: 0px; bottom: 0px; position: relative; animation: jump 1s infinite; } .typing-dots .dot-1 { -webkit-animation-delay: 100ms; animation-delay: 100ms; } .typing-dots .dot-2 { -webkit-animation-delay: 200ms; animation-delay: 200ms; } .typing-dots .dot-3 { -webkit-animation-delay: 300ms; animation-delay: 300ms; } @keyframes jump { 0% { bottom: 0px; } 20% { bottom: 5px; } 40% { bottom: 0px; } } /* ***************************************** * Responsive Design ***************************************** */ @media (max-width: 1060px) { .messenger-infoView { position: fixed; right: 0; top: 0; bottom: 0; max-width: 334px; } } @media (max-width: 980px) { .messenger-listView { position: fixed; left: 0; top: 0; bottom: 0; max-width: 334px; } .listView-x { display: block; } .show-listView { display: inline-block; } } @media (max-width: 680px) { .messenger-messagingView { position: fixed; top: 0; left: 0; height: 100%; } .messenger-infoView { display: none; width: 100%; max-width: unset; } .messenger-listView { width: 100%; max-width: unset; } .listView-x { display: none; } .app-modal-container { transform: unset; } .app-modal-card { max-width: unset; position: fixed; left: 0; right: 0; top: 0; bottom: 0; width: 100%; height: 100%; border-radius: 0px; } } @media (min-width: 680px) { .messenger-listView { display: unset; } } @media only screen and (max-width: 700px) { .imageModal-content { width: 100%; } } @media (max-width: 576px) { .user-name { max-width: 150px; white-space: nowrap; overflow: hidden !important; text-overflow: ellipsis; } .chatify-md-block { display: block; } } .chatify-d-flex { display: flex !important; } .chatify-d-none { display: none !important; } .chatify-d-hidden { visibility: hidden !important; } .chatify-justify-content-between { justify-content: space-between !important; } .chatify-align-items-center { align-items: center !important; } .chat-message-wrapper { display: flex; flex-direction: column; align-items: end; unicode-bidi: bidi-override; direction: ltr; } .pb-3 { padding-bottom: 0.75rem; /* 12px */ } .mb-2 { margin-bottom: 0.5rem; /* 8px */ } .messenger textarea:focus { outline: none; border: none; box-shadow: none; } .chatify-hover-delete-btn { display: none; cursor: pointer; color: #333333; } .message-card:hover .chatify-hover-delete-btn { display: block; } PK‹ZSwDwD)chatify/src/assets/js/font.awesome.min.jsnu[/*! * Font Awesome Free 5.10.2 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) */ !function(){"use strict";var c={},l={};try{"undefined"!=typeof window&&(c=window),"undefined"!=typeof document&&(l=document)}catch(c){}var h=(c.navigator||{}).userAgent,z=void 0===h?"":h,v=c,a=l,m=(v.document,!!a.documentElement&&!!a.head&&"function"==typeof a.addEventListener&&a.createElement,~z.indexOf("MSIE")||z.indexOf("Trident/"),"___FONT_AWESOME___"),s=function(){try{return!0}catch(c){return!1}}();var e=v||{};e[m]||(e[m]={}),e[m].styles||(e[m].styles={}),e[m].hooks||(e[m].hooks={}),e[m].shims||(e[m].shims=[]);var t=e[m];function M(c,z){var l=(2>>0;h--;)l[h]=c[h];return l}function gc(c){return c.classList?bc(c.classList):(c.getAttribute("class")||"").split(" ").filter(function(c){return c})}function Sc(c,l){var h,z=l.split("-"),v=z[0],a=z.slice(1).join("-");return v!==c||""===a||(h=a,~T.indexOf(h))?null:a}function yc(c){return"".concat(c).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function wc(h){return Object.keys(h||{}).reduce(function(c,l){return c+"".concat(l,": ").concat(h[l],";")},"")}function Ac(c){return c.size!==Lc.size||c.x!==Lc.x||c.y!==Lc.y||c.rotate!==Lc.rotate||c.flipX||c.flipY}function kc(c){var l=c.transform,h=c.containerWidth,z=c.iconWidth,v={transform:"translate(".concat(h/2," 256)")},a="translate(".concat(32*l.x,", ").concat(32*l.y,") "),m="scale(".concat(l.size/16*(l.flipX?-1:1),", ").concat(l.size/16*(l.flipY?-1:1),") "),s="rotate(".concat(l.rotate," 0 0)");return{outer:v,inner:{transform:"".concat(a," ").concat(m," ").concat(s)},path:{transform:"translate(".concat(z/2*-1," -256)")}}}var xc={x:0,y:0,width:"100%",height:"100%"};function qc(c){var l=!(1").concat(m.map(Jc).join(""),"")}var $c=function(){};function cl(c){return"string"==typeof(c.getAttribute?c.getAttribute(Z):null)}var ll={replace:function(c){var l=c[0],h=c[1].map(function(c){return Jc(c)}).join("\n");if(l.parentNode&&l.outerHTML)l.outerHTML=h+(G.keepOriginalSource&&"svg"!==l.tagName.toLowerCase()?"\x3c!-- ".concat(l.outerHTML," --\x3e"):"");else if(l.parentNode){var z=document.createElement("span");l.parentNode.replaceChild(z,l),z.outerHTML=h}},nest:function(c){var l=c[0],h=c[1];if(~gc(l).indexOf(G.replacementClass))return ll.replace(c);var z=new RegExp("".concat(G.familyPrefix,"-.*"));delete h[0].attributes.style;var v=h[0].attributes.class.split(" ").reduce(function(c,l){return l===G.replacementClass||l.match(z)?c.toSvg.push(l):c.toNode.push(l),c},{toNode:[],toSvg:[]});h[0].attributes.class=v.toSvg.join(" ");var a=h.map(function(c){return Jc(c)}).join("\n");l.setAttribute("class",v.toNode.join(" ")),l.setAttribute(Z,""),l.innerHTML=a}};function hl(c){c()}function zl(h,c){var z="function"==typeof c?c:$c;if(0===h.length)z();else{var l=hl;G.mutateApproach===w&&(l=o.requestAnimationFrame||hl),l(function(){var c=!0===G.autoReplaceSvg?ll.replace:ll[G.autoReplaceSvg]||ll.replace,l=Rc.begin("mutate");h.map(c),l(),z()})}}var vl=!1;function al(){vl=!1}var ml=null;function sl(c){if(t&&G.observeMutations){var v=c.treeCallback,a=c.nodeCallback,m=c.pseudoElementsCallback,l=c.observeMutationsRoot,h=void 0===l?V:l;ml=new t(function(c){vl||bc(c).forEach(function(c){if("childList"===c.type&&0 $("meta[name=id]").attr("content"); const getMessengerType = () => $("meta[name=type]").attr("content"); const setMessengerId = (id) => $("meta[name=id]").attr("content", id); const setMessengerType = (type) => $("meta[name=type]").attr("content", type); /** *------------------------------------------------------------- * Re-usable methods *------------------------------------------------------------- */ const escapeHtml = (unsafe) => { return unsafe .replace(/&/g, "&") .replace(//g, ">"); }; function actionOnScroll(selector, callback, topScroll = false) { $(selector).on("scroll", function () { let element = $(this).get(0); const condition = topScroll ? element.scrollTop == 0 : element.scrollTop + element.clientHeight >= element.scrollHeight; if (condition) { callback(); } }); } function routerPush(title, url) { $("meta[name=url]").attr("content", url); return window.history.pushState({}, title || document.title, url); } function updateSelectedContact(user_id) { $(document).find(".messenger-list-item").removeClass("m-list-active"); $(document) .find( ".messenger-list-item[data-contact=" + (user_id || getMessengerId()) + "]" ) .addClass("m-list-active"); } /** *------------------------------------------------------------- * Global Templates *------------------------------------------------------------- */ // Loading svg function loadingSVG(size = "25px", className = "", style = "") { return ` `; } function loadingWithContainer(className) { return `
${loadingSVG( "25px", "", "margin:auto" )}
`; } // loading placeholder for users list item function listItemLoading(items) { let template = ""; for (let i = 0; i < items; i++) { template += `
`; } return template; } // loading placeholder for avatars function avatarLoading(items) { let template = ""; for (let i = 0; i < items; i++) { template += `
`; } return template; } // While sending a message, show this temporary message card. function sendTempMessageCard(message, id) { return `

${message}

`; } // upload image preview card. function attachmentTemplate(fileType, fileName, imgURL = null) { if (fileType != "image") { return ( `

` + escapeHtml(fileName) + `

` ); } else { return ( `

` + escapeHtml(fileName) + `

` ); } } // Active Status Circle function activeStatusCircle() { return ``; } /** *------------------------------------------------------------- * Css Media Queries [For responsive design] *------------------------------------------------------------- */ $(window).resize(function () { cssMediaQueries(); }); function cssMediaQueries() { if (window.matchMedia("(min-width: 980px)").matches) { $(".messenger-listView").removeAttr("style"); } if (window.matchMedia("(max-width: 980px)").matches) { $("body") .find(".messenger-list-item") .find("tr[data-action]") .attr("data-action", "1"); $("body").find(".favorite-list-item").find("div").attr("data-action", "1"); } else { $("body") .find(".messenger-list-item") .find("tr[data-action]") .attr("data-action", "0"); $("body").find(".favorite-list-item").find("div").attr("data-action", "0"); } } /** *------------------------------------------------------------- * App Modal *------------------------------------------------------------- */ let app_modal = function ({ show = true, name, data = 0, buttons = true, header = null, body = null, }) { const modal = $(".app-modal[data-name=" + name + "]"); // header header ? modal.find(".app-modal-header").html(header) : ""; // body body ? modal.find(".app-modal-body").html(body) : ""; // buttons buttons == true ? modal.find(".app-modal-footer").show() : modal.find(".app-modal-footer").hide(); // show / hide if (show == true) { modal.show(); $(".app-modal-card[data-name=" + name + "]").addClass("app-show-modal"); $(".app-modal-card[data-name=" + name + "]").attr("data-modal", data); } else { modal.hide(); $(".app-modal-card[data-name=" + name + "]").removeClass("app-show-modal"); $(".app-modal-card[data-name=" + name + "]").attr("data-modal", data); } }; /** *------------------------------------------------------------- * Slide to bottom on [action] - e.g. [message received, sent, loaded] *------------------------------------------------------------- */ function scrollToBottom(container) { $(container) .stop() .animate({ scrollTop: $(container)[0].scrollHeight, }); } /** *------------------------------------------------------------- * click and drag to scroll - function *------------------------------------------------------------- */ function hScroller(scroller) { const slider = document.querySelector(scroller); let isDown = false; let startX; let scrollLeft; slider.addEventListener("mousedown", (e) => { isDown = true; startX = e.pageX - slider.offsetLeft; scrollLeft = slider.scrollLeft; }); slider.addEventListener("mouseleave", () => { isDown = false; }); slider.addEventListener("mouseup", () => { isDown = false; }); slider.addEventListener("mousemove", (e) => { if (!isDown) return; e.preventDefault(); const x = e.pageX - slider.offsetLeft; const walk = (x - startX) * 1; slider.scrollLeft = scrollLeft - walk; }); } /** *------------------------------------------------------------- * Disable/enable message form fields, messaging container... * on load info or if needed elsewhere. * * Default : true *------------------------------------------------------------- */ function disableOnLoad(action = true) { if (action == true) { // hide star button $(".add-to-favorite").hide(); // hide send card $(".messenger-sendCard").hide(); // add loading opacity to messages container messagesContainer.css("opacity", ".5"); // disable message form fields messageInput.attr("readonly", "readonly"); $("#message-form button").attr("disabled", "disabled"); $(".upload-attachment").attr("disabled", "disabled"); } else { // show star button if (getMessengerId() != auth_id) { $(".add-to-favorite").show(); } // show send card $(".messenger-sendCard").show(); // remove loading opacity to messages container messagesContainer.css("opacity", "1"); // enable message form fields messageInput.removeAttr("readonly"); $("#message-form button").removeAttr("disabled"); $(".upload-attachment").removeAttr("disabled"); } } /** *------------------------------------------------------------- * Error message card *------------------------------------------------------------- */ function errorMessageCard(id) { messagesContainer .find(".message-card[data-id=" + id + "]") .addClass("mc-error"); messagesContainer .find(".message-card[data-id=" + id + "]") .find("svg.loadingSVG") .remove(); messagesContainer .find(".message-card[data-id=" + id + "] p") .prepend(''); } /** *------------------------------------------------------------- * Fetch id data (user/group) and update the view *------------------------------------------------------------- */ function IDinfo(id, type) { // clear temporary message id temporaryMsgId = 0; // clear typing now typingNow = 0; // show loading bar NProgress.start(); // disable message form disableOnLoad(); if (messenger != 0) { // get shared photos getSharedPhotos(id); // Get info $.ajax({ url: url + "/idInfo", method: "POST", data: { _token: access_token, id, type }, dataType: "JSON", success: (data) => { // avatar photo $(".messenger-infoView") .find(".avatar") .css("background-image", 'url("' + data.user_avatar + '")'); $(".header-avatar").css( "background-image", 'url("' + data.user_avatar + '")' ); // Show shared and actions $(".messenger-infoView-btns .delete-conversation").show(); $(".messenger-infoView-shared").show(); // fetch messages fetchMessages(id, type, true); // focus on messaging input messageInput.focus(); // update info in view $(".messenger-infoView .info-name").html(data.fetch.name); $(".m-header-messaging .user-name").html(data.fetch.name); // Star status data.favorite > 0 ? $(".add-to-favorite").addClass("favorite") : $(".add-to-favorite").removeClass("favorite"); // form reset and focus $("#message-form").trigger("reset"); cancelAttachment(); messageInput.focus(); }, error: () => { console.error("Error, check server response!"); // remove loading bar NProgress.done(); NProgress.remove(); }, }); } else { // remove loading bar NProgress.done(); NProgress.remove(); } } /** *------------------------------------------------------------- * Send message function *------------------------------------------------------------- */ function sendMessage() { temporaryMsgId += 1; let tempID = `temp_${temporaryMsgId}`; let hasFile = !!$(".upload-attachment").val(); const inputValue = $.trim(messageInput.val()); if (inputValue.length > 0 || hasFile) { const formData = new FormData($("#message-form")[0]); formData.append("id", getMessengerId()); formData.append("type", getMessengerType()); formData.append("temporaryMsgId", tempID); formData.append("_token", access_token); $.ajax({ url: $("#message-form").attr("action"), method: "POST", data: formData, dataType: "JSON", processData: false, contentType: false, beforeSend: () => { // remove message hint $(".messages").find(".message-hint").remove(); // append a temporary message card if (hasFile) { messagesContainer .find(".messages") .append( sendTempMessageCard( inputValue + "\n" + loadingSVG("28px"), tempID ) ); } else { messagesContainer .find(".messages") .append(sendTempMessageCard(inputValue, tempID)); } // scroll to bottom scrollToBottom(messagesContainer); messageInput.css({ height: "42px" }); // form reset and focus $("#message-form").trigger("reset"); cancelAttachment(); messageInput.focus(); }, success: (data) => { if (data.error > 0) { // message card error status errorMessageCard(tempID); console.error(data.error_msg); } else { // update contact item updateContactItem(getMessengerId()); // temporary message card const tempMsgCardElement = messagesContainer.find( `.message-card[data-id=${data.tempID}]` ); // add the message card coming from the server before the temp-card tempMsgCardElement.before(data.message); // then, remove the temporary message card tempMsgCardElement.remove(); // scroll to bottom scrollToBottom(messagesContainer); // send contact item updates sendContactItemUpdates(true); } }, error: () => { // message card error status errorMessageCard(tempID); // error log console.error( "Failed sending the message! Please, check your server response." ); }, }); } return false; } /** *------------------------------------------------------------- * Fetch messages from database *------------------------------------------------------------- */ let messagesPage = 1; let noMoreMessages = false; let messagesLoading = false; function setMessagesLoading(loading = false) { if (!loading) { messagesContainer.find(".messages").find(".loading-messages").remove(); NProgress.done(); NProgress.remove(); } else { messagesContainer .find(".messages") .prepend(loadingWithContainer("loading-messages")); } messagesLoading = loading; } function fetchMessages(id, type, newFetch = false) { if (newFetch) { messagesPage = 1; noMoreMessages = false; } if (messenger != 0 && !noMoreMessages && !messagesLoading) { const messagesElement = messagesContainer.find(".messages"); setMessagesLoading(true); $.ajax({ url: url + "/fetchMessages", method: "POST", data: { _token: access_token, id: id, type: type, page: messagesPage, }, dataType: "JSON", success: (data) => { setMessagesLoading(false); if (messagesPage == 1) { messagesElement.html(data.messages); scrollToBottom(messagesContainer); } else { const lastMsg = messagesElement.find( messagesElement.find(".message-card")[0] ); const curOffset = lastMsg.offset().top - messagesContainer.scrollTop(); messagesElement.prepend(data.messages); messagesContainer.scrollTop(lastMsg.offset().top - curOffset); } // trigger seen event makeSeen(true); // Pagination lock & messages page noMoreMessages = messagesPage >= data?.last_page; if (!noMoreMessages) messagesPage += 1; // Enable message form if messenger not = 0; means if data is valid if (messenger != 0) { disableOnLoad(false); } }, error: (error) => { setMessagesLoading(false); console.error(error); }, }); } } /** *------------------------------------------------------------- * Cancel file attached in the message. *------------------------------------------------------------- */ function cancelAttachment() { $(".messenger-sendCard").find(".attachment-preview").remove(); $(".upload-attachment").replaceWith( $(".upload-attachment").val("").clone(true) ); } /** *------------------------------------------------------------- * Cancel updating avatar in settings *------------------------------------------------------------- */ function cancelUpdatingAvatar() { $(".upload-avatar-preview").css("background-image", defaultAvatarInSettings); $(".upload-avatar").replaceWith($(".upload-avatar").val("").clone(true)); } /** *------------------------------------------------------------- * Pusher channels and event listening.. *------------------------------------------------------------- */ // subscribe to the channel const channelName = "private-chatify"; var channel = pusher.subscribe(`${channelName}.${auth_id}`); var clientSendChannel; var clientListenChannel; function initClientChannel() { if (getMessengerId()) { clientSendChannel = pusher.subscribe(`${channelName}.${getMessengerId()}`); clientListenChannel = pusher.subscribe(`${channelName}.${auth_id}`); } } initClientChannel(); // Listen to messages, and append if data received channel.bind("messaging", function (data) { if (data.from_id == getMessengerId() && data.to_id == auth_id) { $(".messages").find(".message-hint").remove(); messagesContainer.find(".messages").append(data.message); scrollToBottom(messagesContainer); makeSeen(true); // remove unseen counter for the user from the contacts list $(".messenger-list-item[data-contact=" + getMessengerId() + "]") .find("tr>td>b") .remove(); } }); // listen to typing indicator clientListenChannel.bind("client-typing", function (data) { if (data.from_id == getMessengerId() && data.to_id == auth_id) { data.typing == true ? messagesContainer.find(".typing-indicator").show() : messagesContainer.find(".typing-indicator").hide(); } // scroll to bottom scrollToBottom(messagesContainer); }); // listen to seen event clientListenChannel.bind("client-seen", function (data) { if (data.from_id == getMessengerId() && data.to_id == auth_id) { if (data.seen == true) { $(".message-time") .find(".fa-check") .before(' '); $(".message-time").find(".fa-check").remove(); } } }); // listen to contact item updates event clientListenChannel.bind("client-contactItem", function (data) { if (data.update_for == auth_id) { data.updating == true ? updateContactItem(data.update_to) : console.error("[Contact Item updates] Updating failed!"); } }); // listen on message delete event clientListenChannel.bind("client-messageDelete", function (data) { $("body").find(`.message-card[data-id=${data.id}]`).remove(); }); // ------------------------------------- // presence channel [User Active Status] var activeStatusChannel = pusher.subscribe("presence-activeStatus"); // Joined activeStatusChannel.bind("pusher:member_added", function (member) { setActiveStatus(1, member.id); $(".messenger-list-item[data-contact=" + member.id + "]") .find(".activeStatus") .remove(); $(".messenger-list-item[data-contact=" + member.id + "]") .find(".avatar") .before(activeStatusCircle()); }); // Leaved activeStatusChannel.bind("pusher:member_removed", function (member) { setActiveStatus(0, member.id); $(".messenger-list-item[data-contact=" + member.id + "]") .find(".activeStatus") .remove(); }); function handleVisibilityChange() { if (!document.hidden) { makeSeen(true); } } document.addEventListener("visibilitychange", handleVisibilityChange, false); /** *------------------------------------------------------------- * Trigger typing event *------------------------------------------------------------- */ function isTyping(status) { return clientSendChannel.trigger("client-typing", { from_id: auth_id, // Me to_id: getMessengerId(), // Messenger typing: status, }); } /** *------------------------------------------------------------- * Trigger seen event *------------------------------------------------------------- */ function makeSeen(status) { if (document?.hidden) { return; } // remove unseen counter for the user from the contacts list $(".messenger-list-item[data-contact=" + getMessengerId() + "]") .find("tr>td>b") .remove(); // seen $.ajax({ url: url + "/makeSeen", method: "POST", data: { _token: access_token, id: getMessengerId() }, dataType: "JSON", }); return clientSendChannel.trigger("client-seen", { from_id: auth_id, // Me to_id: getMessengerId(), // Messenger seen: status, }); } /** *------------------------------------------------------------- * Trigger contact item updates *------------------------------------------------------------- */ function sendContactItemUpdates(status) { return clientSendChannel.trigger("client-contactItem", { update_for: getMessengerId(), // Messenger update_to: auth_id, // Me updating: status, }); } /** *------------------------------------------------------------- * Trigger message delete *------------------------------------------------------------- */ function sendMessageDeleteEvent(messageId) { return clientChannel.trigger("client-messageDelete", { id: messageId, }); } /** *------------------------------------------------------------- * Check internet connection using pusher states *------------------------------------------------------------- */ function checkInternet(state, selector) { let net_errs = 0; const messengerTitle = $(".messenger-headTitle"); switch (state) { case "connected": if (net_errs < 1) { messengerTitle.text(messengerTitleDefault); selector.addClass("successBG-rgba"); selector.find("span").hide(); selector.slideDown("fast", function () { selector.find(".ic-connected").show(); }); setTimeout(function () { $(".internet-connection").slideUp("fast"); }, 3000); } break; case "connecting": messengerTitle.text($(".ic-connecting").text()); selector.removeClass("successBG-rgba"); selector.find("span").hide(); selector.slideDown("fast", function () { selector.find(".ic-connecting").show(); }); net_errs = 1; break; // Not connected default: messengerTitle.text($(".ic-noInternet").text()); selector.removeClass("successBG-rgba"); selector.find("span").hide(); selector.slideDown("fast", function () { selector.find(".ic-noInternet").show(); }); net_errs = 1; break; } } /** *------------------------------------------------------------- * Get contacts *------------------------------------------------------------- */ let contactsPage = 1; let contactsLoading = false; let noMoreContacts = false; function setContactsLoading(loading = false) { if (!loading) { $(".listOfContacts").find(".loading-contacts").remove(); } else { $(".listOfContacts").append( `
${listItemLoading(4)}
` ); } contactsLoading = loading; } function getContacts() { if (!contactsLoading && !noMoreContacts) { setContactsLoading(true); $.ajax({ url: url + "/getContacts", method: "GET", data: { _token: access_token, page: contactsPage }, dataType: "JSON", success: (data) => { setContactsLoading(false); if (contactsPage < 2) { $(".listOfContacts").html(data.contacts); } else { $(".listOfContacts").append(data.contacts); } updateSelectedContact(); // update data-action required with [responsive design] cssMediaQueries(); // Pagination lock & messages page noMoreContacts = contactsPage >= data?.last_page; if (!noMoreContacts) contactsPage += 1; }, error: (error) => { setContactsLoading(false); console.error(error); }, }); } } /** *------------------------------------------------------------- * Update contact item *------------------------------------------------------------- */ function updateContactItem(user_id) { if (user_id != auth_id) { let listItem = $("body") .find(".listOfContacts") .find(".messenger-list-item[data-contact=" + user_id + "]"); $.ajax({ url: url + "/updateContacts", method: "POST", data: { _token: access_token, user_id, }, dataType: "JSON", success: (data) => { const totalContacts = $(".listOfContacts").find(".messenger-list-item")?.length || 0; if (totalContacts < 1) $(".listOfContacts").find(".message-hint").remove(); listItem.remove(); $(".listOfContacts").prepend(data.contactItem); // update data-action required with [responsive design] cssMediaQueries(); updateSelectedContact(user_id); }, error: () => { console.error("Server error, check your response"); }, }); } } /** *------------------------------------------------------------- * Star *------------------------------------------------------------- */ function star(user_id) { if (getMessengerId() != auth_id) { $.ajax({ url: url + "/star", method: "POST", data: { _token: access_token, user_id: user_id }, dataType: "JSON", success: (data) => { data.status > 0 ? $(".add-to-favorite").addClass("favorite") : $(".add-to-favorite").removeClass("favorite"); }, error: () => { console.error("Server error, check your response"); }, }); } } /** *------------------------------------------------------------- * Get favorite list *------------------------------------------------------------- */ function getFavoritesList() { $(".messenger-favorites").html(avatarLoading(4)); $.ajax({ url: url + "/favorites", method: "POST", data: { _token: access_token }, dataType: "JSON", success: (data) => { if (data.count > 0) { $(".favorites-section").show(); $(".messenger-favorites").html(data.favorites); } else { $(".favorites-section").hide(); } // update data-action required with [responsive design] cssMediaQueries(); }, error: () => { console.error("Server error, check your response"); }, }); } /** *------------------------------------------------------------- * Get shared photos *------------------------------------------------------------- */ function getSharedPhotos(user_id) { $.ajax({ url: url + "/shared", method: "POST", data: { _token: access_token, user_id: user_id }, dataType: "JSON", success: (data) => { $(".shared-photos-list").html(data.shared); }, error: () => { console.error("Server error, check your response"); }, }); } /** *------------------------------------------------------------- * Search in messenger *------------------------------------------------------------- */ let searchPage = 1; let noMoreDataSearch = false; let searchLoading = false; let searchTempVal = ""; function setSearchLoading(loading = false) { if (!loading) { $(".search-records").find(".loading-search").remove(); } else { $(".search-records").append( `` ); } searchLoading = loading; } function messengerSearch(input) { if (input != searchTempVal) { searchPage = 1; noMoreDataSearch = false; searchLoading = false; } searchTempVal = input; if (!searchLoading && !noMoreDataSearch) { if (searchPage < 2) { $(".search-records").html(""); } setSearchLoading(true); $.ajax({ url: url + "/search", method: "GET", data: { _token: access_token, input: input, page: searchPage }, dataType: "JSON", success: (data) => { setSearchLoading(false); if (searchPage < 2) { $(".search-records").html(data.records); } else { $(".search-records").append(data.records); } // update data-action required with [responsive design] cssMediaQueries(); // Pagination lock & messages page noMoreDataSearch = searchPage >= data?.last_page; if (!noMoreDataSearch) searchPage += 1; }, error: (error) => { setSearchLoading(false); console.error(error); }, }); } } /** *------------------------------------------------------------- * Delete Conversation *------------------------------------------------------------- */ function deleteConversation(id) { $.ajax({ url: url + "/deleteConversation", method: "POST", data: { _token: access_token, id: id }, dataType: "JSON", beforeSend: () => { // hide delete modal app_modal({ show: false, name: "delete", }); // Show waiting alert modal app_modal({ show: true, name: "alert", buttons: false, body: loadingSVG("32px", null, "margin:auto"), }); }, success: (data) => { // delete contact from the list $(".listOfContacts") .find(".messenger-list-item[data-contact=" + id + "]") .remove(); // refresh info IDinfo(id, getMessengerType()); if (!data.deleted) console.error("Error occurred, messages can not be deleted!"); // Hide waiting alert modal app_modal({ show: false, name: "alert", buttons: true, body: "", }); }, error: () => { console.error("Server error, check your response"); }, }); } /** *------------------------------------------------------------- * Delete Message By ID *------------------------------------------------------------- */ function deleteMessage(id) { $.ajax({ url: url + "/deleteMessage", method: "POST", data: { _token: access_token, id: id }, dataType: "JSON", beforeSend: () => { // hide delete modal app_modal({ show: false, name: "delete", }); // Show waiting alert modal app_modal({ show: true, name: "alert", buttons: false, body: loadingSVG("32px", null, "margin:auto"), }); }, success: (data) => { $(".messages").find(`.message-card[data-id=${id}]`).remove(); if (!data.deleted) console.error("Error occurred, message can not be deleted!"); sendMessageDeleteEvent(id); // Hide waiting alert modal app_modal({ show: false, name: "alert", buttons: true, body: "", }); }, error: () => { console.error("Server error, check your response"); }, }); } /** *------------------------------------------------------------- * Update Settings *------------------------------------------------------------- */ function updateSettings() { const formData = new FormData($("#update-settings")[0]); if (messengerColor) { formData.append("messengerColor", messengerColor); } if (dark_mode) { formData.append("dark_mode", dark_mode); } $.ajax({ url: url + "/updateSettings", method: "POST", data: formData, dataType: "JSON", processData: false, contentType: false, beforeSend: () => { // close settings modal app_modal({ show: false, name: "settings", }); // Show waiting alert modal app_modal({ show: true, name: "alert", buttons: false, body: loadingSVG("32px", null, "margin:auto"), }); }, success: (data) => { if (data.error) { // Show error message in alert modal app_modal({ show: true, name: "alert", buttons: true, body: data.msg, }); } else { // Hide alert modal app_modal({ show: false, name: "alert", buttons: true, body: "", }); // reload the page location.reload(true); } }, error: () => { console.error("Server error, check your response"); }, }); } /** *------------------------------------------------------------- * Set Active status *------------------------------------------------------------- */ function setActiveStatus(status, user_id) { $.ajax({ url: url + "/setActiveStatus", method: "POST", data: { _token: access_token, user_id: user_id, status: status }, dataType: "JSON", success: (data) => { // Nothing to do }, error: () => { console.error("Server error, check your response"); }, }); } /** *------------------------------------------------------------- * On DOM ready *------------------------------------------------------------- */ $(document).ready(function () { // get contacts list getContacts(); // get contacts list getFavoritesList(); // Clear typing timeout clearTimeout(typingTimeout); // NProgress configurations NProgress.configure({ showSpinner: false, minimum: 0.7, speed: 500 }); // make message input autosize. autosize($(".m-send")); // check if pusher has access to the channel [Internet status] pusher.connection.bind("state_change", function (states) { let selector = $(".internet-connection"); checkInternet(states.current, selector); // listening for pusher:subscription_succeeded channel.bind("pusher:subscription_succeeded", function () { // On connection state change [Updating] and get [info & msgs] if (getMessengerId() != 0) { if ( $(".messenger-list-item") .find("tr[data-action]") .attr("data-action") == "1" ) { $(".messenger-listView").hide(); } IDinfo(getMessengerId(), getMessengerType()); } }); }); // tabs on click, show/hide... $(".messenger-listView-tabs a").on("click", function () { var dataView = $(this).attr("data-view"); $(".messenger-listView-tabs a").removeClass("active-tab"); $(this).addClass("active-tab"); $(".messenger-tab").hide(); $(".messenger-tab[data-view=" + dataView + "]").show(); }); // set item active on click $("body").on("click", ".messenger-list-item", function () { const dataView = $(".messenger-list-item") .find("p[data-type]") .attr("data-type"); $(".messenger-tab").hide(); $(".messenger-tab[data-view=" + dataView + "s]").show(); $(".messenger-list-item").removeClass("m-list-active"); $(this).addClass("m-list-active"); const userID = $(this).attr("data-contact"); routerPush(document.title, `${url}/${userID}`); updateSelectedContact(userID); }); // show info side button $(".messenger-infoView nav a , .show-infoSide").on("click", function () { $(".messenger-infoView").toggle(); }); // make favorites card dragable on click to slide. hScroller(".messenger-favorites"); // click action for list item [user/group] $("body").on("click", ".messenger-list-item", function () { if ($(this).find("tr[data-action]").attr("data-action") == "1") { $(".messenger-listView").hide(); } const dataId = $(this).find("p[data-id]").attr("data-id"); const dataType = $(this).find("p[data-type]").attr("data-type"); setMessengerId(dataId); setMessengerType(dataType); IDinfo(dataId, dataType); }); // click action for favorite button $("body").on("click", ".favorite-list-item", function () { if ($(this).find("div").attr("data-action") == "1") { $(".messenger-listView").hide(); } const uid = $(this).find("div.avatar").attr("data-id"); setMessengerId(uid); setMessengerType("user"); IDinfo(uid, "user"); updateSelectedContact(uid); routerPush(document.title, `${url}/${uid}`); }); // list view buttons $(".listView-x").on("click", function () { $(".messenger-listView").hide(); }); $(".show-listView").on("click", function () { $(".messenger-listView").show(); }); // click action for [add to favorite] button. $(".add-to-favorite").on("click", function () { star(getMessengerId()); }); // calling Css Media Queries cssMediaQueries(); // message form on submit. $("#message-form").on("submit", (e) => { e.preventDefault(); sendMessage(); }); // message input on keyup [Enter to send, Enter+Shift for new line] $("#message-form .m-send").on("keyup", (e) => { // if enter key pressed. if (e.which == 13 || e.keyCode == 13) { // if shift + enter key pressed, do nothing (new line). // if only enter key pressed, send message. if (!e.shiftKey) { triggered = isTyping(false); sendMessage(); } } }); // On [upload attachment] input change, show a preview of the image/file. $("body").on("change", ".upload-attachment", (e) => { let file = e.target.files[0]; if (!attachmentValidate(file)) return false; let reader = new FileReader(); let sendCard = $(".messenger-sendCard"); reader.readAsDataURL(file); reader.addEventListener("loadstart", (e) => { $("#message-form").before(loadingSVG()); }); reader.addEventListener("load", (e) => { $(".messenger-sendCard").find(".loadingSVG").remove(); if (!file.type.match("image.*")) { // if the file not image sendCard.find(".attachment-preview").remove(); // older one sendCard.prepend(attachmentTemplate("file", file.name)); } else { // if the file is an image sendCard.find(".attachment-preview").remove(); // older one sendCard.prepend( attachmentTemplate("image", file.name, e.target.result) ); } }); }); function attachmentValidate(file) { const fileElement = $(".upload-attachment"); const { name: fileName, size: fileSize } = file; const fileExtension = fileName.split(".").pop(); if ( !getAllowedExtensions.includes(fileExtension.toString().toLowerCase()) ) { alert("file type not allowed"); fileElement.val(""); return false; } // Validate file size. if (fileSize > getMaxUploadSize) { alert("File is too large!"); return false; } return true; } // Attachment preview cancel button. $("body").on("click", ".attachment-preview .cancel", () => { cancelAttachment(); }); // typing indicator on [input] keyDown $("#message-form .m-send").on("keydown", () => { if (typingNow < 1) { isTyping(true); typingNow = 1; } clearTimeout(typingTimeout); typingTimeout = setTimeout(function () { isTyping(false); typingNow = 0; }, 1000); }); // Image modal $("body").on("click", ".chat-image", function () { let src = $(this).css("background-image").split(/"/)[1]; $("#imageModalBox").show(); $("#imageModalBoxSrc").attr("src", src); }); $(".imageModal-close").on("click", function () { $("#imageModalBox").hide(); }); // Search input on focus $(".messenger-search").on("focus", function () { $(".messenger-tab").hide(); $('.messenger-tab[data-view="search"]').show(); }); $(".messenger-search").on("blur", function () { setTimeout(function () { $(".messenger-tab").hide(); $('.messenger-tab[data-view="users"]').show(); }, 200); }); // Search action on keyup $(".messenger-search").on("keyup", function (e) { $.trim($(this).val()).length > 0 ? $(".messenger-search").trigger("focus") + messengerSearch($(this).val()) : $(".messenger-tab").hide() + $('.messenger-listView-tabs a[data-view="users"]').trigger("click"); }); // Delete Conversation button $(".messenger-infoView-btns .delete-conversation").on("click", function () { app_modal({ name: "delete", }); }); // Delete Message Button $("body").on("click", ".chatify-hover-delete-btn", function () { app_modal({ name: "delete", data: $(this).data("id"), }); }); // Delete modal [on delete button click] $(".app-modal[data-name=delete]") .find(".app-modal-footer .delete") .on("click", function () { const id = $("body") .find(".app-modal[data-name=delete]") .find(".app-modal-card") .attr("data-modal"); if (id == 0) { deleteConversation(getMessengerId()); } else { deleteMessage(id); } app_modal({ show: false, name: "delete", }); }); // delete modal [cancel button] $(".app-modal[data-name=delete]") .find(".app-modal-footer .cancel") .on("click", function () { app_modal({ show: false, name: "delete", }); }); // Settings button action to show settings modal $("body").on("click", ".settings-btn", function (e) { e.preventDefault(); app_modal({ show: true, name: "settings", }); }); // on submit settings' form $("#update-settings").on("submit", (e) => { e.preventDefault(); updateSettings(); }); // Settings modal [cancel button] $(".app-modal[data-name=settings]") .find(".app-modal-footer .cancel") .on("click", function () { app_modal({ show: false, name: "settings", }); cancelUpdatingAvatar(); }); // upload avatar on change $("body").on("change", ".upload-avatar", (e) => { // store the original avatar if (defaultAvatarInSettings == null) { defaultAvatarInSettings = $(".upload-avatar-preview").css( "background-image" ); } let file = e.target.files[0]; if (!attachmentValidate(file)) return false; let reader = new FileReader(); reader.readAsDataURL(file); reader.addEventListener("loadstart", (e) => { $(".upload-avatar-preview").append( loadingSVG("42px", "upload-avatar-loading") ); }); reader.addEventListener("load", (e) => { $(".upload-avatar-preview").find(".loadingSVG").remove(); if (!file.type.match("image.*")) { // if the file is not an image console.error("File you selected is not an image!"); } else { // if the file is an image $(".upload-avatar-preview").css( "background-image", 'url("' + e.target.result + '")' ); } }); }); // change messenger color button $("body").on("click", ".update-messengerColor .color-btn", function () { messengerColor = $(this).attr("data-color"); $(".update-messengerColor .color-btn").removeClass("m-color-active"); $(this).addClass("m-color-active"); }); // Switch to Dark/Light mode $("body").on("click", ".dark-mode-switch", function () { if ($(this).attr("data-mode") == "0") { $(this).attr("data-mode", "1"); $(this).removeClass("far"); $(this).addClass("fas"); dark_mode = "dark"; } else { $(this).attr("data-mode", "0"); $(this).removeClass("fas"); $(this).addClass("far"); dark_mode = "light"; } }); //Messages pagination actionOnScroll( ".m-body.messages-container", function () { fetchMessages(getMessengerId(), getMessengerType()); }, true ); //Contacts (users) pagination actionOnScroll(".messenger-tab.users-tab", function () { getContacts(); }); //Search pagination actionOnScroll(".messenger-tab.search-tab", function () { messengerSearch($(".messenger-search").val()); }); }); /** *------------------------------------------------------------- * Observer on DOM changes *------------------------------------------------------------- */ let previousMessengerId = getMessengerId(); const observer = new MutationObserver(function (mutations) { if (getMessengerId() !== previousMessengerId) { previousMessengerId = getMessengerId(); initClientChannel(); } }); const config = { subtree: true, childList: true }; // start listening to changes observer.observe(document, config); // stop listening to changes // observer.disconnect(); PK‹ZK=M#M#!chatify/src/assets/js/autosize.jsnu[/* **************************************************************************** * Text Area auto resize **************************************************************************** */ (function (global, factory) { if (typeof define === "function" && define.amd) { define(['module', 'exports'], factory); } else if (typeof exports !== "undefined") { factory(module, exports); } else { var mod = { exports: {} }; factory(mod, mod.exports); global.autosize = mod.exports; } })(this, function (module, exports) { 'use strict'; var map = typeof Map === "function" ? new Map() : function () { var keys = []; var values = []; return { has: function has(key) { return keys.indexOf(key) > -1; }, get: function get(key) { return values[keys.indexOf(key)]; }, set: function set(key, value) { if (keys.indexOf(key) === -1) { keys.push(key); values.push(value); } }, delete: function _delete(key) { var index = keys.indexOf(key); if (index > -1) { keys.splice(index, 1); values.splice(index, 1); } } }; }(); var createEvent = function createEvent(name) { return new Event(name, { bubbles: true }); }; try { new Event('test'); } catch (e) { // IE does not support `new Event()` createEvent = function createEvent(name) { var evt = document.createEvent('Event'); evt.initEvent(name, true, false); return evt; }; } function assign(ta) { if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || map.has(ta)) return; var heightOffset = null; var clientWidth = null; var cachedHeight = null; function init() { var style = window.getComputedStyle(ta, null); if (style.resize === 'vertical') { ta.style.resize = 'none'; } else if (style.resize === 'both') { ta.style.resize = 'horizontal'; } if (style.boxSizing === 'content-box') { heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom)); } else { heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth); } // Fix when a textarea is not on document body and heightOffset is Not a Number if (isNaN(heightOffset)) { heightOffset = 0; } update(); } function changeOverflow(value) { { // Chrome/Safari-specific fix: // When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space // made available by removing the scrollbar. The following forces the necessary text reflow. var width = ta.style.width; ta.style.width = '0px'; // Force reflow: /* jshint ignore:start */ ta.offsetWidth; /* jshint ignore:end */ ta.style.width = width; } ta.style.overflowY = value; } function getParentOverflows(el) { var arr = []; while (el && el.parentNode && el.parentNode instanceof Element) { if (el.parentNode.scrollTop) { arr.push({ node: el.parentNode, scrollTop: el.parentNode.scrollTop }); } el = el.parentNode; } return arr; } function resize() { if (ta.scrollHeight === 0) { // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM. return; } var overflows = getParentOverflows(ta); var docTop = document.documentElement && document.documentElement.scrollTop; // Needed for Mobile IE (ticket #240) ta.style.height = ''; ta.style.height = ta.scrollHeight + heightOffset + 'px'; // used to check if an update is actually necessary on window.resize clientWidth = ta.clientWidth; // prevents scroll-position jumping overflows.forEach(function (el) { el.node.scrollTop = el.scrollTop; }); if (docTop) { document.documentElement.scrollTop = docTop; } } function update() { resize(); var styleHeight = Math.round(parseFloat(ta.style.height)); var computed = window.getComputedStyle(ta, null); // Using offsetHeight as a replacement for computed.height in IE, because IE does not account use of border-box var actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(computed.height)) : ta.offsetHeight; // The actual height not matching the style height (set via the resize method) indicates that // the max-height has been exceeded, in which case the overflow should be allowed. if (actualHeight < styleHeight) { if (computed.overflowY === 'hidden') { changeOverflow('scroll'); resize(); actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(window.getComputedStyle(ta, null).height)) : ta.offsetHeight; } } else { // Normally keep overflow set to hidden, to avoid flash of scrollbar as the textarea expands. if (computed.overflowY !== 'hidden') { changeOverflow('hidden'); resize(); actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(window.getComputedStyle(ta, null).height)) : ta.offsetHeight; } } if (cachedHeight !== actualHeight) { cachedHeight = actualHeight; var evt = createEvent('autosize:resized'); try { ta.dispatchEvent(evt); } catch (err) { // Firefox will throw an error on dispatchEvent for a detached element // https://bugzilla.mozilla.org/show_bug.cgi?id=889376 } } } var pageResize = function pageResize() { if (ta.clientWidth !== clientWidth) { update(); } }; var destroy = function (style) { window.removeEventListener('resize', pageResize, false); ta.removeEventListener('input', update, false); ta.removeEventListener('keyup', update, false); ta.removeEventListener('autosize:destroy', destroy, false); ta.removeEventListener('autosize:update', update, false); Object.keys(style).forEach(function (key) { ta.style[key] = style[key]; }); map.delete(ta); }.bind(ta, { height: ta.style.height, resize: ta.style.resize, overflowY: ta.style.overflowY, overflowX: ta.style.overflowX, wordWrap: ta.style.wordWrap }); ta.addEventListener('autosize:destroy', destroy, false); // IE9 does not fire onpropertychange or oninput for deletions, // so binding to onkeyup to catch most of those events. // There is no way that I know of to detect something like 'cut' in IE9. if ('onpropertychange' in ta && 'oninput' in ta) { ta.addEventListener('keyup', update, false); } window.addEventListener('resize', pageResize, false); ta.addEventListener('input', update, false); ta.addEventListener('autosize:update', update, false); ta.style.overflowX = 'hidden'; ta.style.wordWrap = 'break-word'; map.set(ta, { destroy: destroy, update: update }); init(); } function destroy(ta) { var methods = map.get(ta); if (methods) { methods.destroy(); } } function update(ta) { var methods = map.get(ta); if (methods) { methods.update(); } } var autosize = null; // Do nothing in Node.js environment and IE8 (or lower) if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') { autosize = function autosize(el) { return el; }; autosize.destroy = function (el) { return el; }; autosize.update = function (el) { return el; }; } else { autosize = function autosize(el, options) { if (el) { Array.prototype.forEach.call(el.length ? el : [el], function (x) { return assign(x, options); }); } return el; }; autosize.destroy = function (el) { if (el) { Array.prototype.forEach.call(el.length ? el : [el], destroy); } return el; }; autosize.update = function (el) { if (el) { Array.prototype.forEach.call(el.length ? el : [el], update); } return el; }; } exports.default = autosize; module.exports = exports['default']; });PK‹Z&ks(chatify/src/views/layouts/info.blade.phpnu[{{-- user info and avatar --}}

{{ config('chatify.name') }}

{{-- shared photos --}}

shared photos

PK‹ZhVS S ,chatify/src/views/layouts/listItem.blade.phpnu[{{-- -------------------- Saved Messages -------------------- --}} @if($get == 'saved') {{-- Avatar side --}} {{-- center side --}}

Saved Messages You

Save messages secretly
@endif {{-- -------------------- All users/group list -------------------- --}} @if($get == 'users') {{-- Avatar side --}} {{-- center side --}}
@if($user->active_status) @endif

{{ strlen($user->name) > 12 ? trim(substr($user->name,0,12)).'..' : $user->name }} {{ $lastMessage->created_at->diffForHumans() }}

{{-- Last Message user indicator --}} {!! $lastMessage->from_id == Auth::user()->id ? 'You :' : '' !!} {{-- Last message body --}} @if($lastMessage->attachment == null) {!! strlen($lastMessage->body) > 30 ? trim(substr($lastMessage->body, 0, 30)).'..' : $lastMessage->body !!} @else Attachment @endif {{-- New messages counter --}} {!! $unseenCounter > 0 ? "".$unseenCounter."" : '' !!}
@endif {{-- -------------------- Search Item -------------------- --}} @if($get == 'search_item') {{-- Avatar side --}} {{-- center side --}}

{{ strlen($user->name) > 12 ? trim(substr($user->name,0,12)).'..' : $user->name }}

@endif {{-- -------------------- Shared photos Item -------------------- --}} @if($get == 'sharedPhoto')
@endif PK‹ZU-chatify/src/views/layouts/headLinks.blade.phpnu[{{ config('chatify.name') }} {{-- Meta tags --}} {{-- scripts --}} {{-- styles --}} {{-- Messenger Color Style--}} @include('Chatify::layouts.messengerColor') PK‹ZyB,chatify/src/views/layouts/sendForm.blade.phpnu[
@csrf
PK‹Z㯶/chatify/src/views/layouts/footerLinks.blade.phpnu[ PK‹ZFO++,chatify/src/views/layouts/favorite.blade.phpnu[

{{ strlen($user->name) > 5 ? substr($user->name,0,6).'..' : $user->name }}

PK‹Z0m /chatify/src/views/layouts/messageCard.blade.phpnu[{{-- -------------------- The default card (white) -------------------- --}} @if ($viewType == 'default') @if ($from_id != $to_id)

{!! $message == null && $attachment != null && @$attachment[2] != 'file' ? $attachment[1] : nl2br($message) !!} {{ $time }} {{-- If attachment is a file --}} @if (@$attachment[2] == 'file') {{ $attachment[1] }} @endif

{{-- If attachment is an image --}} @if (@$attachment[2] == 'image')
@endif
@endif @endif {{-- -------------------- Sender card (owner) -------------------- --}} @if ($viewType == 'sender')

{!! $message == null && $attachment != null && @$attachment[2] != 'file' ? $attachment[1] : nl2br($message) !!} {{ $time }} {{-- If attachment is a file --}} @if (@$attachment[2] == 'file') {{ $attachment[1] }} @endif

{{-- If attachment is an image --}} @if (@$attachment[2] == 'image') {{--
--}}
@endif
@endif PK‹ZT>2chatify/src/views/layouts/messengerColor.blade.phpnu[ PK‹ZǍUU*chatify/src/views/layouts/modals.blade.phpnu[{{-- ---------------------- Image modal box ---------------------- --}}
×
{{-- ---------------------- Delete Modal ---------------------- --}}
Are you sure you want to delete this?
You can not undo this action
{{-- ---------------------- Alert Modal ---------------------- --}} {{-- ---------------------- Settings Modal ---------------------- --}}
@csrf {{--
Update your profile settings
--}}
{{-- Udate profile avatar --}}

{{-- Dark/Light Mode --}}

Dark Mode

{{-- change messenger color --}}

{{--

Change {{ config('chatify.name') }} Color

--}}
@foreach (config('chatify.colors') as $color) @if (($loop->index + 1) % 5 == 0)
@endif @endforeach
PK‹ZЦbb%chatify/src/views/pages/app.blade.phpnu[@include('Chatify::layouts.headLinks')
{{-- ----------------------Users/Groups lists side---------------------- --}}
{{-- Header and search bar --}} {{-- tabs and lists --}}
{{-- Lists [Users/Group] --}} {{-- ---------------- [ User Tab ] ---------------- --}}
{{-- Favorites --}}

Favorites

{{-- Saved Messages --}} {!! view('Chatify::layouts.listItem', ['get' => 'saved']) !!} {{-- Contact --}}
{{-- ---------------- [ Group Tab ] ---------------- --}}
{{-- items --}}

Click here for more info!

{{-- ---------------- [ Search Tab ] ---------------- --}}
{{-- items --}}

Search

Type to search..

{{-- ----------------------Messaging side---------------------- --}}
{{-- header title [conversation name] amd buttons --}} {{-- Internet connection --}}
Connected Connecting... No internet access
{{-- Messaging area --}}

Please select a chat to start messaging

{{-- Typing indicator --}}

{{-- Send Message Form --}} @include('Chatify::layouts.sendForm')
{{-- ---------------------- Info side ---------------------- --}}
{{-- nav actions --}} {!! view('Chatify::layouts.info')->render() !!}
@include('Chatify::layouts.modals') @include('Chatify::layouts.footerLinks') PK‹ZڇD}pp chatify/src/Models/ChMessage.phpnu[ env('CHATIFY_NAME', 'Chatify Messenger'), /* |------------------------------------- | The disk on which to store added | files and derived images by default. |------------------------------------- */ 'storage_disk_name' => env('CHATIFY_STORAGE_DISK', 'public'), /* |------------------------------------- | Routes configurations |------------------------------------- */ 'routes' => [ 'prefix' => env('CHATIFY_ROUTES_PREFIX', 'chatify'), 'middleware' => env('CHATIFY_ROUTES_MIDDLEWARE', ['web','auth']), 'namespace' => env('CHATIFY_ROUTES_NAMESPACE', 'Chatify\Http\Controllers'), ], 'api_routes' => [ 'prefix' => env('CHATIFY_API_ROUTES_PREFIX', 'chatify/api'), 'middleware' => env('CHATIFY_API_ROUTES_MIDDLEWARE', ['api']), 'namespace' => env('CHATIFY_API_ROUTES_NAMESPACE', 'Chatify\Http\Controllers\Api'), ], /* |------------------------------------- | Pusher API credentials |------------------------------------- */ 'pusher' => [ 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'cluster' => env('PUSHER_APP_CLUSTER'), 'encrypted' => false, ], ], /* |------------------------------------- | User Avatar |------------------------------------- */ 'user_avatar' => [ 'folder' => 'users-avatar', 'default' => 'avatar.png', ], /* |------------------------------------- | Gravatar | | imageset property options: | [ 404 | mp | identicon (default) | monsterid | wavatar ] |------------------------------------- */ 'gravatar' => [ 'enabled' => false, 'image_size' => 200, 'imageset' => 'identicon' ], /* |------------------------------------- | Attachments |------------------------------------- */ 'attachments' => [ 'folder' => 'attachments', 'download_route_name' => 'attachments.download', 'allowed_images' => (array) ['png','jpg','jpeg','gif'], 'allowed_files' => (array) ['zip','rar','txt'], 'max_upload_size' => env('CHATIFY_MAX_FILE_SIZE', 150), // MB ], /* |------------------------------------- | Messenger's colors |------------------------------------- */ 'colors' => (array) [ '#2180f3', '#2196F3', '#00BCD4', '#3F51B5', '#673AB7', '#4CAF50', '#FFC107', '#FF9800', '#ff2522', '#9C27B0', ], ]; PK‹Z>227chatify/src/Http/Controllers/Api/MessagesController.phpnu[user(), Auth::user(), $request['channel_name'], $request['socket_id'] ); } /** * Fetch data by id for (user/group) * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function idFetchData(Request $request) { return auth()->user(); // Favorite $favorite = Chatify::inFavorite($request['id']); // User data if ($request['type'] == 'user') { $fetch = User::where('id', $request['id'])->first(); if($fetch){ $userAvatar = Chatify::getUserWithAvatar($fetch)->avatar; } } // send the response return Response::json([ 'favorite' => $favorite, 'fetch' => $fetch ?? [], 'user_avatar' => $userAvatar ?? null, ]); } /** * This method to make a links for the attachments * to be downloadable. * * @param string $fileName * @return \Illuminate\Http\JsonResponse */ public function download($fileName) { $path = config('chatify.attachments.folder') . '/' . $fileName; if (Chatify::storage()->exists($path)) { return response()->json([ 'file_name' => $fileName, 'download_path' => Chatify::storage()->url($path) ], 200); } else { return response()->json([ 'message'=>"Sorry, File does not exist in our server or may have been deleted!" ], 404); } } /** * Send a message to database * * @param Request $request * @return JSON response */ public function send(Request $request) { // default variables $error = (object)[ 'status' => 0, 'message' => null ]; $attachment = null; $attachment_title = null; // if there is attachment [file] if ($request->hasFile('file')) { // allowed extensions $allowed_images = Chatify::getAllowedImages(); $allowed_files = Chatify::getAllowedFiles(); $allowed = array_merge($allowed_images, $allowed_files); $file = $request->file('file'); // check file size if ($file->getSize() < Chatify::getMaxUploadSize()) { if (in_array(strtolower($file->getClientOriginalExtension()), $allowed)) { // get attachment name $attachment_title = $file->getClientOriginalName(); // upload attachment and store the new name $attachment = Str::uuid() . "." . $file->getClientOriginalExtension(); $file->storeAs(config('chatify.attachments.folder'), $attachment, config('chatify.storage_disk_name')); } else { $error->status = 1; $error->message = "File extension not allowed!"; } } else { $error->status = 1; $error->message = "File size you are trying to upload is too large!"; } } if (!$error->status) { // send to database $messageID = mt_rand(9, 999999999) + time(); Chatify::newMessage([ 'id' => $messageID, 'type' => $request['type'], 'from_id' => Auth::user()->id, 'to_id' => $request['id'], 'body' => htmlentities(trim($request['message']), ENT_QUOTES, 'UTF-8'), 'attachment' => ($attachment) ? json_encode((object)[ 'new_name' => $attachment, 'old_name' => htmlentities(trim($attachment_title), ENT_QUOTES, 'UTF-8'), ]) : null, ]); // fetch message to send it with the response $messageData = Chatify::fetchMessage($messageID); // send to user using pusher Chatify::push("private-chatify.".$request['id'], 'messaging', [ 'from_id' => Auth::user()->id, 'to_id' => $request['id'], 'message' => Chatify::messageCard($messageData, 'default') ]); } // send the response return Response::json([ 'status' => '200', 'error' => $error, 'message' => $messageData ?? [], 'tempID' => $request['temporaryMsgId'], ]); } /** * fetch [user/group] messages from database * * @param Request $request * @return JSON response */ public function fetch(Request $request) { $query = Chatify::fetchMessagesQuery($request['id'])->latest(); $messages = $query->paginate($request->per_page ?? $this->perPage); $totalMessages = $messages->total(); $lastPage = $messages->lastPage(); $response = [ 'total' => $totalMessages, 'last_page' => $lastPage, 'last_message_id' => collect($messages->items())->last()->id ?? null, 'messages' => $messages->items(), ]; return Response::json($response); } /** * Make messages as seen * * @param Request $request * @return void */ public function seen(Request $request) { // make as seen $seen = Chatify::makeSeen($request['id']); // send the response return Response::json([ 'status' => $seen, ], 200); } /** * Get contacts list * * @param Request $request * @return \Illuminate\Http\JsonResponse response */ public function getContacts(Request $request) { // get all users that received/sent message from/to [Auth user] $users = Message::join('users', function ($join) { $join->on('ch_messages.from_id', '=', 'users.id') ->orOn('ch_messages.to_id', '=', 'users.id'); }) ->where(function ($q) { $q->where('ch_messages.from_id', Auth::user()->id) ->orWhere('ch_messages.to_id', Auth::user()->id); }) ->where('users.id','!=',Auth::user()->id) ->select('users.*',DB::raw('MAX(ch_messages.created_at) max_created_at')) ->orderBy('max_created_at', 'desc') ->groupBy('users.id') ->paginate($request->per_page ?? $this->perPage); return response()->json([ 'contacts' => $users->items(), 'total' => $users->total() ?? 0, 'last_page' => $users->lastPage() ?? 1, ], 200); } /** * Put a user in the favorites list * * @param Request $request * @return void */ public function favorite(Request $request) { // check action [star/unstar] if (Chatify::inFavorite($request['user_id'])) { // UnStar Chatify::makeInFavorite($request['user_id'], 0); $status = 0; } else { // Star Chatify::makeInFavorite($request['user_id'], 1); $status = 1; } // send the response return Response::json([ 'status' => @$status, ], 200); } /** * Get favorites list * * @param Request $request * @return void */ public function getFavorites(Request $request) { $favorites = Favorite::where('user_id', Auth::user()->id)->get(); foreach ($favorites as $favorite) { $favorite->user = User::where('id', $favorite->favorite_id)->first(); } return Response::json([ 'total' => count($favorites), 'favorites' => $favorites ?? [], ], 200); } /** * Search in messenger * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function search(Request $request) { $input = trim(filter_var($request['input'])); $records = User::where('id','!=',Auth::user()->id) ->where('name', 'LIKE', "%{$input}%") ->paginate($request->per_page ?? $this->perPage); foreach ($records->items() as $index => $record) { $records[$index] += Chatify::getUserWithAvatar($record); } return Response::json([ 'records' => $records->items(), 'total' => $records->total(), 'last_page' => $records->lastPage() ], 200); } /** * Get shared photos * * @param Request $request * @return \Illuminate\Http\JsonResponse */ public function sharedPhotos(Request $request) { $images = Chatify::getSharedPhotos($request['user_id']); foreach ($images as $image) { $image = asset(config('chatify.attachments.folder') . $image); } // send the response return Response::json([ 'shared' => $images ?? [], ], 200); } /** * Delete conversation * * @param Request $request * @return void */ public function deleteConversation(Request $request) { // delete $delete = Chatify::deleteConversation($request['id']); // send the response return Response::json([ 'deleted' => $delete ? 1 : 0, ], 200); } public function updateSettings(Request $request) { $msg = null; $error = $success = 0; // dark mode if ($request['dark_mode']) { $request['dark_mode'] == "dark" ? User::where('id', Auth::user()->id)->update(['dark_mode' => 1]) // Make Dark : User::where('id', Auth::user()->id)->update(['dark_mode' => 0]); // Make Light } // If messenger color selected if ($request['messengerColor']) { $messenger_color = trim(filter_var($request['messengerColor'])); User::where('id', Auth::user()->id) ->update(['messenger_color' => $messenger_color]); } // if there is a [file] if ($request->hasFile('avatar')) { // allowed extensions $allowed_images = Chatify::getAllowedImages(); $file = $request->file('avatar'); // check file size if ($file->getSize() < Chatify::getMaxUploadSize()) { if (in_array(strtolower($file->getClientOriginalExtension()), $allowed_images)) { // delete the older one if (Auth::user()->avatar != config('chatify.user_avatar.default')) { $path = Chatify::getUserAvatarUrl(Auth::user()->avatar); if (Chatify::storage()->exists($path)) { Chatify::storage()->delete($path); } } // upload $avatar = Str::uuid() . "." . $file->getClientOriginalExtension(); $update = User::where('id', Auth::user()->id)->update(['avatar' => $avatar]); $file->storeAs(config('chatify.user_avatar.folder'), $avatar, config('chatify.storage_disk_name')); $success = $update ? 1 : 0; } else { $msg = "File extension not allowed!"; $error = 1; } } else { $msg = "File size you are trying to upload is too large!"; $error = 1; } } // send the response return Response::json([ 'status' => $success ? 1 : 0, 'error' => $error ? 1 : 0, 'message' => $error ? $msg : 0, ], 200); } /** * Set user's active status * * @param Request $request * @return void */ public function setActiveStatus(Request $request) { $update = $request['status'] > 0 ? User::where('id', $request['user_id'])->update(['active_status' => 1]) : User::where('id', $request['user_id'])->update(['active_status' => 0]); // send the response return Response::json([ 'status' => $update, ], 200); } } PK‹Z~$aAA3chatify/src/Http/Controllers/MessagesController.phpnu[user(), Auth::user(), $request['channel_name'], $request['socket_id'] ); } /** * Returning the view of the app with the required data. * * @param int $id * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View */ public function index( $id = null) { $routeName= FacadesRequest::route()->getName(); $type = in_array($routeName, ['user','group']) ? $routeName : 'user'; return view('Chatify::pages.app', [ 'id' => $id ?? 0, 'type' => $type ?? 'user', 'messengerColor' => Auth::user()->messenger_color ?? $this->messengerFallbackColor, 'dark_mode' => Auth::user()->dark_mode < 1 ? 'light' : 'dark', ]); } /** * Fetch data by id for (user/group) * * @param Request $request * @return JsonResponse */ public function idFetchData(Request $request) { // Favorite $favorite = Chatify::inFavorite($request['id']); // User data if ($request['type'] == 'user') { $fetch = User::where('id', $request['id'])->first(); if($fetch){ $userAvatar = Chatify::getUserWithAvatar($fetch)->avatar; } } // send the response return Response::json([ 'favorite' => $favorite, 'fetch' => $fetch ?? [], 'user_avatar' => $userAvatar ?? null, ]); } /** * This method to make a links for the attachments * to be downloadable. * * @param string $fileName * @return \Symfony\Component\HttpFoundation\StreamedResponse|void */ public function download($fileName) { if (Chatify::storage()->exists(config('chatify.attachments.folder') . '/' . $fileName)) { return Chatify::storage()->download(config('chatify.attachments.folder') . '/' . $fileName); } else { return abort(404, "Sorry, File does not exist in our server or may have been deleted!"); } } /** * Send a message to database * * @param Request $request * @return JsonResponse */ public function send(Request $request) { // default variables $error = (object)[ 'status' => 0, 'message' => null ]; $attachment = null; $attachment_title = null; // if there is attachment [file] if ($request->hasFile('file')) { // allowed extensions $allowed_images = Chatify::getAllowedImages(); $allowed_files = Chatify::getAllowedFiles(); $allowed = array_merge($allowed_images, $allowed_files); $file = $request->file('file'); // check file size if ($file->getSize() < Chatify::getMaxUploadSize()) { if (in_array(strtolower($file->getClientOriginalExtension()), $allowed)) { // get attachment name $attachment_title = $file->getClientOriginalName(); // upload attachment and store the new name $attachment = Str::uuid() . "." . $file->getClientOriginalExtension(); $file->storeAs(config('chatify.attachments.folder'), $attachment, config('chatify.storage_disk_name')); } else { $error->status = 1; $error->message = "File extension not allowed!"; } } else { $error->status = 1; $error->message = "File size you are trying to upload is too large!"; } } if (!$error->status) { // send to database $messageID = mt_rand(9, 999999999) + time(); Chatify::newMessage([ 'id' => $messageID, 'type' => $request['type'], 'from_id' => Auth::user()->id, 'to_id' => $request['id'], 'body' => htmlentities(trim($request['message']), ENT_QUOTES, 'UTF-8'), 'attachment' => ($attachment) ? json_encode((object)[ 'new_name' => $attachment, 'old_name' => htmlentities(trim($attachment_title), ENT_QUOTES, 'UTF-8'), ]) : null, ]); // fetch message to send it with the response $messageData = Chatify::fetchMessage($messageID); // send to user using pusher Chatify::push("private-chatify.".$request['id'], 'messaging', [ 'from_id' => Auth::user()->id, 'to_id' => $request['id'], 'message' => Chatify::messageCard($messageData, 'default') ]); } // send the response return Response::json([ 'status' => '200', 'error' => $error, 'message' => Chatify::messageCard(@$messageData), 'tempID' => $request['temporaryMsgId'], ]); } /** * fetch [user/group] messages from database * * @param Request $request * @return JsonResponse */ public function fetch(Request $request) { $query = Chatify::fetchMessagesQuery($request['id'])->latest(); $messages = $query->paginate($request->per_page ?? $this->perPage); $totalMessages = $messages->total(); $lastPage = $messages->lastPage(); $response = [ 'total' => $totalMessages, 'last_page' => $lastPage, 'last_message_id' => collect($messages->items())->last()->id ?? null, 'messages' => '', ]; // if there is no messages yet. if ($totalMessages < 1) { $response['messages'] ='

Say \'hi\' and start messaging

'; return Response::json($response); } if (count($messages->items()) < 1) { $response['messages'] = ''; return Response::json($response); } $allMessages = null; foreach ($messages->reverse() as $index => $message) { $allMessages .= Chatify::messageCard( Chatify::fetchMessage($message->id, $index) ); } $response['messages'] = $allMessages; return Response::json($response); } /** * Make messages as seen * * @param Request $request * @return JsonResponse|void */ public function seen(Request $request) { // make as seen $seen = Chatify::makeSeen($request['id']); // send the response return Response::json([ 'status' => $seen, ], 200); } /** * Get contacts list * * @param Request $request * @return JsonResponse */ public function getContacts(Request $request) { // get all users that received/sent message from/to [Auth user] $users = Message::join('users', function ($join) { $join->on('ch_messages.from_id', '=', 'users.id') ->orOn('ch_messages.to_id', '=', 'users.id'); }) ->where(function ($q) { $q->where('ch_messages.from_id', Auth::user()->id) ->orWhere('ch_messages.to_id', Auth::user()->id); }) ->where('users.id','!=',Auth::user()->id) ->select('users.*',DB::raw('MAX(ch_messages.created_at) max_created_at')) ->orderBy('max_created_at', 'desc') ->groupBy('users.id') ->paginate($request->per_page ?? $this->perPage); $usersList = $users->items(); if (count($usersList) > 0) { $contacts = ''; foreach ($usersList as $user) { $contacts .= Chatify::getContactItem($user); } } else { $contacts = '

Your contact list is empty

'; } return Response::json([ 'contacts' => $contacts, 'total' => $users->total() ?? 0, 'last_page' => $users->lastPage() ?? 1, ], 200); } /** * Update user's list item data * * @param Request $request * @return JsonResponse */ public function updateContactItem(Request $request) { // Get user data $user = User::where('id', $request['user_id'])->first(); if(!$user){ return Response::json([ 'message' => 'User not found!', ], 401); } $contactItem = Chatify::getContactItem($user); // send the response return Response::json([ 'contactItem' => $contactItem, ], 200); } /** * Put a user in the favorites list * * @param Request $request * @return JsonResponse|void */ public function favorite(Request $request) { // check action [star/unstar] if (Chatify::inFavorite($request['user_id'])) { // UnStar Chatify::makeInFavorite($request['user_id'], 0); $status = 0; } else { // Star Chatify::makeInFavorite($request['user_id'], 1); $status = 1; } // send the response return Response::json([ 'status' => @$status, ], 200); } /** * Get favorites list * * @param Request $request * @return JsonResponse|void */ public function getFavorites(Request $request) { $favoritesList = null; $favorites = Favorite::where('user_id', Auth::user()->id); foreach ($favorites->get() as $favorite) { // get user data $user = User::where('id', $favorite->favorite_id)->first(); $favoritesList .= view('Chatify::layouts.favorite', [ 'user' => $user, ]); } // send the response return Response::json([ 'count' => $favorites->count(), 'favorites' => $favorites->count() > 0 ? $favoritesList : 0, ], 200); } /** * Search in messenger * * @param Request $request * @return JsonResponse|void */ public function search(Request $request) { $getRecords = null; $input = trim(filter_var($request['input'])); $records = User::where('id','!=',Auth::user()->id) ->where('name', 'LIKE', "%{$input}%") ->paginate($request->per_page ?? $this->perPage); foreach ($records->items() as $record) { $getRecords .= view('Chatify::layouts.listItem', [ 'get' => 'search_item', 'type' => 'user', 'user' => Chatify::getUserWithAvatar($record), ])->render(); } if($records->total() < 1){ $getRecords = '

Nothing to show.

'; } // send the response return Response::json([ 'records' => $getRecords, 'total' => $records->total(), 'last_page' => $records->lastPage() ], 200); } /** * Get shared photos * * @param Request $request * @return JsonResponse|void */ public function sharedPhotos(Request $request) { $shared = Chatify::getSharedPhotos($request['user_id']); $sharedPhotos = null; // shared with its template for ($i = 0; $i < count($shared); $i++) { $sharedPhotos .= view('Chatify::layouts.listItem', [ 'get' => 'sharedPhoto', 'image' => Chatify::getAttachmentUrl($shared[$i]), ])->render(); } // send the response return Response::json([ 'shared' => count($shared) > 0 ? $sharedPhotos : '

Nothing shared yet

', ], 200); } /** * Delete conversation * * @param Request $request * @return JsonResponse */ public function deleteConversation(Request $request) { // delete $delete = Chatify::deleteConversation($request['id']); // send the response return Response::json([ 'deleted' => $delete ? 1 : 0, ], 200); } /** * Delete message * * @param Request $request * @return JsonResponse */ public function deleteMessage(Request $request) { // delete $delete = Chatify::deleteMessage($request['id']); // send the response return Response::json([ 'deleted' => $delete ? 1 : 0, ], 200); } public function updateSettings(Request $request) { $msg = null; $error = $success = 0; // dark mode if ($request['dark_mode']) { $request['dark_mode'] == "dark" ? User::where('id', Auth::user()->id)->update(['dark_mode' => 1]) // Make Dark : User::where('id', Auth::user()->id)->update(['dark_mode' => 0]); // Make Light } // If messenger color selected if ($request['messengerColor']) { $messenger_color = trim(filter_var($request['messengerColor'])); User::where('id', Auth::user()->id) ->update(['messenger_color' => $messenger_color]); } // if there is a [file] if ($request->hasFile('avatar')) { // allowed extensions $allowed_images = Chatify::getAllowedImages(); $file = $request->file('avatar'); // check file size if ($file->getSize() < Chatify::getMaxUploadSize()) { if (in_array(strtolower($file->getClientOriginalExtension()), $allowed_images)) { // delete the older one if (Auth::user()->avatar != config('chatify.user_avatar.default')) { $avatar = Auth::user()->avatar; if (Chatify::storage()->exists($avatar)) { Chatify::storage()->delete($avatar); } } // upload $avatar = Str::uuid() . "." . $file->getClientOriginalExtension(); $update = User::where('id', Auth::user()->id)->update(['avatar' => $avatar]); $file->storeAs(config('chatify.user_avatar.folder'), $avatar, config('chatify.storage_disk_name')); $success = $update ? 1 : 0; } else { $msg = "File extension not allowed!"; $error = 1; } } else { $msg = "File size you are trying to upload is too large!"; $error = 1; } } // send the response return Response::json([ 'status' => $success ? 1 : 0, 'error' => $error ? 1 : 0, 'message' => $error ? $msg : 0, ], 200); } /** * Set user's active status * * @param Request $request * @return JsonResponse */ public function setActiveStatus(Request $request) { $update = $request['status'] > 0 ? User::where('id', $request['user_id'])->update(['active_status' => 1]) : User::where('id', $request['user_id'])->update(['active_status' => 0]); // send the response return Response::json([ 'status' => $update, ], 200); } } PK‹Z5,Ochatify/src/database/migrations/2022_01_10_99999_add_active_status_to_users.phpnu[boolean('active_status')->default(0); } }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('active_status'); }); } } PK‹Z<'!Kchatify/src/database/migrations/2022_01_10_99999_create_favorites_table.phpnu[bigInteger('id'); $table->bigInteger('user_id'); $table->bigInteger('favorite_id'); $table->timestamps(); $table->primary('id'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('ch_favorites'); } } PK‹Z;0;Kchatify/src/database/migrations/2022_01_10_99999_add_dark_mode_to_users.phpnu[boolean('dark_mode')->default(0); } }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('dark_mode'); }); } } PK‹Z3K%%Qchatify/src/database/migrations/2022_01_10_99999_add_messenger_color_to_users.phpnu[string('messenger_color')->default('#2180f3'); } }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('messenger_color'); }); } } PK‹ZŕJchatify/src/database/migrations/2022_01_10_99999_create_messages_table.phpnu[bigInteger('id'); $table->string('type'); $table->bigInteger('from_id'); $table->bigInteger('to_id'); $table->string('body',5000)->nullable(); $table->string('attachment')->nullable(); $table->boolean('seen')->default(false); $table->timestamps(); $table->primary('id'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('ch_messages'); } } PK‹ZHchatify/src/database/migrations/2022_01_10_99999_add_avatar_to_users.phpnu[string('avatar')->default(config('chatify.user_avatar.default')); } }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('avatar'); }); } } PK‹Zy&chatify/src/ChatifyServiceProvider.phpnu[bind('ChatifyMessenger', function () { return new \Chatify\ChatifyMessenger; }); } /** * Bootstrap services. * * @return void */ public function boot() { // Load Views and Routes $this->loadViewsFrom(__DIR__ . '/views', 'Chatify'); $this->loadRoutes(); if ($this->app->runningInConsole()) { $this->commands([ InstallCommand::class, PublishCommand::class, ]); $this->setPublishes(); } } /** * Publishing the files that the user may override. * * @return void */ protected function setPublishes() { // Load user's avatar folder from package's config $userAvatarFolder = json_decode(json_encode(include(__DIR__.'/config/chatify.php')))->user_avatar->folder; // Config $this->publishes([ __DIR__ . '/config/chatify.php' => config_path('chatify.php') ], 'chatify-config'); // Migrations $this->publishes([ __DIR__ . '/database/migrations/2022_01_10_99999_add_active_status_to_users.php' => database_path('migrations/' . date('Y_m_d') . '_999999_add_active_status_to_users.php'), __DIR__ . '/database/migrations/2022_01_10_99999_add_avatar_to_users.php' => database_path('migrations/' . date('Y_m_d') . '_999999_add_avatar_to_users.php'), __DIR__ . '/database/migrations/2022_01_10_99999_add_dark_mode_to_users.php' => database_path('migrations/' . date('Y_m_d') . '_999999_add_dark_mode_to_users.php'), __DIR__ . '/database/migrations/2022_01_10_99999_add_messenger_color_to_users.php' => database_path('migrations/' . date('Y_m_d') . '_999999_add_messenger_color_to_users.php'), __DIR__ . '/database/migrations/2022_01_10_99999_create_favorites_table.php' => database_path('migrations/' . date('Y_m_d') . '_999999_create_favorites_table.php'), __DIR__ . '/database/migrations/2022_01_10_99999_create_messages_table.php' => database_path('migrations/' . date('Y_m_d') . '_999999_create_messages_table.php'), ], 'chatify-migrations'); // Models $isV8 = explode('.', app()->version())[0] >= 8; $this->publishes([ __DIR__ . '/Models' => app_path($isV8 ? 'Models' : '') ], 'chatify-models'); // Controllers $this->publishes([ __DIR__ . '/Http/Controllers' => app_path('Http/Controllers/vendor/Chatify') ], 'chatify-controllers'); // Views $this->publishes([ __DIR__ . '/views' => resource_path('views/vendor/Chatify') ], 'chatify-views'); // Assets $this->publishes([ // CSS __DIR__ . '/assets/css' => public_path('css/chatify'), // JavaScript __DIR__ . '/assets/js' => public_path('js/chatify'), // Images __DIR__ . '/assets/imgs' => storage_path('app/public/' . $userAvatarFolder), ], 'chatify-assets'); } /** * Group the routes and set up configurations to load them. * * @return void */ protected function loadRoutes() { Route::group($this->routesConfigurations(), function () { $this->loadRoutesFrom(__DIR__ . '/routes/web.php'); }); Route::group($this->apiRoutesConfigurations(), function () { $this->loadRoutesFrom(__DIR__ . '/routes/api.php'); }); } /** * Routes configurations. * * @return array */ private function routesConfigurations() { return [ 'prefix' => config('chatify.routes.prefix'), 'namespace' => config('chatify.routes.namespace'), 'middleware' => config('chatify.routes.middleware'), ]; } /** * API routes configurations. * * @return array */ private function apiRoutesConfigurations() { return [ 'prefix' => config('chatify.api_routes.prefix'), 'namespace' => config('chatify.api_routes.namespace'), 'middleware' => config('chatify.api_routes.middleware'), ]; } } PK‹ZXmZchatify/src/routes/api.phpnu[name('api.pusher.auth'); /** * Fetch info for specific id [user/group] */ Route::post('/idInfo', 'MessagesController@idFetchData')->name('api.idInfo'); /** * Send message route */ Route::post('/sendMessage', 'MessagesController@send')->name('api.send.message'); /** * Fetch messages */ Route::post('/fetchMessages', 'MessagesController@fetch')->name('api.fetch.messages'); /** * Download attachments route to create a downloadable links */ Route::get('/download/{fileName}', 'MessagesController@download')->name('api.'.config('chatify.attachments.download_route_name')); /** * Make messages as seen */ Route::post('/makeSeen', 'MessagesController@seen')->name('api.messages.seen'); /** * Get contacts */ Route::get('/getContacts', 'MessagesController@getContacts')->name('api.contacts.get'); /** * Star in favorite list */ Route::post('/star', 'MessagesController@favorite')->name('api.star'); /** * get favorites list */ Route::post('/favorites', 'MessagesController@getFavorites')->name('api.favorites'); /** * Search in messenger */ Route::get('/search', 'MessagesController@search')->name('api.search'); /** * Get shared photos */ Route::post('/shared', 'MessagesController@sharedPhotos')->name('api.shared'); /** * Delete Conversation */ Route::post('/deleteConversation', 'MessagesController@deleteConversation')->name('api.conversation.delete'); /** * Delete Conversation */ Route::post('/updateSettings', 'MessagesController@updateSettings')->name('api.avatar.update'); /** * Set active status */ Route::post('/setActiveStatus', 'MessagesController@setActiveStatus')->name('api.activeStatus.set'); PK‹Z<}chatify/src/routes/web.phpnu[name(config('chatify.routes.prefix'))->middleware(['auth', 'XSS']); /** * Fetch info for specific id [user/group] */ Route::post('/idInfo', 'MessagesController@idFetchData')->middleware(['auth', 'XSS']); /** * Send message route */ Route::post('/sendMessage', 'MessagesController@send')->name('send.message')->middleware(['auth', 'XSS']); /** * Fetch messages */ Route::post('/fetchMessages', 'MessagesController@fetch')->name('fetch.messages')->middleware(['auth', 'XSS']); /** * Download attachments route to create a downloadable links */ Route::get('/download/{fileName}', 'MessagesController@download')->name(config('chatify.attachments.download_route_name'))->middleware(['auth', 'XSS']); /** * Authentication for pusher private channels */ Route::post('/chat/auth', 'MessagesController@pusherAuth')->name('pusher.auth')->middleware(['auth', 'XSS']); /** * Make messages as seen */ Route::post('/makeSeen', 'MessagesController@seen')->name('messages.seen')->middleware(['auth', 'XSS']); /** * Get contacts */ Route::get('/getContacts', 'MessagesController@getContacts')->name('contacts.get')->middleware(['auth', 'XSS']); /** * Update contact item data */ Route::post('/updateContacts', 'MessagesController@updateContactItem')->name('contacts.update')->middleware(['auth', 'XSS']); /** * Star in favorite list */ Route::post('/star', 'MessagesController@favorite')->name('star')->middleware(['auth', 'XSS']); /** * get favorites list */ Route::post('/favorites', 'MessagesController@getFavorites')->name('favorites')->middleware(['auth', 'XSS']); /** * Search in messenger */ Route::get('/search', 'MessagesController@search')->name('search')->middleware(['auth', 'XSS']); /** * Get shared photos */ Route::post('/shared', 'MessagesController@sharedPhotos')->name('shared')->middleware(['auth', 'XSS']); /** * Delete Conversation */ Route::post('/deleteConversation', 'MessagesController@deleteConversation')->name('conversation.delete')->middleware(['auth', 'XSS']); /** * Delete Message */ Route::post('/deleteMessage', 'MessagesController@deleteMessage')->name('message.delete')->middleware(['auth', 'XSS']); /** * Update setting */ Route::post('/updateSettings', 'MessagesController@updateSettings')->name('avatar.update')->middleware(['auth', 'XSS']); /** * Set active status */ Route::post('/setActiveStatus', 'MessagesController@setActiveStatus')->name('activeStatus.set')->middleware(['auth', 'XSS']); /* * [Group] view by id */ Route::get('/group/{id}', 'MessagesController@index')->name('group')->middleware(['auth', 'XSS']); /* * user view by id. * Note : If you added routes after the [User] which is the below one, * it will considered as user id. * * e.g. - The commented routes below : */ // Route::get('/route', function(){ return 'Munaf'; }); // works as a route Route::get('/{id}', 'MessagesController@index')->name('user')->middleware(['auth', 'XSS']); // Route::get('/route', function(){ return 'Munaf'; }); // works as a user id PK‹Z#N&chatify/src/Console/InstallCommand.phpnu[isV8 = explode('.',app()->version())[0] >= 8; $this->info('Installing Chatify...'); $this->line('----------'); $this->line('Configurations...'); $this->modifyModelsPath('/../Http/Controllers/MessagesController.php','User'); $this->modifyModelsPath('/../Http/Controllers/MessagesController.php','ChFavorite'); $this->modifyModelsPath('/../Http/Controllers/MessagesController.php','ChMessage'); $this->modifyModelsPath('/../Http/Controllers/Api/MessagesController.php','User'); $this->modifyModelsPath('/../Http/Controllers/Api/MessagesController.php','ChFavorite'); $this->modifyModelsPath('/../Http/Controllers/Api/MessagesController.php','ChMessage'); $this->modifyModelsPath('/../ChatifyMessenger.php','ChFavorite'); $this->modifyModelsPath('/../ChatifyMessenger.php','ChMessage'); $this->modifyModelsPath('/../Models/ChFavorite.php'); $this->modifyModelsPath('/../Models/ChMessage.php'); $this->info('[✓] done'); $assetsToBePublished = [ 'config' => config_path('chatify.php'), 'views' => resource_path('views/vendor/Chatify'), 'assets' => public_path('css/chatify'), 'models' => app_path(($this->isV8 ? 'Models/' : '').'ChMessage.php'), 'migrations' => database_path('migrations/2019_09_22_192348_create_messages_table.php'), ]; foreach ($assetsToBePublished as $target => $path) { $this->line('----------'); $this->process($target, $path); } $this->line('----------'); $this->line('Creating storage symlink...'); Artisan::call('storage:link'); $this->info('[✓] Storage linked.'); $this->line('----------'); $this->info('[✓] Chatify installed successfully'); } /** * Modify models imports/namespace path according to Laravel version. * * @param string $targetFilePath * @param string $model * @return void */ private function modifyModelsPath($targetFilePath, $model = null){ $path = realpath(__DIR__.$targetFilePath); $contents = File::get($path); $model = !empty($model) ? '\\'.$model : ';'; $contents = str_replace( (!$this->isV8 ? 'App\Models' : 'App').$model, ($this->isV8 ? 'App\Models' : 'App').$model, $contents ); File::put($path, $contents); } /** * Check, publish, or overwrite the assets. * * @param string $target * @param string $path * @return void */ private function process($target, $path) { $this->line('Publishing '.$target.'...'); if (!File::exists($path)) { $this->publish($target); $this->info('[✓] '.$target.' published.'); return; } if ($this->shouldOverwrite($target)) { $this->line('Overwriting '.$target.'...'); $this->publish($target,true); $this->info('[✓] '.$target.' published.'); return; } $this->line('[-] Ignored, The existing '.$target.' was not overwritten'); } /** * Ask to overwrite. * * @param string $target * @return void */ private function shouldOverwrite($target) { return $this->confirm( $target.' already exists. Do you want to overwrite it?', false ); } /** * Call the publish command. * * @param string $tag * @param bool $forcePublish * @return void */ private function publish($tag, $forcePublish = false) { $this->call('vendor:publish', [ '--tag' => 'chatify-'.$tag, '--force' => $forcePublish, ]); } } PK‹Z&chatify/src/Console/PublishCommand.phpnu[option('force')){ $this->call('vendor:publish', [ '--tag' => 'chatify-config', '--force' => true, ]); $this->call('vendor:publish', [ '--tag' => 'chatify-migrations', '--force' => true, ]); $this->call('vendor:publish', [ '--tag' => 'chatify-models', '--force' => true, ]); } $this->call('vendor:publish', [ '--tag' => 'chatify-views', '--force' => true, ]); $this->call('vendor:publish', [ '--tag' => 'chatify-assets', '--force' => true, ]); } } PK‹Zn(chatify/src/Facades/ChatifyMessenger.phpnu[pusher = new Pusher( config('chatify.pusher.key'), config('chatify.pusher.secret'), config('chatify.pusher.app_id'), config('chatify.pusher.options'), ); } /** * This method returns the allowed image extensions * to attach with the message. * * @return array */ public function getAllowedImages() { return config('chatify.attachments.allowed_images'); } /** * This method returns the allowed file extensions * to attach with the message. * * @return array */ public function getAllowedFiles() { return config('chatify.attachments.allowed_files'); } /** * Returns an array contains messenger's colors * * @return array */ public function getMessengerColors() { return config('chatify.colors'); } /** * Trigger an event using Pusher * * @param string $channel * @param string $event * @param array $data * @return void */ public function push($channel, $event, $data) { return $this->pusher->trigger($channel, $event, $data); } /** * Authentication for pusher * * @param User $requestUser * @param User $authUser * @param string $channelName * @param string $socket_id * @param array $data * @return void */ public function pusherAuth($requestUser, $authUser, $channelName, $socket_id) { // Auth data $authData = json_encode([ 'user_id' => $authUser->id, 'user_info' => [ 'name' => $authUser->name ] ]); // check if user authenticated if (Auth::check()) { if($requestUser->id == $authUser->id){ return $this->pusher->socket_auth( $channelName, $socket_id, $authData ); } // if not authorized return response()->json(['message'=>'Unauthorized'], 401); } // if not authenticated return response()->json(['message'=>'Not authenticated'], 403); } /** * Fetch message by id and return the message card * view as a response. * * @param int $id * @return array */ public function fetchMessage($id, $index = null) { $attachment = null; $attachment_type = null; $attachment_title = null; $msg = Message::where('id', $id)->first(); if(!$msg){ return []; } if (isset($msg->attachment)) { $attachmentOBJ = json_decode($msg->attachment); $attachment = $attachmentOBJ->new_name; $attachment_title = htmlentities(trim($attachmentOBJ->old_name), ENT_QUOTES, 'UTF-8'); $ext = pathinfo($attachment, PATHINFO_EXTENSION); $attachment_type = in_array($ext, $this->getAllowedImages()) ? 'image' : 'file'; } return [ 'index' => $index, 'id' => $msg->id, 'from_id' => $msg->from_id, 'to_id' => $msg->to_id, 'message' => $msg->body, 'attachment' => [$attachment, $attachment_title, $attachment_type], 'time' => $msg->created_at->diffForHumans(), 'fullTime' => $msg->created_at, 'viewType' => ($msg->from_id == Auth::user()->id) ? 'sender' : 'default', 'seen' => $msg->seen, ]; } /** * Return a message card with the given data. * * @param array $data * @param string $viewType * @return string */ public function messageCard($data, $viewType = null) { if (!$data) { return ''; } $data['viewType'] = ($viewType) ? $viewType : $data['viewType']; return view('Chatify::layouts.messageCard', $data)->render(); } /** * Default fetch messages query between a Sender and Receiver. * * @param int $user_id * @return Message|\Illuminate\Database\Eloquent\Builder */ public function fetchMessagesQuery($user_id) { return Message::where('from_id', Auth::user()->id)->where('to_id', $user_id) ->orWhere('from_id', $user_id)->where('to_id', Auth::user()->id); } /** * create a new message to database * * @param array $data * @return void */ public function newMessage($data) { $message = new Message(); $message->id = $data['id']; $message->type = $data['type']; $message->from_id = $data['from_id']; $message->to_id = $data['to_id']; $message->body = $data['body']; $message->attachment = $data['attachment']; $message->save(); } /** * Make messages between the sender [Auth user] and * the receiver [User id] as seen. * * @param int $user_id * @return bool */ public function makeSeen($user_id) { Message::Where('from_id', $user_id) ->where('to_id', Auth::user()->id) ->where('seen', 0) ->update(['seen' => 1]); return 1; } /** * Get last message for a specific user * * @param int $user_id * @return Message|Collection|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null */ public function getLastMessageQuery($user_id) { return $this->fetchMessagesQuery($user_id)->latest()->first(); } /** * Count Unseen messages * * @param int $user_id * @return Collection */ public function countUnseenMessages($user_id) { return Message::where('from_id', $user_id)->where('to_id', Auth::user()->id)->where('seen', 0)->count(); } /** * Get user list's item data [Contact Itme] * (e.g. User data, Last message, Unseen Counter...) * * @param int $messenger_id * @param Collection $user * @return string */ public function getContactItem($user) { // get last message $lastMessage = $this->getLastMessageQuery($user->id); // Get Unseen messages counter $unseenCounter = $this->countUnseenMessages($user->id); return view('Chatify::layouts.listItem', [ 'get' => 'users', 'user' => $this->getUserWithAvatar($user), 'lastMessage' => $lastMessage, 'unseenCounter' => $unseenCounter, ])->render(); } /** * Get user with avatar (formatted). * * @param Collection $user * @return Collection */ public function getUserWithAvatar($user) { if ($user->avatar == 'avatar.png' && config('chatify.gravatar.enabled')) { $imageSize = config('chatify.gravatar.image_size'); $imageset = config('chatify.gravatar.imageset'); $user->avatar = 'https://www.gravatar.com/avatar/' . md5(strtolower(trim($user->email))) . '?s=' . $imageSize . '&d=' . $imageset; } else { $user->avatar = self::getUserAvatarUrl($user->avatar); } return $user; } /** * Check if a user in the favorite list * * @param int $user_id * @return boolean */ public function inFavorite($user_id) { return Favorite::where('user_id', Auth::user()->id) ->where('favorite_id', $user_id)->count() > 0 ? true : false; } /** * Make user in favorite list * * @param int $user_id * @param int $star * @return boolean */ public function makeInFavorite($user_id, $action) { if ($action > 0) { // Star $star = new Favorite(); $star->id = rand(9, 99999999); $star->user_id = Auth::user()->id; $star->favorite_id = $user_id; $star->save(); return $star ? true : false; } else { // UnStar $star = Favorite::where('user_id', Auth::user()->id)->where('favorite_id', $user_id)->delete(); return $star ? true : false; } } /** * Get shared photos of the conversation * * @param int $user_id * @return array */ public function getSharedPhotos($user_id) { $images = array(); // Default // Get messages $msgs = $this->fetchMessagesQuery($user_id)->orderBy('created_at', 'DESC'); if ($msgs->count() > 0) { foreach ($msgs->get() as $msg) { // If message has attachment if ($msg->attachment) { $attachment = json_decode($msg->attachment); // determine the type of the attachment in_array(pathinfo($attachment->new_name, PATHINFO_EXTENSION), $this->getAllowedImages()) ? array_push($images, $attachment->new_name) : ''; } } } return $images; } /** * Delete Conversation * * @param int $user_id * @return boolean */ public function deleteConversation($user_id) { try { foreach ($this->fetchMessagesQuery($user_id)->get() as $msg) { // delete file attached if exist if (isset($msg->attachment)) { $path = config('chatify.attachments.folder').'/'.json_decode($msg->attachment)->new_name; if (self::storage()->exists($path)) { self::storage()->delete($path); } } // delete from database $msg->delete(); } return 1; } catch (Exception $e) { return 0; } } /** * Delete message by ID * * @param int $id * @return boolean */ public function deleteMessage($id) { try { $msg = Message::findOrFail($id); if ($msg->from_id == auth()->id()) { // delete file attached if exist if (isset($msg->attachment)) { $path = config('chatify.attachments.folder') . '/' . json_decode($msg->attachment)->new_name; if (self::storage()->exists($path)) { self::storage()->delete($path); } } // delete from database $msg->delete(); } else { return 0; } return 1; } catch (Exception $e) { return 0; } } /** * Return a storage instance with disk name specified in the config. * */ public function storage() { return Storage::disk(config('chatify.storage_disk_name')); } /** * Get user avatar url. * * @param string $user_avatar_name * @return string */ public function getUserAvatarUrl($user_avatar_name) { if(!empty($user_avatar_name)){ return self::storage()->url(config('chatify.user_avatar.folder') . '/' . $user_avatar_name); } } /** * Get attachment's url. * * @param string $attachment_name * @return string */ public function getAttachmentUrl($attachment_name) { return self::storage()->url(config('chatify.attachments.folder') . '/' . $attachment_name); } } PK‹ZR. chatify/README.mdnu[ [![Latest Stable Version](https://poser.pugx.org/munafio/chatify/v/stable)](https://packagist.org/packages/munafio/chatify) [![Total Downloads](https://poser.pugx.org/munafio/chatify/downloads)](https://packagist.org/packages/munafio/chatify) [![License](https://poser.pugx.org/munafio/chatify/license)](https://packagist.org/packages/munafio/chatify) # Chatify Laravel Package A Laravel package helps you add a complete real-time messaging system to your new / existing application with only one command. # Need a Help? 📣 I have created a server for **Chatify** on `Discord` to let you **up-to-date** and help you as much as I can .. so now you can chat with me, get a help, showcases, and most importantly to get announcements and updates about **Chatify**. So, [join now](https://discord.gg/RaxyKVykYJ) and keep updated. # Features - Users chat system. - Real-time contacts list updates. - Favorites contacts list (Like stories style) and add to favorite button. - Saved Messages to save your messages online like Telegram messenger app. - Search functionality. - Contact item's last message indicator (e.g. You: ....). - Real-time user's active status. - Real-time typing indicator. - Real-time seen messages indicator. - Real-time internet connection status. - Upload attachments (Photo/File). - Shared photos, delete conversation.. (User's info right side). - Responsive design with all devices. - User settings and chat customization : user's profile photo, dark mode and chat color. with simple and wonderful UI design. # Demo - [Live Demo](http://chatify-demo.munafio.com/) `Note: since this is a demo link, your uploaded files may be deleted after a while .. you can try then to install the (Demo app) below in your machine.` Register a new user or login using the credentials below : ``` email : demo@e.com password : demo1234 ``` - Demo app - [Click Here](https://github.com/munafio/chatify-demo). - Demo video on YouTube - [Click Here](https://youtu.be/gjo74FUJJPI) # Official Documentation Documentation for Chatify can be found [here](https://chatify.munafio.com) # Change log [CHANGELOG.md](https://github.com/munafio/chatify/blob/master/CHANGELOG.md) # Authors - [Munaf A. Mahdi](https://www.munafio.com/p/about-me.html) # License Chatify is licensed under the [MIT license](https://choosealicense.com/licenses/mit/) PK‹ZY2Fwwchatify/composer.locknu[{ "_readme": [ "This file locks the dependencies of your project to a known state", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], "content-hash": "f2911e63da707332d670e75ec415c343", "packages": [ { "name": "guzzlehttp/guzzle", "version": "7.4.5", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", "reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1dd98b0564cb3f6bd16ce683cb755f94c10fbd82", "reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^1.5", "guzzlehttp/psr7": "^1.9 || ^2.4", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" }, "provide": { "psr/http-client-implementation": "1.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.4.1", "ext-curl": "*", "php-http/client-integration-tests": "^3.0", "phpunit/phpunit": "^8.5.5 || ^9.3.5", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { "ext-curl": "Required for CURL handler support", "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { "dev-master": "7.4-dev" } }, "autoload": { "files": [ "src/functions_include.php" ], "psr-4": { "GuzzleHttp\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Graham Campbell", "email": "hello@gjcampbell.co.uk", "homepage": "https://github.com/GrahamCampbell" }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, { "name": "Jeremy Lindblom", "email": "jeremeamia@gmail.com", "homepage": "https://github.com/jeremeamia" }, { "name": "George Mponos", "email": "gmponos@gmail.com", "homepage": "https://github.com/gmponos" }, { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", "homepage": "https://github.com/Nyholm" }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", "homepage": "https://github.com/sagikazarmark" }, { "name": "Tobias Schultze", "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" } ], "description": "Guzzle is a PHP HTTP client library", "keywords": [ "client", "curl", "framework", "http", "http client", "psr-18", "psr-7", "rest", "web service" ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", "source": "https://github.com/guzzle/guzzle/tree/7.4.5" }, "funding": [ { "url": "https://github.com/GrahamCampbell", "type": "github" }, { "url": "https://github.com/Nyholm", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", "type": "tidelift" } ], "time": "2022-06-20T22:16:13+00:00" }, { "name": "guzzlehttp/promises", "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", "shasum": "" }, "require": { "php": ">=5.5" }, "require-dev": { "symfony/phpunit-bridge": "^4.4 || ^5.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.5-dev" } }, "autoload": { "files": [ "src/functions_include.php" ], "psr-4": { "GuzzleHttp\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Graham Campbell", "email": "hello@gjcampbell.co.uk", "homepage": "https://github.com/GrahamCampbell" }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", "homepage": "https://github.com/Nyholm" }, { "name": "Tobias Schultze", "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" } ], "description": "Guzzle promises library", "keywords": [ "promise" ], "support": { "issues": "https://github.com/guzzle/promises/issues", "source": "https://github.com/guzzle/promises/tree/1.5.1" }, "funding": [ { "url": "https://github.com/GrahamCampbell", "type": "github" }, { "url": "https://github.com/Nyholm", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", "type": "tidelift" } ], "time": "2021-10-22T20:56:57+00:00" }, { "name": "guzzlehttp/psr7", "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", "reference": "13388f00956b1503577598873fffb5ae994b5737" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/psr7/zipball/13388f00956b1503577598873fffb5ae994b5737", "reference": "13388f00956b1503577598873fffb5ae994b5737", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0", "ralouphie/getallheaders": "^3.0" }, "provide": { "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.4.1", "http-interop/http-factory-tests": "^0.9", "phpunit/phpunit": "^8.5.8 || ^9.3.10" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.4-dev" } }, "autoload": { "psr-4": { "GuzzleHttp\\Psr7\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Graham Campbell", "email": "hello@gjcampbell.co.uk", "homepage": "https://github.com/GrahamCampbell" }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, { "name": "George Mponos", "email": "gmponos@gmail.com", "homepage": "https://github.com/gmponos" }, { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", "homepage": "https://github.com/Nyholm" }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", "homepage": "https://github.com/sagikazarmark" }, { "name": "Tobias Schultze", "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", "homepage": "https://sagikazarmark.hu" } ], "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ "http", "message", "psr-7", "request", "response", "stream", "uri", "url" ], "support": { "issues": "https://github.com/guzzle/psr7/issues", "source": "https://github.com/guzzle/psr7/tree/2.4.0" }, "funding": [ { "url": "https://github.com/GrahamCampbell", "type": "github" }, { "url": "https://github.com/Nyholm", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", "type": "tidelift" } ], "time": "2022-06-20T21:43:11+00:00" }, { "name": "paragonie/random_compat", "version": "v9.99.100", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", "shasum": "" }, "require": { "php": ">= 7" }, "require-dev": { "phpunit/phpunit": "4.*|5.*", "vimeo/psalm": "^1" }, "suggest": { "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, "type": "library", "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Paragon Initiative Enterprises", "email": "security@paragonie.com", "homepage": "https://paragonie.com" } ], "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ "csprng", "polyfill", "pseudorandom", "random" ], "support": { "email": "info@paragonie.com", "issues": "https://github.com/paragonie/random_compat/issues", "source": "https://github.com/paragonie/random_compat" }, "time": "2020-10-15T08:29:30+00:00" }, { "name": "paragonie/sodium_compat", "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/paragonie/sodium_compat.git", "reference": "c59cac21abbcc0df06a3dd18076450ea4797b321" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/c59cac21abbcc0df06a3dd18076450ea4797b321", "reference": "c59cac21abbcc0df06a3dd18076450ea4797b321", "shasum": "" }, "require": { "paragonie/random_compat": ">=1", "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" }, "require-dev": { "phpunit/phpunit": "^3|^4|^5|^6|^7|^8|^9" }, "suggest": { "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." }, "type": "library", "autoload": { "files": [ "autoload.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "ISC" ], "authors": [ { "name": "Paragon Initiative Enterprises", "email": "security@paragonie.com" }, { "name": "Frank Denis", "email": "jedisct1@pureftpd.org" } ], "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", "keywords": [ "Authentication", "BLAKE2b", "ChaCha20", "ChaCha20-Poly1305", "Chapoly", "Curve25519", "Ed25519", "EdDSA", "Edwards-curve Digital Signature Algorithm", "Elliptic Curve Diffie-Hellman", "Poly1305", "Pure-PHP cryptography", "RFC 7748", "RFC 8032", "Salpoly", "Salsa20", "X25519", "XChaCha20-Poly1305", "XSalsa20-Poly1305", "Xchacha20", "Xsalsa20", "aead", "cryptography", "ecdh", "elliptic curve", "elliptic curve cryptography", "encryption", "libsodium", "php", "public-key cryptography", "secret-key cryptography", "side-channel resistant" ], "support": { "issues": "https://github.com/paragonie/sodium_compat/issues", "source": "https://github.com/paragonie/sodium_compat/tree/v1.17.0" }, "time": "2021-08-10T02:43:50+00:00" }, { "name": "psr/http-client", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", "shasum": "" }, "require": { "php": "^7.0 || ^8.0", "psr/http-message": "^1.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Http\\Client\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common interface for HTTP clients", "homepage": "https://github.com/php-fig/http-client", "keywords": [ "http", "http-client", "psr", "psr-18" ], "support": { "source": "https://github.com/php-fig/http-client/tree/master" }, "time": "2020-06-29T06:28:15+00:00" }, { "name": "psr/http-factory", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", "shasum": "" }, "require": { "php": ">=7.0.0", "psr/http-message": "^1.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", "message", "psr", "psr-17", "psr-7", "request", "response" ], "support": { "source": "https://github.com/php-fig/http-factory/tree/master" }, "time": "2019-04-30T12:38:16+00:00" }, { "name": "psr/http-message", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", "homepage": "https://github.com/php-fig/http-message", "keywords": [ "http", "http-message", "psr", "psr-7", "request", "response" ], "support": { "source": "https://github.com/php-fig/http-message/tree/master" }, "time": "2016-08-06T14:39:51+00:00" }, { "name": "psr/log", "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", "reference": "d49695b909c3b7628b6289db5479a1c204601f11" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", "reference": "d49695b909c3b7628b6289db5479a1c204601f11", "shasum": "" }, "require": { "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.1.x-dev" } }, "autoload": { "psr-4": { "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for logging libraries", "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ], "support": { "source": "https://github.com/php-fig/log/tree/1.1.4" }, "time": "2021-05-03T11:20:27+00:00" }, { "name": "pusher/pusher-php-server", "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/pusher/pusher-http-php.git", "reference": "7757a09209d20ff95b077ae48dc25f49a6ad94a2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/pusher/pusher-http-php/zipball/7757a09209d20ff95b077ae48dc25f49a6ad94a2", "reference": "7757a09209d20ff95b077ae48dc25f49a6ad94a2", "shasum": "" }, "require": { "ext-curl": "*", "ext-json": "*", "guzzlehttp/guzzle": "^7.2", "paragonie/sodium_compat": "^1.6", "php": "^7.3|^8.0", "psr/log": "^1.0" }, "require-dev": { "overtrue/phplint": "^2.3", "phpunit/phpunit": "^8.5|^9.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "5.0-dev" } }, "autoload": { "psr-4": { "Pusher\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Library for interacting with the Pusher REST API", "keywords": [ "events", "messaging", "php-pusher-server", "publish", "push", "pusher", "real time", "real-time", "realtime", "rest", "trigger" ], "support": { "issues": "https://github.com/pusher/pusher-http-php/issues", "source": "https://github.com/pusher/pusher-http-php/tree/7.0.1" }, "time": "2021-05-26T11:22:28+00:00" }, { "name": "ralouphie/getallheaders", "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/ralouphie/getallheaders.git", "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { "php": ">=5.6" }, "require-dev": { "php-coveralls/php-coveralls": "^2.1", "phpunit/phpunit": "^5 || ^6.5" }, "type": "library", "autoload": { "files": [ "src/getallheaders.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Ralph Khattar", "email": "ralph.khattar@gmail.com" } ], "description": "A polyfill for getallheaders.", "support": { "issues": "https://github.com/ralouphie/getallheaders/issues", "source": "https://github.com/ralouphie/getallheaders/tree/develop" }, "time": "2019-03-08T08:55:37+00:00" }, { "name": "symfony/deprecation-contracts", "version": "v2.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", "shasum": "" }, "require": { "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", "url": "https://github.com/symfony/contracts" } }, "autoload": { "files": [ "function.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "time": "2022-01-02T09:53:40+00:00" } ], "packages-dev": [], "aliases": [], "minimum-stability": "dev", "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": [], "platform-dev": [], "plugin-api-version": "2.3.0" } PK‹Z 9chatify/CHANGELOG.mdnu[# Change log All notable changes to this project will be documented in this file. ## v1.5.4 (2022-12-05) ### Fixed - Channels auth secutiy issue #29 ## v1.5.3 (2022-12-04) ### Fixed - Channels Secutiy issue #29 ## v1.5.2 (2022-07-08) ### Fixed - MessageCard & fetchMessage methods@`ChatifyMessenger.php` fallback. ## v1.5.1 (2022-06-09) ### Fixed - Sync the `sending a message form`'s allowed files/images with the `config` file (Update sendForm.blade.php [#190](https://github.com/munafio/chatify/pull/190)) ## v1.5.0 (2022-06-08) ### Added - Page/Document visibility Support which improves (seen) feature #183 ### Fixed - fix: case insensitive file upload extension check #182 ## v1.4.0 (2022-05-02) ### Added - [Gravatar](https:://gravatar.com) support (optional, can be changed at config/chatify.php). - Delete Message by ID. - Laravel's Storage disk now supported and can be changed from the config. ### Changed - File upload (user avatar & attachments) `allowed files` and `max size` now can be changed from one place which is (config/chatify.php). ### Fixed - Bugs and UI/UX design fixes/improvements. ## v1.3.4 (2022-02-04) ### Fixed - Fixed Installing errors on the migrations step. #163 ## v1.3.3 (2022-01-10) ### Fixed - Fixed file upload size limit error message rephrase #160. ### Changed - Files max upload size changed & added to the config to be customizable. - Changed `Messenger colors` logic to be more flexible and customizable. - Migration files renamed, file date automatically will be changed to the publish/install date. ## v1.3.2 (2022-01-07) ### Fixed - Fixed CSS issue in FF with the contact list #157. - Correct misspelt of `updateContactItem` method (typo error) #159. ## v1.3.1 (2021-12-23) ### Fixed - Fixed migration's rollback, (ch\_) prefix added. ## v1.3.0 (2021-11-30) ### Fixed - UI/Ux fixes & improvements. - Backend fixes & improvements. ### Added - Messages, Contacts, and Search pagination. - API routes. ## v1.2.5 (2021-08-18) ### Fixed - Fixed a security issue on uploaded file-name, which is vulnerable with XSS. ## v1.2.4 (2021-07-15) ### Fixed - README updates. - Install Command fixes & improvements. - Contact list visible onLoad. - Settings’ modal responsive design. ### Added - UPGRADE.md added. - Publish command added. - Package.json additions & modifications. ## v1.2.3 - (2021-06-19) ### Fixed - XSS issue on inputs. - UI/UX fixes & improvements. - Send message fixes (UI & backend). - Update Profile Settings (upload file & error handling ….). - Shared photos not working issue. - Typo error fixes (Your `contatc` list is empty). - Rolling back migrations added. - Get Last message `orderBy` query duplication. ## v1.2.2 - (2021-06-01) ### Fixed - Migrate to database command removed. - Publishable asset `assets` avatar config issue. - Pusher encryption key option removed. - Settings button on click not working issue. ## v1.2.1 - (2021-05-30) ### Fixed - Publishable asset `assets`. ## v1.2.0 - (2021-05-30) ### FIxed - Security issues. - UI/UX issues. - Route [home] not defiend. - `$msg->attachment` issue #9. - Delete conversation issue #89. ### Added - Console commands. - `Models` added to assets to be published. - Laravel 8+ support. ### Changed - Project structure. - composer updated `pusher/pusher-php-server` to v^7.0. - Models & Migrations' tables names changed (added `ch` prefix to avoid duplication) solves issue #68. - Models changed to (`ChMessage`, `ChFavorite`) - Migrations' tables names (`ch_messages`, `ch_favorites`) - Configuration file `config/chatify.php`. ## v1.0.1 - (2020-09-30) ### FIxed - Security issues. ### Added - Routes' controllers namespace included in the configuration. ## v1.0.0 - (2019-12-30) - First release PK‹Zz=chatify/.gitignorenu[ /vendor composer.lock PK‹Z XIaachatify/composer.jsonnu[PK‹Z0hchatify/UPGRADE.mdnu[PK‹Z64  "wchatify/src/assets/imgs/avatar.pngnu[PK‹Z (* %chatify/src/assets/css/light.mode.cssnu[PK‹Z: $chatify/src/assets/css/dark.mode.cssnu[PK‹ZcBB %chatify/src/assets/css/style.cssnu[PK‹ZSwDwD)hchatify/src/assets/js/font.awesome.min.jsnu[PK‹Zmf?""chatify/src/assets/js/code.jsnu[PK‹ZK=M#M#!S^chatify/src/assets/js/autosize.jsnu[PK‹Z&ks(chatify/src/views/layouts/info.blade.phpnu[PK‹ZhVS S ,]chatify/src/views/layouts/listItem.blade.phpnu[PK‹ZU- chatify/src/views/layouts/headLinks.blade.phpnu[PK‹ZyB,chatify/src/views/layouts/sendForm.blade.phpnu[PK‹Z㯶/chatify/src/views/layouts/footerLinks.blade.phpnu[PK‹ZFO++,chatify/src/views/layouts/favorite.blade.phpnu[PK‹Z0m /chatify/src/views/layouts/messageCard.blade.phpnu[PK‹ZT>2chatify/src/views/layouts/messengerColor.blade.phpnu[PK‹ZǍUU* chatify/src/views/layouts/modals.blade.phpnu[PK‹ZЦbb%chatify/src/views/pages/app.blade.phpnu[PK‹ZڇD}pp schatify/src/Models/ChMessage.phpnu[PK‹Z@qq!3chatify/src/Models/ChFavorite.phpnu[PK‹Z; chatify/src/config/chatify.phpnu[PK‹Z>227Mchatify/src/Http/Controllers/Api/MessagesController.phpnu[PK‹Z~$aAA3chatify/src/Http/Controllers/MessagesController.phpnu[PK‹Z5,O"Zchatify/src/database/migrations/2022_01_10_99999_add_active_status_to_users.phpnu[PK‹Z<'!K]chatify/src/database/migrations/2022_01_10_99999_create_favorites_table.phpnu[PK‹Z;0;K achatify/src/database/migrations/2022_01_10_99999_add_dark_mode_to_users.phpnu[PK‹Z3K%%Qdchatify/src/database/migrations/2022_01_10_99999_add_messenger_color_to_users.phpnu[PK‹ZŕJ3hchatify/src/database/migrations/2022_01_10_99999_create_messages_table.phpnu[PK‹ZHBlchatify/src/database/migrations/2022_01_10_99999_add_avatar_to_users.phpnu[PK‹Zy&ochatify/src/ChatifyServiceProvider.phpnu[PK‹ZXmZchatify/src/routes/api.phpnu[PK‹Z<}kchatify/src/routes/web.phpnu[PK‹Z#N&chatify/src/Console/InstallCommand.phpnu[PK‹Z&chatify/src/Console/PublishCommand.phpnu[PK‹Zn(chatify/src/Facades/ChatifyMessenger.phpnu[PK‹Zӭ// "chatify/src/ChatifyMessenger.phpnu[PK‹ZR. =chatify/README.mdnu[PK‹ZY2Fwwchatify/composer.locknu[PK‹Z 9cchatify/CHANGELOG.mdnu[PK‹Zz=schatify/.gitignorenu[PK))`s