shopr

Check-in [08777612d3]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add items. Start of item menu.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:08777612d33ffb2b2f942e187817ebbdf6da6a87e2c0eb60e1a1bdafe68a2693
User & Date: vandys 2019-06-22 22:41:01
Context
2019-06-23
15:44
Start laying out more menu stuff check-in: 0524a8bdc1 user: vandys tags: trunk
2019-06-22
22:41
Add items. Start of item menu. check-in: 08777612d3 user: vandys tags: trunk
18:23
Filtering bringup check-in: 3848b64d45 user: vandys tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to css/shopr.css.

     7      7   span {
     8      8    font-size: large;
     9      9    padding: 4px;
    10     10   }
    11     11   #scrlist2 {
    12     12    margin-bottom: 15px;
    13     13   }
    14         -#scrlist2 > span, #scrlist2 > input {
           14  +#scrlist2 > span, #scrlist2 > input, #scrlist2 > button {
    15     15    border: 3px solid black;
    16     16    border-radius: 6px;
    17     17   }
    18     18   #spanR {
    19     19    background: pink;
    20     20   }
    21     21   #spanG {
................................................................................
    48     48    background: yellow;
    49     49   }
    50     50   #scrlists2 > span {
    51     51    background: pink;
    52     52    border: 3px solid black;
    53     53    border-radius: 6px;
    54     54   }
           55  +
           56  +#scritem {
           57  + background: khaki;
           58  + position: fixed;
           59  + top: 10vh;
           60  + left: 10vw;
           61  + width: 80vw;
           62  + height: 80vh;
           63  + border: 1px solid black;
           64  + z-index: 999;
           65  +}

Changes to html/shopr.html.

    29     29    <div id="scrlist2">
    30     30     <span style="background: white;">+RGY</span>
    31     31     <span id="spanR" onclick="filtR();">R</span>
    32     32     <span id="spanG" onclick="filtG();">G</span>
    33     33     <span id="spanY" onclick="filtY();">Y</span>
    34     34     <input type="text" id="ifilter"
    35     35      onkeyup="update_filter();" placeholder="Choose or Add">
    36         -  <span>+</span>
           36  +  <button onclick="addit();">+</button>
    37     37     <img src="/imgs/menu.svg" style="float: right;" />
    38     38    </div>
    39     39    <div id="items">
    40     40    </div>
    41     41   </div>
    42     42   
    43     43   <div id="scrmenu" style="display: none;">
    44     44   </div>
    45     45   
    46     46   <div id="scritem" style="display: none;">
           47  +<input type="text" placeholder="Item Name" id="itname">
           48  +<button onclick="new_iname();">Update Name</button>
           49  +<hr>
           50  +<button onclick="del_iname();">Delete Item</button>
           51  +<button onclick="cancel_scritem();">Cancel</button>
    47     52   </div>
    48     53   
    49     54   <script>
    50     55   init_page();
    51     56   </script>
    52     57   
    53     58   </body>
    54     59   </html>

Changes to items.py.

    77     77   	    return False
    78     78   	l = self.items[row]
    79     79   
    80     80   	# Update item, which flags change to list
    81     81   	l.assign(col)
    82     82   
    83     83   	return True
           84  +
           85  +    # Add an item to this list
           86  +    #
           87  +    # Return None on failure, else new Item
           88  +    def new_item(self, name):
           89  +	# No dups, please
           90  +	ln = name.lower()
           91  +	for i in self.items:
           92  +	    if i.name.lower() == ln:
           93  +		return None
           94  +
           95  +	# Create data locally
           96  +	i = Item(self, 0, name)
           97  +
           98  +	# Put in DB
           99  +	db = self.get_db()
          100  +	c = db.cursor()
          101  +	c.execute("insert or replace into items values(?,?,?)",
          102  +	    (self.lid, name, 0));
          103  +	db.commit()
          104  +	c.close()
          105  +	db.close()
          106  +
          107  +	# Ok
          108  +	self.items.append(i)
          109  +	self.next_gen()
          110  +	return i
    84    111   
    85    112   class Item(object):
    86    113       def __init__(self, l, idx, name):
    87    114   
    88    115   	# Our parent list
    89    116   	self.parent = l
    90    117   
    91         -	# Always start at first index type (usually red)
    92         -	# When -1, this is a deleted item
    93         -	self.idx = 0
          118  +	# Current indexed state
          119  +	self.idx = idx
    94    120   
    95    121   	# Item's string name
    96    122   	self.name = name
    97    123   
    98    124       # JSON friendly rep of our state
    99    125       def for_json(self):
   100    126   	assert self.idx != -1

Changes to js/shopr.js.

    97     97   //  the server apparently never heard about the change.
    98     98   function set_bright(e) {
    99     99       e.style.border = "6px solid white";
   100    100       cur_bright = e;
   101    101       tmo_bright = setTimeout(cancel_bright, 3000);
   102    102   }
   103    103   
   104         -// Long hold; mostly no-op, menu on current element
          104  +// Back to item list
          105  +function cancel_scritem() {
          106  +    setcur(scrlist);
          107  +}
          108  +
          109  +// Long hold; menu on current element
   105    110   function hold_item(e) {
   106         -    console.log("TBD: hold_item");
          111  +    if (!e.id.startsWith("item")) {
          112  +	return false;
          113  +    }
          114  +    itname.value = e.innerText;
          115  +    setcur(scritem);
   107    116   }
   108    117   
   109    118   // Click an item, probably switch the index of the item
   110    119   function click_item(e) {
   111    120       // Tap on already selected item index?
   112    121       if (e.textContent) {
   113    122   	return false;
................................................................................
   330    339       // Adjust what's hidden based on latest input
   331    340       let ifv = null;
   332    341       if (ifilter.value) {
   333    342   	ifv = ifilter.value.toLowerCase();
   334    343       }
   335    344       let showing = null;
   336    345       for (let i of cur_names.items) {
   337         -	if ((ifv == null) || i.name.toLowerCase().contains(ifv)) {
          346  +	if ((ifv == null) || i.name.toLowerCase().includes(ifv)) {
   338    347   	    showing = "inline";
   339    348   	} else {
   340    349   	    showing = "none";
   341    350   	}
   342    351   
   343    352   	// Update display attribute
   344    353   	for (let e of i.elems) {
   345    354   	    e.style.display = showing;
   346    355   	}
   347    356       }
   348    357   }
          358  +
          359  +// Text input, introduce item with this name
          360  +function addit() {
          361  +    // No text, or only whitespace
          362  +    if (!ifilter.value) {
          363  +	return false;
          364  +    }
          365  +    const ifv = ifilter.value.trim();
          366  +    if (!ifv) {
          367  +	return false;
          368  +    }
          369  +
          370  +    // Dup?
          371  +    const ifs = ifv.toLowerCase();
          372  +    for (let i of cur_names.items) {
          373  +	if (i.name.toLowerCase() == ifs) {
          374  +	    return false;
          375  +	}
          376  +    }
          377  +
          378  +    // Ok, new item
          379  +    const req = new XMLHttpRequest();
          380  +    req.onreadystatechange = () => {
          381  +	if (req.readyState != 4) {
          382  +	    return;
          383  +	}
          384  +
          385  +	if (req.status != 200) {
          386  +	    if (req.status == 403) {
          387  +		// Changed, password, deleted account, ???
          388  +		setcur(scrlogin);
          389  +		alert("Login credentials failed, please try again");
          390  +	    } else {
          391  +		alert("Failed to add item");
          392  +	    }
          393  +	    return;
          394  +	}
          395  +
          396  +	// Server seems happy.  Display update will come via the
          397  +	//  usual long polling.
          398  +	ifilter.value = "";
          399  +	return;
          400  +    };
          401  +
          402  +    // Construct URL; our list, authen
          403  +    let url = "/l/" + encodeURIComponent(cur_list) +
          404  +	".json" + authURL;
          405  +
          406  +    // Requesting URL, including authentication
          407  +    req.open("POST", url);
          408  +    const op = {"op": "add", "name": ifv};
          409  +    req.send(JSON.stringify(op));
          410  +}
   349    411   
   350    412   // Choose a list
   351    413   function sel_list(e) {
   352    414       cur_list = e.currentTarget.textContent;
   353    415       cur_names = null;
   354    416       paint_list();
   355    417       return false;

Changes to main.py.

   103    103   	c.execute("select lid,global from lists where owner=? and name=?",
   104    104   	    (uid, name))
   105    105   	tup = c.fetchone()
   106    106   	if tup is not None:
   107    107   	    lid,isglob = tup
   108    108   	    res = self.lists[ (uid, name) ] = \
   109    109   		items.List(self, lid, bool(isglob), name)
          110  +	    if isglob:
          111  +		self.lists[name] = res
   110    112   	    return res
   111    113   
   112    114   	# Now global DB's with this name
   113    115   	c.execute("select lid from lists where name=? and global=1",
   114    116   	    (name,))
   115    117   	tup = c.fetchone()
   116    118   	if tup is None:

Changes to post.py.

     1      1   #
     2      2   # post.py
     3      3   #	Mixin to implement HTML POST operations
     4      4   #
     5         -import urllib
            5  +import json, urllib
            6  +import pdb
     6      7   
     7      8   # The POST part of our handling
     8      9   class POST_mixin(object):
     9     10   
    10     11       # Configure our WPlayer GET treatment
    11     12       def __init__(self):
    12     13   
    13     14   	# POST handlers
    14     15   	self.dispatchers.append( ("POST", self.post_newlist) )
    15         -	self.dispatchers.append( ("POST", self.post_newitem) )
           16  +	self.dispatchers.append( ("POST", self.post_op_item) )
    16     17   
    17     18       # A new list requested?
    18     19       def post_newlist(self, buf):
    19     20   	return False,None
    20     21   
    21     22       # A new item requested?
    22         -    def post_newitem(self, buf):
           23  +    # /l/<listname>.json
           24  +    def post_op_item(self, buf):
           25  +	pp = self.paths
           26  +	if (len(pp) != 2) or (pp[0] != "l") or (not pp[1].endswith(".json")):
           27  +	    return False,None
           28  +
           29  +	if not self.authenticated():
           30  +	    return True,self.send_error(403)
           31  +
           32  +	# List name, without .json
           33  +	lname = urllib.unquote_plus(pp[1][:-5]).strip()
           34  +	if not lname:
           35  +	    return False,None
           36  +
           37  +	# Decode JSON op
           38  +	try:
           39  +	    req = json.loads(buf)
           40  +	    op = str(req["op"])
           41  +	    if op == "add":
           42  +		name = str(req["name"]).strip()
           43  +		if not name:
           44  +		    return True,self.send_error(400, "Bad item name")
           45  +	    else:
           46  +		return True,self.send_error(400, "No such op")
           47  +	except:
           48  +	    return True,self.send_error(400, "Bad request format")
           49  +
           50  +	# New item
           51  +	approot = self.server.approot
           52  +	if op == "add":
           53  +	    # Under this list
           54  +	    l = approot.get_list(self.uid, lname)
           55  +	    if l is None:
           56  +		return True,self.send_error(404)
           57  +
           58  +	    i = l.new_item(name)
           59  +	    if i is None:
           60  +		return True,self.send_error(400, "Item add failed")
           61  +
           62  +	    return True,self.send_json({"ok": True})
           63  +
    23     64   	return False,None