@@ -12,6 +12,189 @@ static func get_json_data(value):
12
12
return null
13
13
14
14
15
+ # Pass a dictionary { 'key' : 'value' } to format it in a APIs usable .fields
16
+ # Field Path3D using the "dot" (`.`) notation are supported:
17
+ # ex. { "PATH.TO.SUBKEY" : "VALUE" } ==> { "PATH" : { "TO" : { "SUBKEY" : "VALUE" } } }
18
+ static func dict2fields (dict : Dictionary ) -> Dictionary :
19
+ var fields = {}
20
+ var var_type : String = ""
21
+ for field in dict .keys ():
22
+ var field_value = dict [field ]
23
+ if field is String and "." in field :
24
+ var keys : Array = field .split ("." )
25
+ field = keys .pop_front ()
26
+ keys .reverse ()
27
+ for key in keys :
28
+ field_value = { key : field_value }
29
+
30
+ match typeof (field_value ):
31
+ TYPE_NIL : var_type = "nullValue"
32
+ TYPE_BOOL : var_type = "booleanValue"
33
+ TYPE_INT : var_type = "integerValue"
34
+ TYPE_FLOAT : var_type = "doubleValue"
35
+ TYPE_STRING : var_type = "stringValue"
36
+ TYPE_DICTIONARY :
37
+ if is_field_timestamp (field_value ):
38
+ var_type = "timestampValue"
39
+ field_value = dict2timestamp (field_value )
40
+ else :
41
+ var_type = "mapValue"
42
+ field_value = dict2fields (field_value )
43
+ TYPE_ARRAY :
44
+ var_type = "arrayValue"
45
+ field_value = {"values" : array2fields (field_value )}
46
+
47
+ if fields .has (field ) and fields [field ].has ("mapValue" ) and field_value .has ("fields" ):
48
+ for key in field_value ["fields" ].keys ():
49
+ fields [field ]["mapValue" ]["fields" ][key ] = field_value ["fields" ][key ]
50
+ else :
51
+ fields [field ] = { var_type : field_value }
52
+
53
+ return {'fields' : fields }
54
+
55
+ static func from_firebase_type (value : Variant ) -> Variant :
56
+ if value == null :
57
+ return null
58
+
59
+ if value .has ("mapValue" ):
60
+ value = _from_firebase_type_recursive (value .values ()[0 ].fields )
61
+ elif value .has ("timestampValue" ):
62
+ value = Time .get_datetime_dict_from_datetime_string (value .values ()[0 ], false )
63
+ else :
64
+ value = value .values ()[0 ]
65
+
66
+ return value
67
+
68
+ static func _from_firebase_type_recursive (value : Variant ) -> Variant :
69
+ if value == null :
70
+ return null
71
+
72
+ if value .has ("mapValue" ) or value .has ("timestampValue" ):
73
+ value = _from_firebase_type_recursive (value .value ()[0 ].fields )
74
+ else :
75
+ value = value .values ()[0 ]
76
+
77
+ return value
78
+
79
+ static func to_firebase_type (value : Variant ) -> Dictionary :
80
+ var var_type : String = ""
81
+
82
+ match typeof (value ):
83
+ TYPE_NIL : var_type = "nullValue"
84
+ TYPE_BOOL : var_type = "booleanValue"
85
+ TYPE_INT : var_type = "integerValue"
86
+ TYPE_FLOAT : var_type = "doubleValue"
87
+ TYPE_STRING : var_type = "stringValue"
88
+ TYPE_DICTIONARY :
89
+ if is_field_timestamp (value ):
90
+ var_type = "timestampValue"
91
+ value = dict2timestamp (value )
92
+ else :
93
+ var_type = "mapValue"
94
+ value = dict2fields (value )
95
+ TYPE_ARRAY :
96
+ var_type = "arrayValue"
97
+ value = {"values" : array2fields (value )}
98
+
99
+ return { var_type : value }
100
+
101
+ # Pass the .fields inside a Firestore Document to print out the Dictionary { 'key' : 'value' }
102
+ static func fields2dict (doc ) -> Dictionary :
103
+ var dict = {}
104
+ if doc .has ("fields" ):
105
+ var fields = doc ["fields" ]
106
+ print (fields )
107
+ for field in fields .keys ():
108
+ if fields [field ].has ("mapValue" ):
109
+ dict [field ] = (fields2dict (fields [field ].mapValue ))
110
+ elif fields [field ].has ("timestampValue" ):
111
+ dict [field ] = timestamp2dict (fields [field ].timestampValue )
112
+ elif fields [field ].has ("arrayValue" ):
113
+ dict [field ] = fields2array (fields [field ].arrayValue )
114
+ elif fields [field ].has ("integerValue" ):
115
+ dict [field ] = fields [field ].values ()[0 ] as int
116
+ elif fields [field ].has ("doubleValue" ):
117
+ dict [field ] = fields [field ].values ()[0 ] as float
118
+ elif fields [field ].has ("booleanValue" ):
119
+ dict [field ] = fields [field ].values ()[0 ] as bool
120
+ elif fields [field ].has ("nullValue" ):
121
+ dict [field ] = null
122
+ else :
123
+ dict [field ] = fields [field ].values ()[0 ]
124
+ return dict
125
+
126
+ # Pass an Array to parse it to a Firebase arrayValue
127
+ static func array2fields (array : Array ) -> Array :
128
+ var fields : Array = []
129
+ var var_type : String = ""
130
+ for field in array :
131
+ match typeof (field ):
132
+ TYPE_DICTIONARY :
133
+ if is_field_timestamp (field ):
134
+ var_type = "timestampValue"
135
+ field = dict2timestamp (field )
136
+ else :
137
+ var_type = "mapValue"
138
+ field = dict2fields (field )
139
+ TYPE_NIL : var_type = "nullValue"
140
+ TYPE_BOOL : var_type = "booleanValue"
141
+ TYPE_INT : var_type = "integerValue"
142
+ TYPE_FLOAT : var_type = "doubleValue"
143
+ TYPE_STRING : var_type = "stringValue"
144
+ TYPE_ARRAY : var_type = "arrayValue"
145
+ _ : var_type = "FieldTransform"
146
+ fields .append ({ var_type : field })
147
+ return fields
148
+
149
+ # Pass a Firebase arrayValue Dictionary to convert it back to an Array
150
+ static func fields2array (array : Dictionary ) -> Array :
151
+ var fields : Array = []
152
+ if array .has ("values" ):
153
+ for field in array .values :
154
+ var item
155
+ match field .keys ()[0 ]:
156
+ "mapValue" :
157
+ item = fields2dict (field .mapValue )
158
+ "arrayValue" :
159
+ item = fields2array (field .arrayValue )
160
+ "integerValue" :
161
+ item = field .values ()[0 ] as int
162
+ "doubleValue" :
163
+ item = field .values ()[0 ] as float
164
+ "booleanValue" :
165
+ item = field .values ()[0 ] as bool
166
+ "timestampValue" :
167
+ item = timestamp2dict (field .timestampValue )
168
+ "nullValue" :
169
+ item = null
170
+ _ :
171
+ item = field .values ()[0 ]
172
+ fields .append (item )
173
+ return fields
174
+
175
+ # Converts a gdscript Dictionary (most likely obtained with Time.get_datetime_dict_from_system()) to a Firebase Timestamp
176
+ static func dict2timestamp (dict : Dictionary ) -> String :
177
+ # dict.erase('weekday')
178
+ # dict.erase('dst')
179
+ # var dict_values : Array = dict.values()
180
+ var time = Time .get_datetime_string_from_datetime_dict (dict , false )
181
+ return time
182
+ # return "%04d-%02d-%02dT%02d:%02d:%02d.00Z" % dict_values
183
+
184
+ # Converts a Firebase Timestamp back to a gdscript Dictionary
185
+ static func timestamp2dict (timestamp : String ) -> Dictionary :
186
+ return Time .get_datetime_dict_from_datetime_string (timestamp , false )
187
+ # var datetime : Dictionary = {year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0}
188
+ # var dict : PackedStringArray = timestamp.split("T")[0].split("-")
189
+ # dict.append_array(timestamp.split("T")[1].split(":"))
190
+ # for value in dict.size():
191
+ # datetime[datetime.keys()[value]] = int(dict[value])
192
+ # return datetime
193
+
194
+ static func is_field_timestamp (field : Dictionary ) -> bool :
195
+ return field .has_all (['year' ,'month' ,'day' ,'hour' ,'minute' ,'second' ])
196
+
197
+
15
198
# HTTPRequeust seems to have an issue in Web exports where the body returns empty
16
199
# This appears to be caused by the gzip compression being unsupported, so we
17
200
# disable it when web export is detected.
@@ -133,3 +316,11 @@ class ObservableDictionary extends RefCounted:
133
316
func _set (property : StringName , value : Variant ) -> bool :
134
317
update (property , value )
135
318
return true
319
+
320
+ class AwaitDetachable extends Node2D :
321
+ var awaiter : Signal
322
+
323
+ func _init (freeable_node , await_signal : Signal ) -> void :
324
+ awaiter = await_signal
325
+ add_child (freeable_node )
326
+ awaiter .connect (queue_free )
0 commit comments