Commit 9df06ae6 by Иван Кубота

Cards:

Change storage from {c1,c2,c3...} to {cat1: {c1,c2,c3}, cat2: ...} DOM: Fix error in removing removed children Store: AsyncResourceLoaderQueue - Made generic method for loading cached resources Model: async categories.load(id) - load category data with subitems async cards.load(category, id) - load full card data cards.load and new cards populate the same store. Useful for cache. Marked full data as `detailed` Views: Product card display: InfoCard.jsx — display Item and back\next logics ProductCard.jsx — fill markup with data InfoPage.jsx — logics of loading data
parent d485803b
...@@ -3,9 +3,15 @@ import { API } from "../dict/Consts"; ...@@ -3,9 +3,15 @@ import { API } from "../dict/Consts";
Model.cards.getArray = function() { Model.cards.getArray = function() {
const out = []; const out = [];
for(let key in this._props){ let categoryItems;
if(key !== 'commit'){ for(let categoryID in this._props){
out.push(this._props[key]);
if(categoryID !== '_VERSION_'){
categoryItems = this._props[categoryID]
for(let id in categoryItems){
out.push(categoryItems[id]);
}
} }
} }
return out; return out;
...@@ -16,9 +22,8 @@ store.sub(['navigation.current', 'loaded.cards'], async function(page, loaded) { ...@@ -16,9 +22,8 @@ store.sub(['navigation.current', 'loaded.cards'], async function(page, loaded) {
if(page !== 'Login' && !loaded){ if(page !== 'Login' && !loaded){
//try{ //try{
const result = await AsyncAuthAjax.get( API.ENDPOINTS.GET_USER_NEW_CARDS() ); const result = await AsyncAuthAjax.get( API.ENDPOINTS.GET_USER_NEW_CARDS() );
result.forEach(function(card) { result.forEach(function(card) {
Model.cards.set(card.id+'', Object.assign({},card, {seen: false})); Model.cards.set(card.category_id+'.'+card.id, Object.assign({},card, {seen: false}));
}); });
store.set('loaded.cards', true); store.set('loaded.cards', true);
......
...@@ -260,9 +260,9 @@ NS.apply = function(a,b) { ...@@ -260,9 +260,9 @@ NS.apply = function(a,b) {
el.appendChild( tmp ); el.appendChild( tmp );
var list = []; var list = [];
subEl( function(){ subEl( function(){
// TODO: append 2 TextNodes and remove children between them
for( var i = 0, _i = list.length; i < _i; i++ ){ for( var i = 0, _i = list.length; i < _i; i++ ){
el.removeChild( list[ i ] ); list[ i ].parentNode === el && el.removeChild( list[ i ] );
} }
var fragment = document.createDocumentFragment(); var fragment = document.createDocumentFragment();
D.appendChild( fragment, [].slice.call( arguments ) ); D.appendChild( fragment, [].slice.call( arguments ) );
......
...@@ -284,8 +284,11 @@ Store.IF = function(cfg, children){ ...@@ -284,8 +284,11 @@ Store.IF = function(cfg, children){
}; };
if(cfg.condition instanceof HookPrototype){ if(cfg.condition instanceof HookPrototype){
cfg.condition.hook( hook ); cfg.condition.hook( hook );
}else{ }else if(typeof cfg.condition === 'function'){
cfg.condition( hook ); cfg.condition( hook );
}else{
// TODO other hooklikes
hook(cfg.condition)
} }
} }
}; };
......
...@@ -29,7 +29,7 @@ const Consts = { ...@@ -29,7 +29,7 @@ const Consts = {
// Knowledge Endpoints // Knowledge Endpoints
GET_CARD_PRODUCTS: "/api/card_products", GET_CARD_PRODUCTS: "/api/card_products",
GET_CARD_INFOS: "/api/card_infos", GET_CARD_INFOS: "/api/card_infos",
GET_CARD_PRODUCTS_CARD: id => `/api/card_products/${id}`, GET_CARD_PRODUCTS_ITEM: id => `/api/card_products/${id}`,
GET_CARD_INFO_CARD: id => `/api/card_infos/${id}`, GET_CARD_INFO_CARD: id => `/api/card_infos/${id}`,
GET_CARD_INFO_ITEM: id => `/api/card_info_items/${id}`, GET_CARD_INFO_ITEM: id => `/api/card_info_items/${id}`,
CATEGORIES_CATEGORY: id => `/api/categories/${id}`, CATEGORIES_CATEGORY: id => `/api/categories/${id}`,
......
import { AsyncAuthAjax } from "../controller/Ajax"; import { AsyncAuthAjax } from "../controller/Ajax";
import { API } from "../dict/Consts"; import { API } from "../dict/Consts";
let latest, STORE_VERSION = 'v0.2'; let latest, STORE_VERSION = 'v0.3';
const store = new Store(latest = { const store = new Store(latest = {
_VERSION_: STORE_VERSION, _VERSION_: STORE_VERSION,
'navigation': { 'navigation': {
...@@ -77,6 +77,7 @@ const createSavableStore = function(name) { ...@@ -77,6 +77,7 @@ const createSavableStore = function(name) {
}; };
const cards = createSavableStore('cards'); const cards = createSavableStore('cards');
//const products = createSavableStore('products');
const categories = createSavableStore('categories'); const categories = createSavableStore('categories');
const Model = { const Model = {
cards: cards, cards: cards,
...@@ -84,28 +85,94 @@ const Model = { ...@@ -84,28 +85,94 @@ const Model = {
}; };
categories.loading = {}; categories.loading = {};
categories.load = async function(category, cb) {
category+=''; const AsyncResourceLoaderQueue = function(cfg) {
if(this.get(category)){ const loading = {};
cb(this.get(category)) cfg = Object.assign({
}else if(this.loading[category]){ key: (a)=>a,
this.loading[category].push(cb) arg: (a)=>a
}, cfg);
return async function() {
let arg = await cfg.arg.apply(this, arguments),
key = cfg.key.call(this, arg);
if(cfg.checkExist.call(this, arg)){
return cfg.getExisted.call(this, arg);
}else if(loading[key]){
return new Promise((resolve, reject)=>
loading[key].push({resolve, reject}));
}else{ }else{
this.loading[category] = [cb]; loading[key] = [];
try{ try{
const result = await AsyncAuthAjax.get( API.ENDPOINTS.CATEGORIES_CATEGORY( category ) ); const result = await cfg.fetch.call(this, arg);
Model.categories.set(category, result);
this.loading[category].forEach((cb)=>cb(result));
delete this.loading[category];
}catch(e){
loading[key].forEach((promise)=>{
promise.resolve(result);
});
delete loading[key];
return result;
}catch(e){
loading[key].forEach((promise)=>{
promise.reject(e);
});
throw new Error(e)
} }
} }
if(!this.loading[category]){
} }
}; };
categories.load = AsyncResourceLoaderQueue({
arg: (category)=>category+'',
checkExist(category){
return this.get(category) !== void 0
},
getExisted(category) {
return this.get(category)
},
async fetch(category){
const result = await AsyncAuthAjax.get( API.ENDPOINTS.CATEGORIES_CATEGORY( category ) );
this.set(category, result);
return result
}
});
/*categories.load = async function(category) {
return category+='';
};*/
cards.load = AsyncResourceLoaderQueue({
async arg(category, id){
const categoryInfo = await Model.categories.load(category);
return {category, id, type: categoryInfo.type, key: [category,id].join('.')}
},
key: ({key})=>key,
checkExist({key}){
let item = this.get(key);
if(item === void 0)
return false;
if(!item.detailed)
return false;
return true;
},
getExisted({key}) {
return this.get(key)
},
async fetch( {category, id, type, key}){
console.log(type)
const result = await AsyncAuthAjax.get( API.ENDPOINTS[type===1?'GET_CARD_PRODUCTS_ITEM' : 'GET_CARD_INFO_ITEM']( id ) ),
data = {...result, seen: result.isView, type: type, category_id: category, detailed: true};
this.set(key, data);
return data
}
});
/*async function(category, id, cb) {
const data = await categories.load(category);
if(data.type)
data.type
}*/
const tmpStore = new Store({ const tmpStore = new Store({
loaded: false, loaded: false,
navigation: {current: 'Login'}, navigation: {current: 'Login'},
......
...@@ -108,7 +108,7 @@ const CardSlider = (function(){ ...@@ -108,7 +108,7 @@ const CardSlider = (function(){
let firstItem = Math.round( fromLeft / ( this.preset.cardWidth + this.world.padding )); let firstItem = Math.round( fromLeft / ( this.preset.cardWidth + this.world.padding ));
this.leftButtonDisabled.set(firstItem===0); this.leftButtonDisabled.set(firstItem===0);
this.rightButtonDisabled.set(firstItem+this.world.fullCardsCount>=this.items.length); this.rightButtonDisabled.set(firstItem+this.world.fullCardsCount>=this.items.length);
console.log(this.leftButtonDisabled.get(), this.rightButtonDisabled.get()) //console.log(this.leftButtonDisabled.get(), this.rightButtonDisabled.get())
}, },
untune: function() { untune: function() {
this.updateButtonsState(); this.updateButtonsState();
......
...@@ -4,22 +4,63 @@ import ProductCard from '../productCard/ProductCard.jsx'; ...@@ -4,22 +4,63 @@ import ProductCard from '../productCard/ProductCard.jsx';
import Back from '/svg/arr-back.svg'; import Back from '/svg/arr-back.svg';
import Arr from '/svg/arr.svg'; import Arr from '/svg/arr.svg';
const InfoCard = D.declare('view.cmp.InfoCard', (cfg) => { const InfoCard = D.declare('view.cmp.InfoCard', ({item, category}) => {
console.log({item, category})
let backHidden = true,
forwardHidden = true,
sorted = [];
try{
sorted = category.cardProduct.slice().sort((a,b)=>a.id - b.id);
backHidden = sorted[0].id === item.id;
forwardHidden = sorted[sorted.length - 1].id === item.id;
}catch(e){
console.error('format changed', category);
}
let next = function(){
if( forwardHidden )
return false;
for( let i = 0, _i = sorted.length; i < _i; i++ ){
if( sorted[ i ].id === item.id ){
store.set( 'navigation.data.id', sorted[ i + 1 ].id );
break;
}
}
},
back = function() {
if(backHidden)
return false;
for( let i = 0, _i = sorted.length; i < _i; i++ ){
if( sorted[ i ].id === item.id ){
store.set( 'navigation.data.id', sorted[ i - 1 ].id );
break;
}
}
};
let content;
if(item.type === 1){
content = <ProductCard item={item} category={category}/>;
}else{
//content = <ProductCard item={item} category={category}/>;
}
return <div class="info-card"> return <div class="info-card">
<div className="info-card__header"> <div className="info-card__header">
<h1 className="info-card__title">Печенье "Орешки"</h1> <div>TYPE: {item.type}</div>
<p className="info-card__category">Категория Топ 35</p> <h1 className="info-card__title">{item.name}</h1>
<p className="info-card__category">{category.name}</p>
<div className="info-card__stats"> <div className="info-card__stats">
<p>Карточек в категории: <b>20</b></p> <p>Карточек в категории: <b>{category.count||0}</b></p>
<p>Просмотрено: <em>2</em></p> <p>Просмотрено: <em>{category.countView||0}</em></p>
</div> </div>
</div> </div>
<div className="info-card__body"> <div className="info-card__body">
<ProductCard/> {content}
</div> </div>
<div className="info-card__footer"> <div className="info-card__footer">
<Button class={"button info-card__link"}><Back width="17" height="11"/><span>Предыдущая карточка</span></Button> <Button class={D.cls("button info-card__link", {'info-card__link-hidden': backHidden})} onclick={back}><Back width="17" height="11"/><span>Предыдущая карточка</span></Button>
<Button class={"button info-card__link"}><span>Следующая карточка</span><Arr width="17" height="11"/></Button> <Button class={D.cls("button info-card__link", {'info-card__link-hidden': forwardHidden})} onclick={next}><span>Следующая карточка</span><Arr width="17" height="11"/></Button>
</div> </div>
</div> </div>
}) })
......
...@@ -111,3 +111,6 @@ ...@@ -111,3 +111,6 @@
} }
} }
} }
.info-card__link-hidden {
display: none;
}
\ No newline at end of file
import './productCard.scss'; import './productCard.scss';
import Info from "/svg/info.svg"; import Info from "/svg/info.svg";
import Format from "../format/Format";
const ProductCard = D.declare('view.cmp.ProductCard', () => { const {IF} = Store;
const ProductCard = D.declare('view.cmp.ProductCard', ({item}) => {
return <div class="product-card"> return <div class="product-card">
<div className="product-card__image"> <div className="product-card__image">
<img src="/uploads/images/product1.jpg" alt=""/> <img src={item.image} alt=""/>
</div> </div>
<div className="product-card__list"> <div className="product-card__list">
<IF condition={item.items && item.items.length}>
<h2>Ингредиенты</h2> <h2>Ингредиенты</h2>
<ul> <ul>
<li>Вареная сгущенка </li> {
<li>Мука </li> item.items.map(
<li>Масло сливочное </li> subItem=>
<li>Сахар </li> <li>{subItem.name}</li>)
<li>Ингредиент</li> }
</ul> </ul>
</IF>
</div> </div>
<div className="product-card__text"> <div className="product-card__text">
<Info width="30" height="30"/> <Info width="30" height="30"/>
<p><b>Важно знать.</b>Информация о&nbsp;продукте. Все, что вы&nbsp;хотели знать, но&nbsp;боялись спросить.</p> <p><b>Важно знать.</b></p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi lacinia augue quam, in&nbsp;dapibus nulla ullamcorper nec. Mauris ac&nbsp;magna est. Vivamus dapibus venenatis nisi, sit amet dictum tellus aliquam sit amet. Proin sit amet suscipit nisl. </p> <Format>{item.text}</Format>
<p>Mauris ac&nbsp;magna est. Vivamus dapibus venenatis nisi, sit amet dictum tellus aliquam sit amet. Proin sit amet suscipit nisl.</p>
</div> </div>
</div> </div>
}) })
......
...@@ -13,24 +13,30 @@ const InfoPage = D.declare('view.page.InfoPage', () => { ...@@ -13,24 +13,30 @@ const InfoPage = D.declare('view.page.InfoPage', () => {
</button> </button>
<div class="info-page__card"> <div class="info-page__card">
{( update ) => { {( update ) => {
let un, un2; let un;
tmpStore.valEqualOnly( 'afterNavigation.to', 'InfoPage' )( async() => { tmpStore.valEqualOnly( 'beforeNavigation.to', 'InfoPage' )(() => {
update(<div>LOADER</div>)
un && un(); un && un();
un2 && un2(); un = store.sub(['navigation.data.category', 'navigation.data.id'], async function(categoryID, cardID) {
un = store.sub('navigation.data.category', function(category) {
Model.categories.load(category, function(data) {
debugger
});
const category = await Model.categories.load(categoryID);
const card = await Model.cards.load(categoryID, cardID);
update(<InfoCard category={category} item={card}/>)
/*
if(card.type === 1){
/!*update(<StdCard item={card} category={cat}/>)
}else{*!/
}
*/
}); });
console.log( 'data', store.get( 'navigation.data' ) ) console.log( 'data', store.get( 'navigation.data' ) )
} ); } );
}} }}
<InfoCard/>
</div> </div>
</div> </div>
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment