From 4c2f7a0ede1dd16412346bfa426309816ddb268a Mon Sep 17 00:00:00 2001 From: Wilkins Date: Sat, 10 Apr 2021 21:04:02 +0200 Subject: [PATCH 1/2] Adding Messages features - adding a Messages history page into the user menu - adding message history into the trade deals, to remind user of exchanges with traders --- app/Http/Controllers/MessageController.php | 10 + app/Http/Controllers/TradeController.php | 18 +- app/Message.php | 12 +- app/Queries/MessagesQuery.php | 63 +++++ public/css/app.css | 23 ++ public/js/app.js | 256 +++++++++++++----- public/mix-manifest.json | 4 +- .../js/controllers/messages_controller.js | 10 + resources/sass/app.scss | 26 ++ resources/views/layouts/app.blade.php | 3 + .../views/messages/_messagesList.blade.php | 60 ++++ resources/views/messages/history.blade.php | 21 ++ .../views/trade/_possible_deal.blade.php | 16 ++ routes/web.php | 1 + 14 files changed, 444 insertions(+), 79 deletions(-) create mode 100644 app/Queries/MessagesQuery.php create mode 100644 resources/js/controllers/messages_controller.js create mode 100644 resources/views/messages/_messagesList.blade.php create mode 100644 resources/views/messages/history.blade.php diff --git a/app/Http/Controllers/MessageController.php b/app/Http/Controllers/MessageController.php index 5112445..1fb7b9f 100644 --- a/app/Http/Controllers/MessageController.php +++ b/app/Http/Controllers/MessageController.php @@ -7,6 +7,8 @@ use App\Message; use App\User; use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Facades\Auth; +use App\Queries\MessagesQuery; class MessageController extends Controller { @@ -23,4 +25,12 @@ public function store(MessageRequest $request) return response()->json(null, 204); } + + public function history(MessagesQuery $query) + { + $messages = $query->getMessagesWithLoggedInUser(); + $loggedInUser = Auth::user(); + + return view('messages.history', compact('messages', 'loggedInUser')); + } } diff --git a/app/Http/Controllers/TradeController.php b/app/Http/Controllers/TradeController.php index c19af78..150e5ef 100644 --- a/app/Http/Controllers/TradeController.php +++ b/app/Http/Controllers/TradeController.php @@ -3,34 +3,46 @@ namespace App\Http\Controllers; use App\Queries\TradingUsersQuery; +use App\Queries\MessagesQuery; use App\User; class TradeController extends Controller { - public function index(TradingUsersQuery $query) + public function index(TradingUsersQuery $query, MessagesQuery $mQuery) { /** @var User $loggedUser */ $loggedUser = auth()->user(); $users = $query->fetchAll($loggedUser); + $this->feedMessages($users, $mQuery); + return view('trade.index', compact('users')); } - public function senders(int $elephpantId, TradingUsersQuery $query) + public function senders(int $elephpantId, TradingUsersQuery $query, MessagesQuery $mQuery) { /** @var User $loggedUser */ $loggedUser = auth()->user(); $users = $query->fetchAllOnlyIfHeHasElephpant($loggedUser, $elephpantId); + $this->feedMessages($users, $mQuery); return view('trade.index', compact('users')); } - public function receivers(int $elephpantId, TradingUsersQuery $query) + public function receivers(int $elephpantId, TradingUsersQuery $query, MessagesQuery $mQuery) { /** @var User $loggedUser */ $loggedUser = auth()->user(); $users = $query->fetchAllWhoLackElephpant($loggedUser, $elephpantId); + $this->feedMessages($users, $mQuery); return view('trade.index', compact('users')); } + + private function feedMessages($users, MessagesQuery $mQuery) + { + foreach ($users as $user) { + $user->messages = $mQuery->getMessagesWithLoggedInUserAndSomeoneElse($user->id); + } + } } diff --git a/app/Message.php b/app/Message.php index 2c77ae3..f56a4a8 100644 --- a/app/Message.php +++ b/app/Message.php @@ -6,5 +6,15 @@ class Message extends Model { - protected $fillable = ['receiver_id', 'message']; + protected $fillable = ['receiver_id', 'message', 'sender_id', 'created_at']; + + public function sender() + { + return $this->belongsTo(User::class, 'sender_id'); + } + + public function receiver() + { + return $this->belongsTo(User::class, 'receiver_id'); + } } diff --git a/app/Queries/MessagesQuery.php b/app/Queries/MessagesQuery.php new file mode 100644 index 0000000..2b94789 --- /dev/null +++ b/app/Queries/MessagesQuery.php @@ -0,0 +1,63 @@ +id(); + + return $this->getAllQuery() + ->where(function ($query) use ($authUserId) { + $query->where('m.sender_id', '=', $authUserId) + ->orWhere('m.receiver_id', '=', $authUserId); + }) + ->orderBy('m.created_at', 'desc') + ->get(); + } + + public function getMessagesWithLoggedInUserAndSomeoneElse(int $userId): Collection + { + $authUserId = auth()->id(); + + return $this->getAllQuery() + ->where(function ($query) use ($authUserId, $userId) { + $query->where('m.sender_id', '=', $authUserId) + ->orWhere('m.sender_id', '=', $userId); + }) + ->where(function ($query) use ($authUserId, $userId) { + $query->where('m.receiver_id', '=', $userId) + ->orWhere('m.receiver_id', '=', $authUserId); + }) + ->orderBy('m.created_at', 'asc') + ->get(); + } + + public function getAllQuery() + { + return Message::query() + ->select([ + 'm.message', + 'm.created_at', + 'sender.id as sender_id', + 'receiver.id as receiver_id', + ]) + ->with('sender') + ->with('receiver') + ->from('messages as m') + ->join('users as sender', 'm.sender_id', '=', 'sender.id') + ->join('users as receiver', 'm.receiver_id', '=', 'receiver.id'); + } + + public function fetchAll(): Collection + { + return $this->getAllQuery() + ->get(); + } +} diff --git a/public/css/app.css b/public/css/app.css index e81b1ba..45b395c 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -13038,3 +13038,26 @@ body { font-weight: bold; } +.user-message-container .user-message-text { + background-color: #3e86f9; + color: white; + padding: 10px; + border-radius: 1em; + max-width: 16em; +} + +.user-message-container .user-message-timestamp { + background-color: #ccc; + padding: 0.1em 0.5em; + border-radius: 0.5em; + display: inline-block; +} + +.messages-exchanges-container .messages-exchanges-list { + display: none; +} + +.messages-exchanges-container .messages-exchanges-infotext { + line-height: 3em; +} + diff --git a/public/js/app.js b/public/js/app.js index e35ccf8..8d9d5cf 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -2279,6 +2279,7 @@ module.exports = __webpack_require__(/*! ./lib/axios */ "./node_modules/axios/li var utils = __webpack_require__(/*! ./../utils */ "./node_modules/axios/lib/utils.js"); var settle = __webpack_require__(/*! ./../core/settle */ "./node_modules/axios/lib/core/settle.js"); +var cookies = __webpack_require__(/*! ./../helpers/cookies */ "./node_modules/axios/lib/helpers/cookies.js"); var buildURL = __webpack_require__(/*! ./../helpers/buildURL */ "./node_modules/axios/lib/helpers/buildURL.js"); var buildFullPath = __webpack_require__(/*! ../core/buildFullPath */ "./node_modules/axios/lib/core/buildFullPath.js"); var parseHeaders = __webpack_require__(/*! ./../helpers/parseHeaders */ "./node_modules/axios/lib/helpers/parseHeaders.js"); @@ -2299,7 +2300,7 @@ module.exports = function xhrAdapter(config) { // HTTP basic authentication if (config.auth) { var username = config.auth.username || ''; - var password = config.auth.password || ''; + var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : ''; requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password); } @@ -2380,8 +2381,6 @@ module.exports = function xhrAdapter(config) { // This is only done if running in a standard browser environment. // Specifically not if we're in a web worker, or react-native. if (utils.isStandardBrowserEnv()) { - var cookies = __webpack_require__(/*! ./../helpers/cookies */ "./node_modules/axios/lib/helpers/cookies.js"); - // Add xsrf header var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ? cookies.read(config.xsrfCookieName) : @@ -2447,7 +2446,7 @@ module.exports = function xhrAdapter(config) { }); } - if (requestData === undefined) { + if (!requestData) { requestData = null; } @@ -2516,6 +2515,9 @@ axios.all = function all(promises) { }; axios.spread = __webpack_require__(/*! ./helpers/spread */ "./node_modules/axios/lib/helpers/spread.js"); +// Expose isAxiosError +axios.isAxiosError = __webpack_require__(/*! ./helpers/isAxiosError */ "./node_modules/axios/lib/helpers/isAxiosError.js"); + module.exports = axios; // Allow use of default import syntax in TypeScript @@ -2724,9 +2726,10 @@ Axios.prototype.getUri = function getUri(config) { utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { /*eslint func-names:0*/ Axios.prototype[method] = function(url, config) { - return this.request(utils.merge(config || {}, { + return this.request(mergeConfig(config || {}, { method: method, - url: url + url: url, + data: (config || {}).data })); }; }); @@ -2734,7 +2737,7 @@ utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { /*eslint func-names:0*/ Axios.prototype[method] = function(url, data, config) { - return this.request(utils.merge(config || {}, { + return this.request(mergeConfig(config || {}, { method: method, url: url, data: data @@ -2994,7 +2997,7 @@ module.exports = function enhanceError(error, config, code, request, response) { error.response = response; error.isAxiosError = true; - error.toJSON = function() { + error.toJSON = function toJSON() { return { // Standard message: this.message, @@ -3043,59 +3046,73 @@ module.exports = function mergeConfig(config1, config2) { config2 = config2 || {}; var config = {}; - var valueFromConfig2Keys = ['url', 'method', 'params', 'data']; - var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy']; + var valueFromConfig2Keys = ['url', 'method', 'data']; + var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy', 'params']; var defaultToConfig2Keys = [ - 'baseURL', 'url', 'transformRequest', 'transformResponse', 'paramsSerializer', - 'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName', - 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', - 'maxContentLength', 'validateStatus', 'maxRedirects', 'httpAgent', - 'httpsAgent', 'cancelToken', 'socketPath' + 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer', + 'timeout', 'timeoutMessage', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName', + 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'decompress', + 'maxContentLength', 'maxBodyLength', 'maxRedirects', 'transport', 'httpAgent', + 'httpsAgent', 'cancelToken', 'socketPath', 'responseEncoding' ]; + var directMergeKeys = ['validateStatus']; + + function getMergedValue(target, source) { + if (utils.isPlainObject(target) && utils.isPlainObject(source)) { + return utils.merge(target, source); + } else if (utils.isPlainObject(source)) { + return utils.merge({}, source); + } else if (utils.isArray(source)) { + return source.slice(); + } + return source; + } + + function mergeDeepProperties(prop) { + if (!utils.isUndefined(config2[prop])) { + config[prop] = getMergedValue(config1[prop], config2[prop]); + } else if (!utils.isUndefined(config1[prop])) { + config[prop] = getMergedValue(undefined, config1[prop]); + } + } utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) { - if (typeof config2[prop] !== 'undefined') { - config[prop] = config2[prop]; + if (!utils.isUndefined(config2[prop])) { + config[prop] = getMergedValue(undefined, config2[prop]); } }); - utils.forEach(mergeDeepPropertiesKeys, function mergeDeepProperties(prop) { - if (utils.isObject(config2[prop])) { - config[prop] = utils.deepMerge(config1[prop], config2[prop]); - } else if (typeof config2[prop] !== 'undefined') { - config[prop] = config2[prop]; - } else if (utils.isObject(config1[prop])) { - config[prop] = utils.deepMerge(config1[prop]); - } else if (typeof config1[prop] !== 'undefined') { - config[prop] = config1[prop]; + utils.forEach(mergeDeepPropertiesKeys, mergeDeepProperties); + + utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) { + if (!utils.isUndefined(config2[prop])) { + config[prop] = getMergedValue(undefined, config2[prop]); + } else if (!utils.isUndefined(config1[prop])) { + config[prop] = getMergedValue(undefined, config1[prop]); } }); - utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) { - if (typeof config2[prop] !== 'undefined') { - config[prop] = config2[prop]; - } else if (typeof config1[prop] !== 'undefined') { - config[prop] = config1[prop]; + utils.forEach(directMergeKeys, function merge(prop) { + if (prop in config2) { + config[prop] = getMergedValue(config1[prop], config2[prop]); + } else if (prop in config1) { + config[prop] = getMergedValue(undefined, config1[prop]); } }); var axiosKeys = valueFromConfig2Keys .concat(mergeDeepPropertiesKeys) - .concat(defaultToConfig2Keys); + .concat(defaultToConfig2Keys) + .concat(directMergeKeys); var otherKeys = Object - .keys(config2) + .keys(config1) + .concat(Object.keys(config2)) .filter(function filterAxiosKeys(key) { return axiosKeys.indexOf(key) === -1; }); - utils.forEach(otherKeys, function otherKeysDefaultToConfig2(prop) { - if (typeof config2[prop] !== 'undefined') { - config[prop] = config2[prop]; - } else if (typeof config1[prop] !== 'undefined') { - config[prop] = config1[prop]; - } - }); + utils.forEach(otherKeys, mergeDeepProperties); return config; }; @@ -3124,7 +3141,7 @@ var createError = __webpack_require__(/*! ./createError */ "./node_modules/axios */ module.exports = function settle(resolve, reject, response) { var validateStatus = response.config.validateStatus; - if (!validateStatus || validateStatus(response.status)) { + if (!response.status || !validateStatus || validateStatus(response.status)) { resolve(response); } else { reject(createError( @@ -3256,6 +3273,7 @@ var defaults = { xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, + maxBodyLength: -1, validateStatus: function validateStatus(status) { return status >= 200 && status < 300; @@ -3319,7 +3337,6 @@ var utils = __webpack_require__(/*! ./../utils */ "./node_modules/axios/lib/util function encode(val) { return encodeURIComponent(val). - replace(/%40/gi, '@'). replace(/%3A/gi, ':'). replace(/%24/g, '$'). replace(/%2C/gi, ','). @@ -3503,6 +3520,29 @@ module.exports = function isAbsoluteURL(url) { }; +/***/ }), + +/***/ "./node_modules/axios/lib/helpers/isAxiosError.js": +/*!********************************************************!*\ + !*** ./node_modules/axios/lib/helpers/isAxiosError.js ***! + \********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Determines whether the payload is an error thrown by Axios + * + * @param {*} payload The value to test + * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false + */ +module.exports = function isAxiosError(payload) { + return (typeof payload === 'object') && (payload.isAxiosError === true); +}; + + /***/ }), /***/ "./node_modules/axios/lib/helpers/isURLSameOrigin.js": @@ -3828,6 +3868,21 @@ function isObject(val) { return val !== null && typeof val === 'object'; } +/** + * Determine if a value is a plain Object + * + * @param {Object} val The value to test + * @return {boolean} True if value is a plain Object, otherwise false + */ +function isPlainObject(val) { + if (toString.call(val) !== '[object Object]') { + return false; + } + + var prototype = Object.getPrototypeOf(val); + return prototype === null || prototype === Object.prototype; +} + /** * Determine if a value is a Date * @@ -3984,34 +4039,12 @@ function forEach(obj, fn) { function merge(/* obj1, obj2, obj3, ... */) { var result = {}; function assignValue(val, key) { - if (typeof result[key] === 'object' && typeof val === 'object') { + if (isPlainObject(result[key]) && isPlainObject(val)) { result[key] = merge(result[key], val); - } else { - result[key] = val; - } - } - - for (var i = 0, l = arguments.length; i < l; i++) { - forEach(arguments[i], assignValue); - } - return result; -} - -/** - * Function equal to merge with the difference being that no reference - * to original objects is kept. - * - * @see merge - * @param {Object} obj1 Object to merge - * @returns {Object} Result of all merge properties - */ -function deepMerge(/* obj1, obj2, obj3, ... */) { - var result = {}; - function assignValue(val, key) { - if (typeof result[key] === 'object' && typeof val === 'object') { - result[key] = deepMerge(result[key], val); - } else if (typeof val === 'object') { - result[key] = deepMerge({}, val); + } else if (isPlainObject(val)) { + result[key] = merge({}, val); + } else if (isArray(val)) { + result[key] = val.slice(); } else { result[key] = val; } @@ -4042,6 +4075,19 @@ function extend(a, b, thisArg) { return a; } +/** + * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) + * + * @param {string} content with BOM + * @return {string} content value without BOM + */ +function stripBOM(content) { + if (content.charCodeAt(0) === 0xFEFF) { + content = content.slice(1); + } + return content; +} + module.exports = { isArray: isArray, isArrayBuffer: isArrayBuffer, @@ -4051,6 +4097,7 @@ module.exports = { isString: isString, isNumber: isNumber, isObject: isObject, + isPlainObject: isPlainObject, isUndefined: isUndefined, isDate: isDate, isFile: isFile, @@ -4061,9 +4108,9 @@ module.exports = { isStandardBrowserEnv: isStandardBrowserEnv, forEach: forEach, merge: merge, - deepMerge: deepMerge, extend: extend, - trim: trim + trim: trim, + stripBOM: stripBOM }; @@ -39621,6 +39668,7 @@ if (token) { var map = { "./counter_controller.js": "./resources/js/controllers/counter_controller.js", + "./messages_controller.js": "./resources/js/controllers/messages_controller.js", "./ping_controller.js": "./resources/js/controllers/ping_controller.js" }; @@ -39748,6 +39796,68 @@ var _default = /*#__PURE__*/function (_Controller) { _default.targets = ["quantity"]; +/***/ }), + +/***/ "./resources/js/controllers/messages_controller.js": +/*!*********************************************************!*\ + !*** ./resources/js/controllers/messages_controller.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return _default; }); +/* harmony import */ var stimulus__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! stimulus */ "./node_modules/stimulus/index.js"); +function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + +function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } + +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } + +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } + + + +var _default = /*#__PURE__*/function (_Controller) { + _inherits(_default, _Controller); + + var _super = _createSuper(_default); + + function _default() { + _classCallCheck(this, _default); + + return _super.apply(this, arguments); + } + + _createClass(_default, [{ + key: "showExchanges", + value: function showExchanges() { + this.exchangesTarget.style.display = "block"; + this.buttonboxTarget.style.display = "none"; + } + }]); + + return _default; +}(stimulus__WEBPACK_IMPORTED_MODULE_0__["Controller"]); + +_default.targets = ["exchanges", "buttonbox"]; + + /***/ }), /***/ "./resources/js/controllers/ping_controller.js": @@ -39849,8 +39959,8 @@ _default.targets = ["message"]; /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -__webpack_require__(/*! /home/omarkdev/Projects/omarkdev/elephpant.me/resources/js/app.js */"./resources/js/app.js"); -module.exports = __webpack_require__(/*! /home/omarkdev/Projects/omarkdev/elephpant.me/resources/sass/app.scss */"./resources/sass/app.scss"); +__webpack_require__(/*! /home/web/elephpant.wilkins.fr/messages/resources/js/app.js */"./resources/js/app.js"); +module.exports = __webpack_require__(/*! /home/web/elephpant.wilkins.fr/messages/resources/sass/app.scss */"./resources/sass/app.scss"); /***/ }) diff --git a/public/mix-manifest.json b/public/mix-manifest.json index d90198e..92073e9 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,4 +1,4 @@ { - "/js/app.js": "/js/app.js?id=155230fdf6e1cc97cea6", - "/css/app.css": "/css/app.css?id=f5b55f0d77f938ac71c8" + "/js/app.js": "/js/app.js?id=b388c9cab65ec18c3726", + "/css/app.css": "/css/app.css?id=d7af5c083c9f8338d455" } diff --git a/resources/js/controllers/messages_controller.js b/resources/js/controllers/messages_controller.js new file mode 100644 index 0000000..1306689 --- /dev/null +++ b/resources/js/controllers/messages_controller.js @@ -0,0 +1,10 @@ +import {Controller} from "stimulus" + +export default class extends Controller { + static targets = ["exchanges", "buttonbox"]; + + showExchanges() { + this.exchangesTarget.style.display = "block"; + this.buttonboxTarget.style.display = "none"; + } +} diff --git a/resources/sass/app.scss b/resources/sass/app.scss index aa4d343..8e96ce0 100644 --- a/resources/sass/app.scss +++ b/resources/sass/app.scss @@ -62,3 +62,29 @@ body { font-weight: bold; } } + +.user-message-container { + .user-message-text { + background-color: #3e86f9; + color: white; + padding: 10px; + border-radius: 1em; + max-width: 16em; + } + + .user-message-timestamp { + background-color: #ccc; + padding: 0.1em 0.5em; + border-radius: 0.5em; + display: inline-block; + } +} + +.messages-exchanges-container { + .messages-exchanges-list { + display: none; + } + .messages-exchanges-infotext { + line-height: 3em; + } +} diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 71ba7ca..285a2f8 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -90,6 +90,9 @@ function gtag(){dataLayer.push(arguments);} {{ __('Profile') }} + + {{ __('Messages') }} + +
+ +
+ @else +
+ @endif +
+
+
+

+ {{ date('M jS, Y', strtotime($message->created_at)) }} +

+
+
+
+
+ @if (Auth::id() != $sender->id) +

+ {!! nl2br($message->message) !!} +

+ @endif +
+
+ @if (Auth::id() == $sender->id) +

+ {!! nl2br($message->message) !!} +

+ @endif +
+
+
+
+ @if ($previousCouple != $currentCouple) + @endif + @php ($previousCouple = $currentCouple) + @endforeach diff --git a/resources/views/messages/history.blade.php b/resources/views/messages/history.blade.php new file mode 100644 index 0000000..c19d8a3 --- /dev/null +++ b/resources/views/messages/history.blade.php @@ -0,0 +1,21 @@ +@extends('layouts.app') + +@section('content') +
+

Message History

+

Your message history

+
+
+
+
+ @if(!$messages) +
+ You don't have any messages yet. +
+ @else + @include('messages/_messagesList') + @endif +
+
+
+@endsection diff --git a/resources/views/trade/_possible_deal.blade.php b/resources/views/trade/_possible_deal.blade.php index 317e892..0fddf72 100644 --- a/resources/views/trade/_possible_deal.blade.php +++ b/resources/views/trade/_possible_deal.blade.php @@ -19,6 +19,22 @@ @endforeach + @if(count($user->messages)) +
+
+
+ +
+
+ You have a message history with {{ $user->name }} +
+
+ +
+ @endif
diff --git a/routes/web.php b/routes/web.php index 0b2bc26..563481e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -20,6 +20,7 @@ Route::get('/photo/create', 'PhotoController@create')->name('photos.create'); Route::post('/photo', 'PhotoController@store')->name('photos.store'); Route::post('/message', 'MessageController@store')->name('messages.store'); + Route::get('/messages-history', 'MessageController@history')->name('messages.history'); Route::get('/profile', 'ProfileController@edit')->name('profile.edit'); Route::put('/profile', 'ProfileController@update')->name('profile.update'); }); From 29654eb56a82e478c321e3ba8d867a699184ff7b Mon Sep 17 00:00:00 2001 From: Wilkins Date: Thu, 22 Apr 2021 23:26:38 +0200 Subject: [PATCH 2/2] Adding Messages features - Changing message layout --- app/Http/Controllers/HerdController.php | 26 +++++-- app/Http/Controllers/MessageController.php | 4 +- app/Message.php | 5 ++ app/Queries/MessagesQuery.php | 13 +++- public/css/app.css | 12 ++- public/mix-manifest.json | 2 +- resources/sass/app.scss | 12 ++- .../views/messages/_messagesList.blade.php | 73 ++++++------------- resources/views/messages/history.blade.php | 9 ++- .../views/trade/_possible_deal.blade.php | 3 +- 10 files changed, 82 insertions(+), 77 deletions(-) diff --git a/app/Http/Controllers/HerdController.php b/app/Http/Controllers/HerdController.php index 864c3bc..cea473b 100644 --- a/app/Http/Controllers/HerdController.php +++ b/app/Http/Controllers/HerdController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Queries\TradingUsersQuery; +use App\Queries\MessagesQuery; use App\Queries\ElephpantsQuery; use App\User; use Illuminate\Support\Facades\Auth; @@ -47,19 +48,13 @@ private function prepareTradePossibilities($elephpants, $userElephpants) } - public function show(string $username, TradingUsersQuery $query) + public function show(string $username, TradingUsersQuery $query, MessagesQuery $mQuery) { $user = User::whereUsername($username)->firstOrFail(); $elephpants = $user->elephpants()->orderBy('year', 'desc')->orderBy('name', 'desc')->get(); $userElephpants = $user->elephpantsWithQuantity()->toArray(); - $loggedUser = auth()->user(); - - if ($loggedUser) { - $possibleTrades = $query->fetchAllForUser($loggedUser, $user->id); - } else { - $possibleTrades = null; - } + $possibleTrades = $this->getPossibleTrades($user, $query, $mQuery); $stats = [ 'unique' => $unique = count($userElephpants), @@ -69,4 +64,19 @@ public function show(string $username, TradingUsersQuery $query) return view('herd.show', compact('user', 'elephpants', 'stats', 'possibleTrades')); } + + private function getPossibleTrades(User $user, TradingUsersQuery $query, MessagesQuery $mQuery) + { + $loggedUser = auth()->user(); + $possibleTrades = null; + + if ($loggedUser) { + $possibleTrades = $query->fetchAllForUser($loggedUser, $user->id); + foreach ($possibleTrades as $tradeUser) { + $tradeUser->messages = $mQuery->getMessagesWithLoggedInUserAndSomeoneElse($tradeUser->id); + } + } + + return $possibleTrades; + } } diff --git a/app/Http/Controllers/MessageController.php b/app/Http/Controllers/MessageController.php index 1fb7b9f..e1b4b57 100644 --- a/app/Http/Controllers/MessageController.php +++ b/app/Http/Controllers/MessageController.php @@ -28,9 +28,9 @@ public function store(MessageRequest $request) public function history(MessagesQuery $query) { - $messages = $query->getMessagesWithLoggedInUser(); + $interlocutorMessages = $query->getMessagesWithLoggedInUser(); $loggedInUser = Auth::user(); - return view('messages.history', compact('messages', 'loggedInUser')); + return view('messages.history', compact('interlocutorMessages', 'loggedInUser')); } } diff --git a/app/Message.php b/app/Message.php index f56a4a8..ba3921b 100644 --- a/app/Message.php +++ b/app/Message.php @@ -17,4 +17,9 @@ public function receiver() { return $this->belongsTo(User::class, 'receiver_id'); } + + public function interlocutor() + { + return $this->belongsTo(User::class, 'interlocutor_id'); + } } diff --git a/app/Queries/MessagesQuery.php b/app/Queries/MessagesQuery.php index 2b94789..351d5d3 100644 --- a/app/Queries/MessagesQuery.php +++ b/app/Queries/MessagesQuery.php @@ -18,8 +18,10 @@ public function getMessagesWithLoggedInUser(): Collection $query->where('m.sender_id', '=', $authUserId) ->orWhere('m.receiver_id', '=', $authUserId); }) - ->orderBy('m.created_at', 'desc') - ->get(); + ->orderBy('m.created_at', 'asc') + + ->get() + ->groupBy('interlocutor_id'); } public function getMessagesWithLoggedInUserAndSomeoneElse(int $userId): Collection @@ -41,18 +43,23 @@ public function getMessagesWithLoggedInUserAndSomeoneElse(int $userId): Collecti public function getAllQuery() { + $authUserId = auth()->id(); + return Message::query() ->select([ 'm.message', 'm.created_at', 'sender.id as sender_id', 'receiver.id as receiver_id', + 'interlocutor.id as interlocutor_id', ]) ->with('sender') ->with('receiver') + ->with('interlocutor') ->from('messages as m') ->join('users as sender', 'm.sender_id', '=', 'sender.id') - ->join('users as receiver', 'm.receiver_id', '=', 'receiver.id'); + ->join('users as receiver', 'm.receiver_id', '=', 'receiver.id') + ->join('users as interlocutor', Message::raw("CASE WHEN m.sender_id = {$authUserId} then m.receiver_id else m.sender_id end"), '=', 'interlocutor.id'); } public function fetchAll(): Collection diff --git a/public/css/app.css b/public/css/app.css index 45b395c..8efac9a 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -13038,19 +13038,23 @@ body { font-weight: bold; } -.user-message-container .user-message-text { +.user-message-container .user-message-username { background-color: #3e86f9; color: white; - padding: 10px; - border-radius: 1em; + padding: 0.1em 0.5em; + border-radius: 0.5em; max-width: 16em; + margin: 0 0.1em; } .user-message-container .user-message-timestamp { background-color: #ccc; padding: 0.1em 0.5em; border-radius: 0.5em; - display: inline-block; +} + +.user-message-container .user-message-text { + padding: 0.1em 0.5em; } .messages-exchanges-container .messages-exchanges-list { diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 92073e9..57f8cb0 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,4 +1,4 @@ { "/js/app.js": "/js/app.js?id=b388c9cab65ec18c3726", - "/css/app.css": "/css/app.css?id=d7af5c083c9f8338d455" + "/css/app.css": "/css/app.css?id=b5104cfee0d86add334d" } diff --git a/resources/sass/app.scss b/resources/sass/app.scss index 8e96ce0..f2fb9a7 100644 --- a/resources/sass/app.scss +++ b/resources/sass/app.scss @@ -64,19 +64,23 @@ body { } .user-message-container { - .user-message-text { + .user-message-username { background-color: #3e86f9; color: white; - padding: 10px; - border-radius: 1em; + padding: 0.1em 0.5em; + border-radius: 0.5em; max-width: 16em; + margin: 0 0.1em; } .user-message-timestamp { background-color: #ccc; padding: 0.1em 0.5em; border-radius: 0.5em; - display: inline-block; + } + + .user-message-text { + padding: 0.1em 0.5em; } } diff --git a/resources/views/messages/_messagesList.blade.php b/resources/views/messages/_messagesList.blade.php index af59401..6071b81 100644 --- a/resources/views/messages/_messagesList.blade.php +++ b/resources/views/messages/_messagesList.blade.php @@ -1,60 +1,31 @@ - @php ($previousCouple = '0-0') + @php ($firstMessage = $messages[0]) + @if ($showInterlocutor) + @php ($interlocutor = $firstMessage->getRelationValue('interlocutor')) +
+ + @else +

+ Message history +

+
+ @endif +
@foreach($messages as $message) @php ($sender = $message->getRelationValue('sender')) @php ($receiver = $message->getRelationValue('receiver')) - @if (Auth::id() == $sender->id) - @php ($leftUser = $receiver) - @php ($rightUser = $sender) - @else - @php ($rightUser = $receiver) - @php ($leftUser = $sender) - @endif - @php ($currentCouple = $leftUser->id.'-'.$rightUser->id) - @if ($previousCouple != $currentCouple) -
-
-
-
- - From: {{ $leftUser->name }} -
- -
-
- @else -
- @endif -
-
-

- {{ date('M jS, Y', strtotime($message->created_at)) }} -

-
-
-
-
- @if (Auth::id() != $sender->id) -

- {!! nl2br($message->message) !!} -

- @endif -
-
- @if (Auth::id() == $sender->id) -

- {!! nl2br($message->message) !!} -

- @endif +
+ + {{ date('M jS, Y', strtotime($message->created_at)) }} + + {{ $sender->name }} + {!! $message->message !!}
+ @endforeach
- @if ($previousCouple != $currentCouple) - @endif - @php ($previousCouple = $currentCouple) - @endforeach diff --git a/resources/views/messages/history.blade.php b/resources/views/messages/history.blade.php index c19d8a3..85fdcc1 100644 --- a/resources/views/messages/history.blade.php +++ b/resources/views/messages/history.blade.php @@ -8,12 +8,15 @@
- @if(!$messages) + @if(!$interlocutorMessages)
You don't have any messages yet.
- @else - @include('messages/_messagesList') + @else + @foreach ($interlocutorMessages as $messages) + @php ($showInterlocutor = true) + @include('messages/_messagesList') + @endforeach @endif
diff --git a/resources/views/trade/_possible_deal.blade.php b/resources/views/trade/_possible_deal.blade.php index 0fddf72..352f124 100644 --- a/resources/views/trade/_possible_deal.blade.php +++ b/resources/views/trade/_possible_deal.blade.php @@ -19,7 +19,7 @@ @endforeach
- @if(count($user->messages)) + @if (isset($user->messages) && count($user->messages))
@@ -31,6 +31,7 @@