Compare commits
	
		
			482 Commits
		
	
	
		
			1232318a5d
			...
			3.3.0+144
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						ac4fa5eb85
	
				 | 
					
					
						|||
| 
						
						
							
						
						8857718709
	
				 | 
					
					
						|||
| 
						
						
							
						
						dd17b2b9c1
	
				 | 
					
					
						|||
| 
						
						
							
						
						848439f664
	
				 | 
					
					
						|||
| 
						
						
							
						
						f83117424d
	
				 | 
					
					
						|||
| 
						
						
							
						
						8c19c32c76
	
				 | 
					
					
						|||
| 
						
						
							
						
						d62b2bed80
	
				 | 
					
					
						|||
| 
						
						
							
						
						5a23eb1768
	
				 | 
					
					
						|||
| 
						
						
							
						
						5f6e4763d3
	
				 | 
					
					
						|||
| 
						
						
							
						
						580c36fb89
	
				 | 
					
					
						|||
| 
						
						
							
						
						6c25af3b30
	
				 | 
					
					
						|||
| 
						
						
							
						
						a1da72d447
	
				 | 
					
					
						|||
| 
						
						
							
						
						ab4120cc22
	
				 | 
					
					
						|||
| 
						
						
							
						
						52eff0fa25
	
				 | 
					
					
						|||
| 
						
						
							
						
						beeb28abf2
	
				 | 
					
					
						|||
| 
						
						
							
						
						c0ab3837ac
	
				 | 
					
					
						|||
| 
						
						
							
						
						59d38c0d8d
	
				 | 
					
					
						|||
| 
						
						
							
						
						bd2247ce86
	
				 | 
					
					
						|||
| 
						
						
							
						
						da2d3f7f17
	
				 | 
					
					
						|||
| 
						
						
							
						
						7497b77384
	
				 | 
					
					
						|||
| 
						
						
							
						
						f542d9fa97
	
				 | 
					
					
						|||
| 
						
						
							
						
						e70439870e
	
				 | 
					
					
						|||
| 
						
						
							
						
						d764b042fe
	
				 | 
					
					
						|||
| 
						
						
							
						
						a76b97d1d2
	
				 | 
					
					
						|||
| 
						
						
							
						
						cfbe6e580b
	
				 | 
					
					
						|||
| 
						
						
							
						
						f08b9e057f
	
				 | 
					
					
						|||
| 
						
						
							
						
						0509f37c96
	
				 | 
					
					
						|||
| 
						
						
							
						
						a7dc9ac6fa
	
				 | 
					
					
						|||
| 
						
						
							
						
						caf2f5f1f6
	
				 | 
					
					
						|||
| 
						
						
							
						
						12b79af3a2
	
				 | 
					
					
						|||
| 
						
						
							
						
						88f149584e
	
				 | 
					
					
						|||
| 
						
						
							
						
						877001b802
	
				 | 
					
					
						|||
| fec28f6223 | |||
| 85005ff9c3 | |||
| e3c92a3c55 | |||
| 9e9fbc5d6a | |||
| 8d1d836b52 | |||
| bc60ce5d42 | |||
| c093123e3a | |||
| 3de73538c7 | |||
| ba8d5cee09 | |||
| 
						
						
							
						
						5ee2e70442
	
				 | 
					
					
						|||
| 
						
						
							
						
						53a3a32907
	
				 | 
					
					
						|||
| 
						
						
							
						
						9a628779d9
	
				 | 
					
					
						|||
| 
						
						
							
						
						b60bd63d0c
	
				 | 
					
					
						|||
| 
						
						
							
						
						01cc71fd47
	
				 | 
					
					
						|||
| 
						
						
							
						
						a2b0cd0b6a
	
				 | 
					
					
						|||
| 
						
						
							
						
						7f971bcee3
	
				 | 
					
					
						|||
| 
						
						
							
						
						7de98a1731
	
				 | 
					
					
						|||
| 
						
						
							
						
						b52eb95b14
	
				 | 
					
					
						|||
| 
						
						
							
						
						b3ef7d6ad0
	
				 | 
					
					
						|||
| 
						
						
							
						
						d28c11940d
	
				 | 
					
					
						|||
| 
						
						
							
						
						504322c2dd
	
				 | 
					
					
						|||
| 
						
						
							
						
						a07ec3ca36
	
				 | 
					
					
						|||
| d96691e920 | |||
| 
						
						
							
						
						6273b2d917
	
				 | 
					
					
						|||
| 
						
						
							
						
						ab90d244b5
	
				 | 
					
					
						|||
| 
						
						
							
						
						dc6af6d9e5
	
				 | 
					
					
						|||
| 
						
						
							
						
						0ca801d963
	
				 | 
					
					
						|||
| 
						
						
							
						
						3edcdd72af
	
				 | 
					
					
						|||
| 
						
						
							
						
						402bb3fe04
	
				 | 
					
					
						|||
| 
						
						
							
						
						8ba55eb1be
	
				 | 
					
					
						|||
| 
						
						
							
						
						983ae2a1fc
	
				 | 
					
					
						|||
| 
						
						
							
						
						6fc94001b3
	
				 | 
					
					
						|||
| 
						
						
							
						
						44dbcfdc94
	
				 | 
					
					
						|||
| 
						
						
							
						
						b57caf56db
	
				 | 
					
					
						|||
| 
						
						
							
						
						dbcd1b6d36
	
				 | 
					
					
						|||
| 
						
						
							
						
						a8055de910
	
				 | 
					
					
						|||
| 
						
						
							
						
						49b15e7674
	
				 | 
					
					
						|||
| 
						
						
							
						
						e2369c40db
	
				 | 
					
					
						|||
| 
						
						
							
						
						44c5d91620
	
				 | 
					
					
						|||
| 
						
						
							
						
						7a5a2407b7
	
				 | 
					
					
						|||
| 
						
						
							
						
						234434f102
	
				 | 
					
					
						|||
| 
						
						
							
						
						9c3b228d02
	
				 | 
					
					
						|||
| 
						
						
							
						
						82682cae9a
	
				 | 
					
					
						|||
| 
						
						
							
						
						fcbd5fe680
	
				 | 
					
					
						|||
| 
						
						
							
						
						ad91b17af7
	
				 | 
					
					
						|||
| 
						
						
							
						
						24fa637329
	
				 | 
					
					
						|||
| 
						
						
							
						
						926ae5402f
	
				 | 
					
					
						|||
| 
						
						
							
						
						1a37d384e6
	
				 | 
					
					
						|||
| 
						
						
							
						
						d4cf598f69
	
				 | 
					
					
						|||
| 
						
						
							
						
						0106c08891
	
				 | 
					
					
						|||
| 
						
						
							
						
						9697def808
	
				 | 
					
					
						|||
| 
						
						
							
						
						6572875229
	
				 | 
					
					
						|||
| 
						
						
							
						
						66590b9079
	
				 | 
					
					
						|||
| 
						
						
							
						
						08b9604b55
	
				 | 
					
					
						|||
| 
						
						
							
						
						0602bbd277
	
				 | 
					
					
						|||
| 
						
						
							
						
						76e7ba7898
	
				 | 
					
					
						|||
| 
						
						
							
						
						6e6616b236
	
				 | 
					
					
						|||
| 
						
						
							
						
						071d51b25e
	
				 | 
					
					
						|||
| 
						
						
							
						
						a958362461
	
				 | 
					
					
						|||
| 
						
						
							
						
						6749bb00fe
	
				 | 
					
					
						|||
| 
						
						
							
						
						11fb20c673
	
				 | 
					
					
						|||
| 
						
						
							
						
						a7990f83db
	
				 | 
					
					
						|||
| 
						
						
							
						
						5f4cdf7937
	
				 | 
					
					
						|||
| 
						
						
							
						
						3330ca14dd
	
				 | 
					
					
						|||
| 
						
						
							
						
						1719b1c8fe
	
				 | 
					
					
						|||
| 
						
						
							
						
						3c2c51bfaf
	
				 | 
					
					
						|||
| 
						
						
							
						
						239d6750ff
	
				 | 
					
					
						|||
| 
						
						
							
						
						8b0c91977a
	
				 | 
					
					
						|||
| 
						
						
							
						
						f74cca8464
	
				 | 
					
					
						|||
| 
						
						
							
						
						08091d51bf
	
				 | 
					
					
						|||
| 
						
						
							
						
						481190811b
	
				 | 
					
					
						|||
| 
						
						
							
						
						4b32b65d1c
	
				 | 
					
					
						|||
| 
						
						
							
						
						50ac7109bb
	
				 | 
					
					
						|||
| 
						
						
							
						
						62da279c71
	
				 | 
					
					
						|||
| 
						
						
							
						
						fde6dbf891
	
				 | 
					
					
						|||
| 
						
						
							
						
						613bf4fb42
	
				 | 
					
					
						|||
| 
						
						
							
						
						00ae586016
	
				 | 
					
					
						|||
| 
						
						
							
						
						ea0d132dce
	
				 | 
					
					
						|||
| 
						
						
							
						
						aa2df1e847
	
				 | 
					
					
						|||
| 
						
						
							
						
						50672795f3
	
				 | 
					
					
						|||
| 
						
						
							
						
						383de9568d
	
				 | 
					
					
						|||
| 
						
						
							
						
						01fa228e45
	
				 | 
					
					
						|||
| 
						
						
							
						
						1e71ad33a6
	
				 | 
					
					
						|||
| 
						
						
							
						
						92c0260ecd
	
				 | 
					
					
						|||
| 
						
						
							
						
						0a161ad255
	
				 | 
					
					
						|||
| 
						
						
							
						
						c003f27b9a
	
				 | 
					
					
						|||
| 
						
						
							
						
						19db8309c4
	
				 | 
					
					
						|||
| 
						
						
							
						
						aa72ce08e8
	
				 | 
					
					
						|||
| 
						
						
							
						
						4639b00b86
	
				 | 
					
					
						|||
| 
						
						
							
						
						cc5460ea55
	
				 | 
					
					
						|||
| 
						
						
							
						
						eafac811e6
	
				 | 
					
					
						|||
| 
						
						
							
						
						e3be691596
	
				 | 
					
					
						|||
| 
						
						
							
						
						aa180a1358
	
				 | 
					
					
						|||
| 
						
						
							
						
						c2707b8af1
	
				 | 
					
					
						|||
| 
						
						
							
						
						62fd0500f3
	
				 | 
					
					
						|||
| 
						
						
							
						
						eeae865cc8
	
				 | 
					
					
						|||
| 
						
						
							
						
						cdf1413fe0
	
				 | 
					
					
						|||
| 
						
						
							
						
						327b4c04f1
	
				 | 
					
					
						|||
| 
						
						
							
						
						bd903ce29c
	
				 | 
					
					
						|||
| 
						
						
							
						
						1b8ecb15ce
	
				 | 
					
					
						|||
| 
						
						
							
						
						d4e380a97a
	
				 | 
					
					
						|||
| 
						
						
							
						
						126048b4fa
	
				 | 
					
					
						|||
| 
						
						
							
						
						8bec18813d
	
				 | 
					
					
						|||
| 
						
						
							
						
						1ae81794b1
	
				 | 
					
					
						|||
| 
						
						
							
						
						2a7d12de48
	
				 | 
					
					
						|||
| 
						
						
							
						
						64c60ead48
	
				 | 
					
					
						|||
| 001549b190 | |||
| 4595865ad3 | |||
| 
						 | 
					1834643167 | ||
| 
						 | 
					0e816eaa3e | ||
| 
						 | 
					7c1f24b824 | ||
| 
						
						
							
						
						c6594ea2ce
	
				 | 
					
					
						|||
| 
						
						
							
						
						3bec6e683e
	
				 | 
					
					
						|||
| 
						
						
							
						
						83e92e2eed
	
				 | 
					
					
						|||
| 
						 | 
					b7d44d96ba | ||
| 
						
						
							
						
						a83b929d42
	
				 | 
					
					
						|||
| 
						
						
							
						
						9423affa75
	
				 | 
					
					
						|||
| 
						
						
							
						
						cda23db609
	
				 | 
					
					
						|||
| 
						
						
							
						
						61074bc5a3
	
				 | 
					
					
						|||
| 
						
						
							
						
						5feafa9255
	
				 | 
					
					
						|||
| 
						
						
							
						
						e604577c1f
	
				 | 
					
					
						|||
| 
						
						
							
						
						af0ddd1273
	
				 | 
					
					
						|||
| 
						
						
							
						
						8a6bb34808
	
				 | 
					
					
						|||
| 
						
						
							
						
						4ef8445c77
	
				 | 
					
					
						|||
| 
						
						
							
						
						ec39ad6ca3
	
				 | 
					
					
						|||
| 
						
						
							
						
						eabb3154f1
	
				 | 
					
					
						|||
| 
						
						
							
						
						910bf20eef
	
				 | 
					
					
						|||
| 
						
						
							
						
						5efa9b2ae8
	
				 | 
					
					
						|||
| 
						
						
							
						
						dd3e39e891
	
				 | 
					
					
						|||
| 
						
						
							
						
						b6896ded23
	
				 | 
					
					
						|||
| 
						
						
							
						
						f28a73ff9c
	
				 | 
					
					
						|||
| 
						
						
							
						
						a014b64235
	
				 | 
					
					
						|||
| 
						
						
							
						
						7e0e7c20d7
	
				 | 
					
					
						|||
| 
						
						
							
						
						389fa515ba
	
				 | 
					
					
						|||
| 
						
						
							
						
						681ead02eb
	
				 | 
					
					
						|||
| 
						
						
							
						
						8d1c145b0b
	
				 | 
					
					
						|||
| 
						
						
							
						
						51b4754182
	
				 | 
					
					
						|||
| 
						
						
							
						
						8a2b321701
	
				 | 
					
					
						|||
| 
						
						
							
						
						f685a7a249
	
				 | 
					
					
						|||
| 
						
						
							
						
						76009147e9
	
				 | 
					
					
						|||
| 
						
						
							
						
						ce12f28e56
	
				 | 
					
					
						|||
| 
						
						
							
						
						3604373a1e
	
				 | 
					
					
						|||
| 
						
						
							
						
						9704a4c2c7
	
				 | 
					
					
						|||
| 
						
						
							
						
						67def56ad1
	
				 | 
					
					
						|||
| 
						
						
							
						
						1be33916af
	
				 | 
					
					
						|||
| 
						
						
							
						
						e8ff1bfd22
	
				 | 
					
					
						|||
| 
						
						
							
						
						3ae56f3d89
	
				 | 
					
					
						|||
| 
						
						
							
						
						707143e998
	
				 | 
					
					
						|||
| 
						
						
							
						
						1fd34eb2a3
	
				 | 
					
					
						|||
| 
						
						
							
						
						d7ca41e946
	
				 | 
					
					
						|||
| 
						
						
							
						
						ad9fb0719a
	
				 | 
					
					
						|||
| 
						
						
							
						
						e2d315afd4
	
				 | 
					
					
						|||
| 
						
						
							
						
						6124dbfd79
	
				 | 
					
					
						|||
| 
						
						
							
						
						5327f04ec0
	
				 | 
					
					
						|||
| 
						
						
							
						
						41c56a2319
	
				 | 
					
					
						|||
| 
						
						
							
						
						f9d033542e
	
				 | 
					
					
						|||
| 
						
						
							
						
						91784e65e6
	
				 | 
					
					
						|||
| 
						
						
							
						
						9d39c6a825
	
				 | 
					
					
						|||
| 
						
						
							
						
						537e49f1a4
	
				 | 
					
					
						|||
| 
						
						
							
						
						75bbd4df71
	
				 | 
					
					
						|||
| 
						
						
							
						
						6ef4580d93
	
				 | 
					
					
						|||
| 
						
						
							
						
						6ffd498761
	
				 | 
					
					
						|||
| 
						
						
							
						
						27157e7cc1
	
				 | 
					
					
						|||
| 
						
						
							
						
						bbb07d574a
	
				 | 
					
					
						|||
| 
						
						
							
						
						c660a419e2
	
				 | 
					
					
						|||
| 
						
						
							
						
						c3f61467c8
	
				 | 
					
					
						|||
| 
						
						
							
						
						9bc47df452
	
				 | 
					
					
						|||
| 
						
						
							
						
						9ef8ca4d45
	
				 | 
					
					
						|||
| 
						
						
							
						
						b55cbd08d1
	
				 | 
					
					
						|||
| 
						
						
							
						
						8c6bd0feaa
	
				 | 
					
					
						|||
| 
						
						
							
						
						7dd4b20628
	
				 | 
					
					
						|||
| 
						
						
							
						
						fec0cb7640
	
				 | 
					
					
						|||
| 
						
						
							
						
						75deb04a2b
	
				 | 
					
					
						|||
| 
						
						
							
						
						7c7ed21a96
	
				 | 
					
					
						|||
| 
						
						
							
						
						a201f20793
	
				 | 
					
					
						|||
| 
						
						
							
						
						598c51bc1a
	
				 | 
					
					
						|||
| 
						
						
							
						
						e1ea61c5f1
	
				 | 
					
					
						|||
| 
						
						
							
						
						ac424bde36
	
				 | 
					
					
						|||
| 
						
						
							
						
						b43b70df3f
	
				 | 
					
					
						|||
| 
						
						
							
						
						4321aa621a
	
				 | 
					
					
						|||
| 
						
						
							
						
						d5d275fb43
	
				 | 
					
					
						|||
| 
						
						
							
						
						6bb3307144
	
				 | 
					
					
						|||
| 
						
						
							
						
						391604d4a2
	
				 | 
					
					
						|||
| 
						
						
							
						
						1d9361c12f
	
				 | 
					
					
						|||
| 
						
						
							
						
						a129b9cdd0
	
				 | 
					
					
						|||
| 
						
						
							
						
						3bf815ac61
	
				 | 
					
					
						|||
| 
						
						
							
						
						77bae4d6fd
	
				 | 
					
					
						|||
| 
						
						
							
						
						0a301c4c9b
	
				 | 
					
					
						|||
| 
						
						
							
						
						27b390a51c
	
				 | 
					
					
						|||
| 
						
						
							
						
						018386d14e
	
				 | 
					
					
						|||
| 
						
						
							
						
						3825d7c6c7
	
				 | 
					
					
						|||
| 
						
						
							
						
						bf930291e4
	
				 | 
					
					
						|||
| 
						
						
							
						
						a8c4988790
	
				 | 
					
					
						|||
| 
						
						
							
						
						28dd204b1a
	
				 | 
					
					
						|||
| 
						
						
							
						
						3cbc1a59a7
	
				 | 
					
					
						|||
| 
						
						
							
						
						277e9ae3d1
	
				 | 
					
					
						|||
| 
						
						
							
						
						27b3ca25b7
	
				 | 
					
					
						|||
| 
						
						
							
						
						f871cd3b62
	
				 | 
					
					
						|||
| 
						
						
							
						
						a8a59ee30c
	
				 | 
					
					
						|||
| 
						
						
							
						
						2cd1416a13
	
				 | 
					
					
						|||
| 
						
						
							
						
						6be7dfbc61
	
				 | 
					
					
						|||
| 
						
						
							
						
						1abbd85614
	
				 | 
					
					
						|||
| 
						
						
							
						
						31ac5ad07c
	
				 | 
					
					
						|||
| 
						
						
							
						
						ae2ba495e9
	
				 | 
					
					
						|||
| 
						
						
							
						
						637aa44548
	
				 | 
					
					
						|||
| 
						
						
							
						
						44dbfc36d9
	
				 | 
					
					
						|||
| 
						
						
							
						
						5dbe7371cb
	
				 | 
					
					
						|||
| 
						
						
							
						
						6c91093198
	
				 | 
					
					
						|||
| 
						
						
							
						
						3f640b7898
	
				 | 
					
					
						|||
| 
						
						
							
						
						7db164fda6
	
				 | 
					
					
						|||
| 
						
						
							
						
						6df1d96cc9
	
				 | 
					
					
						|||
| 
						
						
							
						
						122a796f8c
	
				 | 
					
					
						|||
| 
						
						
							
						
						fbc7812a16
	
				 | 
					
					
						|||
| 
						
						
							
						
						0b1a23e81a
	
				 | 
					
					
						|||
| 
						
						
							
						
						c87e6cfe07
	
				 | 
					
					
						|||
| 
						
						
							
						
						53d51b8a0e
	
				 | 
					
					
						|||
| 
						
						
							
						
						337ae39e08
	
				 | 
					
					
						|||
| 
						
						
							
						
						8fe3a664a6
	
				 | 
					
					
						|||
| 
						
						
							
						
						3bfc0b8181
	
				 | 
					
					
						|||
| ac2951479b | |||
| 2bfd13d843 | |||
| 28db6f9f01 | |||
| 
						
						
							
						
						a4f7b8415d
	
				 | 
					
					
						|||
| 
						
						
							
						
						2255d3d591
	
				 | 
					
					
						|||
| 
						
						
							
						
						97792ae734
	
				 | 
					
					
						|||
| 
						
						
							
						
						a5d13250cc
	
				 | 
					
					
						|||
| 
						
						
							
						
						de9e235d0c
	
				 | 
					
					
						|||
| 
						
						
							
						
						56fb5451cd
	
				 | 
					
					
						|||
| 
						
						
							
						
						870de961f5
	
				 | 
					
					
						|||
| 
						
						
							
						
						22bf6d1c33
	
				 | 
					
					
						|||
| 
						
						
							
						
						5b62f89531
	
				 | 
					
					
						|||
| 
						
						
							
						
						b1326d8f04
	
				 | 
					
					
						|||
| 
						
						
							
						
						fffca4a78c
	
				 | 
					
					
						|||
| 
						
						
							
						
						42bd7f97cb
	
				 | 
					
					
						|||
| 6377856ae0 | |||
| 0f1c52b9e3 | |||
| 6ed6f60fbc | |||
| e65a414065 | |||
| 214d5c4a53 | |||
| fe33931304 | |||
| 113309257e | |||
| b95a8b2ed2 | |||
| 
						 | 
					e922971a5e | ||
| 9d5b71bead | |||
| 890efa2efb | |||
| 674097e425 | |||
| 
						
						
							
						
						3379dcb7f3
	
				 | 
					
					
						|||
| eb5a849e1f | |||
| 
						
						
							
						
						4981a23e8e
	
				 | 
					
					
						|||
| 
						
						
							
						
						c64d4bacb6
	
				 | 
					
					
						|||
| 
						
						
							
						
						838d18013b
	
				 | 
					
					
						|||
| 
						
						
							
						
						3f7902e463
	
				 | 
					
					
						|||
| 
						
						
							
						
						54560ad5d8
	
				 | 
					
					
						|||
| 
						
						
							
						
						0c729db639
	
				 | 
					
					
						|||
| 
						
						
							
						
						1fbaac8d88
	
				 | 
					
					
						|||
| 
						
						
							
						
						b9dc724f0b
	
				 | 
					
					
						|||
| 
						
						
							
						
						a2cc55696f
	
				 | 
					
					
						|||
| 
						
						
							
						
						e79f857feb
	
				 | 
					
					
						|||
| 
						
						
							
						
						affba29c04
	
				 | 
					
					
						|||
| 
						
						
							
						
						756746b144
	
				 | 
					
					
						|||
| 
						
						
							
						
						28b6eade48
	
				 | 
					
					
						|||
| 1de7ef8c96 | |||
| 67eac5dcf5 | |||
| 
						
						
							
						
						7a44bfa075
	
				 | 
					
					
						|||
| 
						
						
							
						
						1c2f25a152
	
				 | 
					
					
						|||
| 
						
						
							
						
						be26ea280e
	
				 | 
					
					
						|||
| 
						
						
							
						
						b4996d069f
	
				 | 
					
					
						|||
| 
						
						
							
						
						bf4892b34d
	
				 | 
					
					
						|||
| 
						
						
							
						
						5f84751fd5
	
				 | 
					
					
						|||
| 
						
						
							
						
						457d1bac60
	
				 | 
					
					
						|||
| 
						
						
							
						
						02ec11845b
	
				 | 
					
					
						|||
| 
						
						
							
						
						612f1bf004
	
				 | 
					
					
						|||
| 
						
						
							
						
						fd80b713ad
	
				 | 
					
					
						|||
| 
						
						
							
						
						508805368c
	
				 | 
					
					
						|||
| 
						
						
							
						
						98eb28a4ec
	
				 | 
					
					
						|||
| 
						
						
							
						
						d1a2f59dd1
	
				 | 
					
					
						|||
| 
						
						
							
						
						bb9adb963a
	
				 | 
					
					
						|||
| 
						
						
							
						
						83e40cd860
	
				 | 
					
					
						|||
| 
						
						
							
						
						c06fb12f6a
	
				 | 
					
					
						|||
| 
						
						
							
						
						6600cf4df8
	
				 | 
					
					
						|||
| 
						
						
							
						
						4293daaa2f
	
				 | 
					
					
						|||
| 
						
						
							
						
						866674ddde
	
				 | 
					
					
						|||
| 
						
						
							
						
						27d478ba4f
	
				 | 
					
					
						|||
| 
						
						
							
						
						cccade763f
	
				 | 
					
					
						|||
| 
						
						
							
						
						f760b85186
	
				 | 
					
					
						|||
| 
						
						
							
						
						e68c5f4f92
	
				 | 
					
					
						|||
| 
						
						
							
						
						b0f3b6b5c3
	
				 | 
					
					
						|||
| 
						
						
							
						
						cb2af379fa
	
				 | 
					
					
						|||
| 
						
						
							
						
						38f8103265
	
				 | 
					
					
						|||
| 
						
						
							
						
						06bb18bdaa
	
				 | 
					
					
						|||
| 
						
						
							
						
						84c38500d0
	
				 | 
					
					
						|||
| 
						
						
							
						
						9529bbf08b
	
				 | 
					
					
						|||
| 
						
						
							
						
						8baf77bcf7
	
				 | 
					
					
						|||
| 
						
						
							
						
						b2ac5fbef2
	
				 | 
					
					
						|||
| 
						
						
							
						
						c79b1d7aab
	
				 | 
					
					
						|||
| 
						 | 
					4f55a8209c | ||
| 
						 | 
					ace302111a | ||
| 
						 | 
					1391fa0dde | ||
| 
						 | 
					cbdc7acdcd | ||
| 
						 | 
					b80d91825a | ||
| 
						 | 
					1a703b7eba | ||
| 
						 | 
					3621ea7744 | ||
| 
						 | 
					b638343f02 | ||
| 
						 | 
					269a64cabb | ||
| 
						
						
							
						
						406e5187a8
	
				 | 
					
					
						|||
| 
						
						
							
						
						9bdd08d8dd
	
				 | 
					
					
						|||
| 
						
						
							
						
						d737232dcf
	
				 | 
					
					
						|||
| 
						
						
							
						
						c9d751479e
	
				 | 
					
					
						|||
| 
						
						
							
						
						a2c2bfe585
	
				 | 
					
					
						|||
| 
						
						
							
						
						c7f9da0dee
	
				 | 
					
					
						|||
| 
						 | 
					a243cda1df | ||
| 
						 | 
					7b238f32fd | ||
| 
						
						
							
						
						313af28d7f
	
				 | 
					
					
						|||
| 
						
						
							
						
						c64e1e208c
	
				 | 
					
					
						|||
| 
						
						
							
						
						c9b07a9a2a
	
				 | 
					
					
						|||
| 55c0e355f1 | |||
| be414891ec | |||
| 787876ab6a | |||
| 
						
						
							
						
						8578cde620
	
				 | 
					
					
						|||
| 
						
						
							
						
						14d55d45a8
	
				 | 
					
					
						|||
| 
						
						
							
						
						724391584e
	
				 | 
					
					
						|||
| 3a5e45808a | |||
| 
						
						
							
						
						488055955c
	
				 | 
					
					
						|||
| 
						 | 
					313ebc64cc | ||
| 
						 | 
					1ed8b1d0c1 | ||
| 4af816d931 | |||
| 1c058a4323 | |||
| 461ed1fcda | |||
| 
						
						
							
						
						5363afa558
	
				 | 
					
					
						|||
| 
						
						
							
						
						f0d2737da8
	
				 | 
					
					
						|||
| 
						
						
							
						
						1f2f80aa3e
	
				 | 
					
					
						|||
| 
						
						
							
						
						240a872e65
	
				 | 
					
					
						|||
| c1ec6f0849 | |||
| ab42686d4d | |||
| 
						
						
							
						
						c9727e92b8
	
				 | 
					
					
						|||
| 
						
						
							
						
						9b8768061d
	
				 | 
					
					
						|||
| 
						
						
							
						
						0949f0da54
	
				 | 
					
					
						|||
| 
						
						
							
						
						215ca705ac
	
				 | 
					
					
						|||
| 
						
						
							
						
						03457af04a
	
				 | 
					
					
						|||
| 
						
						
							
						
						73c6a1febf
	
				 | 
					
					
						|||
| 
						
						
							
						
						ba8d30bcde
	
				 | 
					
					
						|||
| 
						
						
							
						
						8449658b47
	
				 | 
					
					
						|||
| 
						
						
							
						
						c7f417234e
	
				 | 
					
					
						|||
| 
						
						
							
						
						6c847ee1e1
	
				 | 
					
					
						|||
| 
						
						
							
						
						18ad4d376e
	
				 | 
					
					
						|||
| 
						
						
							
						
						c4d5ba5c9d
	
				 | 
					
					
						|||
| 
						
						
							
						
						1069669049
	
				 | 
					
					
						|||
| 
						
						
							
						
						aa648fec62
	
				 | 
					
					
						|||
| 
						
						
							
						
						541900673a
	
				 | 
					
					
						|||
| 
						
						
							
						
						265502ffd0
	
				 | 
					
					
						|||
| 
						
						
							
						
						3bd79350d1
	
				 | 
					
					
						|||
| 
						
						
							
						
						5294d1fb23
	
				 | 
					
					
						|||
| 
						
						
							
						
						ec1269dcf1
	
				 | 
					
					
						|||
| 
						
						
							
						
						edb0a25f34
	
				 | 
					
					
						|||
| 
						
						
							
						
						7cd10118cc
	
				 | 
					
					
						|||
| 
						
						
							
						
						fcddc8f345
	
				 | 
					
					
						|||
| 
						
						
							
						
						1cc34240da
	
				 | 
					
					
						|||
| 
						
						
							
						
						013f7f02bc
	
				 | 
					
					
						|||
| 
						
						
							
						
						4e79e4100f
	
				 | 
					
					
						|||
| 
						
						
							
						
						feda1f067f
	
				 | 
					
					
						|||
| 
						
						
							
						
						fe0e192a43
	
				 | 
					
					
						|||
| 
						
						
							
						
						93df294142
	
				 | 
					
					
						|||
| 
						
						
							
						
						78d65c39f3
	
				 | 
					
					
						|||
| 
						
						
							
						
						18b0dbd797
	
				 | 
					
					
						|||
| 
						
						
							
						
						80cc8cbb40
	
				 | 
					
					
						|||
| 
						
						
							
						
						646e95a9fc
	
				 | 
					
					
						|||
| 
						
						
							
						
						6f9d51673b
	
				 | 
					
					
						|||
| 
						
						
							
						
						f8c6887769
	
				 | 
					
					
						|||
| 
						
						
							
						
						cd2a507b7f
	
				 | 
					
					
						|||
| 
						
						
							
						
						3cafce00a2
	
				 | 
					
					
						|||
| 
						
						
							
						
						837f3fbe98
	
				 | 
					
					
						|||
| 
						
						
							
						
						ca7cc5d7ee
	
				 | 
					
					
						|||
| 
						
						
							
						
						ef2c14daa2
	
				 | 
					
					
						|||
| 
						
						
							
						
						3a17837cc6
	
				 | 
					
					
						|||
| 
						
						
							
						
						2617a64acf
	
				 | 
					
					
						|||
| 
						
						
							
						
						afe1e12a3b
	
				 | 
					
					
						|||
| 
						
						
							
						
						be80f5ff85
	
				 | 
					
					
						|||
| 
						
						
							
						
						3281d69eba
	
				 | 
					
					
						|||
| 
						
						
							
						
						77b6ce9937
	
				 | 
					
					
						|||
| 
						
						
							
						
						39275f61b5
	
				 | 
					
					
						|||
| 
						
						
							
						
						72193ba8f3
	
				 | 
					
					
						|||
| 
						
						
							
						
						98dd9b6617
	
				 | 
					
					
						|||
| 
						
						
							
						
						a22b94a263
	
				 | 
					
					
						|||
| 
						
						
							
						
						9c75eafdb3
	
				 | 
					
					
						|||
| 
						
						
							
						
						28fda3d0c7
	
				 | 
					
					
						|||
| 
						
						
							
						
						187c2ea43e
	
				 | 
					
					
						|||
| 
						
						
							
						
						ae7d967461
	
				 | 
					
					
						|||
| 
						
						
							
						
						1ce71f1fa1
	
				 | 
					
					
						|||
| 
						
						
							
						
						9b68808c77
	
				 | 
					
					
						|||
| 
						 | 
					99b7bf8199 | ||
| 
						 | 
					eb9bb73c31 | ||
| 
						 | 
					a8c3830d67 | ||
| 
						 | 
					07a5a19141 | ||
| ecc100ac45 | |||
| 573b76d3ff | |||
| f7dad5e419 | |||
| 9f2f1c0848 | |||
| 580d9fd979 | |||
| 3b375abc09 | |||
| c527b5e67c | |||
| e9f09bbe54 | |||
| 3aece9316c | |||
| a61c889c6c | |||
| 0dd3221a56 | |||
| 66918521f8 | |||
| bb1846e462 | |||
| a976a6eaf4 | |||
| 4252f66fd3 | |||
| f2d780b48f | |||
| 300541f9bb | |||
| 43787bb813 | |||
| 3417c51a3b | |||
| f98e603e82 | |||
| c9b71701c8 | |||
| 28e98488f1 | |||
| b4d476613e | |||
| b48a1aac44 | |||
| 596d212593 | |||
| 54f290327e | |||
| 16f248ceab | |||
| 856d811187 | |||
| d07b194c04 | |||
| 2554b58be6 | |||
| a627b5838e | |||
| c479a9f381 | |||
| 02057e663b | |||
| 6501594100 | |||
| c6599edc3d | |||
| 709a0620b6 | |||
| f9b2a96c7c | |||
| 4dca6189cb | |||
| c7f5b63fe5 | |||
| 96c2f45c85 | |||
| 06f04eb3a5 | |||
| 8af97e43b4 | |||
| d1e8234b93 | |||
| a03d6015a6 | |||
| 246ac52d0a | |||
| abf395ff9a | |||
| 4fdc8eb1d0 | |||
| d7dcde898c | |||
| f85484d3ed | |||
| 5060bd30c9 | |||
| 3959f2260b | |||
| 6f4f1216ad | |||
| f401ffbf81 | |||
| 0251697951 | |||
| 178c12b893 | |||
| 4beda9200e | |||
| 7dfe411053 | 
@@ -14,13 +14,13 @@ The backend of the Solar Network is written in Go and is a microservices app. Th
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Commit Messages
 | 
					## Commit Messages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
We're using the gitmoji to clarify the reason and changes of the commit. To learn more about gitmoji, visit https://gitmoji.dev
 | 
					We're using the gitmoji to clarify the reason and changes of the commit. To learn more about gitmoji, visit <https://gitmoji.dev>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
All the commit message should follow `:[gitmoji]: <commit message>` syntax
 | 
					All the commit message should follow `:[gitmoji]: <commit message>` syntax
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Translations & Localization
 | 
					## Translations & Localization
 | 
				
			||||||
 | 
					
 | 
				
			||||||
We're not accepting translation and localization improvements, or fixes on the GitHub or Solsynth Git Repository. If you want to contribute to those, please head to our Crowdin project: https://crowdin.com/project/solian
 | 
					We're not accepting translation and localization improvements, or fixes on the GitHub or Solsynth Git Repository. If you want to contribute to those, please head to our Crowdin project: <https://crowdin.com/project/solian>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## New Features
 | 
					## New Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,7 +30,12 @@ To contribute new features, please create an issue or mention the feature you wa
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Read the error message, check for the update (including pre-releases), and wiki before creating an issue. At the same time, be respectful and don't argue with our developers and contributors in the development chat or GitHub issue. Otherwise your issue may got deleted and your Solar Network Account may got a strike.
 | 
					Read the error message, check for the update (including pre-releases), and wiki before creating an issue. At the same time, be respectful and don't argue with our developers and contributors in the development chat or GitHub issue. Otherwise your issue may got deleted and your Solar Network Account may got a strike.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Styles of Code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Before you create a Pull Request, make sure your code has pass the `flutter analyze` check, if there is any notes, fix as much as possible, if there is no way to fix, do ignore.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When the code contains comments, use English. We do not any other language of comments existing in the codebase. It might confuse future contributors, cause the code hard to understand and maintaiance.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-----------
 | 
					-----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
We appreciate every single commit you contributed. Let's work together and create a better Solar Network!
 | 
					We appreciate every single commit you contributed. Let's work together and create a better Solar Network!
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,3 +63,8 @@ If you want to build the release version, use the flutter build command. Learn m
 | 
				
			|||||||
flutter build <platform>
 | 
					flutter build <platform>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Known Issues
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Due to the issues with the flutter build tools, [see](https://github.com/flutter/flutter/issues/160622).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Since there is a watchOS app for iOS, you're unable to use the flutter cli to run iOS app. Use xcode instead.
 | 
				
			||||||
@@ -24,6 +24,8 @@ android {
 | 
				
			|||||||
    ndkVersion = "29.0.13113456"
 | 
					    ndkVersion = "29.0.13113456"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    compileOptions {
 | 
					    compileOptions {
 | 
				
			||||||
 | 
					        isCoreLibraryDesugaringEnabled = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sourceCompatibility = JavaVersion.VERSION_17
 | 
					        sourceCompatibility = JavaVersion.VERSION_17
 | 
				
			||||||
        targetCompatibility = JavaVersion.VERSION_17
 | 
					        targetCompatibility = JavaVersion.VERSION_17
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -63,6 +65,8 @@ android {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
 | 
					    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    implementation("com.google.android.material:material:1.12.0")
 | 
					    implementation("com.google.android.material:material:1.12.0")
 | 
				
			||||||
    implementation("com.github.bumptech.glide:glide:4.16.0")
 | 
					    implementation("com.github.bumptech.glide:glide:4.16.0")
 | 
				
			||||||
    implementation("com.squareup.okhttp3:okhttp:5.1.0")
 | 
					    implementation("com.squareup.okhttp3:okhttp:5.1.0")
 | 
				
			||||||
@@ -71,3 +75,4 @@ dependencies {
 | 
				
			|||||||
flutter {
 | 
					flutter {
 | 
				
			||||||
    source = "../.."
 | 
					    source = "../.."
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,6 +51,12 @@
 | 
				
			|||||||
                <data android:scheme="http" android:host="solian.app" />
 | 
					                <data android:scheme="http" android:host="solian.app" />
 | 
				
			||||||
                <data android:scheme="https" />
 | 
					                <data android:scheme="https" />
 | 
				
			||||||
            </intent-filter>
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					            <intent-filter android:autoVerify="true">
 | 
				
			||||||
 | 
					                <action android:name="android.intent.action.VIEW" />
 | 
				
			||||||
 | 
					                <category android:name="android.intent.category.DEFAULT" />
 | 
				
			||||||
 | 
					                <category android:name="android.intent.category.BROWSABLE" />
 | 
				
			||||||
 | 
					                <data android:scheme="solian" />
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Share Intent Filters -->
 | 
					            <!-- Share Intent Filters -->
 | 
				
			||||||
            <intent-filter>
 | 
					            <intent-filter>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1079
									
								
								assets/i18n/es-ES.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1079
									
								
								assets/i18n/ja-JP.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1079
									
								
								assets/i18n/ko-KR.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1079
									
								
								assets/i18n/zh-OG.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -116,7 +116,7 @@
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    "postHasAttachments": {
 | 
					    "postHasAttachments": {
 | 
				
			||||||
        "one": "{} 個附件",
 | 
					        "one": "{} 個附件",
 | 
				
			||||||
        "other": "{}個附件"
 | 
					        "other": "{} 個附件"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "edited": "已編輯",
 | 
					    "edited": "已編輯",
 | 
				
			||||||
    "addVideo": "添加視頻",
 | 
					    "addVideo": "添加視頻",
 | 
				
			||||||
@@ -668,7 +668,6 @@
 | 
				
			|||||||
    "publisherFeatureDevelopDescription": "為你的開發者解鎖包括應用套件,API 及更多開發功能。",
 | 
					    "publisherFeatureDevelopDescription": "為你的開發者解鎖包括應用套件,API 及更多開發功能。",
 | 
				
			||||||
    "publisherFeatureDevelopHint": "目前該功能還在開發中,你需要邀請才可解鎖。",
 | 
					    "publisherFeatureDevelopHint": "目前該功能還在開發中,你需要邀請才可解鎖。",
 | 
				
			||||||
    "learnMore": "瞭解更多",
 | 
					    "learnMore": "瞭解更多",
 | 
				
			||||||
    "discoverWebArticles": "來自站外的文章",
 | 
					 | 
				
			||||||
    "webArticlesStand": "文章亭",
 | 
					    "webArticlesStand": "文章亭",
 | 
				
			||||||
    "about": "關於",
 | 
					    "about": "關於",
 | 
				
			||||||
    "somethingWentWrong": "發生了一些錯誤",
 | 
					    "somethingWentWrong": "發生了一些錯誤",
 | 
				
			||||||
@@ -691,8 +690,6 @@
 | 
				
			|||||||
    "sharePostPhoto": "通過圖片分享帖子",
 | 
					    "sharePostPhoto": "通過圖片分享帖子",
 | 
				
			||||||
    "wouldYouLikeToNavigateToChat": "你想要前往聊天頁面嗎?",
 | 
					    "wouldYouLikeToNavigateToChat": "你想要前往聊天頁面嗎?",
 | 
				
			||||||
    "abuseReports": "舉報",
 | 
					    "abuseReports": "舉報",
 | 
				
			||||||
    "discoverRealms": "發現領域",
 | 
					 | 
				
			||||||
    "discoverPublishers": "發現發佈者",
 | 
					 | 
				
			||||||
    "membershipCancel": "取消會員訂閱",
 | 
					    "membershipCancel": "取消會員訂閱",
 | 
				
			||||||
    "membershipCancelConfirm": "你確定要取消會員訂閱嗎?",
 | 
					    "membershipCancelConfirm": "你確定要取消會員訂閱嗎?",
 | 
				
			||||||
    "membershipCancelHint": "你確定要取消會員訂閱嗎?你將不會再次被扣費。你的會員資格將在當前計費週期結束前保持有效。並且你將無法重新訂閱,直到當前訂閱結束。",
 | 
					    "membershipCancelHint": "你確定要取消會員訂閱嗎?你將不會再次被扣費。你的會員資格將在當前計費週期結束前保持有效。並且你將無法重新訂閱,直到當前訂閱結束。",
 | 
				
			||||||
@@ -811,5 +808,274 @@
 | 
				
			|||||||
    "filesListAdditional": {
 | 
					    "filesListAdditional": {
 | 
				
			||||||
        "one": "+{} 個文件被摺疊",
 | 
					        "one": "+{} 個文件被摺疊",
 | 
				
			||||||
        "other": "+{} 個文件被摺疊"
 | 
					        "other": "+{} 個文件被摺疊"
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
 | 
					    "pollQuestions": "問題",
 | 
				
			||||||
 | 
					    "pollAnswerSubmitted": "投票答案已提交。",
 | 
				
			||||||
 | 
					    "modifyAnswers": "修改答案",
 | 
				
			||||||
 | 
					    "back": "返回",
 | 
				
			||||||
 | 
					    "submit": "提交",
 | 
				
			||||||
 | 
					    "pollOptionDefaultLabel": "選項1",
 | 
				
			||||||
 | 
					    "pollUpdated": "投票已更新。",
 | 
				
			||||||
 | 
					    "pollCreated": "投票已創建。",
 | 
				
			||||||
 | 
					    "pollCreate": "創建投票",
 | 
				
			||||||
 | 
					    "pollEdit": "編輯投票",
 | 
				
			||||||
 | 
					    "pollPreviewJsonDebug": "調試預覽",
 | 
				
			||||||
 | 
					    "pollTitleRequired": "標題不可為空",
 | 
				
			||||||
 | 
					    "pollEndDateOptional": "結束日期和時間 (可選)",
 | 
				
			||||||
 | 
					    "notSet": "未設定",
 | 
				
			||||||
 | 
					    "pick": "選擇",
 | 
				
			||||||
 | 
					    "clear": "清除",
 | 
				
			||||||
 | 
					    "questions": "問題",
 | 
				
			||||||
 | 
					    "pollAddQuestion": "添加問題",
 | 
				
			||||||
 | 
					    "pollQuestionTypeSingleChoice": "單選框",
 | 
				
			||||||
 | 
					    "pollQuestionTypeMultipleChoice": "多選框",
 | 
				
			||||||
 | 
					    "pollQuestionTypeFreeText": "自由文本",
 | 
				
			||||||
 | 
					    "pollQuestionTypeYesNo": "是 / 不是",
 | 
				
			||||||
 | 
					    "pollQuestionTypeRating": "評分",
 | 
				
			||||||
 | 
					    "pollNoQuestionsYet": "尚未有問題",
 | 
				
			||||||
 | 
					    "pollNoQuestionsHint": "使用「添加問題」開始建立您的投票。",
 | 
				
			||||||
 | 
					    "pollDebugPreview": "調試預覽",
 | 
				
			||||||
 | 
					    "pollUntitledQuestion": "無標題問題",
 | 
				
			||||||
 | 
					    "moveUp": "往上移動",
 | 
				
			||||||
 | 
					    "moveDown": "往下移動",
 | 
				
			||||||
 | 
					    "required": "必需的",
 | 
				
			||||||
 | 
					    "pollQuestionTitle": "問題標題",
 | 
				
			||||||
 | 
					    "pollQuestionTitleRequired": "問題標題是必需的",
 | 
				
			||||||
 | 
					    "pollQuestionDescriptionOptional": "問題描述(選填)",
 | 
				
			||||||
 | 
					    "options": "選項",
 | 
				
			||||||
 | 
					    "pollAddOption": "添加選項",
 | 
				
			||||||
 | 
					    "pollOptionLabel": "選項標籤",
 | 
				
			||||||
 | 
					    "pollLongTextAnswerPreview": "長文本答案 (預覽)",
 | 
				
			||||||
 | 
					    "pollShortTextAnswerPreview": "短文本答案 (預覽)",
 | 
				
			||||||
 | 
					    "award": "讚賞",
 | 
				
			||||||
 | 
					    "awardPost": "讚賞帖子",
 | 
				
			||||||
 | 
					    "awardMessage": "消息",
 | 
				
			||||||
 | 
					    "awardMessageHint": "輸入您的讚賞消息...",
 | 
				
			||||||
 | 
					    "awardAttitude": "態度",
 | 
				
			||||||
 | 
					    "awardAttitudePositive": "積極",
 | 
				
			||||||
 | 
					    "awardAttitudeNegative": "消极",
 | 
				
			||||||
 | 
					    "awardAmount": "金額",
 | 
				
			||||||
 | 
					    "awardAmountHint": "輸入金額……",
 | 
				
			||||||
 | 
					    "awardAmountRequired": "「金額」為必填字段",
 | 
				
			||||||
 | 
					    "awardAmountInvalid": "請輸入有效金額",
 | 
				
			||||||
 | 
					    "awardMessageTooLong": "消息太長(最多4096個字符)",
 | 
				
			||||||
 | 
					    "awardSuccess": "獎勵已成功發送!",
 | 
				
			||||||
 | 
					    "awardSubmit": "讚賞",
 | 
				
			||||||
 | 
					    "awardPostPreview": "帖子預覽",
 | 
				
			||||||
 | 
					    "awardNoContent": "暫無內容",
 | 
				
			||||||
 | 
					    "awardByPublisher": "由 {} 發表",
 | 
				
			||||||
 | 
					    "awardBenefits": "讚賞福利",
 | 
				
			||||||
 | 
					    "awardBenefitsDescription": "為該帖子授予獎勵可以提升其價值和曝光度。價值更高的帖子更有可能在社區中被推薦和突出顯示。",
 | 
				
			||||||
 | 
					    "checkInResultLevel5": "生日快樂 🥳",
 | 
				
			||||||
 | 
					    "region": "區域",
 | 
				
			||||||
 | 
					    "accountRegionHint": "這個區域將用於內容傳遞和本地化。",
 | 
				
			||||||
 | 
					    "settingsCustomFontsHelper": "使用逗號分隔。",
 | 
				
			||||||
 | 
					    "settingsBackgroundImageEnable": "顯示背景圖片",
 | 
				
			||||||
 | 
					    "settingsDataSavingMode": "低數據模式",
 | 
				
			||||||
 | 
					    "dataSavingHint": "低數據模式",
 | 
				
			||||||
 | 
					    "postTypePost": "帖子",
 | 
				
			||||||
 | 
					    "searchDrafts": "搜尋草稿……",
 | 
				
			||||||
 | 
					    "noSearchResults": "無搜尋結果",
 | 
				
			||||||
 | 
					    "contactMethodMakePublic": "設為公開",
 | 
				
			||||||
 | 
					    "contactMethodMakePrivate": "設定為僅自己可見",
 | 
				
			||||||
 | 
					    "contactMethodPublic": "公開",
 | 
				
			||||||
 | 
					    "contactMethodPrivate": "私密",
 | 
				
			||||||
 | 
					    "discoverRealms": "發現領域",
 | 
				
			||||||
 | 
					    "discoverPublishers": "發現發佈者",
 | 
				
			||||||
 | 
					    "discoverShuffledPost": "隨機帖子",
 | 
				
			||||||
 | 
					    "projects": "項目",
 | 
				
			||||||
 | 
					    "noProjects": "未找到項目。",
 | 
				
			||||||
 | 
					    "deleteProject": "刪除項目",
 | 
				
			||||||
 | 
					    "deleteProjectHint": "確定要刪除此項目嗎?此操作無法撤銷。",
 | 
				
			||||||
 | 
					    "createProject": "新建專案",
 | 
				
			||||||
 | 
					    "editProject": "編輯項目",
 | 
				
			||||||
 | 
					    "projectDetails": "專案描述",
 | 
				
			||||||
 | 
					    "createBot": "創建機器人",
 | 
				
			||||||
 | 
					    "bots": "機器人",
 | 
				
			||||||
 | 
					    "noBots": "還沒有機器人。",
 | 
				
			||||||
 | 
					    "deleteBotHint": "您確定要刪除這個機器人嗎?此操作無法撤銷。",
 | 
				
			||||||
 | 
					    "deleteBot": "刪除機器人",
 | 
				
			||||||
 | 
					    "discoverWebArticles": "來自站外的文章",
 | 
				
			||||||
 | 
					    "messageJumpNotLoaded": "引用的訊息未加載,無法跳轉到該訊息。",
 | 
				
			||||||
 | 
					    "postUnlinkRealm": "未連結到領域",
 | 
				
			||||||
 | 
					    "postSlug": "別名",
 | 
				
			||||||
 | 
					    "postSlugHint": "這個別名可以用於在網頁通過 URL 瀏覽到你的帖子,它應該在同一發布者中是唯一。",
 | 
				
			||||||
 | 
					    "attachmentOnDevice": "離線",
 | 
				
			||||||
 | 
					    "attachmentOnCloud": "在線",
 | 
				
			||||||
 | 
					    "attachments": "附件",
 | 
				
			||||||
 | 
					    "publisherCollabInvitation": "協作邀請",
 | 
				
			||||||
 | 
					    "publisherCollabInvitationCount": {
 | 
				
			||||||
 | 
					        "zero": "無邀請",
 | 
				
			||||||
 | 
					        "one": "{} 個可用邀請",
 | 
				
			||||||
 | 
					        "other": "{} 個可用邀請"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "failedToLoadUserInfo": "無法加載用戶資訊",
 | 
				
			||||||
 | 
					    "failedToLoadUserInfoNetwork": "看起來是網絡問題,您可以點擊下面的按鈕再試一次。",
 | 
				
			||||||
 | 
					    "failedToLoadUserInfoUnauthorized": "看起來您的會話已經登出或不再可用,如果您想的話,您仍然可以嘗試再次獲取用戶資訊。",
 | 
				
			||||||
 | 
					    "okay": "好的",
 | 
				
			||||||
 | 
					    "postDetail": "帖子詳情",
 | 
				
			||||||
 | 
					    "postCount": {
 | 
				
			||||||
 | 
					        "zero": "沒有帖子",
 | 
				
			||||||
 | 
					        "one": "{} 帖子",
 | 
				
			||||||
 | 
					        "other": "{} 帖子"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "mimeType": "類型",
 | 
				
			||||||
 | 
					    "fileSize": "文件大小",
 | 
				
			||||||
 | 
					    "fileHash": "文件哈希",
 | 
				
			||||||
 | 
					    "exifData": "EXIF 數據",
 | 
				
			||||||
 | 
					    "postShuffle": "隨機帖子",
 | 
				
			||||||
 | 
					    "leveling": "等級",
 | 
				
			||||||
 | 
					    "levelingHistory": "經驗記錄",
 | 
				
			||||||
 | 
					    "stellarProgram": "恆星計畫",
 | 
				
			||||||
 | 
					    "socialCredits": "社會信用點",
 | 
				
			||||||
 | 
					    "credits": "信用",
 | 
				
			||||||
 | 
					    "creditsStatus": "積分狀態",
 | 
				
			||||||
 | 
					    "socialCreditsDescription": "社會信用是 Solar Network 評價用戶的一種方式。它基於用戶的行為和互動來計算。以 100 分為基準,分數越高表示用戶在社區中的信譽越好。分數會隨著時間的推移而變化,反映用戶的最新行為。信用等級高的用戶可以享受到更多的福利,反之的用戶部分功能可能受到限制。",
 | 
				
			||||||
 | 
					    "socialCreditsLevelPoor": "糟糕",
 | 
				
			||||||
 | 
					    "socialCreditsLevelNormal": "正常",
 | 
				
			||||||
 | 
					    "socialCreditsLevelGood": "良好",
 | 
				
			||||||
 | 
					    "socialCreditsLevelExcellent": "優秀",
 | 
				
			||||||
 | 
					    "orderByPopularity": "按熱度排序",
 | 
				
			||||||
 | 
					    "orderByReleaseDate": "按發佈日期排序",
 | 
				
			||||||
 | 
					    "editBot": "編輯機器人",
 | 
				
			||||||
 | 
					    "botAutomatedBy": "由 {} 自動化",
 | 
				
			||||||
 | 
					    "botDetails": "機器人描述",
 | 
				
			||||||
 | 
					    "overview": "概述",
 | 
				
			||||||
 | 
					    "keys": "密鑰",
 | 
				
			||||||
 | 
					    "botNotFound": "機器人未找到。",
 | 
				
			||||||
 | 
					    "newBotKey": "新建密鑰",
 | 
				
			||||||
 | 
					    "newBotKeyHint": "輸入新密鑰的名稱,密鑰只會顯示一次。",
 | 
				
			||||||
 | 
					    "revokeBotKey": "撤銷密鑰",
 | 
				
			||||||
 | 
					    "revokeBotKeyHint": "你確定要撤銷這個密鑰?這個操作無法撤回,所有使用該密鑰的應用程式會停止工作。",
 | 
				
			||||||
 | 
					    "noBotKeys": "機器人未找到。",
 | 
				
			||||||
 | 
					    "revoke": "撤銷",
 | 
				
			||||||
 | 
					    "keyName": "密鑰名稱",
 | 
				
			||||||
 | 
					    "newKeyGenerated": "新密鑰已生成",
 | 
				
			||||||
 | 
					    "copyKeyHint": "請安全地保存該密鑰,你不會再次看到它。",
 | 
				
			||||||
 | 
					    "rotateKey": "旋轉密鑰",
 | 
				
			||||||
 | 
					    "rotateBotKey": "旋轉密鑰",
 | 
				
			||||||
 | 
					    "rotateBotKeyHint": "你確認要旋轉這個密鑰?久的密鑰會立即失效,該操作無法撤銷。",
 | 
				
			||||||
 | 
					    "webFeedArticleCount": {
 | 
				
			||||||
 | 
					        "zero": "無文章",
 | 
				
			||||||
 | 
					        "one": "{} 文章",
 | 
				
			||||||
 | 
					        "other": "{} 文章"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "webFeedSubscribed": "你已經訂閱了這個來源",
 | 
				
			||||||
 | 
					    "webFeedUnsubscribed": "你已經取消訂閱這個來源",
 | 
				
			||||||
 | 
					    "appDetails": "應用程式詳情",
 | 
				
			||||||
 | 
					    "secrets": "密鑰",
 | 
				
			||||||
 | 
					    "appNotFound": "找不到應用程式。",
 | 
				
			||||||
 | 
					    "secretCopied": "密鑰已複製到剪貼簿。",
 | 
				
			||||||
 | 
					    "deleteSecret": "刪除密鑰",
 | 
				
			||||||
 | 
					    "deleteSecretHint": "您確定要刪除此密鑰嗎?此操作無法復原。",
 | 
				
			||||||
 | 
					    "generateSecret": "產生新密鑰",
 | 
				
			||||||
 | 
					    "createdAt": "建立於 {}",
 | 
				
			||||||
 | 
					    "newSecretGenerated": "已產生新密鑰",
 | 
				
			||||||
 | 
					    "copySecretHint": "請複製此密鑰並將其存放在安全的地方。您將無法再次看到它。",
 | 
				
			||||||
 | 
					    "expiresIn": "過期時間(秒)",
 | 
				
			||||||
 | 
					    "isOidc": "OIDC 相容",
 | 
				
			||||||
 | 
					    "pinPost": "置頂帖子",
 | 
				
			||||||
 | 
					    "unpinPost": "取消置頂",
 | 
				
			||||||
 | 
					    "pinnedPost": "已置顶",
 | 
				
			||||||
 | 
					    "publisherPage": "發布者頁面",
 | 
				
			||||||
 | 
					    "realmPage": "領域頁面",
 | 
				
			||||||
 | 
					    "replyPage": "回覆頁面",
 | 
				
			||||||
 | 
					    "pinPostPublisherHint": "將這篇文章置顶到您的發佈者頁面",
 | 
				
			||||||
 | 
					    "pinPostRealmHint": "將這篇文章置顶到領域頁面",
 | 
				
			||||||
 | 
					    "pinPostRealmDisabledHint": "這個帖子不屬於任何領域",
 | 
				
			||||||
 | 
					    "pinPostReplyHint": "將這篇文章置顶到回覆頁面",
 | 
				
			||||||
 | 
					    "pinPostReplyDisabledHint": "這篇帖子不是回覆",
 | 
				
			||||||
 | 
					    "pin": "置顶",
 | 
				
			||||||
 | 
					    "unpinPostHint": "你確定要取消置顶這篇帖子嗎?",
 | 
				
			||||||
 | 
					    "all": "所有",
 | 
				
			||||||
 | 
					    "statusPresent": "至今",
 | 
				
			||||||
 | 
					    "accountAutomated": "機器人",
 | 
				
			||||||
 | 
					    "chatBreakClearButton": "清除",
 | 
				
			||||||
 | 
					    "chatBreak5m": "5 分鐘",
 | 
				
			||||||
 | 
					    "chatBreak10m": "10 分鐘",
 | 
				
			||||||
 | 
					    "chatBreak15m": "15 分鐘",
 | 
				
			||||||
 | 
					    "chatBreak30m": "30 分鐘",
 | 
				
			||||||
 | 
					    "chatBreakCustomMinutes": "自訂(分鐘)",
 | 
				
			||||||
 | 
					    "errorGeneric": "錯誤:{}",
 | 
				
			||||||
 | 
					    "searchMessages": "搜尋消息",
 | 
				
			||||||
 | 
					    "messagesCount": "{} 消息",
 | 
				
			||||||
 | 
					    "dotSeparator": ".",
 | 
				
			||||||
 | 
					    "roleValidationHint": "成員角色必須設置在0到100之間",
 | 
				
			||||||
 | 
					    "searchMessagesHint": "搜尋消息…",
 | 
				
			||||||
 | 
					    "searchLinks": "連結",
 | 
				
			||||||
 | 
					    "searchAttachments": "附件",
 | 
				
			||||||
 | 
					    "noMessagesFound": "未找到消息",
 | 
				
			||||||
 | 
					    "openInBrowser": "在瀏覽器打開",
 | 
				
			||||||
 | 
					    "highlightPost": "精選帖子",
 | 
				
			||||||
 | 
					    "filters": "過濾器",
 | 
				
			||||||
 | 
					    "apply": "應用",
 | 
				
			||||||
 | 
					    "pubName": "題目名稱",
 | 
				
			||||||
 | 
					    "realm": "領域",
 | 
				
			||||||
 | 
					    "shuffle": "隨機",
 | 
				
			||||||
 | 
					    "pinned": "已置顶",
 | 
				
			||||||
 | 
					    "noResultsFound": "未找到結果",
 | 
				
			||||||
 | 
					    "toggleFilters": "切換篩檢器",
 | 
				
			||||||
 | 
					    "notableDayNext": "距離 {} 還有",
 | 
				
			||||||
 | 
					    "expandPoll": "展開投票",
 | 
				
			||||||
 | 
					    "collapsePoll": "摺叠投票",
 | 
				
			||||||
 | 
					    "embedView": "嵌入視圖",
 | 
				
			||||||
 | 
					    "embedUri": "嵌入URL",
 | 
				
			||||||
 | 
					    "aspectRatio": "縱橫比",
 | 
				
			||||||
 | 
					    "renderer": "渲染器",
 | 
				
			||||||
 | 
					    "addEmbed": "添加嵌入",
 | 
				
			||||||
 | 
					    "editEmbed": "編輯嵌入",
 | 
				
			||||||
 | 
					    "deleteEmbed": "刪除嵌入",
 | 
				
			||||||
 | 
					    "deleteEmbedConfirm": "您確定要刪除這個嵌入嗎?",
 | 
				
			||||||
 | 
					    "currentEmbed": "當前嵌入",
 | 
				
			||||||
 | 
					    "noEmbed": "尚未嵌入",
 | 
				
			||||||
 | 
					    "save": "保存",
 | 
				
			||||||
 | 
					    "webView": "網頁視圖",
 | 
				
			||||||
 | 
					    "settingsDefaultPool": "預設檔案池",
 | 
				
			||||||
 | 
					    "settingsDefaultPoolHelper": "選擇文件上傳的默認儲存池",
 | 
				
			||||||
 | 
					    "uploadFile": "上傳檔案",
 | 
				
			||||||
 | 
					    "authDeviceChallenges": "設備活動",
 | 
				
			||||||
 | 
					    "authDeviceHint": "向左滑動以編輯標籤,向右滑動以登出設備。",
 | 
				
			||||||
 | 
					    "settingsMessageDisplayStyle": "訊息顯示樣式",
 | 
				
			||||||
 | 
					    "auto": "自動",
 | 
				
			||||||
 | 
					    "manual": "手動",
 | 
				
			||||||
 | 
					    "iframeCode": "Iframe 代碼",
 | 
				
			||||||
 | 
					    "iframeCodeHint": "<iframe src=\"...\" width=\"...\" height=\"...\">",
 | 
				
			||||||
 | 
					    "parseIframe": "解析 Iframe",
 | 
				
			||||||
 | 
					    "messageActions": "消息選項",
 | 
				
			||||||
 | 
					    "viewEmbedLoadHint": "點擊以載入",
 | 
				
			||||||
 | 
					    "levelingStage1": "新手",
 | 
				
			||||||
 | 
					    "levelingStage2": "學徒",
 | 
				
			||||||
 | 
					    "levelingStage3": "學徒工",
 | 
				
			||||||
 | 
					    "levelingStage4": "熟練",
 | 
				
			||||||
 | 
					    "levelingStage5": "專家",
 | 
				
			||||||
 | 
					    "levelingStage6": "大師",
 | 
				
			||||||
 | 
					    "levelingStage7": "宗師",
 | 
				
			||||||
 | 
					    "levelingStage8": "傳說",
 | 
				
			||||||
 | 
					    "levelingStage9": "神話",
 | 
				
			||||||
 | 
					    "levelingStage10": "不朽",
 | 
				
			||||||
 | 
					    "levelingStage11": "神聖",
 | 
				
			||||||
 | 
					    "levelingStage12": "超凡",
 | 
				
			||||||
 | 
					    "uploadAttachment": "上傳附件",
 | 
				
			||||||
 | 
					    "attachmentPreview": "附件預覽",
 | 
				
			||||||
 | 
					    "selectPool": "選擇檔案池",
 | 
				
			||||||
 | 
					    "choosePool": "選擇一個檔案池",
 | 
				
			||||||
 | 
					    "errorLoadingPools": "加載池時出錯",
 | 
				
			||||||
 | 
					    "quotaCostInfo": "這次上傳將消耗 {} 配額點",
 | 
				
			||||||
 | 
					    "uploadConstraints": "上傳限制",
 | 
				
			||||||
 | 
					    "fileSizeExceeded": "檔案大小超過了 {} 的最大限制",
 | 
				
			||||||
 | 
					    "fileTypeNotAccepted": "該文件類型不被此池接受",
 | 
				
			||||||
 | 
					    "files": "附件",
 | 
				
			||||||
 | 
					    "confirmDeleteFile": "你確定要刪除這個文件嗎?",
 | 
				
			||||||
 | 
					    "deleteFile": "刪除文件",
 | 
				
			||||||
 | 
					    "failedToDeleteFile": "刪除文件失敗",
 | 
				
			||||||
 | 
					    "drive": "雲盤",
 | 
				
			||||||
 | 
					    "allPools": "全部的池",
 | 
				
			||||||
 | 
					    "includeRecycled": "包含已回收文件",
 | 
				
			||||||
 | 
					    "confirmDeleteRecycledFiles": "您確定要刪除所有回收的檔案嗎?",
 | 
				
			||||||
 | 
					    "deleteRecycledFiles": "刪除已回收檔案",
 | 
				
			||||||
 | 
					    "recycledFilesDeleted": "已回收檔案刪除成功",
 | 
				
			||||||
 | 
					    "failedToDeleteRecycledFiles": "已回收檔案刪除失敗",
 | 
				
			||||||
 | 
					    "upload": "上傳",
 | 
				
			||||||
 | 
					    "postCompose": "撰寫帖子",
 | 
				
			||||||
 | 
					    "postPublish": "發佈帖子"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								assets/icons/icon-outline.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" fill="none">
 | 
				
			||||||
 | 
					    <path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="12"
 | 
				
			||||||
 | 
					        d="M54 147h86" />
 | 
				
			||||||
 | 
					    <path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="10"
 | 
				
			||||||
 | 
					        d="M57 111s-2-4.5-2-10m22 22s-4 7-11 4m9-22s-2-4.5-2-10" />
 | 
				
			||||||
 | 
					    <path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="12"
 | 
				
			||||||
 | 
					        d="M54 147a32 32 0 0 1-11.999-61.665A39 39 0 0 1 81 46m59 101a30 30 0 0 0 29.933-28" />
 | 
				
			||||||
 | 
					    <circle cx="132" cy="75" r="4" stroke="#fff" stroke-linecap="round" stroke-linejoin="round"
 | 
				
			||||||
 | 
					        stroke-width="8" />
 | 
				
			||||||
 | 
					    <path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="10"
 | 
				
			||||||
 | 
					        d="M112.5 41.217C100.843 47.961 93 60.564 93 75c0 6.375 1.53 12.393 4.242 17.707m69.513-35.419A38.84 38.84 0 0 1 171 75c0 14.433-7.84 27.034-19.493 33.779m-.793-43.317A20.9 20.9 0 0 1 153 75c0 7.77-4.221 14.556-10.495 18.188m-21.003-36.38C115.224 60.44 111 67.226 111 75a20.9 20.9 0 0 0 2.284 9.533" />
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 1.0 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/images/media-offline.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 461 KiB  | 
| 
		 Before Width: | Height: | Size: 307 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/images/oidc/spotify.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 23 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/images/stickers/angry.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.0 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/images/stickers/clap.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.0 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/images/stickers/confuse.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 668 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/images/stickers/party.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/images/stickers/pray.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 666 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/images/stickers/thumb_up.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 623 KiB  | 
@@ -5,3 +5,7 @@ targets:
 | 
				
			|||||||
        options:
 | 
					        options:
 | 
				
			||||||
          explicit_to_json: true
 | 
					          explicit_to_json: true
 | 
				
			||||||
          field_rename: snake
 | 
					          field_rename: snake
 | 
				
			||||||
 | 
					      drift_dev:
 | 
				
			||||||
 | 
					        options:
 | 
				
			||||||
 | 
					          databases:
 | 
				
			||||||
 | 
					            app_database: lib/database/drift_db.dart
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								drift_schemas/app_database/drift_schema_v6.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -21,6 +21,6 @@
 | 
				
			|||||||
  <key>CFBundleVersion</key>
 | 
					  <key>CFBundleVersion</key>
 | 
				
			||||||
  <string>1.0</string>
 | 
					  <string>1.0</string>
 | 
				
			||||||
  <key>MinimumOSVersion</key>
 | 
					  <key>MinimumOSVersion</key>
 | 
				
			||||||
  <string>12.0</string>
 | 
					  <string>13.0</string>
 | 
				
			||||||
</dict>
 | 
					</dict>
 | 
				
			||||||
</plist>
 | 
					</plist>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										15
									
								
								ios/Podfile
									
									
									
									
									
								
							
							
						
						@@ -1,4 +1,3 @@
 | 
				
			|||||||
# Uncomment this line to define a global platform for your project
 | 
					 | 
				
			||||||
platform :ios, '15.0'
 | 
					platform :ios, '15.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
 | 
					# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
 | 
				
			||||||
@@ -32,6 +31,8 @@ target 'Runner' do
 | 
				
			|||||||
  use_modular_headers!
 | 
					  use_modular_headers!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  pod 'Alamofire'
 | 
					  pod 'Alamofire'
 | 
				
			||||||
 | 
					  pod 'Kingfisher', '~> 8.0'
 | 
				
			||||||
 | 
					  pod 'KingfisherWebP'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
 | 
					  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,8 +42,6 @@ target 'Runner' do
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  target 'SolianNotificationService' do
 | 
					  target 'SolianNotificationService' do
 | 
				
			||||||
    inherit! :search_paths
 | 
					    inherit! :search_paths
 | 
				
			||||||
    pod 'Kingfisher', '~> 8.0'
 | 
					 | 
				
			||||||
    pod 'Alamofire'
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  target 'SolianShareExtension' do
 | 
					  target 'SolianShareExtension' do
 | 
				
			||||||
@@ -50,6 +49,16 @@ target 'Runner' do
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					target 'Solian Watch App' do
 | 
				
			||||||
 | 
					  platform :watchos, '11.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  use_frameworks!
 | 
				
			||||||
 | 
					  use_modular_headers!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pod 'Kingfisher', '~> 8.0'
 | 
				
			||||||
 | 
					  pod 'KingfisherWebP'
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
post_install do |installer|
 | 
					post_install do |installer|
 | 
				
			||||||
  installer.pods_project.targets.each do |target|
 | 
					  installer.pods_project.targets.each do |target|
 | 
				
			||||||
    flutter_additional_ios_build_settings(target)
 | 
					    flutter_additional_ios_build_settings(target)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										221
									
								
								ios/Podfile.lock
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,7 @@
 | 
				
			|||||||
PODS:
 | 
					PODS:
 | 
				
			||||||
  - Alamofire (5.10.2)
 | 
					  - Alamofire (5.10.2)
 | 
				
			||||||
 | 
					  - app_links (6.4.1):
 | 
				
			||||||
 | 
					    - Flutter
 | 
				
			||||||
  - connectivity_plus (0.0.1):
 | 
					  - connectivity_plus (0.0.1):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - croppy (0.0.1):
 | 
					  - croppy (0.0.1):
 | 
				
			||||||
@@ -40,83 +42,85 @@ PODS:
 | 
				
			|||||||
  - file_picker (0.0.1):
 | 
					  - file_picker (0.0.1):
 | 
				
			||||||
    - DKImagePickerController/PhotoGallery
 | 
					    - DKImagePickerController/PhotoGallery
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - Firebase/CoreOnly (12.0.0):
 | 
					  - file_saver (0.0.1):
 | 
				
			||||||
    - FirebaseCore (~> 12.0.0)
 | 
					    - Flutter
 | 
				
			||||||
  - Firebase/Crashlytics (12.0.0):
 | 
					  - Firebase/CoreOnly (12.4.0):
 | 
				
			||||||
 | 
					    - FirebaseCore (~> 12.4.0)
 | 
				
			||||||
 | 
					  - Firebase/Crashlytics (12.4.0):
 | 
				
			||||||
    - Firebase/CoreOnly
 | 
					    - Firebase/CoreOnly
 | 
				
			||||||
    - FirebaseCrashlytics (~> 12.0.0)
 | 
					    - FirebaseCrashlytics (~> 12.4.0)
 | 
				
			||||||
  - Firebase/Messaging (12.0.0):
 | 
					  - Firebase/Messaging (12.4.0):
 | 
				
			||||||
    - Firebase/CoreOnly
 | 
					    - Firebase/CoreOnly
 | 
				
			||||||
    - FirebaseMessaging (~> 12.0.0)
 | 
					    - FirebaseMessaging (~> 12.4.0)
 | 
				
			||||||
  - firebase_analytics (12.0.0):
 | 
					  - firebase_analytics (12.0.3):
 | 
				
			||||||
    - firebase_core
 | 
					    - firebase_core
 | 
				
			||||||
    - FirebaseAnalytics (= 12.0.0)
 | 
					    - FirebaseAnalytics (= 12.4.0)
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - firebase_core (4.0.0):
 | 
					  - firebase_core (4.2.0):
 | 
				
			||||||
    - Firebase/CoreOnly (= 12.0.0)
 | 
					    - Firebase/CoreOnly (= 12.4.0)
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - firebase_crashlytics (5.0.0):
 | 
					  - firebase_crashlytics (5.0.3):
 | 
				
			||||||
    - Firebase/Crashlytics (= 12.0.0)
 | 
					    - Firebase/Crashlytics (= 12.4.0)
 | 
				
			||||||
    - firebase_core
 | 
					    - firebase_core
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - firebase_messaging (16.0.0):
 | 
					  - firebase_messaging (16.0.3):
 | 
				
			||||||
    - Firebase/Messaging (= 12.0.0)
 | 
					    - Firebase/Messaging (= 12.4.0)
 | 
				
			||||||
    - firebase_core
 | 
					    - firebase_core
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - FirebaseAnalytics (12.0.0):
 | 
					  - FirebaseAnalytics (12.4.0):
 | 
				
			||||||
    - FirebaseAnalytics/Default (= 12.0.0)
 | 
					    - FirebaseAnalytics/Default (= 12.4.0)
 | 
				
			||||||
    - FirebaseCore (~> 12.0.0)
 | 
					    - FirebaseCore (~> 12.4.0)
 | 
				
			||||||
    - FirebaseInstallations (~> 12.0.0)
 | 
					    - FirebaseInstallations (~> 12.4.0)
 | 
				
			||||||
    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
					    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/MethodSwizzler (~> 8.1)
 | 
					    - GoogleUtilities/MethodSwizzler (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/Network (~> 8.1)
 | 
					    - GoogleUtilities/Network (~> 8.1)
 | 
				
			||||||
    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
					    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
				
			||||||
    - nanopb (~> 3.30910.0)
 | 
					    - nanopb (~> 3.30910.0)
 | 
				
			||||||
  - FirebaseAnalytics/Default (12.0.0):
 | 
					  - FirebaseAnalytics/Default (12.4.0):
 | 
				
			||||||
    - FirebaseCore (~> 12.0.0)
 | 
					    - FirebaseCore (~> 12.4.0)
 | 
				
			||||||
    - FirebaseInstallations (~> 12.0.0)
 | 
					    - FirebaseInstallations (~> 12.4.0)
 | 
				
			||||||
    - GoogleAppMeasurement/Default (= 12.0.0)
 | 
					    - GoogleAppMeasurement/Default (= 12.4.0)
 | 
				
			||||||
    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
					    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/MethodSwizzler (~> 8.1)
 | 
					    - GoogleUtilities/MethodSwizzler (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/Network (~> 8.1)
 | 
					    - GoogleUtilities/Network (~> 8.1)
 | 
				
			||||||
    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
					    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
				
			||||||
    - nanopb (~> 3.30910.0)
 | 
					    - nanopb (~> 3.30910.0)
 | 
				
			||||||
  - FirebaseCore (12.0.0):
 | 
					  - FirebaseCore (12.4.0):
 | 
				
			||||||
    - FirebaseCoreInternal (~> 12.0.0)
 | 
					    - FirebaseCoreInternal (~> 12.4.0)
 | 
				
			||||||
    - GoogleUtilities/Environment (~> 8.1)
 | 
					    - GoogleUtilities/Environment (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/Logger (~> 8.1)
 | 
					    - GoogleUtilities/Logger (~> 8.1)
 | 
				
			||||||
  - FirebaseCoreExtension (12.0.0):
 | 
					  - FirebaseCoreExtension (12.4.0):
 | 
				
			||||||
    - FirebaseCore (~> 12.0.0)
 | 
					    - FirebaseCore (~> 12.4.0)
 | 
				
			||||||
  - FirebaseCoreInternal (12.0.0):
 | 
					  - FirebaseCoreInternal (12.4.0):
 | 
				
			||||||
    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
					    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
				
			||||||
  - FirebaseCrashlytics (12.0.0):
 | 
					  - FirebaseCrashlytics (12.4.0):
 | 
				
			||||||
    - FirebaseCore (~> 12.0.0)
 | 
					    - FirebaseCore (~> 12.4.0)
 | 
				
			||||||
    - FirebaseInstallations (~> 12.0.0)
 | 
					    - FirebaseInstallations (~> 12.4.0)
 | 
				
			||||||
    - FirebaseRemoteConfigInterop (~> 12.0.0)
 | 
					    - FirebaseRemoteConfigInterop (~> 12.4.0)
 | 
				
			||||||
    - FirebaseSessions (~> 12.0.0)
 | 
					    - FirebaseSessions (~> 12.4.0)
 | 
				
			||||||
    - GoogleDataTransport (~> 10.1)
 | 
					    - GoogleDataTransport (~> 10.1)
 | 
				
			||||||
    - GoogleUtilities/Environment (~> 8.1)
 | 
					    - GoogleUtilities/Environment (~> 8.1)
 | 
				
			||||||
    - nanopb (~> 3.30910.0)
 | 
					    - nanopb (~> 3.30910.0)
 | 
				
			||||||
    - PromisesObjC (~> 2.4)
 | 
					    - PromisesObjC (~> 2.4)
 | 
				
			||||||
  - FirebaseInstallations (12.0.0):
 | 
					  - FirebaseInstallations (12.4.0):
 | 
				
			||||||
    - FirebaseCore (~> 12.0.0)
 | 
					    - FirebaseCore (~> 12.4.0)
 | 
				
			||||||
    - GoogleUtilities/Environment (~> 8.1)
 | 
					    - GoogleUtilities/Environment (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/UserDefaults (~> 8.1)
 | 
					    - GoogleUtilities/UserDefaults (~> 8.1)
 | 
				
			||||||
    - PromisesObjC (~> 2.4)
 | 
					    - PromisesObjC (~> 2.4)
 | 
				
			||||||
  - FirebaseMessaging (12.0.0):
 | 
					  - FirebaseMessaging (12.4.0):
 | 
				
			||||||
    - FirebaseCore (~> 12.0.0)
 | 
					    - FirebaseCore (~> 12.4.0)
 | 
				
			||||||
    - FirebaseInstallations (~> 12.0.0)
 | 
					    - FirebaseInstallations (~> 12.4.0)
 | 
				
			||||||
    - GoogleDataTransport (~> 10.1)
 | 
					    - GoogleDataTransport (~> 10.1)
 | 
				
			||||||
    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
					    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/Environment (~> 8.1)
 | 
					    - GoogleUtilities/Environment (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/Reachability (~> 8.1)
 | 
					    - GoogleUtilities/Reachability (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/UserDefaults (~> 8.1)
 | 
					    - GoogleUtilities/UserDefaults (~> 8.1)
 | 
				
			||||||
    - nanopb (~> 3.30910.0)
 | 
					    - nanopb (~> 3.30910.0)
 | 
				
			||||||
  - FirebaseRemoteConfigInterop (12.0.0)
 | 
					  - FirebaseRemoteConfigInterop (12.4.0)
 | 
				
			||||||
  - FirebaseSessions (12.0.0):
 | 
					  - FirebaseSessions (12.4.0):
 | 
				
			||||||
    - FirebaseCore (~> 12.0.0)
 | 
					    - FirebaseCore (~> 12.4.0)
 | 
				
			||||||
    - FirebaseCoreExtension (~> 12.0.0)
 | 
					    - FirebaseCoreExtension (~> 12.4.0)
 | 
				
			||||||
    - FirebaseInstallations (~> 12.0.0)
 | 
					    - FirebaseInstallations (~> 12.4.0)
 | 
				
			||||||
    - GoogleDataTransport (~> 10.1)
 | 
					    - GoogleDataTransport (~> 10.1)
 | 
				
			||||||
    - GoogleUtilities/Environment (~> 8.1)
 | 
					    - GoogleUtilities/Environment (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/UserDefaults (~> 8.1)
 | 
					    - GoogleUtilities/UserDefaults (~> 8.1)
 | 
				
			||||||
@@ -134,6 +138,8 @@ PODS:
 | 
				
			|||||||
    - OrderedSet (~> 6.0.3)
 | 
					    - OrderedSet (~> 6.0.3)
 | 
				
			||||||
  - flutter_keyboard_visibility (0.0.1):
 | 
					  - flutter_keyboard_visibility (0.0.1):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
 | 
					  - flutter_local_notifications (0.0.1):
 | 
				
			||||||
 | 
					    - Flutter
 | 
				
			||||||
  - flutter_native_splash (2.4.3):
 | 
					  - flutter_native_splash (2.4.3):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - flutter_platform_alert (0.0.1):
 | 
					  - flutter_platform_alert (0.0.1):
 | 
				
			||||||
@@ -145,33 +151,34 @@ PODS:
 | 
				
			|||||||
  - flutter_udid (0.0.1):
 | 
					  - flutter_udid (0.0.1):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
    - SAMKeychain
 | 
					    - SAMKeychain
 | 
				
			||||||
  - flutter_webrtc (1.0.0):
 | 
					  - flutter_webrtc (1.2.0):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
    - WebRTC-SDK (= 137.7151.02)
 | 
					    - WebRTC-SDK (= 137.7151.04)
 | 
				
			||||||
  - gal (1.0.0):
 | 
					  - gal (1.0.0):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
    - FlutterMacOS
 | 
					    - FlutterMacOS
 | 
				
			||||||
  - GoogleAdsOnDeviceConversion (2.1.0):
 | 
					  - GoogleAdsOnDeviceConversion (3.1.0):
 | 
				
			||||||
 | 
					    - GoogleUtilities/Environment (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/Logger (~> 8.1)
 | 
					    - GoogleUtilities/Logger (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/Network (~> 8.1)
 | 
					    - GoogleUtilities/Network (~> 8.1)
 | 
				
			||||||
    - nanopb (~> 3.30910.0)
 | 
					    - nanopb (~> 3.30910.0)
 | 
				
			||||||
  - GoogleAppMeasurement/Core (12.0.0):
 | 
					  - GoogleAppMeasurement/Core (12.4.0):
 | 
				
			||||||
    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
					    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/MethodSwizzler (~> 8.1)
 | 
					    - GoogleUtilities/MethodSwizzler (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/Network (~> 8.1)
 | 
					    - GoogleUtilities/Network (~> 8.1)
 | 
				
			||||||
    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
					    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
				
			||||||
    - nanopb (~> 3.30910.0)
 | 
					    - nanopb (~> 3.30910.0)
 | 
				
			||||||
  - GoogleAppMeasurement/Default (12.0.0):
 | 
					  - GoogleAppMeasurement/Default (12.4.0):
 | 
				
			||||||
    - GoogleAdsOnDeviceConversion (= 2.1.0)
 | 
					    - GoogleAdsOnDeviceConversion (~> 3.1.0)
 | 
				
			||||||
    - GoogleAppMeasurement/Core (= 12.0.0)
 | 
					    - GoogleAppMeasurement/Core (= 12.4.0)
 | 
				
			||||||
    - GoogleAppMeasurement/IdentitySupport (= 12.0.0)
 | 
					    - GoogleAppMeasurement/IdentitySupport (= 12.4.0)
 | 
				
			||||||
    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
					    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/MethodSwizzler (~> 8.1)
 | 
					    - GoogleUtilities/MethodSwizzler (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/Network (~> 8.1)
 | 
					    - GoogleUtilities/Network (~> 8.1)
 | 
				
			||||||
    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
					    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
				
			||||||
    - nanopb (~> 3.30910.0)
 | 
					    - nanopb (~> 3.30910.0)
 | 
				
			||||||
  - GoogleAppMeasurement/IdentitySupport (12.0.0):
 | 
					  - GoogleAppMeasurement/IdentitySupport (12.4.0):
 | 
				
			||||||
    - GoogleAppMeasurement/Core (= 12.0.0)
 | 
					    - GoogleAppMeasurement/Core (= 12.4.0)
 | 
				
			||||||
    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
					    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/MethodSwizzler (~> 8.1)
 | 
					    - GoogleUtilities/MethodSwizzler (~> 8.1)
 | 
				
			||||||
    - GoogleUtilities/Network (~> 8.1)
 | 
					    - GoogleUtilities/Network (~> 8.1)
 | 
				
			||||||
@@ -211,11 +218,26 @@ PODS:
 | 
				
			|||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - irondash_engine_context (0.0.1):
 | 
					  - irondash_engine_context (0.0.1):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - Kingfisher (8.5.0)
 | 
					  - Kingfisher (8.6.1)
 | 
				
			||||||
  - livekit_client (2.5.0):
 | 
					  - KingfisherWebP (1.7.2):
 | 
				
			||||||
 | 
					    - Kingfisher (~> 8.0)
 | 
				
			||||||
 | 
					    - libwebp (>= 1.1.0)
 | 
				
			||||||
 | 
					  - libwebp (1.5.0):
 | 
				
			||||||
 | 
					    - libwebp/demux (= 1.5.0)
 | 
				
			||||||
 | 
					    - libwebp/mux (= 1.5.0)
 | 
				
			||||||
 | 
					    - libwebp/sharpyuv (= 1.5.0)
 | 
				
			||||||
 | 
					    - libwebp/webp (= 1.5.0)
 | 
				
			||||||
 | 
					  - libwebp/demux (1.5.0):
 | 
				
			||||||
 | 
					    - libwebp/webp
 | 
				
			||||||
 | 
					  - libwebp/mux (1.5.0):
 | 
				
			||||||
 | 
					    - libwebp/demux
 | 
				
			||||||
 | 
					  - libwebp/sharpyuv (1.5.0)
 | 
				
			||||||
 | 
					  - libwebp/webp (1.5.0):
 | 
				
			||||||
 | 
					    - libwebp/sharpyuv
 | 
				
			||||||
 | 
					  - livekit_client (2.5.3):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
    - flutter_webrtc
 | 
					    - flutter_webrtc
 | 
				
			||||||
    - WebRTC-SDK (= 137.7151.02)
 | 
					    - WebRTC-SDK (= 137.7151.04)
 | 
				
			||||||
  - local_auth_darwin (0.0.1):
 | 
					  - local_auth_darwin (0.0.1):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
    - FlutterMacOS
 | 
					    - FlutterMacOS
 | 
				
			||||||
@@ -248,9 +270,9 @@ PODS:
 | 
				
			|||||||
  - record_ios (1.1.0):
 | 
					  - record_ios (1.1.0):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - SAMKeychain (1.5.3)
 | 
					  - SAMKeychain (1.5.3)
 | 
				
			||||||
  - SDWebImage (5.21.1):
 | 
					  - SDWebImage (5.21.3):
 | 
				
			||||||
    - SDWebImage/Core (= 5.21.1)
 | 
					    - SDWebImage/Core (= 5.21.3)
 | 
				
			||||||
  - SDWebImage/Core (5.21.1)
 | 
					  - SDWebImage/Core (5.21.3)
 | 
				
			||||||
  - share_plus (0.0.1):
 | 
					  - share_plus (0.0.1):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - shared_preferences_foundation (0.0.1):
 | 
					  - shared_preferences_foundation (0.0.1):
 | 
				
			||||||
@@ -289,20 +311,24 @@ PODS:
 | 
				
			|||||||
  - super_native_extensions (0.0.1):
 | 
					  - super_native_extensions (0.0.1):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - SwiftyGif (5.4.5)
 | 
					  - SwiftyGif (5.4.5)
 | 
				
			||||||
 | 
					  - syncfusion_flutter_pdfviewer (0.0.1):
 | 
				
			||||||
 | 
					    - Flutter
 | 
				
			||||||
  - url_launcher_ios (0.0.1):
 | 
					  - url_launcher_ios (0.0.1):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - volume_controller (0.0.1):
 | 
					  - volume_controller (0.0.1):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - wakelock_plus (0.0.1):
 | 
					  - wakelock_plus (0.0.1):
 | 
				
			||||||
    - Flutter
 | 
					    - Flutter
 | 
				
			||||||
  - WebRTC-SDK (137.7151.02)
 | 
					  - WebRTC-SDK (137.7151.04)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES:
 | 
					DEPENDENCIES:
 | 
				
			||||||
  - Alamofire
 | 
					  - Alamofire
 | 
				
			||||||
 | 
					  - app_links (from `.symlinks/plugins/app_links/ios`)
 | 
				
			||||||
  - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
 | 
					  - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
 | 
				
			||||||
  - croppy (from `.symlinks/plugins/croppy/ios`)
 | 
					  - croppy (from `.symlinks/plugins/croppy/ios`)
 | 
				
			||||||
  - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
 | 
					  - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
 | 
				
			||||||
  - file_picker (from `.symlinks/plugins/file_picker/ios`)
 | 
					  - file_picker (from `.symlinks/plugins/file_picker/ios`)
 | 
				
			||||||
 | 
					  - file_saver (from `.symlinks/plugins/file_saver/ios`)
 | 
				
			||||||
  - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
 | 
					  - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
 | 
				
			||||||
  - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
 | 
					  - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
 | 
				
			||||||
  - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
 | 
					  - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
 | 
				
			||||||
@@ -311,6 +337,7 @@ DEPENDENCIES:
 | 
				
			|||||||
  - flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
 | 
					  - flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
 | 
				
			||||||
  - flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
 | 
					  - flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
 | 
				
			||||||
  - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
 | 
					  - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
 | 
				
			||||||
 | 
					  - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
 | 
				
			||||||
  - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
 | 
					  - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
 | 
				
			||||||
  - flutter_platform_alert (from `.symlinks/plugins/flutter_platform_alert/ios`)
 | 
					  - flutter_platform_alert (from `.symlinks/plugins/flutter_platform_alert/ios`)
 | 
				
			||||||
  - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
 | 
					  - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
 | 
				
			||||||
@@ -321,6 +348,7 @@ DEPENDENCIES:
 | 
				
			|||||||
  - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
 | 
					  - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
 | 
				
			||||||
  - irondash_engine_context (from `.symlinks/plugins/irondash_engine_context/ios`)
 | 
					  - irondash_engine_context (from `.symlinks/plugins/irondash_engine_context/ios`)
 | 
				
			||||||
  - Kingfisher (~> 8.0)
 | 
					  - Kingfisher (~> 8.0)
 | 
				
			||||||
 | 
					  - KingfisherWebP
 | 
				
			||||||
  - livekit_client (from `.symlinks/plugins/livekit_client/ios`)
 | 
					  - livekit_client (from `.symlinks/plugins/livekit_client/ios`)
 | 
				
			||||||
  - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
 | 
					  - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
 | 
				
			||||||
  - media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
 | 
					  - media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
 | 
				
			||||||
@@ -338,6 +366,7 @@ DEPENDENCIES:
 | 
				
			|||||||
  - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
 | 
					  - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
 | 
				
			||||||
  - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
 | 
					  - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
 | 
				
			||||||
  - super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`)
 | 
					  - super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`)
 | 
				
			||||||
 | 
					  - syncfusion_flutter_pdfviewer (from `.symlinks/plugins/syncfusion_flutter_pdfviewer/ios`)
 | 
				
			||||||
  - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
 | 
					  - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
 | 
				
			||||||
  - volume_controller (from `.symlinks/plugins/volume_controller/ios`)
 | 
					  - volume_controller (from `.symlinks/plugins/volume_controller/ios`)
 | 
				
			||||||
  - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
 | 
					  - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
 | 
				
			||||||
@@ -362,6 +391,8 @@ SPEC REPOS:
 | 
				
			|||||||
    - GoogleDataTransport
 | 
					    - GoogleDataTransport
 | 
				
			||||||
    - GoogleUtilities
 | 
					    - GoogleUtilities
 | 
				
			||||||
    - Kingfisher
 | 
					    - Kingfisher
 | 
				
			||||||
 | 
					    - KingfisherWebP
 | 
				
			||||||
 | 
					    - libwebp
 | 
				
			||||||
    - nanopb
 | 
					    - nanopb
 | 
				
			||||||
    - OrderedSet
 | 
					    - OrderedSet
 | 
				
			||||||
    - PromisesObjC
 | 
					    - PromisesObjC
 | 
				
			||||||
@@ -373,6 +404,8 @@ SPEC REPOS:
 | 
				
			|||||||
    - WebRTC-SDK
 | 
					    - WebRTC-SDK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXTERNAL SOURCES:
 | 
					EXTERNAL SOURCES:
 | 
				
			||||||
 | 
					  app_links:
 | 
				
			||||||
 | 
					    :path: ".symlinks/plugins/app_links/ios"
 | 
				
			||||||
  connectivity_plus:
 | 
					  connectivity_plus:
 | 
				
			||||||
    :path: ".symlinks/plugins/connectivity_plus/ios"
 | 
					    :path: ".symlinks/plugins/connectivity_plus/ios"
 | 
				
			||||||
  croppy:
 | 
					  croppy:
 | 
				
			||||||
@@ -381,6 +414,8 @@ EXTERNAL SOURCES:
 | 
				
			|||||||
    :path: ".symlinks/plugins/device_info_plus/ios"
 | 
					    :path: ".symlinks/plugins/device_info_plus/ios"
 | 
				
			||||||
  file_picker:
 | 
					  file_picker:
 | 
				
			||||||
    :path: ".symlinks/plugins/file_picker/ios"
 | 
					    :path: ".symlinks/plugins/file_picker/ios"
 | 
				
			||||||
 | 
					  file_saver:
 | 
				
			||||||
 | 
					    :path: ".symlinks/plugins/file_saver/ios"
 | 
				
			||||||
  firebase_analytics:
 | 
					  firebase_analytics:
 | 
				
			||||||
    :path: ".symlinks/plugins/firebase_analytics/ios"
 | 
					    :path: ".symlinks/plugins/firebase_analytics/ios"
 | 
				
			||||||
  firebase_core:
 | 
					  firebase_core:
 | 
				
			||||||
@@ -397,6 +432,8 @@ EXTERNAL SOURCES:
 | 
				
			|||||||
    :path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
 | 
					    :path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
 | 
				
			||||||
  flutter_keyboard_visibility:
 | 
					  flutter_keyboard_visibility:
 | 
				
			||||||
    :path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
 | 
					    :path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
 | 
				
			||||||
 | 
					  flutter_local_notifications:
 | 
				
			||||||
 | 
					    :path: ".symlinks/plugins/flutter_local_notifications/ios"
 | 
				
			||||||
  flutter_native_splash:
 | 
					  flutter_native_splash:
 | 
				
			||||||
    :path: ".symlinks/plugins/flutter_native_splash/ios"
 | 
					    :path: ".symlinks/plugins/flutter_native_splash/ios"
 | 
				
			||||||
  flutter_platform_alert:
 | 
					  flutter_platform_alert:
 | 
				
			||||||
@@ -449,6 +486,8 @@ EXTERNAL SOURCES:
 | 
				
			|||||||
    :path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
 | 
					    :path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
 | 
				
			||||||
  super_native_extensions:
 | 
					  super_native_extensions:
 | 
				
			||||||
    :path: ".symlinks/plugins/super_native_extensions/ios"
 | 
					    :path: ".symlinks/plugins/super_native_extensions/ios"
 | 
				
			||||||
 | 
					  syncfusion_flutter_pdfviewer:
 | 
				
			||||||
 | 
					    :path: ".symlinks/plugins/syncfusion_flutter_pdfviewer/ios"
 | 
				
			||||||
  url_launcher_ios:
 | 
					  url_launcher_ios:
 | 
				
			||||||
    :path: ".symlinks/plugins/url_launcher_ios/ios"
 | 
					    :path: ".symlinks/plugins/url_launcher_ios/ios"
 | 
				
			||||||
  volume_controller:
 | 
					  volume_controller:
 | 
				
			||||||
@@ -458,46 +497,51 @@ EXTERNAL SOURCES:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
SPEC CHECKSUMS:
 | 
					SPEC CHECKSUMS:
 | 
				
			||||||
  Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
 | 
					  Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
 | 
				
			||||||
 | 
					  app_links: 3dbc685f76b1693c66a6d9dd1e9ab6f73d97dc0a
 | 
				
			||||||
  connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
 | 
					  connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
 | 
				
			||||||
  croppy: 979e8ddc254f4642bffe7d52dc7193354b27ba30
 | 
					  croppy: 979e8ddc254f4642bffe7d52dc7193354b27ba30
 | 
				
			||||||
  device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
 | 
					  device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
 | 
				
			||||||
  DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
 | 
					  DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
 | 
				
			||||||
  DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
 | 
					  DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
 | 
				
			||||||
  file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
 | 
					  file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
 | 
				
			||||||
  Firebase: 800d487043c0557d9faed71477a38d9aafb08a41
 | 
					  file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
 | 
				
			||||||
  firebase_analytics: cd56fc56f75c1df30a6ff5290cd56e230996a76d
 | 
					  Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e
 | 
				
			||||||
  firebase_core: 633e1851ffe1b9ab875f6467a4f574c79cef02e4
 | 
					  firebase_analytics: 1d024068b1d4707d5ba7a42a12976ddf3316d835
 | 
				
			||||||
  firebase_crashlytics: 2c6c1a17900a38081d938330e9f48e60ec5b255d
 | 
					  firebase_core: 744984dbbed8b3036abf34f0b98d80f130a7e464
 | 
				
			||||||
  firebase_messaging: d17feef781edc84ebefe62624fb384358ad96361
 | 
					  firebase_crashlytics: f3a9a4338ab99b67042f64e9e22e1bf349cb44ed
 | 
				
			||||||
  FirebaseAnalytics: 6d790cd1b159b4eb61a99948df0934ce505a34f7
 | 
					  firebase_messaging: 82c70650c426a0a14873e1acdb9ec2b443c4e8b4
 | 
				
			||||||
  FirebaseCore: 055f4ab117d5964158c833f3d5e7ec6d91648d4a
 | 
					  FirebaseAnalytics: 0fc2b20091f0ddd21bf73397cf8f0eb5346dc24f
 | 
				
			||||||
  FirebaseCoreExtension: 639afb3de6abd611952be78a794c54a47fa0f361
 | 
					  FirebaseCore: bb595f3114953664e3c1dc032f008a244147cfd3
 | 
				
			||||||
  FirebaseCoreInternal: dedc28e569a4be85f38f3d6af1070a2e12018d55
 | 
					  FirebaseCoreExtension: 7e1f7118ee970e001a8013719fb90950ee5e0018
 | 
				
			||||||
  FirebaseCrashlytics: db75aa0cab8d00f68406fa247c32fe17ade884d7
 | 
					  FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6
 | 
				
			||||||
  FirebaseInstallations: d4c7c958f99c8860d7fcece786314ae790e2f988
 | 
					  FirebaseCrashlytics: a6ece278a837c7e88de2d9b5da0a3542f2342395
 | 
				
			||||||
  FirebaseMessaging: af49f8d7c0a3d2a017d9302c80946f45a7777dde
 | 
					  FirebaseInstallations: ae9f4902cb5bf1d0c5eaa31ec1f4e5495a0714e2
 | 
				
			||||||
  FirebaseRemoteConfigInterop: bfa0ea72ba3dc5af739777296424e46bd6f42613
 | 
					  FirebaseMessaging: d33971b7bb252745ea6cd31ab190d1a1df4b8ed5
 | 
				
			||||||
  FirebaseSessions: 4e784acda213108aafef536535cdfc03504acc42
 | 
					  FirebaseRemoteConfigInterop: 1e31ec72b89c9924367c59bfb5ec9ab60d1d6766
 | 
				
			||||||
  Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
 | 
					  FirebaseSessions: ba7c7a7ca8696a8d540eb3fe3800fbe98c79786d
 | 
				
			||||||
 | 
					  Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
 | 
				
			||||||
  flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
 | 
					  flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
 | 
				
			||||||
  flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
 | 
					  flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
 | 
				
			||||||
  flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
 | 
					  flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
 | 
				
			||||||
 | 
					  flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
 | 
				
			||||||
  flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
 | 
					  flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
 | 
				
			||||||
  flutter_platform_alert: bf3b5fcd4ac14bd637e20527e9c471633071afd3
 | 
					  flutter_platform_alert: bf3b5fcd4ac14bd637e20527e9c471633071afd3
 | 
				
			||||||
  flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
 | 
					  flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
 | 
				
			||||||
  flutter_timezone: 7c838e17ffd4645d261e87037e5bebf6d38fe544
 | 
					  flutter_timezone: 7c838e17ffd4645d261e87037e5bebf6d38fe544
 | 
				
			||||||
  flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
 | 
					  flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
 | 
				
			||||||
  flutter_webrtc: 6f7da106613d52ade777d5b4875a43f48c28b457
 | 
					  flutter_webrtc: c3e21fc0dcd9d8eb246ae4d5256fcbeb2f5ecd22
 | 
				
			||||||
  gal: baecd024ebfd13c441269ca7404792a7152fde89
 | 
					  gal: baecd024ebfd13c441269ca7404792a7152fde89
 | 
				
			||||||
  GoogleAdsOnDeviceConversion: 2be6297a4f048459e0ae17fad9bfd2844e10cf64
 | 
					  GoogleAdsOnDeviceConversion: e03a386840803ea7eef3fd22a061930142c039c1
 | 
				
			||||||
  GoogleAppMeasurement: 8f6ab04ad6ae493b53fcf56bd26323fb2f1384f3
 | 
					  GoogleAppMeasurement: 1e718274b7e015cefd846ac1fcf7820c70dc017d
 | 
				
			||||||
  GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
 | 
					  GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
 | 
				
			||||||
  GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
 | 
					  GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
 | 
				
			||||||
  image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
 | 
					  image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
 | 
				
			||||||
  irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486
 | 
					  irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486
 | 
				
			||||||
  Kingfisher: ff0d31a1f07bdff6a1ebb3ba08b8e6e567b6500c
 | 
					  Kingfisher: 7ac7a7288653787a54206b11a3c74f49ab650f1f
 | 
				
			||||||
  livekit_client: e3b79b99405428aac439b6b76a254cd9a11dbbfb
 | 
					  KingfisherWebP: 38b9721821947f547afb78f933f75f4f9e0ae402
 | 
				
			||||||
  local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19
 | 
					  libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
 | 
				
			||||||
 | 
					  livekit_client: 86c8af579274e4b7a215185a8080db2d4e176f40
 | 
				
			||||||
 | 
					  local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb
 | 
				
			||||||
  media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
 | 
					  media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
 | 
				
			||||||
  media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
 | 
					  media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
 | 
				
			||||||
  nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
 | 
					  nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
 | 
				
			||||||
@@ -505,27 +549,28 @@ SPEC CHECKSUMS:
 | 
				
			|||||||
  OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
 | 
					  OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
 | 
				
			||||||
  package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
 | 
					  package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
 | 
				
			||||||
  pasteboard: 49088aeb6119d51f976a421db60d8e1ab079b63c
 | 
					  pasteboard: 49088aeb6119d51f976a421db60d8e1ab079b63c
 | 
				
			||||||
  path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
 | 
					  path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
 | 
				
			||||||
  pointer_interceptor_ios: ec847ef8b0915778bed2b2cef636f4d177fa8eed
 | 
					  pointer_interceptor_ios: da06a662d5bfd329602b45b2ab41bc0fb5fdb0f0
 | 
				
			||||||
  PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
 | 
					  PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
 | 
				
			||||||
  PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
 | 
					  PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
 | 
				
			||||||
  receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
 | 
					  receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
 | 
				
			||||||
  record_ios: f75fa1d57f840012775c0e93a38a7f3ceea1a374
 | 
					  record_ios: f75fa1d57f840012775c0e93a38a7f3ceea1a374
 | 
				
			||||||
  SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
 | 
					  SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
 | 
				
			||||||
  SDWebImage: f29024626962457f3470184232766516dee8dfea
 | 
					  SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a
 | 
				
			||||||
  share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
 | 
					  share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
 | 
				
			||||||
  shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
 | 
					  shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
 | 
				
			||||||
  sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
 | 
					  sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
 | 
				
			||||||
  sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
 | 
					  sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
 | 
				
			||||||
  sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b
 | 
					  sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b
 | 
				
			||||||
  sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1
 | 
					  sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1
 | 
				
			||||||
  super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4
 | 
					  super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4
 | 
				
			||||||
  SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
 | 
					  SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
 | 
				
			||||||
  url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
 | 
					  syncfusion_flutter_pdfviewer: 90dc48305d2e33d4aa20681d1e98ddeda891bc14
 | 
				
			||||||
 | 
					  url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
 | 
				
			||||||
  volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12
 | 
					  volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12
 | 
				
			||||||
  wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
 | 
					  wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
 | 
				
			||||||
  WebRTC-SDK: d20de357dcbf7c9696b124b39f3ff62125107e4b
 | 
					  WebRTC-SDK: 40d4f5ba05cadff14e4db5614aec402a633f007e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PODFILE CHECKSUM: c818292390b02fa379036ea099713a332bd7193f
 | 
					PODFILE CHECKSUM: 585198f58dca90ac6492607c83a8d17045ab3852
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COCOAPODS: 1.16.2
 | 
					COCOAPODS: 1.16.2
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,8 @@
 | 
				
			|||||||
		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
 | 
							1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
 | 
				
			||||||
		331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
 | 
							331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
 | 
				
			||||||
		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
 | 
							3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
 | 
				
			||||||
 | 
							5D8143680678FCD1D1827271 /* Pods_Solian_Watch_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9C046CF867AE03DC170F861 /* Pods_Solian_Watch_App.framework */; };
 | 
				
			||||||
 | 
							7310A7DF2EB10963002C0FD3 /* Solian Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 7310A7D42EB10962002C0FD3 /* Solian Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 | 
				
			||||||
		73ACDFAD2E3D0E6100B63535 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73ACDFAC2E3D0E6100B63535 /* ReplayKit.framework */; };
 | 
							73ACDFAD2E3D0E6100B63535 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73ACDFAC2E3D0E6100B63535 /* ReplayKit.framework */; };
 | 
				
			||||||
		73ACDFC32E3D0E6100B63535 /* SolianBroadcastExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73ACDFAB2E3D0E6100B63535 /* SolianBroadcastExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 | 
							73ACDFC32E3D0E6100B63535 /* SolianBroadcastExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73ACDFAB2E3D0E6100B63535 /* SolianBroadcastExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 | 
				
			||||||
		73C305D82E0BE878009035B9 /* SolianShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73C305CE2E0BE878009035B9 /* SolianShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 | 
							73C305D82E0BE878009035B9 /* SolianShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73C305CE2E0BE878009035B9 /* SolianShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 | 
				
			||||||
@@ -58,6 +60,17 @@
 | 
				
			|||||||
/* End PBXContainerItemProxy section */
 | 
					/* End PBXContainerItemProxy section */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Begin PBXCopyFilesBuildPhase section */
 | 
					/* Begin PBXCopyFilesBuildPhase section */
 | 
				
			||||||
 | 
							7310A7DE2EB10963002C0FD3 /* Embed Watch Content */ = {
 | 
				
			||||||
 | 
								isa = PBXCopyFilesBuildPhase;
 | 
				
			||||||
 | 
								buildActionMask = 12;
 | 
				
			||||||
 | 
								dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
 | 
				
			||||||
 | 
								dstSubfolderSpec = 16;
 | 
				
			||||||
 | 
								files = (
 | 
				
			||||||
 | 
									7310A7DF2EB10963002C0FD3 /* Solian Watch App.app in Embed Watch Content */,
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								name = "Embed Watch Content";
 | 
				
			||||||
 | 
								runOnlyForDeploymentPostprocessing = 0;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
		73268D1D2DEAFD670076E970 /* Embed Foundation Extensions */ = {
 | 
							73268D1D2DEAFD670076E970 /* Embed Foundation Extensions */ = {
 | 
				
			||||||
			isa = PBXCopyFilesBuildPhase;
 | 
								isa = PBXCopyFilesBuildPhase;
 | 
				
			||||||
			buildActionMask = 2147483647;
 | 
								buildActionMask = 2147483647;
 | 
				
			||||||
@@ -84,6 +97,8 @@
 | 
				
			|||||||
/* End PBXCopyFilesBuildPhase section */
 | 
					/* End PBXCopyFilesBuildPhase section */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Begin PBXFileReference section */
 | 
					/* Begin PBXFileReference section */
 | 
				
			||||||
 | 
							0ECC3D56D018DD87FC342699 /* Pods-Solian Watch App.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Solian Watch App.profile.xcconfig"; path = "Target Support Files/Pods-Solian Watch App/Pods-Solian Watch App.profile.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
 | 
							103EA2362B9E9F127016A1F1 /* Pods-WatchRunner Watch App.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WatchRunner Watch App.profile.xcconfig"; path = "Target Support Files/Pods-WatchRunner Watch App/Pods-WatchRunner Watch App.profile.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		14118AC858B441AB16B7309E /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
 | 
							14118AC858B441AB16B7309E /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
 | 
							1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
 | 
				
			||||||
		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
 | 
							1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
 | 
				
			||||||
@@ -91,15 +106,18 @@
 | 
				
			|||||||
		17FAB080A9C53193ABD9C15B /* Pods-SolianShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-SolianShareExtension/Pods-SolianShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
 | 
							17FAB080A9C53193ABD9C15B /* Pods-SolianShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-SolianShareExtension/Pods-SolianShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		192FDACE67D7CB6AED15C634 /* Pods-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.debug.xcconfig"; sourceTree = "<group>"; };
 | 
							192FDACE67D7CB6AED15C634 /* Pods-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.debug.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		1C14F71D23E4371602065522 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
 | 
							1C14F71D23E4371602065522 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
 | 
							2440CEDEAAD6D51FDA95FA62 /* Pods-Solian Watch App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Solian Watch App.release.xcconfig"; path = "Target Support Files/Pods-Solian Watch App/Pods-Solian Watch App.release.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		252A83CE6862573BB856ED8E /* Pods-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.release.xcconfig"; sourceTree = "<group>"; };
 | 
							252A83CE6862573BB856ED8E /* Pods-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.release.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		27C66EFB5A705F1A822C3EB0 /* Pods-SolianShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianShareExtension.release.xcconfig"; path = "Target Support Files/Pods-SolianShareExtension/Pods-SolianShareExtension.release.xcconfig"; sourceTree = "<group>"; };
 | 
							27C66EFB5A705F1A822C3EB0 /* Pods-SolianShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianShareExtension.release.xcconfig"; path = "Target Support Files/Pods-SolianShareExtension/Pods-SolianShareExtension.release.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		29812C17FFBE7DBBC7203981 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
							29812C17FFBE7DBBC7203981 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
		2D2457F8B2E6EF9C0F935035 /* Pods-NotificationService.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.profile.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.profile.xcconfig"; sourceTree = "<group>"; };
 | 
							2D2457F8B2E6EF9C0F935035 /* Pods-NotificationService.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.profile.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.profile.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
 | 
							31EA49B10397BD4145AD765E /* Pods-Solian Watch App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Solian Watch App.debug.xcconfig"; path = "Target Support Files/Pods-Solian Watch App/Pods-Solian Watch App.debug.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
 | 
							331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
							331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
		39FE4CC6223F0D3C0E1FFD04 /* Pods_SolianNotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SolianNotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
							39FE4CC6223F0D3C0E1FFD04 /* Pods_SolianNotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SolianNotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
		3A1C47BD29CC6AC2587D4DBE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
 | 
							3A1C47BD29CC6AC2587D4DBE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
 | 
							3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
 | 
				
			||||||
 | 
							7310A7D42EB10962002C0FD3 /* Solian Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Solian Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
		737E920B2DB6A9FF00BE9CDB /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
 | 
							737E920B2DB6A9FF00BE9CDB /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
 | 
				
			||||||
		73ACDFAB2E3D0E6100B63535 /* SolianBroadcastExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolianBroadcastExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
							73ACDFAB2E3D0E6100B63535 /* SolianBroadcastExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolianBroadcastExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
		73ACDFAC2E3D0E6100B63535 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
 | 
							73ACDFAC2E3D0E6100B63535 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
@@ -111,6 +129,7 @@
 | 
				
			|||||||
		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 | 
							74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
 | 
							7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
 | 
				
			||||||
		7B40764A2C4CC0E7DC70A0D3 /* Pods_SolianShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SolianShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
							7B40764A2C4CC0E7DC70A0D3 /* Pods_SolianShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SolianShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
 | 
							86D60BA96DA647E1B11AA7F0 /* Pods-WatchRunner Watch App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WatchRunner Watch App.debug.xcconfig"; path = "Target Support Files/Pods-WatchRunner Watch App/Pods-WatchRunner Watch App.debug.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		8B40620B1EEBB09456406A3C /* Pods-SolianNotificationService.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianNotificationService.profile.xcconfig"; path = "Target Support Files/Pods-SolianNotificationService/Pods-SolianNotificationService.profile.xcconfig"; sourceTree = "<group>"; };
 | 
							8B40620B1EEBB09456406A3C /* Pods-SolianNotificationService.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianNotificationService.profile.xcconfig"; path = "Target Support Files/Pods-SolianNotificationService/Pods-SolianNotificationService.profile.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
 | 
							9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
 | 
				
			||||||
		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
 | 
							9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
 | 
				
			||||||
@@ -120,10 +139,12 @@
 | 
				
			|||||||
		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 | 
							97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 | 
				
			||||||
		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 | 
							97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 | 
				
			||||||
		9AE244813FCDFAA941430393 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
 | 
							9AE244813FCDFAA941430393 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
 | 
				
			||||||
 | 
							A2EB1DAFDE9B8E6D88BBF7A3 /* Pods-WatchRunner Watch App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WatchRunner Watch App.release.xcconfig"; path = "Target Support Files/Pods-WatchRunner Watch App/Pods-WatchRunner Watch App.release.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		A499FDB2082EB000933AA8C5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
 | 
							A499FDB2082EB000933AA8C5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		A85FF612AE7623A9934E57CE /* Pods-SolianShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-SolianShareExtension/Pods-SolianShareExtension.profile.xcconfig"; sourceTree = "<group>"; };
 | 
							A85FF612AE7623A9934E57CE /* Pods-SolianShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-SolianShareExtension/Pods-SolianShareExtension.profile.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		AA0CA8A3E15DEE023BB27438 /* Pods_NotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
							AA0CA8A3E15DEE023BB27438 /* Pods_NotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
		B93771F2A63E4148DC6142F7 /* Pods-SolianNotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianNotificationService.release.xcconfig"; path = "Target Support Files/Pods-SolianNotificationService/Pods-SolianNotificationService.release.xcconfig"; sourceTree = "<group>"; };
 | 
							B93771F2A63E4148DC6142F7 /* Pods-SolianNotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianNotificationService.release.xcconfig"; path = "Target Support Files/Pods-SolianNotificationService/Pods-SolianNotificationService.release.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
 | 
							C9C046CF867AE03DC170F861 /* Pods_Solian_Watch_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Solian_Watch_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
		E6B10A9A85BECA2E576C91FF /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
 | 
							E6B10A9A85BECA2E576C91FF /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
		F6D834CA86410B09796B312B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
							F6D834CA86410B09796B312B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
		F830F535CB92E3F2E1653A11 /* Pods-SolianNotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianNotificationService.debug.xcconfig"; path = "Target Support Files/Pods-SolianNotificationService/Pods-SolianNotificationService.debug.xcconfig"; sourceTree = "<group>"; };
 | 
							F830F535CB92E3F2E1653A11 /* Pods-SolianNotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SolianNotificationService.debug.xcconfig"; path = "Target Support Files/Pods-SolianNotificationService/Pods-SolianNotificationService.debug.xcconfig"; sourceTree = "<group>"; };
 | 
				
			||||||
@@ -162,6 +183,13 @@
 | 
				
			|||||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
 | 
					/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
 | 
					/* Begin PBXFileSystemSynchronizedRootGroup section */
 | 
				
			||||||
 | 
							7310A7D52EB10962002C0FD3 /* Solian Watch App */ = {
 | 
				
			||||||
 | 
								isa = PBXFileSystemSynchronizedRootGroup;
 | 
				
			||||||
 | 
								exceptions = (
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								path = "Solian Watch App";
 | 
				
			||||||
 | 
								sourceTree = "<group>";
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
		73268D272DEB012A0076E970 /* Services */ = {
 | 
							73268D272DEB012A0076E970 /* Services */ = {
 | 
				
			||||||
			isa = PBXFileSystemSynchronizedRootGroup;
 | 
								isa = PBXFileSystemSynchronizedRootGroup;
 | 
				
			||||||
			exceptions = (
 | 
								exceptions = (
 | 
				
			||||||
@@ -205,6 +233,14 @@
 | 
				
			|||||||
			);
 | 
								);
 | 
				
			||||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
								runOnlyForDeploymentPostprocessing = 0;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
							7310A7D12EB10962002C0FD3 /* Frameworks */ = {
 | 
				
			||||||
 | 
								isa = PBXFrameworksBuildPhase;
 | 
				
			||||||
 | 
								buildActionMask = 2147483647;
 | 
				
			||||||
 | 
								files = (
 | 
				
			||||||
 | 
									5D8143680678FCD1D1827271 /* Pods_Solian_Watch_App.framework in Frameworks */,
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								runOnlyForDeploymentPostprocessing = 0;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
		73ACDFA82E3D0E6100B63535 /* Frameworks */ = {
 | 
							73ACDFA82E3D0E6100B63535 /* Frameworks */ = {
 | 
				
			||||||
			isa = PBXFrameworksBuildPhase;
 | 
								isa = PBXFrameworksBuildPhase;
 | 
				
			||||||
			buildActionMask = 2147483647;
 | 
								buildActionMask = 2147483647;
 | 
				
			||||||
@@ -258,6 +294,7 @@
 | 
				
			|||||||
				7B40764A2C4CC0E7DC70A0D3 /* Pods_SolianShareExtension.framework */,
 | 
									7B40764A2C4CC0E7DC70A0D3 /* Pods_SolianShareExtension.framework */,
 | 
				
			||||||
				73ACDFAC2E3D0E6100B63535 /* ReplayKit.framework */,
 | 
									73ACDFAC2E3D0E6100B63535 /* ReplayKit.framework */,
 | 
				
			||||||
				73ACDFB82E3D0E6100B63535 /* UIKit.framework */,
 | 
									73ACDFB82E3D0E6100B63535 /* UIKit.framework */,
 | 
				
			||||||
 | 
									C9C046CF867AE03DC170F861 /* Pods_Solian_Watch_App.framework */,
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			name = Frameworks;
 | 
								name = Frameworks;
 | 
				
			||||||
			sourceTree = "<group>";
 | 
								sourceTree = "<group>";
 | 
				
			||||||
@@ -280,6 +317,12 @@
 | 
				
			|||||||
				17FAB080A9C53193ABD9C15B /* Pods-SolianShareExtension.debug.xcconfig */,
 | 
									17FAB080A9C53193ABD9C15B /* Pods-SolianShareExtension.debug.xcconfig */,
 | 
				
			||||||
				27C66EFB5A705F1A822C3EB0 /* Pods-SolianShareExtension.release.xcconfig */,
 | 
									27C66EFB5A705F1A822C3EB0 /* Pods-SolianShareExtension.release.xcconfig */,
 | 
				
			||||||
				A85FF612AE7623A9934E57CE /* Pods-SolianShareExtension.profile.xcconfig */,
 | 
									A85FF612AE7623A9934E57CE /* Pods-SolianShareExtension.profile.xcconfig */,
 | 
				
			||||||
 | 
									86D60BA96DA647E1B11AA7F0 /* Pods-WatchRunner Watch App.debug.xcconfig */,
 | 
				
			||||||
 | 
									A2EB1DAFDE9B8E6D88BBF7A3 /* Pods-WatchRunner Watch App.release.xcconfig */,
 | 
				
			||||||
 | 
									103EA2362B9E9F127016A1F1 /* Pods-WatchRunner Watch App.profile.xcconfig */,
 | 
				
			||||||
 | 
									31EA49B10397BD4145AD765E /* Pods-Solian Watch App.debug.xcconfig */,
 | 
				
			||||||
 | 
									2440CEDEAAD6D51FDA95FA62 /* Pods-Solian Watch App.release.xcconfig */,
 | 
				
			||||||
 | 
									0ECC3D56D018DD87FC342699 /* Pods-Solian Watch App.profile.xcconfig */,
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			path = Pods;
 | 
								path = Pods;
 | 
				
			||||||
			sourceTree = "<group>";
 | 
								sourceTree = "<group>";
 | 
				
			||||||
@@ -303,6 +346,7 @@
 | 
				
			|||||||
				73CDD67B2DEC00480059D95D /* SolianNotificationService */,
 | 
									73CDD67B2DEC00480059D95D /* SolianNotificationService */,
 | 
				
			||||||
				73C305CF2E0BE878009035B9 /* SolianShareExtension */,
 | 
									73C305CF2E0BE878009035B9 /* SolianShareExtension */,
 | 
				
			||||||
				73ACDFAE2E3D0E6100B63535 /* SolianBroadcastExtension */,
 | 
									73ACDFAE2E3D0E6100B63535 /* SolianBroadcastExtension */,
 | 
				
			||||||
 | 
									7310A7D52EB10962002C0FD3 /* Solian Watch App */,
 | 
				
			||||||
				97C146EF1CF9000F007C117D /* Products */,
 | 
									97C146EF1CF9000F007C117D /* Products */,
 | 
				
			||||||
				331C8082294A63A400263BE5 /* RunnerTests */,
 | 
									331C8082294A63A400263BE5 /* RunnerTests */,
 | 
				
			||||||
				91E124CE95BCB4DCD890160D /* Pods */,
 | 
									91E124CE95BCB4DCD890160D /* Pods */,
 | 
				
			||||||
@@ -319,6 +363,7 @@
 | 
				
			|||||||
				73CDD67A2DEC00480059D95D /* SolianNotificationService.appex */,
 | 
									73CDD67A2DEC00480059D95D /* SolianNotificationService.appex */,
 | 
				
			||||||
				73C305CE2E0BE878009035B9 /* SolianShareExtension.appex */,
 | 
									73C305CE2E0BE878009035B9 /* SolianShareExtension.appex */,
 | 
				
			||||||
				73ACDFAB2E3D0E6100B63535 /* SolianBroadcastExtension.appex */,
 | 
									73ACDFAB2E3D0E6100B63535 /* SolianBroadcastExtension.appex */,
 | 
				
			||||||
 | 
									7310A7D42EB10962002C0FD3 /* Solian Watch App.app */,
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			name = Products;
 | 
								name = Products;
 | 
				
			||||||
			sourceTree = "<group>";
 | 
								sourceTree = "<group>";
 | 
				
			||||||
@@ -363,6 +408,28 @@
 | 
				
			|||||||
			productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
 | 
								productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
 | 
				
			||||||
			productType = "com.apple.product-type.bundle.unit-test";
 | 
								productType = "com.apple.product-type.bundle.unit-test";
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
							7310A7D32EB10962002C0FD3 /* Solian Watch App */ = {
 | 
				
			||||||
 | 
								isa = PBXNativeTarget;
 | 
				
			||||||
 | 
								buildConfigurationList = 7310A7E32EB10963002C0FD3 /* Build configuration list for PBXNativeTarget "Solian Watch App" */;
 | 
				
			||||||
 | 
								buildPhases = (
 | 
				
			||||||
 | 
									DDEDA1BA6278B94F0F7B9B61 /* [CP] Check Pods Manifest.lock */,
 | 
				
			||||||
 | 
									7310A7D02EB10962002C0FD3 /* Sources */,
 | 
				
			||||||
 | 
									7310A7D12EB10962002C0FD3 /* Frameworks */,
 | 
				
			||||||
 | 
									7310A7D22EB10962002C0FD3 /* Resources */,
 | 
				
			||||||
 | 
									E29ECA5954168075BDB000DC /* [CP] Embed Pods Frameworks */,
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								buildRules = (
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								dependencies = (
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								fileSystemSynchronizedGroups = (
 | 
				
			||||||
 | 
									7310A7D52EB10962002C0FD3 /* Solian Watch App */,
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								name = "Solian Watch App";
 | 
				
			||||||
 | 
								productName = "WatchRunner Watch App";
 | 
				
			||||||
 | 
								productReference = 7310A7D42EB10962002C0FD3 /* Solian Watch App.app */;
 | 
				
			||||||
 | 
								productType = "com.apple.product-type.application";
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
		73ACDFAA2E3D0E6100B63535 /* SolianBroadcastExtension */ = {
 | 
							73ACDFAA2E3D0E6100B63535 /* SolianBroadcastExtension */ = {
 | 
				
			||||||
			isa = PBXNativeTarget;
 | 
								isa = PBXNativeTarget;
 | 
				
			||||||
			buildConfigurationList = 73ACDFCB2E3D0E6100B63535 /* Build configuration list for PBXNativeTarget "SolianBroadcastExtension" */;
 | 
								buildConfigurationList = 73ACDFCB2E3D0E6100B63535 /* Build configuration list for PBXNativeTarget "SolianBroadcastExtension" */;
 | 
				
			||||||
@@ -434,6 +501,7 @@
 | 
				
			|||||||
				97C146EA1CF9000F007C117D /* Sources */,
 | 
									97C146EA1CF9000F007C117D /* Sources */,
 | 
				
			||||||
				97C146EB1CF9000F007C117D /* Frameworks */,
 | 
									97C146EB1CF9000F007C117D /* Frameworks */,
 | 
				
			||||||
				73268D1D2DEAFD670076E970 /* Embed Foundation Extensions */,
 | 
									73268D1D2DEAFD670076E970 /* Embed Foundation Extensions */,
 | 
				
			||||||
 | 
									7310A7DE2EB10963002C0FD3 /* Embed Watch Content */,
 | 
				
			||||||
				97C146EC1CF9000F007C117D /* Resources */,
 | 
									97C146EC1CF9000F007C117D /* Resources */,
 | 
				
			||||||
				9705A1C41CF9048500538489 /* Embed Frameworks */,
 | 
									9705A1C41CF9048500538489 /* Embed Frameworks */,
 | 
				
			||||||
				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
 | 
									3B06AD1E1E4923F5004D2608 /* Thin Binary */,
 | 
				
			||||||
@@ -463,7 +531,7 @@
 | 
				
			|||||||
			isa = PBXProject;
 | 
								isa = PBXProject;
 | 
				
			||||||
			attributes = {
 | 
								attributes = {
 | 
				
			||||||
				BuildIndependentTargetsInParallel = YES;
 | 
									BuildIndependentTargetsInParallel = YES;
 | 
				
			||||||
				LastSwiftUpdateCheck = 1640;
 | 
									LastSwiftUpdateCheck = 2600;
 | 
				
			||||||
				LastUpgradeCheck = 1510;
 | 
									LastUpgradeCheck = 1510;
 | 
				
			||||||
				ORGANIZATIONNAME = "";
 | 
									ORGANIZATIONNAME = "";
 | 
				
			||||||
				TargetAttributes = {
 | 
									TargetAttributes = {
 | 
				
			||||||
@@ -471,6 +539,9 @@
 | 
				
			|||||||
						CreatedOnToolsVersion = 14.0;
 | 
											CreatedOnToolsVersion = 14.0;
 | 
				
			||||||
						TestTargetID = 97C146ED1CF9000F007C117D;
 | 
											TestTargetID = 97C146ED1CF9000F007C117D;
 | 
				
			||||||
					};
 | 
										};
 | 
				
			||||||
 | 
										7310A7D32EB10962002C0FD3 = {
 | 
				
			||||||
 | 
											CreatedOnToolsVersion = 26.0.1;
 | 
				
			||||||
 | 
										};
 | 
				
			||||||
					73ACDFAA2E3D0E6100B63535 = {
 | 
										73ACDFAA2E3D0E6100B63535 = {
 | 
				
			||||||
						CreatedOnToolsVersion = 16.4;
 | 
											CreatedOnToolsVersion = 16.4;
 | 
				
			||||||
					};
 | 
										};
 | 
				
			||||||
@@ -504,6 +575,7 @@
 | 
				
			|||||||
				73CDD6792DEC00480059D95D /* SolianNotificationService */,
 | 
									73CDD6792DEC00480059D95D /* SolianNotificationService */,
 | 
				
			||||||
				73C305CD2E0BE878009035B9 /* SolianShareExtension */,
 | 
									73C305CD2E0BE878009035B9 /* SolianShareExtension */,
 | 
				
			||||||
				73ACDFAA2E3D0E6100B63535 /* SolianBroadcastExtension */,
 | 
									73ACDFAA2E3D0E6100B63535 /* SolianBroadcastExtension */,
 | 
				
			||||||
 | 
									7310A7D32EB10962002C0FD3 /* Solian Watch App */,
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
/* End PBXProject section */
 | 
					/* End PBXProject section */
 | 
				
			||||||
@@ -516,6 +588,13 @@
 | 
				
			|||||||
			);
 | 
								);
 | 
				
			||||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
								runOnlyForDeploymentPostprocessing = 0;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
							7310A7D22EB10962002C0FD3 /* Resources */ = {
 | 
				
			||||||
 | 
								isa = PBXResourcesBuildPhase;
 | 
				
			||||||
 | 
								buildActionMask = 2147483647;
 | 
				
			||||||
 | 
								files = (
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								runOnlyForDeploymentPostprocessing = 0;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
		73ACDFA92E3D0E6100B63535 /* Resources */ = {
 | 
							73ACDFA92E3D0E6100B63535 /* Resources */ = {
 | 
				
			||||||
			isa = PBXResourcesBuildPhase;
 | 
								isa = PBXResourcesBuildPhase;
 | 
				
			||||||
			buildActionMask = 2147483647;
 | 
								buildActionMask = 2147483647;
 | 
				
			||||||
@@ -566,7 +645,7 @@
 | 
				
			|||||||
			);
 | 
								);
 | 
				
			||||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
								runOnlyForDeploymentPostprocessing = 0;
 | 
				
			||||||
			shellPath = /bin/sh;
 | 
								shellPath = /bin/sh;
 | 
				
			||||||
			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
 | 
								shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n";
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
		4815E0A19398E51078F4160D /* [CP] Check Pods Manifest.lock */ = {
 | 
							4815E0A19398E51078F4160D /* [CP] Check Pods Manifest.lock */ = {
 | 
				
			||||||
			isa = PBXShellScriptBuildPhase;
 | 
								isa = PBXShellScriptBuildPhase;
 | 
				
			||||||
@@ -683,6 +762,45 @@
 | 
				
			|||||||
			shellPath = /bin/sh;
 | 
								shellPath = /bin/sh;
 | 
				
			||||||
			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
 | 
								shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
							DDEDA1BA6278B94F0F7B9B61 /* [CP] Check Pods Manifest.lock */ = {
 | 
				
			||||||
 | 
								isa = PBXShellScriptBuildPhase;
 | 
				
			||||||
 | 
								buildActionMask = 2147483647;
 | 
				
			||||||
 | 
								files = (
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								inputFileListPaths = (
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								inputPaths = (
 | 
				
			||||||
 | 
									"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
 | 
				
			||||||
 | 
									"${PODS_ROOT}/Manifest.lock",
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								name = "[CP] Check Pods Manifest.lock";
 | 
				
			||||||
 | 
								outputFileListPaths = (
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								outputPaths = (
 | 
				
			||||||
 | 
									"$(DERIVED_FILE_DIR)/Pods-Solian Watch App-checkManifestLockResult.txt",
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								runOnlyForDeploymentPostprocessing = 0;
 | 
				
			||||||
 | 
								shellPath = /bin/sh;
 | 
				
			||||||
 | 
								shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 | 
				
			||||||
 | 
								showEnvVarsInLog = 0;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							E29ECA5954168075BDB000DC /* [CP] Embed Pods Frameworks */ = {
 | 
				
			||||||
 | 
								isa = PBXShellScriptBuildPhase;
 | 
				
			||||||
 | 
								buildActionMask = 2147483647;
 | 
				
			||||||
 | 
								files = (
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								inputFileListPaths = (
 | 
				
			||||||
 | 
									"${PODS_ROOT}/Target Support Files/Pods-Solian Watch App/Pods-Solian Watch App-frameworks-${CONFIGURATION}-input-files.xcfilelist",
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								name = "[CP] Embed Pods Frameworks";
 | 
				
			||||||
 | 
								outputFileListPaths = (
 | 
				
			||||||
 | 
									"${PODS_ROOT}/Target Support Files/Pods-Solian Watch App/Pods-Solian Watch App-frameworks-${CONFIGURATION}-output-files.xcfilelist",
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								runOnlyForDeploymentPostprocessing = 0;
 | 
				
			||||||
 | 
								shellPath = /bin/sh;
 | 
				
			||||||
 | 
								shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Solian Watch App/Pods-Solian Watch App-frameworks.sh\"\n";
 | 
				
			||||||
 | 
								showEnvVarsInLog = 0;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
		E86CDE9D6464F4F52B910856 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = {
 | 
							E86CDE9D6464F4F52B910856 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = {
 | 
				
			||||||
			isa = PBXShellScriptBuildPhase;
 | 
								isa = PBXShellScriptBuildPhase;
 | 
				
			||||||
			buildActionMask = 2147483647;
 | 
								buildActionMask = 2147483647;
 | 
				
			||||||
@@ -734,6 +852,13 @@
 | 
				
			|||||||
			);
 | 
								);
 | 
				
			||||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
								runOnlyForDeploymentPostprocessing = 0;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
							7310A7D02EB10962002C0FD3 /* Sources */ = {
 | 
				
			||||||
 | 
								isa = PBXSourcesBuildPhase;
 | 
				
			||||||
 | 
								buildActionMask = 2147483647;
 | 
				
			||||||
 | 
								files = (
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								runOnlyForDeploymentPostprocessing = 0;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
		73ACDFA72E3D0E6100B63535 /* Sources */ = {
 | 
							73ACDFA72E3D0E6100B63535 /* Sources */ = {
 | 
				
			||||||
			isa = PBXSourcesBuildPhase;
 | 
								isa = PBXSourcesBuildPhase;
 | 
				
			||||||
			buildActionMask = 2147483647;
 | 
								buildActionMask = 2147483647;
 | 
				
			||||||
@@ -853,7 +978,7 @@
 | 
				
			|||||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
									GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
				
			||||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
									GCC_WARN_UNUSED_FUNCTION = YES;
 | 
				
			||||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
									GCC_WARN_UNUSED_VARIABLE = YES;
 | 
				
			||||||
				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
				
			||||||
				MTL_ENABLE_DEBUG_INFO = NO;
 | 
									MTL_ENABLE_DEBUG_INFO = NO;
 | 
				
			||||||
				SDKROOT = iphoneos;
 | 
									SDKROOT = iphoneos;
 | 
				
			||||||
				SUPPORTED_PLATFORMS = iphoneos;
 | 
									SUPPORTED_PLATFORMS = iphoneos;
 | 
				
			||||||
@@ -873,6 +998,7 @@
 | 
				
			|||||||
				CUSTOM_GROUP_ID = group.solsynth.solian;
 | 
									CUSTOM_GROUP_ID = group.solsynth.solian;
 | 
				
			||||||
				DEVELOPMENT_TEAM = W7HPZ53V6B;
 | 
									DEVELOPMENT_TEAM = W7HPZ53V6B;
 | 
				
			||||||
				ENABLE_BITCODE = NO;
 | 
									ENABLE_BITCODE = NO;
 | 
				
			||||||
 | 
									EXCLUDED_SOURCE_FILE_NAMES = "";
 | 
				
			||||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
									INFOPLIST_FILE = Runner/Info.plist;
 | 
				
			||||||
				INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
									INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
				
			||||||
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
 | 
									INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
 | 
				
			||||||
@@ -883,9 +1009,12 @@
 | 
				
			|||||||
				);
 | 
									);
 | 
				
			||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 | 
				
			||||||
 | 
									SWIFT_ENABLE_EXPLICIT_MODULES = "$(SWIFT_USE_INTEGRATED_DRIVER)";
 | 
				
			||||||
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
 | 
									SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				VERSIONING_SYSTEM = "apple-generic";
 | 
									VERSIONING_SYSTEM = "apple-generic";
 | 
				
			||||||
 | 
									WATCHOS_DEPLOYMENT_TARGET = 11.6;
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
			name = Profile;
 | 
								name = Profile;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
@@ -893,13 +1022,17 @@
 | 
				
			|||||||
			isa = XCBuildConfiguration;
 | 
								isa = XCBuildConfiguration;
 | 
				
			||||||
			baseConfigurationReference = 14DFD79BE7C26E51B117583C /* Pods-RunnerTests.debug.xcconfig */;
 | 
								baseConfigurationReference = 14DFD79BE7C26E51B117583C /* Pods-RunnerTests.debug.xcconfig */;
 | 
				
			||||||
			buildSettings = {
 | 
								buildSettings = {
 | 
				
			||||||
 | 
									ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
 | 
				
			||||||
				BUNDLE_LOADER = "$(TEST_HOST)";
 | 
									BUNDLE_LOADER = "$(TEST_HOST)";
 | 
				
			||||||
				CODE_SIGN_STYLE = Automatic;
 | 
									CODE_SIGN_STYLE = Automatic;
 | 
				
			||||||
				CURRENT_PROJECT_VERSION = 1;
 | 
									CURRENT_PROJECT_VERSION = 1;
 | 
				
			||||||
				GENERATE_INFOPLIST_FILE = YES;
 | 
									GENERATE_INFOPLIST_FILE = YES;
 | 
				
			||||||
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 15.0;
 | 
				
			||||||
				MARKETING_VERSION = 1.0;
 | 
									MARKETING_VERSION = 1.0;
 | 
				
			||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 | 
				
			||||||
 | 
									SUPPORTS_MACCATALYST = YES;
 | 
				
			||||||
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
 | 
									SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
 | 
				
			||||||
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
									SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
@@ -911,13 +1044,17 @@
 | 
				
			|||||||
			isa = XCBuildConfiguration;
 | 
								isa = XCBuildConfiguration;
 | 
				
			||||||
			baseConfigurationReference = 14118AC858B441AB16B7309E /* Pods-RunnerTests.release.xcconfig */;
 | 
								baseConfigurationReference = 14118AC858B441AB16B7309E /* Pods-RunnerTests.release.xcconfig */;
 | 
				
			||||||
			buildSettings = {
 | 
								buildSettings = {
 | 
				
			||||||
 | 
									ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
 | 
				
			||||||
				BUNDLE_LOADER = "$(TEST_HOST)";
 | 
									BUNDLE_LOADER = "$(TEST_HOST)";
 | 
				
			||||||
				CODE_SIGN_STYLE = Automatic;
 | 
									CODE_SIGN_STYLE = Automatic;
 | 
				
			||||||
				CURRENT_PROJECT_VERSION = 1;
 | 
									CURRENT_PROJECT_VERSION = 1;
 | 
				
			||||||
				GENERATE_INFOPLIST_FILE = YES;
 | 
									GENERATE_INFOPLIST_FILE = YES;
 | 
				
			||||||
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 15.0;
 | 
				
			||||||
				MARKETING_VERSION = 1.0;
 | 
									MARKETING_VERSION = 1.0;
 | 
				
			||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 | 
				
			||||||
 | 
									SUPPORTS_MACCATALYST = YES;
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
 | 
									TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
@@ -927,18 +1064,171 @@
 | 
				
			|||||||
			isa = XCBuildConfiguration;
 | 
								isa = XCBuildConfiguration;
 | 
				
			||||||
			baseConfigurationReference = E6B10A9A85BECA2E576C91FF /* Pods-RunnerTests.profile.xcconfig */;
 | 
								baseConfigurationReference = E6B10A9A85BECA2E576C91FF /* Pods-RunnerTests.profile.xcconfig */;
 | 
				
			||||||
			buildSettings = {
 | 
								buildSettings = {
 | 
				
			||||||
 | 
									ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
 | 
				
			||||||
				BUNDLE_LOADER = "$(TEST_HOST)";
 | 
									BUNDLE_LOADER = "$(TEST_HOST)";
 | 
				
			||||||
				CODE_SIGN_STYLE = Automatic;
 | 
									CODE_SIGN_STYLE = Automatic;
 | 
				
			||||||
				CURRENT_PROJECT_VERSION = 1;
 | 
									CURRENT_PROJECT_VERSION = 1;
 | 
				
			||||||
				GENERATE_INFOPLIST_FILE = YES;
 | 
									GENERATE_INFOPLIST_FILE = YES;
 | 
				
			||||||
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 15.0;
 | 
				
			||||||
				MARKETING_VERSION = 1.0;
 | 
									MARKETING_VERSION = 1.0;
 | 
				
			||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.RunnerTests;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 | 
				
			||||||
 | 
									SUPPORTS_MACCATALYST = YES;
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
 | 
									TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
			name = Profile;
 | 
								name = Profile;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
							7310A7E02EB10963002C0FD3 /* Debug */ = {
 | 
				
			||||||
 | 
								isa = XCBuildConfiguration;
 | 
				
			||||||
 | 
								baseConfigurationReference = 31EA49B10397BD4145AD765E /* Pods-Solian Watch App.debug.xcconfig */;
 | 
				
			||||||
 | 
								buildSettings = {
 | 
				
			||||||
 | 
									ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
				
			||||||
 | 
									ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
 | 
				
			||||||
 | 
									ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 | 
				
			||||||
 | 
									CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 | 
				
			||||||
 | 
									CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
 | 
				
			||||||
 | 
									CLANG_ENABLE_OBJC_WEAK = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
 | 
				
			||||||
 | 
									CODE_SIGN_STYLE = Automatic;
 | 
				
			||||||
 | 
									CURRENT_PROJECT_VERSION = 1;
 | 
				
			||||||
 | 
									DEVELOPMENT_TEAM = W7HPZ53V6B;
 | 
				
			||||||
 | 
									ENABLE_PREVIEWS = YES;
 | 
				
			||||||
 | 
									ENABLE_USER_SCRIPT_SANDBOXING = NO;
 | 
				
			||||||
 | 
									GCC_C_LANGUAGE_STANDARD = gnu17;
 | 
				
			||||||
 | 
									GENERATE_INFOPLIST_FILE = YES;
 | 
				
			||||||
 | 
									INFOPLIST_FILE = "WatchRunner-Watch-App-Info.plist";
 | 
				
			||||||
 | 
									INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
				
			||||||
 | 
									INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
 | 
				
			||||||
 | 
									INFOPLIST_KEY_WKCompanionAppBundleIdentifier = dev.solsynth.solian;
 | 
				
			||||||
 | 
									INFOPLIST_KEY_WKRunsIndependentlyOfCompanionApp = NO;
 | 
				
			||||||
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 18.6;
 | 
				
			||||||
 | 
									LD_RUNPATH_SEARCH_PATHS = (
 | 
				
			||||||
 | 
										"$(inherited)",
 | 
				
			||||||
 | 
										"@executable_path/Frameworks",
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
 | 
				
			||||||
 | 
									MARKETING_VERSION = 1.0;
 | 
				
			||||||
 | 
									MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 | 
				
			||||||
 | 
									MTL_FAST_MATH = YES;
 | 
				
			||||||
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.watchkitapp;
 | 
				
			||||||
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
 | 
									SDKROOT = watchos;
 | 
				
			||||||
 | 
									SKIP_INSTALL = YES;
 | 
				
			||||||
 | 
									STRING_CATALOG_GENERATE_SYMBOLS = YES;
 | 
				
			||||||
 | 
									SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
 | 
				
			||||||
 | 
									SWIFT_APPROACHABLE_CONCURRENCY = YES;
 | 
				
			||||||
 | 
									SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
 | 
				
			||||||
 | 
									SWIFT_EMIT_LOC_STRINGS = YES;
 | 
				
			||||||
 | 
									SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
				
			||||||
 | 
									SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
 | 
				
			||||||
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
 | 
									TARGETED_DEVICE_FAMILY = 4;
 | 
				
			||||||
 | 
									WATCHOS_DEPLOYMENT_TARGET = 11.6;
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								name = Debug;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							7310A7E12EB10963002C0FD3 /* Release */ = {
 | 
				
			||||||
 | 
								isa = XCBuildConfiguration;
 | 
				
			||||||
 | 
								baseConfigurationReference = 2440CEDEAAD6D51FDA95FA62 /* Pods-Solian Watch App.release.xcconfig */;
 | 
				
			||||||
 | 
								buildSettings = {
 | 
				
			||||||
 | 
									ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
				
			||||||
 | 
									ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
 | 
				
			||||||
 | 
									ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 | 
				
			||||||
 | 
									CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 | 
				
			||||||
 | 
									CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
 | 
				
			||||||
 | 
									CLANG_ENABLE_OBJC_WEAK = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
 | 
				
			||||||
 | 
									CODE_SIGN_STYLE = Automatic;
 | 
				
			||||||
 | 
									CURRENT_PROJECT_VERSION = 1;
 | 
				
			||||||
 | 
									DEVELOPMENT_TEAM = W7HPZ53V6B;
 | 
				
			||||||
 | 
									ENABLE_PREVIEWS = YES;
 | 
				
			||||||
 | 
									ENABLE_USER_SCRIPT_SANDBOXING = NO;
 | 
				
			||||||
 | 
									GCC_C_LANGUAGE_STANDARD = gnu17;
 | 
				
			||||||
 | 
									GENERATE_INFOPLIST_FILE = YES;
 | 
				
			||||||
 | 
									INFOPLIST_FILE = "WatchRunner-Watch-App-Info.plist";
 | 
				
			||||||
 | 
									INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
				
			||||||
 | 
									INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
 | 
				
			||||||
 | 
									INFOPLIST_KEY_WKCompanionAppBundleIdentifier = dev.solsynth.solian;
 | 
				
			||||||
 | 
									INFOPLIST_KEY_WKRunsIndependentlyOfCompanionApp = NO;
 | 
				
			||||||
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 18.6;
 | 
				
			||||||
 | 
									LD_RUNPATH_SEARCH_PATHS = (
 | 
				
			||||||
 | 
										"$(inherited)",
 | 
				
			||||||
 | 
										"@executable_path/Frameworks",
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
 | 
				
			||||||
 | 
									MARKETING_VERSION = 1.0;
 | 
				
			||||||
 | 
									MTL_FAST_MATH = YES;
 | 
				
			||||||
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.watchkitapp;
 | 
				
			||||||
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
 | 
									SDKROOT = watchos;
 | 
				
			||||||
 | 
									SKIP_INSTALL = YES;
 | 
				
			||||||
 | 
									STRING_CATALOG_GENERATE_SYMBOLS = YES;
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "watchsimulator watchos";
 | 
				
			||||||
 | 
									SWIFT_APPROACHABLE_CONCURRENCY = YES;
 | 
				
			||||||
 | 
									SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
 | 
				
			||||||
 | 
									SWIFT_EMIT_LOC_STRINGS = YES;
 | 
				
			||||||
 | 
									SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
 | 
				
			||||||
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
 | 
									TARGETED_DEVICE_FAMILY = 4;
 | 
				
			||||||
 | 
									WATCHOS_DEPLOYMENT_TARGET = 11.6;
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								name = Release;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							7310A7E22EB10963002C0FD3 /* Profile */ = {
 | 
				
			||||||
 | 
								isa = XCBuildConfiguration;
 | 
				
			||||||
 | 
								baseConfigurationReference = 0ECC3D56D018DD87FC342699 /* Pods-Solian Watch App.profile.xcconfig */;
 | 
				
			||||||
 | 
								buildSettings = {
 | 
				
			||||||
 | 
									ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
				
			||||||
 | 
									ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
 | 
				
			||||||
 | 
									ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 | 
				
			||||||
 | 
									CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 | 
				
			||||||
 | 
									CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
 | 
				
			||||||
 | 
									CLANG_ENABLE_OBJC_WEAK = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
 | 
				
			||||||
 | 
									CODE_SIGN_STYLE = Automatic;
 | 
				
			||||||
 | 
									CURRENT_PROJECT_VERSION = 1;
 | 
				
			||||||
 | 
									DEVELOPMENT_TEAM = W7HPZ53V6B;
 | 
				
			||||||
 | 
									ENABLE_PREVIEWS = YES;
 | 
				
			||||||
 | 
									ENABLE_USER_SCRIPT_SANDBOXING = NO;
 | 
				
			||||||
 | 
									GCC_C_LANGUAGE_STANDARD = gnu17;
 | 
				
			||||||
 | 
									GENERATE_INFOPLIST_FILE = YES;
 | 
				
			||||||
 | 
									INFOPLIST_FILE = "WatchRunner-Watch-App-Info.plist";
 | 
				
			||||||
 | 
									INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
				
			||||||
 | 
									INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
 | 
				
			||||||
 | 
									INFOPLIST_KEY_WKCompanionAppBundleIdentifier = dev.solsynth.solian;
 | 
				
			||||||
 | 
									INFOPLIST_KEY_WKRunsIndependentlyOfCompanionApp = NO;
 | 
				
			||||||
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 18.6;
 | 
				
			||||||
 | 
									LD_RUNPATH_SEARCH_PATHS = (
 | 
				
			||||||
 | 
										"$(inherited)",
 | 
				
			||||||
 | 
										"@executable_path/Frameworks",
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
 | 
				
			||||||
 | 
									MARKETING_VERSION = 1.0;
 | 
				
			||||||
 | 
									MTL_FAST_MATH = YES;
 | 
				
			||||||
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.watchkitapp;
 | 
				
			||||||
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
 | 
									SDKROOT = watchos;
 | 
				
			||||||
 | 
									SKIP_INSTALL = YES;
 | 
				
			||||||
 | 
									STRING_CATALOG_GENERATE_SYMBOLS = YES;
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "watchsimulator watchos";
 | 
				
			||||||
 | 
									SWIFT_APPROACHABLE_CONCURRENCY = YES;
 | 
				
			||||||
 | 
									SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
 | 
				
			||||||
 | 
									SWIFT_EMIT_LOC_STRINGS = YES;
 | 
				
			||||||
 | 
									SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
 | 
				
			||||||
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
 | 
									TARGETED_DEVICE_FAMILY = 4;
 | 
				
			||||||
 | 
									WATCHOS_DEPLOYMENT_TARGET = 11.6;
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								name = Profile;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
		73ACDFC42E3D0E6100B63535 /* Debug */ = {
 | 
							73ACDFC42E3D0E6100B63535 /* Debug */ = {
 | 
				
			||||||
			isa = XCBuildConfiguration;
 | 
								isa = XCBuildConfiguration;
 | 
				
			||||||
			buildSettings = {
 | 
								buildSettings = {
 | 
				
			||||||
@@ -972,6 +1262,7 @@
 | 
				
			|||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianBroadcastExtension;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianBroadcastExtension;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
				SKIP_INSTALL = YES;
 | 
									SKIP_INSTALL = YES;
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 | 
				
			||||||
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
 | 
									SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
 | 
				
			||||||
				SWIFT_EMIT_LOC_STRINGS = YES;
 | 
									SWIFT_EMIT_LOC_STRINGS = YES;
 | 
				
			||||||
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
									SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
				
			||||||
@@ -1012,6 +1303,7 @@
 | 
				
			|||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianBroadcastExtension;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianBroadcastExtension;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
				SKIP_INSTALL = YES;
 | 
									SKIP_INSTALL = YES;
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 | 
				
			||||||
				SWIFT_EMIT_LOC_STRINGS = YES;
 | 
									SWIFT_EMIT_LOC_STRINGS = YES;
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TARGETED_DEVICE_FAMILY = "1,2";
 | 
									TARGETED_DEVICE_FAMILY = "1,2";
 | 
				
			||||||
@@ -1050,6 +1342,7 @@
 | 
				
			|||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianBroadcastExtension;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianBroadcastExtension;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
				SKIP_INSTALL = YES;
 | 
									SKIP_INSTALL = YES;
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 | 
				
			||||||
				SWIFT_EMIT_LOC_STRINGS = YES;
 | 
									SWIFT_EMIT_LOC_STRINGS = YES;
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TARGETED_DEVICE_FAMILY = "1,2";
 | 
									TARGETED_DEVICE_FAMILY = "1,2";
 | 
				
			||||||
@@ -1078,7 +1371,7 @@
 | 
				
			|||||||
				INFOPLIST_FILE = SolianShareExtension/Info.plist;
 | 
									INFOPLIST_FILE = SolianShareExtension/Info.plist;
 | 
				
			||||||
				INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
 | 
									INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
 | 
				
			||||||
				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 | 
									INFOPLIST_KEY_NSHumanReadableCopyright = "";
 | 
				
			||||||
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 15.0;
 | 
				
			||||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
									LD_RUNPATH_SEARCH_PATHS = (
 | 
				
			||||||
					"$(inherited)",
 | 
										"$(inherited)",
 | 
				
			||||||
					"@executable_path/Frameworks",
 | 
										"@executable_path/Frameworks",
 | 
				
			||||||
@@ -1091,8 +1384,10 @@
 | 
				
			|||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianShareExtension;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianShareExtension;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
				SKIP_INSTALL = YES;
 | 
									SKIP_INSTALL = YES;
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 | 
				
			||||||
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
 | 
									SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
 | 
				
			||||||
				SWIFT_EMIT_LOC_STRINGS = YES;
 | 
									SWIFT_EMIT_LOC_STRINGS = YES;
 | 
				
			||||||
 | 
									SWIFT_ENABLE_EXPLICIT_MODULES = NO;
 | 
				
			||||||
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
									SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TARGETED_DEVICE_FAMILY = "1,2";
 | 
									TARGETED_DEVICE_FAMILY = "1,2";
 | 
				
			||||||
@@ -1121,7 +1416,7 @@
 | 
				
			|||||||
				INFOPLIST_FILE = SolianShareExtension/Info.plist;
 | 
									INFOPLIST_FILE = SolianShareExtension/Info.plist;
 | 
				
			||||||
				INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
 | 
									INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
 | 
				
			||||||
				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 | 
									INFOPLIST_KEY_NSHumanReadableCopyright = "";
 | 
				
			||||||
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 15.0;
 | 
				
			||||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
									LD_RUNPATH_SEARCH_PATHS = (
 | 
				
			||||||
					"$(inherited)",
 | 
										"$(inherited)",
 | 
				
			||||||
					"@executable_path/Frameworks",
 | 
										"@executable_path/Frameworks",
 | 
				
			||||||
@@ -1133,7 +1428,9 @@
 | 
				
			|||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianShareExtension;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianShareExtension;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
				SKIP_INSTALL = YES;
 | 
									SKIP_INSTALL = YES;
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 | 
				
			||||||
				SWIFT_EMIT_LOC_STRINGS = YES;
 | 
									SWIFT_EMIT_LOC_STRINGS = YES;
 | 
				
			||||||
 | 
									SWIFT_ENABLE_EXPLICIT_MODULES = NO;
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TARGETED_DEVICE_FAMILY = "1,2";
 | 
									TARGETED_DEVICE_FAMILY = "1,2";
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
@@ -1161,7 +1458,7 @@
 | 
				
			|||||||
				INFOPLIST_FILE = SolianShareExtension/Info.plist;
 | 
									INFOPLIST_FILE = SolianShareExtension/Info.plist;
 | 
				
			||||||
				INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
 | 
									INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
 | 
				
			||||||
				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 | 
									INFOPLIST_KEY_NSHumanReadableCopyright = "";
 | 
				
			||||||
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 15.0;
 | 
				
			||||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
									LD_RUNPATH_SEARCH_PATHS = (
 | 
				
			||||||
					"$(inherited)",
 | 
										"$(inherited)",
 | 
				
			||||||
					"@executable_path/Frameworks",
 | 
										"@executable_path/Frameworks",
 | 
				
			||||||
@@ -1173,7 +1470,9 @@
 | 
				
			|||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianShareExtension;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianShareExtension;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
				SKIP_INSTALL = YES;
 | 
									SKIP_INSTALL = YES;
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 | 
				
			||||||
				SWIFT_EMIT_LOC_STRINGS = YES;
 | 
									SWIFT_EMIT_LOC_STRINGS = YES;
 | 
				
			||||||
 | 
									SWIFT_ENABLE_EXPLICIT_MODULES = NO;
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TARGETED_DEVICE_FAMILY = "1,2";
 | 
									TARGETED_DEVICE_FAMILY = "1,2";
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
@@ -1348,7 +1647,7 @@
 | 
				
			|||||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
									GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
				
			||||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
									GCC_WARN_UNUSED_FUNCTION = YES;
 | 
				
			||||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
									GCC_WARN_UNUSED_VARIABLE = YES;
 | 
				
			||||||
				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
				
			||||||
				MTL_ENABLE_DEBUG_INFO = YES;
 | 
									MTL_ENABLE_DEBUG_INFO = YES;
 | 
				
			||||||
				ONLY_ACTIVE_ARCH = YES;
 | 
									ONLY_ACTIVE_ARCH = YES;
 | 
				
			||||||
				SDKROOT = iphoneos;
 | 
									SDKROOT = iphoneos;
 | 
				
			||||||
@@ -1399,7 +1698,7 @@
 | 
				
			|||||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
									GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
				
			||||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
									GCC_WARN_UNUSED_FUNCTION = YES;
 | 
				
			||||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
									GCC_WARN_UNUSED_VARIABLE = YES;
 | 
				
			||||||
				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 | 
									IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
				
			||||||
				MTL_ENABLE_DEBUG_INFO = NO;
 | 
									MTL_ENABLE_DEBUG_INFO = NO;
 | 
				
			||||||
				SDKROOT = iphoneos;
 | 
									SDKROOT = iphoneos;
 | 
				
			||||||
				SUPPORTED_PLATFORMS = iphoneos;
 | 
									SUPPORTED_PLATFORMS = iphoneos;
 | 
				
			||||||
@@ -1421,6 +1720,7 @@
 | 
				
			|||||||
				CUSTOM_GROUP_ID = group.solsynth.solian;
 | 
									CUSTOM_GROUP_ID = group.solsynth.solian;
 | 
				
			||||||
				DEVELOPMENT_TEAM = W7HPZ53V6B;
 | 
									DEVELOPMENT_TEAM = W7HPZ53V6B;
 | 
				
			||||||
				ENABLE_BITCODE = NO;
 | 
									ENABLE_BITCODE = NO;
 | 
				
			||||||
 | 
									EXCLUDED_SOURCE_FILE_NAMES = "";
 | 
				
			||||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
									INFOPLIST_FILE = Runner/Info.plist;
 | 
				
			||||||
				INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
									INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
				
			||||||
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
 | 
									INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
 | 
				
			||||||
@@ -1431,10 +1731,12 @@
 | 
				
			|||||||
				);
 | 
									);
 | 
				
			||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
 | 
									SWIFT_ENABLE_EXPLICIT_MODULES = "$(SWIFT_USE_INTEGRATED_DRIVER)";
 | 
				
			||||||
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
 | 
									SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
 | 
				
			||||||
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
									SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				VERSIONING_SYSTEM = "apple-generic";
 | 
									VERSIONING_SYSTEM = "apple-generic";
 | 
				
			||||||
 | 
									WATCHOS_DEPLOYMENT_TARGET = 11.6;
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
			name = Debug;
 | 
								name = Debug;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
@@ -1449,6 +1751,7 @@
 | 
				
			|||||||
				CUSTOM_GROUP_ID = group.solsynth.solian;
 | 
									CUSTOM_GROUP_ID = group.solsynth.solian;
 | 
				
			||||||
				DEVELOPMENT_TEAM = W7HPZ53V6B;
 | 
									DEVELOPMENT_TEAM = W7HPZ53V6B;
 | 
				
			||||||
				ENABLE_BITCODE = NO;
 | 
									ENABLE_BITCODE = NO;
 | 
				
			||||||
 | 
									EXCLUDED_SOURCE_FILE_NAMES = "";
 | 
				
			||||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
									INFOPLIST_FILE = Runner/Info.plist;
 | 
				
			||||||
				INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
									INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
				
			||||||
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
 | 
									INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
 | 
				
			||||||
@@ -1457,11 +1760,15 @@
 | 
				
			|||||||
					"$(inherited)",
 | 
										"$(inherited)",
 | 
				
			||||||
					"@executable_path/Frameworks",
 | 
										"@executable_path/Frameworks",
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
 | 
									ONLY_ACTIVE_ARCH = NO;
 | 
				
			||||||
				PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
 | 
									SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
 | 
				
			||||||
 | 
									SWIFT_ENABLE_EXPLICIT_MODULES = "$(SWIFT_USE_INTEGRATED_DRIVER)";
 | 
				
			||||||
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
 | 
									SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				VERSIONING_SYSTEM = "apple-generic";
 | 
									VERSIONING_SYSTEM = "apple-generic";
 | 
				
			||||||
 | 
									WATCHOS_DEPLOYMENT_TARGET = 11.6;
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
			name = Release;
 | 
								name = Release;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
@@ -1478,6 +1785,16 @@
 | 
				
			|||||||
			defaultConfigurationIsVisible = 0;
 | 
								defaultConfigurationIsVisible = 0;
 | 
				
			||||||
			defaultConfigurationName = Release;
 | 
								defaultConfigurationName = Release;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
							7310A7E32EB10963002C0FD3 /* Build configuration list for PBXNativeTarget "Solian Watch App" */ = {
 | 
				
			||||||
 | 
								isa = XCConfigurationList;
 | 
				
			||||||
 | 
								buildConfigurations = (
 | 
				
			||||||
 | 
									7310A7E02EB10963002C0FD3 /* Debug */,
 | 
				
			||||||
 | 
									7310A7E12EB10963002C0FD3 /* Release */,
 | 
				
			||||||
 | 
									7310A7E22EB10963002C0FD3 /* Profile */,
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								defaultConfigurationIsVisible = 0;
 | 
				
			||||||
 | 
								defaultConfigurationName = Release;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
		73ACDFCB2E3D0E6100B63535 /* Build configuration list for PBXNativeTarget "SolianBroadcastExtension" */ = {
 | 
							73ACDFCB2E3D0E6100B63535 /* Build configuration list for PBXNativeTarget "SolianBroadcastExtension" */ = {
 | 
				
			||||||
			isa = XCConfigurationList;
 | 
								isa = XCConfigurationList;
 | 
				
			||||||
			buildConfigurations = (
 | 
								buildConfigurations = (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,20 @@
 | 
				
			|||||||
               ReferencedContainer = "container:Runner.xcodeproj">
 | 
					               ReferencedContainer = "container:Runner.xcodeproj">
 | 
				
			||||||
            </BuildableReference>
 | 
					            </BuildableReference>
 | 
				
			||||||
         </BuildActionEntry>
 | 
					         </BuildActionEntry>
 | 
				
			||||||
 | 
					         <BuildActionEntry
 | 
				
			||||||
 | 
					            buildForTesting = "YES"
 | 
				
			||||||
 | 
					            buildForRunning = "YES"
 | 
				
			||||||
 | 
					            buildForProfiling = "YES"
 | 
				
			||||||
 | 
					            buildForArchiving = "YES"
 | 
				
			||||||
 | 
					            buildForAnalyzing = "YES">
 | 
				
			||||||
 | 
					            <BuildableReference
 | 
				
			||||||
 | 
					               BuildableIdentifier = "primary"
 | 
				
			||||||
 | 
					               BlueprintIdentifier = "7310A7D32EB10962002C0FD3"
 | 
				
			||||||
 | 
					               BuildableName = "Solian Watch App.app"
 | 
				
			||||||
 | 
					               BlueprintName = "Solian Watch App"
 | 
				
			||||||
 | 
					               ReferencedContainer = "container:Runner.xcodeproj">
 | 
				
			||||||
 | 
					            </BuildableReference>
 | 
				
			||||||
 | 
					         </BuildActionEntry>
 | 
				
			||||||
      </BuildActionEntries>
 | 
					      </BuildActionEntries>
 | 
				
			||||||
   </BuildAction>
 | 
					   </BuildAction>
 | 
				
			||||||
   <TestAction
 | 
					   <TestAction
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,11 @@
 | 
				
			|||||||
import Flutter
 | 
					import Flutter
 | 
				
			||||||
import UIKit
 | 
					import UIKit
 | 
				
			||||||
 | 
					import WatchConnectivity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@main
 | 
					@main
 | 
				
			||||||
@objc class AppDelegate: FlutterAppDelegate {
 | 
					@objc class AppDelegate: FlutterAppDelegate {
 | 
				
			||||||
    let notifyDelegate = NotifyDelegate()
 | 
					    let notifyDelegate = NotifyDelegate()
 | 
				
			||||||
 | 
					    private static var sharedWatchConnectivityService: WatchConnectivityService?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override func application(
 | 
					    override func application(
 | 
				
			||||||
        _ application: UIApplication,
 | 
					        _ application: UIApplication,
 | 
				
			||||||
@@ -12,7 +14,7 @@ import UIKit
 | 
				
			|||||||
        UNUserNotificationCenter.current().delegate = notifyDelegate
 | 
					        UNUserNotificationCenter.current().delegate = notifyDelegate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let replyableMessageCategory = UNNotificationCategory(
 | 
					        let replyableMessageCategory = UNNotificationCategory(
 | 
				
			||||||
            identifier: "REPLYABLE_MESSAGE",
 | 
					            identifier: "CHAT_MESSAGE",
 | 
				
			||||||
            actions: [
 | 
					            actions: [
 | 
				
			||||||
                UNTextInputNotificationAction(
 | 
					                UNTextInputNotificationAction(
 | 
				
			||||||
                    identifier: "reply_action",
 | 
					                    identifier: "reply_action",
 | 
				
			||||||
@@ -23,11 +25,85 @@ import UIKit
 | 
				
			|||||||
            intentIdentifiers: [],
 | 
					            intentIdentifiers: [],
 | 
				
			||||||
            options: []
 | 
					            options: []
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        UNUserNotificationCenter.current().setNotificationCategories([replyableMessageCategory])
 | 
					        UNUserNotificationCenter.current().setNotificationCategories([replyableMessageCategory])
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        GeneratedPluginRegistrant.register(with: self)
 | 
					        GeneratedPluginRegistrant.register(with: self)
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        // Always initialize and retain a strong reference
 | 
				
			||||||
 | 
					        if WCSession.isSupported() {
 | 
				
			||||||
 | 
					            AppDelegate.sharedWatchConnectivityService = WatchConnectivityService.shared
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            print("[iOS] WCSession not supported on this device.")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
 | 
					        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final class WatchConnectivityService: NSObject, WCSessionDelegate {
 | 
				
			||||||
 | 
					    static let shared = WatchConnectivityService()
 | 
				
			||||||
 | 
					    private let session: WCSession = .default
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private override init() {
 | 
				
			||||||
 | 
					        super.init()
 | 
				
			||||||
 | 
					        print("[iOS] Activating WCSession...")
 | 
				
			||||||
 | 
					        session.delegate = self
 | 
				
			||||||
 | 
					        session.activate()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // MARK: - WCSessionDelegate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
 | 
				
			||||||
 | 
					        if let error = error {
 | 
				
			||||||
 | 
					            print("[iOS] WCSession activation failed: \(error.localizedDescription)")
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            print("[iOS] WCSession activated with state: \(activationState.rawValue)")
 | 
				
			||||||
 | 
					            if activationState == .activated {
 | 
				
			||||||
 | 
					                sendDataToWatch()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func sessionDidBecomeInactive(_ session: WCSession) {}
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func sessionDidDeactivate(_ session: WCSession) {
 | 
				
			||||||
 | 
					        session.activate()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
 | 
				
			||||||
 | 
					        print("[iOS] Received message: \(message)")
 | 
				
			||||||
 | 
					        if let request = message["request"] as? String, request == "data" {
 | 
				
			||||||
 | 
					            let token = UserDefaults.standard.getFlutterToken()
 | 
				
			||||||
 | 
					            let serverUrl = UserDefaults.standard.getServerUrl()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            var data: [String: Any] = ["serverUrl": serverUrl ?? ""]
 | 
				
			||||||
 | 
					            if let token = token {
 | 
				
			||||||
 | 
					                data["token"] = token
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            print("[iOS] Replying with data: \(data)")
 | 
				
			||||||
 | 
					            replyHandler(data)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func sendDataToWatch() {
 | 
				
			||||||
 | 
					        guard session.activationState == .activated else {
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let token = UserDefaults.standard.getFlutterToken()
 | 
				
			||||||
 | 
					        let serverUrl = UserDefaults.standard.getServerUrl()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var data: [String: Any] = ["serverUrl": serverUrl ?? ""]
 | 
				
			||||||
 | 
					        if let token = token {
 | 
				
			||||||
 | 
					            data["token"] = token
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            try session.updateApplicationContext(data)
 | 
				
			||||||
 | 
					            print("[iOS] Sent application context: \(data)")
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            print("[iOS] Failed to send application context: \(error.localizedDescription)")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1,334 @@
 | 
				
			|||||||
{"images":[{"size":"20x20","idiom":"universal","filename":"Icon-App-20x20@2x.png","scale":"2x","platform":"ios"},{"size":"20x20","idiom":"universal","filename":"Icon-App-20x20@3x.png","scale":"3x","platform":"ios"},{"size":"29x29","idiom":"universal","filename":"Icon-App-29x29@2x.png","scale":"2x","platform":"ios"},{"size":"29x29","idiom":"universal","filename":"Icon-App-29x29@3x.png","scale":"3x","platform":"ios"},{"size":"38x38","idiom":"universal","filename":"Icon-App-38x38@2x.png","scale":"2x","platform":"ios"},{"size":"38x38","idiom":"universal","filename":"Icon-App-38x38@3x.png","scale":"3x","platform":"ios"},{"size":"40x40","idiom":"universal","filename":"Icon-App-40x40@2x.png","scale":"2x","platform":"ios"},{"size":"40x40","idiom":"universal","filename":"Icon-App-40x40@3x.png","scale":"3x","platform":"ios"},{"size":"60x60","idiom":"universal","filename":"Icon-App-60x60@2x.png","scale":"2x","platform":"ios"},{"size":"60x60","idiom":"universal","filename":"Icon-App-60x60@3x.png","scale":"3x","platform":"ios"},{"size":"64x64","idiom":"universal","filename":"Icon-App-64x64@2x.png","scale":"2x","platform":"ios"},{"size":"64x64","idiom":"universal","filename":"Icon-App-64x64@3x.png","scale":"3x","platform":"ios"},{"size":"68x68","idiom":"universal","filename":"Icon-App-68x68@2x.png","scale":"2x","platform":"ios"},{"size":"76x76","idiom":"universal","filename":"Icon-App-76x76@2x.png","scale":"2x","platform":"ios"},{"size":"83.5x83.5","idiom":"universal","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x","platform":"ios"},{"size":"1024x1024","idiom":"universal","filename":"Icon-App-1024x1024@1x.png","scale":"1x","platform":"ios"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"},{"size":"20x20","idiom":"universal","filename":"Icon-App-Dark-20x20@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"20x20","idiom":"universal","filename":"Icon-App-Dark-20x20@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"29x29","idiom":"universal","filename":"Icon-App-Dark-29x29@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"29x29","idiom":"universal","filename":"Icon-App-Dark-29x29@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"38x38","idiom":"universal","filename":"Icon-App-Dark-38x38@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"38x38","idiom":"universal","filename":"Icon-App-Dark-38x38@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"40x40","idiom":"universal","filename":"Icon-App-Dark-40x40@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"40x40","idiom":"universal","filename":"Icon-App-Dark-40x40@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"60x60","idiom":"universal","filename":"Icon-App-Dark-60x60@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"60x60","idiom":"universal","filename":"Icon-App-Dark-60x60@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"64x64","idiom":"universal","filename":"Icon-App-Dark-64x64@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"64x64","idiom":"universal","filename":"Icon-App-Dark-64x64@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"68x68","idiom":"universal","filename":"Icon-App-Dark-68x68@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"76x76","idiom":"universal","filename":"Icon-App-Dark-76x76@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"83.5x83.5","idiom":"universal","filename":"Icon-App-Dark-83.5x83.5@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]},{"size":"1024x1024","idiom":"universal","filename":"Icon-App-Dark-1024x1024@1x.png","scale":"1x","platform":"ios","appearances":[{"appearance":"luminosity","value":"dark"}]}],"info":{"version":1,"author":"xcode"}}
 | 
					{
 | 
				
			||||||
 | 
					  "images" : [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-20x20@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "20x20"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-20x20@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "20x20"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-29x29@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "29x29"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-29x29@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "29x29"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-38x38@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "38x38"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-38x38@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "38x38"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-40x40@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "40x40"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-40x40@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "40x40"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-60x60@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "60x60"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-60x60@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "60x60"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-64x64@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "64x64"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-64x64@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "64x64"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-68x68@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "68x68"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-76x76@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "76x76"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-83.5x83.5@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "83.5x83.5"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-1024x1024@1x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "1x",
 | 
				
			||||||
 | 
					      "size" : "1024x1024"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-20x20@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "20x20"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-20x20@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "20x20"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-29x29@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "29x29"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-29x29@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "29x29"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-38x38@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "38x38"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-38x38@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "38x38"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-40x40@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "40x40"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-40x40@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "40x40"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-60x60@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "60x60"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-60x60@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "60x60"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-64x64@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "64x64"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-64x64@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "64x64"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-68x68@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "68x68"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-76x76@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "76x76"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-83.5x83.5@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "83.5x83.5"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "appearances" : [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "appearance" : "luminosity",
 | 
				
			||||||
 | 
					          "value" : "dark"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-Dark-1024x1024@1x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "1x",
 | 
				
			||||||
 | 
					      "size" : "1024x1024"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "Icon-App-1024x1024@1x.png",
 | 
				
			||||||
 | 
					      "idiom" : "ios-marketing",
 | 
				
			||||||
 | 
					      "scale" : "1x",
 | 
				
			||||||
 | 
					      "size" : "1024x1024"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "info" : {
 | 
				
			||||||
 | 
					    "author" : "xcode",
 | 
				
			||||||
 | 
					    "version" : 1
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 295 B  | 
| 
		 Before Width: | Height: | Size: 282 B  | 
| 
		 Before Width: | Height: | Size: 406 B  | 
| 
		 Before Width: | Height: | Size: 762 B  | 
@@ -36,6 +36,14 @@
 | 
				
			|||||||
				<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
 | 
									<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
 | 
				
			||||||
			</array>
 | 
								</array>
 | 
				
			||||||
		</dict>
 | 
							</dict>
 | 
				
			||||||
 | 
							<dict>
 | 
				
			||||||
 | 
								<key>CFBundleTypeRole</key>
 | 
				
			||||||
 | 
								<string>Viewer</string>
 | 
				
			||||||
 | 
								<key>CFBundleURLSchemes</key>
 | 
				
			||||||
 | 
								<array>
 | 
				
			||||||
 | 
									<string>solian</string>
 | 
				
			||||||
 | 
								</array>
 | 
				
			||||||
 | 
							</dict>
 | 
				
			||||||
	</array>
 | 
						</array>
 | 
				
			||||||
	<key>CFBundleVersion</key>
 | 
						<key>CFBundleVersion</key>
 | 
				
			||||||
	<string>$(FLUTTER_BUILD_NUMBER)</string>
 | 
						<string>$(FLUTTER_BUILD_NUMBER)</string>
 | 
				
			||||||
@@ -87,6 +95,8 @@
 | 
				
			|||||||
		<string>UIInterfaceOrientationLandscapeRight</string>
 | 
							<string>UIInterfaceOrientationLandscapeRight</string>
 | 
				
			||||||
		<string>UIInterfaceOrientationPortrait</string>
 | 
							<string>UIInterfaceOrientationPortrait</string>
 | 
				
			||||||
	</array>
 | 
						</array>
 | 
				
			||||||
 | 
						<key>WKCompanionAppBundleIdentifier</key>
 | 
				
			||||||
 | 
						<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
 | 
				
			||||||
	<key>UISupportedInterfaceOrientations~ipad</key>
 | 
						<key>UISupportedInterfaceOrientations~ipad</key>
 | 
				
			||||||
	<array>
 | 
						<array>
 | 
				
			||||||
		<string>UIInterfaceOrientationLandscapeLeft</string>
 | 
							<string>UIInterfaceOrientationLandscapeLeft</string>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@
 | 
				
			|||||||
import Foundation
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getAttachmentUrl(for identifier: String) -> String {
 | 
					func getAttachmentUrl(for identifier: String) -> String {
 | 
				
			||||||
    let serverBaseUrl = "https://api.solian.app"
 | 
					    let serverBaseUrl = UserDefaults.standard.getServerUrl()
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    return identifier.starts(with: "http") ? identifier : "\(serverBaseUrl)/drive/files/\(identifier)"
 | 
					    return identifier.starts(with: "http") ? identifier : "\(serverBaseUrl)/drive/files/\(identifier)"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,6 @@ extension UserDefaults {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    func getServerUrl(forKey key: String = "app_server_url") -> String {
 | 
					    func getServerUrl(forKey key: String = "app_server_url") -> String {
 | 
				
			||||||
        return self.getFlutterValue(forKey: key) ?? "https://nt.solian.app"
 | 
					        return self.getFlutterValue(forKey: key) ?? "https://api.solian.app"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "colors" : [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "color" : {
 | 
				
			||||||
 | 
					        "platform" : "universal",
 | 
				
			||||||
 | 
					        "reference" : "systemIndigoColor"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "idiom" : "universal"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "info" : {
 | 
				
			||||||
 | 
					    "author" : "xcode",
 | 
				
			||||||
 | 
					    "version" : 1
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,318 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "images" : [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-20x20@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "20x20"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-20x20@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "20x20"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-29x29@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "29x29"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-29x29@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "29x29"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-38x38@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "38x38"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-38x38@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "38x38"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-40x40@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "40x40"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-40x40@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "40x40"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-60x60@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "60x60"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-60x60@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "60x60"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-64x64@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "64x64"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-64x64@3x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "3x",
 | 
				
			||||||
 | 
					      "size" : "64x64"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-68x68@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "68x68"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-76x76@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "76x76"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-83.5x83.5@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "83.5x83.5"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-ios-1024x1024.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "ios",
 | 
				
			||||||
 | 
					      "size" : "1024x1024"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-mac-16x16.png",
 | 
				
			||||||
 | 
					      "idiom" : "mac",
 | 
				
			||||||
 | 
					      "scale" : "1x",
 | 
				
			||||||
 | 
					      "size" : "16x16"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-mac-16x16@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "mac",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "16x16"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-mac-32x32.png",
 | 
				
			||||||
 | 
					      "idiom" : "mac",
 | 
				
			||||||
 | 
					      "scale" : "1x",
 | 
				
			||||||
 | 
					      "size" : "32x32"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-mac-32x32@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "mac",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "32x32"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-mac-128x128.png",
 | 
				
			||||||
 | 
					      "idiom" : "mac",
 | 
				
			||||||
 | 
					      "scale" : "1x",
 | 
				
			||||||
 | 
					      "size" : "128x128"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-mac-128x128@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "mac",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "128x128"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-mac-256x256.png",
 | 
				
			||||||
 | 
					      "idiom" : "mac",
 | 
				
			||||||
 | 
					      "scale" : "1x",
 | 
				
			||||||
 | 
					      "size" : "256x256"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-mac-256x256@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "mac",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "256x256"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-mac-512x512.png",
 | 
				
			||||||
 | 
					      "idiom" : "mac",
 | 
				
			||||||
 | 
					      "scale" : "1x",
 | 
				
			||||||
 | 
					      "size" : "512x512"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-mac-512x512@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "mac",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "512x512"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-22x22@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "22x22"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-24x24@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "24x24"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-27.5x27.5@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "27.5x27.5"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-29x29@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "29x29"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-30x30@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "30x30"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-32x32@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "32x32"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-33x33@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "33x33"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-40x40@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "40x40"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-43.5x43.5@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "43.5x43.5"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-44x44@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "44x44"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-46x46@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "46x46"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-50x50@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "50x50"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-51x51@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "51x51"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-54x54@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "54x54"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-86x86@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "86x86"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-98x98@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "98x98"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-108x108@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "108x108"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-117x117@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "117x117"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-129x129@2x.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "scale" : "2x",
 | 
				
			||||||
 | 
					      "size" : "129x129"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon-watchos-1024x1024.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "platform" : "watchos",
 | 
				
			||||||
 | 
					      "size" : "1024x1024"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "info" : {
 | 
				
			||||||
 | 
					    "author" : "xcode",
 | 
				
			||||||
 | 
					    "version" : 1
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 45 KiB  | 
| 
		 After Width: | Height: | Size: 1.5 KiB  | 
| 
		 After Width: | Height: | Size: 2.5 KiB  | 
| 
		 After Width: | Height: | Size: 2.4 KiB  | 
| 
		 After Width: | Height: | Size: 4.1 KiB  | 
| 
		 After Width: | Height: | Size: 3.4 KiB  | 
| 
		 After Width: | Height: | Size: 5.7 KiB  | 
| 
		 After Width: | Height: | Size: 3.6 KiB  | 
| 
		 After Width: | Height: | Size: 6.1 KiB  | 
| 
		 After Width: | Height: | Size: 6.1 KiB  | 
| 
		 After Width: | Height: | Size: 9.6 KiB  | 
| 
		 After Width: | Height: | Size: 6.6 KiB  | 
| 
		 After Width: | Height: | Size: 10 KiB  | 
| 
		 After Width: | Height: | Size: 7.0 KiB  | 
| 
		 After Width: | Height: | Size: 7.8 KiB  | 
| 
		 After Width: | Height: | Size: 8.8 KiB  | 
| 
		 After Width: | Height: | Size: 6.6 KiB  | 
| 
		 After Width: | Height: | Size: 14 KiB  | 
| 
		 After Width: | Height: | Size: 473 B  | 
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 After Width: | Height: | Size: 14 KiB  | 
| 
		 After Width: | Height: | Size: 30 KiB  | 
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 After Width: | Height: | Size: 2.7 KiB  | 
| 
		 After Width: | Height: | Size: 30 KiB  | 
| 
		 After Width: | Height: | Size: 45 KiB  | 
| 
		 After Width: | Height: | Size: 45 KiB  | 
| 
		 After Width: | Height: | Size: 12 KiB  | 
| 
		 After Width: | Height: | Size: 13 KiB  | 
| 
		 After Width: | Height: | Size: 14 KiB  | 
| 
		 After Width: | Height: | Size: 1.7 KiB  | 
| 
		 After Width: | Height: | Size: 1.9 KiB  | 
| 
		 After Width: | Height: | Size: 2.3 KiB  | 
| 
		 After Width: | Height: | Size: 2.4 KiB  | 
| 
		 After Width: | Height: | Size: 2.5 KiB  | 
| 
		 After Width: | Height: | Size: 2.7 KiB  | 
| 
		 After Width: | Height: | Size: 2.9 KiB  | 
| 
		 After Width: | Height: | Size: 3.6 KiB  | 
| 
		 After Width: | Height: | Size: 4.1 KiB  | 
| 
		 After Width: | Height: | Size: 4.2 KiB  | 
| 
		 After Width: | Height: | Size: 4.4 KiB  | 
| 
		 After Width: | Height: | Size: 4.9 KiB  | 
| 
		 After Width: | Height: | Size: 5.0 KiB  | 
| 
		 After Width: | Height: | Size: 5.3 KiB  | 
| 
		 After Width: | Height: | Size: 9.1 KiB  | 
| 
		 After Width: | Height: | Size: 10 KiB  | 
							
								
								
									
										6
									
								
								ios/Solian Watch App/Assets.xcassets/Contents.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "info" : {
 | 
				
			||||||
 | 
					    "author" : "xcode",
 | 
				
			||||||
 | 
					    "version" : 1
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								ios/Solian Watch App/Assets.xcassets/Logo.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "images" : [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "filename" : "icon.png",
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "scale" : "1x"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "scale" : "2x"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "idiom" : "universal",
 | 
				
			||||||
 | 
					      "scale" : "3x"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "info" : {
 | 
				
			||||||
 | 
					    "author" : "xcode",
 | 
				
			||||||
 | 
					    "version" : 1
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								ios/Solian Watch App/Assets.xcassets/Logo.imageset/icon.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 70 KiB  | 
							
								
								
									
										50
									
								
								ios/Solian Watch App/ContentView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  ContentView.swift
 | 
				
			||||||
 | 
					//  WatchRunner Watch App
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by LittleSheep on 2025/10/28.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The root view of the app.
 | 
				
			||||||
 | 
					struct ContentView: View {
 | 
				
			||||||
 | 
					    @StateObject private var appState = AppState()
 | 
				
			||||||
 | 
					    @State private var selection: Panel? = .explore
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    enum Panel: Hashable {
 | 
				
			||||||
 | 
					        case explore
 | 
				
			||||||
 | 
					        case chat
 | 
				
			||||||
 | 
					        case notifications
 | 
				
			||||||
 | 
					        case account
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    var body: some View {
 | 
				
			||||||
 | 
					        NavigationSplitView {
 | 
				
			||||||
 | 
					            List(selection: $selection) {
 | 
				
			||||||
 | 
					                AppInfoHeaderView()
 | 
				
			||||||
 | 
					                    .listRowBackground(Color.clear)
 | 
				
			||||||
 | 
					                    .environmentObject(appState)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                Label("Explore", systemImage: "globe.fill").tag(Panel.explore)
 | 
				
			||||||
 | 
					                Label("Chat", systemImage: "message.fill").tag(Panel.chat)
 | 
				
			||||||
 | 
					                Label("Notifications", systemImage: "bell.fill").tag(Panel.notifications)
 | 
				
			||||||
 | 
					                Label("Account", systemImage: "person.circle.fill").tag(Panel.account)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            .listStyle(.automatic)
 | 
				
			||||||
 | 
					        } detail: {
 | 
				
			||||||
 | 
					            switch selection {
 | 
				
			||||||
 | 
					            case .explore:
 | 
				
			||||||
 | 
					                ExploreView().environmentObject(appState)
 | 
				
			||||||
 | 
					            case .chat:
 | 
				
			||||||
 | 
					                ChatView().environmentObject(appState)
 | 
				
			||||||
 | 
					            case .notifications:
 | 
				
			||||||
 | 
					                NotificationView().environmentObject(appState)
 | 
				
			||||||
 | 
					            case .account:
 | 
				
			||||||
 | 
					                AccountView().environmentObject(appState)
 | 
				
			||||||
 | 
					            case .none:
 | 
				
			||||||
 | 
					                Text("Select a panel")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										88
									
								
								ios/Solian Watch App/Layouts/FlowLayout.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  FlowLayout.swift
 | 
				
			||||||
 | 
					//  WatchRunner Watch App
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by LittleSheep on 2025/10/29.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MARK: - Custom Layouts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct FlowLayout: Layout {
 | 
				
			||||||
 | 
					    var alignment: HorizontalAlignment = .leading
 | 
				
			||||||
 | 
					    var spacing: CGFloat = 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
 | 
				
			||||||
 | 
					        let containerWidth = proposal.width ?? 0
 | 
				
			||||||
 | 
					        let sizes = subviews.map { $0.sizeThatFits(.unspecified) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var currentX: CGFloat = 0
 | 
				
			||||||
 | 
					        var currentY: CGFloat = 0
 | 
				
			||||||
 | 
					        var lineHeight: CGFloat = 0
 | 
				
			||||||
 | 
					        var totalHeight: CGFloat = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for size in sizes {
 | 
				
			||||||
 | 
					            if currentX + size.width > containerWidth {
 | 
				
			||||||
 | 
					                // New line
 | 
				
			||||||
 | 
					                currentX = 0
 | 
				
			||||||
 | 
					                currentY += lineHeight + spacing
 | 
				
			||||||
 | 
					                totalHeight = currentY + size.height
 | 
				
			||||||
 | 
					                lineHeight = 0
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            currentX += size.width + spacing
 | 
				
			||||||
 | 
					            lineHeight = max(lineHeight, size.height)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        totalHeight = currentY + lineHeight
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return CGSize(width: containerWidth, height: totalHeight)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
 | 
				
			||||||
 | 
					        let containerWidth = bounds.width
 | 
				
			||||||
 | 
					        let sizes = subviews.map { $0.sizeThatFits(.unspecified) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var currentX: CGFloat = 0
 | 
				
			||||||
 | 
					        var currentY: CGFloat = 0
 | 
				
			||||||
 | 
					        var lineHeight: CGFloat = 0
 | 
				
			||||||
 | 
					        var lineElements: [(offset: Int, size: CGSize)] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        func placeLine() {
 | 
				
			||||||
 | 
					            let lineWidth = lineElements.map { $0.size.width }.reduce(0, +) + CGFloat(lineElements.count - 1) * spacing
 | 
				
			||||||
 | 
					            var startX: CGFloat = 0
 | 
				
			||||||
 | 
					            switch alignment {
 | 
				
			||||||
 | 
					            case .leading:
 | 
				
			||||||
 | 
					                startX = bounds.minX
 | 
				
			||||||
 | 
					            case .center:
 | 
				
			||||||
 | 
					                startX = bounds.minX + (containerWidth - lineWidth) / 2
 | 
				
			||||||
 | 
					            case .trailing:
 | 
				
			||||||
 | 
					                startX = bounds.maxX - lineWidth
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                startX = bounds.minX
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var xOffset = startX
 | 
				
			||||||
 | 
					            for (offset, size) in lineElements {
 | 
				
			||||||
 | 
					                subviews[offset].place(at: CGPoint(x: xOffset, y: bounds.minY + currentY), proposal: ProposedViewSize(size)) // Use bounds.minY + currentY
 | 
				
			||||||
 | 
					                xOffset += size.width + spacing
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            lineElements.removeAll() // Clear elements for the next line
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (offset, size) in sizes.enumerated() {
 | 
				
			||||||
 | 
					            if currentX + size.width > containerWidth && !lineElements.isEmpty {
 | 
				
			||||||
 | 
					                // New line
 | 
				
			||||||
 | 
					                placeLine()
 | 
				
			||||||
 | 
					                currentX = 0
 | 
				
			||||||
 | 
					                currentY += lineHeight + spacing
 | 
				
			||||||
 | 
					                lineHeight = 0
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            lineElements.append((offset, size))
 | 
				
			||||||
 | 
					            currentX += size.width + spacing
 | 
				
			||||||
 | 
					            lineHeight = max(lineHeight, size.height)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        placeLine() // Place the last line
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										365
									
								
								ios/Solian Watch App/Models/Models.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,365 @@
 | 
				
			|||||||
 | 
					//  Models.swift
 | 
				
			||||||
 | 
					//  WatchRunner Watch App
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by LittleSheep on 2025/10/29.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MARK: - Models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct AppToken: Codable {
 | 
				
			||||||
 | 
					    let token: String
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnActivity: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let type: String
 | 
				
			||||||
 | 
					    let data: ActivityData?
 | 
				
			||||||
 | 
					    let createdAt: Date
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum ActivityData: Codable {
 | 
				
			||||||
 | 
					    case post(SnPost)
 | 
				
			||||||
 | 
					    case discovery(DiscoveryData)
 | 
				
			||||||
 | 
					    case unknown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init(from decoder: Decoder) throws {
 | 
				
			||||||
 | 
					        let container = try decoder.singleValueContainer()
 | 
				
			||||||
 | 
					        if let post = try? container.decode(SnPost.self) {
 | 
				
			||||||
 | 
					            self = .post(post)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if let discoveryData = try? container.decode(DiscoveryData.self) {
 | 
				
			||||||
 | 
					            self = .discovery(discoveryData)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self = .unknown
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func encode(to encoder: Encoder) throws {
 | 
				
			||||||
 | 
					        // Not needed for decoding
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnPost: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let title: String?
 | 
				
			||||||
 | 
					    let content: String?
 | 
				
			||||||
 | 
					    let publisher: SnPublisher
 | 
				
			||||||
 | 
					    let attachments: [SnCloudFile]
 | 
				
			||||||
 | 
					    let tags: [SnPostTag]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct DiscoveryData: Codable {
 | 
				
			||||||
 | 
					    let items: [DiscoveryItem]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct DiscoveryItem: Codable, Identifiable {
 | 
				
			||||||
 | 
					    var id = UUID()
 | 
				
			||||||
 | 
					    let type: String
 | 
				
			||||||
 | 
					    let data: DiscoveryItemData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum CodingKeys: String, CodingKey {
 | 
				
			||||||
 | 
					        case type, data
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum DiscoveryItemData: Codable {
 | 
				
			||||||
 | 
					    case realm(SnRealm)
 | 
				
			||||||
 | 
					    case publisher(SnPublisher)
 | 
				
			||||||
 | 
					    case article(SnWebArticle)
 | 
				
			||||||
 | 
					    case unknown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init(from decoder: Decoder) throws {
 | 
				
			||||||
 | 
					        let container = try decoder.singleValueContainer()
 | 
				
			||||||
 | 
					        if let realm = try? container.decode(SnRealm.self) {
 | 
				
			||||||
 | 
					            self = .realm(realm)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if let publisher = try? container.decode(SnPublisher.self) {
 | 
				
			||||||
 | 
					            self = .publisher(publisher)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if let article = try? container.decode(SnWebArticle.self) {
 | 
				
			||||||
 | 
					            self = .article(article)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self = .unknown
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func encode(to encoder: Encoder) throws {
 | 
				
			||||||
 | 
					        // Not needed for decoding
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnRealm: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let name: String
 | 
				
			||||||
 | 
					    let description: String?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnPublisher: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let name: String
 | 
				
			||||||
 | 
					    let nick: String?
 | 
				
			||||||
 | 
					    let description: String?
 | 
				
			||||||
 | 
					    let picture: SnCloudFile?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnCloudFile: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let mimeType: String?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnPostTag: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let slug: String
 | 
				
			||||||
 | 
					    let name: String?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnWebArticle: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let title: String
 | 
				
			||||||
 | 
					    let url: String
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnNotification: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let topic: String
 | 
				
			||||||
 | 
					    let title: String
 | 
				
			||||||
 | 
					    let subtitle: String
 | 
				
			||||||
 | 
					    let content: String
 | 
				
			||||||
 | 
					    let meta: [String: AnyCodable]?
 | 
				
			||||||
 | 
					    let priority: Int
 | 
				
			||||||
 | 
					    let viewedAt: Date?
 | 
				
			||||||
 | 
					    let accountId: String
 | 
				
			||||||
 | 
					    let createdAt: Date
 | 
				
			||||||
 | 
					    let updatedAt: Date
 | 
				
			||||||
 | 
					    let deletedAt: Date?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum CodingKeys: String, CodingKey {
 | 
				
			||||||
 | 
					        case id
 | 
				
			||||||
 | 
					        case topic
 | 
				
			||||||
 | 
					        case title
 | 
				
			||||||
 | 
					        case subtitle
 | 
				
			||||||
 | 
					        case content
 | 
				
			||||||
 | 
					        case meta
 | 
				
			||||||
 | 
					        case priority
 | 
				
			||||||
 | 
					        case viewedAt = "viewedAt"
 | 
				
			||||||
 | 
					        case accountId = "accountId"
 | 
				
			||||||
 | 
					        case createdAt = "createdAt"
 | 
				
			||||||
 | 
					        case updatedAt = "updatedAt"
 | 
				
			||||||
 | 
					        case deletedAt = "deletedAt"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct AnyCodable: Codable {
 | 
				
			||||||
 | 
					    let value: Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init(_ value: Any) {
 | 
				
			||||||
 | 
					        self.value = value
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init(from decoder: Decoder) throws {
 | 
				
			||||||
 | 
					        let container = try decoder.singleValueContainer()
 | 
				
			||||||
 | 
					        if let intValue = try? container.decode(Int.self) {
 | 
				
			||||||
 | 
					            value = intValue
 | 
				
			||||||
 | 
					        } else if let doubleValue = try? container.decode(Double.self) {
 | 
				
			||||||
 | 
					            value = doubleValue
 | 
				
			||||||
 | 
					        } else if let boolValue = try? container.decode(Bool.self) {
 | 
				
			||||||
 | 
					            value = boolValue
 | 
				
			||||||
 | 
					        } else if let stringValue = try? container.decode(String.self) {
 | 
				
			||||||
 | 
					            value = stringValue
 | 
				
			||||||
 | 
					        } else if let arrayValue = try? container.decode([AnyCodable].self) {
 | 
				
			||||||
 | 
					            value = arrayValue
 | 
				
			||||||
 | 
					        } else if let dictValue = try? container.decode([String: AnyCodable].self) {
 | 
				
			||||||
 | 
					            value = dictValue
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            value = NSNull()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func encode(to encoder: Encoder) throws {
 | 
				
			||||||
 | 
					        var container = encoder.singleValueContainer()
 | 
				
			||||||
 | 
					        switch value {
 | 
				
			||||||
 | 
					        case let intValue as Int:
 | 
				
			||||||
 | 
					            try container.encode(intValue)
 | 
				
			||||||
 | 
					        case let doubleValue as Double:
 | 
				
			||||||
 | 
					            try container.encode(doubleValue)
 | 
				
			||||||
 | 
					        case let boolValue as Bool:
 | 
				
			||||||
 | 
					            try container.encode(boolValue)
 | 
				
			||||||
 | 
					        case let stringValue as String:
 | 
				
			||||||
 | 
					            try container.encode(stringValue)
 | 
				
			||||||
 | 
					        case let arrayValue as [AnyCodable]:
 | 
				
			||||||
 | 
					            try container.encode(arrayValue)
 | 
				
			||||||
 | 
					        case let dictValue as [String: AnyCodable]:
 | 
				
			||||||
 | 
					            try container.encode(dictValue)
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            try container.encodeNil()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct NotificationResponse {
 | 
				
			||||||
 | 
					    let notifications: [SnNotification]
 | 
				
			||||||
 | 
					    let total: Int
 | 
				
			||||||
 | 
					    let hasMore: Bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ActivityResponse {
 | 
				
			||||||
 | 
					    let activities: [SnActivity]
 | 
				
			||||||
 | 
					    let hasMore: Bool
 | 
				
			||||||
 | 
					    let nextCursor: String?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnAccount: Codable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let name: String
 | 
				
			||||||
 | 
					    let nick: String
 | 
				
			||||||
 | 
					    let profile: SnUserProfile
 | 
				
			||||||
 | 
					    let createdAt: Date
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnUserProfile: Codable {
 | 
				
			||||||
 | 
					    let bio: String?
 | 
				
			||||||
 | 
					    let picture: SnCloudFile?
 | 
				
			||||||
 | 
					    let background: SnCloudFile?
 | 
				
			||||||
 | 
					    let level: Int
 | 
				
			||||||
 | 
					    let experience: Int
 | 
				
			||||||
 | 
					    let levelingProgress: Double
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnAccountStatus: Codable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let attitude: Int
 | 
				
			||||||
 | 
					    let isOnline: Bool
 | 
				
			||||||
 | 
					    let isInvisible: Bool
 | 
				
			||||||
 | 
					    let isNotDisturb: Bool
 | 
				
			||||||
 | 
					    let isCustomized: Bool
 | 
				
			||||||
 | 
					    let label: String
 | 
				
			||||||
 | 
					    let meta: [String: AnyCodable]?
 | 
				
			||||||
 | 
					    let clearedAt: Date?
 | 
				
			||||||
 | 
					    let accountId: String
 | 
				
			||||||
 | 
					    let createdAt: Date
 | 
				
			||||||
 | 
					    let updatedAt: Date
 | 
				
			||||||
 | 
					    let deletedAt: Date?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MARK: - Chat Models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnChatRoom: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let name: String?
 | 
				
			||||||
 | 
					    let description: String?
 | 
				
			||||||
 | 
					    let type: Int
 | 
				
			||||||
 | 
					    let isPublic: Bool
 | 
				
			||||||
 | 
					    let isCommunity: Bool
 | 
				
			||||||
 | 
					    let picture: SnCloudFile?
 | 
				
			||||||
 | 
					    let background: SnCloudFile?
 | 
				
			||||||
 | 
					    let realmId: String?
 | 
				
			||||||
 | 
					    let realm: SnRealm?
 | 
				
			||||||
 | 
					    let createdAt: Date
 | 
				
			||||||
 | 
					    let updatedAt: Date
 | 
				
			||||||
 | 
					    let deletedAt: Date?
 | 
				
			||||||
 | 
					    let members: [SnChatMember]?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnChatMessage: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let type: String
 | 
				
			||||||
 | 
					    let content: String?
 | 
				
			||||||
 | 
					    let nonce: String?
 | 
				
			||||||
 | 
					    let meta: [String: AnyCodable]
 | 
				
			||||||
 | 
					    let membersMentioned: [String]?
 | 
				
			||||||
 | 
					    let editedAt: Date?
 | 
				
			||||||
 | 
					    let attachments: [SnCloudFile]
 | 
				
			||||||
 | 
					    let reactions: [SnChatReaction]
 | 
				
			||||||
 | 
					    let repliedMessageId: String?
 | 
				
			||||||
 | 
					    let forwardedMessageId: String?
 | 
				
			||||||
 | 
					    let senderId: String
 | 
				
			||||||
 | 
					    let sender: SnChatMember
 | 
				
			||||||
 | 
					    let chatRoomId: String
 | 
				
			||||||
 | 
					    let createdAt: Date
 | 
				
			||||||
 | 
					    let updatedAt: Date
 | 
				
			||||||
 | 
					    let deletedAt: Date?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum CodingKeys: String, CodingKey {
 | 
				
			||||||
 | 
					        case id, type, content, nonce, meta, membersMentioned, editedAt, attachments, reactions, repliedMessageId, forwardedMessageId, senderId, sender, chatRoomId, createdAt, updatedAt, deletedAt
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init(from decoder: Decoder) throws {
 | 
				
			||||||
 | 
					        let container = try decoder.container(keyedBy: CodingKeys.self)
 | 
				
			||||||
 | 
					        id = try container.decode(String.self, forKey: .id)
 | 
				
			||||||
 | 
					        type = try container.decode(String.self, forKey: .type)
 | 
				
			||||||
 | 
					        content = try container.decodeIfPresent(String.self, forKey: .content)
 | 
				
			||||||
 | 
					        nonce = try container.decodeIfPresent(String.self, forKey: .nonce)
 | 
				
			||||||
 | 
					        meta = try container.decode([String: AnyCodable].self, forKey: .meta)
 | 
				
			||||||
 | 
					        membersMentioned = try container.decodeIfPresent([String].self, forKey: .membersMentioned) ?? []
 | 
				
			||||||
 | 
					        editedAt = try container.decodeIfPresent(Date.self, forKey: .editedAt)
 | 
				
			||||||
 | 
					        attachments = try container.decode([SnCloudFile].self, forKey: .attachments)
 | 
				
			||||||
 | 
					        reactions = try container.decode([SnChatReaction].self, forKey: .reactions)
 | 
				
			||||||
 | 
					        repliedMessageId = try container.decodeIfPresent(String.self, forKey: .repliedMessageId)
 | 
				
			||||||
 | 
					        forwardedMessageId = try container.decodeIfPresent(String.self, forKey: .forwardedMessageId)
 | 
				
			||||||
 | 
					        senderId = try container.decode(String.self, forKey: .senderId)
 | 
				
			||||||
 | 
					        sender = try container.decode(SnChatMember.self, forKey: .sender)
 | 
				
			||||||
 | 
					        chatRoomId = try container.decode(String.self, forKey: .chatRoomId)
 | 
				
			||||||
 | 
					        createdAt = try container.decode(Date.self, forKey: .createdAt)
 | 
				
			||||||
 | 
					        updatedAt = try container.decode(Date.self, forKey: .updatedAt)
 | 
				
			||||||
 | 
					        deletedAt = try container.decodeIfPresent(Date.self, forKey: .deletedAt)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnChatReaction: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let messageId: String
 | 
				
			||||||
 | 
					    let senderId: String
 | 
				
			||||||
 | 
					    let sender: SnChatMember
 | 
				
			||||||
 | 
					    let symbol: String
 | 
				
			||||||
 | 
					    let attitude: Int
 | 
				
			||||||
 | 
					    let createdAt: Date
 | 
				
			||||||
 | 
					    let updatedAt: Date
 | 
				
			||||||
 | 
					    let deletedAt: Date?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnChatMember: Codable, Identifiable {
 | 
				
			||||||
 | 
					    let id: String
 | 
				
			||||||
 | 
					    let chatRoomId: String
 | 
				
			||||||
 | 
					    let chatRoom: SnChatRoom?
 | 
				
			||||||
 | 
					    let accountId: String
 | 
				
			||||||
 | 
					    let account: SnAccount
 | 
				
			||||||
 | 
					    let nick: String?
 | 
				
			||||||
 | 
					    let role: Int
 | 
				
			||||||
 | 
					    let notify: Int
 | 
				
			||||||
 | 
					    let joinedAt: Date?
 | 
				
			||||||
 | 
					    let breakUntil: Date?
 | 
				
			||||||
 | 
					    let timeoutUntil: Date?
 | 
				
			||||||
 | 
					    let isBot: Bool
 | 
				
			||||||
 | 
					    let status: SnAccountStatus?
 | 
				
			||||||
 | 
					    let createdAt: Date
 | 
				
			||||||
 | 
					    let updatedAt: Date
 | 
				
			||||||
 | 
					    let deletedAt: Date?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SnChatSummary: Codable {
 | 
				
			||||||
 | 
					    let unreadCount: Int
 | 
				
			||||||
 | 
					    let lastMessage: SnChatMessage?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ChatRoomsResponse {
 | 
				
			||||||
 | 
					    let rooms: [SnChatRoom]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ChatInvitesResponse {
 | 
				
			||||||
 | 
					    let invites: [SnChatMember]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct MessageSyncResponse: Codable {
 | 
				
			||||||
 | 
					    let messages: [SnChatMessage]
 | 
				
			||||||
 | 
					    let currentTimestamp: Date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum CodingKeys: String, CodingKey {
 | 
				
			||||||
 | 
					        case messages
 | 
				
			||||||
 | 
					        case currentTimestamp = "current_timestamp"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										95
									
								
								ios/Solian Watch App/Services/ImageLoader.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  ImageLoader.swift
 | 
				
			||||||
 | 
					//  WatchRunner Watch App
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by LittleSheep on 2025/10/29.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					import Kingfisher
 | 
				
			||||||
 | 
					import KingfisherWebP
 | 
				
			||||||
 | 
					import Combine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MARK: - Image Loader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@MainActor
 | 
				
			||||||
 | 
					class ImageLoader: ObservableObject {
 | 
				
			||||||
 | 
					    @Published var image: Image?
 | 
				
			||||||
 | 
					    @Published var errorMessage: String?
 | 
				
			||||||
 | 
					    @Published var isLoading = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var currentTask: DownloadTask?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    deinit {
 | 
				
			||||||
 | 
					        currentTask?.cancel()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func loadImage(from initialUrl: URL, token: String) async {
 | 
				
			||||||
 | 
					        isLoading = true
 | 
				
			||||||
 | 
					        errorMessage = nil
 | 
				
			||||||
 | 
					        image = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Create request modifier for authorization
 | 
				
			||||||
 | 
					        let modifier = AnyModifier { request in
 | 
				
			||||||
 | 
					            var r = request
 | 
				
			||||||
 | 
					            r.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					            r.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					            return r
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Use WebP processor as default since the app seems to handle WebP images
 | 
				
			||||||
 | 
					        let processor = WebPProcessor.default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Use KingfisherManager to retrieve image with caching
 | 
				
			||||||
 | 
					        currentTask = KingfisherManager.shared.retrieveImage(
 | 
				
			||||||
 | 
					            with: initialUrl,
 | 
				
			||||||
 | 
					            options: [
 | 
				
			||||||
 | 
					                .requestModifier(modifier),
 | 
				
			||||||
 | 
					                .processor(processor),
 | 
				
			||||||
 | 
					                .cacheOriginalImage, // Cache the original image data
 | 
				
			||||||
 | 
					                .loadDiskFileSynchronously // Load from disk cache synchronously if available
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        ) { [weak self] result in
 | 
				
			||||||
 | 
					            guard let self = self else { return }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Task { @MainActor in
 | 
				
			||||||
 | 
					                switch result {
 | 
				
			||||||
 | 
					                case .success(let value):
 | 
				
			||||||
 | 
					                    self.image = Image(uiImage: value.image)
 | 
				
			||||||
 | 
					                    self.isLoading = false
 | 
				
			||||||
 | 
					                case .failure(_):
 | 
				
			||||||
 | 
					                    // If WebP processor fails (likely due to format), try with default processor
 | 
				
			||||||
 | 
					                    let defaultProcessor = DefaultImageProcessor.default
 | 
				
			||||||
 | 
					                    self.currentTask = KingfisherManager.shared.retrieveImage(
 | 
				
			||||||
 | 
					                        with: initialUrl,
 | 
				
			||||||
 | 
					                        options: [
 | 
				
			||||||
 | 
					                            .requestModifier(modifier),
 | 
				
			||||||
 | 
					                            .processor(defaultProcessor),
 | 
				
			||||||
 | 
					                            .cacheOriginalImage,
 | 
				
			||||||
 | 
					                            .loadDiskFileSynchronously
 | 
				
			||||||
 | 
					                        ]
 | 
				
			||||||
 | 
					                    ) { [weak self] fallbackResult in
 | 
				
			||||||
 | 
					                        guard let self = self else { return }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        Task { @MainActor in
 | 
				
			||||||
 | 
					                            switch fallbackResult {
 | 
				
			||||||
 | 
					                            case .success(let value):
 | 
				
			||||||
 | 
					                                self.image = Image(uiImage: value.image)
 | 
				
			||||||
 | 
					                            case .failure(let fallbackError):
 | 
				
			||||||
 | 
					                                self.errorMessage = fallbackError.localizedDescription
 | 
				
			||||||
 | 
					                                print("[watchOS] Image loading failed: \(fallbackError.localizedDescription)")
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            self.isLoading = false
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func cancel() {
 | 
				
			||||||
 | 
					        currentTask?.cancel()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										643
									
								
								ios/Solian Watch App/Services/NetworkService.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,643 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  NetworkService.swift
 | 
				
			||||||
 | 
					//  WatchRunner Watch App
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by LittleSheep on 2025/10/29. //
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Combine
 | 
				
			||||||
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MARK: - WebSocket Data Structures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum WebSocketState: Equatable {
 | 
				
			||||||
 | 
					    case connected
 | 
				
			||||||
 | 
					    case connecting
 | 
				
			||||||
 | 
					    case disconnected
 | 
				
			||||||
 | 
					    case serverDown
 | 
				
			||||||
 | 
					    case duplicateDevice
 | 
				
			||||||
 | 
					    case error(String)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Equatable conformance
 | 
				
			||||||
 | 
					    static func == (lhs: WebSocketState, rhs: WebSocketState) -> Bool {
 | 
				
			||||||
 | 
					        switch (lhs, rhs) {
 | 
				
			||||||
 | 
					        case (.connected, .connected),
 | 
				
			||||||
 | 
					            (.connecting, .connecting),
 | 
				
			||||||
 | 
					            (.disconnected, .disconnected),
 | 
				
			||||||
 | 
					            (.serverDown, .serverDown),
 | 
				
			||||||
 | 
					            (.duplicateDevice, .duplicateDevice):
 | 
				
			||||||
 | 
					            return true
 | 
				
			||||||
 | 
					        case let (.error(a), .error(b)):
 | 
				
			||||||
 | 
					            return a == b
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            return false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct WebSocketPacket {
 | 
				
			||||||
 | 
					    let type: String
 | 
				
			||||||
 | 
					    let data: [String: Any]?
 | 
				
			||||||
 | 
					    let endpoint: String?
 | 
				
			||||||
 | 
					    let errorMessage: String?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MARK: - Network Service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NetworkService {
 | 
				
			||||||
 | 
					    private let session: URLSession
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    init() {
 | 
				
			||||||
 | 
					        let config = URLSessionConfiguration.ephemeral
 | 
				
			||||||
 | 
					        config.waitsForConnectivity = true
 | 
				
			||||||
 | 
					        session = URLSession(configuration: config)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Add a serial queue for WebSocket operations
 | 
				
			||||||
 | 
					    private let webSocketQueue = DispatchQueue(label: "com.solian.websocketQueue")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func fetchActivities(filter: String, cursor: String? = nil, token: String, serverUrl: String) async throws -> ActivityResponse {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        var components = URLComponents(url: baseURL.appendingPathComponent("/sphere/activities"), resolvingAgainstBaseURL: false)!
 | 
				
			||||||
 | 
					        var queryItems = [URLQueryItem(name: "take", value: "20")]
 | 
				
			||||||
 | 
					        if filter.lowercased() != "explore" {
 | 
				
			||||||
 | 
					            queryItems.append(URLQueryItem(name: "filter", value: filter.lowercased()))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if let cursor = cursor {
 | 
				
			||||||
 | 
					            queryItems.append(URLQueryItem(name: "cursor", value: cursor))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        components.queryItems = queryItems
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: components.url!)
 | 
				
			||||||
 | 
					        request.httpMethod = "GET"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, _) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let decoder = JSONDecoder()
 | 
				
			||||||
 | 
					        decoder.dateDecodingStrategy = .iso8601
 | 
				
			||||||
 | 
					        decoder.keyDecodingStrategy = .convertFromSnakeCase
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let activities = try decoder.decode([SnActivity].self, from: data)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let hasMore = (activities.first?.type ?? "empty") != "empty"
 | 
				
			||||||
 | 
					        let nextCursor = activities.isEmpty ? nil : activities.map { $0.createdAt }.min()?.ISO8601Format()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return ActivityResponse(activities: activities, hasMore: hasMore, nextCursor: nextCursor)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func createPost(title: String, content: String, token: String, serverUrl: String) async throws {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let url = baseURL.appendingPathComponent("/sphere/posts")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: url)
 | 
				
			||||||
 | 
					        request.httpMethod = "POST"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let body: [String: Any] = ["title": title, "content": content]
 | 
				
			||||||
 | 
					        request.httpBody = try JSONSerialization.data(withJSONObject: body)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, response) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 201 {
 | 
				
			||||||
 | 
					            let responseBody = String(data: data, encoding: .utf8) ?? ""
 | 
				
			||||||
 | 
					            print("[watchOS] createPost failed with status code: \(httpResponse.statusCode), body: \(responseBody)")
 | 
				
			||||||
 | 
					            throw URLError(URLError.Code(rawValue: httpResponse.statusCode))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func fetchNotifications(offset: Int = 0, take: Int = 20, token: String, serverUrl: String) async throws -> NotificationResponse {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        var components = URLComponents(url: baseURL.appendingPathComponent("/ring/notifications"), resolvingAgainstBaseURL: false)!
 | 
				
			||||||
 | 
					        let queryItems = [URLQueryItem(name: "offset", value: String(offset)), URLQueryItem(name: "take", value: String(take))]
 | 
				
			||||||
 | 
					        components.queryItems = queryItems
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: components.url!)
 | 
				
			||||||
 | 
					        request.httpMethod = "GET"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, response) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let decoder = JSONDecoder()
 | 
				
			||||||
 | 
					        decoder.dateDecodingStrategy = .iso8601
 | 
				
			||||||
 | 
					        decoder.keyDecodingStrategy = .convertFromSnakeCase
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let notifications = try decoder.decode([SnNotification].self, from: data)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let httpResponse = response as? HTTPURLResponse
 | 
				
			||||||
 | 
					        let total = Int(httpResponse?.value(forHTTPHeaderField: "X-Total") ?? "0") ?? 0
 | 
				
			||||||
 | 
					        let hasMore = offset + notifications.count < total
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return NotificationResponse(notifications: notifications, total: total, hasMore: hasMore)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func fetchUserProfile(token: String, serverUrl: String) async throws -> SnAccount {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let url = baseURL.appendingPathComponent("/pass/accounts/me")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: url)
 | 
				
			||||||
 | 
					        request.httpMethod = "GET"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, _) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let decoder = JSONDecoder()
 | 
				
			||||||
 | 
					        decoder.dateDecodingStrategy = .iso8601
 | 
				
			||||||
 | 
					        decoder.keyDecodingStrategy = .convertFromSnakeCase
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return try decoder.decode(SnAccount.self, from: data)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func fetchAccountStatus(token: String, serverUrl: String) async throws -> SnAccountStatus? {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let url = baseURL.appendingPathComponent("/pass/accounts/me/statuses")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: url)
 | 
				
			||||||
 | 
					        request.httpMethod = "GET"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, response) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 404 {
 | 
				
			||||||
 | 
					            return nil
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let decoder = JSONDecoder()
 | 
				
			||||||
 | 
					        decoder.dateDecodingStrategy = .iso8601
 | 
				
			||||||
 | 
					        decoder.keyDecodingStrategy = .convertFromSnakeCase
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return try decoder.decode(SnAccountStatus.self, from: data)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func createOrUpdateStatus(attitude: Int, isInvisible: Bool, isNotDisturb: Bool, label: String?, token: String, serverUrl: String) async throws -> SnAccountStatus {
 | 
				
			||||||
 | 
					        // Check if there\'s already a customized status
 | 
				
			||||||
 | 
					        let existingStatus = try? await fetchAccountStatus(token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					        let method = (existingStatus?.isCustomized == true) ? "PATCH" : "POST"
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let url = baseURL.appendingPathComponent("/pass/accounts/me/statuses")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: url)
 | 
				
			||||||
 | 
					        request.httpMethod = method
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var body: [String: Any] = [
 | 
				
			||||||
 | 
					            "attitude": attitude,
 | 
				
			||||||
 | 
					            "is_invisible": isInvisible,
 | 
				
			||||||
 | 
					            "is_not_disturb": isNotDisturb,
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if let label = label, !label.isEmpty {
 | 
				
			||||||
 | 
					            body["label"] = label
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        request.httpBody = try JSONSerialization.data(withJSONObject: body)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, response) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 201 && httpResponse.statusCode != 200 {
 | 
				
			||||||
 | 
					            let responseBody = String(data: data, encoding: .utf8) ?? ""
 | 
				
			||||||
 | 
					            print("[watchOS] createOrUpdateStatus failed with status code: \(httpResponse.statusCode), body: \(responseBody)")
 | 
				
			||||||
 | 
					            throw URLError(URLError.Code(rawValue: httpResponse.statusCode))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let decoder = JSONDecoder()
 | 
				
			||||||
 | 
					        decoder.dateDecodingStrategy = .iso8601
 | 
				
			||||||
 | 
					        decoder.keyDecodingStrategy = .convertFromSnakeCase
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return try decoder.decode(SnAccountStatus.self, from: data)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func clearStatus(token: String, serverUrl: String) async throws {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let url = baseURL.appendingPathComponent("/pass/accounts/me/statuses")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: url)
 | 
				
			||||||
 | 
					        request.httpMethod = "DELETE"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, response) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 204 {
 | 
				
			||||||
 | 
					            let responseBody = String(data: data, encoding: .utf8) ?? ""
 | 
				
			||||||
 | 
					            print("[watchOS] clearStatus failed with status code: \(httpResponse.statusCode), body: \(responseBody)")
 | 
				
			||||||
 | 
					            throw URLError(URLError.Code(rawValue: httpResponse.statusCode))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // MARK: - Chat API Methods
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func fetchChatRooms(token: String, serverUrl: String) async throws -> ChatRoomsResponse {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let url = baseURL.appendingPathComponent("/sphere/chat")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: url)
 | 
				
			||||||
 | 
					        request.httpMethod = "GET"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, _) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let decoder = JSONDecoder()
 | 
				
			||||||
 | 
					        decoder.dateDecodingStrategy = .iso8601
 | 
				
			||||||
 | 
					        decoder.keyDecodingStrategy = .convertFromSnakeCase
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let rooms = try decoder.decode([SnChatRoom].self, from: data)
 | 
				
			||||||
 | 
					        return ChatRoomsResponse(rooms: rooms)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func fetchChatRoom(identifier: String, token: String, serverUrl: String) async throws -> SnChatRoom {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let url = baseURL.appendingPathComponent("/sphere/chat/\(identifier)")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: url)
 | 
				
			||||||
 | 
					        request.httpMethod = "GET"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, response) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 404 {
 | 
				
			||||||
 | 
					            throw URLError(.resourceUnavailable)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let decoder = JSONDecoder()
 | 
				
			||||||
 | 
					        decoder.dateDecodingStrategy = .iso8601
 | 
				
			||||||
 | 
					        decoder.keyDecodingStrategy = .convertFromSnakeCase
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return try decoder.decode(SnChatRoom.self, from: data)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func fetchChatInvites(token: String, serverUrl: String) async throws -> ChatInvitesResponse {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let url = baseURL.appendingPathComponent("/sphere/chat/invites")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: url)
 | 
				
			||||||
 | 
					        request.httpMethod = "GET"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, _) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let decoder = JSONDecoder()
 | 
				
			||||||
 | 
					        decoder.dateDecodingStrategy = .iso8601
 | 
				
			||||||
 | 
					        decoder.keyDecodingStrategy = .convertFromSnakeCase
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let invites = try decoder.decode([SnChatMember].self, from: data)
 | 
				
			||||||
 | 
					        return ChatInvitesResponse(invites: invites)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func acceptChatInvite(chatRoomId: String, token: String, serverUrl: String) async throws {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let url = baseURL.appendingPathComponent("/sphere/chat/invites/\(chatRoomId)/accept")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: url)
 | 
				
			||||||
 | 
					        request.httpMethod = "POST"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, response) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200 {
 | 
				
			||||||
 | 
					            let responseBody = String(data: data, encoding: .utf8) ?? ""
 | 
				
			||||||
 | 
					            print("[watchOS] acceptChatInvite failed with status code: \(httpResponse.statusCode), body: \(responseBody)")
 | 
				
			||||||
 | 
					            throw URLError(URLError.Code(rawValue: httpResponse.statusCode))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func declineChatInvite(chatRoomId: String, token: String, serverUrl: String) async throws {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let url = baseURL.appendingPathComponent("/sphere/chat/invites/\(chatRoomId)/decline")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: url)
 | 
				
			||||||
 | 
					        request.httpMethod = "POST"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, response) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200 {
 | 
				
			||||||
 | 
					            let responseBody = String(data: data, encoding: .utf8) ?? ""
 | 
				
			||||||
 | 
					            print("[watchOS] declineChatInvite failed with status code: \(httpResponse.statusCode), body: \(responseBody)")
 | 
				
			||||||
 | 
					            throw URLError(URLError.Code(rawValue: httpResponse.statusCode))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // MARK: - Message API Methods
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func fetchChatMessages(chatRoomId: String, token: String, serverUrl: String, before: Date? = nil, take: Int = 50) async throws -> [SnChatMessage] {
 | 
				
			||||||
 | 
					        guard let baseURL = URL(string: serverUrl) else {
 | 
				
			||||||
 | 
					            throw URLError(.badURL)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Try a different pattern: /sphere/chat/messages with roomId as query param
 | 
				
			||||||
 | 
					        var components = URLComponents(
 | 
				
			||||||
 | 
					            url: baseURL.appendingPathComponent("/sphere/chat/\(chatRoomId)/messages"),
 | 
				
			||||||
 | 
					            resolvingAgainstBaseURL: false
 | 
				
			||||||
 | 
					        )!
 | 
				
			||||||
 | 
					        var queryItems = [
 | 
				
			||||||
 | 
					            URLQueryItem(name: "take", value: String(take)),
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        if let before = before {
 | 
				
			||||||
 | 
					            queryItems.append(URLQueryItem(name: "before", value: ISO8601DateFormatter().string(from: before)))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        components.queryItems = queryItems
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        var request = URLRequest(url: components.url!)
 | 
				
			||||||
 | 
					        request.httpMethod = "GET"
 | 
				
			||||||
 | 
					        request.setValue("application/json", forHTTPHeaderField: "Accept")
 | 
				
			||||||
 | 
					        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					        request.setValue("SolianWatch/1.0", forHTTPHeaderField: "User-Agent")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let (data, response) = try await session.data(for: request)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if let httpResponse = response as? HTTPURLResponse {
 | 
				
			||||||
 | 
					            _ = String(data: data, encoding: .utf8) ?? "Unable to decode response body"
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if httpResponse.statusCode != 200 {
 | 
				
			||||||
 | 
					                print("[watchOS] fetchChatMessages failed with status \(httpResponse.statusCode)")
 | 
				
			||||||
 | 
					                throw URLError(URLError.Code(rawValue: httpResponse.statusCode))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Check if data is empty
 | 
				
			||||||
 | 
					        if data.isEmpty {
 | 
				
			||||||
 | 
					            print("[watchOS] fetchChatMessages received empty response data")
 | 
				
			||||||
 | 
					            return []
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let decoder = JSONDecoder()
 | 
				
			||||||
 | 
					        decoder.dateDecodingStrategy = .iso8601
 | 
				
			||||||
 | 
					        decoder.keyDecodingStrategy = .convertFromSnakeCase
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            let messages = try decoder.decode([SnChatMessage].self, from: data)
 | 
				
			||||||
 | 
					            print("[watchOS] fetchChatMessages successfully decoded \(messages.count) messages")
 | 
				
			||||||
 | 
					            return messages
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            print("error: ", error)
 | 
				
			||||||
 | 
					            throw error
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // MARK: - WebSocket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var webSocketTask: URLSessionWebSocketTask?
 | 
				
			||||||
 | 
					    private var heartbeatTimer: Timer?
 | 
				
			||||||
 | 
					    private var reconnectTimer: Timer?
 | 
				
			||||||
 | 
					    private var isDisconnectingManually = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var lastToken: String?
 | 
				
			||||||
 | 
					    private var lastServerUrl: String?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var heartbeatAt: Date?
 | 
				
			||||||
 | 
					    var heartbeatDelay: TimeInterval?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private let connectLock = NSLock()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private let packetSubject = PassthroughSubject<WebSocketPacket, Error>()
 | 
				
			||||||
 | 
					    private let stateSubject = CurrentValueSubject<WebSocketState, Never>(.disconnected) // Changed to CurrentValueSubject
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private var currentConnectionState: WebSocketState = .disconnected { // New property
 | 
				
			||||||
 | 
					        didSet {
 | 
				
			||||||
 | 
					            // Only send updates if the state has actually changed
 | 
				
			||||||
 | 
					            if oldValue != currentConnectionState {
 | 
				
			||||||
 | 
					                stateSubject.send(currentConnectionState)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    var packetStream: AnyPublisher<WebSocketPacket, Error> {
 | 
				
			||||||
 | 
					        packetSubject.eraseToAnyPublisher()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    var stateStream: AnyPublisher<WebSocketState, Never> {
 | 
				
			||||||
 | 
					        stateSubject.eraseToAnyPublisher()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func connectWebSocket(token: String, serverUrl: String) {
 | 
				
			||||||
 | 
					        webSocketQueue.async { [weak self] in
 | 
				
			||||||
 | 
					            guard let self = self else { return }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.connectLock.lock()
 | 
				
			||||||
 | 
					            defer { self.connectLock.unlock() }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Prevent redundant connection attempts
 | 
				
			||||||
 | 
					            if self.currentConnectionState == .connecting || self.currentConnectionState == .connected {
 | 
				
			||||||
 | 
					                print("[WebSocket] Already connecting or connected, ignoring new connect request.")
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.currentConnectionState = .connecting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Ensure any existing task is cancelled before starting a new one
 | 
				
			||||||
 | 
					            self.webSocketTask?.cancel(with: .goingAway, reason: nil)
 | 
				
			||||||
 | 
					            self.webSocketTask = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.isDisconnectingManually = false // Reset this flag for a new connection attempt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.lastToken = token
 | 
				
			||||||
 | 
					            self.lastServerUrl = serverUrl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            guard var urlComponents = URLComponents(string: serverUrl) else {
 | 
				
			||||||
 | 
					                self.currentConnectionState = .error("Invalid server URL")
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            urlComponents.scheme = urlComponents.scheme?.replacingOccurrences(of: "http", with: "ws")
 | 
				
			||||||
 | 
					            urlComponents.path = "/ws"
 | 
				
			||||||
 | 
					            urlComponents.queryItems = [URLQueryItem(name: "deviceAlt", value: "watch")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            guard let url = urlComponents.url else {
 | 
				
			||||||
 | 
					                self.currentConnectionState = .error("Invalid WebSocket URL")
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var request = URLRequest(url: url)
 | 
				
			||||||
 | 
					            request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
 | 
				
			||||||
 | 
					            request.addValue("application/json", forHTTPHeaderField: "Content-Type")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            print("[WebSocket] Trying connecting to \(url)")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.webSocketTask = self.session.webSocketTask(with: request)
 | 
				
			||||||
 | 
					            self.webSocketTask?.resume()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.listenForWebSocketMessages()
 | 
				
			||||||
 | 
					            self.scheduleHeartbeat()
 | 
				
			||||||
 | 
					            self.currentConnectionState = .connected
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private func listenForWebSocketMessages() {
 | 
				
			||||||
 | 
					        // Ensure webSocketTask is still valid before attempting to receive
 | 
				
			||||||
 | 
					        guard let task = webSocketTask else {
 | 
				
			||||||
 | 
					            print("[WebSocket] listenForWebSocketMessages: webSocketTask is nil, stopping listen.")
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        task.receive { [weak self] result in
 | 
				
			||||||
 | 
					            guard let self = self else { return }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            switch result {
 | 
				
			||||||
 | 
					            case .failure(let error):
 | 
				
			||||||
 | 
					                print("[WebSocket] Error in receiving message: \(error)")
 | 
				
			||||||
 | 
					                // Only attempt to reconnect if not manually disconnecting
 | 
				
			||||||
 | 
					                if !self.isDisconnectingManually {
 | 
				
			||||||
 | 
					                    self.currentConnectionState = .error(error.localizedDescription)
 | 
				
			||||||
 | 
					                    self.scheduleReconnect()
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // If manually disconnecting, just ensure state is disconnected
 | 
				
			||||||
 | 
					                    self.currentConnectionState = .disconnected
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            case .success(let message):
 | 
				
			||||||
 | 
					                switch message {
 | 
				
			||||||
 | 
					                case .string(let text):
 | 
				
			||||||
 | 
					                    self.handleWebSocketMessage(text: text)
 | 
				
			||||||
 | 
					                case .data(let data):
 | 
				
			||||||
 | 
					                    if let text = String(data: data, encoding: .utf8) {
 | 
				
			||||||
 | 
					                        self.handleWebSocketMessage(text: text)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                @unknown default:
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // Continue listening for next message only if task is still valid
 | 
				
			||||||
 | 
					                if self.webSocketTask === task { // Check if it's the same task
 | 
				
			||||||
 | 
					                    self.listenForWebSocketMessages()
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    print("[WebSocket] listenForWebSocketMessages: Task changed, stopping listen for old task.")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private func handleWebSocketMessage(text: String) {
 | 
				
			||||||
 | 
					        guard let data = text.data(using: .utf8) else {
 | 
				
			||||||
 | 
					            print("[WebSocket] Could not convert message to data")
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
 | 
				
			||||||
 | 
					               let type = json["type"] as? String
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                let packet = WebSocketPacket(
 | 
				
			||||||
 | 
					                    type: type,
 | 
				
			||||||
 | 
					                    data: json["data"] as? [String: Any],
 | 
				
			||||||
 | 
					                    endpoint: json["endpoint"] as? String,
 | 
				
			||||||
 | 
					                    errorMessage: json["errorMessage"] as? String
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                print("[WebSocket] Received packet: \(packet.type) \(packet.errorMessage ?? "")")
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if packet.type == "error.dupe" {
 | 
				
			||||||
 | 
					                    self.currentConnectionState = .duplicateDevice
 | 
				
			||||||
 | 
					                    self.disconnectWebSocket()
 | 
				
			||||||
 | 
					                    return
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if packet.type == "pong" {
 | 
				
			||||||
 | 
					                    if let beatAt = self.heartbeatAt {
 | 
				
			||||||
 | 
					                        let now = Date()
 | 
				
			||||||
 | 
					                        self.heartbeatDelay = now.timeIntervalSince(beatAt)
 | 
				
			||||||
 | 
					                        print("[WebSocket] Server respond last heartbeat for \((self.heartbeatDelay ?? 0) * 1000) ms")
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                self.packetSubject.send(packet)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            print("[WebSocket] Could not parse message json: \(error.localizedDescription)")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private func scheduleReconnect() {
 | 
				
			||||||
 | 
					        reconnectTimer?.invalidate()
 | 
				
			||||||
 | 
					        reconnectTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { [weak self] _ in
 | 
				
			||||||
 | 
					            guard let self = self, let token = self.lastToken, let serverUrl = self.lastServerUrl else { return }
 | 
				
			||||||
 | 
					            print("[WebSocket] Attempting to reconnect...")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // No need to call disconnectWebSocket here, connectWebSocket will handle cancelling old task
 | 
				
			||||||
 | 
					            self.isDisconnectingManually = false // Reset for the new connection attempt
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.connectWebSocket(token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private func scheduleHeartbeat() {
 | 
				
			||||||
 | 
					        heartbeatTimer?.invalidate()
 | 
				
			||||||
 | 
					        heartbeatTimer = Timer.scheduledTimer(withTimeInterval: 60.0, repeats: true) { [weak self] _ in
 | 
				
			||||||
 | 
					            self?.beatTheHeart()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private func beatTheHeart() {
 | 
				
			||||||
 | 
					        heartbeatAt = Date()
 | 
				
			||||||
 | 
					        print("[WebSocket] We\'re beating the heart! \(String(describing: self.heartbeatAt))")
 | 
				
			||||||
 | 
					        sendWebSocketMessage(message: "{\"type\":\"ping\"}")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func sendWebSocketMessage(message: String) {
 | 
				
			||||||
 | 
					        webSocketTask?.send(.string(message)) { error in
 | 
				
			||||||
 | 
					            if let error = error {
 | 
				
			||||||
 | 
					                print("[WebSocket] Error sending message: \(error.localizedDescription)")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func disconnectWebSocket() {
 | 
				
			||||||
 | 
					        isDisconnectingManually = true
 | 
				
			||||||
 | 
					        reconnectTimer?.invalidate()
 | 
				
			||||||
 | 
					        heartbeatTimer?.invalidate()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Cancel the task and then nil it out
 | 
				
			||||||
 | 
					        webSocketTask?.cancel(with: .goingAway, reason: nil)
 | 
				
			||||||
 | 
					        webSocketTask = nil // Set to nil immediately after cancelling
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.currentConnectionState = .disconnected
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										58
									
								
								ios/Solian Watch App/State/AppState.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  AppState.swift
 | 
				
			||||||
 | 
					//  WatchRunner Watch App
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by LittleSheep on 2025/10/29.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					import Combine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MARK: - App State
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@MainActor
 | 
				
			||||||
 | 
					class AppState: ObservableObject {
 | 
				
			||||||
 | 
					    @Published var token: String? = nil
 | 
				
			||||||
 | 
					    @Published var serverUrl: String? = nil
 | 
				
			||||||
 | 
					    @Published var isReady = false
 | 
				
			||||||
 | 
					    @Published var errorMessage: String? = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let networkService = NetworkService()
 | 
				
			||||||
 | 
					    private var wcService = WatchConnectivityService()
 | 
				
			||||||
 | 
					    private var cancellables = Set<AnyCancellable>()
 | 
				
			||||||
 | 
					    private var hasAttemptedConnection = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init() {
 | 
				
			||||||
 | 
					        wcService.$token.combineLatest(wcService.$serverUrl, wcService.$isFetched, wcService.$errorMessage)
 | 
				
			||||||
 | 
					            .receive(on: DispatchQueue.main)
 | 
				
			||||||
 | 
					            .sink { [weak self] (token: String?, serverUrl: String?, isFetched: Bool?, errorMessage: String?) in
 | 
				
			||||||
 | 
					                guard let self = self else { return }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                self.token = token
 | 
				
			||||||
 | 
					                self.serverUrl = serverUrl
 | 
				
			||||||
 | 
					                self.errorMessage = errorMessage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let token = token, let serverUrl = serverUrl, !token.isEmpty, !serverUrl.isEmpty {
 | 
				
			||||||
 | 
					                    self.isReady = true
 | 
				
			||||||
 | 
					                    // Only connect once when we have valid credentials and tried fetch from phone
 | 
				
			||||||
 | 
					                    if !self.hasAttemptedConnection && isFetched == true {
 | 
				
			||||||
 | 
					                        self.hasAttemptedConnection = true
 | 
				
			||||||
 | 
					                        print("[AppState] Connecting WebSocket to server: \(serverUrl)")
 | 
				
			||||||
 | 
					                        self.networkService.connectWebSocket(token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    self.isReady = false
 | 
				
			||||||
 | 
					                    if self.hasAttemptedConnection {
 | 
				
			||||||
 | 
					                        self.hasAttemptedConnection = false
 | 
				
			||||||
 | 
					                        // Disconnect WebSocket if token or serverUrl become invalid
 | 
				
			||||||
 | 
					                        self.networkService.disconnectWebSocket()
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            .store(in: &cancellables)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    func requestData() {
 | 
				
			||||||
 | 
					        wcService.requestDataFromPhone()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										113
									
								
								ios/Solian Watch App/State/WatchConnectivityService.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,113 @@
 | 
				
			|||||||
 | 
					import WatchConnectivity
 | 
				
			||||||
 | 
					import Combine
 | 
				
			||||||
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WatchConnectivityService: NSObject, WCSessionDelegate, ObservableObject {
 | 
				
			||||||
 | 
					    @Published var token: String?
 | 
				
			||||||
 | 
					    @Published var serverUrl: String?
 | 
				
			||||||
 | 
					    @Published var isFetched: Bool?
 | 
				
			||||||
 | 
					    @Published var errorMessage: String?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private let session: WCSession
 | 
				
			||||||
 | 
					    private let userDefaults = UserDefaults.standard
 | 
				
			||||||
 | 
					    private let tokenKey = "token"
 | 
				
			||||||
 | 
					    private let serverUrlKey = "serverUrl"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override init() {
 | 
				
			||||||
 | 
					        self.session = .default
 | 
				
			||||||
 | 
					        super.init()
 | 
				
			||||||
 | 
					        print("[watchOS] Activating WCSession")
 | 
				
			||||||
 | 
					        self.session.delegate = self
 | 
				
			||||||
 | 
					        self.session.activate()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Load cached data
 | 
				
			||||||
 | 
					        self.token = userDefaults.string(forKey: tokenKey)
 | 
				
			||||||
 | 
					        self.serverUrl = userDefaults.string(forKey: serverUrlKey)
 | 
				
			||||||
 | 
					        self.isFetched = false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
 | 
				
			||||||
 | 
					        if let error = error {
 | 
				
			||||||
 | 
					            print("[watchOS] WCSession activation failed with error: \(error.localizedDescription)")
 | 
				
			||||||
 | 
					            DispatchQueue.main.async {
 | 
				
			||||||
 | 
					                self.errorMessage = "WCSession activation failed: \(error.localizedDescription)"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        print("[watchOS] WCSession activated with state: \(activationState.rawValue)")
 | 
				
			||||||
 | 
					        if activationState == .activated {
 | 
				
			||||||
 | 
					            requestDataFromPhone()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
 | 
				
			||||||
 | 
					        print("[watchOS] Received application context: \(applicationContext)")
 | 
				
			||||||
 | 
					        DispatchQueue.main.async {
 | 
				
			||||||
 | 
					            if let token = applicationContext["token"] as? String {
 | 
				
			||||||
 | 
					                self.token = token
 | 
				
			||||||
 | 
					                self.userDefaults.set(token, forKey: self.tokenKey)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if let serverUrl = applicationContext["serverUrl"] as? String {
 | 
				
			||||||
 | 
					                self.serverUrl = serverUrl
 | 
				
			||||||
 | 
					                self.userDefaults.set(serverUrl, forKey: self.serverUrlKey)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            self.isFetched = true
 | 
				
			||||||
 | 
					            self.errorMessage = nil
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
 | 
				
			||||||
 | 
					        print("[watchOS] Received message: \(message)")
 | 
				
			||||||
 | 
					        DispatchQueue.main.async {
 | 
				
			||||||
 | 
					            if let token = message["token"] as? String {
 | 
				
			||||||
 | 
					                self.token = token
 | 
				
			||||||
 | 
					                self.userDefaults.set(token, forKey: self.tokenKey)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if let serverUrl = message["serverUrl"] as? String {
 | 
				
			||||||
 | 
					                self.serverUrl = serverUrl
 | 
				
			||||||
 | 
					                self.userDefaults.set(serverUrl, forKey: self.serverUrlKey)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func requestDataFromPhone() {
 | 
				
			||||||
 | 
					    // Check if we already have valid data to avoid unnecessary requests
 | 
				
			||||||
 | 
					    if let token = self.token, let serverUrl = self.serverUrl, !token.isEmpty, !serverUrl.isEmpty {
 | 
				
			||||||
 | 
					        print("[watchOS] Skipped fetch - already have valid data")
 | 
				
			||||||
 | 
					        self.isFetched = true
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    guard session.activationState == .activated else {
 | 
				
			||||||
 | 
					        print("[watchOS] Session not activated yet, state: \(session.activationState.rawValue)")
 | 
				
			||||||
 | 
					        DispatchQueue.main.async {
 | 
				
			||||||
 | 
					            self.errorMessage = "Session not ready yet"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    print("[watchOS] Requesting data from phone")
 | 
				
			||||||
 | 
					    session.sendMessage(["request": "data"]) { [weak self] response in
 | 
				
			||||||
 | 
					        guard let self = self else { return }
 | 
				
			||||||
 | 
					        print("[watchOS] Received reply: \(response)")
 | 
				
			||||||
 | 
					        DispatchQueue.main.async {
 | 
				
			||||||
 | 
					            self.isFetched = true
 | 
				
			||||||
 | 
					            if let token = response["token"] as? String {
 | 
				
			||||||
 | 
					                self.token = token
 | 
				
			||||||
 | 
					                self.userDefaults.set(token, forKey: self.tokenKey)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if let serverUrl = response["serverUrl"] as? String {
 | 
				
			||||||
 | 
					                self.serverUrl = serverUrl
 | 
				
			||||||
 | 
					                self.userDefaults.set(serverUrl, forKey: self.serverUrlKey)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            self.errorMessage = nil // Clear any previous errors
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } errorHandler: { error in
 | 
				
			||||||
 | 
					        print("[watchOS] sendMessage failed with error: \(error.localizedDescription)")
 | 
				
			||||||
 | 
					        DispatchQueue.main.async {
 | 
				
			||||||
 | 
					            self.errorMessage = "Failed to get data from phone: \(error.localizedDescription)"
 | 
				
			||||||
 | 
					            // Don't set isFetched = true on error - allow retry
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								ios/Solian Watch App/Utils/AttachmentUtils.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  AttachmentUtils.swift
 | 
				
			||||||
 | 
					//  WatchRunner Watch App
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by LittleSheep on 2025/10/29.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MARK: - Helper Functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getAttachmentUrl(for fileId: String, serverUrl: String) -> URL? {
 | 
				
			||||||
 | 
					    let urlString: String
 | 
				
			||||||
 | 
					    if fileId.starts(with: "http") {
 | 
				
			||||||
 | 
					        urlString = fileId
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        urlString = "\(serverUrl)/drive/files/\(fileId)"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return URL(string: urlString)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										73
									
								
								ios/Solian Watch App/ViewModels/ActivityViewModel.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  ActivityViewModel.swift
 | 
				
			||||||
 | 
					//  WatchRunner Watch App
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by LittleSheep on 2025/10/29.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Foundation
 | 
				
			||||||
 | 
					import Combine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MARK: - View Models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@MainActor
 | 
				
			||||||
 | 
					class ActivityViewModel: ObservableObject {
 | 
				
			||||||
 | 
					    @Published var activities: [SnActivity] = []
 | 
				
			||||||
 | 
					    @Published var isLoading = false
 | 
				
			||||||
 | 
					    @Published var isLoadingMore = false
 | 
				
			||||||
 | 
					    @Published var errorMessage: String?
 | 
				
			||||||
 | 
					    @Published var hasMore = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private let networkService = NetworkService()
 | 
				
			||||||
 | 
					    let filter: String
 | 
				
			||||||
 | 
					    private var isMock = false
 | 
				
			||||||
 | 
					    private var hasFetched = false
 | 
				
			||||||
 | 
					    private var nextCursor: String?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init(filter: String, mockActivities: [SnActivity]? = nil) {
 | 
				
			||||||
 | 
					        self.filter = filter
 | 
				
			||||||
 | 
					        if let mockActivities = mockActivities {
 | 
				
			||||||
 | 
					            self.activities = mockActivities
 | 
				
			||||||
 | 
					            self.isMock = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func fetchActivities(token: String, serverUrl: String) async {
 | 
				
			||||||
 | 
					        if isMock || hasFetched { return }
 | 
				
			||||||
 | 
					        guard !isLoading else { return }
 | 
				
			||||||
 | 
					        isLoading = true
 | 
				
			||||||
 | 
					        errorMessage = nil
 | 
				
			||||||
 | 
					        hasFetched = true
 | 
				
			||||||
 | 
					        nextCursor = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            let response = try await networkService.fetchActivities(filter: filter, cursor: nil, token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					            self.activities = response.activities
 | 
				
			||||||
 | 
					            self.hasMore = response.hasMore
 | 
				
			||||||
 | 
					            self.nextCursor = response.nextCursor
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            self.errorMessage = error.localizedDescription
 | 
				
			||||||
 | 
					            print("[watchOS] fetchActivities failed with error: \(error)")
 | 
				
			||||||
 | 
					            hasFetched = false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isLoading = false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func loadMoreActivities(token: String, serverUrl: String) async {
 | 
				
			||||||
 | 
					        guard !isLoadingMore && hasMore && nextCursor != nil else { return }
 | 
				
			||||||
 | 
					        isLoadingMore = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            let response = try await networkService.fetchActivities(filter: filter, cursor: nextCursor, token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					            self.activities.append(contentsOf: response.activities)
 | 
				
			||||||
 | 
					            self.hasMore = response.hasMore
 | 
				
			||||||
 | 
					            self.nextCursor = response.nextCursor
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            self.errorMessage = error.localizedDescription
 | 
				
			||||||
 | 
					            print("[watchOS] loadMoreActivities failed with error: \(error)")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isLoadingMore = false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								ios/Solian Watch App/ViewModels/ComposePostViewModel.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  ComposePostViewModel.swift
 | 
				
			||||||
 | 
					//  WatchRunner Watch App
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by LittleSheep on 2025/10/29.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Foundation
 | 
				
			||||||
 | 
					import Combine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@MainActor
 | 
				
			||||||
 | 
					class ComposePostViewModel: ObservableObject {
 | 
				
			||||||
 | 
					    @Published var title = ""
 | 
				
			||||||
 | 
					    @Published var content = ""
 | 
				
			||||||
 | 
					    @Published var isPosting = false
 | 
				
			||||||
 | 
					    @Published var errorMessage: String?
 | 
				
			||||||
 | 
					    @Published var didPost = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private let networkService = NetworkService()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func createPost(token: String, serverUrl: String) async {
 | 
				
			||||||
 | 
					        guard !isPosting else { return }
 | 
				
			||||||
 | 
					        isPosting = true
 | 
				
			||||||
 | 
					        errorMessage = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            try await networkService.createPost(title: title, content: content, token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					            didPost = true
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            errorMessage = error.localizedDescription
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isPosting = false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										284
									
								
								ios/Solian Watch App/Views/AccountView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,284 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  AccountView.swift
 | 
				
			||||||
 | 
					//  WatchRunner Watch App
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by LittleSheep on 2025/10/30.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct AccountView: View {
 | 
				
			||||||
 | 
					    @EnvironmentObject var appState: AppState
 | 
				
			||||||
 | 
					    @State private var user: SnAccount?
 | 
				
			||||||
 | 
					    @State private var status: SnAccountStatus?
 | 
				
			||||||
 | 
					    @State private var isLoading = false
 | 
				
			||||||
 | 
					    @State private var error: Error?
 | 
				
			||||||
 | 
					    @State private var showingClearConfirmation = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @StateObject private var profileImageLoader = ImageLoader()
 | 
				
			||||||
 | 
					    @StateObject private var bannerImageLoader = ImageLoader()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private let networkService = NetworkService()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    var body: some View {
 | 
				
			||||||
 | 
					        ScrollView {
 | 
				
			||||||
 | 
					            if isLoading {
 | 
				
			||||||
 | 
					                ProgressView()
 | 
				
			||||||
 | 
					                    .padding()
 | 
				
			||||||
 | 
					            } else if let error = error {
 | 
				
			||||||
 | 
					                VStack {
 | 
				
			||||||
 | 
					                    Text("Failed to load account")
 | 
				
			||||||
 | 
					                        .foregroundColor(.red)
 | 
				
			||||||
 | 
					                    Text(error.localizedDescription)
 | 
				
			||||||
 | 
					                        .font(.caption)
 | 
				
			||||||
 | 
					                        .foregroundColor(.secondary)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                .padding()
 | 
				
			||||||
 | 
					            } else if let user = user {
 | 
				
			||||||
 | 
					                VStack(spacing: 16) {
 | 
				
			||||||
 | 
					                    // Banner
 | 
				
			||||||
 | 
					                    if user.profile.background != nil {
 | 
				
			||||||
 | 
					                        if bannerImageLoader.isLoading {
 | 
				
			||||||
 | 
					                            ProgressView()
 | 
				
			||||||
 | 
					                                .frame(height: 80)
 | 
				
			||||||
 | 
					                        } else if let bannerImage = bannerImageLoader.image {
 | 
				
			||||||
 | 
					                            bannerImage
 | 
				
			||||||
 | 
					                                .resizable()
 | 
				
			||||||
 | 
					                                .aspectRatio(contentMode: .fill)
 | 
				
			||||||
 | 
					                                .frame(height: 80)
 | 
				
			||||||
 | 
					                                .clipped()
 | 
				
			||||||
 | 
					                                .cornerRadius(8)
 | 
				
			||||||
 | 
					                        } else if bannerImageLoader.errorMessage != nil {
 | 
				
			||||||
 | 
					                            Rectangle()
 | 
				
			||||||
 | 
					                                .fill(Color.gray.opacity(0.3))
 | 
				
			||||||
 | 
					                                .frame(height: 80)
 | 
				
			||||||
 | 
					                                .cornerRadius(8)
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            Rectangle()
 | 
				
			||||||
 | 
					                                .fill(Color.gray.opacity(0.3))
 | 
				
			||||||
 | 
					                                .frame(height: 80)
 | 
				
			||||||
 | 
					                                .cornerRadius(8)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Profile Picture
 | 
				
			||||||
 | 
					                    HStack(spacing: 16)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        if profileImageLoader.isLoading {
 | 
				
			||||||
 | 
					                            ProgressView()
 | 
				
			||||||
 | 
					                                .frame(width: 60, height: 60)
 | 
				
			||||||
 | 
					                        } else if let profileImage = profileImageLoader.image {
 | 
				
			||||||
 | 
					                            profileImage
 | 
				
			||||||
 | 
					                                .resizable()
 | 
				
			||||||
 | 
					                                .frame(width: 60, height: 60)
 | 
				
			||||||
 | 
					                                .clipShape(Circle())
 | 
				
			||||||
 | 
					                        } else if profileImageLoader.errorMessage != nil {
 | 
				
			||||||
 | 
					                            Circle()
 | 
				
			||||||
 | 
					                                .fill(Color.red.opacity(0.3))
 | 
				
			||||||
 | 
					                                .frame(width: 60, height: 60)
 | 
				
			||||||
 | 
					                                .overlay(
 | 
				
			||||||
 | 
					                                    Image(systemName: "exclamationmark.triangle")
 | 
				
			||||||
 | 
					                                        .resizable()
 | 
				
			||||||
 | 
					                                        .scaledToFit()
 | 
				
			||||||
 | 
					                                        .foregroundColor(.red)
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            Circle()
 | 
				
			||||||
 | 
					                                .fill(Color.gray.opacity(0.3))
 | 
				
			||||||
 | 
					                                .frame(width: 60, height: 60)
 | 
				
			||||||
 | 
					                                .overlay(
 | 
				
			||||||
 | 
					                                    Image(systemName: "person.circle.fill")
 | 
				
			||||||
 | 
					                                        .resizable()
 | 
				
			||||||
 | 
					                                        .scaledToFit()
 | 
				
			||||||
 | 
					                                        .foregroundColor(.gray)
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        // Username and Handle
 | 
				
			||||||
 | 
					                        VStack(alignment: .leading) {
 | 
				
			||||||
 | 
					                            Text(user.nick)
 | 
				
			||||||
 | 
					                                .font(.headline)
 | 
				
			||||||
 | 
					                            Text("@\(user.name)")
 | 
				
			||||||
 | 
					                                .font(.caption)
 | 
				
			||||||
 | 
					                                .foregroundColor(.secondary)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Status
 | 
				
			||||||
 | 
					                    VStack(alignment: .leading, spacing: 8) {
 | 
				
			||||||
 | 
					                        HStack {
 | 
				
			||||||
 | 
					                            Text("Status")
 | 
				
			||||||
 | 
					                                .font(.subheadline)
 | 
				
			||||||
 | 
					                                .foregroundColor(.secondary)
 | 
				
			||||||
 | 
					                            Spacer()
 | 
				
			||||||
 | 
					                            if status?.isCustomized == true {
 | 
				
			||||||
 | 
					                                Button(action: {
 | 
				
			||||||
 | 
					                                    showingClearConfirmation = true
 | 
				
			||||||
 | 
					                                }) {
 | 
				
			||||||
 | 
					                                    ZStack {
 | 
				
			||||||
 | 
					                                        Circle()
 | 
				
			||||||
 | 
					                                            .fill(Color.red.opacity(0.1))
 | 
				
			||||||
 | 
					                                            .frame(width: 28, height: 28)
 | 
				
			||||||
 | 
					                                        Image(systemName: "trash")
 | 
				
			||||||
 | 
					                                            .foregroundColor(.red)
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                .buttonStyle(.plain)
 | 
				
			||||||
 | 
					                                .frame(width: 28, height: 28)
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            NavigationLink(
 | 
				
			||||||
 | 
					                                destination: StatusCreationView(initialStatus: status?.isCustomized == true ? status : nil)
 | 
				
			||||||
 | 
					                                    .environmentObject(appState)
 | 
				
			||||||
 | 
					                            ) {
 | 
				
			||||||
 | 
					                                ZStack {
 | 
				
			||||||
 | 
					                                    Circle()
 | 
				
			||||||
 | 
					                                        .fill(Color.blue.opacity(0.1))
 | 
				
			||||||
 | 
					                                        .frame(width: 28, height: 28)
 | 
				
			||||||
 | 
					                                    Image(systemName: "pencil")
 | 
				
			||||||
 | 
					                                        .foregroundColor(.blue)
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            .buttonStyle(.plain)
 | 
				
			||||||
 | 
					                            .frame(width: 28, height: 28)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        if let status = status {
 | 
				
			||||||
 | 
					                            VStack(alignment: .leading, spacing: 4) {
 | 
				
			||||||
 | 
					                                HStack {
 | 
				
			||||||
 | 
					                                    Circle()
 | 
				
			||||||
 | 
					                                        .fill(status.isOnline ? Color.green : Color.gray)
 | 
				
			||||||
 | 
					                                        .frame(width: 8, height: 8)
 | 
				
			||||||
 | 
					                                    Text(status.label.isEmpty ? "No status" : status.label)
 | 
				
			||||||
 | 
					                                        .font(.body)
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                
 | 
				
			||||||
 | 
					                                if status.isInvisible {
 | 
				
			||||||
 | 
					                                    Text("Invisible")
 | 
				
			||||||
 | 
					                                        .font(.caption)
 | 
				
			||||||
 | 
					                                        .foregroundColor(.secondary)
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                if status.isNotDisturb {
 | 
				
			||||||
 | 
					                                    Text("Do Not Disturb")
 | 
				
			||||||
 | 
					                                        .font(.caption)
 | 
				
			||||||
 | 
					                                        .foregroundColor(.secondary)
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                if let clearedAt = status.clearedAt {
 | 
				
			||||||
 | 
					                                    Text("Clears: \(clearedAt.formatted(date: .abbreviated, time: .shortened))")
 | 
				
			||||||
 | 
					                                        .font(.caption)
 | 
				
			||||||
 | 
					                                        .foregroundColor(.secondary)
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            Text("No status set")
 | 
				
			||||||
 | 
					                                .font(.body)
 | 
				
			||||||
 | 
					                                .foregroundColor(.secondary)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Level and Progress
 | 
				
			||||||
 | 
					                    VStack(alignment: .leading, spacing: 8) {
 | 
				
			||||||
 | 
					                        Text("Level \(user.profile.level)")
 | 
				
			||||||
 | 
					                            .font(.title3)
 | 
				
			||||||
 | 
					                            .bold()
 | 
				
			||||||
 | 
					                        ProgressView(value: user.profile.levelingProgress)
 | 
				
			||||||
 | 
					                            .progressViewStyle(LinearProgressViewStyle())
 | 
				
			||||||
 | 
					                            .frame(height: 8)
 | 
				
			||||||
 | 
					                        Text("Experience: \(user.profile.experience)")
 | 
				
			||||||
 | 
					                            .font(.caption)
 | 
				
			||||||
 | 
					                            .foregroundColor(.secondary)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Bio
 | 
				
			||||||
 | 
					                    if let bio = user.profile.bio, !bio.isEmpty {
 | 
				
			||||||
 | 
					                        Text(bio)
 | 
				
			||||||
 | 
					                            .font(.body)
 | 
				
			||||||
 | 
					                            .multilineTextAlignment(.center)
 | 
				
			||||||
 | 
					                            .foregroundColor(.secondary)
 | 
				
			||||||
 | 
					                            .frame(alignment: .leading)
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        Text("No bio available")
 | 
				
			||||||
 | 
					                            .font(.body)
 | 
				
			||||||
 | 
					                            .foregroundColor(.secondary)
 | 
				
			||||||
 | 
					                            .frame(alignment: .leading)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Member since
 | 
				
			||||||
 | 
					                    Text("Joined at \(user.createdAt.formatted(.dateTime.month(.abbreviated).year()))")
 | 
				
			||||||
 | 
					                        .font(.caption)
 | 
				
			||||||
 | 
					                        .foregroundColor(.secondary)
 | 
				
			||||||
 | 
					                        .frame(alignment: .leading)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                .padding()
 | 
				
			||||||
 | 
					                // Load images when user data is available
 | 
				
			||||||
 | 
					                .task(id: user.profile.picture?.id) {
 | 
				
			||||||
 | 
					                    if let serverUrl = appState.serverUrl, let pictureId = user.profile.picture?.id, let imageUrl = getAttachmentUrl(for: pictureId, serverUrl: serverUrl), let token = appState.token {
 | 
				
			||||||
 | 
					                        await profileImageLoader.loadImage(from: imageUrl, token: token)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                .task(id: user.profile.background?.id) {
 | 
				
			||||||
 | 
					                    if let serverUrl = appState.serverUrl, let backgroundId = user.profile.background?.id, let imageUrl = getAttachmentUrl(for: backgroundId, serverUrl: serverUrl), let token = appState.token {
 | 
				
			||||||
 | 
					                        await bannerImageLoader.loadImage(from: imageUrl, token: token)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                Text("No account data")
 | 
				
			||||||
 | 
					                    .padding()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .navigationTitle("Account")
 | 
				
			||||||
 | 
					        .confirmationDialog("Clear Status", isPresented: $showingClearConfirmation) {
 | 
				
			||||||
 | 
					            Button("Clear Status", role: .destructive) {
 | 
				
			||||||
 | 
					                Task {
 | 
				
			||||||
 | 
					                    await clearStatus()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Button("Cancel", role: .cancel) {}
 | 
				
			||||||
 | 
					        } message: {
 | 
				
			||||||
 | 
					            Text("Are you sure you want to clear your status? This action cannot be undone.")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .onAppear {
 | 
				
			||||||
 | 
					            Task.detached {
 | 
				
			||||||
 | 
					                await loadUserProfile()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private func loadUserProfile() async {
 | 
				
			||||||
 | 
					        guard let token = appState.token, let serverUrl = appState.serverUrl else {
 | 
				
			||||||
 | 
					            error = NSError(domain: "AccountView", code: 1, userInfo: [NSLocalizedDescriptionKey: "Authentication not available"])
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isLoading = true
 | 
				
			||||||
 | 
					        error = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            user = try await networkService.fetchUserProfile(token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					            status = try await networkService.fetchAccountStatus(token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            self.error = error
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isLoading = false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private func clearStatus() async {
 | 
				
			||||||
 | 
					        guard let token = appState.token, let serverUrl = appState.serverUrl else {
 | 
				
			||||||
 | 
					            error = NSError(domain: "AccountView", code: 1, userInfo: [NSLocalizedDescriptionKey: "Authentication not available"])
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            try await networkService.clearStatus(token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					            // Refresh status after clearing
 | 
				
			||||||
 | 
					            status = try await networkService.fetchAccountStatus(token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            self.error = error
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#Preview {
 | 
				
			||||||
 | 
					    AccountView()
 | 
				
			||||||
 | 
					        .environmentObject(AppState())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										86
									
								
								ios/Solian Watch App/Views/ActivityListView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  ActivityListView.swift
 | 
				
			||||||
 | 
					//  WatchRunner Watch App
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by LittleSheep on 2025/10/29.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MARK: - Views
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ActivityListView: View {
 | 
				
			||||||
 | 
					    @StateObject private var viewModel: ActivityViewModel
 | 
				
			||||||
 | 
					    @EnvironmentObject var appState: AppState
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init(filter: String, mockActivities: [SnActivity]? = nil) {
 | 
				
			||||||
 | 
					        _viewModel = StateObject(wrappedValue: ActivityViewModel(filter: filter, mockActivities: mockActivities))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var body: some View {
 | 
				
			||||||
 | 
					        Group {
 | 
				
			||||||
 | 
					            if viewModel.isLoading {
 | 
				
			||||||
 | 
					                ProgressView()
 | 
				
			||||||
 | 
					            } else if let errorMessage = viewModel.errorMessage {
 | 
				
			||||||
 | 
					                VStack {
 | 
				
			||||||
 | 
					                    Text("Error fetching data")
 | 
				
			||||||
 | 
					                        .font(.headline)
 | 
				
			||||||
 | 
					                    Text(errorMessage)
 | 
				
			||||||
 | 
					                        .font(.caption)
 | 
				
			||||||
 | 
					                        .lineLimit(nil)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                .padding()
 | 
				
			||||||
 | 
					            } else if viewModel.activities.isEmpty {
 | 
				
			||||||
 | 
					                Text("No activities found.")
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                List {
 | 
				
			||||||
 | 
					                    ForEach(viewModel.activities) { activity in
 | 
				
			||||||
 | 
					                        switch activity.type {
 | 
				
			||||||
 | 
					                        case "posts.new", "posts.new.replies":
 | 
				
			||||||
 | 
					                            if case .post(let post) = activity.data {
 | 
				
			||||||
 | 
					                                NavigationLink(
 | 
				
			||||||
 | 
					                                    destination: PostDetailView(post: post).environmentObject(appState)
 | 
				
			||||||
 | 
					                                ) {
 | 
				
			||||||
 | 
					                                    PostRowView(post: post)
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        case "discovery":
 | 
				
			||||||
 | 
					                            if case .discovery(let discoveryData) = activity.data {
 | 
				
			||||||
 | 
					                                DiscoveryView(discoveryData: discoveryData)
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            Text("Unknown activity type: \(activity.type)")
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if viewModel.hasMore {
 | 
				
			||||||
 | 
					                        if viewModel.isLoadingMore {
 | 
				
			||||||
 | 
					                            HStack {
 | 
				
			||||||
 | 
					                                Spacer()
 | 
				
			||||||
 | 
					                                ProgressView()
 | 
				
			||||||
 | 
					                                Spacer()
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            Button("Load More") {
 | 
				
			||||||
 | 
					                                Task {
 | 
				
			||||||
 | 
					                                    if let token = appState.token, let serverUrl = appState.serverUrl {
 | 
				
			||||||
 | 
					                                        await viewModel.loadMoreActivities(token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            .frame(maxWidth: .infinity)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .onAppear {
 | 
				
			||||||
 | 
					            if appState.isReady, let token = appState.token, let serverUrl = appState.serverUrl {
 | 
				
			||||||
 | 
					                Task.detached {
 | 
				
			||||||
 | 
					                    await viewModel.fetchActivities(token: token, serverUrl: serverUrl)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .navigationTitle(viewModel.filter)
 | 
				
			||||||
 | 
					        .navigationBarTitleDisplayMode(.inline)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||