-
Notifications
You must be signed in to change notification settings - Fork 3
WIP: Feat/user channel create delete #68
base: master
Are you sure you want to change the base?
Changes from all commits
c10765e
5ea091e
1a46f36
843717f
ed236f7
5c246da
ec5c810
4f4354d
dd840ff
e9af638
4a78951
4d5dc53
8b55972
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| { | ||
| "projects": { | ||
| "production": "firebase-radio4000", | ||
| "staging": "radio4000-staging" | ||
| } | ||
| "projects": { | ||
| "production": "firebase-radio4000", | ||
| "staging": "radio4000-staging", | ||
| "dev": "radio4000-hugurp" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -98,7 +98,7 @@ | |
| // write: only the user owner can write a channel | ||
| // write: only a user with no channel can write a new one to himself | ||
| ".write": "auth != null && (root.child('users').child(auth.uid).child('channels').child($channelID).exists() || (!data.exists() && !root.child('users').child(auth.uid).child('channels').exists()))", | ||
| ".validate": "newData.hasChildren(['slug', 'title', 'created']) || !newData.exists()", | ||
| ".validate": "newData.hasChildren(['title']) || !newData.exists()", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should generate it in the Backend as well. My mistake! |
||
|
|
||
| "channelPublic": { | ||
| ".validate": "newData.isString() && root.child('channelPublics').child(newData.val()).child('channel').val() == $channelID" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,218 @@ | ||
| const admin = require('firebase-admin') | ||
| const slugify = require('@sindresorhus/slugify') | ||
|
|
||
| const deleteChannelFollowersReferences = async (dbRootRef, channelId, channelPublic) => { | ||
| if (!channelId || !channelPublic) return | ||
|
|
||
| let channelFollowersRef = dbRootRef.child(`/channelPublics/${channelPublic}/followers`) | ||
| let channelFollowersSnap | ||
| try { | ||
| channelFollowersSnap = await channelFollowersRef.once('value') | ||
| } catch (error) { | ||
| console.error('Error getting channel.followers') | ||
| } | ||
|
|
||
| const followers = channelFollowersSnap.val() | ||
|
|
||
| if (!followers || !followers.length) return | ||
|
|
||
| let updates = {} | ||
| Object.keys(followers).forEach(followerId => { | ||
| updates[`/channels/${followerId}/favoriteChannels/${channelId}`] = null | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where does |
||
| }) | ||
|
|
||
| return dbRootRef.update(updates) | ||
| } | ||
|
|
||
| const getUniqueChannelSlug = async (channelData) => { | ||
| let {title, slug} = channelData | ||
| slug = slugify(slug || title) | ||
| let channelsRef = admin.database().ref(`/channels`) | ||
|
|
||
| const channelWithSameSlug = await channelsRef | ||
| .orderByChild('slug') | ||
| .equalTo(slug) | ||
| .once(snapshot => snapshot.val()) | ||
|
|
||
| if (channelWithSameSlug && channelWithSameSlug.length) { | ||
| let randomString = Math.random().toString(36).substring(7); | ||
| return `${slug}-${randomString}` | ||
| } else { | ||
| return slug | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /* | ||
| a channel is created | ||
| */ | ||
| const handleChannelCreate = async (snapshot, context) => { | ||
| const newChannel = snapshot.val() | ||
| const {id: channelId} = context.params | ||
| const {auth} = context | ||
|
|
||
| if (!auth) { | ||
| console.error('Channel create called without auth') | ||
| return | ||
| } | ||
|
|
||
| const {uid} = auth | ||
|
|
||
| if (!channelId || !uid) { | ||
| console.error('Channel create called without channelId or auth.uid') | ||
| return | ||
| } | ||
|
|
||
| // find current-user at ref: /users/:currentUser | ||
| let userChannelRef = snapshot.ref | ||
| let dbRootRef = userChannelRef.parent.parent | ||
|
|
||
| // validate slug, or generate it | ||
| let channelSlug | ||
| try { | ||
| channelSlug = await getUniqueChannelSlug(newChannel) | ||
| } | ||
|
|
||
| try { | ||
| await userChannelRef.update({ | ||
| slug: channelSlug, | ||
| created: admin.database.ServerValue.TIMESTAMP, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so nice if we don't have to deal with timestamps in the frontend |
||
| updated: admin.database.ServerValue.TIMESTAMP | ||
| }) | ||
| } catch (error) { | ||
| console.error('Error setting slug') | ||
| } | ||
|
|
||
| // find current-user at ref: /users/:currentUser | ||
| let userRef = dbRootRef.child(`/users/${uid}`) | ||
|
|
||
| // on user add .channels[channelId]: true | ||
| try { | ||
| await userRef.child(`channels/${channelId}`).set(true) | ||
| } catch (error) { | ||
| console.error('Error settting user.channels[channelId]') | ||
| } | ||
|
|
||
| // new /channelPublics/ | ||
| let channelPublicsRef = dbRootRef.child('/channelPublics') | ||
|
|
||
| // add channelPublic.channel = channelId | ||
| let channelPublic | ||
| try { | ||
| channelPublic = await channelPublicsRef.push({ | ||
| channel: channelId | ||
| }) | ||
| } catch (error) { | ||
| console.error('Error setting channelPublic.channel') | ||
| } | ||
|
|
||
| // add channel.channelPublic = channelPublic.id | ||
| try { | ||
| await userChannelRef.child('channelPublic').set(channelPublic.key) | ||
| } catch (error) { | ||
| console.error('Error setting channel.channelPublic') | ||
| } | ||
| } | ||
|
|
||
| /* | ||
| update channel | ||
| */ | ||
| const handleChannelUpdate = async (change, context) => { | ||
| const {id: channelId} = context.params | ||
| const {auth} = context | ||
|
|
||
| if (!auth) { | ||
| console.error('Channel update called without auth') | ||
| return | ||
| } | ||
|
|
||
| const {uid} = auth | ||
|
|
||
| if (!channelId || !uid) { | ||
| console.error('Channel update called without channelId or auth.uid') | ||
| return | ||
| } | ||
|
|
||
| let userChannelRef = change.after.ref | ||
| const newValue = change.after.val() | ||
| const previousValue = change.before.val() | ||
|
|
||
| if (!newValue.slug || newValue.slug !== previousValue.slug) { | ||
| // validate slug, or generate it | ||
| let channelSlug | ||
| try { | ||
| channelSlug = await getUniqueChannelSlug(newValue) | ||
| } | ||
|
|
||
| try { | ||
| await userChannelRef.update({ | ||
| slug: channelSlug | ||
| }) | ||
| } catch (error) { | ||
| console.error('Error setting slug from channelData') | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /* | ||
| when a channel is deleted | ||
| */ | ||
| const handleChannelDelete = async (change, context) => { | ||
| const channel = change.val() | ||
| const {id: channelId} = context.params | ||
| const {auth} = context | ||
|
|
||
| let channelPublic | ||
| if (channel) { | ||
| channelPublic = channel.channelPublic | ||
| } | ||
|
|
||
| if (!auth) { | ||
| console.error('Channel delete called without auth') | ||
| return | ||
| } | ||
|
|
||
| const {uid} = auth | ||
|
|
||
| if (!channelId || !uid) { | ||
| console.error('Channel delete called without channelId or auth.uid') | ||
| return | ||
| } | ||
|
|
||
| // find current-user at ref: /users/:currentUser | ||
| let userChannelRef = change.ref | ||
| let dbRootRef = userChannelRef.parent.parent | ||
|
|
||
| if (channelPublic) { | ||
| try { | ||
| await deleteChannelFollowersReferences(dbRootRef, channelId, channelPublic) | ||
| } catch (error) { | ||
| console.error('Error deleting channel\'s followers.favorite[channelId] refs') | ||
| } | ||
| } | ||
|
|
||
| // find current-channel-public at ref: /channelPublic/:channel.channelPublic | ||
| let channelPublicRef = dbRootRef.child(`/channelPublics/${channelPublic}`) | ||
| try { | ||
| await channelPublicRef.set(null) | ||
| } catch (error) { | ||
| console.error('Error deleting /channelPublics/:channel.channelPublic') | ||
| } | ||
|
|
||
| // find current-user at ref: /users/:currentUser | ||
| let userRef = dbRootRef.child(`/users/${uid}`) | ||
| try { | ||
| await userRef.child(`/channels/${channelId}`).set(null) | ||
| } catch (error) { | ||
| console.error('Error removing channel on user') | ||
| } | ||
|
|
||
| } | ||
|
|
||
|
|
||
| module.exports = { | ||
| handleChannelCreate, | ||
| handleChannelUpdate, | ||
| handleChannelDelete | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it need 10.10.0 or just 10.x?