Xây dựng ứng dụng web sẵn có cho iPhone của riêng tôi


Phạm Công Án
4 năm trước
Hữu ích 2 Chia sẻ Viết bình luận 0
Đã xem 4605

Trước khi tôi bắt đầu, từ chối trách nhiệm nhanh chóng: Những gì tôi đang xây dựng ở đây hoàn toàn là để giải trí và vì tôi nghĩ nó có thể thú vị. Tôi đang phê bình và cải thiện một hình thức được xây dựng bởi những người thông minh hơn tôi và phù hợp hơn với 99,99% người dùng. Về cơ bản, tôi đã thấy một cái gì đó tôi muốn xây dựng và tôi đã làm nó.

Tôi hiện đang là chủ sở hữu của một chiếc điện thoại HTC M8 - bước đột phá của tôi trở lại Android sau khi sử dụng iPhone cho một vài phiên bản. Tôi thích giao diện người dùng HTC và nói chung, điện thoại khá khó tin, nhưng sau bản cập nhật hệ điều hành Android gần đây nhất, điện thoại của tôi bắt đầu trở nên chậm chạp hơn. Nó đã đến một điểm mà chỉ cần mở điện thoại để chụp ảnh sẽ mất 30-60 giây để nó phản hồi. Các cuộc gọi điện thoại mà tôi không nhận được nhiều cuộc gọi thậm chí còn tệ hơn. Khi tôi bỏ lỡ một cuộc gọi vì giao diện người dùng cơ bản của điện thoại của tôi sẽ không phản hồi, tôi gần như đã ném thứ đó vào nhóm. Tôi đã thử nhiều thứ nhưng cuối cùng lau điện thoại và khôi phục lại từ một bản sao lưu. Nó đã giúp đỡ, nhưng điện thoại vẫn còn chunky. Tôi quyết định đã đến lúc chuyển về iOS và tôi nghĩ rằng iPhone 6S + sẽ là một chiếc điện thoại tuyệt vời để chọn. Tôi cũng quyết định rằng chương trình nâng cấp iPhone mớisẽ là một phù hợp tốt. Từ những gì tôi đã đọc, nó tốt hơn chương trình tiếp theo của ATT. Vấn đề duy nhất là bạn phải đến một cửa hàng Apple để đăng ký chương trình. Cửa hàng Apple gần nhất của tôi là ở Baton Rouge, cách đó khoảng một giờ. Đáng giá một ổ đĩa, nhưng chỉ khi tôi biết tôi sẽ có một thiết bị ở đó để nhận.

May mắn thay, Apple có một hình thức tuyệt vời mà bạn có thể sử dụng để xem điện thoại mong muốn của bạn có khả dụng hay không. Bạn chọn trạng thái, cửa hàng của bạn, mô hình của bạn và sau đó là nhà cung cấp dịch vụ của bạn:

Như bạn có thể thấy, không có sẵn. (Thở dài.) Dĩ nhiên, bạn có thể chuyển sang không có SIM (và tôi đã kiểm tra, HTC và 6S + của tôi sử dụng cùng loại SIM). Điều khiến tôi bận tâm về hình thức này là một vài vấn đề.

  • Trước hết - bạn không thể sử dụng nó trước 8 giờ sáng. Không, chờ đã, ngừng cười, tôi nghiêm túc đấy. Đây là một hệ thống dựa trên web với giờ mở cửa của EDT như một cửa hàng bán lẻ. Có lẽ có một lý do dữ liệu cho điều đó. Tôi đã nói chuyện với một đại diện của Apple vào tuần trước và họ đã đề cập rằng họ nhận được dữ liệu hàng tồn kho mới vào lúc 8. Tôi muốn tưởng tượng rằng các cửa hàng Apple có một số móc thời gian thực tinh vi vào hàng tồn kho nhưng có lẽ không phải vậy. Tuy nhiên, thật đáng kinh ngạc khi nhìn thấy một dấu hiệu đóng kín của YouTube tại một trang web.
  • Khi tôi ở CA tuần trước, tôi đã cố gắng tìm kiếm xung quanh tôi. Mỗi khi bạn chuyển đổi cửa hàng, các hình thức xây dựng lại. Vì vậy, nếu tôi đã chọn 6S + và ATT, tôi sẽ mất các lựa chọn đó. Bây giờ, lý do cho điều này có ý nghĩa. Có thể cửa hàng khác không có sẵn 6S + hoặc ATT, nhưng nó vẫn gây phiền nhiễu. Đó là loại vấn đề mà mã mặt trước thông minh có thể xử lý một cách duyên dáng. Có 5-6 cửa hàng xung quanh tôi ở Nam San Francisco và tôi đã kiểm tra mỗi ngày ở đó và những thứ chết tiệt đó làm tôi khó chịu mỗi ngày. (Như tôi đã nói ở trên, có lẽ tôi không phải là người dùng mục tiêu ở đây.)
  • Cuối cùng, thật tuyệt nếu tôi có thể nói một cách đơn giản, đó là nói với tôi khi 6S + cho ATT hoặc không có SIM có màu xám hoặc bạc có 64 GB kể từ 16 chỉ là ngu ngốc. Không gặp khó khăn gì khi bán iPhone nên một hệ thống như vậy có lẽ không phải là ưu tiên cao đối với họ. (Và rõ ràng, đây chỉ là chương trình nâng cấp. Rõ ràng cửa hàng 'thông thường' cho phép bạn mua ngay bây giờ.)

Vì vậy - chán vào cuối tuần này - Tôi đã làm những gì mà bất kỳ nhà phát triển web tự tôn nào làm - Tôi đã mở các công cụ dev trong khi sử dụng biểu mẫu. Điều đầu tiên tôi nhận thấy là ứng dụng đã nhấn các tệp JSON để điều khiển thả xuống:

Sau đó tôi đã mở từng tệp đó và xem JSON. stores.jsonlà một danh sách theo nghĩa đen của tất cả các cửa hàng có sẵn. Đây là một đoạn:

"stores" : [ {
    "storeNumber" : "R414",
    "storeName" : "4th Street",
    "storeEnabled" : false,
    "storeState" : "California",
    "sellEdition" : false,
    "storeCity" : "Berkeley"
  }, {
    "storeNumber" : "R177",
    "storeName" : "ABQ Uptown",
    "storeEnabled" : true,
    "storeState" : "New Mexico",
    "sellEdition" : false,
    "storeCity" : "Albuquerque"
  }, {

availability.jsondữ liệu sẵn có của khóa học. Đây là một đoạn trích từ nó:

  "R327" : {
    "MKVJ2LL/A" : "NONE",
    "MKQA2LL/A" : "ALL",
    "MKT62LL/A" : "ALL",
    "MKQX2LL/A" : "ALL",
    "MKR92LL/A" : "ALL",
    "MKVV2LL/A" : "NONE",
    "MKW72LL/A" : "NONE",
    "MKRQ2LL/A" : "ALL",
    "MKTM2LL/A" : "NONE",
    "MKQ62LL/A" : "ALL",
    "MKTA2LL/A" : "ALL",
    "MKT72LL/A" : "ALL",
    "MKRR2LL/A" : "ALL",
    "MKV32LL/A" : "NONE",
    "MKVW2LL/A" : "NONE",
    "MKW82LL/A" : "NONE",
    "MKTN2LL/A" : "NONE",
    "MKRE2LL/A" : "ALL",
    "MKR82LL/A" : "ALL",
    "MKWD2LL/A" : "NONE",
    "MKQ72LL/A" : "ALL",
    "MKRC2LL/A" : "ALL",
    "MKVX2LL/A" : "NONE",
    "MKW92LL/A" : "NONE",
    "MKVU2LL/A" : "ALL",
    "MKW62LL/A" : "NONE",
    "MKRF2LL/A" : "ALL",
    "MKUQ2LL/A" : "NONE",
    "MKV22LL/A" : "NONE",
    "MKQY2LL/A" : "ALL",
    "MKTY2LL/A" : "NONE",
    "MKV52LL/A" : "ALL",
    "MKT92LL/A" : "ALL",
    "MKT32LL/A" : "ALL",
    "MKQ82LL/A" : "ALL",
    "timeSlot" : {
      "en_US" : {
        "timeslotTime" : "11:00 AM",
        "contractTimeslotTime" : "11:00 AM"
      }
    },

Chìa khóa ở đó là cửa hàng và từng chi tiết đơn hàng (ngoại trừ timeSlot) đại diện cho một chi tiết đơn hàng mô hình / màu / nhà cung cấp / kích thước. Vì vậy, tôi có thể lấy dữ liệu (nhấp chuột phải vào công cụ dev và mở chúng trong tab mới, sau đó lưu dưới dạng), tôi bắt đầu làm việc trên một ứng dụng web cho phép tôi phân tích dữ liệu theo cách riêng của mình. Cụ thể tôi muốn một vài điều:

  • Hãy để tôi chỉ định một cửa hàng, và sau đó nhiều cửa hàng.
  • Hãy để tôi chỉ định bất kỳ mô hình tôi muốn.
  • Hãy để tôi chỉ định nhiều hãng.

Tôi cũng muốn bỏ qua 16GB, nhưng cuối cùng đã quyết định chống lại điều đó. Tôi bắt đầu làm việc với mã riêng của mình, nó sẽ hút các tệp JSON (bản sao cục bộ của tôi) và để tôi tự phân tích nó. Tôi sẽ hiển thị kết quả trước và sau đó nói về mã. Và vâng - của tôi thì kém hơn Apple rất nhiều.

Trên đầu bạn có thể thấy một trạng thái thả xuống và lưu trữ bộ chọn. Như tôi đã nói, kế hoạch ban đầu của tôi là cung cấp thêm nhiều cửa hàng, nhưng tôi không bao giờ có được điều đó.

Bên dưới nó, bạn có thể thấy các lựa chọn nhà cung cấp và mô hình. Dưới đây là lưới các tùy chọn. Tôi đã sử dụng CSS (woot) để tùy chọn màu xám / mờ không có sẵn. Làm thế nào tôi có được màu iPhone của Apple? Bạn có biết Firefox có một công cụ chọn màu được tích hợp sẵn cho các công cụ dev của họ không?

Các vòng tròn trên cửa hàng Apple thực sự có độ dốc đẹp khi bạn di chuyển từ trung tâm vòng tròn ra bên ngoài. Tôi chỉ cần nhấp vào Lọ vào giữa ish để có được một giá trị phù hợp với tôi.

Ok, vậy bây giờ chúng ta hãy đi vào mã mã. Tôi bắt đầu với một thói quen thiết lập đơn giản:

$(document).ready(function() {
console.log("Make it so.");

//load json files
var storeReq = $.getJSON("data/stores.json");
var availReq = $.getJSON("data/availability.json");
$.when(storeReq,availReq).then(function(stores, avail) {
storeList = stores[0].stores;
availabilityData = avail[0];
doStoresForStates();
doStateDropDowns();
startUp();
});
});

Tôi sẽ bỏ qua doStoresForStates, điều đó chỉ đơn giản là cung cấp cho tôi một biến gốc được khóa bởi trạng thái bao gồm một loạt các cửa hàng. doStateDropDowns điền vào trạng thái thả xuống. Như tôi đã đề cập, ý tưởng là làm cho nó để bạn có thể thêm nhiều cửa hàng, nhưng tôi không bao giờ có được điều đó.

function doStateDropDowns() {
var dds = $(".stateDD");
//generate the option HTML list, but only once
if(stateOptionHTML === "") {
var states = [];
for(var i=0;i<storeList.length;i++) {
if(states.indexOf(storeList[i].storeState) === -1) states.push(storeList[i].storeState);
}
states.sort();
var s = "
-- State --"; for(var i=0;i<states.length;i++) { s += " " + states[i] + ""; } stateOptionHTML = s; } dds.each(function(index) { console.log("doing "+index); var options = $("option", this); if(options.length === 0) { $(this).html(stateOptionHTML); } }); }

Trạng thái thả xuống có một người nghe sự kiện để đáp ứng với những thay đổi. Điều duy nhất tốt đẹp ở đây là việc sử dụng next("select")để có được thả xuống bên cạnh nó.

function doStores() {
var selected = $(this).val();
if(selected === "") return;
var storeHTML = "
-- Location --"; for(var i=0;i " + storeData[selected][i].city + ", " + storeData[selected][i].name + ""; } $(this).next("select").html(storeHTML); }

Ok, vậy bây giờ đã đến phần đáng sợ - xây dựng các trò chơi thực tế dựa trên những gì bạn chọn, lọc kết quả công cụ. Vấn đề đầu tiên tôi gặp phải là dữ liệu mô hình. Như tôi đã nói, mỗi mô hình / công suất / màu sắc / nhà cung cấp có một ID duy nhất. Tôi có thể đã gõ tất cả bằng tay, nhưng thay vào đó, tôi đã sử dụng các công cụ dev:

var ray = [];$(".form-choice-selector").each(function(idx) { ray.push($(this).val()); }); copy(JSON.stringify(ray));

Những gì bạn đang thấy là mã tôi chạy trong bảng điều khiển trình duyệt. Nó đã tìm nạp từng ô di động của màn hình, nhận giá trị (là id sản phẩm) và sau đó được sử dụng copyđể đặt nó vào bảng ghi của tôi. Sau đó tôi có thể dán vào mã của mình. Có 5 nhà mạng và 2 mô hình, vì vậy tôi đã phải làm điều này 10 lần, nhưng phải mất tất cả 1 hoặc 2 phút để nó không phải là một vấn đề lớn.

Mã lọc là một mớ hỗn độn nóng lớn. Như nghiêm túc. Nó có vẻ hoạt động, nhưng tôi không bảo hành nó. Đây rồi - đừng cười quá nhiều với tôi.

function doFilter() {
//get all locations
var locations = [];
var selectedModels = [];
var selectedCarriers = [];

$(".locationDD").each(function(idx) {
if($(this).val() != '') locations.push($(this).val());
});

//if no locations, do nothing
if(locations.length === 0) return;

$(".modelCB").each(function(idx) {
if($(this).is(':checked')) {
selectedModels.push($(this).val());
};
});

$(".carrierCB").each(function(idx) {
if($(this).is(':checked')) {
selectedCarriers.push($(this).val());
};
});

console.log("Begin to filter. "+JSON.stringify(locations)+" "+JSON.stringify(selectedModels)+" "+JSON.stringify(selectedCarriers));

/*
logic is: for each color/capacity, determine if ON/OFF
*/
for(var i=0;i<capacityData.length;i++) {
var capacity = capacityData[i];
for(var j=0;j<colorData.length;j++) {
var color = colorData[j];
var models = getModels(capacity, color, selectedCarriers, selectedModels);

console.log("check "+capacity+" "+color+" models="+JSON.stringify(models));

var enabled = false;

//did we filter by location?
if(locations.length > 0) {
for(var z = 0;z<locations.length;z++) {
var location = locations[z];
for(var k=0;k<models.length;k++) {
//console.log(availabilityData[location][models[k]]);
if(availabilityData[location][models[k]] === "ALL") {
enabled=true;
break;
}
}
}
}
console.log("ENABLED",enabled);
var cell = $("." + color + ".cap" + capacity);
if(!enabled) {
cell.addClass("outofstock");
} else {
cell.removeClass("outofstock");
}
}
}
}

Về cơ bản - Tôi lặp qua một mảng công suất và màu và sau đó kiểm tra tính khả dụng tại mỗi vị trí. (Một lần nữa, hãy nhớ rằng tôi sẽ hỗ trợ nhiều vị trí.) getModelsLà một hàm tiện ích phân tích dữ liệu mô hình mà tôi lượm lặt được từ các công cụ dev. Sau đó tôi chỉ cần thêm vào / xóa một lớp CSS để thêm ảnh hưởng màu xám / mờ đẹp.

That was the front end. In order to keep the application up to date I wrapped the whole thing up in a Node.js app running on IBM Bluemix. All I needed was the ability to suck down the JSON files from Apple on a scheduled basis, and for that I used a cron library I used over at ColdFusion Bloggers. Here is the entirety of the app:

/*eslint-env node*/

var https = require('https');
var fs = require('fs');

// This application uses express as its web server
// for more info, see: http://expressjs.com
var express = require('express');

// cfenv provides access to your Cloud Foundry environment
// for more info, see: https://www.npmjs.com/package/cfenv
var cfenv = require('cfenv');

// create a new express server
var app = express();

// serve the files out of ./public as our main files
app.use(express.static(__dirname + '/public'));

// get the app environment from Cloud Foundry
var appEnv = cfenv.getAppEnv();


//fire and forget sync method
var sync = function() {
var writeStream1 = fs.createWriteStream('./public/data/availability.json');
https.request('https://reserve.cdn-apple.com/US/en_US/reserve/iPhone/availability.json', function(res) {
res.pipe(writeStream1);
}).end();

var writeStream2 = fs.createWriteStream('./public/data/stores.json');
https.request('https://reserve.cdn-apple.com/US/en_US/reserve/iPhone/stores.json', function(res) {
res.pipe(writeStream2);
}).end();
}

var cron = require('cron');
var cronJob = cron.job('* */2 * * *', function() {
sync();
console.log('cron job complete');
});
cronJob.start();

// start server on the specified port and binding host
app.listen(appEnv.port, function() {
// print a message when the server starts listening
console.log("server starting on " + appEnv.url);
});

I don’t have any error handling on the sync function so it is brittle as heck, but it gets the job down. I also set it up to hit Apple once ever 2 hours. I figured that was gentle and wouldn’t over tax Apple.com. I also built in a route I could use for manual testing, but I removed that when I deployed it up to Bluemix.

You can see it yourself here: http://applestorechecker.mybluemix.net/. As I said, it is somewhat brittle. I could also add a simple ‘data files last updated at X’ to the header so you know how fresh the data is. If I weren’t being lazy, I could also add the ability for you to register when your desired model/color/carrier/store has product available, but, alas, I’m lazy today.

Any way, check it out, and let me know if you have any questions!

Hữu ích 2 Chia sẻ Viết bình luận 0
Đã xem 4605