What are we doing for things we want?
What are we doing for things we want?
Mac Mini ဝယ်ရမလား iPad Pro ဝယ်ရမလား?
အလုပ်အတွက်ကတော့ Mac Mini က ပိုကောင်းတယ်။ ပိုပြီးအသုံးဝင်မယ်။ iPad Pro ကတော့ ကိုယ့်ရဲ့ အာသီသအတွက်။ အလုပ်အတွက်လည်း အနည်းနဲ့အများ အထောက်အကူပြုမယ်ဆိုပေမယ့် Mac Mini နဲ့ တော့ မယှဉ်သာဘူး။
နှစ်ခုလုံး ဝယ်နိုင်အောင် လုပ်တာပေါ့။
MAP-BY-KMT က Ads ကိစ္စ ပြီးရင် အကုန်ပြီးပြီ ပြောရမယ်။
iPad Pro အတွက်က Movie Streaming ကို အာရုံစိုက်ရမှာပေါ့ ။ ဒီ Janaray တစ်လ အာရုံစိုက်ပြီးလုပ်ရင် ရမယ်ထင်တယ်။
iPad Pro က instance pleasure ကို ရမယ်။ second screen ရမယ်။ pan နဲ့ ဘာနဲ့ စာသင်တဲ့အခါ အဆင်ပြေကောင်း ပိုပြေလာမယ်။
mac mini ကတော့ ပြန်ရောက်တဲ့အခါ ပိုပြီး Professional ဆန်လာမယ်။
နှစ်ခုလုံးကတော့ မြန်မာနိုင်ငံမှာဆို ဝယ်ဖို့ အဆင်မပြေတဲ့ ပစ္စည်းတွေ။ စျေးနဲ့ availability
နောက်တစ်ခုက ဒီမှာ Encraving ရတယ်။ ဒါကလည်း iPad Pro ဝယ်ဖို့အတွက် တွန်းအားတစ်ခုပဲ။
စိတ်ထဲမှာတော့ iPad Pro က အားသာနေတယ်။ ဒါသုံးပြီး လုပ်ငန်းအရ အထောက်အကူပြုမှာလားဆိုတာကတော့ သိပ်ပြီး ထိရောက်မှာ မဟုတ်ဘူး။
iPad Pro အရင်ဝယ်ပြီး နောက်လမှ Mac Mini ဝယ်ရင်ကောင်းမလား? ဒါကတော့ ဘုရင့်နောင် ဖောင်ဖျတ်တဲ့ ကိန်းပဲ။
256 SSD တစ်ခုလည်း ဝယ်ရမယ်။ SanDisk က ထုတ်တာလေး စျေးလည်းမဆိုးဘူး။ Design ကလည်း Type C နဲ့ Type A နှစ်ခုလုံးပါတော့ မိုဘိုင်းရော ကွန်ပျူတာရော အဆင်ပြေမယ့် သဘော။
Mac Mini က M2 စောင့်လိုက်ပြီး M2 ပါတဲ့ iPad Pro အရင်ဝယ်လိုက်မယ်။ :D
KM ❤️ MSD 2022-02-04
// TODO: make video about ordering ipad pro in india with Graving
အိုကေ 80,000 တန် iPad Pro တစ်လုံး ဝယ်မယ်။
mahar-by-ksmt ဒါကတော့ အိမ်အတွက် ရည်ရွယ်ထားလိုက်မယ်။ သိန်း (၃၀) တစ်လ ၊ နာရီပေါင်း ၂၄၀ =
လုပ်နိုင်တယ်လို့ အရင်ယုံထားရမယ်။ လိုချင်တာတွေကတော့ အများကြီးပေါ့။ စိတ်တွေတောင် နောက်ကျိသွားဉီးမယ်။ တစ်ခုချင်းစီကို အာရုံစိုက်ပြီးလုပ်ပါ။ ရှောင်သင့်တာတွေရှောင်။ စိတ်ကလေးကို တစ်ခုတည်းမှာ စူးစိုက်ပြီး အလုပ်လုပ်။
ကျွဲကူးရေပါ ဖြစ်အောင်က Chat ကို MAP မှာ ထည့်ပေးရမယ်။
comment စနစ် အရင်ထည့်ရမယ်။
wordpress မှာ မတွဲဘဲ ဒီ app တစ်ခုလုံးအတွက် သီးသန့် စနစ်တစ်ခု လုပ်ထားရမယ်။
database က နှစ်ခု ဖြစ်နေမယ်။ wordpress က အဖွဲ့တွေက Server to Server API နဲ့ ပြောရမယ့် သဘောမှာ ရှိမယ်။ data ယူတာ ထည့်တာ နဲ့ ပတ်သက်ပြီ။ data ထည့်တာကတော့ client ဘက်က တိုက်ရိုက်လုပ်လို့ရမယ်။ data ထုတ်တဲ့နေရာမှာ wordpress ကနေ ဒီဘက်က chat-solution ကို api ကနေပဲ လှမ်းမေးရမယ်။
User Account
ကိုယ်ပိုင် email , password နဲ့ ပဲ အရင် register လုပ်ခိုင်းမယ်။ နောက်ပိုင်းမှာ firebase auth နဲ့ verification လုပ်တာမျိုးတွေ လုပ်မယ်။
Public Chatroom
စာ၊ ပုံ , Video , Audio , File အကုန်ပို့နိုင်မယ်။ လောလောဆယ် ဖိုင် size ကို limit လုပ်ထားမယ်။
one to one chat
message
from user_id to user_id | group_id type one_to_one | group
What are we doing?
books in map-by-kmt
that’s also great.
flutter-dev
from zero to hero
This Evening
- OOAK -> filter and pagination with query builder for multiple filter
POS
Chat Solution
- ui layout
- Basic Entity
mahar-by-ksmt
- firebase realtime - database
100 - 5 USD / month
5 * 3000 => 15,000 per month
User တစ်ရာကို 15,000
User တစ်ယောက်ကို 150 လောက် ကျမယ်။ ဒါက server ဖိုး။
Cloud Function နဲ့ Realtime Database ဒါပဲ သုံးမယ်။
architecture အရတော့
Admin Mobile နဲ့ Client Mobile သွားမယ်။
Firebase Realtime Database
JSON တစ်ခုပဲ။
Flutter မှာ ဘယ်လိုလုပ်ကြမလဲ?
install firebase_core in flutter add init code in main()
firebase console မှာ realtime database တစ်ခု create လုပ်။ test mode ဖွင့်ထား နောက်ပိုင်းမှာ Locked Mode ဖွင့်ထားမယ်။ client တွေစီက write request တွေ လက်မခံပဲ ကိုယ်သတ်မှတ်တဲ့ server က လာတဲ့ operation တွေကိုပဲ လက်ခံမယ့် သဘော။
install firebase_database package in flutter
FirebaseDatabase database = FirebaseDatabase.instance;
DatabaseReference ref = FirebaseDatabas.instance.ref("users/123");
// write , overwrite all key and value
await ref.set(
{
"name": "John"
}
);
// update name, leave age, and sex with old data
await ref.set(
{
"name": "John"
}
);
/* နားမလည်တာက path ဆိုတာ ဘာလဲ? */
/* JSON Key ကို ပြောချင်တာလား ? */
DatabaseReference ref = FirebaseDatabas.instance.ref("users");
await ref.update({
"123/age": 19,
"123/address/line1": "1 Mountain View"
});
/* listening data changes */
/* make sure closet level of data , don't listen root database instance */
DatabaseReference starCountRef = FirebaseDatabase.instance.ref("posts/$postId/starCount");
starCountRef.onValue.listen((DatabaseEvent event){
final data = event.snapshot.value;
updateStarCount(data);
/*
event.snapshot.exists => true or false
event.snapshot => may be null
*/
});
/* read once from remote / local */
final ref = FirebaseDatabase.instance.ref();
final snapshot = await ref.child('users/$userId').get(); // get from remote, if not find local
if(snapshot.exists){
print(snapshot.value);
}
else{
print("No data availbale");
}
// တကယ်ကို တစ်ခါတည်း ဖတ်မှာ၊ ပြီးတော့ local ကနေ ချက်ခြင်းလိုချင်တာမျိုး
final event = await ref.once(DatabaseEventType.value);
final username = event.snapshot.value?.username ?? "Anonymous";
// insert and updating
void writeNewPost(String uid, String username, String picture, String title, String body){
final postData = {
'author' : username,
'uid' : uid,
'body' : body,
'title' : title,
'starCount' : 0,
'authorPic' : picture,
};
// Get a key for a new post
// .push() create new child
// .key retrieve of newly created child
// ဒီ key ကိုပဲ user-post မှာ ပြန်သုံးမယ်။
final newPostKey = FirebaseDatabase.instance.ref().child('posts').push().key;
// update in two paths
final Map<String, Map> updates = {};
/* u know, it's not linking, it's storing duplicate things */
updates['/posts/$newPostKey'] = postData;
updates['/user-posts/$uid/$newsPostKey'] = postData;
// ဒါက update အားလုံး failed ရင် failed ဒါမှမဟုတ် အကုန် success ပြမယ်။ actomicity ဆိုပါတော့ ။
return FirebaseDatabase.instance.ref().update(updates);
/* set / update callback functions */
FirebaseDatabase.instance
.ref('users/$userId/email')
.set(updateEmailAddress)
.then((_){
// data saved successfully
})
.catchError((error){
// The write failed ...
})
);
// ဖျတ်မယ်ဆိုရင် delete()
// auto increment သုံးချင်ရင်
// ServerValue.increment(1);
updates["posts/$postId/startCount"] = ServerValue.increment(1);
}
data စပြီး ဘယ်လို ဖတ်မလဲ?
လက်ရှိ record တစ်ခုတည်းကို ဖတ်ရုံပဲ။
name first_digit second_digit final_digit
data structure ကို တက်နိုင်သမျှ nested မသုံးပဲ flat ဖြစ်နိုင်သမျှ ဖြစ်အောင်ထား။
read တွေ write တွေက auth user တွေကိုပဲ ပေးလုပ်တာ များတယ်။ ဒီတော့ firebase auth ပါ လိုလာမယ်။
String, boolean, int , double, Map, List သုံးလို့ရမယ်။
{
}
တက်နိုင်သမျှ key value တွေသာသုံး
Data Structure for mahar-by-ksmt
အကုန်လုံးက Json Object တစ်ခုတည်းမှာ ထည့်ထားမှာ ဆိုတော့ level one ရဲ့ key တွေက Table သဘောမျိုး ဖြစ်နေမယ်။ array မသုံးပဲ key->value pair တွေချည်းသုံးတာ ကောင်းမယ်။ undefined index မဖြစ်တော့ပဲ key မရှိတောင် null ဖြစ်နေတာမျိုး။
ER Diagram
အဓိက သုံးမယ့် Entity တွေက
- User
- Intake
လောလောဆယ်တော့ ဒီ (၂) ခုပဲ။
User ကလည်း လောလောဆယ် Firebase Auth မသုံးသေးတော့ အိုကေတယ်။
အိုကေ ဒီတော့ Intake ကိုပဲ စဉ်းစားကြတာပေါ့။
Intake
- _id
- date
- time
- digit
- name : Intake Name Object
တစ်ခုရှိတာက name ကလည်း မျိုးစုံ ဖြစ်နိုင်တယ်။
IntakeName
- _id
- name
User
- _id
- name
- profile_pic_url
Bet
- _id
- digit
- intake
- user
ဒါကိုပဲ array တစ်ခုတည်းမှာ သိမ်းထားရင် ရပြီ။
Lucky
- _id
- digit
- intake
ဒါလည်း အိုကေပြီ ထင်တယ်။
Data တိုင်းကတော့ store လုပ်ပြီးသွားပြီ။
Report အပိုင်းနဲ့ အလျော်အစားပဲ ကျန်မယ်။
ပထမ Report အတွက်က Aggregrate Function နဲ့ Filter တွေ လိုမယ်။ နောက်ပြီး pagination ဘယ်လိုလုပ်လဲ ကြည့်ရမယ်။ data တွေကတော့ နေရာအစုံမှာ duplicate လုပ်မယ်ဆိုတာ သိထားပါ။
ဒါမှ မဟုတ် aggregrate မလုပ်ပဲ တိုက်ရိုက်တွက်ထားတာက ဒီဘက်မှာ ပိုအဆင်ပြေမလား?
ပထမဆုံး ကြည့်မှာက အခု intake မှာ ဘယ်ဂဏန်းတွေ ဘယ်လောက် ထိုးထားပြီးပြီလဲ?
{
"bet_reports" : {
"_intakeId" : {
"0" : 20,
"1" : 10,
...
"99" : 150,
"sum" : 2000,
"lucky" : 55,
"win" : 1500,
"lose" : 500
}
}
}
path ကလည်း ဒါမျိုး ဖြစ်သွားမယ်။
bet_reports/_intakeId
realtime လည်း ဖြစ်မယ်။ နောက်ပြီး report လည်း ရေးထားပြီးသားဖြစ်မယ်။
ဒီအပေါ်က data ရရင် win / lose အခြေအနေကိုလည်း တိုက်ရိုက် ထုတ်ပြနိုင်နေမယ်။
Provider -> Data Source တွေ ကြားထဲမှာပဲ stream သုံးပြီး Provider နဲ့ UI ကြားမှာတော့ NotifyListener အရင်အတိုင်း သွားကြတာပေါ့။
Provider <-> Repository ကြားမှာ က Clean Stream ဆိုလိုတာက Entity တွေပဲ သယ်ယူပို့ဆောင် လုပ်မယ်။ Repository <-> DataSource ကြားမှာလည်း အတူတူပဲ Clean Stream ကိုပဲ ဆက်သုံးမယ်။ ဒါပေမယ့် ဒီမှာ Failure ပြန်ကောင်း ပြန်လာမယ်။ DataSource <-> Firebase ကြားမှာတော့ Raw Stream ကို သုံးမယ်။ Raw Stream က ရလာတဲ့ Data ကို Clean လုပ်ပြီးမှ Repository အတွက် Clean Stream ကနေ Entity တွေ ပို့ပေးမယ်။ အဲ့မှာ Repository ကနေ Usecase က တဆင့် Data တွေကို Stream နဲ့ ထပ်ပို့ပေးမယ်။
abstract class MaharRepository{
/* User Login အပိုင်းက UI တွေလည်း ပါတာဆိုတော့ ဘယ်လိုလုပ်ကြမလဲ? Provider နဲ့ UI အကြားမှာပဲ ထားမလား? */
Future<Stream<Intake>, Failure> getCurrentIntake({required String accessToken});
// ဒါက admin အတွက်ပဲ။
Future<String, Failure> setCurrentIntake({required String accessToken, required Intake intake});
Future<List<Intake>, Failure> getIntakeListByDate({required String accessToken, required Date date});
Future<List<Gift>, Failure> getGiftListByDate({required String accessToken, required Date date});
}
အခု အခြေအနေအရတော့ Repository က အဓိက အကျဆုံးပဲ။
ပြီးမှ Usecase ပေါ့ ။
Outline ရဖို့က Repository ရေးရင် ရပြီ။ အမှန်ကတော့ Use Case တွေ တန်းစီပြီး ချရေးရမှာ။
အိုကေ UseCase ဆိုပါတော့
ဆိုတော့ကာ လေလေ
Flatering the data structure
တက်နိုင်သမျှ nest နေကြတာ ကျွန်တော်တို့အတွက် Google က အကြံပေးတဲ့ Flat Datastructure က အတော်လေး ထူးဆန်းတယ်။
List ထဲကို data append လုပ်မယ်
DatabaseReference postListRef = FirebaseDatabase.instance.ref("posts");
DatabaseReference newPostRef = postListRef.push();
newPostRef.set({
});
Children တွေရဲ့ Event တွေကို ဘယ်လို listen လုပ်ကြမလဲ
collection တစ်ခုလုံးကို Listen လုပ်တာ မလုပ်ပဲ။ append လုပ်တာ။ update လုပ်တာ။ remove လုပ်တာလောက်ကိုပဲ တိတိကျကျ listen လုပ်ထားရင် ပိုပြီး effecient ဖြစ်တာပေါ့။
nested json တွေ အကုန်လုံးက path အနေနဲ့ သွားတာ။
final commentRef = FirebaseDatabase.instance.ref("post-comments/$postId");
commentRef.onChildAdded.listen((event){
// a new comment has been added
});
commentRef.onChildChanged.listen((event){
// a comment has changed
});
commentRef.onChildRemoved.listen((event){
// a comment has been removed
});
ဒါက sorting အပိုင်း
final myUserId = FirebaseAuth.instance.currentUser?.uid;
final topUserPostsRef = FirebaseDatabase.instance.ref("user-posts/$myUserId").orderByChild("starCount");
for nested json
{
"posts":{
"ts-functions":{
"metrics":{
"views" : 100,
"likes" : 25000
},
"title": "Why we should use TypeScript?",
"description" : "blah blah blah blah "
},
"node-js":{
"metrics":{
"views" : 502,
"likes" : 65203
},
"title": "Node : The fall of JS",
"description" : "blah blah blah blah "
}
}
}
final mostViewedPosts = FirebaseDatabase.instance.ref("posts").orderByChild("metrics/views");
// orderByChild
// orderByKey
// orderByValue
/* We can use only one order by cretia */
Filter ဘယ်လိုလုပ်မလဲ?
// limitToFirst
// limitToLast
// startAt
// startAfter
// endAt
// endBefore
// equalTo
final recentPostsRef = FirebaseDatabase.instance.ref('posts').limitToLast(100);
** ရှာတာ ဖွေတာ မရဘူး *** list နဲ့ ဆွဲထုတ်ရင်တော့ ရမယ်။
orderBy မှာ date ဘာညာသာရကာ သုံးလဲ sorting အတွက်ပဲ။ အိုကေ sorting အတွက် လုပ်တဲ့ နေရာမှာ where clause လို့ သဘောထားလို့တော့ ရမယ်။ မရဘူးပဲ။ where clause မှာက equal to ပါတယ်။ sorting / orderBy မှာက field name တစ်ခုပဲ ပါတာ။
filter မှာတော့ equalTo ဆိုတဲ့ emthod ပါတယ်။ ကိုယ်လိုချင်တဲ့ where clause သုံးလို့ရမလား ကြည့်ရအောင်။
ရပြီ
dinosaursRef.orderByChild("height").equalTo(25);
ဆိုတော့ကာ orderByChild နဲ့ equalTo ကို သုံးပြီး လိုချင်တဲ့ row အတိအကျကို filter လုပ်လို့ရမယ်။
pagination လိုချင်ရင် offset အနေနဲ့ .startAfter limit အနေနဲ့ .limitToFirst
ဆိုပါတော့ ကိုယ်က
user ရဲ့ တစ်ရက်စာ bet list ကို ထုတ်ကြည့်ချင်တယ်။
သိမ်းထားတဲ့ ပုံစံက ဒါမျိုး ဖြစ်မယ်။
user-bets
{
"user-bets" : {
"user_id" : {
"intake_id": {
"bet_id" : {
// bet object
},
"bet_id2" : {
// bet object
}
}
}
}
}
@ input
- userId
- date
ပြန်လာရမှာက List