From 6e42f96c19cd79f76b9edbf1c56718cbcf59f4ea Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 19 Apr 2024 11:15:18 +0400 Subject: [PATCH] Various improvements --- ...ationSequenceCodeEntryControllerNode.swift | 14 +- .../Sources/AdminUserActionsSheet.swift | 4 +- .../Sources/RecentActionsSettingsSheet.swift | 154 +++++++++++++++--- .../Chat/ChatRecentActionsController/BUILD | 3 +- .../Sources/ChatRecentActionsController.swift | 56 ++++++- .../Context Menu/Ban.imageset/Contents.json | 12 ++ .../Chat/Context Menu/Ban.imageset/ban_24.pdf | 83 ++++++++++ 7 files changed, 286 insertions(+), 40 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ban.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ban.imageset/ban_24.pdf diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift index e2abc23356..b2f2228f7c 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift @@ -158,7 +158,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.nextOptionButtonNode = HighlightableButtonNode() self.nextOptionButtonNode.displaysAsynchronously = false - let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: .sms(length: 5), nextType: .call, timeout: 60, strings: self.strings, primaryColor: self.theme.list.itemPrimaryTextColor, accentColor: self.theme.list.itemAccentColor) + let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: .sms(length: 5), nextType: .call, timeout: 60, strings: self.strings, primaryColor: self.theme.list.itemSecondaryTextColor, accentColor: self.theme.list.itemAccentColor) self.nextOptionTitleNode.attributedText = nextOptionText self.nextOptionButtonNode.isUserInteractionEnabled = nextOptionActive self.nextOptionButtonNode.accessibilityLabel = nextOptionText.string @@ -441,7 +441,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF if let strongSelf = self { if let currentTimeoutTime = strongSelf.currentTimeoutTime, currentTimeoutTime > 0 { strongSelf.currentTimeoutTime = currentTimeoutTime - 1 - let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: codeType, nextType: nextType, returnToCode: hasPreviousCode, timeout: strongSelf.currentTimeoutTime, strings: strongSelf.strings, primaryColor: strongSelf.theme.list.itemPrimaryTextColor, accentColor: strongSelf.theme.list.itemAccentColor) + let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: codeType, nextType: nextType, returnToCode: hasPreviousCode, timeout: strongSelf.currentTimeoutTime, strings: strongSelf.strings, primaryColor: strongSelf.theme.list.itemSecondaryTextColor, accentColor: strongSelf.theme.list.itemAccentColor) strongSelf.nextOptionTitleNode.attributedText = nextOptionText strongSelf.nextOptionButtonNode.isUserInteractionEnabled = nextOptionActive strongSelf.nextOptionButtonNode.accessibilityLabel = nextOptionText.string @@ -482,7 +482,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.countdownDisposable.set(nil) } - let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: codeType, nextType: nextType, returnToCode: hasPreviousCode, timeout: self.currentTimeoutTime, strings: self.strings, primaryColor: self.theme.list.itemPrimaryTextColor, accentColor: self.theme.list.itemAccentColor) + let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: codeType, nextType: nextType, returnToCode: hasPreviousCode, timeout: self.currentTimeoutTime, strings: self.strings, primaryColor: self.theme.list.itemSecondaryTextColor, accentColor: self.theme.list.itemAccentColor) self.nextOptionTitleNode.attributedText = nextOptionText self.nextOptionButtonNode.isUserInteractionEnabled = nextOptionActive self.nextOptionButtonNode.accessibilityLabel = nextOptionText.string @@ -556,7 +556,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_EnterCodeTelegramTitle, font: Font.semibold(40.0), textColor: self.theme.list.itemPrimaryTextColor) } - self.textField.textField.placeholder = textFieldPlaceholder + self.textField.textField.attributedPlaceholder = NSAttributedString(string: textFieldPlaceholder, font: Font.regular(20.0), textColor: self.theme.list.itemPlaceholderTextColor) self.titleActivateAreaNode.accessibilityLabel = self.titleNode.attributedText?.string ?? "" @@ -808,7 +808,9 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF if let codeType = self.codeType { let yOffset: CGFloat = layout.size.width > 320.0 ? 18.0 : 5.0 if case .phrase = codeType { - self.hintButtonNode.alpha = 1.0 + if self.errorTextNode.alpha.isZero { + self.hintButtonNode.alpha = 1.0 + } self.hintButtonNode.isUserInteractionEnabled = false self.hintTextNode.attributedText = NSAttributedString(string: self.strings.Login_EnterPhraseHint, font: Font.regular(13.0), textColor: self.theme.list.itemSecondaryTextColor, paragraphAlignment: .center) @@ -974,7 +976,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF var updated = textField.text ?? "" updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string) - return checkValidity(text: updated) + return checkValidity(text: updated) && !updated.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } func checkValidity(text: String) -> Bool { diff --git a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift index a1ad4fef95..671d8394d8 100644 --- a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift +++ b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift @@ -508,7 +508,7 @@ private final class AdminUserActionsSheetComponent: Component { let resetScrolling = self.scrollView.bounds.width != availableSize.width - let sideInset: CGFloat = 16.0 + let sideInset: CGFloat = 16.0 + environment.safeInsets.left if self.component == nil { var (allowedParticipantRights, allowedMediaRights) = rightsFromBannedRights([]) @@ -584,7 +584,7 @@ private final class AdminUserActionsSheetComponent: Component { environment: {}, containerSize: CGSize(width: 120.0, height: 100.0) ) - let leftButtonFrame = CGRect(origin: CGPoint(x: 16.0, y: 0.0), size: leftButtonSize) + let leftButtonFrame = CGRect(origin: CGPoint(x: 16.0 + environment.safeInsets.left, y: 0.0), size: leftButtonSize) if let leftButtonView = self.leftButton.view { if leftButtonView.superview == nil { self.navigationBarContainer.addSubview(leftButtonView) diff --git a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift index 3a603b2143..858d447edc 100644 --- a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift +++ b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift @@ -43,6 +43,29 @@ private enum MembersActionType: Hashable, CaseIterable { return "Members left the Group" } } + + var eventFlags: AdminLogEventsFlags { + switch self { + case .newAdminRights: + return [.promote, .demote] + case .newExceptions: + return [.ban, .unban, .kick, .unkick] + case .newMembers: + return [.invite, .join] + case .leftMembers: + return [.leave] + } + } + + static func actionTypesFromFlags(_ eventFlags: AdminLogEventsFlags) -> [Self] { + var actionTypes: [Self] = [] + for actionType in Self.allCases { + if !actionType.eventFlags.intersection(eventFlags).isEmpty { + actionTypes.append(actionType) + } + } + return actionTypes + } } private enum SettingsActionType: Hashable, CaseIterable { @@ -50,16 +73,37 @@ private enum SettingsActionType: Hashable, CaseIterable { case inviteLinks case videoChats - func title(strings: PresentationStrings) -> String { + func title(isGroup: Bool, strings: PresentationStrings) -> String { switch self { case .groupInfo: - return "Group Info" + return isGroup ? strings.Channel_AdminLogFilter_EventsInfo : strings.Channel_AdminLogFilter_ChannelEventsInfo case .inviteLinks: - return "Invite Links" + return strings.Channel_AdminLogFilter_EventsInviteLinks case .videoChats: - return "Video Chats" + return isGroup ? strings.Channel_AdminLogFilter_EventsCalls : strings.Channel_AdminLogFilter_EventsLiveStreams } } + + var eventFlags: AdminLogEventsFlags { + switch self { + case .groupInfo: + return [.info, .settings] + case .inviteLinks: + return [.invites] + case .videoChats: + return [.calls] + } + } + + static func actionTypesFromFlags(_ eventFlags: AdminLogEventsFlags) -> [Self] { + var actionTypes: [Self] = [] + for actionType in Self.allCases { + if !actionType.eventFlags.intersection(eventFlags).isEmpty { + actionTypes.append(actionType) + } + } + return actionTypes + } } private enum MessagesActionType: Hashable, CaseIterable { @@ -70,13 +114,34 @@ private enum MessagesActionType: Hashable, CaseIterable { func title(strings: PresentationStrings) -> String { switch self { case .deletedMessages: - return "Deleted Messages" + return strings.Channel_AdminLogFilter_EventsDeletedMessages case .editedMessages: - return "Edited Messages" + return strings.Channel_AdminLogFilter_EventsEditedMessages case .pinnedMessages: - return "Pinned Messages" + return strings.Channel_AdminLogFilter_EventsPinned } } + + var eventFlags: AdminLogEventsFlags { + switch self { + case .deletedMessages: + return [.deleteMessages] + case .editedMessages: + return [.editMessages] + case .pinnedMessages: + return [.pinnedMessages] + } + } + + static func actionTypesFromFlags(_ eventFlags: AdminLogEventsFlags) -> [Self] { + var actionTypes: [Self] = [] + for actionType in Self.allCases { + if !actionType.eventFlags.intersection(eventFlags).isEmpty { + actionTypes.append(actionType) + } + } + return actionTypes + } } private enum ActionType: Hashable { @@ -84,12 +149,12 @@ private enum ActionType: Hashable { case settings(SettingsActionType) case messages(MessagesActionType) - func title(strings: PresentationStrings) -> String { + func title(isGroup: Bool, strings: PresentationStrings) -> String { switch self { case let .members(value): return value.title(strings: strings) case let .settings(value): - return value.title(strings: strings) + return value.title(isGroup: isGroup, strings: strings) case let .messages(value): return value.title(strings: strings) } @@ -100,16 +165,22 @@ private final class RecentActionsSettingsSheetComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let peer: EnginePeer let adminPeers: [EnginePeer] - let completion: (RecentActionsSettingsSheet.Result) -> Void + let initialValue: RecentActionsSettingsSheet.Value + let completion: (RecentActionsSettingsSheet.Value) -> Void init( context: AccountContext, + peer: EnginePeer, adminPeers: [EnginePeer], - completion: @escaping (RecentActionsSettingsSheet.Result) -> Void + initialValue: RecentActionsSettingsSheet.Value, + completion: @escaping (RecentActionsSettingsSheet.Value) -> Void ) { self.context = context + self.peer = peer self.adminPeers = adminPeers + self.initialValue = initialValue self.completion = completion } @@ -117,6 +188,9 @@ private final class RecentActionsSettingsSheetComponent: Component { if lhs.context !== rhs.context { return false } + if lhs.peer != rhs.peer { + return false + } if lhs.adminPeers != rhs.adminPeers { return false } @@ -274,8 +348,24 @@ private final class RecentActionsSettingsSheetComponent: Component { } } - private func calculateResult() -> RecentActionsSettingsSheet.Result { - return RecentActionsSettingsSheet.Result( + private func calculateResult() -> RecentActionsSettingsSheet.Value { + var events: AdminLogEventsFlags = [] + var admins: [EnginePeer.Id] = [] + for action in self.selectedMembersActions { + events.formUnion(action.eventFlags) + } + for action in self.selectedSettingsActions { + events.formUnion(action.eventFlags) + } + for action in self.selectedMessagesActions { + events.formUnion(action.eventFlags) + } + for peerId in self.selectedAdmins { + admins.append(peerId) + } + return RecentActionsSettingsSheet.Value( + events: events, + admins: admins ) } @@ -352,13 +442,18 @@ private final class RecentActionsSettingsSheetComponent: Component { let resetScrolling = self.scrollView.bounds.width != availableSize.width - let sideInset: CGFloat = 16.0 - + let sideInset: CGFloat = 16.0 + environment.safeInsets.left + if self.component == nil { - self.selectedMembersActions = Set(MembersActionType.allCases) - self.selectedSettingsActions = Set(SettingsActionType.allCases) - self.selectedMessagesActions = Set(MessagesActionType.allCases) - self.selectedAdmins = Set(component.adminPeers.map(\.id)) + self.selectedMembersActions = Set(MembersActionType.actionTypesFromFlags(component.initialValue.events)) + self.selectedSettingsActions = Set(SettingsActionType.actionTypesFromFlags(component.initialValue.events)) + self.selectedMessagesActions = Set(MessagesActionType.actionTypesFromFlags(component.initialValue.events)) + self.selectedAdmins = component.initialValue.admins.flatMap { Set($0) } ?? Set(component.adminPeers.map(\.id)) + } + + var isGroup = true + if case let .channel(channel) = component.peer, case .broadcast = channel.info { + isGroup = false } self.component = component @@ -394,7 +489,7 @@ private final class RecentActionsSettingsSheetComponent: Component { environment: {}, containerSize: CGSize(width: 120.0, height: 100.0) ) - let leftButtonFrame = CGRect(origin: CGPoint(x: 16.0, y: 0.0), size: leftButtonSize) + let leftButtonFrame = CGRect(origin: CGPoint(x: 16.0 + environment.safeInsets.left, y: 0.0), size: leftButtonSize) if let leftButtonView = self.leftButton.view { if leftButtonView.superview == nil { self.navigationBarContainer.addSubview(leftButtonView) @@ -420,11 +515,11 @@ private final class RecentActionsSettingsSheetComponent: Component { case .members: totalCount = MembersActionType.allCases.count selectedCount = self.selectedMembersActions.count - title = "Members and Admins" + title = isGroup ? "Members and Admins" : "Subscribers and Admins" case .settings: totalCount = SettingsActionType.allCases.count selectedCount = self.selectedSettingsActions.count - title = "Group Settings" + title = isGroup ? "Group Settings" : "Channel Settings" case .messages: totalCount = MessagesActionType.allCases.count selectedCount = self.selectedMessagesActions.count @@ -524,7 +619,7 @@ private final class RecentActionsSettingsSheetComponent: Component { var subItems: [AnyComponentWithIdentity] = [] for actionType in actionTypes { - let actionItemTitle: String = actionType.title(strings: environment.strings) + let actionItemTitle: String = actionType.title(isGroup: isGroup, strings: environment.strings) let subItemToggleAction: () -> Void = { [weak self] in guard let self else { @@ -846,8 +941,13 @@ private final class RecentActionsSettingsSheetComponent: Component { } public class RecentActionsSettingsSheet: ViewControllerComponentContainer { - public final class Result { - init() { + public final class Value { + public let events: AdminLogEventsFlags + public let admins: [EnginePeer.Id]? + + public init(events: AdminLogEventsFlags, admins: [EnginePeer.Id]?) { + self.events = events + self.admins = admins } } @@ -855,10 +955,10 @@ public class RecentActionsSettingsSheet: ViewControllerComponentContainer { private var isDismissed: Bool = false - public init(context: AccountContext, adminPeers: [EnginePeer], completion: @escaping (Result) -> Void) { + public init(context: AccountContext, peer: EnginePeer, adminPeers: [EnginePeer], initialValue: Value, completion: @escaping (Value) -> Void) { self.context = context - super.init(context: context, component: RecentActionsSettingsSheetComponent(context: context, adminPeers: adminPeers, completion: completion), navigationBarAppearance: .none) + super.init(context: context, component: RecentActionsSettingsSheetComponent(context: context, peer: peer, adminPeers: adminPeers, initialValue: initialValue, completion: completion), navigationBarAppearance: .none) self.statusBar.statusBarStyle = .Ignore self.navigationPresentation = .flatModal diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/BUILD b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/BUILD index 740cd3852d..3fee2bfabd 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/BUILD @@ -48,7 +48,8 @@ swift_library( "//submodules/UndoUI", "//submodules/WallpaperBackgroundNode", "//submodules/TextFormat", - "//submodules/CounterControllerTitleView" + "//submodules/CounterControllerTitleView", + "//submodules/TelegramUI/Components/AdminUserActionsSheet", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift index 5cbdfd5789..968ad4dbac 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift @@ -12,6 +12,7 @@ import PresentationDataUtils import ChatPresentationInterfaceState import ChatNavigationButton import CounterControllerTitleView +import AdminUserActionsSheet public final class ChatRecentActionsController: TelegramBaseController { private var controllerNode: ChatRecentActionsControllerNode { @@ -34,6 +35,8 @@ public final class ChatRecentActionsController: TelegramBaseController { private let titleView: CounterControllerTitleView private var rightBarButton: ChatNavigationButton? + private var adminsDisposable: Disposable? + public init(context: AccountContext, peer: Peer, adminPeerId: PeerId?) { self.context = context self.peer = peer @@ -221,6 +224,7 @@ public final class ChatRecentActionsController: TelegramBaseController { deinit { self.presentationDataDisposable?.dispose() + self.adminsDisposable?.dispose() } private func updateThemeAndStrings() { @@ -291,11 +295,55 @@ public final class ChatRecentActionsController: TelegramBaseController { self.updateTitle() } + private var adminsPromise: Promise<[RenderedChannelParticipant]?>? func openFilterSetup() { - self.present(channelRecentActionsFilterController(context: self.context, updatedPresentationData: self.updatedPresentationData, peer: self.peer, events: self.controllerNode.filter.events, adminPeerIds: self.controllerNode.filter.adminPeerIds, apply: { [weak self] events, adminPeerIds in - self?.controllerNode.updateFilter(events: events, adminPeerIds: adminPeerIds) - self?.updateTitle() - }), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + if self.adminsPromise == nil { + self.adminsPromise = Promise() + let (disposable, _) = self.context.peerChannelMemberCategoriesContextsManager.admins(engine: self.context.engine, postbox: self.context.account.postbox, network: self.context.account.network, accountPeerId: self.context.account.peerId, peerId: self.peer.id) { membersState in + if case .loading = membersState.loadingState, membersState.list.isEmpty { + self.adminsPromise?.set(.single(nil)) + } else { + self.adminsPromise?.set(.single(membersState.list)) + } + } + self.adminsDisposable = disposable + } + + guard let adminsPromise = self.adminsPromise else { + return + } + + let _ = (adminsPromise.get() + |> filter { $0 != nil } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] result in + guard let self else { + return + } + var adminPeers: [EnginePeer] = [] + if let result { + for participant in result { + adminPeers.append(EnginePeer(participant.peer)) + } + } + let controller = RecentActionsSettingsSheet( + context: self.context, + peer: EnginePeer(self.peer), + adminPeers: adminPeers, + initialValue: RecentActionsSettingsSheet.Value( + events: self.controllerNode.filter.events, + admins: self.controllerNode.filter.adminPeerIds + ), + completion: { [weak self] result in + guard let self else { + return + } + self.controllerNode.updateFilter(events: result.events, adminPeerIds: result.admins) + self.updateTitle() + } + ) + self.push(controller) + }) } private func updateTitle() { diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ban.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ban.imageset/Contents.json new file mode 100644 index 0000000000..4aeca4c67f --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ban.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ban_24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ban.imageset/ban_24.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ban.imageset/ban_24.pdf new file mode 100644 index 0000000000..5fd4cbbd92 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ban.imageset/ban_24.pdf @@ -0,0 +1,83 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 3.334961 3.334999 cm +0.070588 0.070588 0.070588 scn +1.330000 8.665002 m +1.330000 12.716011 4.613991 16.000002 8.665000 16.000002 c +10.451064 16.000002 12.088030 15.361636 13.360182 14.300619 c +3.029383 3.969820 l +1.968366 5.241972 1.330000 6.878938 1.330000 8.665002 c +h +3.969837 3.029369 m +5.241986 1.968362 6.878945 1.330002 8.665000 1.330002 c +12.716008 1.330002 16.000000 4.613994 16.000000 8.665002 c +16.000000 10.451057 15.361640 12.088016 14.300632 13.360165 c +3.969837 3.029369 l +h +8.665000 17.330002 m +3.879453 17.330002 0.000000 13.450549 0.000000 8.665002 c +0.000000 3.879455 3.879453 0.000000 8.665000 0.000000 c +13.450547 0.000000 17.330002 3.879455 17.330002 8.665002 c +17.330002 13.450549 13.450547 17.330002 8.665000 17.330002 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 821 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000000911 00000 n +0000000933 00000 n +0000001106 00000 n +0000001180 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1239 +%%EOF \ No newline at end of file