Recordings: Toggle between raw logs and summary
This commit is contained in:
23
main/Assets.xcassets/line-collapse.imageset/Contents.json
vendored
Normal file
23
main/Assets.xcassets/line-collapse.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "img.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "img@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "img@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
main/Assets.xcassets/line-collapse.imageset/img.png
vendored
Normal file
BIN
main/Assets.xcassets/line-collapse.imageset/img.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 150 B |
BIN
main/Assets.xcassets/line-collapse.imageset/img@2x.png
vendored
Normal file
BIN
main/Assets.xcassets/line-collapse.imageset/img@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 216 B |
BIN
main/Assets.xcassets/line-collapse.imageset/img@3x.png
vendored
Normal file
BIN
main/Assets.xcassets/line-collapse.imageset/img@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 283 B |
23
main/Assets.xcassets/line-expand.imageset/Contents.json
vendored
Normal file
23
main/Assets.xcassets/line-expand.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "img.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "img@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "img@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
main/Assets.xcassets/line-expand.imageset/img.png
vendored
Normal file
BIN
main/Assets.xcassets/line-expand.imageset/img.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 156 B |
BIN
main/Assets.xcassets/line-expand.imageset/img@2x.png
vendored
Normal file
BIN
main/Assets.xcassets/line-expand.imageset/img@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 224 B |
BIN
main/Assets.xcassets/line-expand.imageset/img@3x.png
vendored
Normal file
BIN
main/Assets.xcassets/line-expand.imageset/img@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 282 B |
@@ -291,7 +291,7 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="293" height="57.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="293" height="57.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="0HB-5f-eB1">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.8" id="0HB-5f-eB1">
|
||||||
<rect key="frame" x="16" y="9" width="33.5" height="20.5"/>
|
<rect key="frame" x="16" y="9" width="33.5" height="20.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="293" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="293" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="J2P-mU-Vad">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.8" id="J2P-mU-Vad">
|
||||||
<rect key="frame" x="16" y="12" width="33.5" height="20.5"/>
|
<rect key="frame" x="16" y="12" width="33.5" height="20.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
@@ -508,7 +508,7 @@
|
|||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zbU-wC-qJG">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.8" translatesAutoresizingMaskIntoConstraints="NO" id="zbU-wC-qJG">
|
||||||
<rect key="frame" x="15" y="11" width="290" height="20.5"/>
|
<rect key="frame" x="15" y="11" width="290" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
@@ -666,7 +666,7 @@
|
|||||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.80000000000000004" id="No8-Bf-ptL">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.8" id="No8-Bf-ptL">
|
||||||
<rect key="frame" x="113" y="12" width="59" height="20.5"/>
|
<rect key="frame" x="113" y="12" width="59" height="20.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
@@ -800,7 +800,7 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="280" height="57.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="280" height="57.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="hr0-Xt-5gV">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.8" id="hr0-Xt-5gV">
|
||||||
<rect key="frame" x="16" y="9" width="33.5" height="20.5"/>
|
<rect key="frame" x="16" y="9" width="33.5" height="20.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
@@ -1000,14 +1000,14 @@ Duration: 60:00</string>
|
|||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="PreviousRecordDetailCell" textLabel="rN0-kA-Eln" detailTextLabel="xRp-XG-oKf" style="IBUITableViewCellStyleValue1" id="ceT-cF-lLF">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="RecordDetailCountedCell" textLabel="rN0-kA-Eln" detailTextLabel="xRp-XG-oKf" style="IBUITableViewCellStyleValue1" id="ceT-cF-lLF">
|
||||||
<rect key="frame" x="0.0" y="28" width="320" height="43.5"/>
|
<rect key="frame" x="0.0" y="28" width="320" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ceT-cF-lLF" id="c5Y-xg-hSL">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ceT-cF-lLF" id="c5Y-xg-hSL">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="rN0-kA-Eln">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.8" id="rN0-kA-Eln">
|
||||||
<rect key="frame" x="16" y="12" width="33.5" height="20.5"/>
|
<rect key="frame" x="16" y="12" width="33.5" height="20.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
@@ -1024,13 +1024,67 @@ Duration: 60:00</string>
|
|||||||
</subviews>
|
</subviews>
|
||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="RecordDetailShortCell" textLabel="rIc-r4-6pg" detailTextLabel="0pW-ZC-wmh" style="IBUITableViewCellStyleValue2" id="hzU-cx-nIs">
|
||||||
|
<rect key="frame" x="0.0" y="71.5" width="320" height="43.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hzU-cx-nIs" id="scX-pQ-E7z">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="right" lineBreakMode="headTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumScaleFactor="0.8" id="rIc-r4-6pg">
|
||||||
|
<rect key="frame" x="16" y="12" width="91" height="20.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
|
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.8" id="0pW-ZC-wmh">
|
||||||
|
<rect key="frame" x="113" y="12" width="44" height="20.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</tableViewCellContentView>
|
||||||
|
</tableViewCell>
|
||||||
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="RecordDetailLongCell" textLabel="xDy-8J-JFT" detailTextLabel="kgF-BN-FdV" style="IBUITableViewCellStyleSubtitle" id="Q4T-JJ-fqY">
|
||||||
|
<rect key="frame" x="0.0" y="115" width="320" height="57.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Q4T-JJ-fqY" id="8hy-Rg-b6Q">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="57.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.8" id="xDy-8J-JFT">
|
||||||
|
<rect key="frame" x="16" y="9" width="33.5" height="20.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.8" adjustsFontSizeToFit="NO" id="kgF-BN-FdV">
|
||||||
|
<rect key="frame" x="16" y="32.5" width="44" height="14.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||||
|
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</tableViewCellContentView>
|
||||||
|
</tableViewCell>
|
||||||
</prototypes>
|
</prototypes>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="dataSource" destination="50g-BI-Q6S" id="SFM-IM-FRx"/>
|
<outlet property="dataSource" destination="50g-BI-Q6S" id="SFM-IM-FRx"/>
|
||||||
<outlet property="delegate" destination="50g-BI-Q6S" id="LBY-sp-dg0"/>
|
<outlet property="delegate" destination="50g-BI-Q6S" id="LBY-sp-dg0"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tableView>
|
</tableView>
|
||||||
<navigationItem key="navigationItem" title="Logs" id="AXT-fV-keV"/>
|
<navigationItem key="navigationItem" title="Logs" id="AXT-fV-keV">
|
||||||
|
<barButtonItem key="rightBarButtonItem" image="line-expand" id="xLc-O7-KVB">
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleDisplayStyle:" destination="50g-BI-Q6S" id="3wo-9O-7gV"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
</navigationItem>
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="lan-I9-b0a" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="lan-I9-b0a" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
@@ -1262,7 +1316,7 @@ Duration: 60:00</string>
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="MrS-rb-RLB">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.8" id="MrS-rb-RLB">
|
||||||
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
|
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
@@ -1299,6 +1353,7 @@ Duration: 60:00</string>
|
|||||||
<image name="intersection" width="25" height="25"/>
|
<image name="intersection" width="25" height="25"/>
|
||||||
<image name="journal" width="25" height="25"/>
|
<image name="journal" width="25" height="25"/>
|
||||||
<image name="jump-to-target" width="20" height="20"/>
|
<image name="jump-to-target" width="20" height="20"/>
|
||||||
|
<image name="line-expand" width="20" height="20"/>
|
||||||
<image name="settings" width="25" height="25"/>
|
<image name="settings" width="25" height="25"/>
|
||||||
<image name="tag" width="25" height="25"/>
|
<image name="tag" width="25" height="25"/>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -412,8 +412,6 @@ extension CreateTable {
|
|||||||
"""}
|
"""}
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias RecordLog = (domain: String, count: Int32)
|
|
||||||
|
|
||||||
extension SQLiteDatabase {
|
extension SQLiteDatabase {
|
||||||
|
|
||||||
// MARK: write
|
// MARK: write
|
||||||
@@ -442,13 +440,24 @@ extension SQLiteDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete one recording log entry with given `recording id`, matching `domain`, and `ts`.
|
||||||
|
/// - Returns: `true` if row was deleted
|
||||||
|
func recordingLogsDelete(_ recId: sqlite3_int64, singleEntry ts: Timestamp, domain: String) throws -> Bool {
|
||||||
|
try run(sql: "DELETE FROM recLog WHERE rid = ? AND ts = ? AND domain = ? LIMIT 1;",
|
||||||
|
bind: [BindInt64(recId), BindInt64(ts), BindText(domain)]) {
|
||||||
|
try ifStep($0, SQLITE_DONE)
|
||||||
|
return numberOfChanges > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: read
|
// MARK: read
|
||||||
|
|
||||||
/// List of domains and count occurences for given recording.
|
/// List of domains and count occurences for given recording.
|
||||||
func recordingLogsGetGrouped(_ r: Recording) -> [RecordLog]? {
|
/// - Returns: List of `(domain, ts)` pairs. Sorted by `ts` in ascending order (oldest first)
|
||||||
try? run(sql: "SELECT domain, COUNT() FROM recLog WHERE rid = ? GROUP BY domain;",
|
func recordingLogsGet(_ r: Recording) -> [DomainTsPair]? {
|
||||||
|
try? run(sql: "SELECT domain, ts FROM recLog WHERE rid = ? ORDER BY ts ASC, rowid DESC;",
|
||||||
bind: [BindInt64(r.id)]) {
|
bind: [BindInt64(r.id)]) {
|
||||||
allRows($0) { (col_text($0, 0) ?? "", sqlite3_column_int($0, 1)) }
|
allRows($0) { (col_text($0, 0) ?? "", col_ts($0, 1)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,5 @@ extension FilterOptions {
|
|||||||
extension Recording {
|
extension Recording {
|
||||||
var fallbackTitle: String { get { "Unnamed Recording #\(id)" } }
|
var fallbackTitle: String { get { "Unnamed Recording #\(id)" } }
|
||||||
var duration: Timestamp? { get { stop == nil ? nil : stop! - start } }
|
var duration: Timestamp? { get { stop == nil ? nil : stop! - start } }
|
||||||
var durationString: String? { get { stop == nil ? nil : TimeFormat.from(duration!) } }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ enum RecordingsDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get list of domains that occured during the recording
|
/// Get list of domains that occured during the recording
|
||||||
static func details(_ r: Recording) -> [RecordLog] {
|
static func details(_ r: Recording) -> [DomainTsPair] {
|
||||||
AppDB?.recordingLogsGetGrouped(r) ?? []
|
AppDB?.recordingLogsGet(r) ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update `title`, `appid`, and `notes` and post `NotifyRecordingChanged` notification.
|
/// Update `title`, `appid`, and `notes` and post `NotifyRecordingChanged` notification.
|
||||||
@@ -43,5 +43,11 @@ enum RecordingsDB {
|
|||||||
static func deleteDetails(_ r: Recording, domain: String) -> Bool {
|
static func deleteDetails(_ r: Recording, domain: String) -> Bool {
|
||||||
((try? AppDB?.recordingLogsDelete(r.id, matchingDomain: domain)) ?? 0) > 0
|
((try? AppDB?.recordingLogsDelete(r.id, matchingDomain: domain)) ?? 0) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete individual entries from recording while keeping the recording alive.
|
||||||
|
/// - Returns: `true` if at least one row is deleted.
|
||||||
|
static func deleteSingle(_ r: Recording, domain: String, ts: Timestamp) -> Bool {
|
||||||
|
(try? AppDB?.recordingLogsDelete(r.id, singleEntry: ts, domain: domain)) ?? false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ extension Timestamp {
|
|||||||
static func now() -> Timestamp { Date().timestamp }
|
static func now() -> Timestamp { Date().timestamp }
|
||||||
/// Create `Timestamp` with `now() - minutes * 60`
|
/// Create `Timestamp` with `now() - minutes * 60`
|
||||||
static func past(minutes: Int) -> Timestamp { now() - Timestamp(minutes * 60) }
|
static func past(minutes: Int) -> Timestamp { now() - Timestamp(minutes * 60) }
|
||||||
|
/// Create `Timestamp` with `m * 60` seconds
|
||||||
|
static func minutes(_ m: Int) -> Timestamp { Timestamp(m * 60) }
|
||||||
|
/// Create `Timestamp` with `h * 3600` seconds
|
||||||
|
static func hours(_ h: Int) -> Timestamp { Timestamp(h * 3600) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Timer {
|
extension Timer {
|
||||||
@@ -68,12 +72,18 @@ struct TimeFormat {
|
|||||||
|
|
||||||
// MARK: static
|
// MARK: static
|
||||||
|
|
||||||
/// Time string with format `HH:mm`
|
/// Time string with format `[HH:]mm:ss` (hours prepended only if duration is 1h+)
|
||||||
static func from(_ duration: Timestamp) -> String {
|
static func from(_ duration: Timestamp) -> String {
|
||||||
String(format: "%02d:%02d", duration / 60, duration % 60)
|
let min = duration / 60
|
||||||
|
let sec = duration % 60
|
||||||
|
if min >= 60 {
|
||||||
|
return String(format: "%02d:%02d:%02d", min / 60, min % 60, sec)
|
||||||
|
} else {
|
||||||
|
return String(format: "%02d:%02d", min, sec)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Duration string with format `HH:mm` or `HH:mm.sss`
|
/// Duration string with format `mm:ss` or `mm:ss.SSS`
|
||||||
static func from(_ duration: TimeInterval, millis: Bool = false) -> String {
|
static func from(_ duration: TimeInterval, millis: Bool = false) -> String {
|
||||||
let t = Int(duration)
|
let t = Int(duration)
|
||||||
if millis {
|
if millis {
|
||||||
@@ -83,7 +93,7 @@ struct TimeFormat {
|
|||||||
return String(format: "%02d:%02d", t / 60, t % 60)
|
return String(format: "%02d:%02d", t / 60, t % 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Duration string with format `HH:mm` or `HH:mm.sss` since reference date
|
/// Duration string with format `mm:ss` or `mm:ss.SSS` since reference date
|
||||||
static func since(_ date: Date, millis: Bool = false) -> String {
|
static func since(_ date: Date, millis: Bool = false) -> String {
|
||||||
from(Date().timeIntervalSince(date), millis: millis)
|
from(Date().timeIntervalSince(date), millis: millis)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class TVCPreviousRecords: UITableViewController, EditActionsRemove {
|
|||||||
let x = dataSource[indexPath.row]
|
let x = dataSource[indexPath.row]
|
||||||
cell.textLabel?.text = x.title ?? x.fallbackTitle
|
cell.textLabel?.text = x.title ?? x.fallbackTitle
|
||||||
cell.textLabel?.textColor = (x.title == nil) ? .systemGray : nil
|
cell.textLabel?.textColor = (x.title == nil) ? .systemGray : nil
|
||||||
cell.detailTextLabel?.text = "at \(DateFormat.seconds(x.start)), duration: \(x.durationString ?? "?")"
|
cell.detailTextLabel?.text = "at \(DateFormat.seconds(x.start)), duration: \(TimeFormat.from(x.duration ?? 0))"
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,23 +2,58 @@ import UIKit
|
|||||||
|
|
||||||
class TVCRecordingDetails: UITableViewController, EditActionsRemove {
|
class TVCRecordingDetails: UITableViewController, EditActionsRemove {
|
||||||
var record: Recording!
|
var record: Recording!
|
||||||
private var dataSource: [RecordLog]!
|
private lazy var isLongRecording: Bool = (record.duration ?? 0) > Timestamp.hours(1)
|
||||||
|
|
||||||
|
private var showRaw: Bool = false
|
||||||
|
/// Sorted by `ts` in ascending order (oldest first)
|
||||||
|
private lazy var dataSourceRaw: [DomainTsPair] = RecordingsDB.details(record)
|
||||||
|
/// Sorted by `count` (descending), then alphabetically
|
||||||
|
private lazy var dataSourceSum: [(domain: String, count: Int)] = {
|
||||||
|
var result: [String:Int] = [:]
|
||||||
|
for x in dataSourceRaw {
|
||||||
|
result[x.domain] = (result[x.domain] ?? 0) + 1 // group and count
|
||||||
|
}
|
||||||
|
return result.map{$0}.sorted {
|
||||||
|
$0.count > $1.count || $0.count == $1.count && $0.domain < $1.domain
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
title = record.title ?? record.fallbackTitle
|
title = record.title ?? record.fallbackTitle
|
||||||
dataSource = RecordingsDB.details(record)
|
}
|
||||||
|
|
||||||
|
@IBAction private func toggleDisplayStyle(_ sender: UIBarButtonItem) {
|
||||||
|
showRaw = !showRaw
|
||||||
|
sender.image = UIImage(named: showRaw ? "line-collapse" : "line-expand")
|
||||||
|
tableView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Table View Data Source
|
// MARK: - Table View Data Source
|
||||||
|
|
||||||
override func tableView(_ _: UITableView, numberOfRowsInSection _: Int) -> Int { dataSource.count }
|
override func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
|
||||||
|
showRaw ? dataSourceRaw.count : dataSourceSum.count
|
||||||
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PreviousRecordDetailCell")!
|
let cell: UITableViewCell
|
||||||
let x = dataSource[indexPath.row]
|
if showRaw {
|
||||||
cell.textLabel?.text = x.domain
|
let x = dataSourceRaw[indexPath.row]
|
||||||
cell.detailTextLabel?.text = "\(x.count)"
|
if isLongRecording {
|
||||||
|
cell = tableView.dequeueReusableCell(withIdentifier: "RecordDetailLongCell")!
|
||||||
|
cell.textLabel?.text = x.domain
|
||||||
|
cell.detailTextLabel?.text = DateFormat.seconds(x.ts)
|
||||||
|
} else {
|
||||||
|
cell = tableView.dequeueReusableCell(withIdentifier: "RecordDetailShortCell")!
|
||||||
|
cell.textLabel?.text = "+ " + TimeFormat.from(x.ts - record.start)
|
||||||
|
cell.detailTextLabel?.text = x.domain
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let x = dataSourceSum[indexPath.row]
|
||||||
|
cell = tableView.dequeueReusableCell(withIdentifier: "RecordDetailCountedCell")!
|
||||||
|
cell.textLabel?.text = x.domain
|
||||||
|
cell.detailTextLabel?.text = "\(x.count)×"
|
||||||
|
}
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,9 +69,25 @@ class TVCRecordingDetails: UITableViewController, EditActionsRemove {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func editableRowCallback(_ index: IndexPath, _ action: RowAction, _ userInfo: Any?) -> Bool {
|
func editableRowCallback(_ index: IndexPath, _ action: RowAction, _ userInfo: Any?) -> Bool {
|
||||||
if RecordingsDB.deleteDetails(record, domain: dataSource[index.row].domain) {
|
if showRaw {
|
||||||
dataSource.remove(at: index.row)
|
let x = dataSourceRaw[index.row]
|
||||||
tableView.deleteRows(at: [index], with: .automatic)
|
if RecordingsDB.deleteSingle(record, domain: x.domain, ts: x.ts) {
|
||||||
|
if let i = dataSourceSum.firstIndex(where: { $0.domain == x.domain }) {
|
||||||
|
dataSourceSum[i].count -= 1
|
||||||
|
if dataSourceSum[i].count == 0 {
|
||||||
|
dataSourceSum.remove(at: i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataSourceRaw.remove(at: index.row)
|
||||||
|
tableView.deleteRows(at: [index], with: .automatic)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let dom = dataSourceSum[index.row].domain
|
||||||
|
if RecordingsDB.deleteDetails(record, domain: dom) {
|
||||||
|
dataSourceRaw.removeAll { $0.domain == dom }
|
||||||
|
dataSourceSum.remove(at: index.row)
|
||||||
|
tableView.deleteRows(at: [index], with: .automatic)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class VCEditRecording: UIViewController, UITextFieldDelegate, UITextViewDelegate
|
|||||||
inputDetails.text = """
|
inputDetails.text = """
|
||||||
Start: \(DateFormat.seconds(record.start))
|
Start: \(DateFormat.seconds(record.start))
|
||||||
End: \(record.stop == nil ? "?" : DateFormat.seconds(record.stop!))
|
End: \(record.stop == nil ? "?" : DateFormat.seconds(record.stop!))
|
||||||
Duration: \(record.durationString ?? "?")
|
Duration: \(TimeFormat.from(record.duration ?? 0))
|
||||||
"""
|
"""
|
||||||
validateSaveButton()
|
validateSaveButton()
|
||||||
if deleteOnCancel { // mark as destructive
|
if deleteOnCancel { // mark as destructive
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class TVCHostDetails: UITableViewController, SyncUpdateDelegate, UITabBarDelegat
|
|||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "HostDetailCell")!
|
let cell = tableView.dequeueReusableCell(withIdentifier: "HostDetailCell")!
|
||||||
let src = dataSource[indexPath.row]
|
let src = dataSource[indexPath.row]
|
||||||
cell.textLabel?.text = DateFormat.seconds(src.ts)
|
cell.textLabel?.text = DateFormat.seconds(src.ts)
|
||||||
cell.detailTextLabel?.text = (src.total > 1) ? "\(src.total)x" : nil
|
cell.detailTextLabel?.text = (src.total > 1) ? "\(src.total)×" : nil
|
||||||
cell.imageView?.image = (src.blocked > 0 ? UIImage(named: "shield-x") : nil)
|
cell.imageView?.image = (src.blocked > 0 ? UIImage(named: "shield-x") : nil)
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user