From a859e1fe8cd58c25892925545e722fbd3027ef19 Mon Sep 17 00:00:00 2001 From: Artus Date: Mon, 4 Nov 2019 22:00:32 +0100 Subject: [PATCH] works on parsing ApiResponse --- main.js | 903 +++++++++++++++++++++++++++++++++++++-------------- src/Main.elm | 306 ++++++++++++----- 2 files changed, 869 insertions(+), 340 deletions(-) diff --git a/main.js b/main.js index 3a837b2..8a8f9cc 100644 --- a/main.js +++ b/main.js @@ -4502,7 +4502,44 @@ function _Url_percentDecode(string) { return $elm$core$Maybe$Nothing; } -}var $author$project$Main$LinkClicked = function (a) { +} + + +var _Bitwise_and = F2(function(a, b) +{ + return a & b; +}); + +var _Bitwise_or = F2(function(a, b) +{ + return a | b; +}); + +var _Bitwise_xor = F2(function(a, b) +{ + return a ^ b; +}); + +function _Bitwise_complement(a) +{ + return ~a; +}; + +var _Bitwise_shiftLeftBy = F2(function(offset, a) +{ + return a << offset; +}); + +var _Bitwise_shiftRightBy = F2(function(offset, a) +{ + return a >> offset; +}); + +var _Bitwise_shiftRightZfBy = F2(function(offset, a) +{ + return a >>> offset; +}); +var $author$project$Main$LinkClicked = function (a) { return {$: 'LinkClicked', a: a}; }; var $author$project$Main$UrlChanged = function (a) { @@ -6465,9 +6502,109 @@ var $author$project$Main$subscriptions = function (_v0) { return $elm$core$Platform$Sub$none; }; var $author$project$Main$Add = {$: 'Add'}; +var $author$project$Main$GotActionResult = function (a) { + return {$: 'GotActionResult', a: a}; +}; var $author$project$Main$ModeSwitched = function (a) { return {$: 'ModeSwitched', a: a}; }; +var $author$project$Main$ApiResponse = F4( + function (value, updates, notification, error) { + return {error: error, notification: notification, updates: updates, value: value}; + }); +var $elm$json$Json$Decode$oneOf = _Json_oneOf; +var $elm$json$Json$Decode$maybe = function (decoder) { + return $elm$json$Json$Decode$oneOf( + _List_fromArray( + [ + A2($elm$json$Json$Decode$map, $elm$core$Maybe$Just, decoder), + $elm$json$Json$Decode$succeed($elm$core$Maybe$Nothing) + ])); +}; +var $author$project$Main$ClaimAdded = function (a) { + return {$: 'ClaimAdded', a: a}; +}; +var $author$project$Main$ClaimRemoved = function (a) { + return {$: 'ClaimRemoved', a: a}; +}; +var $author$project$Main$ItemAdded = function (a) { + return {$: 'ItemAdded', a: a}; +}; +var $author$project$Main$ItemRemoved = function (a) { + return {$: 'ItemRemoved', a: a}; +}; +var $author$project$Main$WealthUpdated = function (a) { + return {$: 'WealthUpdated', a: a}; +}; +var $elm$json$Json$Decode$andThen = _Json_andThen; +var $author$project$Main$updatesDecoder = $elm$json$Json$Decode$oneOf( + _List_fromArray( + [ + A2( + $elm$json$Json$Decode$field, + 'ItemRemoved', + A2( + $elm$json$Json$Decode$andThen, + function (i) { + return $elm$json$Json$Decode$succeed( + $author$project$Main$ItemRemoved(i)); + }, + $author$project$Main$itemDecoder)), + A2( + $elm$json$Json$Decode$field, + 'ItemAdded', + A2( + $elm$json$Json$Decode$andThen, + function (i) { + return $elm$json$Json$Decode$succeed( + $author$project$Main$ItemAdded(i)); + }, + $author$project$Main$itemDecoder)), + A2( + $elm$json$Json$Decode$field, + 'Wealth', + A2( + $elm$json$Json$Decode$andThen, + function (i) { + return $elm$json$Json$Decode$succeed( + $author$project$Main$WealthUpdated(i)); + }, + $author$project$Main$wealthDecoder)), + A2( + $elm$json$Json$Decode$field, + 'ClaimRemoved', + A2( + $elm$json$Json$Decode$andThen, + function (i) { + return $elm$json$Json$Decode$succeed( + $author$project$Main$ClaimRemoved(i)); + }, + $elm$json$Json$Decode$succeed(_Utils_Tuple0))), + A2( + $elm$json$Json$Decode$field, + 'ClaimAdded', + A2( + $elm$json$Json$Decode$andThen, + function (i) { + return $elm$json$Json$Decode$succeed( + $author$project$Main$ClaimAdded(i)); + }, + $elm$json$Json$Decode$succeed(_Utils_Tuple0))) + ])); +var $author$project$Main$apiResponseDecoder = A5( + $elm$json$Json$Decode$map4, + $author$project$Main$ApiResponse, + $elm$json$Json$Decode$maybe( + A2($elm$json$Json$Decode$field, 'value', $elm$json$Json$Decode$string)), + $elm$json$Json$Decode$maybe( + A2( + $elm$json$Json$Decode$field, + 'updates', + $elm$json$Json$Decode$list($author$project$Main$updatesDecoder))), + $elm$json$Json$Decode$maybe( + A2($elm$json$Json$Decode$field, 'notification', $elm$json$Json$Decode$string)), + $elm$json$Json$Decode$maybe( + A2($elm$json$Json$Decode$field, 'error', $elm$json$Json$Decode$string))); var $elm$core$Set$Set_elm_builtin = function (a) { return {$: 'Set_elm_builtin', a: a}; }; @@ -6491,16 +6628,27 @@ var $author$project$Main$printError = function (error) { } }; var $elm$browser$Browser$Navigation$pushUrl = _Browser_pushUrl; -var $author$project$Main$setError = F2( - function (error, model) { - var state = model.state; - return _Utils_update( - model, - { - state: _Utils_update( - state, - {error: error}) - }); +var $elm$core$List$filter = F2( + function (isGood, list) { + return A3( + $elm$core$List$foldr, + F2( + function (x, xs) { + return isGood(x) ? A2($elm$core$List$cons, x, xs) : xs; + }), + _List_Nil, + list); + }); +var $elm$json$Json$Encode$int = _Json_wrap; +var $elm$core$Maybe$map = F2( + function (f, maybe) { + if (maybe.$ === 'Just') { + var value = maybe.a; + return $elm$core$Maybe$Just( + f(value)); + } else { + return $elm$core$Maybe$Nothing; + } }); var $elm$core$Dict$member = F2( function (key, dict) { @@ -6516,6 +6664,196 @@ var $elm$core$Set$member = F2( var dict = _v0.a; return A2($elm$core$Dict$member, key, dict); }); +var $elm$core$Maybe$withDefault = F2( + function (_default, maybe) { + if (maybe.$ === 'Just') { + var value = maybe.a; + return value; + } else { + return _default; + } + }); +var $author$project$Main$itemInSelection = F2( + function (selection, item) { + return A2( + $elm$core$Maybe$withDefault, + false, + A2( + $elm$core$Maybe$map, + $elm$core$Set$member(item.id), + selection)); + }); +var $elm$json$Json$Encode$list = F2( + function (func, entries) { + return _Json_wrap( + A3( + $elm$core$List$foldl, + _Json_addEntry(func), + _Json_emptyArray(_Utils_Tuple0), + entries)); + }); +var $elm$json$Json$Encode$null = _Json_encodeNull; +var $elm$json$Json$Encode$object = function (pairs) { + return _Json_wrap( + A3( + $elm$core$List$foldl, + F2( + function (_v0, obj) { + var k = _v0.a; + var v = _v0.b; + return A3(_Json_addField, k, v, obj); + }), + _Json_emptyObject(_Utils_Tuple0), + pairs)); +}; +var $author$project$Main$targetItemsFor = F2( + function (mode, model) { + switch (mode.$) { + case 'Add': + return _List_Nil; + case 'Buy': + return A2($elm$core$Maybe$withDefault, _List_Nil, model.merchantItems); + case 'Sell': + return A2($elm$core$Maybe$withDefault, _List_Nil, model.loot); + default: + return A2($elm$core$Maybe$withDefault, _List_Nil, model.groupLoot); + } + }); +var $author$project$Main$buildPayload = F2( + function (mode, model) { + var items = A2( + $elm$core$List$filter, + $author$project$Main$itemInSelection(model.state.selection), + A2($author$project$Main$targetItemsFor, mode, model)); + switch (mode.$) { + case 'Buy': + return $elm$json$Json$Encode$object( + _List_fromArray( + [ + _Utils_Tuple2( + 'items', + A2( + $elm$json$Json$Encode$list, + function (i) { + return A2( + $elm$json$Json$Encode$list, + $elm$core$Basics$identity, + _List_fromArray( + [ + $elm$json$Json$Encode$int(i.id), + $elm$json$Json$Encode$null + ])); + }, + items)), + _Utils_Tuple2('global_mod', $elm$json$Json$Encode$null) + ])); + case 'Sell': + return $elm$json$Json$Encode$object( + _List_fromArray( + [ + _Utils_Tuple2( + 'items', + A2( + $elm$json$Json$Encode$list, + function (i) { + return A2( + $elm$json$Json$Encode$list, + $elm$core$Basics$identity, + _List_fromArray( + [ + $elm$json$Json$Encode$int(i.id), + $elm$json$Json$Encode$null + ])); + }, + items)), + _Utils_Tuple2('global_mod', $elm$json$Json$Encode$null) + ])); + case 'Grab': + return $elm$json$Json$Encode$object( + _List_fromArray( + [ + _Utils_Tuple2( + 'items', + A2( + $elm$json$Json$Encode$list, + function (i) { + return $elm$json$Json$Encode$int(i.id); + }, + items)), + _Utils_Tuple2('global_mod', $elm$json$Json$Encode$null) + ])); + default: + return $elm$json$Json$Encode$object( + _List_fromArray( + [ + _Utils_Tuple2( + 'items', + A2( + $elm$json$Json$Encode$list, + function (i) { + return $elm$json$Json$Encode$int(i.id); + }, + items)), + _Utils_Tuple2('global_mod', $elm$json$Json$Encode$null) + ])); + } + }); +var $elm$http$Http$jsonBody = function (value) { + return A2( + _Http_pair, + 'application/json', + A2($elm$json$Json$Encode$encode, 0, value)); +}; +var $author$project$Main$sendRequest = F2( + function (activeMode, model) { + if (activeMode.$ === 'Nothing') { + return $elm$core$Platform$Cmd$none; + } else { + var mode = activeMode.a; + var _v1 = function () { + switch (mode.$) { + case 'Add': + return _Utils_Tuple2( + 'http://localhost:8088/api/players/' + ($elm$core$String$fromInt(model.player.id) + '/loot'), + 'POST'); + case 'Buy': + return _Utils_Tuple2( + 'http://localhost:8088/api/players/' + ($elm$core$String$fromInt(model.player.id) + '/loot'), + 'PUT'); + case 'Sell': + return _Utils_Tuple2( + 'http://localhost:8088/api/players/' + ($elm$core$String$fromInt(model.player.id) + '/loot'), + 'DELETE'); + default: + return _Utils_Tuple2('http://', 'POST'); + } + }(); + var endpoint = _v1.a; + var method = _v1.b; + return $elm$http$Http$request( + { + body: $elm$http$Http$jsonBody( + A2($author$project$Main$buildPayload, mode, model)), + expect: A2($elm$http$Http$expectJson, $author$project$Main$GotActionResult, $author$project$Main$apiResponseDecoder), + headers: _List_Nil, + method: method, + timeout: $elm$core$Maybe$Nothing, + tracker: $elm$core$Maybe$Nothing, + url: endpoint + }); + } + }); +var $author$project$Main$setError = F2( + function (error, model) { + var state = model.state; + return _Utils_update( + model, + { + state: _Utils_update( + state, + {error: error}) + }); + }); var $elm$core$Set$remove = F2( function (key, _v0) { var dict = _v0.a; @@ -6539,6 +6877,7 @@ var $author$project$Main$switchSelectionState = F2( return A2($elm$core$Debug$log, 'ignore switchSelectionState', $elm$core$Maybe$Nothing); } }); +var $elm$core$Debug$toString = _Debug_toString; var $elm$url$Url$addPort = F2( function (maybePort, starter) { if (maybePort.$ === 'Nothing') { @@ -6711,25 +7050,9 @@ var $author$project$Main$update = F2( }) }), $elm$core$Platform$Cmd$none); - default: + case 'ModeSwitched': var newMode = msg.a; var state = model.state; - var _v7 = function () { - if (newMode.$ === 'Nothing') { - return _Utils_Tuple2($elm$core$Maybe$Nothing, $elm$core$Platform$Cmd$none); - } else { - var _new = newMode; - if ((_new.$ === 'Just') && (_new.a.$ === 'Confirm')) { - var _v10 = _new.a; - return _Utils_Tuple2($elm$core$Maybe$Nothing, $elm$core$Platform$Cmd$none); - } else { - var other = _new; - return _Utils_Tuple2(_new, $elm$core$Platform$Cmd$none); - } - } - }(); - var nextMode = _v7.a; - var cmd = _v7.b; return _Utils_Tuple2( _Utils_update( model, @@ -6737,32 +7060,71 @@ var $author$project$Main$update = F2( state: _Utils_update( state, { - activeMode: nextMode, + activeMode: newMode, selection: function () { - if (nextMode.$ === 'Nothing') { + if (newMode.$ === 'Nothing') { return $elm$core$Maybe$Nothing; } else { - if (nextMode.a.$ === 'Grab') { - var _v12 = nextMode.a; + if (newMode.a.$ === 'Grab') { + var _v8 = newMode.a; return $elm$core$Maybe$Just( $elm$core$Set$fromList( _List_fromArray( [34, 38]))); } else { - var others = nextMode.a; + var others = newMode.a; return $elm$core$Maybe$Just($elm$core$Set$empty); } } }() }) }), - cmd); + $elm$core$Platform$Cmd$none); + case 'ConfirmAction': + var currentMode = model.state.activeMode; + return _Utils_Tuple2( + model, + A2($author$project$Main$sendRequest, currentMode, model)); + case 'UndoLastAction': + var playerId = $elm$core$String$fromInt(model.player.id); + return _Utils_Tuple2( + model, + $elm$http$Http$request( + { + body: $elm$http$Http$emptyBody, + expect: A2($elm$http$Http$expectJson, $author$project$Main$GotActionResult, $author$project$Main$apiResponseDecoder), + headers: _List_Nil, + method: 'DELETE', + timeout: $elm$core$Maybe$Nothing, + tracker: $elm$core$Maybe$Nothing, + url: 'http://localhost:8088/api/players/' + (playerId + '/events/last') + })); + default: + var response = msg.a; + if (response.$ === 'Ok') { + var r = response.a; + return _Utils_Tuple2( + A2( + $author$project$Main$setError, + $elm$core$Debug$toString(r), + model), + $elm$core$Platform$Cmd$none); + } else { + var r = response.a; + return _Utils_Tuple2( + A2( + $author$project$Main$setError, + $elm$core$Debug$toString(r), + model), + $elm$core$Platform$Cmd$none); + } } }); var $author$project$Main$Buy = {$: 'Buy'}; -var $author$project$Main$Confirm = {$: 'Confirm'}; +var $author$project$Main$ConfirmAction = {$: 'ConfirmAction'}; var $author$project$Main$Grab = {$: 'Grab'}; var $author$project$Main$Sell = {$: 'Sell'}; +var $author$project$Main$UndoLastAction = {$: 'UndoLastAction'}; var $elm$html$Html$button = _VirtualDom_node('button'); var $elm$json$Json$Encode$string = _Json_wrap; var $elm$html$Html$Attributes$stringProperty = F2( @@ -6797,14 +7159,13 @@ var $elm$html$Html$span = _VirtualDom_node('span'); var $elm$virtual_dom$VirtualDom$text = _VirtualDom_text; var $elm$html$Html$text = $elm$virtual_dom$VirtualDom$text; var $author$project$Main$actionButton = F4( - function (mode, t, icon, color) { + function (msg, t, icon, color) { return A2( $elm$html$Html$button, _List_fromArray( [ $elm$html$Html$Attributes$class('button is-' + color), - $elm$html$Html$Events$onClick( - $author$project$Main$ModeSwitched(mode)) + $elm$html$Html$Events$onClick(msg) ]), _List_fromArray( [ @@ -6836,28 +7197,40 @@ var $author$project$Main$actionButton = F4( var $elm$html$Html$article = _VirtualDom_node('article'); var $elm$html$Html$div = _VirtualDom_node('div'); var $elm$html$Html$hr = _VirtualDom_node('hr'); -var $elm$html$Html$section = _VirtualDom_node('section'); -var $author$project$Main$isSelected = F2( - function (selection, item) { - if (selection.$ === 'Just') { - var s = selection.a; - return A2($elm$core$Set$member, item.id, s); - } else { - return false; - } - }); -var $author$project$Main$renderName = function (item) { +var $author$project$Main$renderId = function (item) { return A2( $elm$html$Html$p, _List_Nil, _List_fromArray( [ - $elm$html$Html$text(item.name) + $elm$html$Html$text( + $elm$core$String$fromInt(item.id)) ])); }; var $author$project$Main$LootViewItemSwitched = function (a) { return {$: 'LootViewItemSwitched', a: a}; }; +var $author$project$Main$canSelectIn = function (mode) { + switch (mode.$) { + case 'Sell': + return true; + case 'Buy': + return true; + case 'Grab': + return true; + default: + return false; + } +}; +var $elm$json$Json$Encode$bool = _Json_wrap; +var $elm$html$Html$Attributes$boolProperty = F2( + function (key, bool) { + return A2( + _VirtualDom_property, + key, + $elm$json$Json$Encode$bool(bool)); + }); +var $elm$html$Html$Attributes$checked = $elm$html$Html$Attributes$boolProperty('checked'); var $elm$core$String$fromFloat = _String_fromNumber; var $elm$html$Html$input = _VirtualDom_node('input'); var $elm$json$Json$Decode$at = F2( @@ -6877,72 +7250,58 @@ var $elm$html$Html$Events$onCheck = function (tagger) { A2($elm$json$Json$Decode$map, tagger, $elm$html$Html$Events$targetChecked)); }; var $elm$html$Html$Attributes$type_ = $elm$html$Html$Attributes$stringProperty('type'); -var $author$project$Main$rowModeControlsRenderer = F2( - function (mode, item) { - var _v0 = function () { +var $author$project$Main$rowControlsForMode = F3( + function (mode, isSelected, item) { + var itemInfo = function () { switch (mode.$) { case 'Buy': - return _Utils_Tuple2( - A2( - $elm$html$Html$p, - _List_fromArray( - [ - $elm$html$Html$Attributes$class('level-item') - ]), - _List_fromArray( - [ - $elm$html$Html$text( - $elm$core$String$fromInt(item.base_price) + 'po') - ])), - true); + return A2( + $elm$html$Html$p, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('level-item') + ]), + _List_fromArray( + [ + $elm$html$Html$text( + $elm$core$String$fromInt(item.base_price) + 'po') + ])); case 'Sell': - return _Utils_Tuple2( - A2( - $elm$html$Html$p, - _List_fromArray( - [ - $elm$html$Html$Attributes$class('level-item') - ]), - _List_fromArray( - [ - $elm$html$Html$text( - $elm$core$String$fromFloat(item.base_price / 2) + 'po') - ])), - true); + return A2( + $elm$html$Html$p, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('level-item') + ]), + _List_fromArray( + [ + $elm$html$Html$text( + $elm$core$String$fromFloat(item.base_price / 2) + 'po') + ])); case 'Grab': - return _Utils_Tuple2( - A2( - $elm$html$Html$p, - _List_fromArray( - [ - $elm$html$Html$Attributes$class('level-item') - ]), - _List_fromArray( - [ - $elm$html$Html$text('Grab') - ])), - true); - case 'Add': - return _Utils_Tuple2( - A2( - $elm$html$Html$p, - _List_fromArray( - [ - $elm$html$Html$Attributes$class('level-item') - ]), - _List_fromArray( - [ - $elm$html$Html$text('New !') - ])), - false); + return A2( + $elm$html$Html$p, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('level-item') + ]), + _List_fromArray( + [ + $elm$html$Html$text('Grab') + ])); default: - return _Utils_Tuple2( - $elm$html$Html$text(''), - false); + return A2( + $elm$html$Html$p, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('level-item') + ]), + _List_fromArray( + [ + $elm$html$Html$text('New !') + ])); } }(); - var itemInfo = _v0.a; - var canSelect = _v0.b; return A2( $elm$html$Html$div, _List_fromArray( @@ -6952,7 +7311,7 @@ var $author$project$Main$rowModeControlsRenderer = F2( A2( $elm$core$List$cons, itemInfo, - canSelect ? _List_fromArray( + $author$project$Main$canSelectIn(mode) ? _List_fromArray( [ A2( $elm$html$Html$input, @@ -6960,6 +7319,8 @@ var $author$project$Main$rowModeControlsRenderer = F2( [ $elm$html$Html$Attributes$class('checkbox level-item'), $elm$html$Html$Attributes$type_('checkbox'), + $elm$html$Html$Attributes$checked( + isSelected(item)), $elm$html$Html$Events$onCheck( function (v) { return $author$project$Main$LootViewItemSwitched(item.id); @@ -6968,20 +7329,11 @@ var $author$project$Main$rowModeControlsRenderer = F2( _List_Nil) ]) : _List_Nil)); }); +var $elm$html$Html$section = _VirtualDom_node('section'); var $elm$html$Html$table = _VirtualDom_node('table'); +var $elm$html$Html$tbody = _VirtualDom_node('tbody'); var $elm$html$Html$th = _VirtualDom_node('th'); var $elm$html$Html$thead = _VirtualDom_node('thead'); -var $elm$core$List$filter = F2( - function (isGood, list) { - return A3( - $elm$core$List$foldr, - F2( - function (x, xs) { - return isGood(x) ? A2($elm$core$List$cons, x, xs) : xs; - }), - _List_Nil, - list); - }); var $elm$core$Tuple$second = function (_v0) { var y = _v0.b; return y; @@ -7004,7 +7356,7 @@ var $elm$core$List$singleton = function (value) { var $elm$html$Html$td = _VirtualDom_node('td'); var $elm$html$Html$tr = _VirtualDom_node('tr'); var $author$project$Main$viewItemTableRow = F3( - function (selected, rowControls, item) { + function (isSelected, rowControls, item) { return A2( $elm$html$Html$tr, _List_fromArray( @@ -7014,7 +7366,7 @@ var $author$project$Main$viewItemTableRow = F3( [ _Utils_Tuple2( 'is-selected', - selected(item)) + isSelected(item)) ])) ]), _List_fromArray( @@ -7063,33 +7415,17 @@ var $author$project$Main$viewItemTableRow = F3( ])) ])); }); -var $author$project$Main$viewChest = F2( - function (items, model) { - var rowControls = function () { - var _v0 = model.state.activeMode; - if (_v0.$ === 'Just') { - var mode = _v0.a; - return $elm$core$Maybe$Just( - $author$project$Main$rowModeControlsRenderer(mode)); - } else { - var _v1 = model.state.route; - if (_v1.$ === 'GroupLoot') { - return $elm$core$Maybe$Just($author$project$Main$renderName); - } else { - var others = _v1; - return $elm$core$Maybe$Nothing; - } - } - }(); +var $author$project$Main$viewChest = F3( + function (isSelected, rowControls, items) { return A2( $elm$html$Html$table, _List_fromArray( [ - $elm$html$Html$Attributes$class('table is-fullwidth is-striped is-light') + $elm$html$Html$Attributes$class('table is-fullwidth is-hoverable') ]), - A2( - $elm$core$List$cons, - A2( + _List_fromArray( + [ + A2( $elm$html$Html$thead, _List_fromArray( [ @@ -7105,13 +7441,14 @@ var $author$project$Main$viewChest = F2( $elm$html$Html$text('Nom') ])) ])), - A2( - $elm$core$List$map, A2( - $author$project$Main$viewItemTableRow, - $author$project$Main$isSelected(model.state.selection), - rowControls), - items))); + $elm$html$Html$tbody, + _List_Nil, + A2( + $elm$core$List$map, + A2($author$project$Main$viewItemTableRow, isSelected, rowControls), + items)) + ])); }); var $author$project$Main$stackedIcon = function (name) { return A2( @@ -7209,7 +7546,6 @@ var $author$project$Main$debugSwitchPlayers = A2( $elm$html$Html$text('Fefi') ])) ])); -var $elm$core$Debug$toString = _Debug_toString; var $author$project$Main$viewDebugSection = function (model) { return A2( $elm$html$Html$div, @@ -7435,6 +7771,34 @@ var $author$project$Main$viewHeaderBar = function (model) { ])) ])); }; +var $elm$core$String$cons = _String_cons; +var $elm$core$String$fromChar = function (_char) { + return A2($elm$core$String$cons, _char, ''); +}; +var $elm$core$Bitwise$and = _Bitwise_and; +var $elm$core$Bitwise$shiftRightBy = _Bitwise_shiftRightBy; +var $elm$core$String$repeatHelp = F3( + function (n, chunk, result) { + return (n <= 0) ? result : A3( + $elm$core$String$repeatHelp, + n >> 1, + _Utils_ap(chunk, chunk), + (!(n & 1)) ? result : _Utils_ap(result, chunk)); + }); +var $elm$core$String$repeat = F2( + function (n, chunk) { + return A3($elm$core$String$repeatHelp, n, chunk, ''); + }); +var $elm$core$String$padLeft = F3( + function (n, _char, string) { + return _Utils_ap( + A2( + $elm$core$String$repeat, + n - $elm$core$String$length(string), + $elm$core$String$fromChar(_char)), + string); + }); +var $elm$html$Html$strong = _VirtualDom_node('strong'); var $author$project$Main$showWealthField = F2( function (name, value) { return A2( @@ -7449,32 +7813,56 @@ var $author$project$Main$showWealthField = F2( $elm$html$Html$p, _List_fromArray( [ - $elm$html$Html$Attributes$class('is-size-4') + $elm$html$Html$Attributes$class('has-text-right') ]), _List_fromArray( [ - $elm$html$Html$text( - $elm$core$String$fromInt(value)) - ])), - A2( - $elm$html$Html$p, - _List_fromArray( - [ - $elm$html$Html$Attributes$class('heading') - ]), - _List_fromArray( - [ - $elm$html$Html$text(name) + A2( + $elm$html$Html$strong, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('heading is-marginless') + ]), + _List_fromArray( + [ + $elm$html$Html$text(name) + ])), + A2( + $elm$html$Html$span, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('is-size-4') + ]), + _List_fromArray( + [ + $elm$html$Html$text(value) + ])) ])) ])); }); -var $author$project$Main$showWealth = function (wealth) { +var $author$project$Main$viewWealth = function (wealth) { return _List_fromArray( [ - A2($author$project$Main$showWealthField, 'pp', wealth.pp), - A2($author$project$Main$showWealthField, 'gp', wealth.gp), - A2($author$project$Main$showWealthField, 'sp', wealth.sp), - A2($author$project$Main$showWealthField, 'cp', wealth.cp) + A2( + $author$project$Main$showWealthField, + 'pp', + $elm$core$String$fromInt(wealth.pp)), + A2( + $author$project$Main$showWealthField, + 'gp', + A3( + $elm$core$String$padLeft, + 2, + _Utils_chr('0'), + $elm$core$String$fromInt(wealth.gp))), + A2( + $author$project$Main$showWealthField, + 'sp', + $elm$core$String$fromInt(wealth.sp)), + A2( + $author$project$Main$showWealthField, + 'cp', + $elm$core$String$fromInt(wealth.cp)) ]); }; var $author$project$Main$viewPlayerBar = F2( @@ -7523,7 +7911,7 @@ var $author$project$Main$viewPlayerBar = F2( ])) ]), _Utils_ap( - $author$project$Main$showWealth(player.wealth), + $author$project$Main$viewWealth(player.wealth), (player.debt > 0) ? _List_fromArray( [ A2( @@ -7563,85 +7951,8 @@ var $author$project$Main$viewSearchBar = A2( $elm$html$Html$Attributes$class('input') ]), _List_Nil); -var $elm$core$Maybe$withDefault = F2( - function (_default, maybe) { - if (maybe.$ === 'Just') { - var value = maybe.a; - return value; - } else { - return _default; - } - }); var $author$project$Main$view = function (model) { - var actionControls = function () { - var _v2 = model.state.activeMode; - if (_v2.$ === 'Just') { - var mode = _v2.a; - return _List_fromArray( - [ - A2( - $elm$html$Html$div, - _List_fromArray( - [ - $elm$html$Html$Attributes$class('buttons has-addons') - ]), - _List_fromArray( - [ - A4( - $author$project$Main$actionButton, - $elm$core$Maybe$Just($author$project$Main$Confirm), - 'Valider', - 'plus', - 'primary'), - A4($author$project$Main$actionButton, $elm$core$Maybe$Nothing, 'Annuler', 'times', 'danger') - ])) - ]); - } else { - var _v3 = model.state.route; - switch (_v3.$) { - case 'PlayerChest': - return _List_fromArray( - [ - A4( - $author$project$Main$actionButton, - $elm$core$Maybe$Just($author$project$Main$Sell), - 'Vendre', - 'coins', - 'danger') - ]); - case 'GroupLoot': - return _List_fromArray( - [ - A4( - $author$project$Main$actionButton, - $elm$core$Maybe$Just($author$project$Main$Grab), - 'Demander', - 'coins', - 'primary') - ]); - case 'Merchant': - return _List_fromArray( - [ - A4( - $author$project$Main$actionButton, - $elm$core$Maybe$Just($author$project$Main$Buy), - 'Acheter', - 'coins', - 'success') - ]); - default: - return _List_fromArray( - [ - A4( - $author$project$Main$actionButton, - $elm$core$Maybe$Just($author$project$Main$Add), - 'Nouveau loot', - 'plus', - 'primary') - ]); - } - } - }(); + var isSelected = $author$project$Main$itemInSelection(model.state.selection); var _v0 = function () { var _v1 = model.state.route; switch (_v1.$) { @@ -7663,6 +7974,98 @@ var $author$project$Main$view = function (model) { }(); var header = _v0.a; var shownLoot = _v0.b; + var _v2 = function () { + var _v3 = model.state.activeMode; + if (_v3.$ === 'Just') { + var mode = _v3.a; + return _Utils_Tuple2( + _List_fromArray( + [ + A2( + $elm$html$Html$div, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('buttons has-addons') + ]), + _List_fromArray( + [ + A4($author$project$Main$actionButton, $author$project$Main$ConfirmAction, 'Valider', 'success', 'primary'), + A4( + $author$project$Main$actionButton, + $author$project$Main$ModeSwitched($elm$core$Maybe$Nothing), + 'Annuler', + 'times', + 'danger') + ])) + ]), + $elm$core$Maybe$Just( + A2($author$project$Main$rowControlsForMode, mode, isSelected))); + } else { + return _Utils_Tuple2( + A2( + $elm$core$List$cons, + A4($author$project$Main$actionButton, $author$project$Main$UndoLastAction, 'Annuler action', 'delete', 'danger'), + function () { + var _v4 = model.state.route; + switch (_v4.$) { + case 'PlayerChest': + return _List_fromArray( + [ + A4( + $author$project$Main$actionButton, + $author$project$Main$ModeSwitched( + $elm$core$Maybe$Just($author$project$Main$Sell)), + 'Vendre', + 'coins', + 'danger') + ]); + case 'GroupLoot': + return _List_fromArray( + [ + A4( + $author$project$Main$actionButton, + $author$project$Main$ModeSwitched( + $elm$core$Maybe$Just($author$project$Main$Grab)), + 'Demander', + 'coins', + 'primary') + ]); + case 'Merchant': + return _List_fromArray( + [ + A4( + $author$project$Main$actionButton, + $author$project$Main$ModeSwitched( + $elm$core$Maybe$Just($author$project$Main$Buy)), + 'Acheter', + 'coins', + 'success') + ]); + default: + return _List_fromArray( + [ + A4( + $author$project$Main$actionButton, + $author$project$Main$ModeSwitched( + $elm$core$Maybe$Just($author$project$Main$Add)), + 'Nouveau loot', + 'plus', + 'primary') + ]); + } + }()), + function () { + var _v5 = model.state.route; + if (_v5.$ === 'GroupLoot') { + return $elm$core$Maybe$Just($author$project$Main$renderId); + } else { + return $elm$core$Maybe$Nothing; + } + }()); + } + }(); + var actionControls = _v2.a; + var rowControls = _v2.b; return { body: _List_fromArray( [ @@ -7687,7 +8090,7 @@ var $author$project$Main$view = function (model) { $elm$html$Html$text(header) ])), $author$project$Main$viewSearchBar, - A2($author$project$Main$viewChest, shownLoot, model) + A3($author$project$Main$viewChest, isSelected, rowControls, shownLoot) ])), A2($elm$html$Html$hr, _List_Nil, _List_Nil), A2( diff --git a/src/Main.elm b/src/Main.elm index 7cc30b1..fc90b1c 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -9,7 +9,8 @@ import Html.Attributes exposing (..) import Html.Events exposing (..) import Svg.Attributes import Http -import Json.Decode exposing (Decoder, field, list, string, int) +import Json.Decode exposing (Decoder, field, list, string, int, succeed) +import Json.Encode as E import Url.Parser as P exposing (Parser, (), oneOf, s) import Set exposing (Set) -- Main @@ -155,14 +156,26 @@ valueDecoder thenDecoder = -- UPDATE +type alias HttpResult a = (Result Http.Error a) + +type alias ApiResponse = + { value : Maybe String + , updates : Maybe (List DbUpdate) + , notification : Maybe String + , error : Maybe String + } + type Msg = LinkClicked Browser.UrlRequest | UrlChanged Url.Url | PlayerChanged Int - | GotPlayer (Result Http.Error Player) - | GotLoot ToChest (Result Http.Error Loot) + | GotPlayer (HttpResult Player) + | GotLoot ToChest (HttpResult Loot) | LootViewItemSwitched Int | ModeSwitched (Maybe ViewMode) + | ConfirmAction + | UndoLastAction + | GotActionResult (HttpResult ApiResponse) update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = @@ -233,29 +246,137 @@ update msg model = ModeSwitched newMode -> let state = model.state - - (nextMode, cmd) = - case newMode of - Nothing -> -- Cancel action - (Nothing, Cmd.none) - new -> - case new of - Just Confirm -> - -- Confirm action and exit - (Nothing, Cmd.none) - other -> - -- Enter mode - (new, Cmd.none) - in ( { model | state = - { state | activeMode = nextMode - , selection = case nextMode of + { state | activeMode = newMode + , selection = case newMode of Nothing -> Nothing Just Grab -> Just (Set.fromList [34, 38]) Just others -> Just Set.empty }} - , cmd) + , Cmd.none ) + + ConfirmAction -> + let + currentMode = model.state.activeMode + in + (model, sendRequest currentMode model) + + UndoLastAction -> + let playerId = String.fromInt model.player.id + in + (model, Http.request + { url = "http://localhost:8088/api/players/" ++ playerId ++"/events/last" + , method = "DELETE" + , headers = [] + , body = Http.emptyBody + , expect = Http.expectJson GotActionResult apiResponseDecoder + , timeout = Nothing + , tracker = Nothing + }) + + GotActionResult response -> + case response of + Ok r -> (setError (Debug.toString r) model, Cmd.none) + Err r -> (setError (Debug.toString r) model, Cmd.none) + +targetItemsFor : ViewMode -> Model -> List Item +targetItemsFor mode model = + case mode of + Add -> [] + Buy -> Maybe.withDefault [] model.merchantItems + Sell ->Maybe.withDefault [] model.loot + Grab -> Maybe.withDefault [] model.groupLoot + +buildPayload : ViewMode -> Model -> E.Value +buildPayload mode model = + let + items = targetItemsFor mode model + |> List.filter (itemInSelection model.state.selection) + in + case mode of + Buy -> E.object + [ ( "items", items |> E.list (\i -> E.list identity [E.int i.id, E.null])) + , ("global_mod", E.null ) + ] + Sell -> E.object + [ ( "items", items |> E.list (\i -> E.list identity [E.int i.id, E.null])) + , ("global_mod", E.null ) + ] + Grab -> E.object + [ ( "items", items |> E.list (\i -> E.int i.id)) + , ("global_mod", E.null ) + ] + Add -> E.object + [ ( "items", items |> E.list (\i -> E.int i.id)) + , ("global_mod", E.null ) + ] + +type DbUpdate + = ItemRemoved Item + | ItemAdded Item + | WealthUpdated Wealth + | ClaimAdded () + | ClaimRemoved () + +-- TODO: update server to produce better json +-- like an object with list of updates of the same type +-- { ItemRemoved : [..], Wealth : [ .. ], .. } +updatesDecoder : Decoder DbUpdate +updatesDecoder = + -- We expect one update but do not know it's kind + Json.Decode.oneOf + [ (field "ItemRemoved" (itemDecoder |> Json.Decode.andThen (\i -> succeed <| ItemRemoved i))) + , (field "ItemAdded" (itemDecoder |> Json.Decode.andThen (\i -> succeed <| ItemAdded i))) + , (field "Wealth" (wealthDecoder |> Json.Decode.andThen (\i -> succeed <| WealthUpdated i))) + , (field "ClaimRemoved" (succeed () |> Json.Decode.andThen (\i -> succeed <| ClaimRemoved i))) + , (field "ClaimAdded" (succeed () |> Json.Decode.andThen (\i -> succeed <| ClaimAdded i))) + ] + + +apiResponseDecoder : Decoder ApiResponse +apiResponseDecoder = + Json.Decode.map4 ApiResponse + (Json.Decode.maybe (field "value" string)) + (Json.Decode.maybe (field "updates" (Json.Decode.list updatesDecoder))) + (Json.Decode.maybe (field "notification" string)) + (Json.Decode.maybe (field "error" string)) + + +sendRequest : Maybe ViewMode -> Model -> Cmd Msg +sendRequest activeMode model = + case activeMode of + Nothing -> Cmd.none + Just mode -> + let + (endpoint, method) = case mode of + Add -> + ( "http://localhost:8088/api/players/" ++ (String.fromInt model.player.id) ++ "/loot" + , "POST" + ) + Buy -> + ( "http://localhost:8088/api/players/" ++ (String.fromInt model.player.id) ++ "/loot" + , "PUT" + ) + Sell -> + ( "http://localhost:8088/api/players/" ++ (String.fromInt model.player.id) ++ "/loot" + , "DELETE" + ) + Grab -> + ( "http://" + , "POST") + in + Http.request + { method = method + , headers = [] + , url = endpoint + , body = Http.jsonBody <| buildPayload mode model + , expect = Http.expectJson GotActionResult apiResponseDecoder + , timeout = Nothing + , tracker = Nothing + } + + -- ERRORS @@ -301,11 +422,18 @@ type ViewMode | Buy | Grab | Add - | Confirm -- Confirm action and exit mode -actionButton mode t icon color = +canSelectIn : ViewMode -> Bool +canSelectIn mode = + case mode of + Sell -> True + Buy -> True + Grab -> True + Add -> False + +actionButton msg t icon color = button [ class <| "button is-" ++ color - , onClick (ModeSwitched mode) ] + , onClick msg ] [ span [ class "icon" ] [ i [ Svg.Attributes.class <| "fas fa-" ++ icon ] [] ] , p [] [text t] ] @@ -313,6 +441,7 @@ actionButton mode t icon color = view : Model -> Browser.Document Msg view model = let + -- What do we show inside the chest ? (header, shownLoot) = case model.state.route of PlayerChest -> @@ -323,21 +452,39 @@ view model = ("Marchand", Maybe.withDefault [] model.merchantItems) NewLoot -> ("Nouveau trésor :)", [] ) - actionControls = + + {- Dynamic renderes to allow the use of ViewMode + + ActionControls is inserted in the PlayerBar's right + and rowControls are inserted, to the right of every item rows + -} + (actionControls, rowControls) = case model.state.activeMode of Just mode -> -- When a mode is active - [ div [ class "buttons has-addons"] - [ actionButton (Just Confirm) "Valider" "plus" "primary" - , actionButton Nothing "Annuler" "times" "danger" + ( [ div [ class "buttons has-addons"] + [ actionButton (ConfirmAction) "Valider" "success" "primary" + , actionButton (ModeSwitched Nothing) "Annuler" "times" "danger" ] - ] - Nothing -> -- Buttons to enter mode - case model.state.route of - PlayerChest -> [actionButton (Just Sell) "Vendre" "coins" "danger"] - GroupLoot -> [actionButton (Just Grab) "Demander" "coins" "primary"] - Merchant -> [actionButton (Just Buy) "Acheter" "coins" "success"] - NewLoot -> [actionButton (Just Add) "Nouveau loot" "plus" "primary"] + ] + , Just (rowControlsForMode mode isSelected) + ) + Nothing -> -- Buttons to enter mode + ( actionButton UndoLastAction "Annuler action" "delete" "danger" + :: case model.state.route of + PlayerChest -> [actionButton (ModeSwitched (Just Sell)) "Vendre" "coins" "danger"] + GroupLoot -> [actionButton (ModeSwitched (Just Grab)) "Demander" "coins" "primary"] + Merchant -> [actionButton (ModeSwitched (Just Buy)) "Acheter" "coins" "success"] + NewLoot -> [actionButton (ModeSwitched (Just Add)) "Nouveau loot" "plus" "primary"] + -- Claim controls for Group chest + , case model.state.route of + GroupLoot -> Just renderId + _ -> Nothing + ) + -- TODO: should we extract the Maybe conversion + -- and represent cannotSelect with Nothing ?? + isSelected = + itemInSelection model.state.selection in { title = "Loot-a-lot in ELM" , body = @@ -346,7 +493,7 @@ view model = , article [class "section container"] [ p [class "heading"] [text header] , viewSearchBar - , viewChest shownLoot model + , viewChest isSelected rowControls shownLoot ] , hr [] [] , section [class "container"] [viewDebugSection model] @@ -355,60 +502,38 @@ view model = -- LOOT Views -isSelected : Maybe Selection -> Item -> Bool -isSelected selection item = - case selection of - Just s -> - Set.member item.id s - Nothing -> - False +itemInSelection : Maybe Selection -> Item -> Bool +itemInSelection selection item = + Maybe.map (Set.member item.id) selection + |> Maybe.withDefault False -renderName item = - p [] [text item.name] +renderId item = + p [] [text <| String.fromInt item.id] -viewChest : Loot -> Model -> Html Msg -viewChest items model = - let - -- If a mode is active, render its controls - -- Otherwise, controls may be rendered depending on current route - rowControls = case model.state.activeMode of - Just mode -> Just (rowModeControlsRenderer mode) - Nothing -> - case model.state.route of - GroupLoot -> Just renderName - others -> Nothing - in - table [ class "table is-fullwidth is-striped is-light"] - <| thead [ class "table-header" ] +viewChest : (Item -> Bool) -> Maybe (Item -> Html Msg) -> Loot -> Html Msg +viewChest isSelected rowControls items = + table [ class "table is-fullwidth is-hoverable"] + [ thead [ class "table-header" ] [ th [] [ text "Nom" ] ] - :: List.map (viewItemTableRow (isSelected model.state.selection) rowControls) items + , tbody [] <| List.map (viewItemTableRow isSelected rowControls) items + ] -- Renders controls for a specific mode -rowModeControlsRenderer : ViewMode -> Item -> Html Msg -rowModeControlsRenderer mode item = +rowControlsForMode : ViewMode -> (Item -> Bool) -> Item -> Html Msg +rowControlsForMode mode isSelected item = let - (itemInfo, canSelect) = case mode of - Buy -> - ( p [class "level-item"] [ text (String.fromInt item.base_price ++ "po")] - , True ) - Sell -> - ( p [class "level-item"] [ text (String.fromFloat (toFloat item.base_price / 2) ++ "po")] - , True ) - Grab -> - ( p [class "level-item"] [ text "Grab" ] - , True ) - Add -> - ( p [class "level-item"] [ text "New !" ] - , False ) - Confirm -> - (text "" - , False ) + itemInfo = case mode of + Buy -> p [class "level-item"] [ text (String.fromInt item.base_price ++ "po")] + Sell -> p [class "level-item"] [ text (String.fromFloat (toFloat item.base_price / 2) ++ "po")] + Grab -> p [class "level-item"] [ text "Grab" ] + Add -> p [class "level-item"] [ text "New !" ] in div [ class "level-right" ] <| itemInfo - :: if canSelect then + :: if canSelectIn mode then [input [ class "checkbox level-item" , type_ "checkbox" + , checked <| isSelected item , onCheck (\v -> LootViewItemSwitched item.id) ] [] ] else @@ -416,8 +541,8 @@ rowModeControlsRenderer mode item = viewItemTableRow : (Item -> Bool) -> Maybe (Item -> Html Msg) -> Item -> Html Msg -viewItemTableRow selected rowControls item = - tr [ classList [ ("is-selected", selected item) ] ] +viewItemTableRow isSelected rowControls item = + tr [ classList [ ("is-selected", isSelected item) ] ] [ td [] [ label [ class "level checkbox" ] <| div [ class "level-left" ] @@ -527,7 +652,7 @@ viewPlayerBar player actionControls = [ span [ class "icon is-large" ] [ i [ class "fas fa-2x fa-piggy-bank" ] [] ]] ] - ++ (showWealth player.wealth) + ++ (viewWealth player.wealth) ++ (if player.debt > 0 then [div [class "level-item"] [p [class "heading is-size-4 has-text-danger"] @@ -541,19 +666,20 @@ viewPlayerBar player actionControls = ] -showWealth : Wealth -> List (Html Msg) -showWealth wealth = - [ showWealthField "pp" wealth.pp - , showWealthField "gp" wealth.gp - , showWealthField "sp" wealth.sp - , showWealthField "cp" wealth.cp +viewWealth : Wealth -> List (Html Msg) +viewWealth wealth = + [ showWealthField "pp" <| String.fromInt wealth.pp + , showWealthField "gp" <| String.padLeft 2 '0' <| String.fromInt wealth.gp + , showWealthField "sp" <| String.fromInt wealth.sp + , showWealthField "cp" <| String.fromInt wealth.cp ] -showWealthField : String -> Int -> Html Msg +showWealthField : String -> String -> Html Msg showWealthField name value = div [ class "level-item" ] - [ p [ class "is-size-4"] [text (String.fromInt value)] - , p [class "heading"] [text name] + [ p [class "has-text-right"] [ strong [ class "heading is-marginless"] [text name] + , span [ class <| "is-size-4" ] [ text value ] + ] ] -- Search Bar