├── _config.yml ├── version ├── .github └── FUNDING.yml ├── README ├── logo.png ├── monzo.png └── screenshot.png ├── LauncherX ├── LauncherX │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── r_icon_16.png │ │ │ ├── r_icon_32.png │ │ │ ├── r_icon_64.png │ │ │ ├── r_icon_1024.png │ │ │ ├── r_icon_128.png │ │ │ ├── r_icon_256-1.png │ │ │ ├── r_icon_256.png │ │ │ ├── r_icon_32-1.png │ │ │ ├── r_icon_512-1.png │ │ │ ├── r_icon_512.png │ │ │ └── Contents.json │ ├── LauncherX.entitlements │ ├── Info.plist │ ├── AppDelegate.swift │ ├── TwitterX.swift │ └── Base.lproj │ │ └── MainMenu.xib └── LauncherX.xcodeproj │ ├── xcshareddata │ └── xcschemes │ │ └── LauncherX.xcscheme │ └── project.pbxproj ├── TwitterX ├── TwitterXAssets │ ├── Images │ │ ├── bookmarks.png │ │ └── bookmarks@2x.png │ └── Info.plist ├── TwitterX │ ├── Theme │ │ ├── TWXDuskTheme.h │ │ └── TWXDuskTheme.m │ ├── Features │ │ ├── TWXTheme.h │ │ ├── TWXSource.h │ │ ├── TWXThemeMenu.h │ │ ├── TWXFeature.h │ │ ├── TWXRepliesCount.h │ │ ├── TWXBookmarkActions.h │ │ ├── TWXCharactersLimit.h │ │ ├── TWXRoundedAvatars.h │ │ ├── TWXBookmarksTimeline.h │ │ ├── TWXLightThemeReborn.h │ │ ├── TWXBookmarksSidebarItem.h │ │ ├── TWXConsumerKeysOverride.h │ │ ├── TWXDirectMessagesCrash.h │ │ ├── TWXEnhancedFullscreen.h │ │ ├── TWXRoundedStatusContent.h │ │ ├── TWXSource.m │ │ ├── TWXCharactersLimit.m │ │ ├── TWXTheme.m │ │ ├── TWXDirectMessagesCrash.m │ │ ├── TWXEnhancedFullscreen.m │ │ ├── TWXRoundedStatusContent.m │ │ ├── TWXLightThemeReborn.m │ │ ├── TWXThemeMenu.m │ │ ├── TWXRoundedAvatars.m │ │ ├── TWXBookmarkActions.m │ │ ├── TWXRepliesCount.m │ │ ├── TWXBookmarksTimeline.m │ │ ├── TWXBookmarksSidebarItem.m │ │ └── TWXConsumerKeysOverride.m │ ├── Utilities │ │ ├── NSString+TWX.h │ │ ├── NSColor+TWX.h │ │ ├── NSString+TWX.m │ │ ├── NSImage+TWX.h │ │ ├── NSColor+TWX.m │ │ ├── NSBundle+TWX.h │ │ ├── NSView+TWX.h │ │ ├── NSBundle+TWX.m │ │ ├── NSImage+TWX.m │ │ ├── TWXRuntime.h │ │ ├── NSView+TWX.m │ │ └── TWXRuntime.m │ ├── TwitterX.h │ ├── Info.plist │ ├── Service │ │ ├── TWXAPI+Bookmarks.h │ │ ├── TWXAPI.h │ │ ├── TWXAPI.m │ │ └── TWXAPI+Bookmarks.m │ └── TwitterX.m └── TwitterX.xcodeproj │ ├── xcshareddata │ └── xcschemes │ │ └── TwitterX.xcscheme │ └── project.pbxproj ├── TwitterX.xcworkspace └── contents.xcworkspacedata ├── .gitignore ├── CHANGELOG.md ├── LICENSE └── README.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | {"version_number":"0.2.2"} 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: nakiostudio 2 | -------------------------------------------------------------------------------- /README/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/README/logo.png -------------------------------------------------------------------------------- /README/monzo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/README/monzo.png -------------------------------------------------------------------------------- /README/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/README/screenshot.png -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /TwitterX/TwitterXAssets/Images/bookmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/TwitterX/TwitterXAssets/Images/bookmarks.png -------------------------------------------------------------------------------- /TwitterX/TwitterXAssets/Images/bookmarks@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/TwitterX/TwitterXAssets/Images/bookmarks@2x.png -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_16.png -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_32.png -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_64.png -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_1024.png -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_128.png -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_256-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_256-1.png -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_256.png -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_32-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_32-1.png -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_512-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_512-1.png -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_512.png -------------------------------------------------------------------------------- /TwitterX.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Theme/TWXDuskTheme.h: -------------------------------------------------------------------------------- 1 | // 2 | // TWXDuskheme.h 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/07. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TWXDuskTheme : NSObject 12 | + (id)themeIdentifier; 13 | @end 14 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXTheme.h: -------------------------------------------------------------------------------- 1 | // 2 | // TWXTheme.h 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/08. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TWXFeature.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface TWXTheme: NSObject 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // TWXSource.h 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/08. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TWXFeature.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface TWXSource: NSObject 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/NSString+TWX.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+TWX.h 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/08. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSString (TWX) 14 | -(NSString *) stringByStrippingHTML; 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/NSColor+TWX.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSColor+TWX.h 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/07. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSColor (TWX) 14 | + (instancetype)colorFromHexString:(NSString *)hexString; 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXThemeMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // TWXThemeMenu.h 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/07. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TWXFeature.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface TWXThemeMenu: NSObject 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # CI 26 | test_output/ 27 | .fastlane/report.xml 28 | .fastlane/README.md 29 | -------------------------------------------------------------------------------- /LauncherX/LauncherX/LauncherX.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.network.client 10 | 11 | com.apple.security.network.server 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/NSString+TWX.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+TWX.m 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/08. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import "NSString+TWX.h" 10 | 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @implementation NSString (TWX) 15 | -(NSString *) stringByStrippingHTML { 16 | NSRange r; 17 | NSString *s = [self copy]; 18 | while ((r = [s rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound) 19 | s = [s stringByReplacingCharactersInRange:r withString:@""]; 20 | return s; 21 | } 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/TwitterX.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | 13 | FOUNDATION_EXPORT double TwitterXVersionNumber; 14 | FOUNDATION_EXPORT const unsigned char TwitterXVersionString[]; 15 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/NSImage+TWX.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSImage (TWX) 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/NSColor+TWX.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSColor+TWX.m 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/07. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import "NSColor+TWX.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @implementation NSColor (TWX) 14 | 15 | + (instancetype)colorFromHexString:(NSString *)hexString { 16 | unsigned rgbValue = 0; 17 | NSScanner *scanner = [NSScanner scannerWithString:hexString]; 18 | [scanner setScanLocation:1]; // bypass '#' character 19 | [scanner scanHexInt:&rgbValue]; 20 | return [NSColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0]; 21 | } 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXFeature.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @protocol TWXFeature 16 | 17 | + (void)loadFeature; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXRepliesCount.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXFeature.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXRepliesCount: NSObject 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/NSBundle+TWX.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSBundle (TWX) 16 | 17 | + (nullable NSBundle *)twx_assetsBundle; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXBookmarkActions.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXFeature.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXBookmarkActions: NSObject 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXCharactersLimit.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXFeature.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXCharactersLimit: NSObject 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXRoundedAvatars.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXFeature.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXRoundedAvatars: NSObject 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXBookmarksTimeline.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXFeature.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXBookmarksTimeline: NSObject 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXLightThemeReborn.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXFeature.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXLightThemeReborn: NSObject 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXBookmarksSidebarItem.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXFeature.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXBookmarksSidebarItem: NSObject 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXConsumerKeysOverride.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXFeature.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXConsumerKeysOverride: NSObject 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXDirectMessagesCrash.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXFeature.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXDirectMessagesCrash: NSObject 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXEnhancedFullscreen.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXFeature.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXEnhancedFullscreen: NSObject 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXRoundedStatusContent.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXFeature.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXRoundedStatusContent: NSObject 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /TwitterX/TwitterXAssets/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | NSHumanReadableCopyright 22 | Copyright © 2018 twitterx. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v.0.2.2 4 | 5 | * Shown preferences menu when the login modal is presented. 6 | 7 | ## v.0.2.1 8 | 9 | * Fixed issue bookmarking retweets. 10 | 11 | ## v.0.2.0 12 | 13 | * Implemented Bookmarks. 14 | 15 | ## v.0.1.1 16 | 17 | * Drag and drop no longer needed as the Twitter app is 18 | automatically detected. 19 | * OAuth consumer key and secret configurable from the 20 | preferences menu. 21 | 22 | ## v.0.1.0 23 | 24 | * Fixed authentication. For this change to work you may need to 25 | sign out and sign in again. 26 | 27 | ## v.0.0.3 28 | 29 | * Fixed macOS 10.10 compatibility issues. 30 | 31 | ## v.0.0.2 32 | 33 | * Supported macOS versions greater or equal to `10.9`. 34 | 35 | ## v.0.0.1 36 | 37 | * Increased tweet length limit to 280 characters. 38 | * Rounded avatar images. 39 | * Fixed fullscreen toolbar bug. 40 | * Updated light theme appearance. 41 | * Added reply counters to timeline statuses. 42 | * Fixed crash loading direct messages. 43 | 44 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.2.2 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2018 twitterx. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // TWXSource.m 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/08. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import "TWXSource.h" 10 | #import "TWXRuntime.h" 11 | #import 12 | #import "NSString+TWX.h" 13 | 14 | @implementation TWXSource 15 | + (void)loadFeature { 16 | [TWXRuntime exchangeInstanceMethod:@"updateAttributedStrings" ofClass:@"TMNewDetailedStatusCell"]; 17 | } 18 | @end 19 | 20 | @implementation NSObject (TWX) 21 | - (void)TMNewDetailedStatusCell_updateAttributedStrings { 22 | [self TMNewDetailedStatusCell_updateAttributedStrings]; 23 | id status = [self valueForKey:@"_status"]; 24 | NSString* sourceHTML = [status valueForKey:@"_source"]; 25 | NSString* source = [sourceHTML stringByStrippingHTML]; 26 | NSTextView* textView = [self valueForKey:@"dateTimeAndLocation"]; 27 | textView.string = [textView.string stringByAppendingString:source]; 28 | } 29 | @end 30 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Service/TWXAPI+Bookmarks.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXAPI.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface TWXAPI (Bookmarks) 17 | 18 | - (void)fetchBookmarksWithCompletion:(void(^)(NSArray *))completion; 19 | 20 | - (void)addBookmarkForTweetWithIdentifier:(NSString *)identifier; 21 | 22 | - (void)removeBookmarkForTweetWithIdentifier:(NSString *)identifier completion:(void(^)(id))completion ; 23 | 24 | @end 25 | 26 | NS_ASSUME_NONNULL_END 27 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXCharactersLimit.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import "TWXCharactersLimit.h" 12 | #import "TWXRuntime.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @implementation TWXCharactersLimit 17 | 18 | + (void)loadFeature { 19 | [TWXRuntime exchangeInstanceMethod:@"characterLimit" ofClass:@"TwitterComposition"]; 20 | } 21 | 22 | @end 23 | 24 | @implementation NSObject (TWX) 25 | 26 | - (NSInteger)TwitterComposition_characterLimit { 27 | return 280; 28 | } 29 | 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END 33 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Service/TWXAPI.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface TWXAPI: NSObject 16 | 17 | - (instancetype)initWithTwitterAccount:(id)twitterAccount; 18 | 19 | - (void)v1_1_requestTo:(NSString *)endpoint method:(NSString*)method parameters:(NSDictionary *)parameters callback:(void(^)(id))callback; 20 | 21 | - (void)v2_requestTo:(NSString *)endpoint method:(NSString*)method parameters:(NSDictionary *)parameters callback:(void(^)(id))callback; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/NSView+TWX.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NSView (TWX) 16 | 17 | - (void)anchorToAttribute:(NSLayoutAttribute)toAttr ofView:(NSView *)view fromAttribute:(NSLayoutAttribute)fromAttr; 18 | 19 | - (void)anchorToAttribute:(NSLayoutAttribute)toAttr ofView:(NSView *)view fromAttribute:(NSLayoutAttribute)fromAttr constant:(CGFloat)constant; 20 | 21 | - (void)anchorDimenstionAttribute:(NSLayoutAttribute)attr toConstant:(CGFloat)constant; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Carlos Vidal 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXTheme.m: -------------------------------------------------------------------------------- 1 | // 2 | // TWXTheme.m 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/08. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import "TWXTheme.h" 10 | #import 11 | #import "TWXRuntime.h" 12 | #import "NSView+TWX.h" 13 | #import "TWXDuskTheme.h" 14 | 15 | @implementation TWXTheme 16 | + (void)loadFeature { 17 | [TWXRuntime exchangeClassMethod:@"saveThemeIdentifierToUserDefaults:" ofClass:@"TMTheme"]; 18 | [TWXRuntime exchangeClassMethod:@"themeIdentifierFromUserDefaults" ofClass:@"TMTheme"]; 19 | } 20 | @end 21 | 22 | @implementation NSObject (TWX) 23 | 24 | NSString *const TMSelectedThemeIdentifierDefaultsKey = @"TMSelectedThemeIdentifier"; 25 | 26 | + (void)TMTheme_saveThemeIdentifierToUserDefaults:(id)arg1 { 27 | [[NSUserDefaults standardUserDefaults] setObject:arg1 forKey:TMSelectedThemeIdentifierDefaultsKey]; 28 | [[NSUserDefaults standardUserDefaults] synchronize]; 29 | } 30 | + (id)TMTheme_themeIdentifierFromUserDefaults { 31 | return [[NSUserDefaults standardUserDefaults] stringForKey:TMSelectedThemeIdentifierDefaultsKey]; 32 | } 33 | @end 34 | -------------------------------------------------------------------------------- /LauncherX/LauncherX/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 0.2.2 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright (c) 2018 Carlos Vidal - The MIT License 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/NSBundle+TWX.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import "NSBundle+TWX.h" 12 | #import "TWXRuntime.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @implementation NSBundle (TWX) 17 | 18 | + (nullable NSBundle *)twx_assetsBundle { 19 | static NSBundle *bundle; 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | NSBundle *const frameworkBundle = [NSBundle bundleForClass:[TWXRuntime class]]; 23 | NSURL *__nullable const assetsBundleURL = [frameworkBundle URLForResource:@"TwitterXAssets" withExtension:@"bundle"]; 24 | if (assetsBundleURL) { 25 | bundle = [NSBundle bundleWithURL:assetsBundleURL]; 26 | } 27 | }); 28 | return bundle; 29 | } 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXDirectMessagesCrash.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXDirectMessagesCrash.h" 13 | #import "TWXRuntime.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @implementation TWXDirectMessagesCrash 18 | 19 | + (void)loadFeature { 20 | [TWXRuntime exchangeClassMethod:@"attributedTextForMessage:cellType:" ofClass:@"TMMessageCell"]; 21 | } 22 | 23 | @end 24 | 25 | @implementation NSObject (TWX) 26 | 27 | + (nullable id)TMMessageCell_attributedTextForMessage:(nullable id)message cellType:(NSInteger)cellType { 28 | if ([NSStringFromClass(self) isEqualToString:@"TMMessageCell"] && message == nil) { 29 | return nil; 30 | } 31 | return [self TMMessageCell_attributedTextForMessage:message cellType:cellType]; 32 | } 33 | 34 | @end 35 | 36 | NS_ASSUME_NONNULL_END 37 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/NSImage+TWX.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import "NSImage+TWX.h" 12 | #import "TWXRuntime.h" 13 | #import "NSBundle+TWX.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @implementation NSImage (TWX) 18 | 19 | + (void)load { 20 | [TWXRuntime exchangeClassMethod:@"imageNamed:" ofClass:@"NSImage"]; 21 | } 22 | 23 | @end 24 | 25 | @implementation NSObject (TWX) 26 | 27 | + (NSImage *)NSImage_imageNamed:(NSString *)name { 28 | NSParameterAssert(name); 29 | 30 | NSImage *__nullable const mainBundleImage = [self NSImage_imageNamed:name]; 31 | if (mainBundleImage) { 32 | return mainBundleImage; 33 | } 34 | 35 | NSBundle *__nullable assetsBundle = [NSBundle twx_assetsBundle]; 36 | return [assetsBundle imageForResource:name]; 37 | } 38 | 39 | @end 40 | 41 | NS_ASSUME_NONNULL_END 42 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXEnhancedFullscreen.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXEnhancedFullscreen.h" 13 | #import "TWXRuntime.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @implementation TWXEnhancedFullscreen 18 | 19 | + (void)loadFeature { 20 | [TWXRuntime exchangeInstanceMethod:@"backgroundColor" ofClass:@"NSToolbarFullScreenWindow"]; 21 | } 22 | 23 | @end 24 | 25 | @implementation NSObject (TWX) 26 | 27 | - (nullable NSColor *)NSToolbarFullScreenWindow_backgroundColor { 28 | if ([self isKindOfClass:[@"NSToolbarFullScreenWindow" twx_class]]) { 29 | NSWindow *const window = (NSWindow *)self; 30 | [window.contentView.layer.sublayers.firstObject setHidden:YES]; 31 | return [NSColor clearColor]; 32 | } 33 | return [self NSToolbarFullScreenWindow_backgroundColor]; 34 | } 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/TWXRuntime.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface TWXRuntime: NSObject 16 | 17 | + (void)exchangeInstanceMethod:(NSString *)method ofClass:(NSString *)classString prefix:(nullable NSString *)prefix; 18 | 19 | + (void)exchangeInstanceMethod:(NSString *)method ofClass:(NSString *)classString; 20 | 21 | + (void)exchangeClassMethod:(NSString *)method ofClass:(NSString *)classString; 22 | 23 | @end 24 | 25 | @interface NSString (TWXRuntime) 26 | 27 | - (Class)twx_class; 28 | 29 | @end 30 | 31 | @interface NSObject (TWXRuntime) 32 | 33 | - (void)twx_performSelector:(SEL)selector value:(unsigned long long)value; 34 | 35 | - (void)twx_performSelector:(SEL)selector withObject:(id)object value:(unsigned long long)value; 36 | 37 | - (void)twx_invoke:(NSString*)method arg:(id)arg; 38 | 39 | - (id)twx_invokeAndReturnValue:(NSString*)method; 40 | 41 | @end 42 | 43 | NS_ASSUME_NONNULL_END 44 | -------------------------------------------------------------------------------- /LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "r_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "r_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "r_icon_32-1.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "r_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "r_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "r_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "r_icon_256-1.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "r_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "r_icon_512-1.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "r_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXRoundedStatusContent.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXRoundedStatusContent.h" 13 | #import "TWXRuntime.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @implementation TWXRoundedStatusContent 18 | 19 | + (void)loadFeature { 20 | [TWXRuntime exchangeInstanceMethod:@"layout" ofClass:@"TMNewTimelineStatusCell"]; 21 | } 22 | 23 | @end 24 | 25 | @implementation NSObject (TWX) 26 | 27 | - (void)TMNewTimelineStatusCell_layout { 28 | [self TMNewTimelineStatusCell_layout]; 29 | 30 | if ([self isKindOfClass:[@"TMNewTimelineStatusCell" twx_class]]) { 31 | NSView *const cell = (NSView *)self; 32 | NSView *const containerView = (NSView *)[self performSelector:@selector(containerView)]; 33 | containerView.layer.cornerRadius = 12.0f; 34 | NSTextField *const textField = (NSTextField *)[self performSelector:@selector(tweetTextField)]; 35 | if (cell.layer.backgroundColor) { 36 | textField.backgroundColor = [NSColor colorWithCGColor:cell.layer.backgroundColor]; 37 | } 38 | } 39 | } 40 | 41 | @end 42 | 43 | NS_ASSUME_NONNULL_END 44 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/TwitterX.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import "TwitterX.h" 12 | #import "TWXCharactersLimit.h" 13 | #import "TWXRoundedAvatars.h" 14 | #import "TWXEnhancedFullscreen.h" 15 | #import "TWXRoundedStatusContent.h" 16 | #import "TWXLightThemeReborn.h" 17 | #import "TWXConsumerKeysOverride.h" 18 | #import "TWXRepliesCount.h" 19 | #import "TWXDirectMessagesCrash.h" 20 | #import "TWXBookmarkActions.h" 21 | #import "TWXBookmarksTimeline.h" 22 | #import "TWXBookmarksSidebarItem.h" 23 | #import "TWXThemeMenu.h" 24 | #import "TWXTheme.h" 25 | #import "TWXSource.h" 26 | 27 | NS_ASSUME_NONNULL_BEGIN 28 | 29 | @implementation NSObject (TWX) 30 | 31 | + (void)load { 32 | [TWXCharactersLimit loadFeature]; 33 | [TWXRoundedAvatars loadFeature]; 34 | [TWXEnhancedFullscreen loadFeature]; 35 | [TWXRoundedStatusContent loadFeature]; 36 | [TWXLightThemeReborn loadFeature]; 37 | [TWXConsumerKeysOverride loadFeature]; 38 | [TWXRepliesCount loadFeature]; 39 | [TWXDirectMessagesCrash loadFeature]; 40 | [TWXBookmarkActions loadFeature]; 41 | [TWXBookmarksTimeline loadFeature]; 42 | [TWXBookmarksSidebarItem loadFeature]; 43 | [TWXTheme loadFeature]; 44 | [TWXThemeMenu loadFeature]; 45 | [TWXSource loadFeature]; 46 | } 47 | 48 | @end 49 | 50 | NS_ASSUME_NONNULL_END 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [![TwitterX](/README/logo.png)](#) 2 | 3 | An educational project aiming to maintain Twitter for macOS working while adding new features... 4 | All this by injecting code into the official binary. 5 | 6 |

7 | 8 |

9 | 10 | ## TL;DR; 11 | [Tutorial](https://medium.com/@clipper101/the-best-twitter-app-for-macos-d86778e535c3) by Clipp3r. 12 | 13 | ## Why 14 | 15 | Twitter for macOS provides the best *native and free* experience, although it lacks some features 16 | available on the web version. 17 | 18 | ## How 19 | 20 | > As of 16th of March 2018 you need to provide a valid consumer key and consumer secret in order 21 | > to make the application work. Using custom keys implies uncertain consequences, use TwitterX at 22 | > your own risk. 23 | 24 | For this project to work you need to have Twitter for macOS in your computer. Twitter removed the 25 | application from the macOS AppStore but if you ever installed the application it will still be 26 | available under the **purchases** section of the AppStore. 27 | 28 | TwitterX is a framework that once it is loaded modifies the application behaviour at runtime. This 29 | project provides **LauncherX**, an application that eases the task of loading TwitterX. 30 | 31 | [Download LauncherX](https://github.com/nakiostudio/TwitterX/releases) 32 | 33 | If you want to run the unmodified version of Twitter simply open the Twitter application instead of 34 | LauncherX. 35 | 36 | **This project does not and will never modify the Twitter binary. Upsetting Twitter is not among the 37 | goals of this project therefore modifying or distributing code copyrighted by Twitter will never be 38 | an option.** 39 | 40 | ## Future 41 | 42 | The application is still fully functional although it is hard to tell for how long will this continue. 43 | 44 | ## Author 45 | 46 | [Carlos Vidal](https://www.twitter.com/nakiostudio) 47 | 48 | ## LICENSE 49 | 50 | This project is licensed under the terms of the MIT license. See the LICENSE file. 51 | 52 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXLightThemeReborn.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXLightThemeReborn.h" 13 | #import "TWXRuntime.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @implementation TWXLightThemeReborn 18 | 19 | + (void)loadFeature { 20 | [TWXRuntime exchangeInstanceMethod:@"sidebarBackgroundColor" ofClass:@"TMLightTheme"]; 21 | [TWXRuntime exchangeInstanceMethod:@"sidebarButtonColor" ofClass:@"TMLightTheme"]; 22 | [TWXRuntime exchangeInstanceMethod:@"sidebarButtonSelectedColor" ofClass:@"TMLightTheme"]; 23 | } 24 | 25 | @end 26 | 27 | @implementation NSObject (TWX) 28 | 29 | - (NSColor *)TMLightTheme_sidebarBackgroundColor { 30 | if ([self isKindOfClass:[@"TMLightTheme" twx_class]]) { 31 | return [NSColor whiteColor]; 32 | } 33 | return [self TMLightTheme_sidebarBackgroundColor]; 34 | } 35 | 36 | - (NSColor *)TMLightTheme_sidebarButtonColor { 37 | if ([self isKindOfClass:[@"TMLightTheme" twx_class]]) { 38 | return [NSColor colorWithRed:102.0f/255.0f green:119.0f/255.0f blue:135.0f/255.0f alpha:1.0f]; 39 | } 40 | return [self TMLightTheme_sidebarButtonColor]; 41 | } 42 | 43 | - (NSColor *)TMLightTheme_sidebarButtonSelectedColor { 44 | if ([self isKindOfClass:[@"TMLightTheme" twx_class]]) { 45 | return [NSColor colorWithRed:29.0f/255.0f green:161.0f/255.0f blue:242.0f/255.0f alpha:1.0f]; 46 | } 47 | return [self TMLightTheme_sidebarButtonColor]; 48 | } 49 | 50 | @end 51 | 52 | NS_ASSUME_NONNULL_END 53 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/NSView+TWX.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import "NSView+TWX.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @implementation NSView (TWX) 16 | 17 | - (void)anchorToAttribute:(NSLayoutAttribute)toAttr ofView:(NSView *)view fromAttribute:(NSLayoutAttribute)fromAttr { 18 | [self anchorToAttribute:toAttr ofView:view fromAttribute:fromAttr constant:0.f]; 19 | } 20 | 21 | - (void)anchorToAttribute:(NSLayoutAttribute)toAttr ofView:(NSView *)view fromAttribute:(NSLayoutAttribute)fromAttr constant:(CGFloat)constant { 22 | NSParameterAssert(view); 23 | 24 | NSLayoutConstraint *const constraint = 25 | [NSLayoutConstraint 26 | constraintWithItem:self 27 | attribute:fromAttr 28 | relatedBy:NSLayoutRelationEqual 29 | toItem:view 30 | attribute:toAttr 31 | multiplier:1.0f 32 | constant:constant]; 33 | 34 | if (@available(macOS 10.10, *)) { 35 | [NSLayoutConstraint activateConstraints:@[constraint]]; 36 | return; 37 | } 38 | 39 | NSView *const ownerView = (fromAttr == NSLayoutAttributeWidth || fromAttr == NSLayoutAttributeHeight) ? self : self.superview; 40 | [ownerView addConstraint:constraint]; 41 | } 42 | 43 | - (void)anchorDimenstionAttribute:(NSLayoutAttribute)attr toConstant:(CGFloat)constant { 44 | NSLayoutConstraint *const constraint = 45 | [NSLayoutConstraint 46 | constraintWithItem:self 47 | attribute:attr 48 | relatedBy:NSLayoutRelationEqual 49 | toItem:nil 50 | attribute:attr 51 | multiplier:1.0f 52 | constant:constant]; 53 | 54 | if (@available(macOS 10.10, *)) { 55 | [NSLayoutConstraint activateConstraints:@[constraint]]; 56 | return; 57 | } 58 | 59 | [self addConstraint:constraint]; 60 | } 61 | 62 | @end 63 | 64 | NS_ASSUME_NONNULL_END 65 | -------------------------------------------------------------------------------- /LauncherX/LauncherX/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | import AppKit 12 | 13 | @NSApplicationMain 14 | class AppDelegate: NSObject, NSApplicationDelegate { 15 | 16 | private let twitterX: TwitterX = TwitterX() 17 | 18 | func applicationDidFinishLaunching(_ aNotification: Notification) { 19 | // Check for updates and launch Twitter app 20 | self.twitterX.checkUpdates { [weak self] (isThereUpdate) in 21 | if isThereUpdate { 22 | self?.displayUpdateModal() 23 | } 24 | self?.launchTwitterApp() 25 | } 26 | } 27 | 28 | // MARK: - Private 29 | 30 | private func launchTwitterApp() { 31 | self.twitterX.launchTwitterApp { [weak self] (success, error) in 32 | if success { 33 | exit(0) 34 | } 35 | if let error = error { 36 | self?.displayError(error) 37 | } 38 | } 39 | } 40 | 41 | private func displayError(_ error: Error) { 42 | let alert: NSAlert = NSAlert(error: error) 43 | alert.runModal() 44 | } 45 | 46 | private func displayUpdateModal() { 47 | let alert: NSAlert = NSAlert() 48 | alert.messageText = "There is a new version of TwitterX available" 49 | alert.informativeText = "New tweaks and fixes are available. Click on \"Get update\" to visit the changelog and download the latest version." 50 | alert.addButton(withTitle: "Get update") 51 | alert.addButton(withTitle: "Not now") 52 | if alert.runModal().rawValue == 1000 { 53 | NSWorkspace.shared.open(URL(string: "https://github.com/nakiostudio/TwitterX/releases")!) 54 | } 55 | } 56 | 57 | @IBAction private func didTapQuitMenuItem(_ sender: Any?) { 58 | exit(0) 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /LauncherX/LauncherX/TwitterX.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | import AppKit 12 | 13 | final class TwitterX { 14 | 15 | private static let latestVersionURL: URL = URL(string: "https://raw.githubusercontent.com/nakiostudio/TwitterX/master/version")! 16 | 17 | private let urlSession: URLSession 18 | private let userDefaults: UserDefaults 19 | private let twitterAppURL: URL? 20 | private let twitterXFrameworkURL: URL! 21 | private let versionNumber: String! 22 | 23 | init(urlSession: URLSession = .shared, userDefaults: UserDefaults = .standard) { 24 | self.urlSession = urlSession 25 | self.userDefaults = userDefaults 26 | self.twitterAppURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.twitter.twitter-mac") 27 | let twitterXFrameworkPath: String = Bundle.main.path(forResource: "TwitterX", ofType: "framework")! + "/Versions/A/TwitterX" 28 | self.twitterXFrameworkURL = URL(fileURLWithPath: twitterXFrameworkPath) 29 | self.versionNumber = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String 30 | } 31 | 32 | // MARK: - Public 33 | 34 | func launchTwitterApp(completion: (Bool, Error?) -> Void) { 35 | guard let twitterAppURL = self.twitterAppURL else { 36 | completion(false, nil) 37 | return 38 | } 39 | 40 | do { 41 | try NSWorkspace.shared.launchApplication( 42 | at: twitterAppURL, 43 | options: [], 44 | configuration: [.environment: ["DYLD_INSERT_LIBRARIES": self.twitterXFrameworkURL.path]] 45 | ) 46 | completion(true, nil) 47 | } catch let error { 48 | completion(false, error) 49 | } 50 | } 51 | 52 | func checkUpdates(completion: @escaping (Bool) -> Void) { 53 | self.urlSession.dataTask(with: TwitterX.latestVersionURL) { (data, response, error) in 54 | guard let data = data, error == nil, 55 | let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any], 56 | let latestVersionNumber = json?["version_number"] as? String else { 57 | DispatchQueue.main.async { 58 | completion(false) 59 | } 60 | return 61 | } 62 | 63 | DispatchQueue.main.async { 64 | completion(latestVersionNumber.compare(self.versionNumber, options: .numeric) == .orderedDescending) 65 | } 66 | }.resume() 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXThemeMenu.m: -------------------------------------------------------------------------------- 1 | // 2 | // TWXThemeMenu.m 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/07. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import "TWXThemeMenu.h" 10 | #import 11 | #import "TWXRuntime.h" 12 | #import "NSView+TWX.h" 13 | #import "TWXDuskTheme.h" 14 | 15 | @implementation TWXThemeMenu 16 | + (void)loadFeature { 17 | [[@"TMTheme" twx_class] twx_invoke:@"registerThemeClass:" arg:TWXDuskTheme.class]; 18 | [TWXRuntime exchangeInstanceMethod:@"setGeneralView:" 19 | ofClass:@"TweetiePreferencesWindowController" 20 | prefix: @"TWXThemeMenu"]; 21 | [TWXRuntime exchangeInstanceMethod:@"didChangeDarkModeSetting:" ofClass:@"TweetiePreferencesWindowController"]; 22 | [TWXRuntime exchangeInstanceMethod:@"windowDidLoad" ofClass:@"TweetiePreferencesWindowController"]; 23 | } 24 | @end 25 | 26 | @implementation NSObject (TWX) 27 | 28 | - (void)TweetiePreferencesWindowController_windowDidLoad { 29 | [self TweetiePreferencesWindowController_windowDidLoad]; 30 | 31 | // select current identifier 32 | NSPopUpButton* themePopUpButton = [self valueForKey:@"_darkButton"]; 33 | NSString* currentThemeIdentifier = [[@"TMTheme" twx_class] twx_invokeAndReturnValue:@"currentThemeIdentifier"]; 34 | NSInteger currentThemeIndex = [[self TWX_allThemeIdentifiers] indexOfObject:currentThemeIdentifier]; 35 | NSLog(@"TWXThemeMenu_TweetiePreferencesWindowController_setGeneralView %ld", (long)currentThemeIndex); 36 | [themePopUpButton selectItemAtIndex:currentThemeIndex]; 37 | } 38 | 39 | - (NSArray*)TWX_allThemeNames { 40 | return @[NSLocalizedStringFromTable(@"YLa-rz-sxj.title", @"Preferences", nil), 41 | NSLocalizedStringFromTable(@"07L-lv-aR5.title", @"Preferences", nil), 42 | @"Dusk"]; 43 | } 44 | 45 | - (NSArray*)TWX_allThemeIdentifiers { 46 | return @[@"com.twitter.twitter.themeIdentifiers.lightTheme", 47 | @"com.twitter.twitter.themeIdentifiers.darkTheme", 48 | TWXDuskTheme.themeIdentifier]; 49 | } 50 | 51 | - (void)TWXThemeMenu_TweetiePreferencesWindowController_setGeneralView:(NSView *)view { 52 | if (![self isKindOfClass:[@"TweetiePreferencesWindowController" twx_class]]) { 53 | [self TWXThemeMenu_TweetiePreferencesWindowController_setGeneralView:view]; 54 | return; 55 | } 56 | 57 | NSPopUpButton* themePopUpButton = [self valueForKey:@"_darkButton"]; 58 | [themePopUpButton removeAllItems]; 59 | for (id name in [self TWX_allThemeNames]) { 60 | [themePopUpButton addItemWithTitle:name]; 61 | } 62 | 63 | [self TWXThemeMenu_TweetiePreferencesWindowController_setGeneralView:view]; 64 | } 65 | 66 | - (void)TweetiePreferencesWindowController_didChangeDarkModeSetting:(NSPopUpButton *)sender { 67 | NSInteger index = [sender indexOfSelectedItem]; 68 | NSString* identifier = [self TWX_allThemeIdentifiers][index]; 69 | [[@"TMTheme" twx_class] twx_invoke:@"switchToThemeWithIdentifier:" arg:identifier]; 70 | [[@"TMTheme" twx_class] twx_invoke:@"saveThemeIdentifierToUserDefaults:" arg:identifier]; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXRoundedAvatars.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXRoundedAvatars.h" 13 | #import "TWXRuntime.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @interface NSView (TWX) 18 | 19 | - (void)roundView; 20 | 21 | @end 22 | 23 | @implementation TWXRoundedAvatars 24 | 25 | + (void)loadFeature { 26 | [TWXRuntime exchangeInstanceMethod:@"layout" ofClass:@"TMAvatarImageView"]; 27 | [TWXRuntime exchangeInstanceMethod:@"layout" ofClass:@"TMEditableAvatarView"]; 28 | [TWXRuntime exchangeInstanceMethod:@"layout" ofClass:@"TMComposerAvatarView"]; 29 | [TWXRuntime exchangeInstanceMethod:@"setBaseImage:" ofClass:@"TMAvatarSidebarButton"]; 30 | } 31 | 32 | @end 33 | 34 | @implementation NSObject (Feature) 35 | 36 | - (void)TMAvatarImageView_layout { 37 | [self TMAvatarImageView_layout]; 38 | 39 | if ([self isKindOfClass:[@"TMAvatarImageView" twx_class]]) { 40 | [(NSView *)self roundView]; 41 | } 42 | } 43 | 44 | - (void)TMEditableAvatarView_layout { 45 | [self TMEditableAvatarView_layout]; 46 | 47 | if ([self isKindOfClass:[@"TMEditableAvatarView" twx_class]]) { 48 | [(NSView *)self roundView]; 49 | [((NSView *)self).superview roundView]; 50 | } 51 | } 52 | 53 | - (void)TMComposerAvatarView_layout { 54 | [self TMComposerAvatarView_layout]; 55 | 56 | if ([self isKindOfClass:[@"TMComposerAvatarView" twx_class]]) { 57 | [(NSView *)self roundView]; 58 | } 59 | } 60 | 61 | - (void)TMAvatarSidebarButton_setBaseImage:(nullable NSImage *)image { 62 | if (image == nil || ![self isKindOfClass:[@"TMAvatarSidebarButton" twx_class]]) { 63 | [self TMAvatarSidebarButton_setBaseImage:image]; 64 | return; 65 | } 66 | 67 | NSImage *const roundedImage = [[NSImage alloc] initWithSize:[image size]]; 68 | [roundedImage lockFocus]; 69 | [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh]; 70 | NSRect const imageFrame = NSRectFromCGRect(CGRectMake(0, 0, image.size.width, image.size.height)); 71 | [[NSBezierPath bezierPathWithRoundedRect:imageFrame xRadius:image.size.height * 0.5f yRadius:image.size.height * 0.5f] addClip]; 72 | [image drawAtPoint:NSZeroPoint fromRect:NSMakeRect(0, 0, image.size.width, image.size.height) operation:NSCompositingOperationSourceOver fraction:1]; 73 | [roundedImage unlockFocus]; 74 | 75 | [self TMAvatarSidebarButton_setBaseImage:roundedImage]; 76 | } 77 | 78 | @end 79 | 80 | @implementation NSView (TWX) 81 | 82 | - (void)roundView { 83 | self.wantsLayer = YES; 84 | self.layer.masksToBounds = YES; 85 | self.layer.cornerRadius = self.bounds.size.height * 0.5f; 86 | } 87 | 88 | @end 89 | 90 | NS_ASSUME_NONNULL_END 91 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXBookmarkActions.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import 13 | #import "TWXBookmarkActions.h" 14 | #import "TWXRuntime.h" 15 | #import "TWXAPI+Bookmarks.h" 16 | 17 | NS_ASSUME_NONNULL_BEGIN 18 | 19 | static void *TwitterAPIRefKey = &TwitterAPIRefKey; 20 | 21 | @implementation TWXBookmarkActions 22 | 23 | + (void)loadFeature { 24 | [TWXRuntime exchangeInstanceMethod:@"_addMenuItemsForStatus:toMenu:" ofClass:@"TMBaseViewController"]; 25 | } 26 | 27 | @end 28 | 29 | @implementation NSObject (TWX) 30 | 31 | - (void)TMBaseViewController__addMenuItemsForStatus:(id)status toMenu:(NSMenu *)menu { 32 | NSParameterAssert(status && menu); 33 | 34 | if (![self isKindOfClass:[@"TMBaseViewController" twx_class]]) { 35 | [self TMBaseViewController__addMenuItemsForStatus:status toMenu:menu]; 36 | return; 37 | } 38 | 39 | __block NSInteger indexToInsert = 0; 40 | [menu.itemArray enumerateObjectsUsingBlock:^(NSMenuItem *_Nonnull item, NSUInteger idx, BOOL *_Nonnull stop) { 41 | if ([item.title isEqualToString:@""]) { 42 | indexToInsert = idx; 43 | *stop = YES; 44 | } 45 | }]; 46 | 47 | NSString *const title = [self twx_isBookmarksScreen] ? @"Remove from Bookmarks" : @"Add to Bookmarks..."; 48 | NSMenuItem *const bookmarksMenuItem = [[NSMenuItem alloc] initWithTitle:title action:@selector(twx_didTapBookmarkMenuItem:) keyEquivalent:@"M"]; 49 | bookmarksMenuItem.target = self; 50 | bookmarksMenuItem.representedObject = status; 51 | [menu insertItem:bookmarksMenuItem atIndex:indexToInsert]; 52 | } 53 | 54 | #pragma mark - Convenience 55 | 56 | - (void)twx_didTapBookmarkMenuItem:(NSMenuItem *)menuItem { 57 | NSParameterAssert(menuItem.representedObject); 58 | 59 | TWXAPI *const api = [self twx_api]; 60 | id const status = menuItem.representedObject; 61 | id __nullable const retweetedStatus = [status performSelector:@selector(retweetedStatus)]; 62 | id const targetStatus = retweetedStatus ?: status; 63 | NSString *__nullable const statusID = [targetStatus performSelector:@selector(statusID)]; 64 | if (!statusID) { 65 | return; 66 | } 67 | 68 | if (![self twx_isBookmarksScreen]) { 69 | [api addBookmarkForTweetWithIdentifier:statusID]; 70 | return; 71 | } 72 | 73 | __weak typeof(self) weakSelf = self; 74 | [api removeBookmarkForTweetWithIdentifier:statusID completion:^(id _Nonnull request) { 75 | [weakSelf performSelector:@selector(twx_fetchBookmarks)]; 76 | }]; 77 | } 78 | 79 | - (BOOL)twx_isBookmarksScreen { 80 | NSViewController *const viewController = (NSViewController *)self; 81 | return [viewController.title isEqualToString:@"Bookmarks"]; 82 | } 83 | 84 | - (TWXAPI *)twx_api { 85 | TWXAPI *__nullable const existingAPI = objc_getAssociatedObject(self, TwitterAPIRefKey); 86 | if (existingAPI) { 87 | return existingAPI; 88 | } 89 | 90 | TWXAPI *const newAPI = [[TWXAPI alloc] initWithTwitterAccount:[self performSelector:@selector(account)]]; 91 | objc_setAssociatedObject(self, TwitterAPIRefKey, newAPI, OBJC_ASSOCIATION_RETAIN); 92 | return newAPI; 93 | } 94 | 95 | @end 96 | 97 | NS_ASSUME_NONNULL_END 98 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Utilities/TWXRuntime.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXRuntime.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @implementation TWXRuntime 17 | 18 | + (void)exchangeInstanceMethod:(NSString *)method ofClass:(NSString *)classString prefix:(nullable NSString *)prefix { 19 | NSParameterAssert(method); 20 | NSParameterAssert(classString); 21 | 22 | SEL const originalSelector = NSSelectorFromString(method); 23 | NSString* selectorName = [[classString stringByAppendingString:@"_"] stringByAppendingString:method]; 24 | if (prefix) { 25 | selectorName = [[prefix stringByAppendingString:@"_"] stringByAppendingString:selectorName]; 26 | } 27 | SEL const swizzledSelector = NSSelectorFromString(selectorName); 28 | 29 | Method const originalMethod = class_getInstanceMethod([classString twx_class], originalSelector); 30 | Method const swizzledMethod = class_getInstanceMethod([NSObject class], swizzledSelector); 31 | method_exchangeImplementations(originalMethod, swizzledMethod); 32 | } 33 | 34 | + (void)exchangeInstanceMethod:(NSString *)method ofClass:(NSString *)classString { 35 | [self exchangeInstanceMethod:method ofClass:classString prefix:nil]; 36 | } 37 | 38 | + (void)exchangeClassMethod:(NSString *)method ofClass:(NSString *)classString { 39 | NSParameterAssert(method); 40 | NSParameterAssert(classString); 41 | 42 | SEL const originalSelector = NSSelectorFromString(method); 43 | SEL const swizzledSelector = NSSelectorFromString([[classString stringByAppendingString:@"_"] stringByAppendingString:method]); 44 | 45 | Method const originalMethod = class_getClassMethod([classString twx_class], originalSelector); 46 | Method const swizzledMethod = class_getClassMethod([NSObject class], swizzledSelector); 47 | method_exchangeImplementations(originalMethod, swizzledMethod); 48 | } 49 | 50 | @end 51 | 52 | @implementation NSString (TWXRuntime) 53 | 54 | - (Class)twx_class { 55 | return NSClassFromString(self); 56 | } 57 | 58 | @end 59 | 60 | @implementation NSObject (TWXRuntime) 61 | 62 | - (void)twx_performSelector:(SEL)selector value:(unsigned long long)value { 63 | NSInvocation *const invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]]; 64 | invocation.target = self; 65 | invocation.selector = selector; 66 | [invocation setArgument:&value atIndex:2]; 67 | [invocation invoke]; 68 | } 69 | 70 | - (void)twx_performSelector:(SEL)selector withObject:(id)object value:(unsigned long long)value { 71 | NSParameterAssert(object); 72 | 73 | NSInvocation *const invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]]; 74 | invocation.target = self; 75 | invocation.selector = selector; 76 | [invocation setArgument:&object atIndex:2]; 77 | [invocation setArgument:&value atIndex:3]; 78 | [invocation invoke]; 79 | 80 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 81 | object; 82 | }); 83 | } 84 | 85 | - (void)twx_invoke:(NSString*)method arg:(id)arg { 86 | SEL aSelector = NSSelectorFromString(method); 87 | 88 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:aSelector]]; 89 | 90 | [invocation setTarget:self]; 91 | [invocation setSelector:aSelector]; 92 | [invocation setArgument:&arg atIndex:2]; 93 | 94 | [invocation invoke]; 95 | } 96 | 97 | - (id)twx_invokeAndReturnValue:(NSString*)method { 98 | SEL aSelector = NSSelectorFromString(method); 99 | 100 | id returnStruct; 101 | 102 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:aSelector]]; 103 | 104 | [invocation setTarget:self]; 105 | [invocation setSelector:aSelector]; 106 | 107 | [invocation invoke]; 108 | [invocation getReturnValue:&returnStruct]; 109 | 110 | return returnStruct; 111 | } 112 | 113 | @end 114 | 115 | NS_ASSUME_NONNULL_END 116 | -------------------------------------------------------------------------------- /TwitterX/TwitterX.xcodeproj/xcshareddata/xcschemes/TwitterX.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 70 | 74 | 75 | 76 | 82 | 83 | 84 | 85 | 89 | 90 | 91 | 92 | 93 | 94 | 100 | 101 | 107 | 108 | 109 | 110 | 112 | 113 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /LauncherX/LauncherX/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXRepliesCount.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import 13 | #import "TWXRepliesCount.h" 14 | #import "TWXRuntime.h" 15 | #import "NSView+TWX.h" 16 | 17 | NS_ASSUME_NONNULL_BEGIN 18 | 19 | static void *TwitterStatusRepliesCountKey = &TwitterStatusRepliesCountKey; 20 | 21 | @interface TWXRepliesTextField: NSTextField 22 | 23 | @end 24 | 25 | @implementation TWXRepliesTextField 26 | 27 | @end 28 | 29 | @implementation TWXRepliesCount 30 | 31 | + (void)loadFeature { 32 | [TWXRuntime exchangeInstanceMethod:@"v1_1_GET:parameters:callback:" ofClass:@"TwitterAPI"]; 33 | [TWXRuntime exchangeClassMethod:@"statusWithDictionary:" ofClass:@"TwitterStatus"]; 34 | [TWXRuntime exchangeInstanceMethod:@"setStatus:" ofClass:@"TMNewTimelineStatusCell"]; 35 | } 36 | 37 | @end 38 | 39 | @implementation NSObject (TWX) 40 | 41 | - (void)TwitterAPI_v1_1_GET:(NSString *)endpoint parameters:(NSDictionary *)parameters callback:(id)callback { 42 | NSMutableDictionary *customParameters = [parameters mutableCopy]; 43 | 44 | if ([self isKindOfClass:[@"TwitterAPI" twx_class]] && [endpoint rangeOfString:@"statuses/"].location != NSNotFound) { 45 | [customParameters setObject:@"iPhone-13" forKey:@"cards_platform"]; 46 | [customParameters setObject:@1 forKey:@"include_reply_count"]; 47 | } 48 | 49 | [self TwitterAPI_v1_1_GET:endpoint parameters:customParameters callback:callback]; 50 | } 51 | 52 | + (nullable id)TwitterStatus_statusWithDictionary:(NSDictionary *)dictionary { 53 | id __nullable const status = [self TwitterStatus_statusWithDictionary:dictionary]; 54 | 55 | if ([NSStringFromClass(self) isEqualToString:@"TwitterStatus"]) { 56 | objc_setAssociatedObject(status, TwitterStatusRepliesCountKey, dictionary[@"reply_count"], OBJC_ASSOCIATION_COPY); 57 | } 58 | 59 | return status; 60 | } 61 | 62 | - (void)TMNewTimelineStatusCell_setStatus:(id)status { 63 | [self TMNewTimelineStatusCell_setStatus:status]; 64 | 65 | if ([self isKindOfClass:[@"TMNewTimelineStatusCell" twx_class]]) { 66 | NSView *const cell = (NSView *)self; 67 | NSTextField *__nullable const retweetsTextField = (id)[self performSelector:@selector(retweetTextField)]; 68 | NSButton *__nullable const replyButton = (id)[self performSelector:@selector(replyButton)]; 69 | NSTextField *__nullable repliesTextField; 70 | for (NSView *subview in cell.subviews) { 71 | if ([subview isKindOfClass:[TWXRepliesTextField class]]) { 72 | repliesTextField = (id)subview; 73 | break; 74 | } 75 | } 76 | 77 | if (repliesTextField == nil && replyButton) { 78 | repliesTextField = [[TWXRepliesTextField alloc] initWithFrame:NSMakeRect(0.f, 0.f, 0.f, 0.f)]; 79 | repliesTextField.translatesAutoresizingMaskIntoConstraints = NO; 80 | repliesTextField.backgroundColor = [NSColor clearColor]; 81 | repliesTextField.bordered = retweetsTextField.bordered; 82 | repliesTextField.bezeled = retweetsTextField.bezeled; 83 | repliesTextField.editable = retweetsTextField.editable; 84 | repliesTextField.selectable = retweetsTextField.selectable; 85 | repliesTextField.font = [NSFont systemFontOfSize:13.0f]; 86 | repliesTextField.textColor = [[[@"TMTheme" twx_class] performSelector:@selector(currentTheme)] performSelector:@selector(buttonGrayIconColor)]; 87 | [cell addSubview:repliesTextField]; 88 | 89 | [repliesTextField anchorToAttribute:NSLayoutAttributeTrailing ofView:replyButton fromAttribute:NSLayoutAttributeLeading]; 90 | [repliesTextField anchorToAttribute:NSLayoutAttributeCenterY ofView:replyButton fromAttribute:NSLayoutAttributeCenterY]; 91 | [repliesTextField setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; 92 | [repliesTextField setContentCompressionResistancePriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; 93 | } 94 | 95 | NSNumber *__nullable const repliesCount = objc_getAssociatedObject(status, TwitterStatusRepliesCountKey) ?: @(0); 96 | repliesTextField.stringValue = repliesCount.integerValue > 0 ? repliesCount.stringValue : @""; 97 | } 98 | } 99 | 100 | @end 101 | 102 | NS_ASSUME_NONNULL_END 103 | -------------------------------------------------------------------------------- /LauncherX/LauncherX.xcodeproj/xcshareddata/xcschemes/LauncherX.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 84 | 86 | 92 | 93 | 94 | 95 | 96 | 97 | 103 | 105 | 111 | 112 | 113 | 114 | 116 | 117 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Service/TWXAPI.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXAPI.h" 13 | #import "TWXRuntime.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @interface TWXAPI () 18 | 19 | @property (nonatomic, readonly, strong) id twitterAPI; 20 | @property (nonatomic, readonly, strong) id twitterAccount; 21 | 22 | @end 23 | 24 | @implementation TWXAPI 25 | 26 | - (instancetype)initWithTwitterAccount:(id)twitterAccount { 27 | NSParameterAssert(twitterAccount); 28 | NSParameterAssert([twitterAccount isKindOfClass:[@"TwitterAccount" twx_class]]); 29 | 30 | self = [super init]; 31 | 32 | if (self) { 33 | self->_twitterAccount = twitterAccount; 34 | self->_twitterAPI = [[self class] createTwitterAPIWithAccount:twitterAccount]; 35 | } 36 | 37 | return self; 38 | } 39 | 40 | #pragma mark - Public 41 | 42 | - (void)v1_1_requestTo:(NSString *)endpoint method:(NSString*)method parameters:(NSDictionary *)parameters callback:(void(^)(id))callback { 43 | NSString *const apiRoot = @"https://api.twitter.com/1.1"; 44 | [self requestTo:endpoint apiRoot:apiRoot method:method parameters:parameters multiPartFormData:nil persistent:NO callback:callback start:@"" excludingParameters:nil]; 45 | } 46 | 47 | - (void)v2_requestTo:(NSString *)endpoint method:(NSString*)method parameters:(NSDictionary *)parameters callback:(void(^)(id))callback { 48 | NSString *const apiRoot = @"https://api.twitter.com/2"; 49 | [self requestTo:endpoint apiRoot:apiRoot method:method parameters:parameters multiPartFormData:nil persistent:NO callback:callback start:@"" excludingParameters:nil]; 50 | } 51 | 52 | #pragma mark - Private 53 | 54 | - (void)requestTo:(NSString *)endpoint apiRoot:(NSString *)apiRoot method:(NSString*)method parameters:(NSDictionary *)parameters multiPartFormData:(nullable NSData *)data 55 | persistent:(BOOL)persistent callback:(void(^)(id))callback start:(NSString *)start excludingParameters:(nullable NSDictionary *)excludingParameters { 56 | NSParameterAssert(endpoint && apiRoot && method && parameters && callback && start); 57 | 58 | const SEL requestSelector = @selector(request:apiRoot:method:parameters:multiPartFormData:persistent:callback:start:excludingParameters:); 59 | NSInvocation *const requestInvocation = [NSInvocation invocationWithMethodSignature:[self.twitterAPI methodSignatureForSelector:requestSelector]]; 60 | requestInvocation.selector = requestSelector; 61 | requestInvocation.target = self.twitterAPI; 62 | [requestInvocation setArgument:&endpoint atIndex:2]; 63 | [requestInvocation setArgument:&apiRoot atIndex:3]; 64 | [requestInvocation setArgument:&method atIndex:4]; 65 | [requestInvocation setArgument:¶meters atIndex:5]; 66 | [requestInvocation setArgument:&data atIndex:6]; 67 | [requestInvocation setArgument:&persistent atIndex:7]; 68 | [requestInvocation setArgument:&callback atIndex:8]; 69 | [requestInvocation setArgument:&start atIndex:9]; 70 | [requestInvocation setArgument:&excludingParameters atIndex:10]; 71 | [requestInvocation invoke]; 72 | 73 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 74 | endpoint; apiRoot; method; parameters; data; start; excludingParameters; 75 | }); 76 | } 77 | 78 | #pragma mark - Static 79 | 80 | + (nullable id)createTwitterAPIWithAccount:(id)twitterAccount { 81 | NSParameterAssert(twitterAccount); 82 | 83 | void *result; 84 | 85 | // Arguments 86 | NSString *apiRoot = @"https://api.twitter.com/2"; 87 | void(^__nullable callback)(id) = nil; 88 | 89 | // Invocation 90 | id const allocatedAPI = [NSClassFromString(@"TwitterAPI") alloc]; 91 | const SEL selector = @selector(initWithAccount:apiRoot:callback:); 92 | NSInvocation *const invocation = [NSInvocation invocationWithMethodSignature:[allocatedAPI methodSignatureForSelector:selector]]; 93 | invocation.selector = selector; 94 | invocation.target = allocatedAPI; 95 | [invocation setArgument:&twitterAccount atIndex:2]; 96 | [invocation setArgument:&apiRoot atIndex:3]; 97 | [invocation setArgument:&callback atIndex:4]; 98 | [invocation invoke]; 99 | [invocation getReturnValue:&result]; 100 | 101 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 102 | twitterAccount; apiRoot; callback; 103 | }); 104 | 105 | return (__bridge NSObject *)result; 106 | } 107 | 108 | @end 109 | 110 | NS_ASSUME_NONNULL_END 111 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Service/TWXAPI+Bookmarks.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import "TWXAPI+Bookmarks.h" 13 | #import "TWXRuntime.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @implementation TWXAPI (Bookmarks) 18 | 19 | - (void)fetchBookmarksWithCompletion:(void(^)(NSArray *))completion { 20 | NSParameterAssert(completion); 21 | 22 | NSDictionary *const parameters = [[self class] defaultParameters]; 23 | [self v2_requestTo:@"timeline/bookmark.json" method:@"GET" parameters:parameters callback:^(id _Nonnull request) { 24 | NSData *__nullable const data = (id)[request performSelector:@selector(data)]; 25 | if (!data) { 26 | completion(@[]); 27 | return; 28 | } 29 | 30 | NSError *parseError; 31 | NSDictionary *__nullable const jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&parseError]; 32 | if (parseError || !jsonResponse) { 33 | completion(@[]); 34 | return; 35 | } 36 | 37 | NSMutableArray *const tweets = [NSMutableArray array]; 38 | NSDictionary *__nullable const tweetsDictionaries = jsonResponse[@"globalObjects"][@"tweets"]; 39 | NSDictionary *__nullable const userDictionaries = jsonResponse[@"globalObjects"][@"users"]; 40 | NSArray *const sortedTweetIds = [[self class] sortedTweetIdentifiersForInJsonResponse:jsonResponse]; 41 | for (NSString *const tweetId in sortedTweetIds) { 42 | NSMutableDictionary *__nullable const tweetDictionary = tweetsDictionaries[tweetId]; 43 | if (!tweetDictionary) { 44 | continue; 45 | } 46 | NSMutableDictionary *const mutableTweetDictionary = [tweetDictionary mutableCopy]; 47 | [mutableTweetDictionary setObject:userDictionaries[mutableTweetDictionary[@"user_id_str"]] ?: @{} forKey:@"user"]; 48 | id const tweet = [[@"TwitterStatus" twx_class] performSelector:@selector(statusWithDictionary:) withObject:mutableTweetDictionary]; 49 | if (tweet) { 50 | [tweets addObject:tweet]; 51 | } 52 | } 53 | 54 | completion([tweets copy]); 55 | }]; 56 | } 57 | 58 | - (void)addBookmarkForTweetWithIdentifier:(NSString *)identifier { 59 | NSMutableDictionary *const parameters = [[[self class] defaultParameters] mutableCopy]; 60 | [parameters setObject:@(identifier.integerValue) forKey:@"tweet_id"]; 61 | [self v1_1_requestTo:@"bookmark/entries/add.json" method:@"POST" parameters:parameters callback:^(id _Nonnull request) { 62 | [[NSNotificationCenter defaultCenter] postNotificationName:@"com.twitter.mac.TMSidebarItemViewControllerBookmarkNotification" object:nil]; 63 | }]; 64 | } 65 | 66 | - (void)removeBookmarkForTweetWithIdentifier:(NSString *)identifier completion:(void(^)(id))completion { 67 | __weak typeof(self) weakSelf = self; 68 | [self fetchBookmarksWithCompletion:^(NSArray *_Nonnull tweets) { 69 | for (id const tweet in tweets) { 70 | NSString *const tweetID = [tweet performSelector:@selector(statusID)]; 71 | if ([tweetID isEqualToString:identifier]) { 72 | [weakSelf _proceedDeletingBookmarkWithIdentifier:identifier completion:completion]; 73 | return; 74 | } 75 | } 76 | }]; 77 | } 78 | 79 | #pragma mark - Private 80 | 81 | - (void)_proceedDeletingBookmarkWithIdentifier:(NSString *)identifier completion:(void(^)(id))completion { 82 | NSMutableDictionary *const parameters = [[[self class] defaultParameters] mutableCopy]; 83 | [parameters setObject:@(identifier.integerValue) forKey:@"tweet_id"]; 84 | [self v1_1_requestTo:@"bookmark/entries/remove.json" method:@"POST" parameters:parameters callback:completion]; 85 | } 86 | 87 | #pragma mark - Private static 88 | 89 | + (NSDictionary *)defaultParameters { 90 | return @{ 91 | @"cards_platform": @"iPhone-13", 92 | @"contributor_details": @(1), 93 | @"include_cards": @(1), 94 | @"include_carousels": @(1), 95 | @"include_entities": @(1), 96 | @"include_ext_media_color": @"true", 97 | @"include_media_features": @"true", 98 | @"include_my_retweet": @"true", 99 | @"include_profile_interstitial_type": @"true", 100 | @"include_profile_location": @"true", 101 | @"include_reply_count": @(1), 102 | @"include_user_entities": @"true", 103 | @"include_user_hashtag_entities": @"true", 104 | @"include_user_mention_entities": @"true", 105 | @"include_user_symbol_entities": @"true", 106 | @"request_context": @"ptr", 107 | @"tweet_mode": @"extended" 108 | }; 109 | } 110 | 111 | + (NSArray *)sortedTweetIdentifiersForInJsonResponse:(NSDictionary *)jsonResponse { 112 | NSParameterAssert(jsonResponse); 113 | 114 | NSArray *__nullable const instructionsArray = jsonResponse[@"timeline"][@"instructions"]; 115 | if (!instructionsArray || instructionsArray.count == 0) { 116 | return @[]; 117 | } 118 | 119 | NSMutableArray *const result = [NSMutableArray array]; 120 | NSArray *const expectedEntries = instructionsArray[0][@"addEntries"][@"entries"] ?: @[]; 121 | for (NSDictionary *const entry in expectedEntries) { 122 | NSString *const entryId = entry[@"entryId"]; 123 | if ([entryId hasPrefix:@"tweet-"]) { 124 | NSString *const tweetId = [entryId stringByReplacingCharactersInRange:NSMakeRange(0, 6) withString:@""]; 125 | [result addObject:tweetId]; 126 | } 127 | } 128 | 129 | return result; 130 | } 131 | 132 | @end 133 | 134 | NS_ASSUME_NONNULL_END 135 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXBookmarksTimeline.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import 13 | #import "TWXBookmarksTimeline.h" 14 | #import "TWXRuntime.h" 15 | #import "TWXAPI+Bookmarks.h" 16 | 17 | NS_ASSUME_NONNULL_BEGIN 18 | 19 | static void *TWXBookmarksTimelineAPIRefKey = &TWXBookmarksTimelineAPIRefKey; 20 | static void *TWXBookmarksTimelineStatusesRefKey = &TWXBookmarksTimelineStatusesRefKey; 21 | static void *TWXBookmarksTimelineIsBookmarksScreenRefKey = &TWXBookmarksTimelineIsBookmarksScreenRefKey; 22 | 23 | @implementation TWXBookmarksTimeline 24 | 25 | + (void)loadFeature { 26 | [TWXRuntime exchangeInstanceMethod:@"viewDidLoad" ofClass:@"TWMHomeTimelineController"]; 27 | [TWXRuntime exchangeInstanceMethod:@"viewDidAppear" ofClass:@"TWMHomeTimelineController"]; 28 | [TWXRuntime exchangeInstanceMethod:@"viewWillDisappear" ofClass:@"TWMHomeTimelineController"]; 29 | [TWXRuntime exchangeInstanceMethod:@"numberOfStatusCells" ofClass:@"TWMHomeTimelineController"]; 30 | [TWXRuntime exchangeInstanceMethod:@"numberOfRowsInTableView:" ofClass:@"TWMHomeTimelineController"]; 31 | [TWXRuntime exchangeInstanceMethod:@"statusForRow:" ofClass:@"TWMHomeTimelineController"]; 32 | } 33 | 34 | @end 35 | 36 | @implementation NSObject (TWX) 37 | 38 | - (void)TWMHomeTimelineController_viewDidLoad { 39 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) { 40 | [self TWMHomeTimelineController_viewDidLoad]; 41 | return; 42 | } 43 | 44 | [self TWMHomeTimelineController_viewDidLoad]; 45 | 46 | for (NSString *const name in [self twx_undesiredNotificationNames]) { 47 | [[NSNotificationCenter defaultCenter] removeObserver:self name:name object:nil]; 48 | } 49 | 50 | [self twx_fetchBookmarks]; 51 | } 52 | 53 | - (void)TWMHomeTimelineController_viewDidAppear { 54 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) { 55 | [self TWMHomeTimelineController_viewDidAppear]; 56 | return; 57 | } 58 | 59 | NSTableView *const tableView = [self performSelector:@selector(tableView)]; 60 | tableView.delegate = (id)self; 61 | tableView.dataSource = (id)self; 62 | } 63 | 64 | - (void)TWMHomeTimelineController_viewWillDisappear { 65 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) { 66 | [self TWMHomeTimelineController_viewWillDisappear]; 67 | return; 68 | } 69 | } 70 | 71 | - (NSInteger)TWMHomeTimelineController_numberOfStatusCells { 72 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) { 73 | return [self TWMHomeTimelineController_numberOfStatusCells]; 74 | } 75 | 76 | return [self twx_statuses].count; 77 | } 78 | 79 | - (NSInteger)TWMHomeTimelineController_numberOfRowsInTableView:(NSTableView *)tableView { 80 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) { 81 | return [self TWMHomeTimelineController_numberOfRowsInTableView:tableView]; 82 | } 83 | 84 | return [self twx_statuses].count; 85 | } 86 | 87 | - (id)TWMHomeTimelineController_statusForRow:(NSInteger)row { 88 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) { 89 | return [self TWMHomeTimelineController_statusForRow:row]; 90 | } 91 | 92 | NSArray *const statuses = [self twx_statuses]; 93 | if (statuses.count > row) { 94 | return statuses[row]; 95 | } 96 | 97 | return nil; 98 | } 99 | 100 | #pragma mark - Convenience 101 | 102 | - (BOOL)twx_isBookmarksScreen { 103 | NSViewController *const viewController = (NSViewController *)self; 104 | return [viewController.title isEqualToString:@"Bookmarks"]; 105 | } 106 | 107 | - (NSArray *)twx_statuses { 108 | return objc_getAssociatedObject(self, TWXBookmarksTimelineStatusesRefKey) ?: @[]; 109 | } 110 | 111 | - (void)twx_fetchBookmarks { 112 | __weak typeof(self) weakSelf = self; 113 | [[self twx_api] fetchBookmarksWithCompletion:^(NSArray *_Nonnull statuses) { 114 | dispatch_async(dispatch_get_main_queue(), ^{ 115 | objc_setAssociatedObject(weakSelf, TWXBookmarksTimelineStatusesRefKey, statuses, OBJC_ASSOCIATION_COPY); 116 | NSTableView *const tableView = [weakSelf performSelector:@selector(tableView)]; 117 | [tableView reloadData]; 118 | 119 | if (statuses.count > 0) { 120 | [weakSelf performSelector:@selector(showTableView)]; 121 | return; 122 | } 123 | 124 | tableView.hidden = YES; 125 | 126 | NSTextField *const emptyLabel = [self performSelector:@selector(emptyLabel)]; 127 | emptyLabel.stringValue = statuses.count == 0 ? @"You haven't added any Tweets to your Bookmarks yet" : @""; 128 | emptyLabel.hidden = statuses.count != 0; 129 | 130 | NSView *const loadingView = [self performSelector:@selector(loadingView)]; 131 | loadingView.hidden = NO; 132 | 133 | NSView *const loadingIndicator = [self performSelector:@selector(loadingIndicator)]; 134 | loadingIndicator.hidden = YES; 135 | }); 136 | }]; 137 | } 138 | 139 | - (TWXAPI *)twx_api { 140 | TWXAPI *__nullable const existingAPI = objc_getAssociatedObject(self, TWXBookmarksTimelineAPIRefKey); 141 | if (existingAPI) { 142 | return existingAPI; 143 | } 144 | 145 | TWXAPI *const newAPI = [[TWXAPI alloc] initWithTwitterAccount:[self performSelector:@selector(account)]]; 146 | objc_setAssociatedObject(self, TWXBookmarksTimelineAPIRefKey, newAPI, OBJC_ASSOCIATION_RETAIN); 147 | return newAPI; 148 | } 149 | 150 | - (NSArray *)twx_undesiredNotificationNames { 151 | return @[ 152 | @"com.twitter.mac.TWMHomeTimelineHasNewerTweetsNotification", 153 | @"com.twitter.mac.TWMHomeTimelineRateLimitNotification", 154 | @"com.twitter.mac.TWMHomeTimelineRateLimitExpiredNotification", 155 | @"com.twitter.mac.TWMHomeTimelineHasUpdatedTimelineActivityNotification", 156 | @"com.twitter.mac.TWMHomeTimelineTweetDeletedNotification" 157 | ]; 158 | } 159 | 160 | @end 161 | 162 | NS_ASSUME_NONNULL_END 163 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXBookmarksSidebarItem.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import 13 | #import "TWXBookmarksSidebarItem.h" 14 | #import "TWXRuntime.h" 15 | #import "NSView+TWX.h" 16 | 17 | NS_ASSUME_NONNULL_BEGIN 18 | 19 | static void *TWXBookmarksSidebarItemBookmarksButtonRefKey = &TWXBookmarksSidebarItemBookmarksButtonRefKey; 20 | 21 | @implementation TWXBookmarksSidebarItem 22 | 23 | + (void)loadFeature { 24 | [TWXRuntime exchangeInstanceMethod:@"viewDidLoad" ofClass:@"TMSidebarItemViewController"]; 25 | [TWXRuntime exchangeInstanceMethod:@"themeChanged:" ofClass:@"TMSidebarItemViewController"]; 26 | [TWXRuntime exchangeInstanceMethod:@"toggleButtonStates:" ofClass:@"TMSidebarItemViewController"]; 27 | [TWXRuntime exchangeInstanceMethod:@"jumpToAccount:item:animated:" ofClass:@"TMRootViewController"]; 28 | } 29 | 30 | @end 31 | 32 | @implementation NSObject (TWX) 33 | 34 | - (void)TMSidebarItemViewController_viewDidLoad { 35 | [self TMSidebarItemViewController_viewDidLoad]; 36 | 37 | NSButton *const directMessagesButton = [self performSelector:@selector(directMessagesButton)]; 38 | NSButton *const profileButton = [self performSelector:@selector(profileButton)]; 39 | NSView *const containerView = [profileButton superview]; 40 | 41 | for (NSLayoutConstraint *const constraint in containerView.constraints) { 42 | if (constraint.secondItem == directMessagesButton && constraint.secondAttribute == NSLayoutAttributeBottom) { 43 | [containerView removeConstraint:constraint]; 44 | break; 45 | } 46 | } 47 | 48 | NSLayoutConstraint *const heightConstraint = [self performSelector:@selector(heightConstraint)]; 49 | heightConstraint.constant += 43.f; 50 | 51 | NSLayoutConstraint *const containerHeightConstraint = [self performSelector:@selector(buttonContainerHeightConstraint)]; 52 | containerHeightConstraint.constant += 43.f; 53 | 54 | [self performSelector:@selector(setHeightConstraint:) withObject:nil]; 55 | 56 | NSButton *const bookmarksButton = [self twx_bookmarksButton]; 57 | [bookmarksButton performSelector:@selector(setColorsForStates:) withObject:[profileButton performSelector:@selector(colorsForStates)]]; 58 | [containerView addSubview:bookmarksButton]; 59 | [bookmarksButton anchorToAttribute:NSLayoutAttributeLeading ofView:containerView fromAttribute:NSLayoutAttributeLeading]; 60 | [bookmarksButton anchorToAttribute:NSLayoutAttributeTrailing ofView:containerView fromAttribute:NSLayoutAttributeTrailing]; 61 | [bookmarksButton anchorToAttribute:NSLayoutAttributeBottom ofView:directMessagesButton fromAttribute:NSLayoutAttributeTop constant:5.0f]; 62 | [profileButton anchorToAttribute:NSLayoutAttributeBottom ofView:bookmarksButton fromAttribute:NSLayoutAttributeTop constant: 5.0f]; 63 | 64 | [[NSNotificationCenter defaultCenter] 65 | addObserver:self 66 | selector:@selector(twx_didReceiveAddedToBookmarkNotification:) 67 | name:@"com.twitter.mac.TMSidebarItemViewControllerBookmarkNotification" 68 | object:nil]; 69 | } 70 | 71 | - (void)TMSidebarItemViewController_themeChanged:(id)theme { 72 | if (![self isKindOfClass:[@"TMSidebarItemViewController" twx_class]]) { 73 | [self TMSidebarItemViewController_themeChanged:theme]; 74 | return; 75 | } 76 | 77 | [self TMSidebarItemViewController_themeChanged:theme]; 78 | 79 | NSButton *const directMessagesButton = [self performSelector:@selector(directMessagesButton)]; 80 | NSButton *const bookmarksButton = [self twx_bookmarksButton]; 81 | [bookmarksButton performSelector:@selector(setColorsForStates:) withObject:[directMessagesButton performSelector:@selector(colorsForStates)]]; 82 | } 83 | 84 | - (void)TMSidebarItemViewController_toggleButtonStates:(NSInteger)states { 85 | [self TMSidebarItemViewController_toggleButtonStates:states]; 86 | [[self twx_bookmarksButton] setState:NSControlStateValueOff]; 87 | } 88 | 89 | - (void)TMRootViewController_jumpToAccount:(id)account item:(NSInteger)item animated:(BOOL)animated { 90 | if (![self isKindOfClass:[@"TMRootViewController" twx_class]] || item != 1000) { 91 | return [self TMRootViewController_jumpToAccount:account item:item animated:animated]; 92 | } 93 | 94 | NSInteger const currentItem = (NSInteger)[self performSelector:@selector(currentItem)]; 95 | if (currentItem == item) { 96 | return; 97 | } 98 | 99 | NSViewController *const rootViewController = (NSViewController *)self; 100 | id const appDelegate = [NSApp delegate]; 101 | [appDelegate performSelector:@selector(setSelectedAccount:) withObject:account]; 102 | 103 | NSViewController *bookmarksTimelineController = [[[@"TWMHomeTimelineController" twx_class] alloc] init]; 104 | [bookmarksTimelineController performSelector:@selector(setAccount:) withObject:account]; 105 | [bookmarksTimelineController performSelector:@selector(setTitle:) withObject:@"Bookmarks"]; 106 | 107 | NSViewController *const navigationController = [rootViewController performSelector:@selector(navigationController)]; 108 | [navigationController twx_performSelector:@selector(setRootViewController:animated:) withObject:bookmarksTimelineController value:animated]; 109 | 110 | [rootViewController.view.window makeFirstResponder:bookmarksTimelineController.view]; 111 | [rootViewController performSelector:@selector(setCurrentContentController:) withObject:bookmarksTimelineController]; 112 | [rootViewController twx_performSelector:@selector(setCurrentItem:) value:item]; 113 | } 114 | 115 | #pragma mark - Convenience 116 | 117 | - (NSButton *)twx_bookmarksButton { 118 | NSButton *__nullable const existingButton = objc_getAssociatedObject(self, TWXBookmarksSidebarItemBookmarksButtonRefKey); 119 | if (existingButton) { 120 | return existingButton; 121 | } 122 | 123 | NSButton *const newButton = [[[@"TMSidebarButton" twx_class] alloc] init]; 124 | newButton.translatesAutoresizingMaskIntoConstraints = NO; 125 | [newButton performSelector:@selector(commonInit)]; 126 | [newButton performSelector:@selector(setBaseImageName:) withObject:@"bookmarks"]; 127 | [newButton setTarget:self]; 128 | [newButton setAction:@selector(twx_switchToBookmarks:)]; 129 | objc_setAssociatedObject(self, TWXBookmarksSidebarItemBookmarksButtonRefKey, newButton, OBJC_ASSOCIATION_RETAIN); 130 | return newButton; 131 | } 132 | 133 | - (void)twx_switchToBookmarks:(NSButton *)sender { 134 | NSArray *buttons = @[ 135 | [self performSelector:@selector(homeButton)], 136 | [self performSelector:@selector(momentsButton)], 137 | [self performSelector:@selector(notificationsButton)], 138 | [self performSelector:@selector(directMessagesButton)], 139 | [self performSelector:@selector(listsButton)], 140 | [self performSelector:@selector(profileButton)], 141 | [self performSelector:@selector(searchButton)] 142 | ]; 143 | [buttons enumerateObjectsUsingBlock:^(NSButton * _Nonnull button, NSUInteger idx, BOOL * _Nonnull stop) { 144 | [button setState:NSControlStateValueOff]; 145 | }]; 146 | 147 | NSButton *const bookmarksButton = [self twx_bookmarksButton]; 148 | [bookmarksButton setState:NSControlStateValueOn]; 149 | [bookmarksButton twx_performSelector:@selector(setUnreadItems:) value:0]; 150 | 151 | NSInteger const bookmarksIndex = 1000; 152 | NSViewController *__nullable const parentViewController = ((NSViewController *)self).parentViewController; 153 | id const account = [self performSelector:@selector(account)]; 154 | [parentViewController twx_performSelector:@selector(_internalJumpToAccount:item:) withObject:account value:bookmarksIndex]; 155 | } 156 | 157 | - (void)twx_didReceiveAddedToBookmarkNotification:(NSNotification *)notification { 158 | dispatch_async(dispatch_get_main_queue(), ^{ 159 | NSButton *const bookmarksButton = [self twx_bookmarksButton]; 160 | if (bookmarksButton.state == NSControlStateValueOff) { 161 | [bookmarksButton twx_performSelector:@selector(setUnreadItems:) value:1]; 162 | } 163 | }); 164 | } 165 | 166 | @end 167 | 168 | NS_ASSUME_NONNULL_END 169 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Features/TWXConsumerKeysOverride.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal 2 | // 3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 9 | // SOFTWARE. 10 | 11 | #import 12 | #import 13 | #import "TWXConsumerKeysOverride.h" 14 | #import "TWXRuntime.h" 15 | #import "NSView+TWX.h" 16 | 17 | NS_ASSUME_NONNULL_BEGIN 18 | 19 | NSString *const TWXOAuthConsumerKeyDefaultsKey = @"TWXOAuthConsumerKeyDefaults"; 20 | NSString *const TWXOAuthConsumerSecretDefaultsKey = @"TWXOAuthConsumerSecretDefaults"; 21 | static void *TWXConsumerTextFieldsDelegateKey = &TWXConsumerTextFieldsDelegateKey; 22 | 23 | @interface TWXConsumerTextFieldsDelegate: NSObject 24 | 25 | @end 26 | 27 | @implementation TWXConsumerKeysOverride 28 | 29 | + (void)loadFeature { 30 | [TWXRuntime exchangeClassMethod:@"userAgent" ofClass:@"TwitterAPI"]; 31 | [TWXRuntime exchangeInstanceMethod:@"setValue:forHTTPHeaderField:" ofClass:@"ABHTTPRequest"]; 32 | [TWXRuntime exchangeInstanceMethod:@"oAuthConsumerKey" ofClass:@"TwitterAccount"]; 33 | [TWXRuntime exchangeInstanceMethod:@"oAuthConsumerSecret" ofClass:@"TwitterAccount"]; 34 | [TWXRuntime exchangeInstanceMethod:@"setGeneralView:" ofClass:@"TweetiePreferencesWindowController"]; 35 | [TWXRuntime exchangeInstanceMethod:@"windowDidLoad" ofClass:@"TMLoginWindowController"]; 36 | [TWXRuntime exchangeInstanceMethod:@"worksWhenModal" ofClass:@"NSWindow"]; 37 | } 38 | 39 | @end 40 | 41 | @implementation NSObject (TWX) 42 | 43 | + (NSString *)TwitterAPI_userAgent { 44 | return @"Twitter-iPhone/7.17.1 iOS/9.3.5 (Apple;iPhone4,1;;;;;1;2011)"; 45 | } 46 | 47 | - (void)ABHTTPRequest_setValue:(id)value forHTTPHeaderField:(NSString *)field { 48 | if (![self isKindOfClass:[@"ABHTTPRequest" twx_class]]) { 49 | [self ABHTTPRequest_setValue:value forHTTPHeaderField:field]; 50 | return; 51 | } 52 | 53 | id customValue = value; 54 | 55 | if ([field isEqualToString:@"X-Twitter-Client"]) { 56 | customValue = @"Twitter-iPhone"; 57 | } 58 | else if ([field isEqualToString:@"X-Twitter-Client-Version"]) { 59 | customValue = @"7.17.1"; 60 | [self ABHTTPRequest_setValue:@"5" forHTTPHeaderField:@"X-Twitter-API-Version"]; 61 | } 62 | 63 | [self ABHTTPRequest_setValue:customValue forHTTPHeaderField:field]; 64 | } 65 | 66 | - (NSString *)TwitterAccount_oAuthConsumerKey { 67 | if (![self isKindOfClass:[@"TwitterAccount" twx_class]]) { 68 | return [self TwitterAccount_oAuthConsumerKey]; 69 | } 70 | 71 | return [[NSUserDefaults standardUserDefaults] stringForKey:TWXOAuthConsumerKeyDefaultsKey] ?: [self TwitterAccount_oAuthConsumerKey]; 72 | } 73 | 74 | - (NSString *)TwitterAccount_oAuthConsumerSecret { 75 | if (![self isKindOfClass:[@"TwitterAccount" twx_class]]) { 76 | return [self TwitterAccount_oAuthConsumerSecret]; 77 | } 78 | 79 | return [[NSUserDefaults standardUserDefaults] stringForKey:TWXOAuthConsumerSecretDefaultsKey] ?: [self TwitterAccount_oAuthConsumerSecret]; 80 | } 81 | 82 | - (void)TweetiePreferencesWindowController_setGeneralView:(NSView *)view { 83 | if (![self isKindOfClass:[@"TweetiePreferencesWindowController" twx_class]]) { 84 | [self TweetiePreferencesWindowController_setGeneralView:view]; 85 | return; 86 | } 87 | 88 | TWXConsumerTextFieldsDelegate *const delegate = [[TWXConsumerTextFieldsDelegate alloc] init]; 89 | objc_setAssociatedObject(self, TWXConsumerTextFieldsDelegateKey, delegate, OBJC_ASSOCIATION_RETAIN); 90 | 91 | // Consumer Key TextField 92 | NSTextField *const consumerKeyTextField = [[NSTextField alloc] initWithFrame:NSMakeRect(0.f, 0.f, 0.f, 0.f)]; 93 | consumerKeyTextField.translatesAutoresizingMaskIntoConstraints = NO; 94 | consumerKeyTextField.stringValue = [[NSUserDefaults standardUserDefaults] stringForKey:TWXOAuthConsumerKeyDefaultsKey] ?: @""; 95 | consumerKeyTextField.tag = 0; 96 | consumerKeyTextField.delegate = delegate; 97 | [view addSubview:consumerKeyTextField]; 98 | 99 | [consumerKeyTextField anchorDimenstionAttribute:NSLayoutAttributeWidth toConstant:185.f]; 100 | [consumerKeyTextField anchorToAttribute:NSLayoutAttributeTrailing ofView:view fromAttribute:NSLayoutAttributeTrailing constant:-94.f]; 101 | [consumerKeyTextField anchorToAttribute:NSLayoutAttributeTop ofView:view fromAttribute:NSLayoutAttributeTop constant: 27.0]; 102 | 103 | NSTextField *const consumerKeyLabel = [[self class] createLabel]; 104 | consumerKeyLabel.translatesAutoresizingMaskIntoConstraints = NO; 105 | consumerKeyLabel.stringValue = @"OAuth consumer key:"; 106 | consumerKeyLabel.alignment = NSRightTextAlignment; 107 | [view addSubview:consumerKeyLabel]; 108 | 109 | [consumerKeyLabel anchorToAttribute:NSLayoutAttributeLeading ofView:view fromAttribute:NSLayoutAttributeLeading constant:20.f]; 110 | [consumerKeyLabel anchorToAttribute:NSLayoutAttributeLeading ofView:consumerKeyTextField fromAttribute:NSLayoutAttributeTrailing constant:-9.f]; 111 | [consumerKeyLabel anchorToAttribute:NSLayoutAttributeLastBaseline ofView:consumerKeyTextField fromAttribute:NSLayoutAttributeLastBaseline]; 112 | 113 | // Consumer Secret TextField 114 | NSTextField *const consumerSecretTextField = [[NSTextField alloc] initWithFrame:NSMakeRect(0.f, 0.f, 0.f, 0.f)]; 115 | consumerSecretTextField.translatesAutoresizingMaskIntoConstraints = NO; 116 | consumerSecretTextField.stringValue = [[NSUserDefaults standardUserDefaults] stringForKey:TWXOAuthConsumerSecretDefaultsKey] ?: @""; 117 | consumerSecretTextField.tag = 1; 118 | consumerSecretTextField.delegate = delegate; 119 | [view addSubview:consumerSecretTextField]; 120 | 121 | [consumerSecretTextField anchorDimenstionAttribute:NSLayoutAttributeWidth toConstant:185.f]; 122 | [consumerSecretTextField anchorToAttribute:NSLayoutAttributeTrailing ofView:view fromAttribute:NSLayoutAttributeTrailing constant:-94.f]; 123 | [consumerSecretTextField anchorToAttribute:NSLayoutAttributeBottom ofView:consumerKeyTextField fromAttribute:NSLayoutAttributeTop constant:10.f]; 124 | 125 | NSTextField *const consumerSecretLabel = [[self class] createLabel]; 126 | consumerSecretLabel.translatesAutoresizingMaskIntoConstraints = NO; 127 | consumerSecretLabel.stringValue = @"OAuth consumer secret:"; 128 | consumerSecretLabel.alignment = NSRightTextAlignment; 129 | [view addSubview:consumerSecretLabel]; 130 | 131 | [consumerSecretLabel anchorToAttribute:NSLayoutAttributeLeading ofView:view fromAttribute:NSLayoutAttributeLeading constant:20.f]; 132 | [consumerSecretLabel anchorToAttribute:NSLayoutAttributeLeading ofView:consumerSecretTextField fromAttribute:NSLayoutAttributeTrailing constant:-9.f]; 133 | [consumerSecretLabel anchorToAttribute:NSLayoutAttributeLastBaseline ofView:consumerSecretTextField fromAttribute:NSLayoutAttributeLastBaseline]; 134 | 135 | // Info 136 | NSTextField *const infoLabel = [[self class] createLabel]; 137 | infoLabel.translatesAutoresizingMaskIntoConstraints = NO; 138 | infoLabel.stringValue = @"(requires re-authentication if changed)"; 139 | infoLabel.font = [NSFont systemFontOfSize:9.f]; 140 | infoLabel.alphaValue = 0.48f; 141 | [view addSubview:infoLabel]; 142 | 143 | [infoLabel anchorToAttribute:NSLayoutAttributeLeading ofView:consumerSecretTextField fromAttribute:NSLayoutAttributeLeading]; 144 | [infoLabel anchorToAttribute:NSLayoutAttributeTrailing ofView:consumerSecretTextField fromAttribute:NSLayoutAttributeTrailing]; 145 | [infoLabel anchorToAttribute:NSLayoutAttributeBottom ofView:consumerSecretTextField fromAttribute:NSLayoutAttributeTop constant:2.f]; 146 | 147 | // Anchor existing content 148 | view.frame = NSMakeRect(0.f, 0.f, 481.f, 444.f); 149 | for (NSLayoutConstraint *constraint in view.constraints) { 150 | if ([constraint.firstItem isKindOfClass:[NSPopUpButton class]] && constraint.firstAttribute == NSLayoutAttributeTop && constraint.secondItem == view) { 151 | [view removeConstraint:constraint]; 152 | [constraint.firstItem anchorToAttribute:NSLayoutAttributeBottom ofView:infoLabel fromAttribute:NSLayoutAttributeTop constant:20.f]; 153 | break; 154 | } 155 | } 156 | 157 | [self TweetiePreferencesWindowController_setGeneralView:view]; 158 | } 159 | 160 | - (BOOL)NSWindow_worksWhenModal { 161 | return YES; 162 | } 163 | 164 | - (void)TMLoginWindowController_windowDidLoad { 165 | [self TMLoginWindowController_windowDidLoad]; 166 | if ([self isKindOfClass:[@"TMLoginWindowController" twx_class]]) { 167 | [[NSApp delegate] performSelector:@selector(preferences:) withObject:nil]; 168 | } 169 | } 170 | 171 | #pragma mark - Static 172 | 173 | + (NSTextField *)createLabel { 174 | NSTextField *const label = [[NSTextField alloc] initWithFrame:NSMakeRect(0.f, 0.f, 0.f, 0.f)]; 175 | label.font = [NSFont systemFontOfSize:13.f]; 176 | label.drawsBackground = NO; 177 | label.selectable = NO; 178 | label.bordered = NO; 179 | label.editable = NO; 180 | label.bezeled = NO; 181 | return label; 182 | } 183 | 184 | @end 185 | 186 | @implementation TWXConsumerTextFieldsDelegate 187 | 188 | - (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor { 189 | NSString *const defaultsKey = control.tag == 0 ? TWXOAuthConsumerKeyDefaultsKey : TWXOAuthConsumerSecretDefaultsKey; 190 | [[NSUserDefaults standardUserDefaults] setObject:fieldEditor.string forKey:defaultsKey]; 191 | return YES; 192 | } 193 | 194 | @end 195 | 196 | NS_ASSUME_NONNULL_END 197 | -------------------------------------------------------------------------------- /LauncherX/LauncherX.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C1A59BD420542686003189A1 /* TwitterX.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A59BD320542686003189A1 /* TwitterX.swift */; }; 11 | C1DEB0332053195700137EAA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1DEB0322053195700137EAA /* AppDelegate.swift */; }; 12 | C1DEB0352053195700137EAA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1DEB0342053195700137EAA /* Assets.xcassets */; }; 13 | C1DEB0382053195700137EAA /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = C1DEB0362053195700137EAA /* MainMenu.xib */; }; 14 | C1DEB049205325B100137EAA /* TwitterX.framework in Resources */ = {isa = PBXBuildFile; fileRef = C1DEB046205325A700137EAA /* TwitterX.framework */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXCopyFilesBuildPhase section */ 18 | C1DEB0442053256C00137EAA /* Embed Frameworks */ = { 19 | isa = PBXCopyFilesBuildPhase; 20 | buildActionMask = 2147483647; 21 | dstPath = ""; 22 | dstSubfolderSpec = 10; 23 | files = ( 24 | ); 25 | name = "Embed Frameworks"; 26 | runOnlyForDeploymentPostprocessing = 0; 27 | }; 28 | /* End PBXCopyFilesBuildPhase section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | C1A59BD320542686003189A1 /* TwitterX.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwitterX.swift; sourceTree = ""; }; 32 | C1DEB02F2053195700137EAA /* LauncherX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LauncherX.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | C1DEB0322053195700137EAA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 34 | C1DEB0342053195700137EAA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 35 | C1DEB0372053195700137EAA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 36 | C1DEB0392053195700137EAA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | C1DEB03A2053195700137EAA /* LauncherX.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LauncherX.entitlements; sourceTree = ""; }; 38 | C1DEB046205325A700137EAA /* TwitterX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TwitterX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | C1DEB02C2053195700137EAA /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | ); 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | /* End PBXFrameworksBuildPhase section */ 50 | 51 | /* Begin PBXGroup section */ 52 | C1DEB0262053195700137EAA = { 53 | isa = PBXGroup; 54 | children = ( 55 | C1DEB046205325A700137EAA /* TwitterX.framework */, 56 | C1DEB0312053195700137EAA /* LauncherX */, 57 | C1DEB0302053195700137EAA /* Products */, 58 | ); 59 | sourceTree = ""; 60 | }; 61 | C1DEB0302053195700137EAA /* Products */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | C1DEB02F2053195700137EAA /* LauncherX.app */, 65 | ); 66 | name = Products; 67 | sourceTree = ""; 68 | }; 69 | C1DEB0312053195700137EAA /* LauncherX */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | C1A59BD320542686003189A1 /* TwitterX.swift */, 73 | C1DEB0322053195700137EAA /* AppDelegate.swift */, 74 | C1DEB0342053195700137EAA /* Assets.xcassets */, 75 | C1DEB0362053195700137EAA /* MainMenu.xib */, 76 | C1DEB0392053195700137EAA /* Info.plist */, 77 | C1DEB03A2053195700137EAA /* LauncherX.entitlements */, 78 | ); 79 | path = LauncherX; 80 | sourceTree = ""; 81 | }; 82 | /* End PBXGroup section */ 83 | 84 | /* Begin PBXNativeTarget section */ 85 | C1DEB02E2053195700137EAA /* LauncherX */ = { 86 | isa = PBXNativeTarget; 87 | buildConfigurationList = C1DEB03D2053195700137EAA /* Build configuration list for PBXNativeTarget "LauncherX" */; 88 | buildPhases = ( 89 | C1DEB02B2053195700137EAA /* Sources */, 90 | C1DEB02C2053195700137EAA /* Frameworks */, 91 | C1DEB02D2053195700137EAA /* Resources */, 92 | C1DEB0442053256C00137EAA /* Embed Frameworks */, 93 | ); 94 | buildRules = ( 95 | ); 96 | dependencies = ( 97 | ); 98 | name = LauncherX; 99 | productName = LauncherX; 100 | productReference = C1DEB02F2053195700137EAA /* LauncherX.app */; 101 | productType = "com.apple.product-type.application"; 102 | }; 103 | /* End PBXNativeTarget section */ 104 | 105 | /* Begin PBXProject section */ 106 | C1DEB0272053195700137EAA /* Project object */ = { 107 | isa = PBXProject; 108 | attributes = { 109 | LastSwiftUpdateCheck = 0920; 110 | LastUpgradeCheck = 0920; 111 | ORGANIZATIONNAME = nakioStudio; 112 | TargetAttributes = { 113 | C1DEB02E2053195700137EAA = { 114 | CreatedOnToolsVersion = 9.2; 115 | ProvisioningStyle = Automatic; 116 | SystemCapabilities = { 117 | com.apple.Sandbox = { 118 | enabled = 1; 119 | }; 120 | }; 121 | }; 122 | }; 123 | }; 124 | buildConfigurationList = C1DEB02A2053195700137EAA /* Build configuration list for PBXProject "LauncherX" */; 125 | compatibilityVersion = "Xcode 8.0"; 126 | developmentRegion = en; 127 | hasScannedForEncodings = 0; 128 | knownRegions = ( 129 | en, 130 | Base, 131 | ); 132 | mainGroup = C1DEB0262053195700137EAA; 133 | productRefGroup = C1DEB0302053195700137EAA /* Products */; 134 | projectDirPath = ""; 135 | projectRoot = ""; 136 | targets = ( 137 | C1DEB02E2053195700137EAA /* LauncherX */, 138 | ); 139 | }; 140 | /* End PBXProject section */ 141 | 142 | /* Begin PBXResourcesBuildPhase section */ 143 | C1DEB02D2053195700137EAA /* Resources */ = { 144 | isa = PBXResourcesBuildPhase; 145 | buildActionMask = 2147483647; 146 | files = ( 147 | C1DEB049205325B100137EAA /* TwitterX.framework in Resources */, 148 | C1DEB0352053195700137EAA /* Assets.xcassets in Resources */, 149 | C1DEB0382053195700137EAA /* MainMenu.xib in Resources */, 150 | ); 151 | runOnlyForDeploymentPostprocessing = 0; 152 | }; 153 | /* End PBXResourcesBuildPhase section */ 154 | 155 | /* Begin PBXSourcesBuildPhase section */ 156 | C1DEB02B2053195700137EAA /* Sources */ = { 157 | isa = PBXSourcesBuildPhase; 158 | buildActionMask = 2147483647; 159 | files = ( 160 | C1A59BD420542686003189A1 /* TwitterX.swift in Sources */, 161 | C1DEB0332053195700137EAA /* AppDelegate.swift in Sources */, 162 | ); 163 | runOnlyForDeploymentPostprocessing = 0; 164 | }; 165 | /* End PBXSourcesBuildPhase section */ 166 | 167 | /* Begin PBXVariantGroup section */ 168 | C1DEB0362053195700137EAA /* MainMenu.xib */ = { 169 | isa = PBXVariantGroup; 170 | children = ( 171 | C1DEB0372053195700137EAA /* Base */, 172 | ); 173 | name = MainMenu.xib; 174 | sourceTree = ""; 175 | }; 176 | /* End PBXVariantGroup section */ 177 | 178 | /* Begin XCBuildConfiguration section */ 179 | C1DEB03B2053195700137EAA /* Debug */ = { 180 | isa = XCBuildConfiguration; 181 | buildSettings = { 182 | ALWAYS_SEARCH_USER_PATHS = NO; 183 | CLANG_ANALYZER_NONNULL = YES; 184 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 185 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 186 | CLANG_CXX_LIBRARY = "libc++"; 187 | CLANG_ENABLE_MODULES = YES; 188 | CLANG_ENABLE_OBJC_ARC = YES; 189 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 190 | CLANG_WARN_BOOL_CONVERSION = YES; 191 | CLANG_WARN_COMMA = YES; 192 | CLANG_WARN_CONSTANT_CONVERSION = YES; 193 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 194 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 195 | CLANG_WARN_EMPTY_BODY = YES; 196 | CLANG_WARN_ENUM_CONVERSION = YES; 197 | CLANG_WARN_INFINITE_RECURSION = YES; 198 | CLANG_WARN_INT_CONVERSION = YES; 199 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 200 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 201 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 202 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 203 | CLANG_WARN_STRICT_PROTOTYPES = YES; 204 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 205 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 206 | CLANG_WARN_UNREACHABLE_CODE = YES; 207 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 208 | CODE_SIGN_IDENTITY = "-"; 209 | COPY_PHASE_STRIP = NO; 210 | DEBUG_INFORMATION_FORMAT = dwarf; 211 | ENABLE_STRICT_OBJC_MSGSEND = YES; 212 | ENABLE_TESTABILITY = YES; 213 | GCC_C_LANGUAGE_STANDARD = gnu11; 214 | GCC_DYNAMIC_NO_PIC = NO; 215 | GCC_NO_COMMON_BLOCKS = YES; 216 | GCC_OPTIMIZATION_LEVEL = 0; 217 | GCC_PREPROCESSOR_DEFINITIONS = ( 218 | "DEBUG=1", 219 | "$(inherited)", 220 | ); 221 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 222 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 223 | GCC_WARN_UNDECLARED_SELECTOR = YES; 224 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 225 | GCC_WARN_UNUSED_FUNCTION = YES; 226 | GCC_WARN_UNUSED_VARIABLE = YES; 227 | MACOSX_DEPLOYMENT_TARGET = 10.10; 228 | MTL_ENABLE_DEBUG_INFO = YES; 229 | ONLY_ACTIVE_ARCH = YES; 230 | SDKROOT = macosx; 231 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 232 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 233 | }; 234 | name = Debug; 235 | }; 236 | C1DEB03C2053195700137EAA /* Release */ = { 237 | isa = XCBuildConfiguration; 238 | buildSettings = { 239 | ALWAYS_SEARCH_USER_PATHS = NO; 240 | CLANG_ANALYZER_NONNULL = YES; 241 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 242 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 243 | CLANG_CXX_LIBRARY = "libc++"; 244 | CLANG_ENABLE_MODULES = YES; 245 | CLANG_ENABLE_OBJC_ARC = YES; 246 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 247 | CLANG_WARN_BOOL_CONVERSION = YES; 248 | CLANG_WARN_COMMA = YES; 249 | CLANG_WARN_CONSTANT_CONVERSION = YES; 250 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 251 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 252 | CLANG_WARN_EMPTY_BODY = YES; 253 | CLANG_WARN_ENUM_CONVERSION = YES; 254 | CLANG_WARN_INFINITE_RECURSION = YES; 255 | CLANG_WARN_INT_CONVERSION = YES; 256 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 257 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 258 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 259 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 260 | CLANG_WARN_STRICT_PROTOTYPES = YES; 261 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 262 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 263 | CLANG_WARN_UNREACHABLE_CODE = YES; 264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 265 | CODE_SIGN_IDENTITY = "-"; 266 | COPY_PHASE_STRIP = NO; 267 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 268 | ENABLE_NS_ASSERTIONS = NO; 269 | ENABLE_STRICT_OBJC_MSGSEND = YES; 270 | GCC_C_LANGUAGE_STANDARD = gnu11; 271 | GCC_NO_COMMON_BLOCKS = YES; 272 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 273 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 274 | GCC_WARN_UNDECLARED_SELECTOR = YES; 275 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 276 | GCC_WARN_UNUSED_FUNCTION = YES; 277 | GCC_WARN_UNUSED_VARIABLE = YES; 278 | MACOSX_DEPLOYMENT_TARGET = 10.10; 279 | MTL_ENABLE_DEBUG_INFO = NO; 280 | SDKROOT = macosx; 281 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 282 | }; 283 | name = Release; 284 | }; 285 | C1DEB03E2053195700137EAA /* Debug */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CODE_SIGN_ENTITLEMENTS = LauncherX/LauncherX.entitlements; 290 | CODE_SIGN_STYLE = Automatic; 291 | COMBINE_HIDPI_IMAGES = YES; 292 | INFOPLIST_FILE = LauncherX/Info.plist; 293 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 294 | MACOSX_DEPLOYMENT_TARGET = 10.10; 295 | PRODUCT_BUNDLE_IDENTIFIER = com.nakioStudio.LauncherX; 296 | PRODUCT_NAME = "$(TARGET_NAME)"; 297 | SWIFT_VERSION = 4.0; 298 | }; 299 | name = Debug; 300 | }; 301 | C1DEB03F2053195700137EAA /* Release */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 305 | CODE_SIGN_ENTITLEMENTS = LauncherX/LauncherX.entitlements; 306 | CODE_SIGN_STYLE = Automatic; 307 | COMBINE_HIDPI_IMAGES = YES; 308 | INFOPLIST_FILE = LauncherX/Info.plist; 309 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 310 | MACOSX_DEPLOYMENT_TARGET = 10.10; 311 | PRODUCT_BUNDLE_IDENTIFIER = com.nakioStudio.LauncherX; 312 | PRODUCT_NAME = "$(TARGET_NAME)"; 313 | SWIFT_VERSION = 4.0; 314 | }; 315 | name = Release; 316 | }; 317 | /* End XCBuildConfiguration section */ 318 | 319 | /* Begin XCConfigurationList section */ 320 | C1DEB02A2053195700137EAA /* Build configuration list for PBXProject "LauncherX" */ = { 321 | isa = XCConfigurationList; 322 | buildConfigurations = ( 323 | C1DEB03B2053195700137EAA /* Debug */, 324 | C1DEB03C2053195700137EAA /* Release */, 325 | ); 326 | defaultConfigurationIsVisible = 0; 327 | defaultConfigurationName = Release; 328 | }; 329 | C1DEB03D2053195700137EAA /* Build configuration list for PBXNativeTarget "LauncherX" */ = { 330 | isa = XCConfigurationList; 331 | buildConfigurations = ( 332 | C1DEB03E2053195700137EAA /* Debug */, 333 | C1DEB03F2053195700137EAA /* Release */, 334 | ); 335 | defaultConfigurationIsVisible = 0; 336 | defaultConfigurationName = Release; 337 | }; 338 | /* End XCConfigurationList section */ 339 | }; 340 | rootObject = C1DEB0272053195700137EAA /* Project object */; 341 | } 342 | -------------------------------------------------------------------------------- /TwitterX/TwitterX/Theme/TWXDuskTheme.m: -------------------------------------------------------------------------------- 1 | // 2 | // TWXDuskheme.m 3 | // TwitterX 4 | // 5 | // Created by Tomoya Hirano on 2018/04/07. 6 | // Copyright © 2018年 twitterx. All rights reserved. 7 | // 8 | 9 | #import "TWXDuskTheme.h" 10 | #import 11 | #import "NSColor+TWX.h" 12 | 13 | @implementation TWXDuskTheme 14 | + (void)load{ 15 | 16 | } 17 | + (id)themeImageFilenameIdentifier{ 18 | return @""; 19 | } 20 | + (id)themeIdentifier{ 21 | return @"com.twitter.twitter.themeIdentifiers.duskTheme"; 22 | } 23 | - (id)imageNamed:(NSString *)name{ 24 | NSLog(@"%@", name); 25 | return [NSImage imageNamed:name]; 26 | } 27 | - (id)_themedImageNameForImageName:(id)arg1{ 28 | return @""; 29 | } 30 | - (id)inactiveBadgeBackgroundColor{ 31 | return [NSColor colorFromHexString:@"#1F2028"]; 32 | } 33 | - (id)liveBadgeBackgroundColor{ 34 | return [NSColor colorFromHexString:@"#1F2028"]; 35 | } 36 | - (id)appearance { 37 | return nil; 38 | } 39 | - (id)videoPlayerPlaceholderColor{ 40 | return [NSColor whiteColor]; 41 | } 42 | - (id)GIFSearchRed{ 43 | return [NSColor whiteColor]; 44 | } 45 | - (id)GIFSearchOrange{ 46 | return [NSColor whiteColor]; 47 | } 48 | - (id)GIFSearchYellow{ 49 | return [NSColor whiteColor]; 50 | } 51 | - (id)GIFSearchBlue{ 52 | return [NSColor whiteColor]; 53 | } 54 | - (id)GIFSearchGreen{ 55 | return [NSColor whiteColor]; 56 | } 57 | - (id)GIFSearchPurple{ 58 | return [NSColor whiteColor]; 59 | } 60 | - (id)messageMineSelectedTextBackgroundColor{ 61 | return [NSColor colorFromHexString:@"#1F2028"]; 62 | } 63 | - (id)selectedTextBackgroundColor{ 64 | return [NSColor colorFromHexString:@"#1F2028"]; 65 | } 66 | - (id)copyrightTextColor{ 67 | return [NSColor whiteColor]; 68 | } 69 | - (id)versionTextColor{ 70 | return [NSColor whiteColor]; 71 | } 72 | - (id)logoColor{ 73 | return [NSColor colorFromHexString:@"#616168"]; 74 | } 75 | - (id)aboutBackgroundColor{ 76 | return [NSColor colorFromHexString:@"#1F2028"]; 77 | } 78 | - (id)momentsBlurColor{ 79 | return [NSColor colorFromHexString:@"#2D2D2E"]; 80 | } 81 | - (id)momentCoverSubtextColor{ 82 | return [NSColor colorFromHexString:@"#FFFFFF"]; 83 | } 84 | - (id)momentCoverTitleTextColor{ 85 | return [NSColor colorFromHexString:@"#FFFFFF"]; 86 | } 87 | - (id)momentCoverImageOverlayColor{ 88 | return [[NSColor colorFromHexString:@"#2D2D2E"] colorWithAlphaComponent:0.5]; 89 | } 90 | - (id)momentHeroTimeTextColor{ 91 | return [NSColor colorFromHexString:@"#616168"]; 92 | } 93 | - (id)momentHeroTitleTextColor{ 94 | return [NSColor colorFromHexString:@"#FFFFFF"]; 95 | } 96 | - (id)momentHeroSubcategoryTextColor{ 97 | return [NSColor colorFromHexString:@"#FFFFFF"]; 98 | } 99 | - (id)momentTimeTextColor{ 100 | return [NSColor colorFromHexString:@"#616168"]; 101 | } 102 | - (id)momentTitleTextColor{ 103 | return [NSColor colorFromHexString:@"#FFFFFF"]; 104 | } 105 | - (id)momentSubcategoryTextColor{ 106 | return [NSColor colorFromHexString:@"#7CC155"]; 107 | } 108 | - (id)momentsGuideNavigationBackgroundColor{ 109 | return [NSColor colorFromHexString:@"#1F2028"]; 110 | } 111 | - (id)momentsNavigationActiveColor{ 112 | return [NSColor colorFromHexString:@"#FFFFFF"]; 113 | } 114 | - (id)momentsNavigationColor{ 115 | return [NSColor colorFromHexString:@"#FFFFFF"]; 116 | } 117 | - (id)popoverSeparatorColor{ 118 | return [NSColor colorFromHexString:@"#2D2D2E"]; 119 | } 120 | - (id)popoverRemoveTextColorForState:(long long)arg1{ 121 | return [NSColor whiteColor]; 122 | } 123 | - (id)popoverRemoveTextColor{ 124 | return [NSColor whiteColor]; 125 | } 126 | - (id)popoverActionTextColorForState:(long long)arg1{ 127 | return [NSColor whiteColor]; 128 | } 129 | - (id)popoverActionTextColor{ 130 | return [NSColor whiteColor]; 131 | } 132 | - (id)popoverWarningTextColor{ 133 | return [NSColor whiteColor]; 134 | } 135 | - (id)popoverSubheadTextColor{ 136 | return [NSColor whiteColor]; 137 | } 138 | - (id)popoverTextColor{ 139 | return [NSColor whiteColor]; 140 | } 141 | - (id)searchSuggestionsOverlayBackgroundColor{ 142 | return [NSColor colorFromHexString:@"#1F2028"]; 143 | } 144 | - (id)searchFieldBackgroundColorForState:(long long)arg1{ 145 | return [NSColor colorFromHexString:@"#1F2028"]; 146 | } 147 | - (id)searchFieldBackgroundColor{ 148 | return [NSColor colorFromHexString:@"#1F2028"]; 149 | } 150 | - (id)trendNeutralColor{ 151 | return [NSColor whiteColor]; 152 | } 153 | - (id)trendDownColor{ 154 | return [NSColor whiteColor]; 155 | } 156 | - (id)trendUpColor{ 157 | return [NSColor whiteColor]; 158 | } 159 | - (id)trendColor{ 160 | return [NSColor whiteColor]; 161 | } 162 | - (id)savedSearchIconColor{ 163 | return [NSColor whiteColor]; 164 | } 165 | - (id)searchFilterAndClearIconColor{ 166 | return [NSColor whiteColor]; 167 | } 168 | - (id)searchScopeIconColor{ 169 | return [NSColor whiteColor]; 170 | } 171 | - (id)searchPlaceholderTextColor{ 172 | return [NSColor whiteColor]; 173 | } 174 | - (id)searchTextColor{ 175 | return [NSColor whiteColor]; 176 | } 177 | - (id)searchIconColor{ 178 | return [NSColor whiteColor]; 179 | } 180 | - (id)settingsToggleThumbColor{ 181 | return [NSColor whiteColor]; 182 | } 183 | - (id)settingsToggleOffColorForState:(long long)arg1{ 184 | return [NSColor whiteColor]; 185 | } 186 | - (id)settingsToggleOffColor{ 187 | return [NSColor whiteColor]; 188 | } 189 | - (id)settingsToggleOnColorForState:(long long)arg1{ 190 | return [NSColor whiteColor]; 191 | } 192 | - (id)settingsToggleOnColor{ 193 | return [NSColor whiteColor]; 194 | } 195 | - (id)profileSecondaryTextColor{ 196 | return [NSColor colorFromHexString:@"#616168"]; 197 | } 198 | - (id)profilePrimaryTextColor{ 199 | return [NSColor colorFromHexString:@"#FFFFFF"]; 200 | } 201 | - (id)profileBackButtonColorForState:(long long)arg1{ 202 | return [NSColor colorFromHexString:@"#616168"]; 203 | } 204 | - (id)profileBackButtonColor{ 205 | return [NSColor colorFromHexString:@"#616168"]; 206 | } 207 | - (id)followerRequestsCountBackgroundColor{ 208 | return [NSColor colorFromHexString:@"#1F2028"]; 209 | } 210 | - (id)followerRequestsCountTextColor{ 211 | return [NSColor whiteColor]; 212 | } 213 | - (id)profileProtectedIconColor{ 214 | return [NSColor whiteColor]; 215 | } 216 | - (id)profileProtectedBackgroundColor{ 217 | return [NSColor colorFromHexString:@"#1F2028"]; 218 | } 219 | - (id)profileProtectedTextColor{ 220 | return [NSColor whiteColor]; 221 | } 222 | - (id)profileAvatarBorderColor{ 223 | return [NSColor colorFromHexString:@"#2D2D2E"]; 224 | } 225 | - (id)profileAvatarPlaceholderColor{ 226 | return [NSColor whiteColor]; 227 | } 228 | - (id)profileBannerPlaceholdercolor{ 229 | return [NSColor whiteColor]; 230 | } 231 | - (id)profileTabBackgroundColorForState:(long long)arg1{ 232 | return [NSColor colorFromHexString:@"#1F2028"]; 233 | } 234 | - (id)profileTabBackgroundColor{ 235 | return [NSColor colorFromHexString:@"#1F2028"]; 236 | } 237 | - (id)profileTabSelectedBackgroundColorForState:(long long)arg1{ 238 | return [NSColor colorFromHexString:@"#1F2028"]; 239 | } 240 | - (id)profileTabSelectedBackgroundColor{ 241 | return [NSColor colorFromHexString:@"#1F2028"]; 242 | } 243 | - (id)profileTabSelectedTextColorForState:(long long)arg1{ 244 | return [NSColor whiteColor]; 245 | } 246 | - (id)profileTabSelectedTextColor{ 247 | return [NSColor whiteColor]; 248 | } 249 | - (id)profileTabTextColorForState:(long long)arg1{ 250 | return [NSColor whiteColor]; 251 | } 252 | - (id)profileTabTextColor{ 253 | return [NSColor whiteColor]; 254 | } 255 | - (id)profileTabBackgroundRuleColor{ 256 | return [NSColor whiteColor]; 257 | } 258 | - (id)profileTabDividerColor{ 259 | return [NSColor colorFromHexString:@"#616168"]; 260 | } 261 | - (id)profileFollowersCountColor{ 262 | return [NSColor whiteColor]; 263 | } 264 | - (id)profileFollowersLabelColor{ 265 | return [NSColor whiteColor]; 266 | } 267 | - (id)profileEditPlaceholderColor{ 268 | return [NSColor whiteColor]; 269 | } 270 | - (id)profileTitleBarBottomColor{ 271 | return [NSColor whiteColor]; 272 | } 273 | - (id)profileTitleBarTopColor{ 274 | return [NSColor whiteColor]; 275 | } 276 | - (id)followsYouTokenBackgroundColor{ 277 | return [NSColor colorFromHexString:@"#1F2028"]; 278 | } 279 | - (id)followsYouTokenColor{ 280 | return [NSColor whiteColor]; 281 | } 282 | - (id)directMessageFullNameColor{ 283 | return [NSColor whiteColor]; 284 | } 285 | - (id)directMessageEmptyOtherUserAvatarBackgroundColor{ 286 | return [NSColor colorFromHexString:@"#1F2028"]; 287 | } 288 | - (id)directMessageLeaveConversationTextColorForState:(long long)arg1{ 289 | return [NSColor whiteColor]; 290 | } 291 | - (id)directMessageLeaveConversationTextColor{ 292 | return [NSColor whiteColor]; 293 | } 294 | - (id)directMessageAlreadyAddedIconColor{ 295 | return [NSColor whiteColor]; 296 | } 297 | - (id)directMessageNoMatchTextColor{ 298 | return [NSColor whiteColor]; 299 | } 300 | - (id)directMessageAddPeopleTextColorForState:(long long)arg1{ 301 | return [NSColor whiteColor]; 302 | } 303 | - (id)directMessageAddPeopleTextColor{ 304 | return [NSColor whiteColor]; 305 | } 306 | - (id)directMessageComposeMediaIconColorForState:(long long)arg1{ 307 | return [NSColor whiteColor]; 308 | } 309 | - (id)directMessageComposeMediaIconColor{ 310 | return [NSColor whiteColor]; 311 | } 312 | - (id)directMessageComposePlaceholderTextColor{ 313 | return [NSColor whiteColor]; 314 | } 315 | - (id)directMessageComposeTextColor{ 316 | return [NSColor whiteColor]; 317 | } 318 | - (id)directMessageComposeBorderColor{ 319 | return [NSColor colorFromHexString:@"#2D2D2E"]; 320 | } 321 | - (id)directMessageComposeBackgroundColor{ 322 | return [NSColor colorFromHexString:@"#1F2028"]; 323 | } 324 | - (id)directMessageActionTextColor{ 325 | return [NSColor whiteColor]; 326 | } 327 | - (id)recipientTokenTextColorForState:(long long)arg1{ 328 | return [NSColor whiteColor]; 329 | } 330 | - (id)recipientTokenTextColor{ 331 | return [NSColor whiteColor]; 332 | } 333 | - (id)recipientTokenBackgroundColorForState:(long long)arg1{ 334 | return [NSColor colorFromHexString:@"#1F2028"]; 335 | } 336 | - (id)recipientTokenBackgroundColor{ 337 | return [NSColor colorFromHexString:@"#1F2028"]; 338 | } 339 | - (id)otherChatBubbleBackgroundColor{ 340 | return [NSColor colorFromHexString:@"#1F2028"]; 341 | } 342 | - (id)otherChatBubbleHashtagColor{ 343 | return [NSColor whiteColor]; 344 | } 345 | - (id)otherChatBubbleLinkColor{ 346 | return [NSColor colorFromHexString:@"#7CC155"]; 347 | } 348 | - (id)otherChatBubbleTextColor{ 349 | return [NSColor whiteColor]; 350 | } 351 | - (id)mineChatBubbleBackgroundColor{ 352 | return [NSColor colorFromHexString:@"#1F2028"]; 353 | } 354 | - (id)mineChatBubbleHashtagColor{ 355 | return [NSColor whiteColor]; 356 | } 357 | - (id)mineChatBubbleLinkColor{ 358 | return [NSColor colorFromHexString:@"#7CC155"]; 359 | } 360 | - (id)mineChatBubbleTextColor{ 361 | return [NSColor whiteColor]; 362 | } 363 | - (id)participantBackgroundColor{ 364 | return [NSColor colorFromHexString:@"#1F2028"]; 365 | } 366 | - (id)pollChoiceInputTextColor{ 367 | return [NSColor whiteColor]; 368 | } 369 | - (id)pollChoiceInputPlaceholderTextColor{ 370 | return [NSColor whiteColor]; 371 | } 372 | - (id)pollChoiceInputBackgroundColor{ 373 | return [NSColor colorFromHexString:@"#1F2028"]; 374 | } 375 | - (id)cardPollWinningBarColor{ 376 | return [NSColor whiteColor]; 377 | } 378 | - (id)cardPollBarColor{ 379 | return [NSColor whiteColor]; 380 | } 381 | - (id)cardPollBackgroundColor{ 382 | return [NSColor colorFromHexString:@"#1F2028"]; 383 | } 384 | - (id)cardButtonTextColorForState:(long long)arg1{ 385 | return [NSColor whiteColor]; 386 | } 387 | - (id)cardButtonTextColor{ 388 | return [NSColor whiteColor]; 389 | } 390 | - (id)cardButtonBorderColorForState:(long long)arg1{ 391 | return [NSColor colorFromHexString:@"#2D2D2E"]; 392 | } 393 | - (id)cardButtonBorderColor{ 394 | return [NSColor colorFromHexString:@"#2D2D2E"]; 395 | } 396 | - (id)cardBorderColor{ 397 | return [NSColor colorFromHexString:@"#2D2D2E"]; 398 | } 399 | - (id)detailMetadataCountColor{ 400 | return [NSColor colorFromHexString:@"#FFFFFF"]; 401 | } 402 | - (id)endOfDetailBackgroundColor{ 403 | return [NSColor colorFromHexString:@"#1F2028"]; 404 | } 405 | - (id)mediaViewerIconColor{ 406 | return [NSColor whiteColor]; 407 | } 408 | - (id)mediaViewerBackgroundColor{ 409 | return [NSColor colorFromHexString:@"#1F2028"]; 410 | } 411 | - (id)messageListExcerptTextColor{ 412 | return [NSColor whiteColor]; 413 | } 414 | - (id)messageListActionTextColor{ 415 | return [NSColor whiteColor]; 416 | } 417 | - (id)conversationMutedColor{ 418 | return [NSColor whiteColor]; 419 | } 420 | - (id)messageRepliedColor{ 421 | return [NSColor whiteColor]; 422 | } 423 | - (id)unreadIndicatorColor{ 424 | return [NSColor whiteColor]; 425 | } 426 | - (id)unreadMessageColor{ 427 | return [NSColor whiteColor]; 428 | } 429 | - (id)listInputPlaceHolderTextColor{ 430 | return [NSColor whiteColor]; 431 | } 432 | - (id)listInputBackgroundTextColor{ 433 | return [NSColor whiteColor]; 434 | } 435 | - (id)listProtectedIconColor{ 436 | return [NSColor whiteColor]; 437 | } 438 | - (id)autocompleteBackgroundColor{ 439 | return [NSColor colorFromHexString:@"#1F2028"]; 440 | } 441 | - (id)autocompleteSeparatorColor{ 442 | return [NSColor colorFromHexString:@"#2D2D2E"]; 443 | } 444 | - (id)autocompleteHighlightColor{ 445 | return [NSColor whiteColor]; 446 | } 447 | - (id)autocompleteHashTagColor{ 448 | return [NSColor whiteColor]; 449 | } 450 | - (id)autocompleteFullNameColor{ 451 | return [NSColor whiteColor]; 452 | } 453 | - (id)autocompleteTwitterHandleColor{ 454 | return [NSColor whiteColor]; 455 | } 456 | - (id)composeActionTextSelectedColorForState:(long long)arg1{ 457 | return [NSColor whiteColor]; 458 | } 459 | - (id)composeActionTextSelectedColor{ 460 | return [NSColor whiteColor]; 461 | } 462 | - (id)composeActionIconSelectedColorForState:(long long)arg1{ 463 | return [NSColor whiteColor]; 464 | } 465 | - (id)composeActionIconSelectedColor{ 466 | return [NSColor whiteColor]; 467 | } 468 | - (id)composeActionTextColorForState:(long long)arg1{ 469 | return [NSColor whiteColor]; 470 | } 471 | - (id)composeActionTextColor{ 472 | return [NSColor whiteColor]; 473 | } 474 | - (id)composeActionIconColorForState:(long long)arg1{ 475 | return [NSColor whiteColor]; 476 | } 477 | - (id)composeEmojiIconColor{ 478 | return [NSColor whiteColor]; 479 | } 480 | - (id)composeActionIconColor{ 481 | return [NSColor whiteColor]; 482 | } 483 | - (id)composeBackgroundColor{ 484 | return [NSColor colorFromHexString:@"#1F2028"]; 485 | } 486 | - (id)userRowSelectedTextBackgroundColor{ 487 | return [NSColor colorFromHexString:@"#1F2028"]; 488 | } 489 | - (id)userRowSelectedBackgroundColor{ 490 | return [NSColor colorFromHexString:@"#1F2028"]; 491 | } 492 | - (id)mediaDeleteOverlayColor{ 493 | return [NSColor whiteColor]; 494 | } 495 | - (id)placeholderTextColor{ 496 | return [NSColor whiteColor]; 497 | } 498 | - (id)addMediaIconColorForState:(long long)arg1{ 499 | return [NSColor whiteColor]; 500 | } 501 | - (id)addMediaIconColor{ 502 | return [NSColor whiteColor]; 503 | } 504 | - (id)characterCountOverflowColor{ 505 | return [NSColor whiteColor]; 506 | } 507 | - (id)characterCountColor{ 508 | return [NSColor whiteColor]; 509 | } 510 | - (id)insertionPointColor{ 511 | return [NSColor whiteColor]; 512 | } 513 | - (id)notificationTweetTextColor{ 514 | return [NSColor whiteColor]; 515 | } 516 | - (id)notificationListIconColor{ 517 | return [NSColor whiteColor]; 518 | } 519 | - (id)notificationContactIconColor{ 520 | return [NSColor whiteColor]; 521 | } 522 | - (id)notificationFollowIconColor{ 523 | return [NSColor whiteColor]; 524 | } 525 | - (id)notificationRetweetIconColor{ 526 | return [NSColor whiteColor]; 527 | } 528 | - (id)notificationFavoriteIconColor{ 529 | return [NSColor whiteColor]; 530 | } 531 | - (id)quoteMentionSelectedBackgroundColor{ 532 | return [NSColor colorFromHexString:@"#1F2028"]; 533 | } 534 | - (id)quoteSelectedBackgroundColor{ 535 | return [NSColor colorFromHexString:@"#1F2028"]; 536 | } 537 | - (id)quoteBackgroundColor{ 538 | return [NSColor colorFromHexString:@"#1F2028"]; 539 | } 540 | - (id)quoteMentionSelectedBorderColor{ 541 | return [NSColor colorFromHexString:@"#2D2D2E"]; 542 | } 543 | - (id)quoteSelectedBorderColor{ 544 | return [NSColor colorFromHexString:@"#2D2D2E"]; 545 | } 546 | - (id)quoteBorderColor{ 547 | return [NSColor colorFromHexString:@"#2D2D2E"]; 548 | } 549 | - (id)newFollowerColor{ 550 | return [NSColor whiteColor]; 551 | } 552 | - (id)retweetSelectedColorForState:(long long)arg1{ 553 | return [NSColor whiteColor]; 554 | } 555 | - (id)retweetSelectedColor{ 556 | return [NSColor colorFromHexString:@"#7CC155"]; 557 | } 558 | - (id)favoriteSelectedColorForState:(long long)arg1{ 559 | return [NSColor whiteColor]; 560 | } 561 | - (id)favoriteSelectedColor{ 562 | return [NSColor colorFromHexString:@"#DA2C38"]; 563 | } 564 | - (id)tweetActionTextColorForState:(long long)arg1{ 565 | return [NSColor whiteColor]; 566 | } 567 | - (id)tweetActionTextColor{ 568 | return [NSColor colorFromHexString:@"#616168"]; 569 | } 570 | - (id)tweetActionIconColorForState:(long long)arg1{ 571 | return [NSColor colorFromHexString:@"#616168"]; 572 | } 573 | - (id)tweetActionIconColor{ 574 | return [NSColor colorFromHexString:@"#616168"]; 575 | } 576 | - (id)inputCursorColor{ 577 | return [NSColor whiteColor]; 578 | } 579 | - (id)inputErrorBorderColor{ 580 | return [NSColor colorFromHexString:@"#2D2D2E"]; 581 | } 582 | - (id)inputErrorBackgroundColor{ 583 | return [NSColor colorFromHexString:@"#1F2028"]; 584 | } 585 | - (id)inputErrorPlaceholderTextColor{ 586 | return [NSColor whiteColor]; 587 | } 588 | - (id)inputErrorTextColor{ 589 | return [NSColor whiteColor]; 590 | } 591 | - (id)inputBorderColor{ 592 | return [NSColor colorFromHexString:@"#2D2D2E"]; 593 | } 594 | - (id)inputBackgroundColor{ 595 | return [NSColor colorFromHexString:@"#1F2028"]; 596 | } 597 | - (id)inputTextColor{ 598 | return [NSColor whiteColor]; 599 | } 600 | - (id)minorTextColor{ 601 | return [NSColor whiteColor]; 602 | } 603 | - (id)errorTextColor{ 604 | return [NSColor whiteColor]; 605 | } 606 | - (id)actionTextColorForState:(long long)arg1{ 607 | return [NSColor whiteColor]; 608 | } 609 | - (id)actionTextColor{ 610 | return [NSColor whiteColor]; 611 | } 612 | - (id)atReplyColorForState:(long long)arg1{ 613 | return [NSColor whiteColor]; 614 | } 615 | - (id)atReplyColor{ 616 | return [NSColor whiteColor]; 617 | } 618 | - (id)hashtagColorForState:(long long)arg1{ 619 | return [NSColor whiteColor]; 620 | } 621 | - (id)hashtagColor{ 622 | return [NSColor colorFromHexString:@"#0090B9"]; 623 | } 624 | - (id)linkColorForState:(long long)arg1{ 625 | return [NSColor colorFromHexString:@"#7CC155"]; 626 | } 627 | - (id)linkColor{ 628 | return [NSColor colorFromHexString:@"#7CC155"]; 629 | } 630 | - (id)subheadTextColor{ 631 | return [NSColor whiteColor]; 632 | } 633 | - (id)bodyTextColor{ 634 | return [NSColor colorFromHexString:@"#FFFFFF"]; 635 | } 636 | - (id)metadataLinkColor{ 637 | return [NSColor colorFromHexString:@"#7CC155"]; 638 | } 639 | - (id)metadataColor{ 640 | return [NSColor whiteColor]; 641 | } 642 | - (id)timestampColor{ 643 | return [NSColor colorFromHexString:@"#616168"]; 644 | } 645 | - (id)usernameColor{ 646 | return [NSColor colorFromHexString:@"#B31789"]; 647 | } 648 | - (id)fullNameColor{ 649 | return [NSColor colorFromHexString:@"#B31789"]; 650 | } 651 | - (id)progressIndicatorColor{ 652 | return [NSColor whiteColor]; 653 | } 654 | - (id)retweetedTextColor{ 655 | return [NSColor colorFromHexString:@"#616168"]; 656 | } 657 | - (id)retweetedIconColor{ 658 | return [NSColor colorFromHexString:@"#7CC155"]; 659 | } 660 | - (id)endOfLineIconColor{ 661 | return [NSColor whiteColor]; 662 | } 663 | - (id)endOfLineBackgroundColor{ 664 | return [NSColor colorFromHexString:@"#1F2028"]; 665 | } 666 | - (id)closeButtonColorForState:(long long)arg1{ 667 | return [NSColor colorFromHexString:@"#616168"]; 668 | } 669 | - (id)closeButtonColor{ 670 | return [NSColor colorFromHexString:@"#616168"]; 671 | } 672 | - (id)backButtonColorForState:(long long)arg1{ 673 | return [NSColor colorFromHexString:@"#616168"]; 674 | } 675 | - (id)backButtonColor{ 676 | return [NSColor colorFromHexString:@"#616168"]; 677 | } 678 | - (id)mediaPlaceholderColor{ 679 | return [NSColor whiteColor]; 680 | } 681 | - (id)topTweetIconColor{ 682 | return [NSColor whiteColor]; 683 | } 684 | - (id)promotedIconColor{ 685 | return [NSColor whiteColor]; 686 | } 687 | - (id)mentionSelectedBackgroundColor{ 688 | return [NSColor colorFromHexString:@"#1F2028"]; 689 | } 690 | - (id)selectedBackgroundColor{ 691 | return [NSColor colorFromHexString:@"#1F2028"]; 692 | } 693 | - (id)mentionBackgroundColor{ 694 | return [NSColor colorFromHexString:@"#1F2028"]; 695 | } 696 | - (id)horizontalSeparatorColor{ 697 | return [NSColor colorFromHexString:@"#2D2D2E"]; 698 | } 699 | - (id)emptyStateTextColor{ 700 | return [NSColor whiteColor]; 701 | } 702 | - (id)emptyStateIconColor{ 703 | return [NSColor whiteColor]; 704 | } 705 | - (id)appBackgroundColor{ 706 | return [NSColor colorFromHexString:@"#1F2028"]; 707 | } 708 | - (id)titlebarTextButtonTextColorForState:(long long)arg1{ 709 | return [NSColor colorFromHexString:@"#FFFFFF"]; 710 | } 711 | - (id)titlebarTextButtonTextColor{ 712 | return [NSColor colorFromHexString:@"#616168"]; 713 | } 714 | - (id)titleColorForState:(long long)arg1{ 715 | return [NSColor whiteColor]; 716 | } 717 | - (id)titleColor{ 718 | return [NSColor colorFromHexString:@"#FFFFFF"]; 719 | } 720 | - (id)rowActionIconColorForState:(long long)arg1{ 721 | return [NSColor whiteColor]; 722 | } 723 | - (id)rowActionIconColor{ 724 | return [NSColor whiteColor]; 725 | } 726 | - (id)rowUnselectedIconColorForState:(long long)arg1{ 727 | return [NSColor whiteColor]; 728 | } 729 | - (id)rowUnselectedIconColor{ 730 | return [NSColor whiteColor]; 731 | } 732 | - (id)rowSelectedIconColorForState:(long long)arg1{ 733 | return [NSColor whiteColor]; 734 | } 735 | - (id)rowSelectedIconColor{ 736 | return [NSColor whiteColor]; 737 | } 738 | - (id)borderlessTextColorForState:(long long)arg1{ 739 | return [NSColor colorFromHexString:@"#2D2D2E"]; 740 | } 741 | - (id)buttonWhiteTextColorForState:(long long)arg1{ 742 | return [NSColor colorFromHexString:@"#2D2D2E"]; 743 | } 744 | - (id)buttonWhiteTextColor{ 745 | return [NSColor whiteColor]; 746 | } 747 | - (id)buttonWhiteIconColorForState:(long long)arg1{ 748 | return [NSColor whiteColor]; 749 | } 750 | - (id)buttonWhiteIconColor{ 751 | return [NSColor whiteColor]; 752 | } 753 | - (id)buttonMuteTextColorForState:(long long)arg1{ 754 | return [NSColor whiteColor]; 755 | } 756 | - (id)buttonMuteTextColor{ 757 | return [NSColor whiteColor]; 758 | } 759 | - (id)buttonMuteIconColorForState:(long long)arg1{ 760 | return [NSColor whiteColor]; 761 | } 762 | - (id)buttonMuteIconColor{ 763 | return [NSColor whiteColor]; 764 | } 765 | - (id)buttonGrayTextColorForState:(long long)arg1{ 766 | return [NSColor whiteColor]; 767 | } 768 | - (id)buttonGrayTextColor{ 769 | return [NSColor whiteColor]; 770 | } 771 | - (id)buttonGrayIconColorForState:(long long)arg1{ 772 | return [NSColor whiteColor]; 773 | } 774 | - (id)buttonGrayIconColor{ 775 | return [NSColor whiteColor]; 776 | } 777 | - (id)buttonPollTextColorForState:(long long)arg1{ 778 | return [NSColor whiteColor]; 779 | } 780 | - (id)buttonPollTextColor{ 781 | return [NSColor whiteColor]; 782 | } 783 | - (id)buttonPollIconColorForState:(long long)arg1{ 784 | return [NSColor whiteColor]; 785 | } 786 | - (id)buttonPollIconColor{ 787 | return [NSColor whiteColor]; 788 | } 789 | - (id)buttonBlueTextColorForState:(long long)arg1{ 790 | return [NSColor whiteColor]; 791 | } 792 | - (id)buttonBlueTextColor{ 793 | return [NSColor whiteColor]; 794 | } 795 | - (id)buttonBlueIconColorForState:(long long)arg1{ 796 | return [NSColor whiteColor]; 797 | } 798 | - (id)buttonBlueIconColor{ 799 | return [NSColor whiteColor]; 800 | } 801 | - (id)buttonKeyTextColorForState:(long long)arg1{ 802 | return [NSColor whiteColor]; 803 | } 804 | - (id)buttonKeyTextColor{ 805 | return [NSColor whiteColor]; 806 | } 807 | - (id)buttonKeyIconColorForState:(long long)arg1{ 808 | return [NSColor whiteColor]; 809 | } 810 | - (id)buttonKeyIconColor{ 811 | return [NSColor whiteColor]; 812 | } 813 | - (id)buttonStandardTextColorForState:(long long)arg1{ 814 | return [NSColor whiteColor]; 815 | } 816 | - (id)buttonStandardTextColor{ 817 | return [NSColor whiteColor]; 818 | } 819 | - (id)buttonStandardIconColorForState:(long long)arg1{ 820 | return [NSColor whiteColor]; 821 | } 822 | - (id)buttonStandardIconColor{ 823 | return [NSColor whiteColor]; 824 | } 825 | - (id)sidebarBackgroundColorForState:(long long)arg1{ 826 | return [NSColor colorFromHexString:@"#1F2028"]; 827 | } 828 | - (id)sidebarBackgroundColor{ 829 | return [NSColor colorFromHexString:@"#1F2028"]; 830 | } 831 | - (id)sidebarSeparatorColor{ 832 | return [NSColor colorFromHexString:@"#2D2D2E"]; 833 | } 834 | - (id)sidebarButtonSelectedColorForState:(long long)arg1{ 835 | return [NSColor whiteColor]; 836 | } 837 | - (id)sidebarButtonSelectedColor{ 838 | return [NSColor colorFromHexString:@"#FFFFFF"]; 839 | } 840 | - (id)sidebarButtonColorForState:(long long)arg1{ 841 | return [NSColor colorFromHexString:@"#616168"]; 842 | } 843 | - (id)sidebarButtonColor{ 844 | return [NSColor whiteColor]; 845 | } 846 | - (id)iconGray{ 847 | return [NSColor whiteColor]; 848 | } 849 | - (id)textGray{ 850 | return [NSColor whiteColor]; 851 | } 852 | - (id)backgroundGray{ 853 | return [NSColor whiteColor]; 854 | } 855 | - (id)placeholderGray{ 856 | return [NSColor whiteColor]; 857 | } 858 | - (id)disabledGray{ 859 | return [NSColor whiteColor]; 860 | } 861 | - (id)inactiveGray{ 862 | return [NSColor whiteColor]; 863 | } 864 | - (id)strongGray{ 865 | return [NSColor whiteColor]; 866 | } 867 | - (id)weakGray{ 868 | return [NSColor whiteColor]; 869 | } 870 | - (id)red{ 871 | return [NSColor whiteColor]; 872 | } 873 | - (id)blue{ 874 | return [NSColor whiteColor]; 875 | } 876 | - (id)neededColor{ 877 | return [NSColor whiteColor]; 878 | } 879 | - (id)modifiedColorForControlState:(long long)arg1 color:(id)arg2{ 880 | return [NSColor whiteColor]; 881 | } 882 | - (id)pressedColorForColor:(id)arg1{ 883 | return [NSColor whiteColor]; 884 | } 885 | - (id)hoverColorForColor:(id)arg1{ 886 | return [NSColor whiteColor]; 887 | } 888 | - (id)inactiveColorForColor:(id)arg1{ 889 | return [NSColor whiteColor]; 890 | } 891 | - (id)disabledColorForColor:(id)arg1{ 892 | return [NSColor whiteColor]; 893 | } 894 | @end 895 | -------------------------------------------------------------------------------- /TwitterX/TwitterX.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 33075926203D9EF700CAA86C /* TWXEnhancedFullscreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 33075924203D9EF700CAA86C /* TWXEnhancedFullscreen.h */; }; 11 | 33075927203D9EF700CAA86C /* TWXEnhancedFullscreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 33075925203D9EF700CAA86C /* TWXEnhancedFullscreen.m */; }; 12 | 335D38CD20387EE300379A21 /* TwitterX.h in Headers */ = {isa = PBXBuildFile; fileRef = 335D38CB20387EE300379A21 /* TwitterX.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | 335D38D320387EF100379A21 /* TWXFeature.h in Headers */ = {isa = PBXBuildFile; fileRef = 335D38B420386E4F00379A21 /* TWXFeature.h */; }; 14 | 335D38D520387EF600379A21 /* TWXCharactersLimit.h in Headers */ = {isa = PBXBuildFile; fileRef = 335D38B820386E8300379A21 /* TWXCharactersLimit.h */; }; 15 | 335D38D620387EF900379A21 /* TWXCharactersLimit.m in Sources */ = {isa = PBXBuildFile; fileRef = 335D38B920386E8300379A21 /* TWXCharactersLimit.m */; }; 16 | 335D38D720387EFC00379A21 /* TWXRoundedAvatars.h in Headers */ = {isa = PBXBuildFile; fileRef = 335D38BC20386EA500379A21 /* TWXRoundedAvatars.h */; }; 17 | 335D38D820387EFF00379A21 /* TWXRoundedAvatars.m in Sources */ = {isa = PBXBuildFile; fileRef = 335D38BD20386EA500379A21 /* TWXRoundedAvatars.m */; }; 18 | 335D38DD2038DA6100379A21 /* TWXRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = 335D38DB2038DA6100379A21 /* TWXRuntime.h */; }; 19 | 335D38DE2038DA6100379A21 /* TWXRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = 335D38DC2038DA6100379A21 /* TWXRuntime.m */; }; 20 | 335D38E02038E45C00379A21 /* TwitterX.m in Sources */ = {isa = PBXBuildFile; fileRef = 335D38DF2038E45C00379A21 /* TwitterX.m */; }; 21 | 33B95C172062767F0060B8D5 /* TWXAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C152062767F0060B8D5 /* TWXAPI.h */; }; 22 | 33B95C182062767F0060B8D5 /* TWXAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C162062767F0060B8D5 /* TWXAPI.m */; }; 23 | 33B95C1D206294EE0060B8D5 /* TWXAPI+Bookmarks.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C1B206294EE0060B8D5 /* TWXAPI+Bookmarks.h */; }; 24 | 33B95C1E206294EE0060B8D5 /* TWXAPI+Bookmarks.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C1C206294EE0060B8D5 /* TWXAPI+Bookmarks.m */; }; 25 | 33B95C212062D1B30060B8D5 /* TWXBookmarksTimeline.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C1F2062D1B30060B8D5 /* TWXBookmarksTimeline.h */; }; 26 | 33B95C222062D1B30060B8D5 /* TWXBookmarksTimeline.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C202062D1B30060B8D5 /* TWXBookmarksTimeline.m */; }; 27 | 33B95C252062E7720060B8D5 /* TWXBookmarksSidebarItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C232062E7720060B8D5 /* TWXBookmarksSidebarItem.h */; }; 28 | 33B95C262062E7720060B8D5 /* TWXBookmarksSidebarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C242062E7720060B8D5 /* TWXBookmarksSidebarItem.m */; }; 29 | 33B95C34206308190060B8D5 /* TwitterXAssets.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 33B95C2B2062FF820060B8D5 /* TwitterXAssets.bundle */; }; 30 | 33B95C3E206310760060B8D5 /* NSImage+TWX.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C3C206310760060B8D5 /* NSImage+TWX.h */; }; 31 | 33B95C3F206310760060B8D5 /* NSImage+TWX.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C3D206310760060B8D5 /* NSImage+TWX.m */; }; 32 | 33B95C4620631AB30060B8D5 /* bookmarks.png in Resources */ = {isa = PBXBuildFile; fileRef = 33B95C4420631AB20060B8D5 /* bookmarks.png */; }; 33 | 33B95C4720631AB30060B8D5 /* bookmarks@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 33B95C4520631AB20060B8D5 /* bookmarks@2x.png */; }; 34 | 33B95C4A2063A6EA0060B8D5 /* NSBundle+TWX.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C482063A6EA0060B8D5 /* NSBundle+TWX.h */; }; 35 | 33B95C4B2063A6EA0060B8D5 /* NSBundle+TWX.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C492063A6EA0060B8D5 /* NSBundle+TWX.m */; }; 36 | 37E3E2222078D41800C5B683 /* TWXDuskTheme.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E2202078D41800C5B683 /* TWXDuskTheme.h */; }; 37 | 37E3E2232078D41800C5B683 /* TWXDuskTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E2212078D41800C5B683 /* TWXDuskTheme.m */; }; 38 | 37E3E2262078D93200C5B683 /* TWXThemeMenu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E2242078D93200C5B683 /* TWXThemeMenu.h */; }; 39 | 37E3E2272078D93200C5B683 /* TWXThemeMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E2252078D93200C5B683 /* TWXThemeMenu.m */; }; 40 | 37E3E2322078F98700C5B683 /* NSColor+TWX.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E2302078F98700C5B683 /* NSColor+TWX.h */; }; 41 | 37E3E2332078F98700C5B683 /* NSColor+TWX.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E2312078F98700C5B683 /* NSColor+TWX.m */; }; 42 | 37E3E23A2079ED5900C5B683 /* TWXTheme.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E2382079ED5900C5B683 /* TWXTheme.h */; }; 43 | 37E3E23B2079ED5900C5B683 /* TWXTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E2392079ED5900C5B683 /* TWXTheme.m */; }; 44 | 37E3E23E2079FE8700C5B683 /* NSString+TWX.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E23C2079FE8700C5B683 /* NSString+TWX.h */; }; 45 | 37E3E23F2079FE8700C5B683 /* NSString+TWX.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E23D2079FE8700C5B683 /* NSString+TWX.m */; }; 46 | 37E3E2422079FF3600C5B683 /* TWXSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E2402079FF3600C5B683 /* TWXSource.h */; }; 47 | 37E3E2432079FF3600C5B683 /* TWXSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E2412079FF3600C5B683 /* TWXSource.m */; }; 48 | C13554702061C4E700F6504D /* TWXBookmarkActions.h in Headers */ = {isa = PBXBuildFile; fileRef = C135546E2061C4E700F6504D /* TWXBookmarkActions.h */; }; 49 | C13554712061C4E700F6504D /* TWXBookmarkActions.m in Sources */ = {isa = PBXBuildFile; fileRef = C135546F2061C4E700F6504D /* TWXBookmarkActions.m */; }; 50 | C18C74F620507003001B0AAC /* TWXDirectMessagesCrash.h in Headers */ = {isa = PBXBuildFile; fileRef = C18C74F420507003001B0AAC /* TWXDirectMessagesCrash.h */; }; 51 | C18C74F720507003001B0AAC /* TWXDirectMessagesCrash.m in Sources */ = {isa = PBXBuildFile; fileRef = C18C74F520507003001B0AAC /* TWXDirectMessagesCrash.m */; }; 52 | C1B3282F2059160D00009EA8 /* NSView+TWX.h in Headers */ = {isa = PBXBuildFile; fileRef = C1B3282D2059160D00009EA8 /* NSView+TWX.h */; }; 53 | C1B328302059160D00009EA8 /* NSView+TWX.m in Sources */ = {isa = PBXBuildFile; fileRef = C1B3282E2059160D00009EA8 /* NSView+TWX.m */; }; 54 | C1D846F42041BB080016E52D /* TWXRoundedStatusContent.h in Headers */ = {isa = PBXBuildFile; fileRef = C1D846F22041BB080016E52D /* TWXRoundedStatusContent.h */; }; 55 | C1D846F52041BB080016E52D /* TWXRoundedStatusContent.m in Sources */ = {isa = PBXBuildFile; fileRef = C1D846F32041BB080016E52D /* TWXRoundedStatusContent.m */; }; 56 | C1D846F82041F5950016E52D /* TWXLightThemeReborn.h in Headers */ = {isa = PBXBuildFile; fileRef = C1D846F62041F5940016E52D /* TWXLightThemeReborn.h */; }; 57 | C1D846F92041F5950016E52D /* TWXLightThemeReborn.m in Sources */ = {isa = PBXBuildFile; fileRef = C1D846F72041F5940016E52D /* TWXLightThemeReborn.m */; }; 58 | C1D846FC2042B08C0016E52D /* TWXRepliesCount.h in Headers */ = {isa = PBXBuildFile; fileRef = C1D846FA2042B08C0016E52D /* TWXRepliesCount.h */; }; 59 | C1D846FD2042B08C0016E52D /* TWXRepliesCount.m in Sources */ = {isa = PBXBuildFile; fileRef = C1D846FB2042B08C0016E52D /* TWXRepliesCount.m */; }; 60 | C1D84700204358FE0016E52D /* TWXConsumerKeysOverride.h in Headers */ = {isa = PBXBuildFile; fileRef = C1D846FE204358FE0016E52D /* TWXConsumerKeysOverride.h */; }; 61 | C1D84701204358FE0016E52D /* TWXConsumerKeysOverride.m in Sources */ = {isa = PBXBuildFile; fileRef = C1D846FF204358FE0016E52D /* TWXConsumerKeysOverride.m */; }; 62 | /* End PBXBuildFile section */ 63 | 64 | /* Begin PBXFileReference section */ 65 | 33075924203D9EF700CAA86C /* TWXEnhancedFullscreen.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXEnhancedFullscreen.h; sourceTree = ""; }; 66 | 33075925203D9EF700CAA86C /* TWXEnhancedFullscreen.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXEnhancedFullscreen.m; sourceTree = ""; }; 67 | 335D38B420386E4F00379A21 /* TWXFeature.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXFeature.h; sourceTree = ""; }; 68 | 335D38B820386E8300379A21 /* TWXCharactersLimit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXCharactersLimit.h; sourceTree = ""; }; 69 | 335D38B920386E8300379A21 /* TWXCharactersLimit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXCharactersLimit.m; sourceTree = ""; }; 70 | 335D38BC20386EA500379A21 /* TWXRoundedAvatars.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXRoundedAvatars.h; sourceTree = ""; }; 71 | 335D38BD20386EA500379A21 /* TWXRoundedAvatars.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXRoundedAvatars.m; sourceTree = ""; }; 72 | 335D38C920387EE300379A21 /* TwitterX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TwitterX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 73 | 335D38CB20387EE300379A21 /* TwitterX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TwitterX.h; sourceTree = ""; }; 74 | 335D38CC20387EE300379A21 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 75 | 335D38DB2038DA6100379A21 /* TWXRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXRuntime.h; sourceTree = ""; }; 76 | 335D38DC2038DA6100379A21 /* TWXRuntime.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXRuntime.m; sourceTree = ""; }; 77 | 335D38DF2038E45C00379A21 /* TwitterX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TwitterX.m; sourceTree = ""; }; 78 | 33B95C152062767F0060B8D5 /* TWXAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXAPI.h; sourceTree = ""; }; 79 | 33B95C162062767F0060B8D5 /* TWXAPI.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXAPI.m; sourceTree = ""; }; 80 | 33B95C1B206294EE0060B8D5 /* TWXAPI+Bookmarks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TWXAPI+Bookmarks.h"; sourceTree = ""; }; 81 | 33B95C1C206294EE0060B8D5 /* TWXAPI+Bookmarks.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "TWXAPI+Bookmarks.m"; sourceTree = ""; }; 82 | 33B95C1F2062D1B30060B8D5 /* TWXBookmarksTimeline.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXBookmarksTimeline.h; sourceTree = ""; }; 83 | 33B95C202062D1B30060B8D5 /* TWXBookmarksTimeline.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXBookmarksTimeline.m; sourceTree = ""; }; 84 | 33B95C232062E7720060B8D5 /* TWXBookmarksSidebarItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXBookmarksSidebarItem.h; sourceTree = ""; }; 85 | 33B95C242062E7720060B8D5 /* TWXBookmarksSidebarItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXBookmarksSidebarItem.m; sourceTree = ""; }; 86 | 33B95C2B2062FF820060B8D5 /* TwitterXAssets.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TwitterXAssets.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 87 | 33B95C2D2062FF820060B8D5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 88 | 33B95C3C206310760060B8D5 /* NSImage+TWX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSImage+TWX.h"; sourceTree = ""; }; 89 | 33B95C3D206310760060B8D5 /* NSImage+TWX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSImage+TWX.m"; sourceTree = ""; }; 90 | 33B95C4420631AB20060B8D5 /* bookmarks.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bookmarks.png; sourceTree = ""; }; 91 | 33B95C4520631AB20060B8D5 /* bookmarks@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bookmarks@2x.png"; sourceTree = ""; }; 92 | 33B95C482063A6EA0060B8D5 /* NSBundle+TWX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSBundle+TWX.h"; sourceTree = ""; }; 93 | 33B95C492063A6EA0060B8D5 /* NSBundle+TWX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+TWX.m"; sourceTree = ""; }; 94 | 37E3E2202078D41800C5B683 /* TWXDuskTheme.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXDuskTheme.h; sourceTree = ""; }; 95 | 37E3E2212078D41800C5B683 /* TWXDuskTheme.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXDuskTheme.m; sourceTree = ""; }; 96 | 37E3E2242078D93200C5B683 /* TWXThemeMenu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXThemeMenu.h; sourceTree = ""; }; 97 | 37E3E2252078D93200C5B683 /* TWXThemeMenu.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXThemeMenu.m; sourceTree = ""; }; 98 | 37E3E2302078F98700C5B683 /* NSColor+TWX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSColor+TWX.h"; sourceTree = ""; }; 99 | 37E3E2312078F98700C5B683 /* NSColor+TWX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSColor+TWX.m"; sourceTree = ""; }; 100 | 37E3E2382079ED5900C5B683 /* TWXTheme.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXTheme.h; sourceTree = ""; }; 101 | 37E3E2392079ED5900C5B683 /* TWXTheme.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXTheme.m; sourceTree = ""; }; 102 | 37E3E23C2079FE8700C5B683 /* NSString+TWX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+TWX.h"; sourceTree = ""; }; 103 | 37E3E23D2079FE8700C5B683 /* NSString+TWX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+TWX.m"; sourceTree = ""; }; 104 | 37E3E2402079FF3600C5B683 /* TWXSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXSource.h; sourceTree = ""; }; 105 | 37E3E2412079FF3600C5B683 /* TWXSource.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXSource.m; sourceTree = ""; }; 106 | C135546E2061C4E700F6504D /* TWXBookmarkActions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXBookmarkActions.h; sourceTree = ""; }; 107 | C135546F2061C4E700F6504D /* TWXBookmarkActions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXBookmarkActions.m; sourceTree = ""; }; 108 | C18C74F420507003001B0AAC /* TWXDirectMessagesCrash.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXDirectMessagesCrash.h; sourceTree = ""; }; 109 | C18C74F520507003001B0AAC /* TWXDirectMessagesCrash.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXDirectMessagesCrash.m; sourceTree = ""; }; 110 | C1B3282D2059160D00009EA8 /* NSView+TWX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSView+TWX.h"; sourceTree = ""; }; 111 | C1B3282E2059160D00009EA8 /* NSView+TWX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSView+TWX.m"; sourceTree = ""; }; 112 | C1D846F22041BB080016E52D /* TWXRoundedStatusContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXRoundedStatusContent.h; sourceTree = ""; }; 113 | C1D846F32041BB080016E52D /* TWXRoundedStatusContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXRoundedStatusContent.m; sourceTree = ""; }; 114 | C1D846F62041F5940016E52D /* TWXLightThemeReborn.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXLightThemeReborn.h; sourceTree = ""; }; 115 | C1D846F72041F5940016E52D /* TWXLightThemeReborn.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXLightThemeReborn.m; sourceTree = ""; }; 116 | C1D846FA2042B08C0016E52D /* TWXRepliesCount.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXRepliesCount.h; sourceTree = ""; }; 117 | C1D846FB2042B08C0016E52D /* TWXRepliesCount.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXRepliesCount.m; sourceTree = ""; }; 118 | C1D846FE204358FE0016E52D /* TWXConsumerKeysOverride.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXConsumerKeysOverride.h; sourceTree = ""; }; 119 | C1D846FF204358FE0016E52D /* TWXConsumerKeysOverride.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXConsumerKeysOverride.m; sourceTree = ""; }; 120 | /* End PBXFileReference section */ 121 | 122 | /* Begin PBXFrameworksBuildPhase section */ 123 | 335D38C520387EE300379A21 /* Frameworks */ = { 124 | isa = PBXFrameworksBuildPhase; 125 | buildActionMask = 2147483647; 126 | files = ( 127 | ); 128 | runOnlyForDeploymentPostprocessing = 0; 129 | }; 130 | 33B95C282062FF820060B8D5 /* Frameworks */ = { 131 | isa = PBXFrameworksBuildPhase; 132 | buildActionMask = 2147483647; 133 | files = ( 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | /* End PBXFrameworksBuildPhase section */ 138 | 139 | /* Begin PBXGroup section */ 140 | 335D389720382C9100379A21 = { 141 | isa = PBXGroup; 142 | children = ( 143 | 335D38A220382C9100379A21 /* TwitterX */, 144 | 33B95C2C2062FF820060B8D5 /* TwitterXAssets */, 145 | 335D38A120382C9100379A21 /* Products */, 146 | ); 147 | sourceTree = ""; 148 | }; 149 | 335D38A120382C9100379A21 /* Products */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 335D38C920387EE300379A21 /* TwitterX.framework */, 153 | 33B95C2B2062FF820060B8D5 /* TwitterXAssets.bundle */, 154 | ); 155 | name = Products; 156 | sourceTree = ""; 157 | }; 158 | 335D38A220382C9100379A21 /* TwitterX */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 37E3E21F2078D3F200C5B683 /* Theme */, 162 | 33B95C1A206294D20060B8D5 /* Service */, 163 | 335D38DA2038DA4B00379A21 /* Utilities */, 164 | 335D38B220386E2D00379A21 /* Features */, 165 | 335D38CB20387EE300379A21 /* TwitterX.h */, 166 | 335D38DF2038E45C00379A21 /* TwitterX.m */, 167 | 335D38CC20387EE300379A21 /* Info.plist */, 168 | ); 169 | path = TwitterX; 170 | sourceTree = ""; 171 | }; 172 | 335D38B220386E2D00379A21 /* Features */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 335D38B420386E4F00379A21 /* TWXFeature.h */, 176 | 335D38B820386E8300379A21 /* TWXCharactersLimit.h */, 177 | 335D38B920386E8300379A21 /* TWXCharactersLimit.m */, 178 | 335D38BC20386EA500379A21 /* TWXRoundedAvatars.h */, 179 | 335D38BD20386EA500379A21 /* TWXRoundedAvatars.m */, 180 | 33075924203D9EF700CAA86C /* TWXEnhancedFullscreen.h */, 181 | 33075925203D9EF700CAA86C /* TWXEnhancedFullscreen.m */, 182 | C1D846F22041BB080016E52D /* TWXRoundedStatusContent.h */, 183 | C1D846F32041BB080016E52D /* TWXRoundedStatusContent.m */, 184 | C1D846F62041F5940016E52D /* TWXLightThemeReborn.h */, 185 | C1D846F72041F5940016E52D /* TWXLightThemeReborn.m */, 186 | C1D846FE204358FE0016E52D /* TWXConsumerKeysOverride.h */, 187 | C1D846FF204358FE0016E52D /* TWXConsumerKeysOverride.m */, 188 | C1D846FA2042B08C0016E52D /* TWXRepliesCount.h */, 189 | C1D846FB2042B08C0016E52D /* TWXRepliesCount.m */, 190 | C18C74F420507003001B0AAC /* TWXDirectMessagesCrash.h */, 191 | C18C74F520507003001B0AAC /* TWXDirectMessagesCrash.m */, 192 | C135546E2061C4E700F6504D /* TWXBookmarkActions.h */, 193 | C135546F2061C4E700F6504D /* TWXBookmarkActions.m */, 194 | 33B95C1F2062D1B30060B8D5 /* TWXBookmarksTimeline.h */, 195 | 33B95C202062D1B30060B8D5 /* TWXBookmarksTimeline.m */, 196 | 33B95C232062E7720060B8D5 /* TWXBookmarksSidebarItem.h */, 197 | 33B95C242062E7720060B8D5 /* TWXBookmarksSidebarItem.m */, 198 | 37E3E2242078D93200C5B683 /* TWXThemeMenu.h */, 199 | 37E3E2252078D93200C5B683 /* TWXThemeMenu.m */, 200 | 37E3E2382079ED5900C5B683 /* TWXTheme.h */, 201 | 37E3E2392079ED5900C5B683 /* TWXTheme.m */, 202 | 37E3E2402079FF3600C5B683 /* TWXSource.h */, 203 | 37E3E2412079FF3600C5B683 /* TWXSource.m */, 204 | ); 205 | path = Features; 206 | sourceTree = ""; 207 | }; 208 | 335D38DA2038DA4B00379A21 /* Utilities */ = { 209 | isa = PBXGroup; 210 | children = ( 211 | 335D38DB2038DA6100379A21 /* TWXRuntime.h */, 212 | 335D38DC2038DA6100379A21 /* TWXRuntime.m */, 213 | C1B3282D2059160D00009EA8 /* NSView+TWX.h */, 214 | C1B3282E2059160D00009EA8 /* NSView+TWX.m */, 215 | 33B95C482063A6EA0060B8D5 /* NSBundle+TWX.h */, 216 | 33B95C492063A6EA0060B8D5 /* NSBundle+TWX.m */, 217 | 33B95C3C206310760060B8D5 /* NSImage+TWX.h */, 218 | 33B95C3D206310760060B8D5 /* NSImage+TWX.m */, 219 | 37E3E2302078F98700C5B683 /* NSColor+TWX.h */, 220 | 37E3E2312078F98700C5B683 /* NSColor+TWX.m */, 221 | 37E3E23C2079FE8700C5B683 /* NSString+TWX.h */, 222 | 37E3E23D2079FE8700C5B683 /* NSString+TWX.m */, 223 | ); 224 | path = Utilities; 225 | sourceTree = ""; 226 | }; 227 | 33B95C1A206294D20060B8D5 /* Service */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | 33B95C152062767F0060B8D5 /* TWXAPI.h */, 231 | 33B95C162062767F0060B8D5 /* TWXAPI.m */, 232 | 33B95C1B206294EE0060B8D5 /* TWXAPI+Bookmarks.h */, 233 | 33B95C1C206294EE0060B8D5 /* TWXAPI+Bookmarks.m */, 234 | ); 235 | path = Service; 236 | sourceTree = ""; 237 | }; 238 | 33B95C2C2062FF820060B8D5 /* TwitterXAssets */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | 33B95C2D2062FF820060B8D5 /* Info.plist */, 242 | 33B95C3720630A210060B8D5 /* Images */, 243 | ); 244 | path = TwitterXAssets; 245 | sourceTree = ""; 246 | }; 247 | 33B95C3720630A210060B8D5 /* Images */ = { 248 | isa = PBXGroup; 249 | children = ( 250 | 33B95C4420631AB20060B8D5 /* bookmarks.png */, 251 | 33B95C4520631AB20060B8D5 /* bookmarks@2x.png */, 252 | ); 253 | path = Images; 254 | sourceTree = ""; 255 | }; 256 | 37E3E21F2078D3F200C5B683 /* Theme */ = { 257 | isa = PBXGroup; 258 | children = ( 259 | 37E3E2202078D41800C5B683 /* TWXDuskTheme.h */, 260 | 37E3E2212078D41800C5B683 /* TWXDuskTheme.m */, 261 | ); 262 | path = Theme; 263 | sourceTree = ""; 264 | }; 265 | /* End PBXGroup section */ 266 | 267 | /* Begin PBXHeadersBuildPhase section */ 268 | 335D38C620387EE300379A21 /* Headers */ = { 269 | isa = PBXHeadersBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | C1D846FC2042B08C0016E52D /* TWXRepliesCount.h in Headers */, 273 | 335D38D520387EF600379A21 /* TWXCharactersLimit.h in Headers */, 274 | 33B95C4A2063A6EA0060B8D5 /* NSBundle+TWX.h in Headers */, 275 | 37E3E2322078F98700C5B683 /* NSColor+TWX.h in Headers */, 276 | 33B95C1D206294EE0060B8D5 /* TWXAPI+Bookmarks.h in Headers */, 277 | C1D84700204358FE0016E52D /* TWXConsumerKeysOverride.h in Headers */, 278 | C18C74F620507003001B0AAC /* TWXDirectMessagesCrash.h in Headers */, 279 | C1D846F82041F5950016E52D /* TWXLightThemeReborn.h in Headers */, 280 | 37E3E2262078D93200C5B683 /* TWXThemeMenu.h in Headers */, 281 | 335D38DD2038DA6100379A21 /* TWXRuntime.h in Headers */, 282 | 37E3E23A2079ED5900C5B683 /* TWXTheme.h in Headers */, 283 | 33B95C3E206310760060B8D5 /* NSImage+TWX.h in Headers */, 284 | 335D38D320387EF100379A21 /* TWXFeature.h in Headers */, 285 | 33B95C172062767F0060B8D5 /* TWXAPI.h in Headers */, 286 | C1D846F42041BB080016E52D /* TWXRoundedStatusContent.h in Headers */, 287 | C13554702061C4E700F6504D /* TWXBookmarkActions.h in Headers */, 288 | C1B3282F2059160D00009EA8 /* NSView+TWX.h in Headers */, 289 | 335D38CD20387EE300379A21 /* TwitterX.h in Headers */, 290 | 37E3E23E2079FE8700C5B683 /* NSString+TWX.h in Headers */, 291 | 37E3E2422079FF3600C5B683 /* TWXSource.h in Headers */, 292 | 33B95C252062E7720060B8D5 /* TWXBookmarksSidebarItem.h in Headers */, 293 | 37E3E2222078D41800C5B683 /* TWXDuskTheme.h in Headers */, 294 | 33B95C212062D1B30060B8D5 /* TWXBookmarksTimeline.h in Headers */, 295 | 335D38D720387EFC00379A21 /* TWXRoundedAvatars.h in Headers */, 296 | 33075926203D9EF700CAA86C /* TWXEnhancedFullscreen.h in Headers */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXHeadersBuildPhase section */ 301 | 302 | /* Begin PBXNativeTarget section */ 303 | 335D38C820387EE300379A21 /* TwitterX */ = { 304 | isa = PBXNativeTarget; 305 | buildConfigurationList = 335D38CE20387EE300379A21 /* Build configuration list for PBXNativeTarget "TwitterX" */; 306 | buildPhases = ( 307 | 335D38C420387EE300379A21 /* Sources */, 308 | 335D38C520387EE300379A21 /* Frameworks */, 309 | 335D38C620387EE300379A21 /* Headers */, 310 | 335D38C720387EE300379A21 /* Resources */, 311 | ); 312 | buildRules = ( 313 | ); 314 | dependencies = ( 315 | ); 316 | name = TwitterX; 317 | productName = TwitterX; 318 | productReference = 335D38C920387EE300379A21 /* TwitterX.framework */; 319 | productType = "com.apple.product-type.framework"; 320 | }; 321 | 33B95C2A2062FF820060B8D5 /* TwitterXAssets */ = { 322 | isa = PBXNativeTarget; 323 | buildConfigurationList = 33B95C2E2062FF820060B8D5 /* Build configuration list for PBXNativeTarget "TwitterXAssets" */; 324 | buildPhases = ( 325 | 33B95C272062FF820060B8D5 /* Sources */, 326 | 33B95C282062FF820060B8D5 /* Frameworks */, 327 | 33B95C292062FF820060B8D5 /* Resources */, 328 | ); 329 | buildRules = ( 330 | ); 331 | dependencies = ( 332 | ); 333 | name = TwitterXAssets; 334 | productName = TwitterXAssets; 335 | productReference = 33B95C2B2062FF820060B8D5 /* TwitterXAssets.bundle */; 336 | productType = "com.apple.product-type.bundle"; 337 | }; 338 | /* End PBXNativeTarget section */ 339 | 340 | /* Begin PBXProject section */ 341 | 335D389820382C9100379A21 /* Project object */ = { 342 | isa = PBXProject; 343 | attributes = { 344 | LastUpgradeCheck = 0920; 345 | ORGANIZATIONNAME = twitterx; 346 | TargetAttributes = { 347 | 335D38C820387EE300379A21 = { 348 | CreatedOnToolsVersion = 9.2; 349 | ProvisioningStyle = Automatic; 350 | }; 351 | 33B95C2A2062FF820060B8D5 = { 352 | CreatedOnToolsVersion = 9.2; 353 | ProvisioningStyle = Automatic; 354 | }; 355 | }; 356 | }; 357 | buildConfigurationList = 335D389B20382C9100379A21 /* Build configuration list for PBXProject "TwitterX" */; 358 | compatibilityVersion = "Xcode 8.0"; 359 | developmentRegion = en; 360 | hasScannedForEncodings = 0; 361 | knownRegions = ( 362 | en, 363 | ); 364 | mainGroup = 335D389720382C9100379A21; 365 | productRefGroup = 335D38A120382C9100379A21 /* Products */; 366 | projectDirPath = ""; 367 | projectRoot = ""; 368 | targets = ( 369 | 335D38C820387EE300379A21 /* TwitterX */, 370 | 33B95C2A2062FF820060B8D5 /* TwitterXAssets */, 371 | ); 372 | }; 373 | /* End PBXProject section */ 374 | 375 | /* Begin PBXResourcesBuildPhase section */ 376 | 335D38C720387EE300379A21 /* Resources */ = { 377 | isa = PBXResourcesBuildPhase; 378 | buildActionMask = 2147483647; 379 | files = ( 380 | 33B95C34206308190060B8D5 /* TwitterXAssets.bundle in Resources */, 381 | ); 382 | runOnlyForDeploymentPostprocessing = 0; 383 | }; 384 | 33B95C292062FF820060B8D5 /* Resources */ = { 385 | isa = PBXResourcesBuildPhase; 386 | buildActionMask = 2147483647; 387 | files = ( 388 | 33B95C4720631AB30060B8D5 /* bookmarks@2x.png in Resources */, 389 | 33B95C4620631AB30060B8D5 /* bookmarks.png in Resources */, 390 | ); 391 | runOnlyForDeploymentPostprocessing = 0; 392 | }; 393 | /* End PBXResourcesBuildPhase section */ 394 | 395 | /* Begin PBXSourcesBuildPhase section */ 396 | 335D38C420387EE300379A21 /* Sources */ = { 397 | isa = PBXSourcesBuildPhase; 398 | buildActionMask = 2147483647; 399 | files = ( 400 | 335D38E02038E45C00379A21 /* TwitterX.m in Sources */, 401 | 335D38D820387EFF00379A21 /* TWXRoundedAvatars.m in Sources */, 402 | 33B95C182062767F0060B8D5 /* TWXAPI.m in Sources */, 403 | 37E3E23F2079FE8700C5B683 /* NSString+TWX.m in Sources */, 404 | C18C74F720507003001B0AAC /* TWXDirectMessagesCrash.m in Sources */, 405 | 37E3E2272078D93200C5B683 /* TWXThemeMenu.m in Sources */, 406 | 33B95C3F206310760060B8D5 /* NSImage+TWX.m in Sources */, 407 | 33B95C1E206294EE0060B8D5 /* TWXAPI+Bookmarks.m in Sources */, 408 | 33B95C4B2063A6EA0060B8D5 /* NSBundle+TWX.m in Sources */, 409 | 33075927203D9EF700CAA86C /* TWXEnhancedFullscreen.m in Sources */, 410 | 37E3E2432079FF3600C5B683 /* TWXSource.m in Sources */, 411 | 37E3E23B2079ED5900C5B683 /* TWXTheme.m in Sources */, 412 | C1D84701204358FE0016E52D /* TWXConsumerKeysOverride.m in Sources */, 413 | 37E3E2332078F98700C5B683 /* NSColor+TWX.m in Sources */, 414 | C13554712061C4E700F6504D /* TWXBookmarkActions.m in Sources */, 415 | 335D38D620387EF900379A21 /* TWXCharactersLimit.m in Sources */, 416 | 37E3E2232078D41800C5B683 /* TWXDuskTheme.m in Sources */, 417 | 33B95C262062E7720060B8D5 /* TWXBookmarksSidebarItem.m in Sources */, 418 | C1D846FD2042B08C0016E52D /* TWXRepliesCount.m in Sources */, 419 | 335D38DE2038DA6100379A21 /* TWXRuntime.m in Sources */, 420 | 33B95C222062D1B30060B8D5 /* TWXBookmarksTimeline.m in Sources */, 421 | C1D846F92041F5950016E52D /* TWXLightThemeReborn.m in Sources */, 422 | C1B328302059160D00009EA8 /* NSView+TWX.m in Sources */, 423 | C1D846F52041BB080016E52D /* TWXRoundedStatusContent.m in Sources */, 424 | ); 425 | runOnlyForDeploymentPostprocessing = 0; 426 | }; 427 | 33B95C272062FF820060B8D5 /* Sources */ = { 428 | isa = PBXSourcesBuildPhase; 429 | buildActionMask = 2147483647; 430 | files = ( 431 | ); 432 | runOnlyForDeploymentPostprocessing = 0; 433 | }; 434 | /* End PBXSourcesBuildPhase section */ 435 | 436 | /* Begin XCBuildConfiguration section */ 437 | 335D38A720382C9100379A21 /* Debug */ = { 438 | isa = XCBuildConfiguration; 439 | buildSettings = { 440 | ALWAYS_SEARCH_USER_PATHS = NO; 441 | CLANG_ANALYZER_NONNULL = YES; 442 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 443 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 444 | CLANG_CXX_LIBRARY = "libc++"; 445 | CLANG_ENABLE_MODULES = YES; 446 | CLANG_ENABLE_OBJC_ARC = YES; 447 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 448 | CLANG_WARN_BOOL_CONVERSION = YES; 449 | CLANG_WARN_COMMA = YES; 450 | CLANG_WARN_CONSTANT_CONVERSION = YES; 451 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 452 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 453 | CLANG_WARN_EMPTY_BODY = YES; 454 | CLANG_WARN_ENUM_CONVERSION = YES; 455 | CLANG_WARN_INFINITE_RECURSION = YES; 456 | CLANG_WARN_INT_CONVERSION = YES; 457 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 458 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 459 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 460 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 461 | CLANG_WARN_STRICT_PROTOTYPES = YES; 462 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 463 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 464 | CLANG_WARN_UNREACHABLE_CODE = YES; 465 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 466 | CODE_SIGN_IDENTITY = "-"; 467 | COPY_PHASE_STRIP = NO; 468 | DEBUG_INFORMATION_FORMAT = dwarf; 469 | ENABLE_STRICT_OBJC_MSGSEND = YES; 470 | ENABLE_TESTABILITY = YES; 471 | GCC_C_LANGUAGE_STANDARD = gnu11; 472 | GCC_DYNAMIC_NO_PIC = NO; 473 | GCC_NO_COMMON_BLOCKS = YES; 474 | GCC_OPTIMIZATION_LEVEL = 0; 475 | GCC_PREPROCESSOR_DEFINITIONS = ( 476 | "DEBUG=1", 477 | "$(inherited)", 478 | ); 479 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 480 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 481 | GCC_WARN_UNDECLARED_SELECTOR = YES; 482 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 483 | GCC_WARN_UNUSED_FUNCTION = YES; 484 | GCC_WARN_UNUSED_VARIABLE = YES; 485 | MACOSX_DEPLOYMENT_TARGET = 10.10; 486 | MTL_ENABLE_DEBUG_INFO = YES; 487 | ONLY_ACTIVE_ARCH = YES; 488 | SDKROOT = macosx; 489 | }; 490 | name = Debug; 491 | }; 492 | 335D38A820382C9100379A21 /* Release */ = { 493 | isa = XCBuildConfiguration; 494 | buildSettings = { 495 | ALWAYS_SEARCH_USER_PATHS = NO; 496 | CLANG_ANALYZER_NONNULL = YES; 497 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 498 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 499 | CLANG_CXX_LIBRARY = "libc++"; 500 | CLANG_ENABLE_MODULES = YES; 501 | CLANG_ENABLE_OBJC_ARC = YES; 502 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 503 | CLANG_WARN_BOOL_CONVERSION = YES; 504 | CLANG_WARN_COMMA = YES; 505 | CLANG_WARN_CONSTANT_CONVERSION = YES; 506 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 507 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 508 | CLANG_WARN_EMPTY_BODY = YES; 509 | CLANG_WARN_ENUM_CONVERSION = YES; 510 | CLANG_WARN_INFINITE_RECURSION = YES; 511 | CLANG_WARN_INT_CONVERSION = YES; 512 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 513 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 514 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 515 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 516 | CLANG_WARN_STRICT_PROTOTYPES = YES; 517 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 518 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 519 | CLANG_WARN_UNREACHABLE_CODE = YES; 520 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 521 | CODE_SIGN_IDENTITY = "-"; 522 | COPY_PHASE_STRIP = NO; 523 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 524 | ENABLE_NS_ASSERTIONS = NO; 525 | ENABLE_STRICT_OBJC_MSGSEND = YES; 526 | GCC_C_LANGUAGE_STANDARD = gnu11; 527 | GCC_NO_COMMON_BLOCKS = YES; 528 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 529 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 530 | GCC_WARN_UNDECLARED_SELECTOR = YES; 531 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 532 | GCC_WARN_UNUSED_FUNCTION = YES; 533 | GCC_WARN_UNUSED_VARIABLE = YES; 534 | MACOSX_DEPLOYMENT_TARGET = 10.10; 535 | MTL_ENABLE_DEBUG_INFO = NO; 536 | SDKROOT = macosx; 537 | }; 538 | name = Release; 539 | }; 540 | 335D38CF20387EE300379A21 /* Debug */ = { 541 | isa = XCBuildConfiguration; 542 | buildSettings = { 543 | CODE_SIGN_IDENTITY = ""; 544 | CODE_SIGN_STYLE = Automatic; 545 | COMBINE_HIDPI_IMAGES = YES; 546 | CURRENT_PROJECT_VERSION = 1; 547 | DEFINES_MODULE = YES; 548 | DYLIB_COMPATIBILITY_VERSION = 1; 549 | DYLIB_CURRENT_VERSION = 1; 550 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 551 | FRAMEWORK_VERSION = A; 552 | GCC_WARN_UNDECLARED_SELECTOR = NO; 553 | GCC_WARN_UNUSED_VALUE = NO; 554 | GCC_WARN_UNUSED_VARIABLE = YES; 555 | INFOPLIST_FILE = TwitterX/Info.plist; 556 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 557 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 558 | MACOSX_DEPLOYMENT_TARGET = 10.10; 559 | PRODUCT_BUNDLE_IDENTIFIER = com.twitterx.TwitterX; 560 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 561 | SKIP_INSTALL = YES; 562 | VERSIONING_SYSTEM = "apple-generic"; 563 | VERSION_INFO_PREFIX = ""; 564 | }; 565 | name = Debug; 566 | }; 567 | 335D38D020387EE300379A21 /* Release */ = { 568 | isa = XCBuildConfiguration; 569 | buildSettings = { 570 | CODE_SIGN_IDENTITY = ""; 571 | CODE_SIGN_STYLE = Automatic; 572 | COMBINE_HIDPI_IMAGES = YES; 573 | CURRENT_PROJECT_VERSION = 1; 574 | DEFINES_MODULE = YES; 575 | DYLIB_COMPATIBILITY_VERSION = 1; 576 | DYLIB_CURRENT_VERSION = 1; 577 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 578 | FRAMEWORK_VERSION = A; 579 | GCC_WARN_UNDECLARED_SELECTOR = NO; 580 | GCC_WARN_UNUSED_VALUE = NO; 581 | GCC_WARN_UNUSED_VARIABLE = YES; 582 | INFOPLIST_FILE = TwitterX/Info.plist; 583 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 584 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 585 | MACOSX_DEPLOYMENT_TARGET = 10.10; 586 | PRODUCT_BUNDLE_IDENTIFIER = com.twitterx.TwitterX; 587 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 588 | SKIP_INSTALL = YES; 589 | VERSIONING_SYSTEM = "apple-generic"; 590 | VERSION_INFO_PREFIX = ""; 591 | }; 592 | name = Release; 593 | }; 594 | 33B95C2F2062FF820060B8D5 /* Debug */ = { 595 | isa = XCBuildConfiguration; 596 | buildSettings = { 597 | CODE_SIGN_STYLE = Automatic; 598 | COMBINE_HIDPI_IMAGES = YES; 599 | INFOPLIST_FILE = TwitterXAssets/Info.plist; 600 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; 601 | MACOSX_DEPLOYMENT_TARGET = 10.9; 602 | PRODUCT_BUNDLE_IDENTIFIER = com.twitterx.TwitterXAssets; 603 | PRODUCT_NAME = "$(TARGET_NAME)"; 604 | SKIP_INSTALL = YES; 605 | WRAPPER_EXTENSION = bundle; 606 | }; 607 | name = Debug; 608 | }; 609 | 33B95C302062FF820060B8D5 /* Release */ = { 610 | isa = XCBuildConfiguration; 611 | buildSettings = { 612 | CODE_SIGN_STYLE = Automatic; 613 | COMBINE_HIDPI_IMAGES = YES; 614 | INFOPLIST_FILE = TwitterXAssets/Info.plist; 615 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; 616 | MACOSX_DEPLOYMENT_TARGET = 10.9; 617 | PRODUCT_BUNDLE_IDENTIFIER = com.twitterx.TwitterXAssets; 618 | PRODUCT_NAME = "$(TARGET_NAME)"; 619 | SKIP_INSTALL = YES; 620 | WRAPPER_EXTENSION = bundle; 621 | }; 622 | name = Release; 623 | }; 624 | /* End XCBuildConfiguration section */ 625 | 626 | /* Begin XCConfigurationList section */ 627 | 335D389B20382C9100379A21 /* Build configuration list for PBXProject "TwitterX" */ = { 628 | isa = XCConfigurationList; 629 | buildConfigurations = ( 630 | 335D38A720382C9100379A21 /* Debug */, 631 | 335D38A820382C9100379A21 /* Release */, 632 | ); 633 | defaultConfigurationIsVisible = 0; 634 | defaultConfigurationName = Release; 635 | }; 636 | 335D38CE20387EE300379A21 /* Build configuration list for PBXNativeTarget "TwitterX" */ = { 637 | isa = XCConfigurationList; 638 | buildConfigurations = ( 639 | 335D38CF20387EE300379A21 /* Debug */, 640 | 335D38D020387EE300379A21 /* Release */, 641 | ); 642 | defaultConfigurationIsVisible = 0; 643 | defaultConfigurationName = Release; 644 | }; 645 | 33B95C2E2062FF820060B8D5 /* Build configuration list for PBXNativeTarget "TwitterXAssets" */ = { 646 | isa = XCConfigurationList; 647 | buildConfigurations = ( 648 | 33B95C2F2062FF820060B8D5 /* Debug */, 649 | 33B95C302062FF820060B8D5 /* Release */, 650 | ); 651 | defaultConfigurationIsVisible = 0; 652 | defaultConfigurationName = Release; 653 | }; 654 | /* End XCConfigurationList section */ 655 | }; 656 | rootObject = 335D389820382C9100379A21 /* Project object */; 657 | } 658 | --------------------------------------------------------------------------------