{ "rules": { "chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()" } } } } }
chats//messages
userID
members
$chatID
user_abc
user_xyz
members/user_xyz
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).exists()" } } }
"lurker"
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'" } } }
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() != 'lurker'" } }
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'" }, "members": { ".read": "data.child(auth.uid).exists()", ".write": "data.child(auth.uid).exists()" } } }
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'" }, "members": { ".read": "data.child(auth.uid).val() == 'owner'", ".write": "data.child(auth.uid).val() == 'owner'" } } }
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'" }, "members": { ".read": "data.child(auth.uid).val() == 'owner'", ".write": "data.child(auth.uid).val() == 'owner'" }, "pending": { "$uid": { ".write": "$uid === auth.uid" } } } }
pending/
"pending": { "$uid": { ".write": "$uid === auth.uid && !data.exists()" } }
"pending": { "$uid": { ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()" } }
pending
"pending": { ".read": "data.parent().child('members').child(auth.uid).val() === 'owner'", ".write": "data.parent().child('members').child(auth.uid).val() === 'owner'", "$uid": { ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()" } }
"members": { ".read": "data.child(auth.uid).val() == 'owner'", ".write": "data.child(auth.uid).val() == 'owner' ||(!data.exists()&&newData.child(auth.uid).val()=='owner')" }
/chats/chat_345/members
{ "user_zzz" : "owner" }
(user_zzz)
chat_456/messages/abc
chat_456
"user_zzz"
/chats/chat_987/members
{ "rules": { "chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'" }, "members": { ".read": "data.child(auth.uid).val() == 'owner'", ".write": "data.child(auth.uid).val() == 'owner' ||(!data.exists()&&newData.child(auth.uid).val()=='owner')" }, "pending": { ".read": "data.parent().child('members').child(auth.uid).val() === 'owner'", ".write": "data.parent().child('members').child(auth.uid).val() === 'owner'", "$uid": { ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()" } } } } } }
carpeta /app/App_Resources/Android
carpeta /app/App_Resources/iOS
package.json
"nativescript-angular": "1.2.0", "nativescript-camera": "^0.0.8", "nativescript-iqkeyboardmanager": "^1.0.1", "nativescript-plugin-firebase": "^3.8.4", "nativescript-theme-core": "^1.0.2",
platforms
tns run ios
tns run android.
tns livesync ios --watch
tns livesync android --watch
app/main.ts
// this import should be first in order to load some required settings (like globals and reflect-metadata) import { platformNativeScriptDynamic } from "nativescript-angular/platform"; import { AppModule } from "./app.module"; import { BackendService } from "./services/backend.service"; import firebase = require("nativescript-plugin-firebase"); firebase.init({ //persist should be set to false as otherwise numbers aren't returned during livesync persist: false, storageBucket: 'gs://giftler-f48c4.appspot.com', onAuthStateChanged: (data: any) => { console.log(JSON.stringify(data)) if (data.loggedIn) { BackendService.token = data.user.uid; } else { BackendService.token = ""; } } }).then( function (instance) { console.log("firebase.init done"); }, function (error) { console.log("firebase.init error: " + error); } ); platformNativeScriptDynamic().bootstrapModule(AppModule);
app/login/login.component.ts
user@nativescript.org
onAuthStateChanged
onAuthStateChanged: (data: any) => { console.log(JSON.stringify(data)) if (data.loggedIn) { BackendService.token = data.user.uid; } else { BackendService.token = ""; } }
loggedIn
token
app/services/backend.service.ts
app/auth-guard.service.ts
export class AuthGuard implements CanActivate { constructor(private router: Router) { } canActivate() { if (BackendService.isLoggedIn()) { return true; } else { this.router.navigate(["/login"]); return false; } }
const listRoutes: Routes = [ { path: "", component: ListComponent, canActivate: [AuthGuard] }, ];
app/list/list.html
app/list/list.component.ts
public gifts$: Observable;
ngOnInit(){ this.gifts$ = this.firebaseService.getMyWishList(); }
getMyWishList(): Observable { return new Observable((observer: any) => { let path = 'Gifts'; let onValueEvent = (snapshot: any) => { this.ngZone.run(() => { let results = this.handleSnapshot(snapshot.value); console.log(JSON.stringify(results)) observer.next(results); }); }; firebase.addValueEventListener(onValueEvent, `/${path}`); }).share(); }
handleSnapshot
handleSnapshot(data: any) { //empty array, then refill and filter this._allItems = []; if (data) { for (let id in data) { let result = (Object).assign({id: id}, data[id]); if(BackendService.token === result.UID){ this._allItems.push(result); } } this.publishUpdates(); } return this._allItems; }
publishUpdates() { // here, we sort must emit a *new* value (immutability!) this._allItems.sort(function(a, b){ if(a.date < b.date) return -1; if(a.date > b.date) return 1; return 0; }) this.items.next([...this._allItems]); }
<Label class="gold card" textWrap="true" [text]="message$ | async"></Label>
message$
ngOnInit(){ this.message$ = this.firebaseService.getMyMessage(); }
(app/services/firebase.service.ts
getMyMessage(): Observable{ return new Observable((observer:any) => { firebase.getRemoteConfig({ developerMode: false, cacheExpirationSeconds: 300, properties: [{ key: "message", default: "Happy Holidays!" }] }).then( function (result) { console.log("Fetched at " + result.lastFetch + (result.throttled ? " (throttled)" : "")); for (let entry in result.properties) { observer.next(result.properties[entry]); } } ); }).share(); }
app/list-detail/list-detail.component.ts
ngOnInit(){ camera.requestPermissions(); ... }
takePhoto() { let options = { width: 300" height: 300" keepAspectRatio: true, saveToGallery: true }; camera.takePicture(options) .then(imageAsset => { imageSource.fromAsset(imageAsset).then(res => { this.image = res; //save the source image to a file, then send that file path to firebase this.saveToFile(this.image); }) }).catch(function (err) { console.log("Error -> " + err.message); }); }
saveToFile(res){ let imgsrc = res; this.imagePath = this.utilsService.documentsPath(`photo-${Date.now()}.png`); imgsrc.saveToFile(this.imagePath, enums.ImageFormat.png); }
/Gifts
editGift(id: string){ if(this.image){ //upload the file, then save all this.firebaseService.uploadFile(this.imagePath).then((uploadedFile: any) => { this.uploadedImageName = uploadedFile.name; //get downloadURL and store it as a full path; this.firebaseService.getDownloadUrl(this.uploadedImageName).then((downloadUrl: string) => { this.firebaseService.editGift(id,this.description,downloadUrl).then((result:any) => { alert(result) }, (error: any) => { alert(error); }); }) }, (error: any) => { alert('File upload error: ' + error); }); } else { //just edit the description this.firebaseService.editDescription(id,this.description).then((result:any) => { alert(result) }, (error: any) => { alert(error); }); } }
uploadFile(localPath: string, file?: any): Promise { let filename = this.utils.getFilename(localPath); let remotePath = `${filename}`; return firebase.uploadFile({ remoteFullPath: remotePath, localFullPath: localPath, onProgress: function(status) { console.log("Uploaded fraction: " + status.fractionCompleted); console.log("Percentage complete: " + status.percentageCompleted); } }); } getDownloadUrl(remoteFilePath: string): Promise { return firebase.getDownloadUrl({ remoteFullPath: remoteFilePath}) .then( function (url:string) { return url; }, function (errorMessage:any) { console.log(errorMessage); }); } editGift(id:string, description: string, imagepath: string){ this.publishUpdates(); return firebase.update("/Gifts/"+id+"",{ description: description, imagepath: imagepath}) .then( function (result:any) { return 'You have successfully edited this gift!'; }, function (errorMessage:any) { console.log(errorMessage); }); }
POST https://mybusiness.googleapis.com/v3/123456/locations:reportInsights { "locationNames": [ “accounts/110714876951578713336/locations/14372810722634034850”, ], “basicRequest” : { "metricRequests": [ { "metric": QUERIES_DIRECT, }, { "metric": QUERIES_INDIRECT, } ], "timeRange": { "startTime": 2016-10-12T01:01:23.045123456Z, "endTime": 2017-01-10T23:59:59.045123456Z, }, }, }
{ "locationMetrics": [ { "locationName": "accounts/110714876951578713336/locations/ 14372810722634034850", "timeZone": "America/Los_Angeles", "metricValues": [ { "metric": "QUERIES_DIRECT", "totalValue": { "metricOption": "AGGREGATED_TOTAL", "timeDimension": { "timeRange": { "startTime": "2016-10-12T04:00:00Z", "endTime": "2017-01-10T04:00:00Z" } }, "value": "36738" } }, { "metric": "QUERIES_INDIRECT", "totalValue": { "metricOption": "AGGREGATED_TOTAL", "timeDimension": { "timeRange": { "startTime": "2016-10-12T04:00:00Z", "endTime": "2017-01-10T04:00:00Z" } }, "value": "81770" } } ] } ] }
POST https://mybusiness.googleapis.com/v3/123456/locations:reportInsights { “locationNames": [ “accounts/110714876951578713336/locations/14372810722634034850”, ], "drivingDirectionsRequest”: { "numDays": NINETY, }, }
{ "locationDrivingDirectionMetrics": [ { "locationName": "accounts/110714876951578713336/locations/ 14372810722634034850", "topDirectionSources": [ { "dayCount": 90, "regionCounts": [ { "latlng": { "latitude": 37.789, "longitude": -121.392 }, "label": "94105", "count": "2980", }, { "latlng": { "latitude": 37.779, "longitude": -122.421 }, "label": "94102", "count": "887", }, { "latlng": { "latitude": 37.773, "longitude": -122.410 }, "label": "94103", "count": "886", } ] } ], "timeZone": "America/Los_Angeles" } ] }