Compare commits
No commits in common. "main" and "BasicStructureAndStorage" have entirely different histories.
main
...
BasicStruc
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,2 @@
|
|||||||
xcuserdata
|
xcuserdata
|
||||||
*.xcuserdatad
|
*.xcuserdatad
|
||||||
.DS_STORE
|
|
||||||
SLAppDevel.developerprofile
|
|
||||||
|
|||||||
22
LICENSE
22
LICENSE
@ -1,22 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2023 Severin Memmishofer
|
|
||||||
Copyright (c) 2023 Sebastian Lenzlinger
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@ -1,6 +1,2 @@
|
|||||||
# RippleChat
|
# RippleChat
|
||||||
RippleChat is a meant to be a TinySSB compatible chat client. It is started as a university project as a proof of concept for p2p messaging in a Secure Scuttlebut fashion on iOS.
|
2023 © Severin Memmishofer, Sebastian Lenzlinger
|
||||||
At this stage it communicates via BLE.
|
|
||||||
It is not currently compatible with TinySSB conforming apps like [Tremola](https://github.com/cn-uofbasel/tremola).
|
|
||||||
Future work includes switching to the tinySSB protocol and heavily beautifying th UI.
|
|
||||||
|
|
||||||
|
|||||||
@ -15,22 +15,20 @@
|
|||||||
96454F362A558EBE0040BEBD /* RippleChatUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96454F352A558EBE0040BEBD /* RippleChatUITests.swift */; };
|
96454F362A558EBE0040BEBD /* RippleChatUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96454F352A558EBE0040BEBD /* RippleChatUITests.swift */; };
|
||||||
96454F382A558EBE0040BEBD /* RippleChatUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96454F372A558EBE0040BEBD /* RippleChatUITestsLaunchTests.swift */; };
|
96454F382A558EBE0040BEBD /* RippleChatUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96454F372A558EBE0040BEBD /* RippleChatUITestsLaunchTests.swift */; };
|
||||||
96454F452A5593900040BEBD /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 96454F442A5593900040BEBD /* .gitignore */; };
|
96454F452A5593900040BEBD /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 96454F442A5593900040BEBD /* .gitignore */; };
|
||||||
|
96BD330E2A5C254B007A6E53 /* TextApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BD330D2A5C254B007A6E53 /* TextApp.swift */; };
|
||||||
96BD33102A5C27B0007A6E53 /* NewFeedEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BD330F2A5C27B0007A6E53 /* NewFeedEntryView.swift */; };
|
96BD33102A5C27B0007A6E53 /* NewFeedEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BD330F2A5C27B0007A6E53 /* NewFeedEntryView.swift */; };
|
||||||
96BD33132A5C400B007A6E53 /* FeedListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BD33122A5C400B007A6E53 /* FeedListView.swift */; };
|
96BD33132A5C400B007A6E53 /* FeedListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BD33122A5C400B007A6E53 /* FeedListView.swift */; };
|
||||||
96BD33162A5C403C007A6E53 /* DiscoveryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BD33152A5C403C007A6E53 /* DiscoveryView.swift */; };
|
96BD33162A5C403C007A6E53 /* PeeringView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BD33152A5C403C007A6E53 /* PeeringView.swift */; };
|
||||||
F581F59B2A5AE72F0081C383 /* BTCentral.swift in Sources */ = {isa = PBXBuildFile; fileRef = F581F59A2A5AE72F0081C383 /* BTCentral.swift */; };
|
F581F59B2A5AE72F0081C383 /* BluetoothController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F581F59A2A5AE72F0081C383 /* BluetoothController.swift */; };
|
||||||
F5847B622A599BF4009E28D4 /* Bodyy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5847B612A599BF4009E28D4 /* Bodyy.swift */; };
|
F5847B622A599BF4009E28D4 /* Body.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5847B612A599BF4009E28D4 /* Body.swift */; };
|
||||||
F5847B642A599CC3009E28D4 /* LogEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5847B632A599CC3009E28D4 /* LogEntry.swift */; };
|
F5847B642A599CC3009E28D4 /* LogEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5847B632A599CC3009E28D4 /* LogEntry.swift */; };
|
||||||
F5847B662A599EA4009E28D4 /* Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5847B652A599EA4009E28D4 /* Feed.swift */; };
|
F5847B662A599EA4009E28D4 /* Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5847B652A599EA4009E28D4 /* Feed.swift */; };
|
||||||
F5847B6A2A59AB24009E28D4 /* FeedStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5847B692A59AB24009E28D4 /* FeedStore.swift */; };
|
F5847B6A2A59AB24009E28D4 /* FeedStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5847B692A59AB24009E28D4 /* FeedStore.swift */; };
|
||||||
F58EB2D02A5590E800E22DA6 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = F58EB2CF2A5590E800E22DA6 /* README.md */; };
|
F58EB2D02A5590E800E22DA6 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = F58EB2CF2A5590E800E22DA6 /* README.md */; };
|
||||||
F59375722A5FF344001FA46A /* FeedDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F59375712A5FF344001FA46A /* FeedDetailView.swift */; };
|
|
||||||
F5A4B1212A5D4D1F00F5AE01 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A4B1202A5D4D1F00F5AE01 /* SettingsView.swift */; };
|
F5A4B1212A5D4D1F00F5AE01 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A4B1202A5D4D1F00F5AE01 /* SettingsView.swift */; };
|
||||||
F5A4B1232A5D5F8B00F5AE01 /* BTPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A4B1222A5D5F8B00F5AE01 /* BTPeripheral.swift */; };
|
F5A4B1232A5D5F8B00F5AE01 /* BTPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A4B1222A5D5F8B00F5AE01 /* BTPeripheral.swift */; };
|
||||||
F5A4B1252A5D7A8D00F5AE01 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A4B1242A5D7A8D00F5AE01 /* DataStore.swift */; };
|
F5A4B1252A5D7A8D00F5AE01 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A4B1242A5D7A8D00F5AE01 /* DataStore.swift */; };
|
||||||
F5A4B1272A5D861E00F5AE01 /* SettingsEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A4B1262A5D861E00F5AE01 /* SettingsEditView.swift */; };
|
F5A4B1272A5D861E00F5AE01 /* SettingsEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5A4B1262A5D861E00F5AE01 /* SettingsEditView.swift */; };
|
||||||
F5F1419C2A5EFA3600C81B1A /* LogEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F1419B2A5EFA3600C81B1A /* LogEntryView.swift */; };
|
|
||||||
F5F1419E2A5EFA4700C81B1A /* FeedCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F1419D2A5EFA4700C81B1A /* FeedCardView.swift */; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -62,22 +60,20 @@
|
|||||||
96454F352A558EBE0040BEBD /* RippleChatUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RippleChatUITests.swift; sourceTree = "<group>"; };
|
96454F352A558EBE0040BEBD /* RippleChatUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RippleChatUITests.swift; sourceTree = "<group>"; };
|
||||||
96454F372A558EBE0040BEBD /* RippleChatUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RippleChatUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
96454F372A558EBE0040BEBD /* RippleChatUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RippleChatUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
||||||
96454F442A5593900040BEBD /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
|
96454F442A5593900040BEBD /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
|
||||||
|
96BD330D2A5C254B007A6E53 /* TextApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextApp.swift; sourceTree = "<group>"; };
|
||||||
96BD330F2A5C27B0007A6E53 /* NewFeedEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewFeedEntryView.swift; sourceTree = "<group>"; };
|
96BD330F2A5C27B0007A6E53 /* NewFeedEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewFeedEntryView.swift; sourceTree = "<group>"; };
|
||||||
96BD33122A5C400B007A6E53 /* FeedListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListView.swift; sourceTree = "<group>"; };
|
96BD33122A5C400B007A6E53 /* FeedListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListView.swift; sourceTree = "<group>"; };
|
||||||
96BD33152A5C403C007A6E53 /* DiscoveryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryView.swift; sourceTree = "<group>"; };
|
96BD33152A5C403C007A6E53 /* PeeringView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeeringView.swift; sourceTree = "<group>"; };
|
||||||
F581F59A2A5AE72F0081C383 /* BTCentral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTCentral.swift; sourceTree = "<group>"; };
|
F581F59A2A5AE72F0081C383 /* BluetoothController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothController.swift; sourceTree = "<group>"; };
|
||||||
F5847B612A599BF4009E28D4 /* Bodyy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bodyy.swift; sourceTree = "<group>"; };
|
F5847B612A599BF4009E28D4 /* Body.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Body.swift; sourceTree = "<group>"; };
|
||||||
F5847B632A599CC3009E28D4 /* LogEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogEntry.swift; sourceTree = "<group>"; };
|
F5847B632A599CC3009E28D4 /* LogEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogEntry.swift; sourceTree = "<group>"; };
|
||||||
F5847B652A599EA4009E28D4 /* Feed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feed.swift; sourceTree = "<group>"; };
|
F5847B652A599EA4009E28D4 /* Feed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feed.swift; sourceTree = "<group>"; };
|
||||||
F5847B692A59AB24009E28D4 /* FeedStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedStore.swift; sourceTree = "<group>"; };
|
F5847B692A59AB24009E28D4 /* FeedStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedStore.swift; sourceTree = "<group>"; };
|
||||||
F58EB2CF2A5590E800E22DA6 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
F58EB2CF2A5590E800E22DA6 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
F59375712A5FF344001FA46A /* FeedDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedDetailView.swift; sourceTree = "<group>"; };
|
|
||||||
F5A4B1202A5D4D1F00F5AE01 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
F5A4B1202A5D4D1F00F5AE01 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||||
F5A4B1222A5D5F8B00F5AE01 /* BTPeripheral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPeripheral.swift; sourceTree = "<group>"; };
|
F5A4B1222A5D5F8B00F5AE01 /* BTPeripheral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPeripheral.swift; sourceTree = "<group>"; };
|
||||||
F5A4B1242A5D7A8D00F5AE01 /* DataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStore.swift; sourceTree = "<group>"; };
|
F5A4B1242A5D7A8D00F5AE01 /* DataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStore.swift; sourceTree = "<group>"; };
|
||||||
F5A4B1262A5D861E00F5AE01 /* SettingsEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsEditView.swift; sourceTree = "<group>"; };
|
F5A4B1262A5D861E00F5AE01 /* SettingsEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsEditView.swift; sourceTree = "<group>"; };
|
||||||
F5F1419B2A5EFA3600C81B1A /* LogEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogEntryView.swift; sourceTree = "<group>"; };
|
|
||||||
F5F1419D2A5EFA4700C81B1A /* FeedCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedCardView.swift; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -133,14 +129,15 @@
|
|||||||
96454F1A2A558EBC0040BEBD /* RippleChatApp.swift */,
|
96454F1A2A558EBC0040BEBD /* RippleChatApp.swift */,
|
||||||
96454F1C2A558EBC0040BEBD /* ContentView.swift */,
|
96454F1C2A558EBC0040BEBD /* ContentView.swift */,
|
||||||
96BD33112A5C3FFC007A6E53 /* Views */,
|
96BD33112A5C3FFC007A6E53 /* Views */,
|
||||||
F581F59A2A5AE72F0081C383 /* BTCentral.swift */,
|
F581F59A2A5AE72F0081C383 /* BluetoothController.swift */,
|
||||||
F5A4B1222A5D5F8B00F5AE01 /* BTPeripheral.swift */,
|
|
||||||
96454F1E2A558EBD0040BEBD /* Assets.xcassets */,
|
96454F1E2A558EBD0040BEBD /* Assets.xcassets */,
|
||||||
96454F202A558EBD0040BEBD /* Preview Content */,
|
96454F202A558EBD0040BEBD /* Preview Content */,
|
||||||
F5847B612A599BF4009E28D4 /* Bodyy.swift */,
|
F5847B612A599BF4009E28D4 /* Body.swift */,
|
||||||
F5847B632A599CC3009E28D4 /* LogEntry.swift */,
|
F5847B632A599CC3009E28D4 /* LogEntry.swift */,
|
||||||
F5847B652A599EA4009E28D4 /* Feed.swift */,
|
F5847B652A599EA4009E28D4 /* Feed.swift */,
|
||||||
F5847B692A59AB24009E28D4 /* FeedStore.swift */,
|
F5847B692A59AB24009E28D4 /* FeedStore.swift */,
|
||||||
|
96BD330D2A5C254B007A6E53 /* TextApp.swift */,
|
||||||
|
F5A4B1222A5D5F8B00F5AE01 /* BTPeripheral.swift */,
|
||||||
F5A4B1242A5D7A8D00F5AE01 /* DataStore.swift */,
|
F5A4B1242A5D7A8D00F5AE01 /* DataStore.swift */,
|
||||||
);
|
);
|
||||||
path = RippleChat;
|
path = RippleChat;
|
||||||
@ -176,12 +173,9 @@
|
|||||||
children = (
|
children = (
|
||||||
96BD330F2A5C27B0007A6E53 /* NewFeedEntryView.swift */,
|
96BD330F2A5C27B0007A6E53 /* NewFeedEntryView.swift */,
|
||||||
96BD33122A5C400B007A6E53 /* FeedListView.swift */,
|
96BD33122A5C400B007A6E53 /* FeedListView.swift */,
|
||||||
96BD33152A5C403C007A6E53 /* DiscoveryView.swift */,
|
96BD33152A5C403C007A6E53 /* PeeringView.swift */,
|
||||||
F5A4B1202A5D4D1F00F5AE01 /* SettingsView.swift */,
|
F5A4B1202A5D4D1F00F5AE01 /* SettingsView.swift */,
|
||||||
F5A4B1262A5D861E00F5AE01 /* SettingsEditView.swift */,
|
F5A4B1262A5D861E00F5AE01 /* SettingsEditView.swift */,
|
||||||
F5F1419B2A5EFA3600C81B1A /* LogEntryView.swift */,
|
|
||||||
F5F1419D2A5EFA4700C81B1A /* FeedCardView.swift */,
|
|
||||||
F59375712A5FF344001FA46A /* FeedDetailView.swift */,
|
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -318,22 +312,20 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
F5847B622A599BF4009E28D4 /* Bodyy.swift in Sources */,
|
F5847B622A599BF4009E28D4 /* Body.swift in Sources */,
|
||||||
96BD33162A5C403C007A6E53 /* DiscoveryView.swift in Sources */,
|
96BD33162A5C403C007A6E53 /* PeeringView.swift in Sources */,
|
||||||
F5847B662A599EA4009E28D4 /* Feed.swift in Sources */,
|
F5847B662A599EA4009E28D4 /* Feed.swift in Sources */,
|
||||||
96BD33132A5C400B007A6E53 /* FeedListView.swift in Sources */,
|
96BD33132A5C400B007A6E53 /* FeedListView.swift in Sources */,
|
||||||
F5847B642A599CC3009E28D4 /* LogEntry.swift in Sources */,
|
F5847B642A599CC3009E28D4 /* LogEntry.swift in Sources */,
|
||||||
96BD33102A5C27B0007A6E53 /* NewFeedEntryView.swift in Sources */,
|
96BD33102A5C27B0007A6E53 /* NewFeedEntryView.swift in Sources */,
|
||||||
F5A4B1232A5D5F8B00F5AE01 /* BTPeripheral.swift in Sources */,
|
F5A4B1232A5D5F8B00F5AE01 /* BTPeripheral.swift in Sources */,
|
||||||
F5A4B1252A5D7A8D00F5AE01 /* DataStore.swift in Sources */,
|
F5A4B1252A5D7A8D00F5AE01 /* DataStore.swift in Sources */,
|
||||||
F5F1419C2A5EFA3600C81B1A /* LogEntryView.swift in Sources */,
|
|
||||||
F5847B6A2A59AB24009E28D4 /* FeedStore.swift in Sources */,
|
F5847B6A2A59AB24009E28D4 /* FeedStore.swift in Sources */,
|
||||||
F5A4B1272A5D861E00F5AE01 /* SettingsEditView.swift in Sources */,
|
F5A4B1272A5D861E00F5AE01 /* SettingsEditView.swift in Sources */,
|
||||||
F59375722A5FF344001FA46A /* FeedDetailView.swift in Sources */,
|
F581F59B2A5AE72F0081C383 /* BluetoothController.swift in Sources */,
|
||||||
F5F1419E2A5EFA4700C81B1A /* FeedCardView.swift in Sources */,
|
|
||||||
F581F59B2A5AE72F0081C383 /* BTCentral.swift in Sources */,
|
|
||||||
96454F1D2A558EBC0040BEBD /* ContentView.swift in Sources */,
|
96454F1D2A558EBC0040BEBD /* ContentView.swift in Sources */,
|
||||||
F5A4B1212A5D4D1F00F5AE01 /* SettingsView.swift in Sources */,
|
F5A4B1212A5D4D1F00F5AE01 /* SettingsView.swift in Sources */,
|
||||||
|
96BD330E2A5C254B007A6E53 /* TextApp.swift in Sources */,
|
||||||
96454F1B2A558EBC0040BEBD /* RippleChatApp.swift in Sources */,
|
96454F1B2A558EBC0040BEBD /* RippleChatApp.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -524,7 +516,7 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"RippleChat/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"RippleChat/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = GN2B48NJ47;
|
DEVELOPMENT_TEAM = B5S58UWR64;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Allow for bluetooth use";
|
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Allow for bluetooth use";
|
||||||
@ -539,7 +531,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = unibas.inetsec.RippleChat1;
|
PRODUCT_BUNDLE_IDENTIFIER = unibas.inetsec.RippleChat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|||||||
@ -1,120 +0,0 @@
|
|||||||
//
|
|
||||||
// BluetoothController.swift
|
|
||||||
// RippleChat
|
|
||||||
//
|
|
||||||
// Created by Severin Memmishofer on 09.07.23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import CoreBluetooth
|
|
||||||
|
|
||||||
class BTCentral: NSObject, ObservableObject {
|
|
||||||
private var centralManager: CBCentralManager?
|
|
||||||
private var peripherals: [CBPeripheral] = []
|
|
||||||
@Published var peripheralNames: [String] = []
|
|
||||||
|
|
||||||
var writeCharacteristics: [CBCharacteristic] = []
|
|
||||||
|
|
||||||
let BLE_SERVICE_UUID = CBUUID(string: "6e400001-7646-4b5b-9a50-71becce51558")
|
|
||||||
let BLE_CHARACTERISTIC_UUID_RX = CBUUID(string: "6e400002-7646-4b5b-9a50-71becce51558")
|
|
||||||
|
|
||||||
override init() {
|
|
||||||
super.init()
|
|
||||||
self.centralManager = CBCentralManager(delegate: self, queue: .main)
|
|
||||||
}
|
|
||||||
|
|
||||||
// func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension BTCentral: CBCentralManagerDelegate, CBPeripheralDelegate {
|
|
||||||
|
|
||||||
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
|
||||||
if central.state == .poweredOn {
|
|
||||||
print("Device is powered on...")
|
|
||||||
self.centralManager?.scanForPeripherals(withServices: [BLE_SERVICE_UUID])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
|
|
||||||
if !peripherals.contains(peripheral) {
|
|
||||||
peripheral.delegate = self
|
|
||||||
centralManager!.connect(peripheral)
|
|
||||||
self.peripherals.append(peripheral)
|
|
||||||
self.peripheralNames.append(peripheral.name ?? "unnamed device")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
|
|
||||||
peripheral.discoverServices([BLE_SERVICE_UUID])
|
|
||||||
print("Connected to device \(String(describing: peripheral.name))")
|
|
||||||
if(centralManager?.delegate == nil) {
|
|
||||||
print("central is nil")
|
|
||||||
} else {
|
|
||||||
print("central is not nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {
|
|
||||||
// Not implemented yet
|
|
||||||
}
|
|
||||||
|
|
||||||
// func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
|
|
||||||
// print("Discovering services...")
|
|
||||||
// peripheral.discoverCharacteristics(BLE_CHARACTERISTIC_UUID_RX)
|
|
||||||
// }
|
|
||||||
|
|
||||||
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
|
|
||||||
print("*******************************************************")
|
|
||||||
|
|
||||||
if ((error) != nil) {
|
|
||||||
print("Error discovering services: \(error!.localizedDescription)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let services = peripheral.services else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//We need to discover the all characteristic
|
|
||||||
for service in services {
|
|
||||||
peripheral.discoverCharacteristics(nil, for: service)
|
|
||||||
}
|
|
||||||
print("Discovered Services: \(services)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
|
|
||||||
guard let characteristics = service.characteristics else {
|
|
||||||
print("No characteristics found for service \(service.uuid)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for characteristic in characteristics {
|
|
||||||
if characteristic.uuid.isEqual(BLE_CHARACTERISTIC_UUID_RX) {
|
|
||||||
self.writeCharacteristics.append(characteristic)
|
|
||||||
peripheral.setNotifyValue(true, for: characteristic)
|
|
||||||
peripheral.readValue(for: characteristic)
|
|
||||||
print("Characteristic: \(characteristic.uuid)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
|
|
||||||
print("updating characteristic value...")
|
|
||||||
print(characteristic.value ?? "Characteristic is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeToCharacteristics(message: String) {
|
|
||||||
guard let messageData = message.data(using: .utf8) else {
|
|
||||||
print("Could not convert message to data.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
print("Writing to Characteristic...")
|
|
||||||
// Go through
|
|
||||||
for characteristic in writeCharacteristics {
|
|
||||||
// Go through connected peripherals and write to their characteristic
|
|
||||||
for peripheral in peripherals {
|
|
||||||
peripheral.writeValue(messageData, for: characteristic, type: .withoutResponse)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -10,8 +10,6 @@ import CoreBluetooth
|
|||||||
|
|
||||||
class BluetoothPeripheral: NSObject, ObservableObject {
|
class BluetoothPeripheral: NSObject, ObservableObject {
|
||||||
|
|
||||||
@Published var incomingMsg: String = ""
|
|
||||||
@Published var wantVector: WantMessage = WantMessage()
|
|
||||||
private var peripheralManager: CBPeripheralManager?
|
private var peripheralManager: CBPeripheralManager?
|
||||||
|
|
||||||
let BLE_SERVICE_UUID = CBUUID(string: "6e400001-7646-4b5b-9a50-71becce51558")
|
let BLE_SERVICE_UUID = CBUUID(string: "6e400001-7646-4b5b-9a50-71becce51558")
|
||||||
@ -23,101 +21,64 @@ class BluetoothPeripheral: NSObject, ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Change variable names, etc...
|
||||||
|
|
||||||
extension BluetoothPeripheral: CBPeripheralManagerDelegate {
|
extension BluetoothPeripheral: CBPeripheralManagerDelegate {
|
||||||
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
|
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
|
||||||
switch peripheral.state {
|
switch peripheral.state {
|
||||||
case .unknown:
|
case .unknown:
|
||||||
print("BT Device is UNKNOWN")
|
print("Bluetooth Device is UNKNOWN")
|
||||||
case .unsupported:
|
case .unsupported:
|
||||||
print("BT Device is UNSUPPORTED")
|
print("Bluetooth Device is UNSUPPORTED")
|
||||||
case .unauthorized:
|
case .unauthorized:
|
||||||
print("BT Device is UNAUTHORIZED")
|
print("Bluetooth Device is UNAUTHORIZED")
|
||||||
case .resetting:
|
case .resetting:
|
||||||
print("BT Device is RESETTING")
|
print("Bluetooth Device is RESETTING")
|
||||||
case .poweredOff:
|
case .poweredOff:
|
||||||
print("BT Device is POWERED OFF")
|
print("Bluetooth Device is POWERED OFF")
|
||||||
case .poweredOn:
|
case .poweredOn:
|
||||||
print("BT Device is POWERED ON")
|
print("Bluetooth Device is POWERED ON")
|
||||||
addBTService()
|
addServices()
|
||||||
@unknown default:
|
@unknown default:
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addBTService() {
|
func addServices() {
|
||||||
let myCharacteristic = CBMutableCharacteristic(type: BLE_CHARACTERISTIC_UUID_RX, properties: [.read, .write, .notify, .writeWithoutResponse], value: nil, permissions: [.readable, .writeable])
|
|
||||||
|
let myCharacteristic = CBMutableCharacteristic(type: BLE_CHARACTERISTIC_UUID_RX, properties: [.read, .write, .notify], value: nil, permissions: [.readable])
|
||||||
|
|
||||||
|
// 2. Create instance of CBMutableService
|
||||||
let myService = CBMutableService(type: BLE_SERVICE_UUID, primary: true)
|
let myService = CBMutableService(type: BLE_SERVICE_UUID, primary: true)
|
||||||
|
|
||||||
|
// 3. Add characteristics to the service
|
||||||
myService.characteristics = [myCharacteristic]
|
myService.characteristics = [myCharacteristic]
|
||||||
|
|
||||||
|
// 4. Add service to peripheralManager
|
||||||
peripheralManager!.add(myService)
|
peripheralManager!.add(myService)
|
||||||
}
|
|
||||||
|
// 5. Start advertising
|
||||||
func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) {
|
peripheralManager!.startAdvertising([CBAdvertisementDataLocalNameKey : "RippleChat", CBAdvertisementDataServiceUUIDsKey : BLE_SERVICE_UUID])
|
||||||
peripheralManager!.startAdvertising([CBAdvertisementDataLocalNameKey : "RippleChat", CBAdvertisementDataServiceUUIDsKey: [BLE_SERVICE_UUID]])
|
|
||||||
print("Started Advertising")
|
print("Started Advertising")
|
||||||
if(peripheralManager?.delegate == nil) {
|
|
||||||
print("peripheral is nil")
|
|
||||||
} else {
|
|
||||||
print("peripheral is not nil")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func discoverServices(_ serviceUUIDs: [CBUUID]?) {
|
// func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
|
||||||
print("test Peripheral")
|
//
|
||||||
print("Discovering services... \(String(describing: serviceUUIDs))")
|
// messageLabel.text = "Data getting Read"
|
||||||
}
|
// readValueLabel.text = value
|
||||||
|
|
||||||
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
|
|
||||||
print("Discovering Services Peripheral")
|
|
||||||
// print("*******************************************************")
|
|
||||||
//
|
//
|
||||||
// if ((error) != nil) {
|
// // Perform your additional operations here
|
||||||
// print("Error discovering services: \(error!.localizedDescription)")
|
//
|
||||||
// return
|
// }
|
||||||
// }
|
//
|
||||||
// guard let services = peripheral.services else {
|
// func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
|
||||||
// return
|
//
|
||||||
// }
|
// messageLabel.text = "Writing Data"
|
||||||
// //We need to discover the all characteristic
|
//
|
||||||
// for service in services {
|
// if let value = requests.first?.value {
|
||||||
// peripheral.discoverCharacteristics(nil, for: service)
|
// writeValueLabel.text = value.hexEncodedString()
|
||||||
// }
|
// //Perform here your additional operations on the data you get
|
||||||
// print("Discovered Services: \(services)")
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
|
|
||||||
for request in requests {
|
|
||||||
if let value = request.value {
|
|
||||||
// Handle the received data
|
|
||||||
let receivedData = Data(value)
|
|
||||||
|
|
||||||
print(receivedData)
|
|
||||||
|
|
||||||
// Decode the received JSON string into your data structure
|
|
||||||
let decoder = JSONDecoder()
|
|
||||||
do {
|
|
||||||
let receivedObject = try decoder.decode(WantMessage.self, from: receivedData)
|
|
||||||
// Use the received object to update your app state as needed
|
|
||||||
print("Received Write")
|
|
||||||
self.incomingMsg = ""
|
|
||||||
self.incomingMsg = receivedObject.printMsg()
|
|
||||||
self.wantVector = receivedObject
|
|
||||||
print(receivedObject.printMsg())
|
|
||||||
} catch {
|
|
||||||
print("Failed to decode JSON: \(error)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// If you want to write back to the central
|
|
||||||
// if let central = request.central {
|
|
||||||
// let dataToWrite = // some data you want to send back
|
|
||||||
// let writeType: CBCharacteristicWriteType = // choose .withResponse or .withoutResponse
|
|
||||||
// central.writeValue(dataToWrite, for: request.characteristic, type: writeType)
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Respond to the write request
|
|
||||||
peripheral.respond(to: request, withResult: .success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
38
RippleChat/BluetoothController.swift
Normal file
38
RippleChat/BluetoothController.swift
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// BluetoothController.swift
|
||||||
|
// RippleChat
|
||||||
|
//
|
||||||
|
// Created by Severin Memmishofer on 09.07.23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import CoreBluetooth
|
||||||
|
|
||||||
|
class BluetoothController: NSObject, ObservableObject {
|
||||||
|
private var centralManager: CBCentralManager?
|
||||||
|
private var peripherals: [CBPeripheral] = []
|
||||||
|
@Published var peripheralNames: [String] = []
|
||||||
|
|
||||||
|
let BLE_SERVICE_UUID = CBUUID(string: "6e400001-7646-4b5b-9a50-71becce51558")
|
||||||
|
let BLE_CHARACTERISTIC_UUID_RX = CBUUID(string: "6e400002-7646-4b5b-9a50-71becce51558")
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
super.init()
|
||||||
|
self.centralManager = CBCentralManager(delegate: self, queue: .main)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BluetoothController: CBCentralManagerDelegate {
|
||||||
|
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
||||||
|
if central.state == .poweredOn {
|
||||||
|
self.centralManager?.scanForPeripherals(withServices: [BLE_SERVICE_UUID])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
|
||||||
|
if !peripherals.contains(peripheral) {
|
||||||
|
self.peripherals.append(peripheral)
|
||||||
|
self.peripheralNames.append(peripheral.name ?? "unnamed device")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,10 +5,9 @@
|
|||||||
// Created by Severin Memmishofer on 08.07.23.
|
// Created by Severin Memmishofer on 08.07.23.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Bodyy: Codable {
|
struct Body: Codable {
|
||||||
|
|
||||||
let tag: String
|
let tag: String
|
||||||
let value: String
|
let value: String
|
||||||
@ -10,44 +10,34 @@ import CoreBluetooth
|
|||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@State var currentView = 0
|
@State var currentView = 0
|
||||||
@EnvironmentObject var dataStore: DataStore
|
@StateObject var dataStore = DataStore()
|
||||||
@StateObject private var bluetoothController = BTCentral()
|
|
||||||
@StateObject private var bluetoothPeripheral = BluetoothPeripheral()
|
|
||||||
@Environment(\.scenePhase) private var scenePhase
|
|
||||||
let saveAction: ()->Void
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
switch self.currentView {
|
switch self.currentView {
|
||||||
case 0:
|
case 0:
|
||||||
DiscoveryView()
|
PeeringView()
|
||||||
.environmentObject(dataStore)
|
.environmentObject(dataStore)
|
||||||
.environmentObject(bluetoothController)
|
|
||||||
.environmentObject(bluetoothPeripheral)
|
|
||||||
.navigationTitle("Peering")
|
|
||||||
case 1:
|
case 1:
|
||||||
FeedListView()
|
FeedListView(feeds: [])
|
||||||
.environmentObject(dataStore)
|
.environmentObject(dataStore)
|
||||||
.environmentObject(bluetoothController)
|
|
||||||
.environmentObject(bluetoothPeripheral)
|
|
||||||
.navigationTitle("Feeds")
|
|
||||||
case 2:
|
case 2:
|
||||||
SettingsView()
|
SettingsView()
|
||||||
.environmentObject(dataStore)
|
.environmentObject(dataStore)
|
||||||
.environmentObject(bluetoothController)
|
|
||||||
.environmentObject(bluetoothPeripheral)
|
|
||||||
.navigationTitle("Settings")
|
.navigationTitle("Settings")
|
||||||
default:
|
default:
|
||||||
FeedListView()
|
FeedListView(feeds: [])
|
||||||
.environmentObject(dataStore)
|
.environmentObject(dataStore)
|
||||||
}
|
}
|
||||||
HStack {
|
}
|
||||||
Spacer()
|
.padding()
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .bottomBar) {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
self.currentView = 0
|
self.currentView = 0
|
||||||
}) {
|
}) {
|
||||||
VStack {
|
VStack {
|
||||||
Image(systemName: "dot.radiowaves.left.and.right")
|
Label("Discovery", systemImage: "dot.radiowaves.left.and.right")
|
||||||
Text("Discovery")
|
Text("Discovery")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +46,7 @@ struct ContentView: View {
|
|||||||
self.currentView = 1
|
self.currentView = 1
|
||||||
}) {
|
}) {
|
||||||
VStack {
|
VStack {
|
||||||
Image(systemName: "person.2")
|
Label("Feeds", systemImage: "person.2")
|
||||||
Text("Feeds")
|
Text("Feeds")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,24 +55,19 @@ struct ContentView: View {
|
|||||||
self.currentView = 2
|
self.currentView = 2
|
||||||
}) {
|
}) {
|
||||||
VStack {
|
VStack {
|
||||||
Image(systemName: "gear")
|
Label("Settings", systemImage: "gear")
|
||||||
Text("Settings")
|
Text("Settings")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer()
|
|
||||||
}
|
}
|
||||||
.frame(height: UIScreen.main.bounds.height * 0.05)
|
|
||||||
}
|
}
|
||||||
.onChange(of: scenePhase) { phase in
|
|
||||||
if phase == .inactive { saveAction() }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentView_Previews: PreviewProvider {
|
struct ContentView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ContentView(saveAction: {})
|
ContentView()
|
||||||
.environmentObject(DataStore())
|
.environmentObject(DataStore())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,128 +8,16 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
@MainActor
|
|
||||||
class DataStore: ObservableObject {
|
class DataStore: ObservableObject {
|
||||||
|
|
||||||
typealias FID = String
|
|
||||||
typealias SEQ = Int
|
|
||||||
|
|
||||||
@Published var personalID: String
|
@Published var personalID: String
|
||||||
@Published var personalFeed: Feed
|
@Published var friends: [String]
|
||||||
@Published var friends: [FID:SEQ]
|
@Published var feeds: [Feed]
|
||||||
@Published var feedStores: [FeedStore]
|
|
||||||
|
|
||||||
|
init(personalID: String = "", friends: [String] = [], feeds: [Feed] = []) {
|
||||||
init(personalID: String = "", personalFeed: Feed = Feed(), friends: [String:Int] = [:], feedStores: [FeedStore] = []) {
|
|
||||||
self.personalID = personalID
|
self.personalID = personalID
|
||||||
self.friends = friends
|
self.friends = friends
|
||||||
self.feedStores = feedStores
|
self.feeds = feeds
|
||||||
self.personalFeed = personalFeed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private func fileURL(for filename: String) throws -> URL {
|
|
||||||
try FileManager.default.url(for: .documentDirectory,
|
|
||||||
in: .userDomainMask,
|
|
||||||
appropriateFor: nil,
|
|
||||||
create: false)
|
|
||||||
.appendingPathComponent("\(filename).json")
|
|
||||||
}
|
|
||||||
|
|
||||||
func savePersonalID() async throws {
|
|
||||||
let task = Task {
|
|
||||||
let data = try JSONEncoder().encode(personalID)
|
|
||||||
let outfile = try fileURL(for: "personalID")
|
|
||||||
try data.write(to: outfile)
|
|
||||||
}
|
|
||||||
_ = try await task.value
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveFriends() async throws {
|
|
||||||
let task = Task {
|
|
||||||
let data = try JSONEncoder().encode(friends)
|
|
||||||
let outfile = try fileURL(for: "friends")
|
|
||||||
try data.write(to: outfile)
|
|
||||||
}
|
|
||||||
_ = try await task.value
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadPersonalID() async throws {
|
|
||||||
let task = Task<String, Error> {
|
|
||||||
let fileURL = try self.fileURL(for: "personalID")
|
|
||||||
guard let data = try? Data(contentsOf: fileURL) else {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
let personalID = try JSONDecoder().decode(String.self, from: data)
|
|
||||||
return personalID
|
|
||||||
}
|
|
||||||
let personalID = try await task.value
|
|
||||||
self.personalID = personalID
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadFriends() async throws {
|
|
||||||
let task = Task<[String:Int], Error> {
|
|
||||||
let fileURL = try self.fileURL(for: "friends")
|
|
||||||
guard let data = try? Data(contentsOf: fileURL) else {
|
|
||||||
return [:]
|
|
||||||
}
|
|
||||||
let friends = try JSONDecoder().decode([String:Int].self, from: data)
|
|
||||||
return friends
|
|
||||||
}
|
|
||||||
let friends = try await task.value
|
|
||||||
self.friends = friends
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadFeedStores() async throws {
|
|
||||||
let task = Task<[FeedStore], Error> {
|
|
||||||
var feedStores: [FeedStore] = []
|
|
||||||
for feedStore in self.feedStores {
|
|
||||||
try await feedStore.load()
|
|
||||||
feedStores.append(feedStore)
|
|
||||||
}
|
|
||||||
return feedStores
|
|
||||||
}
|
|
||||||
let feedStores = try await task.value
|
|
||||||
self.feedStores = feedStores
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveFeedStores() async throws {
|
|
||||||
let task = Task {
|
|
||||||
for feedStore in self.feedStores {
|
|
||||||
try await feedStore.save(feed: feedStore.feed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ = try await task.value
|
|
||||||
}
|
|
||||||
|
|
||||||
func savePersonalFeed() async throws {
|
|
||||||
let task = Task {
|
|
||||||
let data = try JSONEncoder().encode(personalFeed)
|
|
||||||
let outfile = try fileURL(for: "personalFeed")
|
|
||||||
try data.write(to: outfile)
|
|
||||||
}
|
|
||||||
_ = try await task.value
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadPersonalFeed() async throws {
|
|
||||||
let task = Task<Feed, Error> {
|
|
||||||
let fileURL = try self.fileURL(for: "personalFeed")
|
|
||||||
guard let data = try? Data(contentsOf: fileURL) else {
|
|
||||||
return Feed(feedID: self.personalID)
|
|
||||||
}
|
|
||||||
let personalFeed = try JSONDecoder().decode(Feed.self, from: data)
|
|
||||||
return personalFeed
|
|
||||||
}
|
|
||||||
let personalFeed = try await task.value
|
|
||||||
self.personalFeed = personalFeed
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extension DataStore {
|
|
||||||
static let sampleDataStore = DataStore(personalID: "BOB", friends: SettingsView_Previews.friends, feedStores: [FeedStore(feed: Feed.sampleFeed), FeedStore(feed: Feed.sampleFeed2)])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,42 +10,30 @@ import Foundation
|
|||||||
struct Feed: Codable {
|
struct Feed: Codable {
|
||||||
|
|
||||||
let feedID: String
|
let feedID: String
|
||||||
var feed: [LogEntry]
|
let feed: [LogEntry]
|
||||||
|
|
||||||
init(feedID: String = "", feed: [LogEntry] = []) {
|
init(feedID: String = "", feed: [LogEntry] = []) {
|
||||||
self.feedID = feedID
|
self.feedID = feedID
|
||||||
self.feed = feed
|
self.feed = feed
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLastLogEntry() -> LogEntry {
|
|
||||||
if self.feed.isEmpty {
|
|
||||||
return LogEntry()
|
|
||||||
} else {
|
|
||||||
return self.feed.last!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mutating func appendLogEntry(log: LogEntry) {
|
|
||||||
self.feed.append(log)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Feed {
|
extension Feed {
|
||||||
|
|
||||||
static let sampleData: [LogEntry] =
|
static let sampleData: [LogEntry] =
|
||||||
[
|
[
|
||||||
LogEntry(feedid: "BOB", sequenceNumber: 1, body: Bodyy(tag: Apps.nam, value: "Bob")),
|
LogEntry(feedid: "BOB", sequenceNumber: 1, body: Body(tag: Apps.nam, value: "Bob")),
|
||||||
LogEntry(feedid: "BOB", sequenceNumber: 2, body: Bodyy(tag: Apps.txt, value: "My first post!")),
|
LogEntry(feedid: "BOB", sequenceNumber: 2, body: Body(tag: Apps.txt, value: "My first post!")),
|
||||||
LogEntry(feedid: "BOB", sequenceNumber: 3, body: Bodyy(tag: Apps.txt, value: "Welcome Alice"))
|
LogEntry(feedid: "BOB", sequenceNumber: 3, body: Body(tag: Apps.txt, value: "Welcome Alice"))
|
||||||
]
|
]
|
||||||
|
|
||||||
static let sampleData2: [LogEntry] =
|
static let sampleData2: [LogEntry] =
|
||||||
[
|
[
|
||||||
LogEntry(feedid: "ALI", sequenceNumber: 1, body: Bodyy(tag: Apps.nam, value: "Alice")),
|
LogEntry(feedid: "ALI", sequenceNumber: 1, body: Body(tag: Apps.nam, value: "Alice")),
|
||||||
LogEntry(feedid: "ALI", sequenceNumber: 2, body: Bodyy(tag: Apps.txt, value: "Alice' first post!")),
|
LogEntry(feedid: "ALI", sequenceNumber: 2, body: Body(tag: Apps.txt, value: "Alice' first post!")),
|
||||||
LogEntry(feedid: "ALI", sequenceNumber: 3, body: Bodyy(tag: Apps.txt, value: "Welcome Bob")),
|
LogEntry(feedid: "ALI", sequenceNumber: 3, body: Body(tag: Apps.txt, value: "Welcome Bob")),
|
||||||
LogEntry(feedid: "ALI", sequenceNumber: 4, body: Bodyy(tag: Apps.txt, value: "Whaddup DAWG"))
|
LogEntry(feedid: "ALI", sequenceNumber: 4, body: Body(tag: Apps.txt, value: "Whaddup DAWG"))
|
||||||
]
|
]
|
||||||
|
|
||||||
static let sampleFeed: Feed = Feed(feedID: "BOB", feed: sampleData)
|
static let sampleFeed: Feed = Feed(feedID: "BOB", feed: sampleData)
|
||||||
|
|||||||
@ -8,9 +8,8 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class FeedStore: ObservableObject, Identifiable {
|
class FeedStore: ObservableObject {
|
||||||
|
|
||||||
let id: UUID = UUID()
|
|
||||||
@Published var feed: Feed
|
@Published var feed: Feed
|
||||||
|
|
||||||
init(feed: Feed) {
|
init(feed: Feed) {
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
//
|
|
||||||
// File.swift
|
|
||||||
// RippleChat
|
|
||||||
//
|
|
||||||
// Created by Severin Memmishofer on 13.07.23.
|
|
||||||
//
|
|
||||||
|
|
||||||
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
|
|
||||||
if let error = error {
|
|
||||||
print("Error discovering services: \(error.localizedDescription)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for service in peripheral.services ?? [] {
|
|
||||||
if service.uuid == CBUUID(string: "YourServiceUUIDHere") {
|
|
||||||
peripheral.discoverCharacteristics([CBUUID(string: "YourCharacteristicUUIDHere")], for: service)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
|
|
||||||
if let error = error {
|
|
||||||
print("Error discovering characteristics: \(error.localizedDescription)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for characteristic in service.characteristics ?? [] {
|
|
||||||
if characteristic.uuid == CBUUID(string: "YourCharacteristicUUIDHere") {
|
|
||||||
// You've found your characteristic!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,22 +7,16 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct LogEntry: Codable, Identifiable {
|
struct LogEntry: Codable {
|
||||||
|
|
||||||
var id: UUID = UUID()
|
|
||||||
|
|
||||||
let feedid: String
|
let feedid: String
|
||||||
let sequenceNumber: Int
|
let sequenceNumber: Int
|
||||||
let body: Bodyy
|
let body: Body
|
||||||
|
|
||||||
init(feedid: String = "", sequenceNumber: Int = 0, body: Bodyy = Bodyy()) {
|
init(feedid: String, sequenceNumber: Int, body: Body) {
|
||||||
self.feedid = feedid
|
self.feedid = feedid
|
||||||
self.sequenceNumber = sequenceNumber
|
self.sequenceNumber = sequenceNumber
|
||||||
self.body = body
|
self.body = body
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LogEntry {
|
|
||||||
static let sampleLogEntry = LogEntry(feedid: "BOB", sequenceNumber: 2, body: Bodyy(tag: Apps.txt, value: "My first post!"))
|
|
||||||
}
|
|
||||||
|
|||||||
@ -9,35 +9,10 @@ import SwiftUI
|
|||||||
|
|
||||||
@main
|
@main
|
||||||
struct RippleChatApp: App {
|
struct RippleChatApp: App {
|
||||||
@StateObject private var dataStore = DataStore()
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
ContentView() {
|
ContentView()
|
||||||
Task {
|
|
||||||
do {
|
|
||||||
try await dataStore.savePersonalID()
|
|
||||||
try await dataStore.savePersonalFeed()
|
|
||||||
try await dataStore.saveFriends()
|
|
||||||
try await dataStore.saveFeedStores()
|
|
||||||
} catch {
|
|
||||||
fatalError(error.localizedDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.environmentObject(dataStore)
|
|
||||||
.task {
|
|
||||||
do {
|
|
||||||
try await dataStore.loadPersonalID()
|
|
||||||
try await dataStore.loadPersonalFeed()
|
|
||||||
try await dataStore.loadFriends()
|
|
||||||
try await dataStore.loadFeedStores()
|
|
||||||
} catch {
|
|
||||||
// Handle the error
|
|
||||||
print("Error loading data: \(error)")
|
|
||||||
fatalError(error.localizedDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,80 +0,0 @@
|
|||||||
//
|
|
||||||
// PeeringView.swift
|
|
||||||
// RippleChat
|
|
||||||
//
|
|
||||||
// Created by Sebastian Lenzlinger on 10.07.23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct DiscoveryView: View {
|
|
||||||
@EnvironmentObject var dataStore: DataStore
|
|
||||||
@EnvironmentObject var btCentral: BTCentral
|
|
||||||
@EnvironmentObject var btPeripheral: BluetoothPeripheral
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
NavigationStack {
|
|
||||||
List(btCentral.peripheralNames, id: \.self) { peripheral in
|
|
||||||
Text(peripheral)
|
|
||||||
}
|
|
||||||
.navigationTitle("Peer Discovery")
|
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
|
||||||
List {
|
|
||||||
ForEach(btPeripheral.wantVector.friends.keys.sorted(), id: \.self) { friend in
|
|
||||||
Text("Feed: \(friend.description), SEQ: \(btPeripheral.wantVector.friends[friend] ?? -1)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text("Incoming msg: \(btPeripheral.incomingMsg)")
|
|
||||||
.onChange(of: btPeripheral.incomingMsg) { newValue in
|
|
||||||
compareWithSavedFeeds(newVector: btPeripheral.wantVector)
|
|
||||||
}
|
|
||||||
Button(action: {
|
|
||||||
do {
|
|
||||||
var combinedDict = dataStore.friends
|
|
||||||
combinedDict[dataStore.personalFeed.feedID] = dataStore.personalFeed.feed.count
|
|
||||||
let WANT_msg = WantMessage(friends: combinedDict)
|
|
||||||
let encoded_msg = try JSONEncoder().encode(WANT_msg)
|
|
||||||
btCentral.writeToCharacteristics(message: String(data: encoded_msg, encoding: .utf8)!)
|
|
||||||
//btController.writeToCharacteristics(message: "Test")
|
|
||||||
print("Pressed Button")
|
|
||||||
} catch {
|
|
||||||
fatalError(error.localizedDescription)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Text("Send WANT-Vector")
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareWithSavedFeeds(newVector: WantMessage) {
|
|
||||||
print("comparing with saved Feeds...")
|
|
||||||
for friend in newVector.friends.keys.sorted() {
|
|
||||||
if(dataStore.friends.keys.contains(friend.description)) {
|
|
||||||
print("Found friend \(friend.description) in own Feeds!")
|
|
||||||
var missingFeedEntries: Int = -1
|
|
||||||
if let ownCount = dataStore.friends[friend] {
|
|
||||||
missingFeedEntries = newVector.friends[friend]! - ownCount
|
|
||||||
}
|
|
||||||
print("You are \(missingFeedEntries) behind on the feed of \(friend.description)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DiscoveryView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
DiscoveryView()
|
|
||||||
.environmentObject(BluetoothPeripheral())
|
|
||||||
.environmentObject(BTCentral())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WantMessage: Codable {
|
|
||||||
var command = "WANT"
|
|
||||||
var friends = [String:Int]()
|
|
||||||
|
|
||||||
func printMsg() -> String {
|
|
||||||
return ("{\(command) : \(friends.description)}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
//
|
|
||||||
// FeedDetailView.swift
|
|
||||||
// RippleChat
|
|
||||||
//
|
|
||||||
// Created by Severin Memmishofer on 12.07.23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct FeedCardView: View {
|
|
||||||
@EnvironmentObject var dataStore: DataStore
|
|
||||||
var feed: Feed
|
|
||||||
|
|
||||||
private var lastLogEntry: LogEntry {
|
|
||||||
if feed.feed.isEmpty {
|
|
||||||
return LogEntry()
|
|
||||||
} else {
|
|
||||||
return feed.feed.last!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
VStack (alignment: .leading) {
|
|
||||||
HStack {
|
|
||||||
Text("feedID: \(feed.feedID)")
|
|
||||||
Spacer()
|
|
||||||
Text("SEQ: \(lastLogEntry.sequenceNumber)")
|
|
||||||
}
|
|
||||||
Text("Last: \(lastLogEntry.body.value)")
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FeedCardView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
FeedCardView(feed: Feed.sampleFeed)
|
|
||||||
.environmentObject(DataStore.sampleDataStore)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
//
|
|
||||||
// FeedDetailView.swift
|
|
||||||
// RippleChat
|
|
||||||
//
|
|
||||||
// Created by Severin Memmishofer on 13.07.23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct FeedDetailView: View {
|
|
||||||
@EnvironmentObject var dataStore: DataStore
|
|
||||||
var feed: Feed
|
|
||||||
|
|
||||||
init(feed: Feed) {
|
|
||||||
self.feed = feed
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
NavigationStack {
|
|
||||||
List(feed.feed) { logEntry in
|
|
||||||
LogEntryView(logEntry: logEntry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.navigationTitle("Feed: \(feed.feedID)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FeedDetailView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
FeedDetailView(feed: Feed.sampleFeed)
|
|
||||||
.environmentObject(DataStore.sampleDataStore)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -8,33 +8,35 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct FeedListView: View {
|
struct FeedListView: View {
|
||||||
|
@State var feeds: [Feed]
|
||||||
|
@StateObject var store = FeedStore(feed: Feed.sampleFeed)
|
||||||
|
var feedStores = [FeedStore(feed: Feed.sampleFeed), FeedStore(feed: Feed.sampleFeed2)]
|
||||||
@EnvironmentObject var dataStore: DataStore
|
@EnvironmentObject var dataStore: DataStore
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
Text("FeedListView")
|
||||||
Form {
|
Image(systemName: "globe")
|
||||||
Section(header: Text("Your own Feed:")) {
|
.imageScale(.large)
|
||||||
NavigationLink(destination: FeedDetailView(feed: dataStore.personalFeed)) {
|
.foregroundColor(.accentColor)
|
||||||
FeedCardView(feed: dataStore.personalFeed)
|
Text("Hello, world!")
|
||||||
}
|
Button("Save Feed") {
|
||||||
}
|
Task {
|
||||||
Section(header: Text("Feeds of your Firends")) {
|
do {
|
||||||
List(dataStore.feedStores) { feedStore in
|
for feed in feedStores {
|
||||||
NavigationLink(destination: FeedDetailView(feed: feedStore.feed)) {
|
try await feed.save(feed: feed.feed)
|
||||||
FeedCardView(feed: feedStore.feed)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
fatalError(error.localizedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Feeds")
|
|
||||||
NewFeedEntryView()
|
|
||||||
}
|
}
|
||||||
|
Spacer()
|
||||||
|
NewFeedEntryView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FeedListView_Previews: PreviewProvider {
|
struct FeedListView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
FeedListView()
|
FeedListView(feeds: [])
|
||||||
.environmentObject(DataStore.sampleDataStore)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
//
|
|
||||||
// FeedView.swift
|
|
||||||
// RippleChat
|
|
||||||
//
|
|
||||||
// Created by Severin Memmishofer on 12.07.23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct FeedView: View {
|
|
||||||
var body: some View {
|
|
||||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FeedView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
FeedView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
//
|
|
||||||
// LogEntryView.swift
|
|
||||||
// RippleChat
|
|
||||||
//
|
|
||||||
// Created by Severin Memmishofer on 12.07.23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct LogEntryView: View {
|
|
||||||
var logEntry: LogEntry
|
|
||||||
|
|
||||||
init(logEntry: LogEntry) {
|
|
||||||
self.logEntry = logEntry
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
HStack {
|
|
||||||
VStack {
|
|
||||||
HStack {
|
|
||||||
Text("SEQ: \(logEntry.sequenceNumber)")
|
|
||||||
Spacer()
|
|
||||||
Text("Tag: \(logEntry.body.tag)")
|
|
||||||
}
|
|
||||||
HStack {
|
|
||||||
Text("Value: \(logEntry.body.value)")
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LogEntryView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
LogEntryView(logEntry: LogEntry.sampleLogEntry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,31 +9,16 @@ import SwiftUI
|
|||||||
|
|
||||||
struct NewFeedEntryView: View {
|
struct NewFeedEntryView: View {
|
||||||
@State private var newEntry: String = ""
|
@State private var newEntry: String = ""
|
||||||
@EnvironmentObject var dataStore: DataStore
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
HStack {
|
HStack {
|
||||||
TextField("Enter your new feed message:", text: $newEntry)
|
TextField("Enter your new feed message:", text: $newEntry)
|
||||||
Button(action: {
|
Button(action: {}) {
|
||||||
let nextSeq = dataStore.personalFeed.getLastLogEntry().sequenceNumber + 1
|
|
||||||
let newBody = Bodyy(tag: Apps.txt, value: newEntry)
|
|
||||||
let newLogEntry = LogEntry(feedid: dataStore.personalID, sequenceNumber: nextSeq, body: newBody)
|
|
||||||
dataStore.personalFeed.appendLogEntry(log: newLogEntry)
|
|
||||||
newEntry = ""
|
|
||||||
}) {
|
|
||||||
Text("Send")
|
Text("Send")
|
||||||
}
|
}
|
||||||
.task {
|
|
||||||
do {
|
|
||||||
try await dataStore.savePersonalFeed()
|
|
||||||
} catch {
|
|
||||||
// Handle the error
|
|
||||||
print("Error loading data: \(error)")
|
|
||||||
fatalError(error.localizedDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Text("New entry: \(newEntry)")
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
@ -42,6 +27,5 @@ struct NewFeedEntryView: View {
|
|||||||
struct NewFeedEntryView_Previews: PreviewProvider {
|
struct NewFeedEntryView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
NewFeedEntryView()
|
NewFeedEntryView()
|
||||||
.environmentObject(DataStore.sampleDataStore)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
RippleChat/Views/PeeringView.swift
Normal file
30
RippleChat/Views/PeeringView.swift
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// PeeringView.swift
|
||||||
|
// RippleChat
|
||||||
|
//
|
||||||
|
// Created by Sebastian Lenzlinger on 10.07.23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PeeringView: View {
|
||||||
|
@ObservedObject private var bluetoothController = BluetoothController()
|
||||||
|
@ObservedObject private var bluetoothPeripheral = BluetoothPeripheral()
|
||||||
|
@EnvironmentObject var dataStore: DataStore
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Text("Peering View")
|
||||||
|
NavigationView {
|
||||||
|
List(bluetoothController.peripheralNames, id: \.self) { peripheral in
|
||||||
|
Text(peripheral)
|
||||||
|
}
|
||||||
|
.navigationTitle("Peripherals")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PeeringView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
PeeringView()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,36 +16,24 @@ struct SettingsEditView: View {
|
|||||||
Section(header: Text("Personal Feed ID")) {
|
Section(header: Text("Personal Feed ID")) {
|
||||||
//Label(dataStore.personalID, systemImage: "person.crop.circle")
|
//Label(dataStore.personalID, systemImage: "person.crop.circle")
|
||||||
HStack {
|
HStack {
|
||||||
TextField(dataStore.personalID, text: $dataStore.personalID)
|
TextField(dataStore.personalID, text: $dataStore.personalID)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section(header: Text("Friends")) {
|
Section(header: Text("Friends")) {
|
||||||
ForEach(dataStore.friends.keys.sorted(), id: \.self) { friend in
|
ForEach(dataStore.friends) { friend in
|
||||||
if let seq = dataStore.friends[friend] {
|
Label(friend, systemImage: "person")
|
||||||
Label("\(friend) - SEQ: \(seq)", systemImage: "person")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onDelete { indexSet in do {
|
|
||||||
indexSet.forEach { index in
|
|
||||||
let key = dataStore.friends.keys.sorted()[index]
|
|
||||||
dataStore.friends.removeValue(forKey: key)
|
|
||||||
}
|
|
||||||
dataStore.feedStores.remove(atOffsets: indexSet)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.onDelete {indices in
|
||||||
|
dataStore.friends.remove(atOffsets: indices)
|
||||||
}
|
}
|
||||||
HStack {
|
HStack {
|
||||||
TextField("New Feed", text: $newFeedID)
|
TextField("New Feed", text: $newFeedID)
|
||||||
Button(action: {
|
Button(action: {
|
||||||
let newFeed = Feed(feedID: newFeedID)
|
|
||||||
let newFeedStore = FeedStore(feed: newFeed)
|
|
||||||
dataStore.feedStores.append(newFeedStore)
|
|
||||||
withAnimation {
|
withAnimation {
|
||||||
dataStore.friends[newFeedID] = 0
|
let feedid = newFeedID
|
||||||
|
dataStore.friends.append(feedid)
|
||||||
newFeedID = ""
|
newFeedID = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
}) {
|
}) {
|
||||||
Image(systemName: "plus.circle.fill")
|
Image(systemName: "plus.circle.fill")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,32 +12,28 @@ struct SettingsView: View {
|
|||||||
@EnvironmentObject var dataStore: DataStore
|
@EnvironmentObject var dataStore: DataStore
|
||||||
|
|
||||||
@State private var isPresentingEditView = false
|
@State private var isPresentingEditView = false
|
||||||
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
NavigationStack {
|
|
||||||
List {
|
List {
|
||||||
Section(header: Text("Personal Feed ID")) {
|
HStack {
|
||||||
Label(dataStore.personalID, systemImage: "person.crop.circle")
|
Spacer()
|
||||||
}
|
|
||||||
Section(header: Text("Friends")) {
|
|
||||||
ForEach(dataStore.friends.keys.sorted(), id: \.self) { friend in
|
|
||||||
if let seq = dataStore.friends[friend] {
|
|
||||||
Label("\(friend) - SEQ: \(seq)", systemImage: "person")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.navigationTitle("Settings")
|
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
|
||||||
.toolbar {
|
|
||||||
ToolbarItem(placement: .confirmationAction) {
|
|
||||||
Button("Edit") {
|
Button("Edit") {
|
||||||
isPresentingEditView = true
|
isPresentingEditView = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Section(header: Text("Personal Feed ID")) {
|
||||||
|
Label(dataStore.personalID, systemImage: "person.crop.circle")
|
||||||
|
}
|
||||||
|
Section(header: Text("Friends")) {
|
||||||
|
ForEach(dataStore.friends) { friend in
|
||||||
|
Label(friend, systemImage: "person")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.navigationTitle("Settings")
|
||||||
.sheet(isPresented: $isPresentingEditView) {
|
.sheet(isPresented: $isPresentingEditView) {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
SettingsEditView()
|
SettingsEditView()
|
||||||
@ -51,23 +47,21 @@ struct SettingsView: View {
|
|||||||
ToolbarItem(placement: .confirmationAction) {
|
ToolbarItem(placement: .confirmationAction) {
|
||||||
Button("Done") {
|
Button("Done") {
|
||||||
isPresentingEditView = false
|
isPresentingEditView = false
|
||||||
dataStore.personalFeed = Feed(feedID: dataStore.personalID)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct SettingsView_Previews: PreviewProvider {
|
struct SettingsView_Previews: PreviewProvider {
|
||||||
static var friends = [
|
static var friends = [
|
||||||
"BOS":1,
|
"BOS",
|
||||||
"ALI":2,
|
"ALI",
|
||||||
"CYN":3
|
"CYN"
|
||||||
]
|
]
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
SettingsView()
|
SettingsView()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user